diff --git a/doc/testing.html b/doc/testing.html index b9838735e4f..31f4fbd1778 100644 --- a/doc/testing.html +++ b/doc/testing.html @@ -535,6 +535,8 @@

REPEAT_COUNT

REPORT

Use this report style when reporting test results (sent to JTReg as -report). Defaults to files.

+

MANUAL

+

Set to true to execute manual tests only.

Gtest keywords

REPEAT

The number of times to repeat the tests diff --git a/doc/testing.md b/doc/testing.md index 0144610a5bf..b95f59de9fd 100644 --- a/doc/testing.md +++ b/doc/testing.md @@ -512,6 +512,10 @@ helps to reproduce intermittent test failures. Defaults to 0. Use this report style when reporting test results (sent to JTReg as `-report`). Defaults to `files`. +#### MANUAL + +Set to `true` to execute manual tests only. + ### Gtest keywords #### REPEAT diff --git a/make/RunTests.gmk b/make/RunTests.gmk index 947389f64f9..1f50b97531b 100644 --- a/make/RunTests.gmk +++ b/make/RunTests.gmk @@ -206,7 +206,7 @@ $(eval $(call ParseKeywordVariable, JTREG, \ SINGLE_KEYWORDS := JOBS TIMEOUT_FACTOR FAILURE_HANDLER_TIMEOUT \ TEST_MODE ASSERT VERBOSE RETAIN TEST_THREAD_FACTORY JVMTI_STRESS_AGENT \ MAX_MEM RUN_PROBLEM_LISTS RETRY_COUNT REPEAT_COUNT MAX_OUTPUT REPORT \ - AOT_JDK $(CUSTOM_JTREG_SINGLE_KEYWORDS), \ + AOT_JDK MANUAL $(CUSTOM_JTREG_SINGLE_KEYWORDS), \ STRING_KEYWORDS := OPTIONS JAVA_OPTIONS VM_OPTIONS KEYWORDS \ EXTRA_PROBLEM_LISTS LAUNCHER_OPTIONS \ $(CUSTOM_JTREG_STRING_KEYWORDS), \ @@ -911,7 +911,13 @@ define SetupRunJtregTestBody -vmoption:-Dtest.boot.jdk="$$(BOOT_JDK)" \ -vmoption:-Djava.io.tmpdir="$$($1_TEST_TMP_DIR)" - $1_JTREG_BASIC_OPTIONS += -automatic -ignore:quiet + $1_JTREG_BASIC_OPTIONS += -ignore:quiet + + ifeq ($$(JTREG_MANUAL), true) + $1_JTREG_BASIC_OPTIONS += -manual + else + $1_JTREG_BASIC_OPTIONS += -automatic + endif # Make it possible to specify the JIB_DATA_DIR for tests using the # JIB Artifact resolver @@ -1151,6 +1157,7 @@ define SetupRunJtregTestBody $$(EXPR) $$($1_PASSED) + $$($1_FAILED) + $$($1_ERROR) + $$($1_SKIPPED))) \ , \ $$(eval $1_PASSED_AND_RUNTIME_SKIPPED := 0) \ + $$(eval $1_PASSED := 0) \ $$(eval $1_RUNTIME_SKIPPED := 0) \ $$(eval $1_SKIPPED := 0) \ $$(eval $1_FAILED := 0) \ diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4 index 9d58a280998..6298bcae416 100644 --- a/make/autoconf/flags-cflags.m4 +++ b/make/autoconf/flags-cflags.m4 @@ -282,10 +282,17 @@ AC_DEFUN([FLAGS_SETUP_OPTIMIZATION], C_O_FLAG_DEBUG_JVM="-O0" C_O_FLAG_NONE="-O0" + if test "x$TOOLCHAIN_TYPE" = xgcc; then + C_O_FLAG_LTO="-flto=auto -fuse-linker-plugin -fno-strict-aliasing -fno-fat-lto-objects" + else + C_O_FLAG_LTO="-flto -fno-strict-aliasing" + fi + if test "x$TOOLCHAIN_TYPE" = xclang && test "x$OPENJDK_TARGET_OS" = xaix; then C_O_FLAG_HIGHEST_JVM="${C_O_FLAG_HIGHEST_JVM} -finline-functions" C_O_FLAG_HIGHEST="${C_O_FLAG_HIGHEST} -finline-functions" C_O_FLAG_HI="${C_O_FLAG_HI} -finline-functions" + C_O_FLAG_LTO="${C_O_FLAG_LTO} -ffat-lto-objects" fi # -D_FORTIFY_SOURCE=2 hardening option needs optimization (at least -O1) enabled @@ -317,6 +324,7 @@ AC_DEFUN([FLAGS_SETUP_OPTIMIZATION], C_O_FLAG_DEBUG_JVM="" C_O_FLAG_NONE="-Od" C_O_FLAG_SIZE="-O1" + C_O_FLAG_LTO="-GL" fi # Now copy to C++ flags @@ -328,6 +336,7 @@ AC_DEFUN([FLAGS_SETUP_OPTIMIZATION], CXX_O_FLAG_DEBUG_JVM="$C_O_FLAG_DEBUG_JVM" CXX_O_FLAG_NONE="$C_O_FLAG_NONE" CXX_O_FLAG_SIZE="$C_O_FLAG_SIZE" + CXX_O_FLAG_LTO="$C_O_FLAG_LTO" # Adjust optimization flags according to debug level. case $DEBUG_LEVEL in @@ -360,12 +369,15 @@ AC_DEFUN([FLAGS_SETUP_OPTIMIZATION], AC_SUBST(C_O_FLAG_NORM) AC_SUBST(C_O_FLAG_NONE) AC_SUBST(C_O_FLAG_SIZE) + AC_SUBST(C_O_FLAG_LTO) + AC_SUBST(CXX_O_FLAG_HIGHEST_JVM) AC_SUBST(CXX_O_FLAG_HIGHEST) AC_SUBST(CXX_O_FLAG_HI) AC_SUBST(CXX_O_FLAG_NORM) AC_SUBST(CXX_O_FLAG_NONE) AC_SUBST(CXX_O_FLAG_SIZE) + AC_SUBST(CXX_O_FLAG_LTO) ]) AC_DEFUN([FLAGS_SETUP_CFLAGS], diff --git a/make/autoconf/flags-ldflags.m4 b/make/autoconf/flags-ldflags.m4 index 66f8904db89..572790b567b 100644 --- a/make/autoconf/flags-ldflags.m4 +++ b/make/autoconf/flags-ldflags.m4 @@ -50,7 +50,14 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER], # add -z,relro (mark relocations read only) for all libs # add -z,now ("full relro" - more of the Global Offset Table GOT is marked read only) # add --no-as-needed to disable default --as-needed link flag on some GCC toolchains + # add --icf=all (Identical Code Folding — merges identical functions) BASIC_LDFLAGS="-Wl,-z,defs -Wl,-z,relro -Wl,-z,now -Wl,--no-as-needed -Wl,--exclude-libs,ALL" + if test "x$LINKER_TYPE" = "xgold"; then + if test x$DEBUG_LEVEL = xrelease; then + BASIC_LDFLAGS="$BASIC_LDFLAGS -Wl,--icf=all" + fi + fi + # Linux : remove unused code+data in link step if test "x$ENABLE_LINKTIME_GC" = xtrue; then if test "x$OPENJDK_TARGET_CPU" = xs390x; then @@ -61,6 +68,7 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER], fi BASIC_LDFLAGS_JVM_ONLY="" + LDFLAGS_LTO="-flto=auto -fuse-linker-plugin -fno-strict-aliasing" LDFLAGS_CXX_PARTIAL_LINKING="$MACHINE_FLAG -r" @@ -68,6 +76,7 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER], BASIC_LDFLAGS_JVM_ONLY="-mno-omit-leaf-frame-pointer -mstack-alignment=16 \ -fPIC" + LDFLAGS_LTO="-flto=auto -fuse-linker-plugin -fno-strict-aliasing" LDFLAGS_CXX_PARTIAL_LINKING="$MACHINE_FLAG -r" if test "x$OPENJDK_TARGET_OS" = xlinux; then @@ -87,6 +96,7 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER], BASIC_LDFLAGS="-opt:ref" BASIC_LDFLAGS_JDK_ONLY="-incremental:no" BASIC_LDFLAGS_JVM_ONLY="-opt:icf,8 -subsystem:windows" + LDFLAGS_LTO="-LTCG:INCREMENTAL" fi if (test "x$TOOLCHAIN_TYPE" = xgcc || test "x$TOOLCHAIN_TYPE" = xclang) \ @@ -148,6 +158,7 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER], # Export some intermediate variables for compatibility LDFLAGS_CXX_JDK="$DEBUGLEVEL_LDFLAGS_JDK_ONLY" + AC_SUBST(LDFLAGS_LTO) AC_SUBST(LDFLAGS_CXX_JDK) AC_SUBST(LDFLAGS_CXX_PARTIAL_LINKING) ]) diff --git a/make/autoconf/spec.gmk.template b/make/autoconf/spec.gmk.template index 0b336721d65..b3d58704c50 100644 --- a/make/autoconf/spec.gmk.template +++ b/make/autoconf/spec.gmk.template @@ -513,12 +513,14 @@ C_O_FLAG_HI := @C_O_FLAG_HI@ C_O_FLAG_NORM := @C_O_FLAG_NORM@ C_O_FLAG_NONE := @C_O_FLAG_NONE@ C_O_FLAG_SIZE := @C_O_FLAG_SIZE@ +C_O_FLAG_LTO := @C_O_FLAG_LTO@ CXX_O_FLAG_HIGHEST_JVM := @CXX_O_FLAG_HIGHEST_JVM@ CXX_O_FLAG_HIGHEST := @CXX_O_FLAG_HIGHEST@ CXX_O_FLAG_HI := @CXX_O_FLAG_HI@ CXX_O_FLAG_NORM := @CXX_O_FLAG_NORM@ CXX_O_FLAG_NONE := @CXX_O_FLAG_NONE@ CXX_O_FLAG_SIZE := @CXX_O_FLAG_SIZE@ +CXX_O_FLAG_LTO := @CXX_O_FLAG_LTO@ GENDEPS_FLAGS := @GENDEPS_FLAGS@ @@ -587,6 +589,9 @@ LDFLAGS_CXX_JDK := @LDFLAGS_CXX_JDK@ # LDFLAGS specific to partial linking. LDFLAGS_CXX_PARTIAL_LINKING := @LDFLAGS_CXX_PARTIAL_LINKING@ +# LDFLAGS specific to link time optimization +LDFLAGS_LTO := @LDFLAGS_LTO@ + # Sometimes a different linker is needed for c++ libs LDCXX := @LDCXX@ # The flags for linking libstdc++ linker. diff --git a/make/autoconf/toolchain.m4 b/make/autoconf/toolchain.m4 index 4662c62d901..15210efe4a7 100644 --- a/make/autoconf/toolchain.m4 +++ b/make/autoconf/toolchain.m4 @@ -516,6 +516,7 @@ AC_DEFUN([TOOLCHAIN_EXTRACT_LD_VERSION], if [ [[ "$LINKER_VERSION_STRING" == *gold* ]] ]; then [ LINKER_VERSION_NUMBER=`$ECHO $LINKER_VERSION_STRING | \ $SED -e 's/.* \([0-9][0-9]*\(\.[0-9][0-9]*\)*\).*) .*/\1/'` ] + LINKER_TYPE=gold else [ LINKER_VERSION_NUMBER=`$ECHO $LINKER_VERSION_STRING | \ $SED -e 's/.* \([0-9][0-9]*\(\.[0-9][0-9]*\)*\).*/\1/'` ] diff --git a/make/common/NativeCompilation.gmk b/make/common/NativeCompilation.gmk index 9721f1c0aca..28e186adf5f 100644 --- a/make/common/NativeCompilation.gmk +++ b/make/common/NativeCompilation.gmk @@ -98,6 +98,7 @@ include native/Paths.gmk # SYSROOT_CFLAGS the compiler flags for using the specific sysroot # SYSROOT_LDFLAGS the linker flags for using the specific sysroot # OPTIMIZATION sets optimization level to NONE, LOW, HIGH, HIGHEST, HIGHEST_JVM, SIZE +# LINK_TIME_OPTIMIZATION if set to true, enables link time optimization # DISABLED_WARNINGS_ Disable the given warnings for the specified toolchain # DISABLED_WARNINGS__ Disable the given warnings for the specified # toolchain and target OS diff --git a/make/common/native/Flags.gmk b/make/common/native/Flags.gmk index 747e090b816..843701cb4db 100644 --- a/make/common/native/Flags.gmk +++ b/make/common/native/Flags.gmk @@ -194,6 +194,11 @@ define SetupCompilerFlags $1_EXTRA_CXXFLAGS += $(CFLAGS_WARNINGS_ARE_ERRORS) endif + ifeq (true, $$($1_LINK_TIME_OPTIMIZATION)) + $1_EXTRA_CFLAGS += $(C_O_FLAG_LTO) + $1_EXTRA_CXXFLAGS += $(CXX_O_FLAG_LTO) + endif + ifeq (NONE, $$($1_OPTIMIZATION)) $1_OPT_CFLAGS := $(C_O_FLAG_NONE) $1_OPT_CXXFLAGS := $(CXX_O_FLAG_NONE) @@ -222,6 +227,10 @@ define SetupLinkerFlags # Pickup extra OPENJDK_TARGET_OS_TYPE, OPENJDK_TARGET_OS and TOOLCHAIN_TYPE # dependent variables for LDFLAGS and LIBS, and additionally the pair dependent # TOOLCHAIN_TYPE plus OPENJDK_TARGET_OS + ifeq ($$($1_LINK_TIME_OPTIMIZATION), true) + $1_EXTRA_LDFLAGS += $(LDFLAGS_LTO) + endif + $1_EXTRA_LDFLAGS += $$($1_LDFLAGS_$(OPENJDK_TARGET_OS_TYPE)) $$($1_LDFLAGS_$(OPENJDK_TARGET_OS)) \ $$($1_LDFLAGS_$(TOOLCHAIN_TYPE)) $$($1_LDFLAGS_$(TOOLCHAIN_TYPE)_$(OPENJDK_TARGET_OS)) $1_EXTRA_LIBS += $$($1_LIBS_$(OPENJDK_TARGET_OS_TYPE)) $$($1_LIBS_$(OPENJDK_TARGET_OS)) \ diff --git a/make/hotspot/lib/CompileJvm.gmk b/make/hotspot/lib/CompileJvm.gmk index a8b90c92e4d..b0ea27e5081 100644 --- a/make/hotspot/lib/CompileJvm.gmk +++ b/make/hotspot/lib/CompileJvm.gmk @@ -234,6 +234,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJVM, \ LDFLAGS := $(JVM_LDFLAGS), \ LIBS := $(JVM_LIBS), \ OPTIMIZATION := $(JVM_OPTIMIZATION), \ + LINK_TIME_OPTIMIZATION := $(JVM_LTO), \ OBJECT_DIR := $(JVM_OUTPUTDIR)/objs, \ STRIPFLAGS := $(JVM_STRIPFLAGS), \ EMBED_MANIFEST := true, \ diff --git a/make/hotspot/lib/JvmFeatures.gmk b/make/hotspot/lib/JvmFeatures.gmk index 79bbd6a4106..90ea8a985e3 100644 --- a/make/hotspot/lib/JvmFeatures.gmk +++ b/make/hotspot/lib/JvmFeatures.gmk @@ -175,22 +175,12 @@ ifeq ($(call check-jvm-feature, link-time-opt), true) # Set JVM_OPTIMIZATION directly so other jvm-feature flags can override it # later on if desired JVM_OPTIMIZATION := HIGHEST_JVM - ifeq ($(call isCompiler, gcc), true) - JVM_CFLAGS_FEATURES += -flto=auto -fuse-linker-plugin -fno-strict-aliasing \ - -fno-fat-lto-objects - JVM_LDFLAGS_FEATURES += $(CXX_O_FLAG_HIGHEST_JVM) -flto=auto \ - -fuse-linker-plugin -fno-strict-aliasing - else ifeq ($(call isCompiler, clang), true) - JVM_CFLAGS_FEATURES += -flto -fno-strict-aliasing - ifeq ($(call isBuildOs, aix), true) - JVM_CFLAGS_FEATURES += -ffat-lto-objects - endif - JVM_LDFLAGS_FEATURES += $(CXX_O_FLAG_HIGHEST_JVM) -flto -fno-strict-aliasing - else ifeq ($(call isCompiler, microsoft), true) - JVM_CFLAGS_FEATURES += -GL - JVM_LDFLAGS_FEATURES += -LTCG:INCREMENTAL + JVM_LTO := true + ifneq ($(call isCompiler, microsoft), true) + JVM_LDFLAGS_FEATURES += $(CXX_O_FLAG_HIGHEST_JVM) endif else + JVM_LTO := false ifeq ($(call isCompiler, gcc), true) JVM_LDFLAGS_FEATURES += -O1 endif diff --git a/make/modules/java.desktop/lib/ClientLibraries.gmk b/make/modules/java.desktop/lib/ClientLibraries.gmk index 2c29092cdd6..b036973b776 100644 --- a/make/modules/java.desktop/lib/ClientLibraries.gmk +++ b/make/modules/java.desktop/lib/ClientLibraries.gmk @@ -226,6 +226,7 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false) EXCLUDE_FILES := imageioJPEG.c jpegdecoder.c pngtest.c, \ EXCLUDES := $(LIBSPLASHSCREEN_EXCLUDES), \ OPTIMIZATION := SIZE, \ + LINK_TIME_OPTIMIZATION := true, \ CFLAGS := $(LIBSPLASHSCREEN_CFLAGS) \ $(GIFLIB_CFLAGS) $(LIBJPEG_CFLAGS) $(PNG_CFLAGS) $(LIBZ_CFLAGS) \ $(ICONV_CFLAGS), \ diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index e8f9733fe7e..364db407bd3 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -1,6 +1,7 @@ // // Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. // Copyright (c) 2014, 2024, Red Hat, Inc. All rights reserved. +// Copyright 2025 Arm Limited and/or its affiliates. // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. // // This code is free software; you can redistribute it and/or modify it @@ -1194,15 +1195,10 @@ class HandlerImpl { public: - static int emit_exception_handler(C2_MacroAssembler *masm); static int emit_deopt_handler(C2_MacroAssembler* masm); - static uint size_exception_handler() { - return MacroAssembler::far_codestub_branch_size(); - } - static uint size_deopt_handler() { - // count one adr and one far branch instruction + // count one branch instruction and one far call instruction sequence return NativeInstruction::instruction_size + MacroAssembler::far_codestub_branch_size(); } }; @@ -2261,25 +2257,6 @@ uint MachUEPNode::size(PhaseRegAlloc* ra_) const //============================================================================= -// Emit exception handler code. -int HandlerImpl::emit_exception_handler(C2_MacroAssembler* masm) -{ - // mov rscratch1 #exception_blob_entry_point - // br rscratch1 - // Note that the code buffer's insts_mark is always relative to insts. - // That's why we must use the macroassembler to generate a handler. - address base = __ start_a_stub(size_exception_handler()); - if (base == nullptr) { - ciEnv::current()->record_failure("CodeCache is full"); - return 0; // CodeBuffer::expand failed - } - int offset = __ offset(); - __ far_jump(RuntimeAddress(OptoRuntime::exception_blob()->entry_point())); - assert(__ offset() - offset <= (int) size_exception_handler(), "overflow"); - __ end_a_stub(); - return offset; -} - // Emit deopt handler code. int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm) { @@ -2290,14 +2267,20 @@ int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm) ciEnv::current()->record_failure("CodeCache is full"); return 0; // CodeBuffer::expand failed } + int offset = __ offset(); + Label start; + __ bind(start); + __ far_call(RuntimeAddress(SharedRuntime::deopt_blob()->unpack())); - __ adr(lr, __ pc()); - __ far_jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack())); + int entry_offset = __ offset(); + __ b(start); assert(__ offset() - offset == (int) size_deopt_handler(), "overflow"); + assert(__ offset() - entry_offset >= NativePostCallNop::first_check_size, + "out of bounds read in post-call NOP check"); __ end_a_stub(); - return offset; + return entry_offset; } // REQUIRED MATCHER CODE @@ -3388,28 +3371,28 @@ encode %{ // aarch64_enc_cmpxchg_acq is that we use load-acquire in the // CompareAndSwap sequence to serve as a barrier on acquiring a // lock. - enc_class aarch64_enc_cmpxchg_acq(memory mem, iRegLNoSp oldval, iRegLNoSp newval) %{ + enc_class aarch64_enc_cmpxchg_acq(memory mem, iRegL oldval, iRegL newval) %{ guarantee($mem$$index == -1 && $mem$$disp == 0, "impossible encoding"); __ cmpxchg($mem$$base$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, /*acquire*/ true, /*release*/ true, /*weak*/ false, noreg); %} - enc_class aarch64_enc_cmpxchgw_acq(memory mem, iRegINoSp oldval, iRegINoSp newval) %{ + enc_class aarch64_enc_cmpxchgw_acq(memory mem, iRegI oldval, iRegI newval) %{ guarantee($mem$$index == -1 && $mem$$disp == 0, "impossible encoding"); __ cmpxchg($mem$$base$$Register, $oldval$$Register, $newval$$Register, Assembler::word, /*acquire*/ true, /*release*/ true, /*weak*/ false, noreg); %} - enc_class aarch64_enc_cmpxchgs_acq(memory mem, iRegINoSp oldval, iRegINoSp newval) %{ + enc_class aarch64_enc_cmpxchgs_acq(memory mem, iRegI oldval, iRegI newval) %{ guarantee($mem$$index == -1 && $mem$$disp == 0, "impossible encoding"); __ cmpxchg($mem$$base$$Register, $oldval$$Register, $newval$$Register, Assembler::halfword, /*acquire*/ true, /*release*/ true, /*weak*/ false, noreg); %} - enc_class aarch64_enc_cmpxchgb_acq(memory mem, iRegINoSp oldval, iRegINoSp newval) %{ + enc_class aarch64_enc_cmpxchgb_acq(memory mem, iRegI oldval, iRegI newval) %{ guarantee($mem$$index == -1 && $mem$$disp == 0, "impossible encoding"); __ cmpxchg($mem$$base$$Register, $oldval$$Register, $newval$$Register, Assembler::byte, /*acquire*/ true, /*release*/ true, @@ -3417,7 +3400,7 @@ encode %{ %} // auxiliary used for CompareAndSwapX to set result register - enc_class aarch64_enc_cset_eq(iRegINoSp res) %{ + enc_class aarch64_enc_cset_eq(iRegI res) %{ Register res_reg = as_Register($res$$reg); __ cset(res_reg, Assembler::EQ); %} @@ -8403,7 +8386,7 @@ instruct castVVMask(pRegGov dst) // XXX No flag versions for CompareAndSwap{I,L,P,N} because matcher // can't match them -instruct compareAndSwapB(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoSp newval, rFlagsReg cr) %{ +instruct compareAndSwapB(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval, rFlagsReg cr) %{ match(Set res (CompareAndSwapB mem (Binary oldval newval))); ins_cost(2 * VOLATILE_REF_COST); @@ -8421,7 +8404,7 @@ instruct compareAndSwapB(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoS ins_pipe(pipe_slow); %} -instruct compareAndSwapS(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoSp newval, rFlagsReg cr) %{ +instruct compareAndSwapS(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval, rFlagsReg cr) %{ match(Set res (CompareAndSwapS mem (Binary oldval newval))); ins_cost(2 * VOLATILE_REF_COST); @@ -8439,7 +8422,7 @@ instruct compareAndSwapS(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoS ins_pipe(pipe_slow); %} -instruct compareAndSwapI(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoSp newval, rFlagsReg cr) %{ +instruct compareAndSwapI(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval, rFlagsReg cr) %{ match(Set res (CompareAndSwapI mem (Binary oldval newval))); ins_cost(2 * VOLATILE_REF_COST); @@ -8457,7 +8440,7 @@ instruct compareAndSwapI(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoS ins_pipe(pipe_slow); %} -instruct compareAndSwapL(iRegINoSp res, indirect mem, iRegLNoSp oldval, iRegLNoSp newval, rFlagsReg cr) %{ +instruct compareAndSwapL(iRegINoSp res, indirect mem, iRegL oldval, iRegL newval, rFlagsReg cr) %{ match(Set res (CompareAndSwapL mem (Binary oldval newval))); ins_cost(2 * VOLATILE_REF_COST); @@ -8494,7 +8477,7 @@ instruct compareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval ins_pipe(pipe_slow); %} -instruct compareAndSwapN(iRegINoSp res, indirect mem, iRegNNoSp oldval, iRegNNoSp newval, rFlagsReg cr) %{ +instruct compareAndSwapN(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval, rFlagsReg cr) %{ match(Set res (CompareAndSwapN mem (Binary oldval newval))); predicate(n->as_LoadStore()->barrier_data() == 0); @@ -8515,7 +8498,7 @@ instruct compareAndSwapN(iRegINoSp res, indirect mem, iRegNNoSp oldval, iRegNNoS // alternative CompareAndSwapX when we are eliding barriers -instruct compareAndSwapBAcq(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoSp newval, rFlagsReg cr) %{ +instruct compareAndSwapBAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval, rFlagsReg cr) %{ predicate(needs_acquiring_load_exclusive(n)); match(Set res (CompareAndSwapB mem (Binary oldval newval))); @@ -8534,7 +8517,7 @@ instruct compareAndSwapBAcq(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegI ins_pipe(pipe_slow); %} -instruct compareAndSwapSAcq(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoSp newval, rFlagsReg cr) %{ +instruct compareAndSwapSAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval, rFlagsReg cr) %{ predicate(needs_acquiring_load_exclusive(n)); match(Set res (CompareAndSwapS mem (Binary oldval newval))); @@ -8553,7 +8536,7 @@ instruct compareAndSwapSAcq(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegI ins_pipe(pipe_slow); %} -instruct compareAndSwapIAcq(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoSp newval, rFlagsReg cr) %{ +instruct compareAndSwapIAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval, rFlagsReg cr) %{ predicate(needs_acquiring_load_exclusive(n)); match(Set res (CompareAndSwapI mem (Binary oldval newval))); @@ -8572,7 +8555,7 @@ instruct compareAndSwapIAcq(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegI ins_pipe(pipe_slow); %} -instruct compareAndSwapLAcq(iRegINoSp res, indirect mem, iRegLNoSp oldval, iRegLNoSp newval, rFlagsReg cr) %{ +instruct compareAndSwapLAcq(iRegINoSp res, indirect mem, iRegL oldval, iRegL newval, rFlagsReg cr) %{ predicate(needs_acquiring_load_exclusive(n)); match(Set res (CompareAndSwapL mem (Binary oldval newval))); @@ -8610,7 +8593,7 @@ instruct compareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP new ins_pipe(pipe_slow); %} -instruct compareAndSwapNAcq(iRegINoSp res, indirect mem, iRegNNoSp oldval, iRegNNoSp newval, rFlagsReg cr) %{ +instruct compareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval, rFlagsReg cr) %{ predicate(needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() == 0); match(Set res (CompareAndSwapN mem (Binary oldval newval))); diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index 9ab463125fe..37a6a130e0d 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -449,12 +449,20 @@ int LIR_Assembler::emit_deopt_handler() { int offset = code_offset(); - __ adr(lr, pc()); - __ far_jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack())); + Label start; + __ bind(start); + + __ far_call(RuntimeAddress(SharedRuntime::deopt_blob()->unpack())); + + int entry_offset = __ offset(); + __ b(start); + guarantee(code_offset() - offset <= deopt_handler_size(), "overflow"); + assert(code_offset() - entry_offset >= NativePostCallNop::first_check_size, + "out of bounds read in post-call NOP check"); __ end_a_stub(); - return offset; + return entry_offset; } void LIR_Assembler::add_debug_info_for_branch(address adr, CodeEmitInfo* info) { diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp index 12b941fc4f7..729cd2827b7 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp @@ -71,7 +71,7 @@ friend class ArrayCopyStub; // CompiledDirectCall::to_trampoline_stub_size() _call_stub_size = 13 * NativeInstruction::instruction_size, _exception_handler_size = DEBUG_ONLY(1*K) NOT_DEBUG(175), - _deopt_handler_size = 7 * NativeInstruction::instruction_size + _deopt_handler_size = 4 * NativeInstruction::instruction_size }; public: diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp index 5a7fececafa..f2003dd9b55 100644 --- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp @@ -394,12 +394,6 @@ void NativePostCallNop::make_deopt() { NativeDeoptInstruction::insert(addr_at(0)); } -#ifdef ASSERT -static bool is_movk_to_zr(uint32_t insn) { - return ((insn & 0xffe0001f) == 0xf280001f); -} -#endif - bool NativePostCallNop::patch(int32_t oopmap_slot, int32_t cb_offset) { if (((oopmap_slot & 0xff) != oopmap_slot) || ((cb_offset & 0xffffff) != cb_offset)) { return false; // cannot encode diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp index df5d97c2376..c30cb911d96 100644 --- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp @@ -526,14 +526,31 @@ inline NativeLdSt* NativeLdSt_at(address addr) { // can store an offset from the initial nop to the nmethod. class NativePostCallNop: public NativeInstruction { +private: + static bool is_movk_to_zr(uint32_t insn) { + return ((insn & 0xffe0001f) == 0xf280001f); + } + public: + enum AArch64_specific_constants { + // The two parts should be checked separately to prevent out of bounds access in case + // the return address points to the deopt handler stub code entry point which could be + // at the end of page. + first_check_size = instruction_size + }; + bool check() const { - uint64_t insns = *(uint64_t*)addr_at(0); - // Check for two instructions: nop; movk zr, xx - // These instructions only ever appear together in a post-call - // NOP, so it's unnecessary to check that the third instruction is - // a MOVK as well. - return (insns & 0xffe0001fffffffff) == 0xf280001fd503201f; + // Check the first instruction is NOP. + if (is_nop()) { + uint32_t insn = *(uint32_t*)addr_at(first_check_size); + // Check next instruction is MOVK zr, xx. + // These instructions only ever appear together in a post-call + // NOP, so it's unnecessary to check that the third instruction is + // a MOVK as well. + return is_movk_to_zr(insn); + } + + return false; } bool decode(int32_t& oopmap_slot, int32_t& cb_offset) const { diff --git a/src/hotspot/cpu/aarch64/runtime_aarch64.cpp b/src/hotspot/cpu/aarch64/runtime_aarch64.cpp index d45f9865bd2..e36aa21b567 100644 --- a/src/hotspot/cpu/aarch64/runtime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/runtime_aarch64.cpp @@ -260,8 +260,6 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() { //------------------------------generate_exception_blob--------------------------- // creates exception blob at the end -// Using exception blob, this code is jumped from a compiled method. -// (see emit_exception_handler in aarch64.ad file) // // Given an exception pc at a call we call into the runtime for the // handler in this method. This handler might merely restore state diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad index 92c0df68deb..af010caf616 100644 --- a/src/hotspot/cpu/arm/arm.ad +++ b/src/hotspot/cpu/arm/arm.ad @@ -105,14 +105,8 @@ class HandlerImpl { public: - static int emit_exception_handler(C2_MacroAssembler *masm); static int emit_deopt_handler(C2_MacroAssembler* masm); - static uint size_exception_handler() { - return ( 3 * 4 ); - } - - static uint size_deopt_handler() { return ( 9 * 4 ); } @@ -876,26 +870,6 @@ uint MachUEPNode::size(PhaseRegAlloc *ra_) const { //============================================================================= -// Emit exception handler code. -int HandlerImpl::emit_exception_handler(C2_MacroAssembler* masm) { - address base = __ start_a_stub(size_exception_handler()); - if (base == nullptr) { - ciEnv::current()->record_failure("CodeCache is full"); - return 0; // CodeBuffer::expand failed - } - - int offset = __ offset(); - - // OK to trash LR, because exception blob will kill it - __ jump(OptoRuntime::exception_blob()->entry_point(), relocInfo::runtime_call_type, LR_tmp); - - assert(__ offset() - offset <= (int) size_exception_handler(), "overflow"); - - __ end_a_stub(); - - return offset; -} - int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm) { // Can't use any of the current frame's registers as we may have deopted // at a poll and everything can be live. @@ -906,19 +880,28 @@ int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm) { } int offset = __ offset(); - address deopt_pc = __ pc(); - __ sub(SP, SP, wordSize); // make room for saved PC - __ push(LR); // save LR that may be live when we get here - __ mov_relative_address(LR, deopt_pc); - __ str(LR, Address(SP, wordSize)); // save deopt PC - __ pop(LR); // restore LR + Label start; + __ bind(start); + __ jump(SharedRuntime::deopt_blob()->unpack(), relocInfo::runtime_call_type, noreg); + int entry_offset = __ offset(); + address deopt_pc = __ pc(); + // Preserve R0 and reserve space for the address of the entry point + __ push(RegisterSet(R0) | RegisterSet(R1)); + // Store the entry point address + __ mov_relative_address(R0, deopt_pc); + __ str(R0, Address(SP, wordSize)); + __ pop(R0); // restore R0 + __ b(start); + assert(__ offset() - offset <= (int) size_deopt_handler(), "overflow"); + assert(__ offset() - entry_offset >= NativePostCallNop::first_check_size, + "out of bounds read in post-call NOP check"); __ end_a_stub(); - return offset; + return entry_offset; } bool Matcher::match_rule_supported(int opcode) { diff --git a/src/hotspot/cpu/arm/arm_32.ad b/src/hotspot/cpu/arm/arm_32.ad index 00bf3bd61e4..9438e8da8b5 100644 --- a/src/hotspot/cpu/arm/arm_32.ad +++ b/src/hotspot/cpu/arm/arm_32.ad @@ -62,22 +62,22 @@ register %{ // Integer/Long Registers // ---------------------------- -reg_def R_R0 (SOC, SOC, Op_RegI, 0, R(0)->as_VMReg()); -reg_def R_R1 (SOC, SOC, Op_RegI, 1, R(1)->as_VMReg()); -reg_def R_R2 (SOC, SOC, Op_RegI, 2, R(2)->as_VMReg()); -reg_def R_R3 (SOC, SOC, Op_RegI, 3, R(3)->as_VMReg()); -reg_def R_R4 (SOC, SOE, Op_RegI, 4, R(4)->as_VMReg()); -reg_def R_R5 (SOC, SOE, Op_RegI, 5, R(5)->as_VMReg()); -reg_def R_R6 (SOC, SOE, Op_RegI, 6, R(6)->as_VMReg()); -reg_def R_R7 (SOC, SOE, Op_RegI, 7, R(7)->as_VMReg()); -reg_def R_R8 (SOC, SOE, Op_RegI, 8, R(8)->as_VMReg()); -reg_def R_R9 (SOC, SOE, Op_RegI, 9, R(9)->as_VMReg()); -reg_def R_R10(NS, SOE, Op_RegI, 10, R(10)->as_VMReg()); -reg_def R_R11(NS, SOE, Op_RegI, 11, R(11)->as_VMReg()); -reg_def R_R12(SOC, SOC, Op_RegI, 12, R(12)->as_VMReg()); -reg_def R_R13(NS, NS, Op_RegI, 13, R(13)->as_VMReg()); -reg_def R_R14(SOC, SOC, Op_RegI, 14, R(14)->as_VMReg()); -reg_def R_R15(NS, NS, Op_RegI, 15, R(15)->as_VMReg()); +reg_def R_R0 (SOC, SOC, Op_RegI, 0, as_Register(0)->as_VMReg()); +reg_def R_R1 (SOC, SOC, Op_RegI, 1, as_Register(1)->as_VMReg()); +reg_def R_R2 (SOC, SOC, Op_RegI, 2, as_Register(2)->as_VMReg()); +reg_def R_R3 (SOC, SOC, Op_RegI, 3, as_Register(3)->as_VMReg()); +reg_def R_R4 (SOC, SOE, Op_RegI, 4, as_Register(4)->as_VMReg()); +reg_def R_R5 (SOC, SOE, Op_RegI, 5, as_Register(5)->as_VMReg()); +reg_def R_R6 (SOC, SOE, Op_RegI, 6, as_Register(6)->as_VMReg()); +reg_def R_R7 (SOC, SOE, Op_RegI, 7, as_Register(7)->as_VMReg()); +reg_def R_R8 (SOC, SOE, Op_RegI, 8, as_Register(8)->as_VMReg()); +reg_def R_R9 (SOC, SOE, Op_RegI, 9, as_Register(9)->as_VMReg()); +reg_def R_R10(NS, SOE, Op_RegI, 10, as_Register(10)->as_VMReg()); +reg_def R_R11(NS, SOE, Op_RegI, 11, as_Register(11)->as_VMReg()); +reg_def R_R12(SOC, SOC, Op_RegI, 12, as_Register(12)->as_VMReg()); +reg_def R_R13(NS, NS, Op_RegI, 13, as_Register(13)->as_VMReg()); +reg_def R_R14(SOC, SOC, Op_RegI, 14, as_Register(14)->as_VMReg()); +reg_def R_R15(NS, NS, Op_RegI, 15, as_Register(15)->as_VMReg()); // ---------------------------- // Float/Double Registers diff --git a/src/hotspot/cpu/arm/assembler_arm_32.hpp b/src/hotspot/cpu/arm/assembler_arm_32.hpp index ae13644ecf9..d6524f08680 100644 --- a/src/hotspot/cpu/arm/assembler_arm_32.hpp +++ b/src/hotspot/cpu/arm/assembler_arm_32.hpp @@ -114,7 +114,7 @@ class RegisterSet { } RegisterSet(Register first, Register last) { - assert(first < last, "encoding constraint"); + assert(first->encoding() < last->encoding(), "encoding constraint"); _encoding = (1 << (last->encoding() + 1)) - (1 << first->encoding()); } diff --git a/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp b/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp index 8e49cfcbcaa..3ef02e44b65 100644 --- a/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp +++ b/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp @@ -181,7 +181,7 @@ void MonitorEnterStub::emit_code(LIR_Assembler* ce) { const Register lock_reg = _lock_reg->as_pointer_register(); ce->verify_reserved_argument_area_size(2); - if (obj_reg < lock_reg) { + if (obj_reg->encoding() < lock_reg->encoding()) { __ stmia(SP, RegisterSet(obj_reg) | RegisterSet(lock_reg)); } else { __ str(obj_reg, Address(SP)); diff --git a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp index 219c49d1f14..b314577c2c8 100644 --- a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp +++ b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp @@ -272,14 +272,22 @@ int LIR_Assembler::emit_deopt_handler() { int offset = code_offset(); + Label start; + __ bind(start); + + __ jump(SharedRuntime::deopt_blob()->unpack(), relocInfo::runtime_call_type, noreg); + + int entry_offset = __ offset(); __ mov_relative_address(LR, __ pc()); __ push(LR); // stub expects LR to be saved - __ jump(SharedRuntime::deopt_blob()->unpack(), relocInfo::runtime_call_type, noreg); + __ b(start); assert(code_offset() - offset <= deopt_handler_size(), "overflow"); + assert(code_offset() - entry_offset >= NativePostCallNop::first_check_size, + "out of bounds read in post-call NOP check"); __ end_a_stub(); - return offset; + return entry_offset; } @@ -2631,11 +2639,11 @@ void LIR_Assembler::volatile_move_op(LIR_Opr src, LIR_Opr dest, BasicType type, const Register src_hi = src->as_register_hi(); assert(addr->index()->is_illegal() && addr->disp() == 0, "The address is simple already"); - if (src_lo < src_hi) { + if (src_lo->encoding() < src_hi->encoding()) { null_check_offset = __ offset(); __ stmia(addr->base()->as_register(), RegisterSet(src_lo) | RegisterSet(src_hi)); } else { - assert(src_lo < Rtemp, "Rtemp is higher than any allocatable register"); + assert(src_lo->encoding() < Rtemp->encoding(), "Rtemp is higher than any allocatable register"); __ mov(Rtemp, src_hi); null_check_offset = __ offset(); __ stmia(addr->base()->as_register(), RegisterSet(src_lo) | RegisterSet(Rtemp)); @@ -2648,10 +2656,10 @@ void LIR_Assembler::volatile_move_op(LIR_Opr src, LIR_Opr dest, BasicType type, assert(addr->index()->is_illegal() && addr->disp() == 0, "The address is simple already"); null_check_offset = __ offset(); - if (dest_lo < dest_hi) { + if (dest_lo->encoding() < dest_hi->encoding()) { __ ldmia(addr->base()->as_register(), RegisterSet(dest_lo) | RegisterSet(dest_hi)); } else { - assert(dest_lo < Rtemp, "Rtemp is higher than any allocatable register"); + assert(dest_lo->encoding() < Rtemp->encoding(), "Rtemp is higher than any allocatable register"); __ ldmia(addr->base()->as_register(), RegisterSet(dest_lo) | RegisterSet(Rtemp)); __ mov(dest_hi, Rtemp); } diff --git a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.hpp b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.hpp index 77d13532685..615d2f188ff 100644 --- a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.hpp +++ b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.hpp @@ -54,7 +54,7 @@ enum { _call_stub_size = 16, _exception_handler_size = PRODUCT_ONLY(68) NOT_PRODUCT(68+60), - _deopt_handler_size = 16 + _deopt_handler_size = 20 }; public: diff --git a/src/hotspot/cpu/arm/interp_masm_arm.cpp b/src/hotspot/cpu/arm/interp_masm_arm.cpp index 720413c9c5b..23ecea24eb2 100644 --- a/src/hotspot/cpu/arm/interp_masm_arm.cpp +++ b/src/hotspot/cpu/arm/interp_masm_arm.cpp @@ -409,7 +409,7 @@ void InterpreterMacroAssembler::pop_i(Register r) { void InterpreterMacroAssembler::pop_l(Register lo, Register hi) { assert_different_registers(lo, hi); - assert(lo < hi, "lo must be < hi"); + assert(lo->encoding() < hi->encoding(), "lo must be < hi"); pop(RegisterSet(lo) | RegisterSet(hi)); } @@ -459,7 +459,7 @@ void InterpreterMacroAssembler::push_i(Register r) { void InterpreterMacroAssembler::push_l(Register lo, Register hi) { assert_different_registers(lo, hi); - assert(lo < hi, "lo must be < hi"); + assert(lo->encoding() < hi->encoding(), "lo must be < hi"); push(RegisterSet(lo) | RegisterSet(hi)); } diff --git a/src/hotspot/cpu/arm/nativeInst_arm_32.hpp b/src/hotspot/cpu/arm/nativeInst_arm_32.hpp index ee856bcfe60..82385bf0244 100644 --- a/src/hotspot/cpu/arm/nativeInst_arm_32.hpp +++ b/src/hotspot/cpu/arm/nativeInst_arm_32.hpp @@ -430,6 +430,13 @@ inline NativeCall* nativeCall_before(address return_address) { class NativePostCallNop: public NativeInstruction { public: + enum arm_specific_constants { + // If the check is adjusted to read beyond size of the instruction sequence at the deopt + // handler stub code entry point, it has to happen in two stages - to prevent out of bounds + // access in case the return address points to the entry point which could be at + // the end of page. + first_check_size = instruction_size + }; bool check() const { return is_nop(); } bool decode(int32_t& oopmap_slot, int32_t& cb_offset) const { return false; } bool patch(int32_t oopmap_slot, int32_t cb_offset) { return false; } diff --git a/src/hotspot/cpu/arm/register_arm.cpp b/src/hotspot/cpu/arm/register_arm.cpp index ea3ef87e670..296c55e2e16 100644 --- a/src/hotspot/cpu/arm/register_arm.cpp +++ b/src/hotspot/cpu/arm/register_arm.cpp @@ -25,12 +25,19 @@ #include "register_arm.hpp" #include "utilities/debug.hpp" -const int ConcreteRegisterImpl::max_gpr = ConcreteRegisterImpl::num_gpr; -const int ConcreteRegisterImpl::max_fpr = ConcreteRegisterImpl::num_fpr + - ConcreteRegisterImpl::max_gpr; +Register::RegisterImpl all_RegisterImpls [Register::number_of_registers + 1]; +FloatRegister::FloatRegisterImpl all_FloatRegisterImpls [FloatRegister::number_of_registers + 1]; +VFPSystemRegister::VFPSystemRegisterImpl all_VFPSystemRegisterImpls [VFPSystemRegister::number_of_registers + 1] { + { -1 }, //vfpsnoreg + { VFPSystemRegister::FPSID }, + { VFPSystemRegister::FPSCR }, + { VFPSystemRegister::MVFR0 }, + { VFPSystemRegister::MVFR1 } +}; -const char* RegisterImpl::name() const { - const char* names[number_of_registers] = { +const char* Register::RegisterImpl::name() const { + static const char* names[number_of_registers + 1] = { + "noreg", "r0", "r1", "r2", "r3", "r4", "r5", "r6", #if (FP_REG_NUM == 7) "fp", @@ -45,13 +52,14 @@ const char* RegisterImpl::name() const { #endif "r12", "sp", "lr", "pc" }; - return is_valid() ? names[encoding()] : "noreg"; + return names[encoding() + 1]; } -const char* FloatRegisterImpl::name() const { - const char* names[number_of_registers] = { - "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", - "s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15", +const char* FloatRegister::FloatRegisterImpl::name() const { + static const char* names[number_of_registers + 1] = { + "fnoreg", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15", "s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23", "s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31" #ifdef COMPILER2 @@ -61,5 +69,5 @@ const char* FloatRegisterImpl::name() const { "s56", "s57?","s58", "s59?","s60", "s61?","s62", "s63?" #endif }; - return is_valid() ? names[encoding()] : "fnoreg"; + return names[encoding() + 1]; } diff --git a/src/hotspot/cpu/arm/register_arm.hpp b/src/hotspot/cpu/arm/register_arm.hpp index e0688af0d36..401d25a4fce 100644 --- a/src/hotspot/cpu/arm/register_arm.hpp +++ b/src/hotspot/cpu/arm/register_arm.hpp @@ -31,26 +31,6 @@ class VMRegImpl; typedef VMRegImpl* VMReg; -// These are declared ucontext.h -#undef R0 -#undef R1 -#undef R2 -#undef R3 -#undef R4 -#undef R5 -#undef R6 -#undef R7 -#undef R8 -#undef R9 -#undef R10 -#undef R11 -#undef R12 -#undef R13 -#undef R14 -#undef R15 - -#define R(r) ((Register)(r)) - ///////////////////////////////// // Support for different ARM ABIs // Note: default ABI is for linux @@ -94,25 +74,86 @@ typedef VMRegImpl* VMReg; #define ALIGN_WIDE_ARGUMENTS 1 #endif -#define R0 ((Register)0) -#define R1 ((Register)1) -#define R2 ((Register)2) -#define R3 ((Register)3) -#define R4 ((Register)4) -#define R5 ((Register)5) -#define R6 ((Register)6) -#define R7 ((Register)7) -#define R8 ((Register)8) -#define R9 ((Register)9) -#define R10 ((Register)10) -#define R11 ((Register)11) -#define R12 ((Register)12) -#define R13 ((Register)13) -#define R14 ((Register)14) -#define R15 ((Register)15) - - -#define FP ((Register)FP_REG_NUM) +class Register { + private: + int _encoding; + + constexpr explicit Register(int encoding) : _encoding(encoding) {} + + public: + enum { + number_of_registers = 16, + max_slots_per_register = 1 + }; + + class RegisterImpl : public AbstractRegisterImpl { + friend class Register; + + static constexpr const RegisterImpl* first(); + + public: + + // accessors and testers + int raw_encoding() const { return this - first(); } + int encoding() const { assert(is_valid(), "invalid register"); return raw_encoding(); } + bool is_valid() const { return 0 <= raw_encoding() && raw_encoding() < number_of_registers; } + + inline Register successor() const; + + VMReg as_VMReg() const; + + const char* name() const; + }; + + + inline friend constexpr Register as_Register(int encoding); + + constexpr Register() : _encoding(-1) {} //noreg + + int operator==(const Register r) const { return _encoding == r._encoding; } + int operator!=(const Register r) const { return _encoding != r._encoding; } + + const RegisterImpl* operator->() const { return RegisterImpl::first() + _encoding; } +}; + +extern Register::RegisterImpl all_RegisterImpls[Register::number_of_registers + 1] INTERNAL_VISIBILITY; + +inline constexpr const Register::RegisterImpl* Register::RegisterImpl::first() { + return all_RegisterImpls + 1; +} + +constexpr Register noreg = Register(); + +inline constexpr Register as_Register(int encoding) { + if (0 <= encoding && encoding < Register::number_of_registers) { + return Register(encoding); + } + return noreg; +} + +inline Register Register::RegisterImpl::successor() const { + assert(is_valid(), "sainty"); + return as_Register(encoding() + 1); +} + +constexpr Register R0 = as_Register( 0); +constexpr Register R1 = as_Register( 1); +constexpr Register R2 = as_Register( 2); +constexpr Register R3 = as_Register( 3); +constexpr Register R4 = as_Register( 4); +constexpr Register R5 = as_Register( 5); +constexpr Register R6 = as_Register( 6); +constexpr Register R7 = as_Register( 7); +constexpr Register R8 = as_Register( 8); +constexpr Register R9 = as_Register( 9); +constexpr Register R10 = as_Register(10); +constexpr Register R11 = as_Register(11); +constexpr Register R12 = as_Register(12); +constexpr Register R13 = as_Register(13); +constexpr Register R14 = as_Register(14); +constexpr Register R15 = as_Register(15); + +constexpr Register FP = as_Register(FP_REG_NUM); // Safe use of registers which may be FP on some platforms. // @@ -122,185 +163,170 @@ typedef VMRegImpl* VMReg; // as FP on supported ABIs (and replace R# by altFP_#_11). altFP_#_11 // must be #define to R11 if and only if # is FP_REG_NUM. #if (FP_REG_NUM == 7) -#define altFP_7_11 ((Register)11) +constexpr Register altFP_7_11 = R11; #else -#define altFP_7_11 ((Register)7) +constexpr Register altFP_7_11 = R7; #endif -#define SP R13 -#define LR R14 -#define PC R15 +constexpr Register SP = R13; +constexpr Register LR = R14; +constexpr Register PC = R15; -class RegisterImpl; -typedef RegisterImpl* Register; +class FloatRegister { + private: + int _encoding; -inline Register as_Register(int encoding) { - return (Register)(intptr_t)encoding; -} + constexpr explicit FloatRegister(int encoding) : _encoding(encoding) {} -class RegisterImpl : public AbstractRegisterImpl { public: enum { - number_of_registers = 16 + number_of_registers = NOT_COMPILER2(32) COMPILER2_PRESENT(64), + max_slots_per_register = 1 }; - Register successor() const { return as_Register(encoding() + 1); } + class FloatRegisterImpl : public AbstractRegisterImpl { + friend class FloatRegister; - inline friend Register as_Register(int encoding); + static constexpr const FloatRegisterImpl* first(); - VMReg as_VMReg(); + public: - // accessors - int encoding() const { assert(is_valid(), "invalid register"); return value(); } - const char* name() const; + // accessors and testers + int raw_encoding() const { return this - first(); } + int encoding() const { assert(is_valid(), "invalid register"); return raw_encoding(); } + bool is_valid() const { return 0 <= raw_encoding() && raw_encoding() < number_of_registers; } + inline FloatRegister successor() const; - // testers - bool is_valid() const { return 0 <= value() && value() < number_of_registers; } - -}; + VMReg as_VMReg() const; -CONSTANT_REGISTER_DECLARATION(Register, noreg, (-1)); + int hi_bits() const { + return (encoding() >> 1) & 0xf; + } + int lo_bit() const { + return encoding() & 1; + } -// Use FloatRegister as shortcut -class FloatRegisterImpl; -typedef FloatRegisterImpl* FloatRegister; + int hi_bit() const { + return encoding() >> 5; + } -inline FloatRegister as_FloatRegister(int encoding) { - return (FloatRegister)(intptr_t)encoding; -} - -class FloatRegisterImpl : public AbstractRegisterImpl { - public: - enum { - number_of_registers = NOT_COMPILER2(32) COMPILER2_PRESENT(64) + const char* name() const; }; - inline friend FloatRegister as_FloatRegister(int encoding); + inline friend constexpr FloatRegister as_FloatRegister(int encoding); - VMReg as_VMReg(); + constexpr FloatRegister() : _encoding(-1) {} // fnoreg - int encoding() const { assert(is_valid(), "invalid register"); return value(); } - bool is_valid() const { return 0 <= (intx)this && (intx)this < number_of_registers; } - FloatRegister successor() const { return as_FloatRegister(encoding() + 1); } + int operator==(const FloatRegister r) const { return _encoding == r._encoding; } + int operator!=(const FloatRegister r) const { return _encoding != r._encoding; } - const char* name() const; + const FloatRegisterImpl* operator->() const { return FloatRegisterImpl::first() + _encoding; } +}; - int hi_bits() const { - return (encoding() >> 1) & 0xf; - } +extern FloatRegister::FloatRegisterImpl all_FloatRegisterImpls[FloatRegister::number_of_registers + 1] INTERNAL_VISIBILITY; - int lo_bit() const { - return encoding() & 1; - } +inline constexpr const FloatRegister::FloatRegisterImpl* FloatRegister::FloatRegisterImpl::first() { + return all_FloatRegisterImpls + 1; +} + +constexpr FloatRegister fnoreg = FloatRegister(); - int hi_bit() const { - return encoding() >> 5; +inline constexpr FloatRegister as_FloatRegister(int encoding) { + if (0 <= encoding && encoding < FloatRegister::number_of_registers) { + return FloatRegister(encoding); } -}; + return fnoreg; +} -CONSTANT_REGISTER_DECLARATION(FloatRegister, fnoreg, (-1)); +inline FloatRegister FloatRegister::FloatRegisterImpl::successor() const { + assert(is_valid(), "sainty"); + return as_FloatRegister(encoding() + 1); +} /* * S1-S6 are named with "_reg" suffix to avoid conflict with * constants defined in sharedRuntimeTrig.cpp */ -CONSTANT_REGISTER_DECLARATION(FloatRegister, S0, ( 0)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S1_reg, ( 1)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S2_reg, ( 2)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S3_reg, ( 3)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S4_reg, ( 4)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S5_reg, ( 5)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S6_reg, ( 6)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S7, ( 7)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S8, ( 8)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S9, ( 9)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S10, (10)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S11, (11)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S12, (12)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S13, (13)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S14, (14)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S15, (15)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S16, (16)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S17, (17)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S18, (18)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S19, (19)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S20, (20)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S21, (21)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S22, (22)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S23, (23)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S24, (24)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S25, (25)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S26, (26)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S27, (27)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S28, (28)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S29, (29)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S30, (30)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, S31, (31)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, Stemp, (30)); - -CONSTANT_REGISTER_DECLARATION(FloatRegister, D0, ( 0)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D1, ( 2)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D2, ( 4)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D3, ( 6)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D4, ( 8)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D5, ( 10)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D6, ( 12)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D7, ( 14)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D8, ( 16)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D9, ( 18)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D10, ( 20)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D11, ( 22)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D12, ( 24)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D13, ( 26)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D14, ( 28)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D15, (30)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D16, (32)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D17, (34)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D18, (36)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D19, (38)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D20, (40)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D21, (42)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D22, (44)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D23, (46)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D24, (48)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D25, (50)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D26, (52)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D27, (54)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D28, (56)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D29, (58)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D30, (60)); -CONSTANT_REGISTER_DECLARATION(FloatRegister, D31, (62)); +constexpr FloatRegister S0 = as_FloatRegister( 0); +constexpr FloatRegister S1_reg = as_FloatRegister(1); +constexpr FloatRegister S2_reg = as_FloatRegister(2); +constexpr FloatRegister S3_reg = as_FloatRegister(3); +constexpr FloatRegister S4_reg = as_FloatRegister(4); +constexpr FloatRegister S5_reg = as_FloatRegister(5); +constexpr FloatRegister S6_reg = as_FloatRegister(6); +constexpr FloatRegister S7 = as_FloatRegister( 7); +constexpr FloatRegister S8 = as_FloatRegister( 8); +constexpr FloatRegister S9 = as_FloatRegister( 9); +constexpr FloatRegister S10 = as_FloatRegister(10); +constexpr FloatRegister S11 = as_FloatRegister(11); +constexpr FloatRegister S12 = as_FloatRegister(12); +constexpr FloatRegister S13 = as_FloatRegister(13); +constexpr FloatRegister S14 = as_FloatRegister(14); +constexpr FloatRegister S15 = as_FloatRegister(15); +constexpr FloatRegister S16 = as_FloatRegister(16); +constexpr FloatRegister S17 = as_FloatRegister(17); +constexpr FloatRegister S18 = as_FloatRegister(18); +constexpr FloatRegister S19 = as_FloatRegister(19); +constexpr FloatRegister S20 = as_FloatRegister(20); +constexpr FloatRegister S21 = as_FloatRegister(21); +constexpr FloatRegister S22 = as_FloatRegister(22); +constexpr FloatRegister S23 = as_FloatRegister(23); +constexpr FloatRegister S24 = as_FloatRegister(24); +constexpr FloatRegister S25 = as_FloatRegister(25); +constexpr FloatRegister S26 = as_FloatRegister(26); +constexpr FloatRegister S27 = as_FloatRegister(27); +constexpr FloatRegister S28 = as_FloatRegister(28); +constexpr FloatRegister S29 = as_FloatRegister(29); +constexpr FloatRegister S30 = as_FloatRegister(30); +constexpr FloatRegister S31 = as_FloatRegister(31); +constexpr FloatRegister Stemp = S30; + +constexpr FloatRegister D0 = as_FloatRegister( 0); +constexpr FloatRegister D1 = as_FloatRegister( 2); +constexpr FloatRegister D2 = as_FloatRegister( 4); +constexpr FloatRegister D3 = as_FloatRegister( 6); +constexpr FloatRegister D4 = as_FloatRegister( 8); +constexpr FloatRegister D5 = as_FloatRegister(10); +constexpr FloatRegister D6 = as_FloatRegister(12); +constexpr FloatRegister D7 = as_FloatRegister(14); +constexpr FloatRegister D8 = as_FloatRegister(16); +constexpr FloatRegister D9 = as_FloatRegister(18); +constexpr FloatRegister D10 = as_FloatRegister(20); +constexpr FloatRegister D11 = as_FloatRegister(22); +constexpr FloatRegister D12 = as_FloatRegister(24); +constexpr FloatRegister D13 = as_FloatRegister(26); +constexpr FloatRegister D14 = as_FloatRegister(28); +constexpr FloatRegister D15 = as_FloatRegister(30); +constexpr FloatRegister D16 = as_FloatRegister(32); +constexpr FloatRegister D17 = as_FloatRegister(34); +constexpr FloatRegister D18 = as_FloatRegister(36); +constexpr FloatRegister D19 = as_FloatRegister(38); +constexpr FloatRegister D20 = as_FloatRegister(40); +constexpr FloatRegister D21 = as_FloatRegister(42); +constexpr FloatRegister D22 = as_FloatRegister(44); +constexpr FloatRegister D23 = as_FloatRegister(46); +constexpr FloatRegister D24 = as_FloatRegister(48); +constexpr FloatRegister D25 = as_FloatRegister(50); +constexpr FloatRegister D26 = as_FloatRegister(52); +constexpr FloatRegister D27 = as_FloatRegister(54); +constexpr FloatRegister D28 = as_FloatRegister(56); +constexpr FloatRegister D29 = as_FloatRegister(58); +constexpr FloatRegister D30 = as_FloatRegister(60); +constexpr FloatRegister D31 = as_FloatRegister(62); class ConcreteRegisterImpl : public AbstractRegisterImpl { public: enum { - log_vmregs_per_word = LogBytesPerWord - LogBytesPerInt, // VMRegs are of 4-byte size -#ifdef COMPILER2 - log_bytes_per_fpr = 2, // quad vectors -#else - log_bytes_per_fpr = 2, // double vectors -#endif - log_words_per_fpr = log_bytes_per_fpr - LogBytesPerWord, - words_per_fpr = 1 << log_words_per_fpr, - log_vmregs_per_fpr = log_bytes_per_fpr - LogBytesPerInt, - log_vmregs_per_gpr = log_vmregs_per_word, - vmregs_per_gpr = 1 << log_vmregs_per_gpr, - vmregs_per_fpr = 1 << log_vmregs_per_fpr, - - num_gpr = RegisterImpl::number_of_registers << log_vmregs_per_gpr, - max_gpr0 = num_gpr, - num_fpr = FloatRegisterImpl::number_of_registers << log_vmregs_per_fpr, - max_fpr0 = max_gpr0 + num_fpr, - number_of_registers = num_gpr + num_fpr + 1+1 // APSR and FPSCR so that c2's REG_COUNT <= ConcreteRegisterImpl::number_of_registers - }; + max_gpr = Register::number_of_registers * Register::max_slots_per_register, + max_fpr = max_gpr + FloatRegister::number_of_registers * FloatRegister::max_slots_per_register, - static const int max_gpr; - static const int max_fpr; + number_of_registers = max_fpr + 1+1 // APSR and FPSCR so that c2's REG_COUNT <= ConcreteRegisterImpl::number_of_registers + }; }; typedef AbstractRegSet RegSet; @@ -328,100 +354,156 @@ inline FloatRegister AbstractRegSet::last() { -class VFPSystemRegisterImpl; -typedef VFPSystemRegisterImpl* VFPSystemRegister; -class VFPSystemRegisterImpl : public AbstractRegisterImpl { +class VFPSystemRegister { + private: + int _store_idx; + + constexpr explicit VFPSystemRegister(int store_idx) : _store_idx(store_idx) {} + + enum { + _FPSID_store_idx = 0, + _FPSCR_store_idx = 1, + _MVFR0_store_idx = 2, + _MVFR1_store_idx = 3 + }; + public: - int encoding() const { return value(); } + enum { + FPSID = 0, + FPSCR = 1, + MVFR0 = 6, + MVFR1 = 7, + number_of_registers = 4 + }; + + class VFPSystemRegisterImpl : public AbstractRegisterImpl { + friend class VFPSystemRegister; + + int _encoding; + + static constexpr const VFPSystemRegisterImpl* first(); + + public: + constexpr VFPSystemRegisterImpl(int encoding) : _encoding(encoding) {} + + int encoding() const { return _encoding; } + }; + + inline friend constexpr VFPSystemRegister as_VFPSystemRegister(int encoding); + + constexpr VFPSystemRegister() : _store_idx(-1) {} // vfpsnoreg + + int operator==(const VFPSystemRegister r) const { return _store_idx == r._store_idx; } + int operator!=(const VFPSystemRegister r) const { return _store_idx != r._store_idx; } + + const VFPSystemRegisterImpl* operator->() const { return VFPSystemRegisterImpl::first() + _store_idx; } }; -#define FPSID ((VFPSystemRegister)0) -#define FPSCR ((VFPSystemRegister)1) -#define MVFR0 ((VFPSystemRegister)0x6) -#define MVFR1 ((VFPSystemRegister)0x7) +extern VFPSystemRegister::VFPSystemRegisterImpl all_VFPSystemRegisterImpls[VFPSystemRegister::number_of_registers + 1] INTERNAL_VISIBILITY; + +inline constexpr const VFPSystemRegister::VFPSystemRegisterImpl* VFPSystemRegister::VFPSystemRegisterImpl::first() { + return all_VFPSystemRegisterImpls + 1; +} + +constexpr VFPSystemRegister vfpsnoreg = VFPSystemRegister(); + +inline constexpr VFPSystemRegister as_VFPSystemRegister(int encoding) { + switch (encoding) { + case VFPSystemRegister::FPSID: return VFPSystemRegister(VFPSystemRegister::_FPSID_store_idx); + case VFPSystemRegister::FPSCR: return VFPSystemRegister(VFPSystemRegister::_FPSCR_store_idx); + case VFPSystemRegister::MVFR0: return VFPSystemRegister(VFPSystemRegister::_MVFR0_store_idx); + case VFPSystemRegister::MVFR1: return VFPSystemRegister(VFPSystemRegister::_MVFR1_store_idx); + default: return vfpsnoreg; + } +} + +constexpr VFPSystemRegister FPSID = as_VFPSystemRegister(VFPSystemRegister::FPSID); +constexpr VFPSystemRegister FPSCR = as_VFPSystemRegister(VFPSystemRegister::FPSCR); +constexpr VFPSystemRegister MVFR0 = as_VFPSystemRegister(VFPSystemRegister::MVFR0); +constexpr VFPSystemRegister MVFR1 = as_VFPSystemRegister(VFPSystemRegister::MVFR1); /* * Register definitions shared across interpreter and compiler */ -#define Rexception_obj R4 -#define Rexception_pc R5 +constexpr Register Rexception_obj = R4; +constexpr Register Rexception_pc = R5; /* * Interpreter register definitions common to C++ and template interpreters. */ -#define Rlocals R8 -#define Rmethod R9 -#define Rthread R10 -#define Rtemp R12 +constexpr Register Rlocals = R8; +constexpr Register Rmethod = R9; +constexpr Register Rthread = R10; +constexpr Register Rtemp = R12; // Interpreter calling conventions -#define Rparams SP -#define Rsender_sp R4 +constexpr Register Rparams = SP; +constexpr Register Rsender_sp = R4; // JSR292 // Note: R5_mh is needed only during the call setup, including adapters // This does not seem to conflict with Rexception_pc // In case of issues, R3 might be OK but adapters calling the runtime would have to save it -#define R5_mh R5 // MethodHandle register, used during the call setup +constexpr Register R5_mh = R5; // MethodHandle register, used during the call setup /* * C++ Interpreter Register Defines */ -#define Rsave0 R4 -#define Rsave1 R5 -#define Rsave2 R6 -#define Rstate altFP_7_11 // R7 or R11 -#define Ricklass R8 +constexpr Register Rsave0 = R4; +constexpr Register Rsave1 = R5; +constexpr Register Rsave2 = R6; +constexpr Register Rstate = altFP_7_11; // R7 or R11 +constexpr Register Ricklass = R8; /* * TemplateTable Interpreter Register Usage */ // Temporary registers -#define R0_tmp R0 -#define R1_tmp R1 -#define R2_tmp R2 -#define R3_tmp R3 -#define R4_tmp R4 -#define R5_tmp R5 -#define R12_tmp R12 -#define LR_tmp LR +constexpr Register R0_tmp = R0; +constexpr Register R1_tmp = R1; +constexpr Register R2_tmp = R2; +constexpr Register R3_tmp = R3; +constexpr Register R4_tmp = R4; +constexpr Register R5_tmp = R5; +constexpr Register R12_tmp = R12; +constexpr Register LR_tmp = LR; -#define S0_tmp S0 -#define S1_tmp S1_reg +constexpr FloatRegister S0_tmp = S0; +constexpr FloatRegister S1_tmp = S1_reg; -#define D0_tmp D0 -#define D1_tmp D1 +constexpr FloatRegister D0_tmp = D0; +constexpr FloatRegister D1_tmp = D1; // Temporary registers saved across VM calls (according to C calling conventions) -#define Rtmp_save0 R4 -#define Rtmp_save1 R5 +constexpr Register Rtmp_save0 = R4; +constexpr Register Rtmp_save1 = R5; // Cached TOS value -#define R0_tos R0 +constexpr Register R0_tos = R0; -#define R0_tos_lo R0 -#define R1_tos_hi R1 +constexpr Register R0_tos_lo = R0; +constexpr Register R1_tos_hi = R1; -#define S0_tos S0 -#define D0_tos D0 +constexpr FloatRegister S0_tos = S0; +constexpr FloatRegister D0_tos = D0; // Dispatch table -#define RdispatchTable R6 +constexpr Register RdispatchTable = R6; // Bytecode pointer -#define Rbcp altFP_7_11 +constexpr Register Rbcp = altFP_7_11; // Pre-loaded next bytecode for the dispatch -#define R3_bytecode R3 +constexpr Register R3_bytecode = R3; // Conventions between bytecode templates and stubs -#define R2_ClassCastException_obj R2 -#define R4_ArrayIndexOutOfBounds_index R4 +constexpr Register R2_ClassCastException_obj = R2; +constexpr Register R4_ArrayIndexOutOfBounds_index = R4; // Interpreter expression stack top -#define Rstack_top SP +constexpr Register Rstack_top = SP; /* * Linux 32-bit ARM C ABI Register calling conventions @@ -444,10 +526,11 @@ class VFPSystemRegisterImpl : public AbstractRegisterImpl { * R14 (LR) Link register * R15 (PC) Program Counter */ -#define c_rarg0 R0 -#define c_rarg1 R1 -#define c_rarg2 R2 -#define c_rarg3 R3 + +constexpr Register c_rarg0 = R0; +constexpr Register c_rarg1 = R1; +constexpr Register c_rarg2 = R2; +constexpr Register c_rarg3 = R3; #define GPR_PARAMS 4 @@ -455,10 +538,10 @@ class VFPSystemRegisterImpl : public AbstractRegisterImpl { // Java ABI // XXX Is this correct? -#define j_rarg0 c_rarg0 -#define j_rarg1 c_rarg1 -#define j_rarg2 c_rarg2 -#define j_rarg3 c_rarg3 +constexpr Register j_rarg0 = c_rarg0; +constexpr Register j_rarg1 = c_rarg1; +constexpr Register j_rarg2 = c_rarg2; +constexpr Register j_rarg3 = c_rarg3; #endif // CPU_ARM_REGISTER_ARM_HPP diff --git a/src/hotspot/cpu/arm/runtime_arm.cpp b/src/hotspot/cpu/arm/runtime_arm.cpp index 8d48de5795a..29fd0aa0a10 100644 --- a/src/hotspot/cpu/arm/runtime_arm.cpp +++ b/src/hotspot/cpu/arm/runtime_arm.cpp @@ -182,8 +182,6 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() { //------------------------------ generate_exception_blob --------------------------- // creates exception blob at the end -// Using exception blob, this code is jumped from a compiled method. -// (see emit_exception_handler in sparc.ad file) // // Given an exception pc at a call we call into the runtime for the // handler in this method. This handler might merely restore state diff --git a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp index 76e38d29478..13e1f4493ff 100644 --- a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp +++ b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp @@ -70,7 +70,7 @@ class RegisterSaver { enum RegisterLayout { - fpu_save_size = FloatRegisterImpl::number_of_registers, + fpu_save_size = FloatRegister::number_of_registers, #ifndef __SOFTFP__ D0_offset = 0, #endif @@ -139,8 +139,8 @@ OopMap* RegisterSaver::save_live_registers(MacroAssembler* masm, if (VM_Version::has_vfp3_32()) { __ fpush(FloatRegisterSet(D16, 16)); } else { - if (FloatRegisterImpl::number_of_registers > 32) { - assert(FloatRegisterImpl::number_of_registers == 64, "nb fp registers should be 64"); + if (FloatRegister::number_of_registers > 32) { + assert(FloatRegister::number_of_registers == 64, "nb fp registers should be 64"); __ sub(SP, SP, 32 * wordSize); } } @@ -182,8 +182,8 @@ void RegisterSaver::restore_live_registers(MacroAssembler* masm, bool restore_lr if (VM_Version::has_vfp3_32()) { __ fpop(FloatRegisterSet(D16, 16)); } else { - if (FloatRegisterImpl::number_of_registers > 32) { - assert(FloatRegisterImpl::number_of_registers == 64, "nb fp registers should be 64"); + if (FloatRegister::number_of_registers > 32) { + assert(FloatRegister::number_of_registers == 64, "nb fp registers should be 64"); __ add(SP, SP, 32 * wordSize); } } diff --git a/src/hotspot/cpu/arm/vmreg_arm.cpp b/src/hotspot/cpu/arm/vmreg_arm.cpp index 4ce1dd0be20..efaf38ef729 100644 --- a/src/hotspot/cpu/arm/vmreg_arm.cpp +++ b/src/hotspot/cpu/arm/vmreg_arm.cpp @@ -30,14 +30,14 @@ void VMRegImpl::set_regName() { Register reg = ::as_Register(0); int i; for (i = 0; i < ConcreteRegisterImpl::max_gpr; reg = reg->successor()) { - for (int j = 0; j < (1 << ConcreteRegisterImpl::log_vmregs_per_gpr); j++) { + for (int j = 0; j < Register::max_slots_per_register; j++) { regName[i++] = reg->name(); } } #ifndef __SOFTFP__ FloatRegister freg = ::as_FloatRegister(0); for ( ; i < ConcreteRegisterImpl::max_fpr ; ) { - for (int j = 0; j < (1 << ConcreteRegisterImpl::log_vmregs_per_fpr); j++) { + for (int j = 0; j < Register::max_slots_per_register; j++) { regName[i++] = freg->name(); } freg = freg->successor(); diff --git a/src/hotspot/cpu/arm/vmreg_arm.hpp b/src/hotspot/cpu/arm/vmreg_arm.hpp index c13f443b804..f1dfd09a1e6 100644 --- a/src/hotspot/cpu/arm/vmreg_arm.hpp +++ b/src/hotspot/cpu/arm/vmreg_arm.hpp @@ -36,20 +36,20 @@ inline Register as_Register() { assert(is_Register(), "must be"); assert(is_concrete(), "concrete register expected"); - return ::as_Register(value() >> ConcreteRegisterImpl::log_vmregs_per_gpr); + return ::as_Register(value() / Register::max_slots_per_register); } inline FloatRegister as_FloatRegister() { assert(is_FloatRegister(), "must be"); assert(is_concrete(), "concrete register expected"); - return ::as_FloatRegister((value() - ConcreteRegisterImpl::max_gpr) >> ConcreteRegisterImpl::log_vmregs_per_fpr); + return ::as_FloatRegister((value() - ConcreteRegisterImpl::max_gpr) / FloatRegister::max_slots_per_register); } inline bool is_concrete() { if (is_Register()) { - return ((value() & right_n_bits(ConcreteRegisterImpl::log_vmregs_per_gpr)) == 0); + return (value() % Register::max_slots_per_register == 0); } else if (is_FloatRegister()) { - return (((value() - ConcreteRegisterImpl::max_gpr) & right_n_bits(ConcreteRegisterImpl::log_vmregs_per_fpr)) == 0); + return (value() % FloatRegister::max_slots_per_register == 0); // Single slot } else { return false; } diff --git a/src/hotspot/cpu/arm/vmreg_arm.inline.hpp b/src/hotspot/cpu/arm/vmreg_arm.inline.hpp index f122b9ede70..3e5c18dbda0 100644 --- a/src/hotspot/cpu/arm/vmreg_arm.inline.hpp +++ b/src/hotspot/cpu/arm/vmreg_arm.inline.hpp @@ -25,11 +25,11 @@ #ifndef CPU_ARM_VMREG_ARM_INLINE_HPP #define CPU_ARM_VMREG_ARM_INLINE_HPP -inline VMReg RegisterImpl::as_VMReg() { - return VMRegImpl::as_VMReg(encoding() << ConcreteRegisterImpl::log_vmregs_per_gpr); +inline VMReg Register::RegisterImpl::as_VMReg() const { + return VMRegImpl::as_VMReg(encoding() * Register::max_slots_per_register); } -inline VMReg FloatRegisterImpl::as_VMReg() { - return VMRegImpl::as_VMReg((encoding() << ConcreteRegisterImpl::log_vmregs_per_fpr) + ConcreteRegisterImpl::max_gpr); +inline VMReg FloatRegister::FloatRegisterImpl::as_VMReg() const { + return VMRegImpl::as_VMReg((encoding() * FloatRegister::max_slots_per_register) + ConcreteRegisterImpl::max_gpr); } #endif // CPU_ARM_VMREG_ARM_INLINE_HPP diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp index 108da2039f6..0b48653ae64 100644 --- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp @@ -264,12 +264,19 @@ int LIR_Assembler::emit_deopt_handler() { } int offset = code_offset(); + Label start; + + __ bind(start); __ bl64_patchable(SharedRuntime::deopt_blob()->unpack(), relocInfo::runtime_call_type); + int entry_offset = __ offset(); + __ b(start); guarantee(code_offset() - offset <= deopt_handler_size(), "overflow"); + assert(code_offset() - entry_offset >= NativePostCallNop::first_check_size, + "out of bounds read in post-call NOP check"); __ end_a_stub(); - return offset; + return entry_offset; } diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.hpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.hpp index e4de2eb5c46..6a2f6264850 100644 --- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.hpp @@ -63,7 +63,7 @@ enum { _static_call_stub_size = 4 * BytesPerInstWord + MacroAssembler::b64_patchable_size, // or smaller _call_stub_size = _static_call_stub_size + MacroAssembler::trampoline_stub_size, // or smaller _exception_handler_size = MacroAssembler::b64_patchable_size, // or smaller - _deopt_handler_size = MacroAssembler::bl64_patchable_size + _deopt_handler_size = MacroAssembler::bl64_patchable_size + BytesPerInstWord }; // '_static_call_stub_size' is only used on ppc (see LIR_Assembler::emit_static_call_stub() diff --git a/src/hotspot/cpu/ppc/nativeInst_ppc.hpp b/src/hotspot/cpu/ppc/nativeInst_ppc.hpp index dcb5c2bb3cb..75ca50674bf 100644 --- a/src/hotspot/cpu/ppc/nativeInst_ppc.hpp +++ b/src/hotspot/cpu/ppc/nativeInst_ppc.hpp @@ -51,8 +51,6 @@ class NativeInstruction { friend class Relocation; public: - bool is_post_call_nop() const { return MacroAssembler::is_post_call_nop(long_at(0)); } - bool is_jump() const { return Assembler::is_b(long_at(0)); } // See NativeGeneralJump. bool is_sigtrap_ic_miss_check() { @@ -531,6 +529,14 @@ class NativePostCallNop: public NativeInstruction { }; public: + enum ppc_specific_constants { + // If the check is adjusted to read beyond size of the instruction at the deopt handler stub + // code entry point, it has to happen in two stages - to prevent out of bounds access in case + // the return address points to the entry point which could be at the end of page. + first_check_size = BytesPerInstWord + }; + + bool is_post_call_nop() const { return MacroAssembler::is_post_call_nop(long_at(0)); } bool check() const { return is_post_call_nop(); } bool decode(int32_t& oopmap_slot, int32_t& cb_offset) const { uint32_t instr_bits = long_at(0); diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index c169d673aaf..762536df07f 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -2088,17 +2088,11 @@ class HandlerImpl { public: - static int emit_exception_handler(C2_MacroAssembler *masm); static int emit_deopt_handler(C2_MacroAssembler* masm); - static uint size_exception_handler() { - // The exception_handler is a b64_patchable. - return MacroAssembler::b64_patchable_size; - } - static uint size_deopt_handler() { // The deopt_handler is a bl64_patchable. - return MacroAssembler::bl64_patchable_size; + return MacroAssembler::bl64_patchable_size + BytesPerInstWord; } }; @@ -2114,22 +2108,6 @@ public: source %{ -int HandlerImpl::emit_exception_handler(C2_MacroAssembler *masm) { - address base = __ start_a_stub(size_exception_handler()); - if (base == nullptr) { - ciEnv::current()->record_failure("CodeCache is full"); - return 0; // CodeBuffer::expand failed - } - - int offset = __ offset(); - __ b64_patchable((address)OptoRuntime::exception_blob()->content_begin(), - relocInfo::runtime_call_type); - assert(__ offset() - offset == (int)size_exception_handler(), "must be fixed size"); - __ end_a_stub(); - - return offset; -} - // The deopt_handler is like the exception handler, but it calls to // the deoptimization blob instead of jumping to the exception blob. int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm) { @@ -2140,12 +2118,23 @@ int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm) { } int offset = __ offset(); + + Label start; + __ bind(start); + __ bl64_patchable((address)SharedRuntime::deopt_blob()->unpack(), relocInfo::runtime_call_type); + + int entry_offset = __ offset(); + + __ b(start); + assert(__ offset() - offset == (int) size_deopt_handler(), "must be fixed size"); + assert(__ offset() - entry_offset >= NativePostCallNop::first_check_size, + "out of bounds read in post-call NOP check"); __ end_a_stub(); - return offset; + return entry_offset; } //============================================================================= diff --git a/src/hotspot/cpu/ppc/runtime_ppc.cpp b/src/hotspot/cpu/ppc/runtime_ppc.cpp index 2654075f702..ab658e9de58 100644 --- a/src/hotspot/cpu/ppc/runtime_ppc.cpp +++ b/src/hotspot/cpu/ppc/runtime_ppc.cpp @@ -46,7 +46,6 @@ //------------------------------generate_exception_blob--------------------------- // Creates exception blob at the end. -// Using exception blob, this code is jumped from a compiled method. // // Given an exception pc at a call we call into the runtime for the // handler in this method. This handler might merely restore state diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index db45a2fa4c8..4e427ace404 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp @@ -83,7 +83,6 @@ class RegisterSaver { static OopMap* push_frame_reg_args_and_save_live_registers(MacroAssembler* masm, int* out_frame_size_in_bytes, bool generate_oop_map, - int return_pc_adjustment, ReturnPCLocation return_pc_location, bool save_vectors = false); static void restore_live_registers_and_pop_frame(MacroAssembler* masm, @@ -262,7 +261,6 @@ static const RegisterSaver::LiveRegType RegisterSaver_LiveVecRegs[] = { OopMap* RegisterSaver::push_frame_reg_args_and_save_live_registers(MacroAssembler* masm, int* out_frame_size_in_bytes, bool generate_oop_map, - int return_pc_adjustment, ReturnPCLocation return_pc_location, bool save_vectors) { // Push an abi_reg_args-frame and store all registers which may be live. @@ -271,7 +269,6 @@ OopMap* RegisterSaver::push_frame_reg_args_and_save_live_registers(MacroAssemble // propagated to the RegisterMap of the caller frame during // StackFrameStream construction (needed for deoptimization; see // compiledVFrame::create_stack_value). - // If return_pc_adjustment != 0 adjust the return pc by return_pc_adjustment. // Updated return pc is returned in R31 (if not return_pc_is_pre_saved). // calculate frame size @@ -305,14 +302,11 @@ OopMap* RegisterSaver::push_frame_reg_args_and_save_live_registers(MacroAssemble // Do the save_LR by hand and adjust the return pc if requested. switch (return_pc_location) { case return_pc_is_lr: __ mflr(R31); break; - case return_pc_is_pre_saved: assert(return_pc_adjustment == 0, "unsupported"); break; + case return_pc_is_pre_saved: break; case return_pc_is_thread_saved_exception_pc: __ ld(R31, thread_(saved_exception_pc)); break; default: ShouldNotReachHere(); } if (return_pc_location != return_pc_is_pre_saved) { - if (return_pc_adjustment != 0) { - __ addi(R31, R31, return_pc_adjustment); - } __ std(R31, frame_size_in_bytes + _abi0(lr), R1_SP); } @@ -2907,22 +2901,15 @@ void SharedRuntime::generate_deopt_blob() { // deopt_handler: call_deopt_stub // cur. return pc --> ... // - // So currently SR_LR points behind the call in the deopt handler. - // We adjust it such that it points to the start of the deopt handler. // The return_pc has been stored in the frame of the deoptee and // will replace the address of the deopt_handler in the call // to Deoptimization::fetch_unroll_info below. - // We can't grab a free register here, because all registers may - // contain live values, so let the RegisterSaver do the adjustment - // of the return pc. - const int return_pc_adjustment_no_exception = -MacroAssembler::bl64_patchable_size; // Push the "unpack frame" // Save everything in sight. map = RegisterSaver::push_frame_reg_args_and_save_live_registers(masm, &first_frame_size_in_bytes, /*generate_oop_map=*/ true, - return_pc_adjustment_no_exception, RegisterSaver::return_pc_is_lr); assert(map != nullptr, "OopMap must have been created"); @@ -2957,7 +2944,6 @@ void SharedRuntime::generate_deopt_blob() { RegisterSaver::push_frame_reg_args_and_save_live_registers(masm, &first_frame_size_in_bytes, /*generate_oop_map=*/ false, - /*return_pc_adjustment_exception=*/ 0, RegisterSaver::return_pc_is_pre_saved); // Deopt during an exception. Save exec mode for unpack_frames. @@ -2975,7 +2961,6 @@ void SharedRuntime::generate_deopt_blob() { RegisterSaver::push_frame_reg_args_and_save_live_registers(masm, &first_frame_size_in_bytes, /*generate_oop_map=*/ false, - /*return_pc_adjustment_reexecute=*/ 0, RegisterSaver::return_pc_is_pre_saved); __ li(exec_mode_reg, Deoptimization::Unpack_reexecute); #endif @@ -3266,7 +3251,6 @@ SafepointBlob* SharedRuntime::generate_handler_blob(StubId id, address call_ptr) map = RegisterSaver::push_frame_reg_args_and_save_live_registers(masm, &frame_size_in_bytes, /*generate_oop_map=*/ true, - /*return_pc_adjustment=*/0, return_pc_location, save_vectors); // The following is basically a call_VM. However, we need the precise @@ -3367,7 +3351,6 @@ RuntimeStub* SharedRuntime::generate_resolve_blob(StubId id, address destination map = RegisterSaver::push_frame_reg_args_and_save_live_registers(masm, &frame_size_in_bytes, /*generate_oop_map*/ true, - /*return_pc_adjustment*/ 0, RegisterSaver::return_pc_is_lr); // Use noreg as last_Java_pc, the return pc will be reconstructed diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp index 9d8ae770ccf..e77a2067e89 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp @@ -377,12 +377,20 @@ int LIR_Assembler::emit_deopt_handler() { int offset = code_offset(); - __ auipc(ra, 0); - __ far_jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack())); + Label start; + __ bind(start); + + __ far_call(RuntimeAddress(SharedRuntime::deopt_blob()->unpack())); + + int entry_offset = __ offset(); + __ j(start); + guarantee(code_offset() - offset <= deopt_handler_size(), "overflow"); + assert(code_offset() - entry_offset >= NativePostCallNop::first_check_size, + "out of bounds read in post-call NOP check"); __ end_a_stub(); - return offset; + return entry_offset; } void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) { diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp index e4efb2c171d..ed2ab0c4861 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp @@ -72,7 +72,7 @@ friend class ArrayCopyStub; // See emit_exception_handler for detail _exception_handler_size = DEBUG_ONLY(256) NOT_DEBUG(32), // or smaller // See emit_deopt_handler for detail - // auipc (1) + far_jump (2) + // far_call (2) + j (1) _deopt_handler_size = 1 * MacroAssembler::instruction_size + 2 * MacroAssembler::instruction_size }; diff --git a/src/hotspot/cpu/riscv/nativeInst_riscv.hpp b/src/hotspot/cpu/riscv/nativeInst_riscv.hpp index d990cfbc50d..b28e33759b2 100644 --- a/src/hotspot/cpu/riscv/nativeInst_riscv.hpp +++ b/src/hotspot/cpu/riscv/nativeInst_riscv.hpp @@ -311,12 +311,19 @@ inline bool NativeInstruction::is_jump_or_nop() { // can store an offset from the initial nop to the nmethod. class NativePostCallNop: public NativeInstruction { public: + enum RISCV_specific_constants { + // The two parts should be checked separately to prevent out of bounds access in + // case the return address points to the deopt handler stub code entry point + // which could be at the end of page. + first_check_size = instruction_size + }; + bool check() const { // Check for two instructions: nop; lui zr, hi20 // These instructions only ever appear together in a post-call // NOP, so it's unnecessary to check that the third instruction is // an addiw as well. - return is_nop() && MacroAssembler::is_lui_to_zr_at(addr_at(4)); + return is_nop() && MacroAssembler::is_lui_to_zr_at(addr_at(first_check_size)); } bool decode(int32_t& oopmap_slot, int32_t& cb_offset) const; bool patch(int32_t oopmap_slot, int32_t cb_offset); diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 7acbb5a478b..bb2ed57ef82 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -1049,15 +1049,10 @@ class HandlerImpl { public: - static int emit_exception_handler(C2_MacroAssembler *masm); static int emit_deopt_handler(C2_MacroAssembler* masm); - static uint size_exception_handler() { - return MacroAssembler::far_branch_size(); - } - static uint size_deopt_handler() { - // count auipc + far branch + // count far call + j return NativeInstruction::instruction_size + MacroAssembler::far_branch_size(); } }; @@ -1838,25 +1833,6 @@ uint MachUEPNode::size(PhaseRegAlloc* ra_) const //============================================================================= -// Emit exception handler code. -int HandlerImpl::emit_exception_handler(C2_MacroAssembler* masm) -{ - // auipc t1, #exception_blob_entry_point - // jr (offset)t1 - // Note that the code buffer's insts_mark is always relative to insts. - // That's why we must use the macroassembler to generate a handler. - address base = __ start_a_stub(size_exception_handler()); - if (base == nullptr) { - ciEnv::current()->record_failure("CodeCache is full"); - return 0; // CodeBuffer::expand failed - } - int offset = __ offset(); - __ far_jump(RuntimeAddress(OptoRuntime::exception_blob()->entry_point())); - assert(__ offset() - offset <= (int) size_exception_handler(), "overflow"); - __ end_a_stub(); - return offset; -} - // Emit deopt handler code. int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm) { @@ -1867,12 +1843,19 @@ int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm) } int offset = __ offset(); - __ auipc(ra, 0); - __ far_jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack())); + Label start; + __ bind(start); + + __ far_call(RuntimeAddress(SharedRuntime::deopt_blob()->unpack())); + + int entry_offset = __ offset(); + __ j(start); assert(__ offset() - offset <= (int) size_deopt_handler(), "overflow"); + assert(__ offset() - entry_offset >= NativePostCallNop::first_check_size, + "out of bounds read in post-call NOP check"); __ end_a_stub(); - return offset; + return entry_offset; } // REQUIRED MATCHER CODE diff --git a/src/hotspot/cpu/riscv/runtime_riscv.cpp b/src/hotspot/cpu/riscv/runtime_riscv.cpp index e1add8dbb82..c52d5a31066 100644 --- a/src/hotspot/cpu/riscv/runtime_riscv.cpp +++ b/src/hotspot/cpu/riscv/runtime_riscv.cpp @@ -249,8 +249,6 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() { //------------------------------generate_exception_blob--------------------------- // creates exception blob at the end -// Using exception blob, this code is jumped from a compiled method. -// (see emit_exception_handler in riscv.ad file) // // Given an exception pc at a call we call into the runtime for the // handler in this method. This handler might merely restore state diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.hpp b/src/hotspot/cpu/riscv/vm_version_riscv.hpp index 16f2e5d8f5b..168a3a576d0 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.hpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.hpp @@ -89,11 +89,12 @@ class VM_Version : public Abstract_VM_Version { FLAG_SET_DEFAULT(flag, true); \ } else { \ FLAG_SET_DEFAULT(flag, false); \ - stringStream ss; \ - deps_string(ss, dep0, ##__VA_ARGS__); \ - warning("Cannot enable " #flag ", it's missing dependent extension(s) %s", ss.as_string(true)); \ /* Sync CPU features with flags */ \ disable_feature(); \ + stringStream ss; \ + ss.print("missing dependent extension(s): "); \ + deps_string(ss, dep0, ##__VA_ARGS__); \ + log_disabled(ss.as_string(true)); \ } \ } else { \ /* Sync CPU features with flags */ \ @@ -101,11 +102,12 @@ class VM_Version : public Abstract_VM_Version { disable_feature(); \ } else if (!deps_all_enabled(dep0, ##__VA_ARGS__)) { \ FLAG_SET_DEFAULT(flag, false); \ - stringStream ss; \ - deps_string(ss, dep0, ##__VA_ARGS__); \ - warning("Cannot enable " #flag ", it's missing dependent extension(s) %s", ss.as_string(true)); \ /* Sync CPU features with flags */ \ disable_feature(); \ + stringStream ss; \ + ss.print("missing dependent extension(s): "); \ + deps_string(ss, dep0, ##__VA_ARGS__); \ + log_disabled(ss.as_string(true)); \ } \ } \ } \ @@ -136,6 +138,7 @@ class VM_Version : public Abstract_VM_Version { RVExtFeatures::current()->clear_feature(_cpu_feature_index); } void log_enabled(); + void log_disabled(const char* reason); protected: bool deps_all_enabled(RVExtFeatureValue* dep0, ...) { diff --git a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp index 298234156c3..93d6051aa76 100644 --- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp @@ -272,14 +272,27 @@ int LIR_Assembler::emit_deopt_handler() { // Not enough space left for the handler. bailout("deopt handler overflow"); return -1; - } int offset = code_offset(); + } + + int offset = code_offset(); + + Label start; + __ bind(start); + // Size must be constant (see HandlerImpl::emit_deopt_handler). __ load_const(Z_R1_scratch, SharedRuntime::deopt_blob()->unpack()); __ call(Z_R1_scratch); + + int entry_offset = __ offset(); + + __ z_bru(start); + guarantee(code_offset() - offset <= deopt_handler_size(), "overflow"); + assert(code_offset() - entry_offset >= NativePostCallNop::first_check_size, + "out of bounds read in post-call NOP check"); __ end_a_stub(); - return offset; + return entry_offset; } void LIR_Assembler::jobject2reg(jobject o, Register reg) { diff --git a/src/hotspot/cpu/s390/nativeInst_s390.hpp b/src/hotspot/cpu/s390/nativeInst_s390.hpp index 16400df3f26..9852bc410b1 100644 --- a/src/hotspot/cpu/s390/nativeInst_s390.hpp +++ b/src/hotspot/cpu/s390/nativeInst_s390.hpp @@ -649,6 +649,13 @@ class NativeGeneralJump: public NativeInstruction { class NativePostCallNop: public NativeInstruction { public: + enum z_specific_constants { + // Once the check is implemented, this has to specify number of bytes checked on the first + // read. If the check would read beyond size of the instruction at the deopt handler stub + // code entry point, then it has to happen in two stages - to prevent out of bounds access + // in case the return address points to the entry point which could be at the end of page. + first_check_size = 0 // check is unimplemented + }; bool check() const { Unimplemented(); return false; } bool decode(int32_t& oopmap_slot, int32_t& cb_offset) const { return false; } bool patch(int32_t oopmap_slot, int32_t cb_offset) { Unimplemented(); return false; } diff --git a/src/hotspot/cpu/s390/runtime_s390.cpp b/src/hotspot/cpu/s390/runtime_s390.cpp index 314c407af91..658fba069b4 100644 --- a/src/hotspot/cpu/s390/runtime_s390.cpp +++ b/src/hotspot/cpu/s390/runtime_s390.cpp @@ -43,8 +43,6 @@ //------------------------------generate_exception_blob--------------------------- // creates exception blob at the end -// Using exception blob, this code is jumped from a compiled method. -// (see emit_exception_handler in s390.ad file) // // Given an exception pc at a call we call into the runtime for the // handler in this method. This handler might merely restore state diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index cab3965ecfa..6fe051b55c7 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -1649,15 +1649,10 @@ source_hpp %{ // Header information of the source block. class HandlerImpl { public: - static int emit_exception_handler(C2_MacroAssembler *masm); static int emit_deopt_handler(C2_MacroAssembler* masm); - static uint size_exception_handler() { - return NativeJump::max_instruction_size(); - } - static uint size_deopt_handler() { - return NativeCall::max_instruction_size(); + return NativeCall::max_instruction_size() + MacroAssembler::jump_pcrelative_size(); } }; @@ -1672,43 +1667,6 @@ public: source %{ -// This exception handler code snippet is placed after the method's -// code. It is the return point if an exception occurred. it jumps to -// the exception blob. -// -// If the method gets deoptimized, the method and this code snippet -// get patched. -// -// 1) Trampoline code gets patched into the end of this exception -// handler. the trampoline code jumps to the deoptimization blob. -// -// 2) The return address in the method's code will get patched such -// that it jumps to the trampoline. -// -// 3) The handler will get patched such that it does not jump to the -// exception blob, but to an entry in the deoptimization blob being -// aware of the exception. -int HandlerImpl::emit_exception_handler(C2_MacroAssembler *masm) { - Register temp_reg = Z_R1; - - address base = __ start_a_stub(size_exception_handler()); - if (base == nullptr) { - ciEnv::current()->record_failure("CodeCache is full"); - return 0; // CodeBuffer::expand failed - } - - int offset = __ offset(); - // Use unconditional pc-relative jump with 32-bit range here. - __ load_const_optimized(temp_reg, (address)OptoRuntime::exception_blob()->content_begin()); - __ z_br(temp_reg); - - assert(__ offset() - offset <= (int) size_exception_handler(), "overflow"); - - __ end_a_stub(); - - return offset; -} - // Emit deopt handler code. int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm) { address base = __ start_a_stub(size_deopt_handler()); @@ -1720,14 +1678,24 @@ int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm) { int offset = __ offset(); + Label start; + __ bind(start); + // Size_deopt_handler() must be exact on zarch, so for simplicity // we do not use load_const_opt here. __ load_const(Z_R1, SharedRuntime::deopt_blob()->unpack()); __ call(Z_R1); + + int entry_offset = __ offset(); + + __ z_bru(start); + assert(__ offset() - offset == (int) size_deopt_handler(), "must be fixed size"); + assert(__ offset() - entry_offset >= NativePostCallNop::first_check_size, + "out of bounds read in post-call NOP check"); __ end_a_stub(); - return offset; + return entry_offset; } //============================================================================= diff --git a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp index a3605f649cc..5b6f7dcd984 100644 --- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp +++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp @@ -2544,14 +2544,10 @@ void SharedRuntime::generate_deopt_blob() { // Normal entry (non-exception case) // // We have been called from the deopt handler of the deoptee. - // Z_R14 points behind the call in the deopt handler. We adjust - // it such that it points to the start of the deopt handler. + // Z_R14 points to the entry point of the deopt handler. // The return_pc has been stored in the frame of the deoptee and // will replace the address of the deopt_handler in the call // to Deoptimization::fetch_unroll_info below. - // The (int) cast is necessary, because -((unsigned int)14) - // is an unsigned int. - __ add2reg(Z_R14, -(int)NativeCall::max_instruction_size()); const Register exec_mode_reg = Z_tmp_1; diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp index e3ba0ebb56a..cbc5c6988d4 100644 --- a/src/hotspot/cpu/x86/assembler_x86.cpp +++ b/src/hotspot/cpu/x86/assembler_x86.cpp @@ -3860,6 +3860,46 @@ void Assembler::evmovdquq(Address dst, KRegister mask, XMMRegister src, bool mer emit_operand(src, dst, 0); } +void Assembler::vmovsldup(XMMRegister dst, XMMRegister src, int vector_len) { + assert(vector_len == AVX_512bit ? VM_Version::supports_evex() : VM_Version::supports_avx(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int16(0x12, (0xC0 | encode)); +} + +void Assembler::vmovshdup(XMMRegister dst, XMMRegister src, int vector_len) { + assert(vector_len == AVX_512bit ? VM_Version::supports_evex() : VM_Version::supports_avx(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int16(0x16, (0xC0 | encode)); +} + +void Assembler::evmovsldup(XMMRegister dst, KRegister mask, XMMRegister src, bool merge, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(vector_len == AVX_512bit || VM_Version::supports_avx512vl(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.set_is_evex_instruction(); + if (merge) { + attributes.reset_is_clear_context(); + } + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int16(0x12, (0xC0 | encode)); +} + +void Assembler::evmovshdup(XMMRegister dst, KRegister mask, XMMRegister src, bool merge, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(vector_len == AVX_512bit || VM_Version::supports_avx512vl(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.set_is_evex_instruction(); + if (merge) { + attributes.reset_is_clear_context(); + } + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int16(0x16, (0xC0 | encode)); +} + // Uses zero extension on 64bit void Assembler::movl(Register dst, int32_t imm32) { diff --git a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp index c863191df4c..43471a88391 100644 --- a/src/hotspot/cpu/x86/assembler_x86.hpp +++ b/src/hotspot/cpu/x86/assembler_x86.hpp @@ -1664,6 +1664,11 @@ class Assembler : public AbstractAssembler { void evmovdqaq(XMMRegister dst, Address src, int vector_len); void evmovdqaq(XMMRegister dst, KRegister mask, Address src, bool merge, int vector_len); + void vmovsldup(XMMRegister dst, XMMRegister src, int vector_len); + void vmovshdup(XMMRegister dst, XMMRegister src, int vector_len); + void evmovsldup(XMMRegister dst, KRegister mask, XMMRegister src, bool merge, int vector_len); + void evmovshdup(XMMRegister dst, KRegister mask, XMMRegister src, bool merge, int vector_len); + // Move lower 64bit to high 64bit in 128bit register void movlhps(XMMRegister dst, XMMRegister src); diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp index edeb0baea0e..a2ea7af606d 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp @@ -450,14 +450,22 @@ int LIR_Assembler::emit_deopt_handler() { } int offset = code_offset(); - InternalAddress here(__ pc()); - __ pushptr(here.addr(), rscratch1); - __ jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack())); + Label start; + __ bind(start); + + __ call(RuntimeAddress(SharedRuntime::deopt_blob()->unpack())); + + int entry_offset = __ offset(); + + __ jmp(start); + guarantee(code_offset() - offset <= deopt_handler_size(), "overflow"); + assert(code_offset() - entry_offset >= NativePostCallNop::first_check_size, + "out of bounds read in post-call NOP check"); __ end_a_stub(); - return offset; + return entry_offset; } void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) { diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.hpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.hpp index 8524dc90276..33f7b063e77 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.hpp @@ -48,7 +48,7 @@ enum { _call_stub_size = 28, _exception_handler_size = DEBUG_ONLY(1*K) NOT_DEBUG(175), - _deopt_handler_size = 17 + _deopt_handler_size = 7 }; public: diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp index 4cecaa55345..695eea6ad03 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp @@ -1368,6 +1368,7 @@ class MacroAssembler: public Assembler { void vpcmpeqw(XMMRegister dst, XMMRegister nds, Address src, int vector_len); void vpcmpeqw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + using Assembler::evpcmpeqd; void evpcmpeqd(KRegister kdst, KRegister mask, XMMRegister nds, AddressLiteral src, int vector_len, Register rscratch = noreg); // Vector compares diff --git a/src/hotspot/cpu/x86/nativeInst_x86.hpp b/src/hotspot/cpu/x86/nativeInst_x86.hpp index 3e767006480..ec7fc3b154a 100644 --- a/src/hotspot/cpu/x86/nativeInst_x86.hpp +++ b/src/hotspot/cpu/x86/nativeInst_x86.hpp @@ -73,6 +73,7 @@ class NativeInstruction { s_char sbyte_at(int offset) const { return *(s_char*) addr_at(offset); } u_char ubyte_at(int offset) const { return *(u_char*) addr_at(offset); } + jshort short_at(int offset) const { return *(jshort*) addr_at(offset); } jint int_at(int offset) const { return *(jint*) addr_at(offset); } intptr_t ptr_at(int offset) const { return *(intptr_t*) addr_at(offset); } @@ -578,10 +579,15 @@ class NativePostCallNop: public NativeInstruction { instruction_code = 0x0f, instruction_size = 8, instruction_offset = 0, - displacement_offset = 4 + displacement_offset = 4, + + // The two parts should be checked separately to prevent out of bounds access in case + // the return address points to the deopt handler stub code entry point which could be + // at the end of page. + first_check_size = 2 }; - bool check() const { return int_at(0) == 0x841f0f; } + bool check() const { return short_at(0) == 0x1f0f && short_at(first_check_size) == 0x0084; } bool decode(int32_t& oopmap_slot, int32_t& cb_offset) const { int32_t data = int_at(displacement_offset); if (data == 0) { diff --git a/src/hotspot/cpu/x86/runtime_x86_64.cpp b/src/hotspot/cpu/x86/runtime_x86_64.cpp index 7b98cf4fad7..5bf65299a0c 100644 --- a/src/hotspot/cpu/x86/runtime_x86_64.cpp +++ b/src/hotspot/cpu/x86/runtime_x86_64.cpp @@ -242,8 +242,6 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() { //------------------------------generate_exception_blob--------------------------- // creates exception blob at the end -// Using exception blob, this code is jumped from a compiled method. -// (see emit_exception_handler in x86_64.ad file) // // Given an exception pc at a call we call into the runtime for the // handler in this method. This handler might merely restore state diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_dilithium.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_dilithium.cpp index 9555d60c8a4..b9590939468 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_dilithium.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_dilithium.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, Intel 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 @@ -30,8 +31,6 @@ #define __ _masm-> -#define xmm(i) as_XMMRegister(i) - #ifdef PRODUCT #define BLOCK_COMMENT(str) /* nothing */ #else @@ -40,15 +39,13 @@ #define BIND(label) bind(label); BLOCK_COMMENT(#label ":") -#define XMMBYTES 64 - // Constants // ATTRIBUTE_ALIGNED(64) static const uint32_t dilithiumAvx512Consts[] = { 58728449, // montQInvModR - 8380417, // dilithium_q - 2365951, // montRSquareModQ - 5373807 // Barrett addend for modular reduction + 8380417, // dilithium_q + 2365951, // montRSquareModQ + 5373807 // Barrett addend for modular reduction }; const int montQInvModRIdx = 0; @@ -60,393 +57,591 @@ static address dilithiumAvx512ConstsAddr(int offset) { return ((address) dilithiumAvx512Consts) + offset; } -const Register scratch = r10; -const XMMRegister montMulPerm = xmm28; -const XMMRegister montQInvModR = xmm30; -const XMMRegister dilithium_q = xmm31; +ATTRIBUTE_ALIGNED(64) static const uint32_t unshufflePerms[] = { + // Shuffle for the 128-bit element swap (uint64_t) + 0, 0, 1, 0, 8, 0, 9, 0, 4, 0, 5, 0, 12, 0, 13, 0, + 10, 0, 11, 0, 2, 0, 3, 0, 14, 0, 15, 0, 6, 0, 7, 0, + // Final shuffle for AlmostNtt + 0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23, + 24, 8, 25, 9, 26, 10, 27, 11, 28, 12, 29, 13, 30, 14, 31, 15, -ATTRIBUTE_ALIGNED(64) static const uint32_t dilithiumAvx512Perms[] = { - // collect montmul results into the destination register - 17, 1, 19, 3, 21, 5, 23, 7, 25, 9, 27, 11, 29, 13, 31, 15, - // ntt - // level 4 - 0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23, - 8, 9, 10, 11, 12, 13, 14, 15, 24, 25, 26, 27, 28, 29, 30, 31, - // level 5 - 0, 1, 2, 3, 16, 17, 18, 19, 8, 9, 10, 11, 24, 25, 26, 27, - 4, 5, 6, 7, 20, 21, 22, 23, 12, 13, 14, 15, 28, 29, 30, 31, - // level 6 - 0, 1, 16, 17, 4, 5, 20, 21, 8, 9, 24, 25, 12, 13, 28, 29, - 2, 3, 18, 19, 6, 7, 22, 23, 10, 11, 26, 27, 14, 15, 30, 31, - // level 7 - 0, 16, 2, 18, 4, 20, 6, 22, 8, 24, 10, 26, 12, 28, 14, 30, - 1, 17, 3, 19, 5, 21, 7, 23, 9, 25, 11, 27, 13, 29, 15, 31, - 0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23, - 8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31, - - // ntt inverse - // level 0 - 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, - 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, - // level 1 - 0, 16, 2, 18, 4, 20, 6, 22, 8, 24, 10, 26, 12, 28, 14, 30, - 1, 17, 3, 19, 5, 21, 7, 23, 9, 25, 11, 27, 13, 29, 15, 31, - // level 2 - 0, 1, 16, 17, 4, 5, 20, 21, 8, 9, 24, 25, 12, 13, 28, 29, - 2, 3, 18, 19, 6, 7, 22, 23, 10, 11, 26, 27, 14, 15, 30, 31, - // level 3 - 0, 1, 2, 3, 16, 17, 18, 19, 8, 9, 10, 11, 24, 25, 26, 27, - 4, 5, 6, 7, 20, 21, 22, 23, 12, 13, 14, 15, 28, 29, 30, 31, - // level 4 - 0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23, - 8, 9, 10, 11, 12, 13, 14, 15, 24, 25, 26, 27, 28, 29, 30, 31 + // Initial shuffle for AlmostInverseNtt + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, + 17, 19, 21, 23, 25, 27, 29, 31, 1, 3, 5, 7, 9, 11, 13, 15 }; -const int montMulPermsIdx = 0; -const int nttL4PermsIdx = 64; -const int nttL5PermsIdx = 192; -const int nttL6PermsIdx = 320; -const int nttL7PermsIdx = 448; -const int nttInvL0PermsIdx = 704; -const int nttInvL1PermsIdx = 832; -const int nttInvL2PermsIdx = 960; -const int nttInvL3PermsIdx = 1088; -const int nttInvL4PermsIdx = 1216; - -static address dilithiumAvx512PermsAddr() { - return (address) dilithiumAvx512Perms; +static address unshufflePermsAddr(int offset) { + return ((address) unshufflePerms) + offset*64; } -// We do Montgomery multiplications of two vectors of 16 ints each in 4 steps: -// 1. Do the multiplications of the corresponding even numbered slots into -// the odd numbered slots of a third register. -// 2. Swap the even and odd numbered slots of the original input registers. -// 3. Similar to step 1, but into a different output register. -// 4. Combine the outputs of step 1 and step 3 into the output of the Montgomery -// multiplication. -// (For levels 0-6 in the Ntt and levels 1-7 of the inverse Ntt we only swap the -// odd-even slots of the first multiplicand as in the second (zetas) the -// odd slots contain the same number as the corresponding even one.) -// The indexes of the registers to be multiplied -// are in inputRegs1[] and inputRegs[2]. -// The results go to the registers whose indexes are in outputRegs. -// scratchRegs should contain 12 different register indexes. -// The set in outputRegs should not overlap with the set of the middle four -// scratch registers. -// The sets in inputRegs1 and inputRegs2 cannot overlap with the set of the -// first eight scratch registers. -// In most of the cases, the odd and the corresponding even slices of the -// registers indexed by the numbers in inputRegs2 will contain the same number, -// this should be indicated by calling this function with -// input2NeedsShuffle=false . +// The following function swaps elements A<->B, C<->D, and so forth. +// input1[] is shuffled in place; shuffle of input2[] is copied to output2[]. +// Element size (in bits) is specified by size parameter. +// +-----+-----+-----+-----+----- +// | | A | | C | ... +// +-----+-----+-----+-----+----- +// +-----+-----+-----+-----+----- +// | B | | D | | ... +// +-----+-----+-----+-----+----- +// +// NOTE: size 0 and 1 are used for initial and final shuffles respectively of +// dilithiumAlmostInverseNtt and dilithiumAlmostNtt. For size 0 and 1, input1[] +// and input2[] are modified in-place (and output2 is used as a temporary) // -static void montMul64(int outputRegs[], int inputRegs1[], int inputRegs2[], - int scratchRegs[], bool input2NeedsShuffle, - MacroAssembler *_masm) { +// Using C++ lambdas for improved readability (to hide parameters that always repeat) +static auto whole_shuffle(Register scratch, KRegister mergeMask1, KRegister mergeMask2, + const XMMRegister unshuffle1, const XMMRegister unshuffle2, int vector_len, MacroAssembler *_masm) { - for (int i = 0; i < 4; i++) { - __ vpmuldq(xmm(scratchRegs[i]), xmm(inputRegs1[i]), xmm(inputRegs2[i]), - Assembler::AVX_512bit); - } - for (int i = 0; i < 4; i++) { - __ vpmulld(xmm(scratchRegs[i + 4]), xmm(scratchRegs[i]), montQInvModR, - Assembler::AVX_512bit); - } - for (int i = 0; i < 4; i++) { - __ vpmuldq(xmm(scratchRegs[i + 4]), xmm(scratchRegs[i + 4]), dilithium_q, - Assembler::AVX_512bit); - } - for (int i = 0; i < 4; i++) { - __ evpsubd(xmm(scratchRegs[i + 4]), k0, xmm(scratchRegs[i]), - xmm(scratchRegs[i + 4]), false, Assembler::AVX_512bit); + int regCnt = 4; + if (vector_len == Assembler::AVX_256bit) { + regCnt = 2; } - for (int i = 0; i < 4; i++) { - __ vpshufd(xmm(inputRegs1[i]), xmm(inputRegs1[i]), 0xB1, - Assembler::AVX_512bit); - if (input2NeedsShuffle) { - __ vpshufd(xmm(inputRegs2[i]), xmm(inputRegs2[i]), 0xB1, - Assembler::AVX_512bit); + return [=](const XMMRegister output2[], const XMMRegister input1[], + const XMMRegister input2[], int size) { + if (vector_len == Assembler::AVX_256bit) { + switch (size) { + case 128: + for (int i = 0; i < regCnt; i++) { + __ vperm2i128(output2[i], input1[i], input2[i], 0b110001); + } + for (int i = 0; i < regCnt; i++) { + __ vinserti128(input1[i], input1[i], input2[i], 1); + } + break; + case 64: + for (int i = 0; i < regCnt; i++) { + __ vshufpd(output2[i], input1[i], input2[i], 0b11111111, vector_len); + } + for (int i = 0; i < regCnt; i++) { + __ vshufpd(input1[i], input1[i], input2[i], 0b00000000, vector_len); + } + break; + case 32: + for (int i = 0; i < regCnt; i++) { + __ vmovshdup(output2[i], input1[i], vector_len); + } + for (int i = 0; i < regCnt; i++) { + __ vpblendd(output2[i], output2[i], input2[i], 0b10101010, vector_len); + } + for (int i = 0; i < regCnt; i++) { + __ vmovsldup(input2[i], input2[i], vector_len); + } + for (int i = 0; i < regCnt; i++) { + __ vpblendd(input1[i], input1[i], input2[i], 0b10101010, vector_len); + } + break; + // Special cases + case 1: // initial shuffle for dilithiumAlmostInverseNtt + // shuffle all even 32bit columns to input1, and odd to input2 + for (int i = 0; i < regCnt; i++) { + // 0b-3-1-3-1 + __ vshufps(output2[i], input1[i], input2[i], 0b11011101, vector_len); + } + for (int i = 0; i < regCnt; i++) { + // 0b-2-0-2-0 + __ vshufps(input1[i], input1[i], input2[i], 0b10001000, vector_len); + } + for (int i = 0; i < regCnt; i++) { + __ vpermq(input2[i], output2[i], 0b11011000, vector_len); + } + for (int i = 0; i < regCnt; i++) { + // 0b-3-1-2-0 + __ vpermq(input1[i], input1[i], 0b11011000, vector_len); + } + break; + case 0: // final unshuffle for dilithiumAlmostNtt + // reverse case 1: all even are in input1 and odd in input2, put back + for (int i = 0; i < regCnt; i++) { + __ vpunpckhdq(output2[i], input1[i], input2[i], vector_len); + } + for (int i = 0; i < regCnt; i++) { + __ vpunpckldq(input1[i], input1[i], input2[i], vector_len); + } + for (int i = 0; i < regCnt; i++) { + __ vperm2i128(input2[i], input1[i], output2[i], 0b110001); + } + for (int i = 0; i < regCnt; i++) { + __ vinserti128(input1[i], input1[i], output2[i], 1); + } + break; + default: + assert(false, "Don't call here"); + } + } else { + switch (size) { + case 256: + for (int i = 0; i < regCnt; i++) { + // 0b-3-2-3-2 + __ evshufi64x2(output2[i], input1[i], input2[i], 0b11101110, vector_len); + } + for (int i = 0; i < regCnt; i++) { + __ vinserti64x4(input1[i], input1[i], input2[i], 1); + } + break; + case 128: + for (int i = 0; i < regCnt; i++) { + __ vmovdqu(output2[i], input2[i], vector_len); + } + for (int i = 0; i < regCnt; i++) { + __ evpermt2q(output2[i], unshuffle2, input1[i], vector_len); + } + for (int i = 0; i < regCnt; i++) { + __ evpermt2q(input1[i], unshuffle1, input2[i], vector_len); + } + + break; + case 64: + for (int i = 0; i < regCnt; i++) { + __ vshufpd(output2[i], input1[i], input2[i], 0b11111111, vector_len); + } + for (int i = 0; i < regCnt; i++) { + __ vshufpd(input1[i], input1[i], input2[i], 0b00000000, vector_len); + } + break; + case 32: + for (int i = 0; i < regCnt; i++) { + __ vmovdqu(output2[i], input2[i], vector_len); + } + for (int i = 0; i < regCnt; i++) { + __ evmovshdup(output2[i], mergeMask2, input1[i], true, vector_len); + } + for (int i = 0; i < regCnt; i++) { + __ evmovsldup(input1[i], mergeMask1, input2[i], true, vector_len); + } + break; + // Special cases + case 1: // initial shuffle for dilithiumAlmostInverseNtt + // shuffle all even 32bit columns to input1, and odd to input2 + for (int i = 0; i < regCnt; i++) { + __ vmovdqu(output2[i], input2[i], vector_len); + } + for (int i = 0; i < regCnt; i++) { + __ evpermt2d(input2[i], unshuffle2, input1[i], vector_len); + } + for (int i = 0; i < regCnt; i++) { + __ evpermt2d(input1[i], unshuffle1, output2[i], vector_len); + } + break; + case 0: // final unshuffle for dilithiumAlmostNtt + // reverse case 1: all even are in input1 and odd in input2, put back + for (int i = 0; i < regCnt; i++) { + __ vmovdqu(output2[i], input2[i], vector_len); + } + for (int i = 0; i < regCnt; i++) { + __ evpermt2d(input2[i], unshuffle2, input1[i], vector_len); + } + for (int i = 0; i < regCnt; i++) { + __ evpermt2d(input1[i], unshuffle1, output2[i], vector_len); + } + break; + default: + assert(false, "Don't call here"); + } } - } + }; // return +} - for (int i = 0; i < 4; i++) { - __ vpmuldq(xmm(scratchRegs[i]), xmm(inputRegs1[i]), xmm(inputRegs2[i]), - Assembler::AVX_512bit); - } - for (int i = 0; i < 4; i++) { - __ vpmulld(xmm(scratchRegs[i + 8]), xmm(scratchRegs[i]), montQInvModR, - Assembler::AVX_512bit); - } - for (int i = 0; i < 4; i++) { - __ vpmuldq(xmm(scratchRegs[i + 8]), xmm(scratchRegs[i + 8]), dilithium_q, - Assembler::AVX_512bit); - } - for (int i = 0; i < 4; i++) { - __ evpsubd(xmm(outputRegs[i]), k0, xmm(scratchRegs[i]), - xmm(scratchRegs[i + 8]), false, Assembler::AVX_512bit); +// We do Montgomery multiplications of two AVX registers in 4 steps: +// 1. Do the multiplications of the corresponding even numbered slots into +// the odd numbered slots of the scratch2 register. +// 2. Swap the even and odd numbered slots of the original input registers.(*Note) +// 3. Similar to step 1, but multiplication result is placed into output register. +// 4. Combine odd/even slots respectively from the scratch2 and output registers +// into the output register for the final result of the Montgomery multiplication. +// (*Note: For levels 0-6 in the Ntt and levels 1-7 of the inverse Ntt, need NOT +// swap the second operand (zetas) since the odd slots contain the same number +// as the corresponding even one. This is indicated by input2NeedsShuffle=false) +// +// The registers to be multiplied are in input1[] and inputs2[]. The results go +// into output[]. Two scratch[] register arrays are expected. input1[] can +// overlap with either output[] or scratch1[] +// - If AVX512, all register arrays are of length 4 +// - If AVX2, first two registers of each array are in xmm0-xmm15 range +// Constants montQInvModR, dilithium_q and mergeMask expected to have already +// been loaded. +// +// Using C++ lambdas for improved readability (to hide parameters that always repeat) +static auto whole_montMul(XMMRegister montQInvModR, XMMRegister dilithium_q, + KRegister mergeMask, int vector_len, MacroAssembler *_masm) { + int regCnt = 4; + int regSize = 64; + if (vector_len == Assembler::AVX_256bit) { + regCnt = 2; + regSize = 32; } - for (int i = 0; i < 4; i++) { - __ evpermt2d(xmm(outputRegs[i]), montMulPerm, xmm(scratchRegs[i + 4]), - Assembler::AVX_512bit); - } -} + return [=](const XMMRegister output[], const XMMRegister input1[], + const XMMRegister input2[], const XMMRegister scratch1[], + const XMMRegister scratch2[], bool input2NeedsShuffle = false) { + // (Register overloading) Can't always use scratch1 (could override input1). + // If so, use output: + const XMMRegister* scratch = scratch1 == input1 ? output: scratch1; + + // scratch = input1_even * intput2_even + for (int i = 0; i < regCnt; i++) { + __ vpmuldq(scratch[i], input1[i], input2[i], vector_len); + } + + // scratch2_low = scratch_low * montQInvModR + for (int i = 0; i < regCnt; i++) { + __ vpmuldq(scratch2[i], scratch[i], montQInvModR, vector_len); + } + + // scratch2 = scratch2_low * dilithium_q + for (int i = 0; i < regCnt; i++) { + __ vpmuldq(scratch2[i], scratch2[i], dilithium_q, vector_len); + } + + // scratch2_high = scratch2_high - scratch_high + for (int i = 0; i < regCnt; i++) { + __ vpsubd(scratch2[i], scratch[i], scratch2[i], vector_len); + } + + // input1_even = input1_odd + // input2_even = input2_odd + for (int i = 0; i < regCnt; i++) { + __ vpshufd(input1[i], input1[i], 0xB1, vector_len); + if (input2NeedsShuffle) { + __ vpshufd(input2[i], input2[i], 0xB1, vector_len); + } + } + + // scratch1 = input1_even*intput2_even + for (int i = 0; i < regCnt; i++) { + __ vpmuldq(scratch1[i], input1[i], input2[i], vector_len); + } -static void montMul64(int outputRegs[], int inputRegs1[], int inputRegs2[], - int scratchRegs[], MacroAssembler *_masm) { - montMul64(outputRegs, inputRegs1, inputRegs2, scratchRegs, false, _masm); + // output = scratch1_low * montQInvModR + for (int i = 0; i < regCnt; i++) { + __ vpmuldq(output[i], scratch1[i], montQInvModR, vector_len); + } + + // output = output * dilithium_q + for (int i = 0; i < regCnt; i++) { + __ vpmuldq(output[i], output[i], dilithium_q, vector_len); + } + + // output_high = scratch1_high - output_high + for (int i = 0; i < regCnt; i++) { + __ vpsubd(output[i], scratch1[i], output[i], vector_len); + } + + // output = select(output_high, scratch2_high) + if (vector_len == Assembler::AVX_256bit) { + for (int i = 0; i < regCnt; i++) { + __ vmovshdup(scratch2[i], scratch2[i], vector_len); + } + for (int i = 0; i < regCnt; i++) { + __ vpblendd(output[i], output[i], scratch2[i], 0b01010101, vector_len); + } + } else { + for (int i = 0; i < regCnt; i++) { + __ evmovshdup(output[i], mergeMask, scratch2[i], true, vector_len); + } + } + }; // return } -static void sub_add(int subResult[], int addResult[], - int input1[], int input2[], MacroAssembler *_masm) { +static void sub_add(const XMMRegister subResult[], const XMMRegister addResult[], + const XMMRegister input1[], const XMMRegister input2[], + int vector_len, MacroAssembler *_masm) { + int regCnt = 4; + if (vector_len == Assembler::AVX_256bit) { + regCnt = 2; + } - for (int i = 0; i < 4; i++) { - __ evpsubd(xmm(subResult[i]), k0, xmm(input1[i]), xmm(input2[i]), false, - Assembler::AVX_512bit); + for (int i = 0; i < regCnt; i++) { + __ vpsubd(subResult[i], input1[i], input2[i], vector_len); } - for (int i = 0; i < 4; i++) { - __ evpaddd(xmm(addResult[i]), k0, xmm(input1[i]), xmm(input2[i]), false, - Assembler::AVX_512bit); + for (int i = 0; i < regCnt; i++) { + __ vpaddd(addResult[i], input1[i], input2[i], vector_len); } } -static void loadPerm(int destinationRegs[], Register perms, - int offset, MacroAssembler *_masm) { - __ evmovdqul(xmm(destinationRegs[0]), Address(perms, offset), - Assembler::AVX_512bit); - for (int i = 1; i < 4; i++) { - __ evmovdqul(xmm(destinationRegs[i]), xmm(destinationRegs[0]), - Assembler::AVX_512bit); - } -} +static void loadXmms(const XMMRegister destinationRegs[], Register source, int offset, + int vector_len, MacroAssembler *_masm, int regCnt = -1, int memStep = -1) { -static void load4Xmms(int destinationRegs[], Register source, int offset, - MacroAssembler *_masm) { - for (int i = 0; i < 4; i++) { - __ evmovdqul(xmm(destinationRegs[i]), Address(source, offset + i * XMMBYTES), - Assembler::AVX_512bit); + if (vector_len == Assembler::AVX_256bit) { + regCnt = regCnt == -1 ? 2 : regCnt; + memStep = memStep == -1 ? 32 : memStep; + } else { + regCnt = 4; + memStep = 64; } -} -static void loadXmm29(Register source, int offset, MacroAssembler *_masm) { - __ evmovdqul(xmm29, Address(source, offset), Assembler::AVX_512bit); + for (int i = 0; i < regCnt; i++) { + __ vmovdqu(destinationRegs[i], Address(source, offset + i * memStep), vector_len); + } } -static void store4Xmms(Register destination, int offset, int xmmRegs[], - MacroAssembler *_masm) { - for (int i = 0; i < 4; i++) { - __ evmovdqul(Address(destination, offset + i * XMMBYTES), xmm(xmmRegs[i]), - Assembler::AVX_512bit); +static void storeXmms(Register destination, int offset, const XMMRegister xmmRegs[], + int vector_len, MacroAssembler *_masm, int regCnt = -1, int memStep = -1) { + if (vector_len == Assembler::AVX_256bit) { + regCnt = regCnt == -1 ? 2 : regCnt; + memStep = memStep == -1 ? 32 : memStep; + } else { + regCnt = 4; + memStep = 64; } -} -static int xmm0_3[] = {0, 1, 2, 3}; -static int xmm0145[] = {0, 1, 4, 5}; -static int xmm0246[] = {0, 2, 4, 6}; -static int xmm0426[] = {0, 4, 2, 6}; -static int xmm1357[] = {1, 3, 5, 7}; -static int xmm1537[] = {1, 5, 3, 7}; -static int xmm2367[] = {2, 3, 6, 7}; -static int xmm4_7[] = {4, 5, 6, 7}; -static int xmm8_11[] = {8, 9, 10, 11}; -static int xmm12_15[] = {12, 13, 14, 15}; -static int xmm16_19[] = {16, 17, 18, 19}; -static int xmm20_23[] = {20, 21, 22, 23}; -static int xmm20222426[] = {20, 22, 24, 26}; -static int xmm21232527[] = {21, 23, 25, 27}; -static int xmm24_27[] = {24, 25, 26, 27}; -static int xmm4_20_24[] = {4, 5, 6, 7, 20, 21, 22, 23, 24, 25, 26, 27}; -static int xmm16_27[] = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}; -static int xmm29_29[] = {29, 29, 29, 29}; + for (int i = 0; i < regCnt; i++) { + __ vmovdqu(Address(destination, offset + i * memStep), xmmRegs[i], vector_len); + } +} // Dilithium NTT function except for the final "normalization" to |coeff| < Q. // Implements // static int implDilithiumAlmostNtt(int[] coeffs, int zetas[]) {} // // coeffs (int[256]) = c_rarg0 -// zetas (int[256]) = c_rarg1 -// +// zetas (int[128*8]) = c_rarg1 // -static address generate_dilithiumAlmostNtt_avx512(StubGenerator *stubgen, - MacroAssembler *_masm) { - +static address generate_dilithiumAlmostNtt_avx(StubGenerator *stubgen, + int vector_len, MacroAssembler *_masm) { __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumAlmostNtt_id; StubCodeMark mark(stubgen, stub_id); address start = __ pc(); __ enter(); - Label L_loop, L_end; - const Register coeffs = c_rarg0; const Register zetas = c_rarg1; - const Register iterations = c_rarg2; - - const Register perms = r11; - - __ lea(perms, ExternalAddress(dilithiumAvx512PermsAddr())); - - __ evmovdqul(montMulPerm, Address(perms, montMulPermsIdx), Assembler::AVX_512bit); + const Register scratch = r10; // Each level represents one iteration of the outer for loop of the Java version // In each of these iterations half of the coefficients are (Montgomery) // multiplied by a zeta corresponding to the coefficient and then these // products will be added to and subtracted from the other half of the - // coefficients. In each level we just collect the coefficients (using - // evpermi2d() instructions where necessary, i.e. in levels 4-7) that need to + // coefficients. In each level we just shuffle the coefficients that need to // be multiplied by the zetas in one set, the rest to another set of vector // registers, then redistribute the addition/substraction results. // For levels 0 and 1 the zetas are not different within the 4 xmm registers - // that we would use for them, so we use only one, xmm29. - loadXmm29(zetas, 0, _masm); + // that we would use for them, so we use only one register. + + // AVX2 version uses the first half of these arrays + const XMMRegister Coeffs1[] = {xmm0, xmm1, xmm16, xmm17}; + const XMMRegister Coeffs2[] = {xmm2, xmm3, xmm18, xmm19}; + const XMMRegister Coeffs3[] = {xmm4, xmm5, xmm20, xmm21}; + const XMMRegister Coeffs4[] = {xmm6, xmm7, xmm22, xmm23}; + const XMMRegister Scratch1[] = {xmm8, xmm9, xmm24, xmm25}; + const XMMRegister Scratch2[] = {xmm10, xmm11, xmm26, xmm27}; + const XMMRegister Zetas1[] = {xmm12, xmm12, xmm12, xmm12}; + const XMMRegister Zetas2[] = {xmm12, xmm12, xmm13, xmm13}; + const XMMRegister Zetas3[] = {xmm12, xmm13, xmm28, xmm29}; + const XMMRegister montQInvModR = xmm14; + const XMMRegister dilithium_q = xmm15; + const XMMRegister unshuffle1 = xmm30; + const XMMRegister unshuffle2 = xmm31; + KRegister mergeMask1 = k1; + KRegister mergeMask2 = k2; + // lambdas to hide repeated parameters + auto shuffle = whole_shuffle(scratch, mergeMask1, mergeMask2, unshuffle1, unshuffle2, vector_len, _masm); + auto montMul64 = whole_montMul(montQInvModR, dilithium_q, mergeMask2, vector_len, _masm); + __ vpbroadcastd(montQInvModR, ExternalAddress(dilithiumAvx512ConstsAddr(montQInvModRIdx)), - Assembler::AVX_512bit, scratch); // q^-1 mod 2^32 + vector_len, scratch); // q^-1 mod 2^32 __ vpbroadcastd(dilithium_q, ExternalAddress(dilithiumAvx512ConstsAddr(dilithium_qIdx)), - Assembler::AVX_512bit, scratch); // q - - // load all coefficients into the vector registers Zmm_0-Zmm_15, - // 16 coefficients into each - load4Xmms(xmm0_3, coeffs, 0, _masm); - load4Xmms(xmm4_7, coeffs, 4 * XMMBYTES, _masm); - load4Xmms(xmm8_11, coeffs, 8 * XMMBYTES, _masm); - load4Xmms(xmm12_15, coeffs, 12 * XMMBYTES, _masm); - - // level 0 and 1 can be done entirely in registers as the zetas on these - // levels are the same for all the montmuls that we can do in parallel - - // level 0 - montMul64(xmm16_19, xmm8_11, xmm29_29, xmm16_27, _masm); - sub_add(xmm8_11, xmm0_3, xmm0_3, xmm16_19, _masm); - montMul64(xmm16_19, xmm12_15, xmm29_29, xmm16_27, _masm); - loadXmm29(zetas, 512, _masm); // for level 1 - sub_add(xmm12_15, xmm4_7, xmm4_7, xmm16_19, _masm); - - // level 1 - - montMul64(xmm16_19, xmm4_7, xmm29_29, xmm16_27, _masm); - loadXmm29(zetas, 768, _masm); - sub_add(xmm4_7, xmm0_3, xmm0_3, xmm16_19, _masm); - montMul64(xmm16_19, xmm12_15, xmm29_29, xmm16_27, _masm); - sub_add(xmm12_15, xmm8_11, xmm8_11, xmm16_19, _masm); - - // levels 2 to 7 are done in 2 batches, by first saving half of the coefficients - // from level 1 into memory, doing all the level 2 to level 7 computations - // on the remaining half in the vector registers, saving the result to - // memory after level 7, then loading back the coefficients that we saved after - // level 1 and do the same computation with those - - store4Xmms(coeffs, 8 * XMMBYTES, xmm8_11, _masm); - store4Xmms(coeffs, 12 * XMMBYTES, xmm12_15, _masm); - - __ movl(iterations, 2); - - __ align(OptoLoopAlignment); - __ BIND(L_loop); - - __ subl(iterations, 1); - - // level 2 - load4Xmms(xmm12_15, zetas, 2 * 512, _masm); - montMul64(xmm16_19, xmm2367, xmm12_15, xmm16_27, _masm); - load4Xmms(xmm12_15, zetas, 3 * 512, _masm); // for level 3 - sub_add(xmm2367, xmm0145, xmm0145, xmm16_19, _masm); - - // level 3 - - montMul64(xmm16_19, xmm1357, xmm12_15, xmm16_27, _masm); - sub_add(xmm1357, xmm0246, xmm0246, xmm16_19, _masm); - - // level 4 - loadPerm(xmm16_19, perms, nttL4PermsIdx, _masm); - loadPerm(xmm12_15, perms, nttL4PermsIdx + 64, _masm); - load4Xmms(xmm24_27, zetas, 4 * 512, _masm); - - for (int i = 0; i < 8; i += 2) { - __ evpermi2d(xmm(i/2 + 16), xmm(i), xmm(i + 1), Assembler::AVX_512bit); - } - for (int i = 0; i < 8; i += 2) { - __ evpermi2d(xmm(i / 2 + 12), xmm(i), xmm(i + 1), Assembler::AVX_512bit); - } - - montMul64(xmm12_15, xmm12_15, xmm24_27, xmm4_20_24, _masm); - sub_add(xmm1357, xmm0246, xmm16_19, xmm12_15, _masm); - - // level 5 - loadPerm(xmm16_19, perms, nttL5PermsIdx, _masm); - loadPerm(xmm12_15, perms, nttL5PermsIdx + 64, _masm); - load4Xmms(xmm24_27, zetas, 5 * 512, _masm); - - for (int i = 0; i < 8; i += 2) { - __ evpermi2d(xmm(i/2 + 16), xmm(i), xmm(i + 1), Assembler::AVX_512bit); - } - for (int i = 0; i < 8; i += 2) { - __ evpermi2d(xmm(i / 2 + 12), xmm(i), xmm(i + 1), Assembler::AVX_512bit); - } - - montMul64(xmm12_15, xmm12_15, xmm24_27, xmm4_20_24, _masm); - sub_add(xmm1357, xmm0246, xmm16_19, xmm12_15, _masm); - - // level 6 - loadPerm(xmm16_19, perms, nttL6PermsIdx, _masm); - loadPerm(xmm12_15, perms, nttL6PermsIdx + 64, _masm); - load4Xmms(xmm24_27, zetas, 6 * 512, _masm); - - for (int i = 0; i < 8; i += 2) { - __ evpermi2d(xmm(i/2 + 16), xmm(i), xmm(i + 1), Assembler::AVX_512bit); - } - for (int i = 0; i < 8; i += 2) { - __ evpermi2d(xmm(i / 2 + 12), xmm(i), xmm(i + 1), Assembler::AVX_512bit); - } - - montMul64(xmm12_15, xmm12_15, xmm24_27, xmm4_20_24, _masm); - sub_add(xmm1357, xmm0246, xmm16_19, xmm12_15, _masm); - - // level 7 - loadPerm(xmm16_19, perms, nttL7PermsIdx, _masm); - loadPerm(xmm12_15, perms, nttL7PermsIdx + 64, _masm); - load4Xmms(xmm24_27, zetas, 7 * 512, _masm); - - for (int i = 0; i < 8; i += 2) { - __ evpermi2d(xmm(i / 2 + 16), xmm(i), xmm(i + 1), Assembler::AVX_512bit); - } - for (int i = 0; i < 8; i += 2) { - __ evpermi2d(xmm(i / 2 + 12), xmm(i), xmm(i + 1), Assembler::AVX_512bit); - } + vector_len, scratch); // q + + if (vector_len == Assembler::AVX_512bit) { + // levels 0-3, register shuffles: + const XMMRegister Coeffs1_1[] = {xmm0, xmm1, xmm2, xmm3}; + const XMMRegister Coeffs2_1[] = {xmm16, xmm17, xmm18, xmm19}; + const XMMRegister Coeffs3_1[] = {xmm4, xmm5, xmm6, xmm7}; + const XMMRegister Coeffs4_1[] = {xmm20, xmm21, xmm22, xmm23}; + const XMMRegister Coeffs1_2[] = {xmm0, xmm16, xmm2, xmm18}; + const XMMRegister Coeffs2_2[] = {xmm1, xmm17, xmm3, xmm19}; + const XMMRegister Coeffs3_2[] = {xmm4, xmm20, xmm6, xmm22}; + const XMMRegister Coeffs4_2[] = {xmm5, xmm21, xmm7, xmm23}; + + // Constants for shuffle and montMul64 + __ mov64(scratch, 0b1010101010101010); + __ kmovwl(mergeMask1, scratch); + __ knotwl(mergeMask2, mergeMask1); + __ vmovdqu(unshuffle1, ExternalAddress(unshufflePermsAddr(0)), vector_len, scratch); + __ vmovdqu(unshuffle2, ExternalAddress(unshufflePermsAddr(1)), vector_len, scratch); + + int memStep = 4 * 64; // 4*64-byte registers + loadXmms(Coeffs1, coeffs, 0*memStep, vector_len, _masm); + loadXmms(Coeffs2, coeffs, 1*memStep, vector_len, _masm); + loadXmms(Coeffs3, coeffs, 2*memStep, vector_len, _masm); + loadXmms(Coeffs4, coeffs, 3*memStep, vector_len, _masm); + + // level 0-3 can be done by shuffling registers (also notice fewer zetas loads, they repeat) + // level 0 - 128 + // scratch1 = coeffs3 * zetas1 + // coeffs3, coeffs1 = coeffs1 ± scratch1 + // scratch1 = coeffs4 * zetas1 + // coeffs4, coeffs2 = coeffs2 ± scratch1 + __ vmovdqu(Zetas1[0], Address(zetas, 0), vector_len); + montMul64(Scratch1, Coeffs3, Zetas1, Coeffs3, Scratch2); + sub_add(Coeffs3, Coeffs1, Coeffs1, Scratch1, vector_len, _masm); + montMul64(Scratch1, Coeffs4, Zetas1, Coeffs4, Scratch2); + sub_add(Coeffs4, Coeffs2, Coeffs2, Scratch1, vector_len, _masm); + + // level 1 - 64 + __ vmovdqu(Zetas1[0], Address(zetas, 512), vector_len); + montMul64(Scratch1, Coeffs2, Zetas1, Coeffs2, Scratch2); + sub_add(Coeffs2, Coeffs1, Coeffs1, Scratch1, vector_len, _masm); + + __ vmovdqu(Zetas1[0], Address(zetas, 4*64 + 512), vector_len); + montMul64(Scratch1, Coeffs4, Zetas1, Coeffs4, Scratch2); + sub_add(Coeffs4, Coeffs3, Coeffs3, Scratch1, vector_len, _masm); + + // level 2 - 32 + __ vmovdqu(Zetas2[0], Address(zetas, 2 * 512), vector_len); + __ vmovdqu(Zetas2[2], Address(zetas, 2*64 + 2 * 512), vector_len); + montMul64(Scratch1, Coeffs2_1, Zetas2, Coeffs2_1, Scratch2); + sub_add(Coeffs2_1, Coeffs1_1, Coeffs1_1, Scratch1, vector_len, _masm); + + __ vmovdqu(Zetas2[0], Address(zetas, 4*64 + 2 * 512), vector_len); + __ vmovdqu(Zetas2[2], Address(zetas, 6*64 + 2 * 512), vector_len); + montMul64(Scratch1, Coeffs4_1, Zetas2, Coeffs4_1, Scratch2); + sub_add(Coeffs4_1, Coeffs3_1, Coeffs3_1, Scratch1, vector_len, _masm); + + // level 3 - 16 + loadXmms(Zetas3, zetas, 3 * 512, vector_len, _masm); + montMul64(Scratch1, Coeffs2_2, Zetas3, Coeffs2_2, Scratch2); + sub_add(Coeffs2_2, Coeffs1_2, Coeffs1_2, Scratch1, vector_len, _masm); + + loadXmms(Zetas3, zetas, 4*64 + 3 * 512, vector_len, _masm); + montMul64(Scratch1, Coeffs4_2, Zetas3, Coeffs4_2, Scratch2); + sub_add(Coeffs4_2, Coeffs3_2, Coeffs3_2, Scratch1, vector_len, _masm); + + for (int level = 4, distance = 8; level<8; level++, distance /= 2) { + // zetas = load(level * 512) + // coeffs1_2, scratch1 = shuffle(coeffs1_2, coeffs2_2) + // scratch1 = scratch1 * zetas + // coeffs2_2 = coeffs1_2 - scratch1 + // coeffs1_2 = coeffs1_2 + scratch1 + loadXmms(Zetas3, zetas, level * 512, vector_len, _masm); + shuffle(Scratch1, Coeffs1_2, Coeffs2_2, distance * 32); // Coeffs2_2 freed + montMul64(Scratch1, Scratch1, Zetas3, Coeffs2_2, Scratch2, level==7); + sub_add(Coeffs2_2, Coeffs1_2, Coeffs1_2, Scratch1, vector_len, _masm); + + loadXmms(Zetas3, zetas, 4*64 + level * 512, vector_len, _masm); + shuffle(Scratch1, Coeffs3_2, Coeffs4_2, distance * 32); // Coeffs4_2 freed + montMul64(Scratch1, Scratch1, Zetas3, Coeffs4_2, Scratch2, level==7); + sub_add(Coeffs4_2, Coeffs3_2, Coeffs3_2, Scratch1, vector_len, _masm); + } - montMul64(xmm12_15, xmm12_15, xmm24_27, xmm4_20_24, true, _masm); - loadPerm(xmm0246, perms, nttL7PermsIdx + 2 * XMMBYTES, _masm); - loadPerm(xmm1357, perms, nttL7PermsIdx + 3 * XMMBYTES, _masm); - sub_add(xmm21232527, xmm20222426, xmm16_19, xmm12_15, _masm); + // Constants for final unshuffle + __ vmovdqu(unshuffle1, ExternalAddress(unshufflePermsAddr(2)), vector_len, scratch); + __ vmovdqu(unshuffle2, ExternalAddress(unshufflePermsAddr(3)), vector_len, scratch); + shuffle(Scratch1, Coeffs1_2, Coeffs2_2, 0); + shuffle(Scratch1, Coeffs3_2, Coeffs4_2, 0); + + storeXmms(coeffs, 0*memStep, Coeffs1, vector_len, _masm); + storeXmms(coeffs, 1*memStep, Coeffs2, vector_len, _masm); + storeXmms(coeffs, 2*memStep, Coeffs3, vector_len, _masm); + storeXmms(coeffs, 3*memStep, Coeffs4, vector_len, _masm); + } else { // Assembler::AVX_256bit + // levels 0-4, register shuffles: + const XMMRegister Coeffs1_1[] = {xmm0, xmm2}; + const XMMRegister Coeffs2_1[] = {xmm1, xmm3}; + const XMMRegister Coeffs3_1[] = {xmm4, xmm6}; + const XMMRegister Coeffs4_1[] = {xmm5, xmm7}; + + const XMMRegister Coeffs1_2[] = {xmm0, xmm1, xmm2, xmm3}; + const XMMRegister Coeffs2_2[] = {xmm4, xmm5, xmm6, xmm7}; + + // Since we cannot fit the entire payload into registers, we process the + // input in two stages. For the first half, load 8 registers, each 32 integers + // apart. With one load, we can process level 0-2 (128-, 64- and 32-integers + // apart). For the remaining levels, load 8 registers from consecutive memory + // (16-, 8-, 4-, 2-, 1-integer apart) + // Levels 5, 6, 7 (4-, 2-, 1-integer apart) require shuffles within registers. + // On the other levels, shuffles can be done by rearranging the register order + + // Four batches of 8 registers each, 128 bytes apart + for (int i=0; i<4; i++) { + loadXmms(Coeffs1_2, coeffs, i*32 + 0*128, vector_len, _masm, 4, 128); + loadXmms(Coeffs2_2, coeffs, i*32 + 4*128, vector_len, _masm, 4, 128); + + // level 0-2 can be done by shuffling registers (also notice fewer zetas loads, they repeat) + // level 0 - 128 + __ vmovdqu(Zetas1[0], Address(zetas, 0), vector_len); + montMul64(Scratch1, Coeffs3, Zetas1, Coeffs3, Scratch2); + sub_add(Coeffs3, Coeffs1, Coeffs1, Scratch1, vector_len, _masm); + montMul64(Scratch1, Coeffs4, Zetas1, Coeffs4, Scratch2); + sub_add(Coeffs4, Coeffs2, Coeffs2, Scratch1, vector_len, _masm); + + // level 1 - 64 + __ vmovdqu(Zetas1[0], Address(zetas, 512), vector_len); + montMul64(Scratch1, Coeffs2, Zetas1, Coeffs2, Scratch2); + sub_add(Coeffs2, Coeffs1, Coeffs1, Scratch1, vector_len, _masm); + + __ vmovdqu(Zetas1[0], Address(zetas, 4*64 + 512), vector_len); + montMul64(Scratch1, Coeffs4, Zetas1, Coeffs4, Scratch2); + sub_add(Coeffs4, Coeffs3, Coeffs3, Scratch1, vector_len, _masm); + + // level 2 - 32 + loadXmms(Zetas3, zetas, 2 * 512, vector_len, _masm, 2, 128); + montMul64(Scratch1, Coeffs2_1, Zetas3, Coeffs2_1, Scratch2); + sub_add(Coeffs2_1, Coeffs1_1, Coeffs1_1, Scratch1, vector_len, _masm); + + loadXmms(Zetas3, zetas, 4*64 + 2 * 512, vector_len, _masm, 2, 128); + montMul64(Scratch1, Coeffs4_1, Zetas3, Coeffs4_1, Scratch2); + sub_add(Coeffs4_1, Coeffs3_1, Coeffs3_1, Scratch1, vector_len, _masm); + + storeXmms(coeffs, i*32 + 0*128, Coeffs1_2, vector_len, _masm, 4, 128); + storeXmms(coeffs, i*32 + 4*128, Coeffs2_2, vector_len, _masm, 4, 128); + } - for (int i = 0; i < 8; i += 2) { - __ evpermi2d(xmm(i), xmm(i + 20), xmm(i + 21), Assembler::AVX_512bit); - __ evpermi2d(xmm(i + 1), xmm(i + 20), xmm(i + 21), Assembler::AVX_512bit); + // Four batches of 8 registers, consecutive loads + for (int i=0; i<4; i++) { + loadXmms(Coeffs1_2, coeffs, i*256, vector_len, _masm, 4); + loadXmms(Coeffs2_2, coeffs, 128 + i*256, vector_len, _masm, 4); + + // level 3 - 16 + __ vmovdqu(Zetas1[0], Address(zetas, i*128 + 3 * 512), vector_len); + montMul64(Scratch1, Coeffs2, Zetas1, Coeffs2, Scratch2); + sub_add(Coeffs2, Coeffs1, Coeffs1, Scratch1, vector_len, _masm); + + __ vmovdqu(Zetas1[0], Address(zetas, i*128 + 64 + 3 * 512), vector_len); + montMul64(Scratch1, Coeffs4, Zetas1, Coeffs4, Scratch2); + sub_add(Coeffs4, Coeffs3, Coeffs3, Scratch1, vector_len, _masm); + + // level 4 - 8 + loadXmms(Zetas3, zetas, i*128 + 4 * 512, vector_len, _masm); + montMul64(Scratch1, Coeffs2_1, Zetas3, Coeffs2_1, Scratch2); + sub_add(Coeffs2_1, Coeffs1_1, Coeffs1_1, Scratch1, vector_len, _masm); + + loadXmms(Zetas3, zetas, i*128 + 64 + 4 * 512, vector_len, _masm); + montMul64(Scratch1, Coeffs4_1, Zetas3, Coeffs4_1, Scratch2); + sub_add(Coeffs4_1, Coeffs3_1, Coeffs3_1, Scratch1, vector_len, _masm); + + for (int level = 5, distance = 4; level<8; level++, distance /= 2) { + // zetas = load(level * 512) + // coeffs1_2, scratch1 = shuffle(coeffs1_2, coeffs2_2) + // scratch1 = scratch1 * zetas + // coeffs2_2 = coeffs1_2 - scratch1 + // coeffs1_2 = coeffs1_2 + scratch1 + loadXmms(Zetas3, zetas, i*128 + level * 512, vector_len, _masm); + shuffle(Scratch1, Coeffs1_1, Coeffs2_1, distance * 32); //Coeffs2_2 freed + montMul64(Scratch1, Scratch1, Zetas3, Coeffs2_1, Scratch2, level==7); + sub_add(Coeffs2_1, Coeffs1_1, Coeffs1_1, Scratch1, vector_len, _masm); + + loadXmms(Zetas3, zetas, i*128 + 64 + level * 512, vector_len, _masm); + shuffle(Scratch1, Coeffs3_1, Coeffs4_1, distance * 32); //Coeffs4_2 freed + montMul64(Scratch1, Scratch1, Zetas3, Coeffs4_1, Scratch2, level==7); + sub_add(Coeffs4_1, Coeffs3_1, Coeffs3_1, Scratch1, vector_len, _masm); + } + + shuffle(Scratch1, Coeffs1_1, Coeffs2_1, 0); + shuffle(Scratch1, Coeffs3_1, Coeffs4_1, 0); + + storeXmms(coeffs, i*256, Coeffs1_2, vector_len, _masm, 4); + storeXmms(coeffs, 128 + i*256, Coeffs2_2, vector_len, _masm, 4); + } } - __ cmpl(iterations, 0); - __ jcc(Assembler::equal, L_end); - - store4Xmms(coeffs, 0, xmm0_3, _masm); - store4Xmms(coeffs, 4 * XMMBYTES, xmm4_7, _masm); - - load4Xmms(xmm0_3, coeffs, 8 * XMMBYTES, _masm); - load4Xmms(xmm4_7, coeffs, 12 * XMMBYTES, _masm); - - __ addptr(zetas, 4 * XMMBYTES); - - __ jmp(L_loop); - - __ BIND(L_end); - - store4Xmms(coeffs, 8 * XMMBYTES, xmm0_3, _masm); - store4Xmms(coeffs, 12 * XMMBYTES, xmm4_7, _masm); - __ leave(); // required for proper stackwalking of RuntimeStub frame __ mov64(rax, 0); // return 0 __ ret(0); @@ -459,173 +654,234 @@ static address generate_dilithiumAlmostNtt_avx512(StubGenerator *stubgen, // static int implDilithiumAlmostInverseNtt(int[] coeffs, int[] zetas) {} // // coeffs (int[256]) = c_rarg0 -// zetas (int[256]) = c_rarg1 -static address generate_dilithiumAlmostInverseNtt_avx512(StubGenerator *stubgen, - MacroAssembler *_masm) { - +// zetas (int[128*8]) = c_rarg1 +static address generate_dilithiumAlmostInverseNtt_avx(StubGenerator *stubgen, + int vector_len, MacroAssembler *_masm) { __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumAlmostInverseNtt_id; StubCodeMark mark(stubgen, stub_id); address start = __ pc(); __ enter(); - Label L_loop, L_end; - const Register coeffs = c_rarg0; const Register zetas = c_rarg1; + const Register scratch = r10; + + // AVX2 version uses the first half of these arrays + const XMMRegister Coeffs1[] = {xmm0, xmm1, xmm16, xmm17}; + const XMMRegister Coeffs2[] = {xmm2, xmm3, xmm18, xmm19}; + const XMMRegister Coeffs3[] = {xmm4, xmm5, xmm20, xmm21}; + const XMMRegister Coeffs4[] = {xmm6, xmm7, xmm22, xmm23}; + const XMMRegister Scratch1[] = {xmm8, xmm9, xmm24, xmm25}; + const XMMRegister Scratch2[] = {xmm10, xmm11, xmm26, xmm27}; + const XMMRegister Zetas1[] = {xmm12, xmm12, xmm12, xmm12}; + const XMMRegister Zetas2[] = {xmm12, xmm12, xmm13, xmm13}; + const XMMRegister Zetas3[] = {xmm12, xmm13, xmm28, xmm29}; + const XMMRegister montQInvModR = xmm14; + const XMMRegister dilithium_q = xmm15; + const XMMRegister unshuffle1 = xmm30; + const XMMRegister unshuffle2 = xmm31; + KRegister mergeMask1 = k1; + KRegister mergeMask2 = k2; + // lambdas to hide repeated parameters + auto shuffle = whole_shuffle(scratch, mergeMask1, mergeMask2, unshuffle1, unshuffle2, vector_len, _masm); + auto montMul64 = whole_montMul(montQInvModR, dilithium_q, mergeMask2, vector_len, _masm); - const Register iterations = c_rarg2; - - const Register perms = r11; - - __ lea(perms, ExternalAddress(dilithiumAvx512PermsAddr())); - - __ evmovdqul(montMulPerm, Address(perms, montMulPermsIdx), Assembler::AVX_512bit); __ vpbroadcastd(montQInvModR, ExternalAddress(dilithiumAvx512ConstsAddr(montQInvModRIdx)), - Assembler::AVX_512bit, scratch); // q^-1 mod 2^32 + vector_len, scratch); // q^-1 mod 2^32 __ vpbroadcastd(dilithium_q, ExternalAddress(dilithiumAvx512ConstsAddr(dilithium_qIdx)), - Assembler::AVX_512bit, scratch); // q + vector_len, scratch); // q // Each level represents one iteration of the outer for loop of the // Java version. // In each of these iterations half of the coefficients are added to and // subtracted from the other half of the coefficients then the result of - // the substartion is (Montgomery) multiplied by the corresponding zetas. - // In each level we just collect the coefficients (using evpermi2d() - // instructions where necessary, i.e. on levels 0-4) so that the results of + // the subtraction is (Montgomery) multiplied by the corresponding zetas. + // In each level we just shuffle the coefficients so that the results of // the additions and subtractions go to the vector registers so that they // align with each other and the zetas. - // We do levels 0-6 in two batches, each batch entirely in the vector registers - load4Xmms(xmm0_3, coeffs, 0, _masm); - load4Xmms(xmm4_7, coeffs, 4 * XMMBYTES, _masm); - - __ movl(iterations, 2); - - __ align(OptoLoopAlignment); - __ BIND(L_loop); - - __ subl(iterations, 1); - - // level 0 - loadPerm(xmm8_11, perms, nttInvL0PermsIdx, _masm); - loadPerm(xmm12_15, perms, nttInvL0PermsIdx + 64, _masm); - - for (int i = 0; i < 8; i += 2) { - __ evpermi2d(xmm(i / 2 + 8), xmm(i), xmm(i + 1), Assembler::AVX_512bit); - __ evpermi2d(xmm(i / 2 + 12), xmm(i), xmm(i + 1), Assembler::AVX_512bit); - } - - load4Xmms(xmm4_7, zetas, 0, _masm); - sub_add(xmm24_27, xmm0_3, xmm8_11, xmm12_15, _masm); - montMul64(xmm4_7, xmm4_7, xmm24_27, xmm16_27, true, _masm); - - // level 1 - loadPerm(xmm8_11, perms, nttInvL1PermsIdx, _masm); - loadPerm(xmm12_15, perms, nttInvL1PermsIdx + 64, _masm); - - for (int i = 0; i < 4; i++) { - __ evpermi2d(xmm(i + 8), xmm(i), xmm(i + 4), Assembler::AVX_512bit); - __ evpermi2d(xmm(i + 12), xmm(i), xmm(i + 4), Assembler::AVX_512bit); - } - - load4Xmms(xmm4_7, zetas, 512, _masm); - sub_add(xmm24_27, xmm0_3, xmm8_11, xmm12_15, _masm); - montMul64(xmm4_7, xmm24_27, xmm4_7, xmm16_27, _masm); - - // level 2 - loadPerm(xmm8_11, perms, nttInvL2PermsIdx, _masm); - loadPerm(xmm12_15, perms, nttInvL2PermsIdx + 64, _masm); - - for (int i = 0; i < 4; i++) { - __ evpermi2d(xmm(i + 8), xmm(i), xmm(i + 4), Assembler::AVX_512bit); - __ evpermi2d(xmm(i + 12), xmm(i), xmm(i + 4), Assembler::AVX_512bit); - } - - load4Xmms(xmm4_7, zetas, 2 * 512, _masm); - sub_add(xmm24_27, xmm0_3, xmm8_11, xmm12_15, _masm); - montMul64(xmm4_7, xmm24_27, xmm4_7, xmm16_27, _masm); - - // level 3 - loadPerm(xmm8_11, perms, nttInvL3PermsIdx, _masm); - loadPerm(xmm12_15, perms, nttInvL3PermsIdx + 64, _masm); - - for (int i = 0; i < 4; i++) { - __ evpermi2d(xmm(i + 8), xmm(i), xmm(i + 4), Assembler::AVX_512bit); - __ evpermi2d(xmm(i + 12), xmm(i), xmm(i + 4), Assembler::AVX_512bit); - } - - load4Xmms(xmm4_7, zetas, 3 * 512, _masm); - sub_add(xmm24_27, xmm0_3, xmm8_11, xmm12_15, _masm); - montMul64(xmm4_7, xmm24_27, xmm4_7, xmm16_27, _masm); - - // level 4 - loadPerm(xmm8_11, perms, nttInvL4PermsIdx, _masm); - loadPerm(xmm12_15, perms, nttInvL4PermsIdx + 64, _masm); - - for (int i = 0; i < 4; i++) { - __ evpermi2d(xmm(i + 8), xmm(i), xmm(i + 4), Assembler::AVX_512bit); - __ evpermi2d(xmm(i + 12), xmm(i), xmm(i + 4), Assembler::AVX_512bit); - } - - load4Xmms(xmm4_7, zetas, 4 * 512, _masm); - sub_add(xmm24_27, xmm0_3, xmm8_11, xmm12_15, _masm); - montMul64(xmm4_7, xmm24_27, xmm4_7, xmm16_27, _masm); - - // level 5 - load4Xmms(xmm12_15, zetas, 5 * 512, _masm); - sub_add(xmm8_11, xmm0_3, xmm0426, xmm1537, _masm); - montMul64(xmm4_7, xmm8_11, xmm12_15, xmm16_27, _masm); - - // level 6 - load4Xmms(xmm12_15, zetas, 6 * 512, _masm); - sub_add(xmm8_11, xmm0_3, xmm0145, xmm2367, _masm); - montMul64(xmm4_7, xmm8_11, xmm12_15, xmm16_27, _masm); - - __ cmpl(iterations, 0); - __ jcc(Assembler::equal, L_end); - - // save the coefficients of the first batch, adjust the zetas - // and load the second batch of coefficients - store4Xmms(coeffs, 0, xmm0_3, _masm); - store4Xmms(coeffs, 4 * XMMBYTES, xmm4_7, _masm); - - __ addptr(zetas, 4 * XMMBYTES); + if (vector_len == Assembler::AVX_512bit) { + // levels 4-7, register shuffles: + const XMMRegister Coeffs1_1[] = {xmm0, xmm1, xmm2, xmm3}; + const XMMRegister Coeffs2_1[] = {xmm16, xmm17, xmm18, xmm19}; + const XMMRegister Coeffs3_1[] = {xmm4, xmm5, xmm6, xmm7}; + const XMMRegister Coeffs4_1[] = {xmm20, xmm21, xmm22, xmm23}; + const XMMRegister Coeffs1_2[] = {xmm0, xmm16, xmm2, xmm18}; + const XMMRegister Coeffs2_2[] = {xmm1, xmm17, xmm3, xmm19}; + const XMMRegister Coeffs3_2[] = {xmm4, xmm20, xmm6, xmm22}; + const XMMRegister Coeffs4_2[] = {xmm5, xmm21, xmm7, xmm23}; + + // Constants for shuffle and montMul64 + __ mov64(scratch, 0b1010101010101010); + __ kmovwl(mergeMask1, scratch); + __ knotwl(mergeMask2, mergeMask1); + __ vmovdqu(unshuffle1, ExternalAddress(unshufflePermsAddr(4)), vector_len, scratch); + __ vmovdqu(unshuffle2, ExternalAddress(unshufflePermsAddr(5)), vector_len, scratch); + + int memStep = 4 * 64; + loadXmms(Coeffs1, coeffs, 0*memStep, vector_len, _masm); + loadXmms(Coeffs2, coeffs, 1*memStep, vector_len, _masm); + loadXmms(Coeffs3, coeffs, 2*memStep, vector_len, _masm); + loadXmms(Coeffs4, coeffs, 3*memStep, vector_len, _masm); + + shuffle(Scratch1, Coeffs1_2, Coeffs2_2, 1); + shuffle(Scratch1, Coeffs3_2, Coeffs4_2, 1); + + // Constants for shuffle(128) + __ vmovdqu(unshuffle1, ExternalAddress(unshufflePermsAddr(0)), vector_len, scratch); + __ vmovdqu(unshuffle2, ExternalAddress(unshufflePermsAddr(1)), vector_len, scratch); + for (int level = 0, distance = 1; level<4; level++, distance *= 2) { + // zetas = load(level * 512) + // coeffs1_2 = coeffs1_2 + coeffs2_2 + // scratch1 = coeffs1_2 - coeffs2_2 + // scratch1 = scratch1 * zetas + // coeffs1_2, coeffs2_2 = shuffle(coeffs1_2, scratch1) + loadXmms(Zetas3, zetas, level * 512, vector_len, _masm); + sub_add(Scratch1, Coeffs1_2, Coeffs1_2, Coeffs2_2, vector_len, _masm); // Coeffs2_2 freed + montMul64(Scratch1, Scratch1, Zetas3, Coeffs2_2, Scratch2, level==0); + shuffle(Coeffs2_2, Coeffs1_2, Scratch1, distance * 32); + + loadXmms(Zetas3, zetas, 4*64 + level * 512, vector_len, _masm); + sub_add(Scratch1, Coeffs3_2, Coeffs3_2, Coeffs4_2, vector_len, _masm); // Coeffs4_2 freed + montMul64(Scratch1, Scratch1, Zetas3, Coeffs4_2, Scratch2, level==0); + shuffle(Coeffs4_2, Coeffs3_2, Scratch1, distance * 32); + } - load4Xmms(xmm0_3, coeffs, 8 * XMMBYTES, _masm); - load4Xmms(xmm4_7, coeffs, 12 * XMMBYTES, _masm); + // level 4 + loadXmms(Zetas3, zetas, 4 * 512, vector_len, _masm); + sub_add(Scratch1, Coeffs1_2, Coeffs1_2, Coeffs2_2, vector_len, _masm); // Coeffs2_2 freed + montMul64(Coeffs2_2, Scratch1, Zetas3, Scratch1, Scratch2); - __ jmp(L_loop); + loadXmms(Zetas3, zetas, 4*64 + 4 * 512, vector_len, _masm); + sub_add(Scratch1, Coeffs3_2, Coeffs3_2, Coeffs4_2, vector_len, _masm); // Coeffs4_2 freed + montMul64(Coeffs4_2, Scratch1, Zetas3, Scratch1, Scratch2); - __ BIND(L_end); + // level 5 + __ vmovdqu(Zetas2[0], Address(zetas, 5 * 512), vector_len); + __ vmovdqu(Zetas2[2], Address(zetas, 2*64 + 5 * 512), vector_len); + sub_add(Scratch1, Coeffs1_1, Coeffs1_1, Coeffs2_1, vector_len, _masm); // Coeffs2_1 freed + montMul64(Coeffs2_1, Scratch1, Zetas2, Scratch1, Scratch2); - // load the coeffs of the first batch of coefficients that were saved after - // level 6 into Zmm_8-Zmm_15 and do the last level entirely in the vector - // registers - load4Xmms(xmm8_11, coeffs, 0, _masm); - load4Xmms(xmm12_15, coeffs, 4 * XMMBYTES, _masm); + __ vmovdqu(Zetas2[0], Address(zetas, 4*64 + 5 * 512), vector_len); + __ vmovdqu(Zetas2[2], Address(zetas, 6*64 + 5 * 512), vector_len); + sub_add(Scratch1, Coeffs3_1, Coeffs3_1, Coeffs4_1, vector_len, _masm); // Coeffs4_1 freed + montMul64(Coeffs4_1, Scratch1, Zetas2, Scratch1, Scratch2); - // level 7 + // level 6 + __ vmovdqu(Zetas1[0], Address(zetas, 6 * 512), vector_len); + sub_add(Scratch1, Coeffs1, Coeffs1, Coeffs2, vector_len, _masm); // Coeffs2 freed + montMul64(Coeffs2, Scratch1, Zetas1, Scratch1, Scratch2); - loadXmm29(zetas, 7 * 512, _masm); + __ vmovdqu(Zetas1[0], Address(zetas, 4*64 + 6 * 512), vector_len); + sub_add(Scratch1, Coeffs3, Coeffs3, Coeffs4, vector_len, _masm); // Coeffs4 freed + montMul64(Coeffs4, Scratch1, Zetas1, Scratch1, Scratch2); - for (int i = 0; i < 8; i++) { - __ evpaddd(xmm(i + 16), k0, xmm(i), xmm(i + 8), false, Assembler::AVX_512bit); - } + // level 7 + __ vmovdqu(Zetas1[0], Address(zetas, 7 * 512), vector_len); + sub_add(Scratch1, Coeffs1, Coeffs1, Coeffs3, vector_len, _masm); // Coeffs3 freed + montMul64(Coeffs3, Scratch1, Zetas1, Scratch1, Scratch2); + sub_add(Scratch1, Coeffs2, Coeffs2, Coeffs4, vector_len, _masm); // Coeffs4 freed + montMul64(Coeffs4, Scratch1, Zetas1, Scratch1, Scratch2); + + storeXmms(coeffs, 0*memStep, Coeffs1, vector_len, _masm); + storeXmms(coeffs, 1*memStep, Coeffs2, vector_len, _masm); + storeXmms(coeffs, 2*memStep, Coeffs3, vector_len, _masm); + storeXmms(coeffs, 3*memStep, Coeffs4, vector_len, _masm); + } else { // Assembler::AVX_256bit + // Permutations of Coeffs1, Coeffs2, Coeffs3 and Coeffs4 + const XMMRegister Coeffs1_1[] = {xmm0, xmm2}; + const XMMRegister Coeffs2_1[] = {xmm1, xmm3}; + const XMMRegister Coeffs3_1[] = {xmm4, xmm6}; + const XMMRegister Coeffs4_1[] = {xmm5, xmm7}; + + const XMMRegister Coeffs1_2[] = {xmm0, xmm1, xmm2, xmm3}; + const XMMRegister Coeffs2_2[] = {xmm4, xmm5, xmm6, xmm7}; + + // Four batches of 8 registers, consecutive loads + for (int i=0; i<4; i++) { + loadXmms(Coeffs1_2, coeffs, i*256, vector_len, _masm, 4); + loadXmms(Coeffs2_2, coeffs, 128 + i*256, vector_len, _masm, 4); + + shuffle(Scratch1, Coeffs1_1, Coeffs2_1, 1); + shuffle(Scratch1, Coeffs3_1, Coeffs4_1, 1); + + for (int level = 0, distance = 1; level <= 2; level++, distance *= 2) { + // zetas = load(level * 512) + // coeffs1_2 = coeffs1_2 + coeffs2_2 + // scratch1 = coeffs1_2 - coeffs2_2 + // scratch1 = scratch1 * zetas + // coeffs1_2, coeffs2_2 = shuffle(coeffs1_2, scratch1) + loadXmms(Zetas3, zetas, i*128 + level * 512, vector_len, _masm); + sub_add(Scratch1, Coeffs1_1, Coeffs1_1, Coeffs2_1, vector_len, _masm); // Coeffs2_1 freed + montMul64(Scratch1, Scratch1, Zetas3, Coeffs2_1, Scratch2, level==0); + shuffle(Coeffs2_1, Coeffs1_1, Scratch1, distance * 32); + + loadXmms(Zetas3, zetas, i*128 + 64 + level * 512, vector_len, _masm); + sub_add(Scratch1, Coeffs3_1, Coeffs3_1, Coeffs4_1, vector_len, _masm); // Coeffs4_1 freed + montMul64(Scratch1, Scratch1, Zetas3, Coeffs4_1, Scratch2, level==0); + shuffle(Coeffs4_1, Coeffs3_1, Scratch1, distance * 32); + } + + // level 3 + loadXmms(Zetas3, zetas, i*128 + 3 * 512, vector_len, _masm); + sub_add(Scratch1, Coeffs1_1, Coeffs1_1, Coeffs2_1, vector_len, _masm); // Coeffs2_1 freed + montMul64(Coeffs2_1, Scratch1, Zetas3, Scratch1, Scratch2); + + loadXmms(Zetas3, zetas, i*128 + 64 + 3 * 512, vector_len, _masm); + sub_add(Scratch1, Coeffs3_1, Coeffs3_1, Coeffs4_1, vector_len, _masm); // Coeffs4_1 freed + montMul64(Coeffs4_1, Scratch1, Zetas3, Scratch1, Scratch2); + + // level 4 + __ vmovdqu(Zetas1[0], Address(zetas, i*128 + 4 * 512), vector_len); + sub_add(Scratch1, Coeffs1, Coeffs1, Coeffs2, vector_len, _masm); // Coeffs2 freed + montMul64(Coeffs2, Scratch1, Zetas1, Scratch1, Scratch2); + + __ vmovdqu(Zetas1[0], Address(zetas, i*128 + 64 + 4 * 512), vector_len); + sub_add(Scratch1, Coeffs3, Coeffs3, Coeffs4, vector_len, _masm); // Coeffs4 freed + montMul64(Coeffs4, Scratch1, Zetas1, Scratch1, Scratch2); + + storeXmms(coeffs, i*256, Coeffs1_2, vector_len, _masm, 4); + storeXmms(coeffs, 128 + i*256, Coeffs2_2, vector_len, _masm, 4); + } - for (int i = 0; i < 8; i++) { - __ evpsubd(xmm(i), k0, xmm(i + 8), xmm(i), false, Assembler::AVX_512bit); + // Four batches of 8 registers each, 128 bytes apart + for (int i=0; i<4; i++) { + loadXmms(Coeffs1_2, coeffs, i*32 + 0*128, vector_len, _masm, 4, 128); + loadXmms(Coeffs2_2, coeffs, i*32 + 4*128, vector_len, _masm, 4, 128); + + // level 5 + loadXmms(Zetas3, zetas, 5 * 512, vector_len, _masm, 2, 128); + sub_add(Scratch1, Coeffs1_1, Coeffs1_1, Coeffs2_1, vector_len, _masm); // Coeffs2_1 freed + montMul64(Coeffs2_1, Scratch1, Zetas3, Scratch1, Scratch2); + + loadXmms(Zetas3, zetas, 4*64 + 5 * 512, vector_len, _masm, 2, 128); + sub_add(Scratch1, Coeffs3_1, Coeffs3_1, Coeffs4_1, vector_len, _masm); // Coeffs4_1 freed + montMul64(Coeffs4_1, Scratch1, Zetas3, Scratch1, Scratch2); + + // level 6 + __ vmovdqu(Zetas1[0], Address(zetas, 6 * 512), vector_len); + sub_add(Scratch1, Coeffs1, Coeffs1, Coeffs2, vector_len, _masm); // Coeffs2 freed + montMul64(Coeffs2, Scratch1, Zetas1, Scratch1, Scratch2); + + __ vmovdqu(Zetas1[0], Address(zetas, 4*64 + 6 * 512), vector_len); + sub_add(Scratch1, Coeffs3, Coeffs3, Coeffs4, vector_len, _masm); // Coeffs4 freed + montMul64(Coeffs4, Scratch1, Zetas1, Scratch1, Scratch2); + + // level 7 + __ vmovdqu(Zetas1[0], Address(zetas, 7 * 512), vector_len); + sub_add(Scratch1, Coeffs1, Coeffs1, Coeffs3, vector_len, _masm); // Coeffs3 freed + montMul64(Coeffs3, Scratch1, Zetas1, Scratch1, Scratch2); + sub_add(Scratch1, Coeffs2, Coeffs2, Coeffs4, vector_len, _masm); // Coeffs4 freed + montMul64(Coeffs4, Scratch1, Zetas1, Scratch1, Scratch2); + + storeXmms(coeffs, i*32 + 0*128, Coeffs1_2, vector_len, _masm, 4, 128); + storeXmms(coeffs, i*32 + 4*128, Coeffs2_2, vector_len, _masm, 4, 128); + } } - store4Xmms(coeffs, 0, xmm16_19, _masm); - store4Xmms(coeffs, 4 * XMMBYTES, xmm20_23, _masm); - montMul64(xmm0_3, xmm0_3, xmm29_29, xmm16_27, _masm); - montMul64(xmm4_7, xmm4_7, xmm29_29, xmm16_27, _masm); - store4Xmms(coeffs, 8 * XMMBYTES, xmm0_3, _masm); - store4Xmms(coeffs, 12 * XMMBYTES, xmm4_7, _masm); - __ leave(); // required for proper stackwalking of RuntimeStub frame __ mov64(rax, 0); // return 0 __ ret(0); @@ -641,8 +897,8 @@ static address generate_dilithiumAlmostInverseNtt_avx512(StubGenerator *stubgen, // result (int[256]) = c_rarg0 // poly1 (int[256]) = c_rarg1 // poly2 (int[256]) = c_rarg2 -static address generate_dilithiumNttMult_avx512(StubGenerator *stubgen, - MacroAssembler *_masm) { +static address generate_dilithiumNttMult_avx(StubGenerator *stubgen, + int vector_len, MacroAssembler *_masm) { __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumNttMult_id; @@ -655,40 +911,60 @@ static address generate_dilithiumNttMult_avx512(StubGenerator *stubgen, const Register result = c_rarg0; const Register poly1 = c_rarg1; const Register poly2 = c_rarg2; - - const Register perms = r10; // scratch reused after not needed any more + const Register scratch = r10; const Register len = r11; - const XMMRegister montRSquareModQ = xmm29; + const XMMRegister montQInvModR = xmm8; + const XMMRegister dilithium_q = xmm9; + + const XMMRegister Poly1[] = {xmm0, xmm1, xmm16, xmm17}; + const XMMRegister Poly2[] = {xmm2, xmm3, xmm18, xmm19}; + const XMMRegister Scratch1[] = {xmm4, xmm5, xmm20, xmm21}; + const XMMRegister Scratch2[] = {xmm6, xmm7, xmm22, xmm23}; + const XMMRegister MontRSquareModQ[] = {xmm10, xmm10, xmm10, xmm10}; + KRegister mergeMask = k1; + // lambda to hide repeated parameters + auto montMul64 = whole_montMul(montQInvModR, dilithium_q, mergeMask, vector_len, _masm); __ vpbroadcastd(montQInvModR, ExternalAddress(dilithiumAvx512ConstsAddr(montQInvModRIdx)), - Assembler::AVX_512bit, scratch); // q^-1 mod 2^32 + vector_len, scratch); // q^-1 mod 2^32 __ vpbroadcastd(dilithium_q, ExternalAddress(dilithiumAvx512ConstsAddr(dilithium_qIdx)), - Assembler::AVX_512bit, scratch); // q - __ vpbroadcastd(montRSquareModQ, + vector_len, scratch); // q + __ vpbroadcastd(MontRSquareModQ[0], ExternalAddress(dilithiumAvx512ConstsAddr(montRSquareModQIdx)), - Assembler::AVX_512bit, scratch); // 2^64 mod q + vector_len, scratch); // 2^64 mod q + if (vector_len == Assembler::AVX_512bit) { + __ mov64(scratch, 0b0101010101010101); + __ kmovwl(mergeMask, scratch); + } - __ lea(perms, ExternalAddress(dilithiumAvx512PermsAddr())); - __ evmovdqul(montMulPerm, Address(perms, montMulPermsIdx), Assembler::AVX_512bit); + // Total payload is 256*int32s. + // - memStep is number of bytes one iteration processes. + // - loopCnt is number of iterations it will take to process entire payload. + int loopCnt = 4; + int memStep = 4 * 64; + if (vector_len == Assembler::AVX_256bit) { + loopCnt = 16; + memStep = 2 * 32; + } - __ movl(len, 4); + __ movl(len, loopCnt); __ align(OptoLoopAlignment); __ BIND(L_loop); - load4Xmms(xmm4_7, poly2, 0, _masm); - load4Xmms(xmm0_3, poly1, 0, _masm); - montMul64(xmm4_7, xmm4_7, xmm29_29, xmm16_27, _masm); - montMul64(xmm0_3, xmm0_3, xmm4_7, xmm16_27, true, _masm); - store4Xmms(result, 0, xmm0_3, _masm); + loadXmms(Poly2, poly2, 0, vector_len, _masm); + loadXmms(Poly1, poly1, 0, vector_len, _masm); + montMul64(Poly2, Poly2, MontRSquareModQ, Scratch1, Scratch2); + montMul64(Poly1, Poly1, Poly2, Scratch1, Scratch2, true); + storeXmms(result, 0, Poly1, vector_len, _masm); __ subl(len, 1); - __ addptr(poly1, 4 * XMMBYTES); - __ addptr(poly2, 4 * XMMBYTES); - __ addptr(result, 4 * XMMBYTES); + __ addptr(poly1, memStep); + __ addptr(poly2, memStep); + __ addptr(result, memStep); __ cmpl(len, 0); __ jcc(Assembler::notEqual, L_loop); @@ -705,8 +981,8 @@ static address generate_dilithiumNttMult_avx512(StubGenerator *stubgen, // // coeffs (int[256]) = c_rarg0 // constant (int) = c_rarg1 -static address generate_dilithiumMontMulByConstant_avx512(StubGenerator *stubgen, - MacroAssembler *_masm) { +static address generate_dilithiumMontMulByConstant_avx(StubGenerator *stubgen, + int vector_len, MacroAssembler *_masm) { __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumMontMulByConstant_id; @@ -718,38 +994,64 @@ static address generate_dilithiumMontMulByConstant_avx512(StubGenerator *stubgen const Register coeffs = c_rarg0; const Register rConstant = c_rarg1; - - const Register perms = c_rarg2; // not used for argument + const Register scratch = r10; const Register len = r11; - const XMMRegister constant = xmm29; + const XMMRegister montQInvModR = xmm8; + const XMMRegister dilithium_q = xmm9; - __ lea(perms, ExternalAddress(dilithiumAvx512PermsAddr())); + const XMMRegister Coeffs1[] = {xmm0, xmm1, xmm16, xmm17}; + const XMMRegister Coeffs2[] = {xmm2, xmm3, xmm18, xmm19}; + const XMMRegister Scratch1[] = {xmm4, xmm5, xmm20, xmm21}; + const XMMRegister Scratch2[] = {xmm6, xmm7, xmm22, xmm23}; + const XMMRegister Constant[] = {xmm10, xmm10, xmm10, xmm10}; + XMMRegister constant = Constant[0]; + KRegister mergeMask = k1; + // lambda to hide repeated parameters + auto montMul64 = whole_montMul(montQInvModR, dilithium_q, mergeMask, vector_len, _masm); - // the following four vector registers are used in montMul64 + // load constants for montMul64 __ vpbroadcastd(montQInvModR, ExternalAddress(dilithiumAvx512ConstsAddr(montQInvModRIdx)), - Assembler::AVX_512bit, scratch); // q^-1 mod 2^32 + vector_len, scratch); // q^-1 mod 2^32 __ vpbroadcastd(dilithium_q, ExternalAddress(dilithiumAvx512ConstsAddr(dilithium_qIdx)), - Assembler::AVX_512bit, scratch); // q - __ evmovdqul(montMulPerm, Address(perms, montMulPermsIdx), Assembler::AVX_512bit); - __ evpbroadcastd(constant, rConstant, Assembler::AVX_512bit); // constant multiplier + vector_len, scratch); // q + if (vector_len == Assembler::AVX_256bit) { + __ movdl(constant, rConstant); + __ vpbroadcastd(constant, constant, vector_len); // constant multiplier + } else { + __ evpbroadcastd(constant, rConstant, Assembler::AVX_512bit); // constant multiplier + + __ mov64(scratch, 0b0101010101010101); //dw-mask + __ kmovwl(mergeMask, scratch); + } + + // Total payload is 256*int32s. + // - memStep is number of bytes one montMul64 processes. + // - loopCnt is number of iterations it will take to process entire payload. + // - (two memSteps per loop) + int memStep = 4 * 64; + int loopCnt = 2; + if (vector_len == Assembler::AVX_256bit) { + memStep = 2 * 32; + loopCnt = 8; + } - __ movl(len, 2); + __ movl(len, loopCnt); __ align(OptoLoopAlignment); __ BIND(L_loop); - load4Xmms(xmm0_3, coeffs, 0, _masm); - load4Xmms(xmm4_7, coeffs, 4 * XMMBYTES, _masm); - montMul64(xmm0_3, xmm0_3, xmm29_29, xmm16_27, _masm); - montMul64(xmm4_7, xmm4_7, xmm29_29, xmm16_27, _masm); - store4Xmms(coeffs, 0, xmm0_3, _masm); - store4Xmms(coeffs, 4 * XMMBYTES, xmm4_7, _masm); + loadXmms(Coeffs1, coeffs, 0, vector_len, _masm); + loadXmms(Coeffs2, coeffs, memStep, vector_len, _masm); + montMul64(Coeffs1, Coeffs1, Constant, Scratch1, Scratch2); + montMul64(Coeffs2, Coeffs2, Constant, Scratch1, Scratch2); + storeXmms(coeffs, 0, Coeffs1, vector_len, _masm); + storeXmms(coeffs, memStep, Coeffs2, vector_len, _masm); __ subl(len, 1); - __ addptr(coeffs, 512); + __ addptr(coeffs, 2 * memStep); __ cmpl(len, 0); __ jcc(Assembler::notEqual, L_loop); @@ -769,9 +1071,8 @@ static address generate_dilithiumMontMulByConstant_avx512(StubGenerator *stubgen // highPart (int[256]) = c_rarg2 // twoGamma2 (int) = c_rarg3 // multiplier (int) = c_rarg4 -static address generate_dilithiumDecomposePoly_avx512(StubGenerator *stubgen, - MacroAssembler *_masm) { - +static address generate_dilithiumDecomposePoly_avx(StubGenerator *stubgen, + int vector_len, MacroAssembler *_masm) { __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumDecomposePoly_id; StubCodeMark mark(stubgen, stub_id); @@ -785,26 +1086,45 @@ static address generate_dilithiumDecomposePoly_avx512(StubGenerator *stubgen, const Register highPart = c_rarg2; const Register rTwoGamma2 = c_rarg3; + const Register scratch = r10; const Register len = r11; - const XMMRegister zero = xmm24; - const XMMRegister one = xmm25; - const XMMRegister qMinus1 = xmm26; - const XMMRegister gamma2 = xmm27; - const XMMRegister twoGamma2 = xmm28; - const XMMRegister barrettMultiplier = xmm29; - const XMMRegister barrettAddend = xmm30; - - __ vpxor(zero, zero, zero, Assembler::AVX_512bit); // 0 - __ vpternlogd(xmm0, 0xff, xmm0, xmm0, Assembler::AVX_512bit); // -1 - __ vpsubd(one, zero, xmm0, Assembler::AVX_512bit); // 1 + + const XMMRegister one = xmm0; + const XMMRegister gamma2 = xmm1; + const XMMRegister twoGamma2 = xmm2; + const XMMRegister barrettMultiplier = xmm3; + const XMMRegister barrettAddend = xmm4; + const XMMRegister dilithium_q = xmm5; + const XMMRegister zero = xmm29; // AVX512-only + const XMMRegister minusOne = xmm30; // AVX512-only + const XMMRegister qMinus1 = xmm31; // AVX512-only + + XMMRegister RPlus[] = {xmm6, xmm7, xmm16, xmm17}; + XMMRegister Quotient[] = {xmm8, xmm9, xmm18, xmm19}; + XMMRegister R0[] = {xmm10, xmm11, xmm20, xmm21}; + XMMRegister Mask[] = {xmm12, xmm13, xmm22, xmm23}; + XMMRegister Tmp1[] = {xmm14, xmm15, xmm24, xmm25}; + __ vpbroadcastd(dilithium_q, ExternalAddress(dilithiumAvx512ConstsAddr(dilithium_qIdx)), - Assembler::AVX_512bit, scratch); // q + vector_len, scratch); // q __ vpbroadcastd(barrettAddend, ExternalAddress(dilithiumAvx512ConstsAddr(barrettAddendIdx)), - Assembler::AVX_512bit, scratch); // addend for Barrett reduction + vector_len, scratch); // addend for Barrett reduction + if (vector_len == Assembler::AVX_512bit) { + __ vpxor(zero, zero, zero, vector_len); // 0 + __ vpternlogd(minusOne, 0xff, minusOne, minusOne, vector_len); // -1 + __ vpsrld(one, minusOne, 31, vector_len); + __ vpsubd(qMinus1, dilithium_q, one, vector_len); // q - 1 + __ evpbroadcastd(twoGamma2, rTwoGamma2, vector_len); // 2 * gamma2 + } else { + __ vpcmpeqd(one, one, one, vector_len); + __ vpsrld(one, one, 31, vector_len); + __ movdl(twoGamma2, rTwoGamma2); + __ vpbroadcastd(twoGamma2, twoGamma2, vector_len); // 2 * gamma2 + } - __ evpbroadcastd(twoGamma2, rTwoGamma2, Assembler::AVX_512bit); // 2 * gamma2 + __ vpsrad(gamma2, twoGamma2, 1, vector_len); // gamma2 #ifndef _WIN64 const Register rMultiplier = c_rarg4; @@ -813,201 +1133,185 @@ static address generate_dilithiumDecomposePoly_avx512(StubGenerator *stubgen, const Register rMultiplier = c_rarg3; // arg3 is already consumed, reused here __ movptr(rMultiplier, multiplier_mem); #endif - __ evpbroadcastd(barrettMultiplier, rMultiplier, - Assembler::AVX_512bit); // multiplier for mod 2 * gamma2 reduce + if (vector_len == Assembler::AVX_512bit) { + __ evpbroadcastd(barrettMultiplier, rMultiplier, + vector_len); // multiplier for mod 2 * gamma2 reduce + } else { + __ movdl(barrettMultiplier, rMultiplier); + __ vpbroadcastd(barrettMultiplier, barrettMultiplier, vector_len); + } - __ evpsubd(qMinus1, k0, dilithium_q, one, false, Assembler::AVX_512bit); // q - 1 - __ evpsrad(gamma2, k0, twoGamma2, 1, false, Assembler::AVX_512bit); // gamma2 + // Total payload is 1024 bytes + int memStep = 4 * 64; // Number of bytes per loop iteration + int regCnt = 4; // Register array length + if (vector_len == Assembler::AVX_256bit) { + memStep = 2 * 32; + regCnt = 2; + } __ movl(len, 1024); __ align(OptoLoopAlignment); __ BIND(L_loop); - load4Xmms(xmm0_3, input, 0, _masm); + loadXmms(RPlus, input, 0, vector_len, _masm); - __ addptr(input, 4 * XMMBYTES); + __ addptr(input, memStep); - // rplus in xmm0 // rplus = rplus - ((rplus + 5373807) >> 23) * dilithium_q; - __ evpaddd(xmm4, k0, xmm0, barrettAddend, false, Assembler::AVX_512bit); - __ evpaddd(xmm5, k0, xmm1, barrettAddend, false, Assembler::AVX_512bit); - __ evpaddd(xmm6, k0, xmm2, barrettAddend, false, Assembler::AVX_512bit); - __ evpaddd(xmm7, k0, xmm3, barrettAddend, false, Assembler::AVX_512bit); - - __ evpsrad(xmm4, k0, xmm4, 23, false, Assembler::AVX_512bit); - __ evpsrad(xmm5, k0, xmm5, 23, false, Assembler::AVX_512bit); - __ evpsrad(xmm6, k0, xmm6, 23, false, Assembler::AVX_512bit); - __ evpsrad(xmm7, k0, xmm7, 23, false, Assembler::AVX_512bit); - - __ evpmulld(xmm4, k0, xmm4, dilithium_q, false, Assembler::AVX_512bit); - __ evpmulld(xmm5, k0, xmm5, dilithium_q, false, Assembler::AVX_512bit); - __ evpmulld(xmm6, k0, xmm6, dilithium_q, false, Assembler::AVX_512bit); - __ evpmulld(xmm7, k0, xmm7, dilithium_q, false, Assembler::AVX_512bit); - - __ evpsubd(xmm0, k0, xmm0, xmm4, false, Assembler::AVX_512bit); - __ evpsubd(xmm1, k0, xmm1, xmm5, false, Assembler::AVX_512bit); - __ evpsubd(xmm2, k0, xmm2, xmm6, false, Assembler::AVX_512bit); - __ evpsubd(xmm3, k0, xmm3, xmm7, false, Assembler::AVX_512bit); - // rplus in xmm0 + for (int i = 0; i < regCnt; i++) { + __ vpaddd(Tmp1[i], RPlus[i], barrettAddend, vector_len); + } + + for (int i = 0; i < regCnt; i++) { + __ vpsrad(Tmp1[i], Tmp1[i], 23, vector_len); + } + + for (int i = 0; i < regCnt; i++) { + __ vpmulld(Tmp1[i], Tmp1[i], dilithium_q, vector_len); + } + + for (int i = 0; i < regCnt; i++) { + __ vpsubd(RPlus[i], RPlus[i], Tmp1[i], vector_len); + } + // rplus = rplus + ((rplus >> 31) & dilithium_q); - __ evpsrad(xmm4, k0, xmm0, 31, false, Assembler::AVX_512bit); - __ evpsrad(xmm5, k0, xmm1, 31, false, Assembler::AVX_512bit); - __ evpsrad(xmm6, k0, xmm2, 31, false, Assembler::AVX_512bit); - __ evpsrad(xmm7, k0, xmm3, 31, false, Assembler::AVX_512bit); - - __ evpandd(xmm4, k0, xmm4, dilithium_q, false, Assembler::AVX_512bit); - __ evpandd(xmm5, k0, xmm5, dilithium_q, false, Assembler::AVX_512bit); - __ evpandd(xmm6, k0, xmm6, dilithium_q, false, Assembler::AVX_512bit); - __ evpandd(xmm7, k0, xmm7, dilithium_q, false, Assembler::AVX_512bit); - - __ evpaddd(xmm0, k0, xmm0, xmm4, false, Assembler::AVX_512bit); - __ evpaddd(xmm1, k0, xmm1, xmm5, false, Assembler::AVX_512bit); - __ evpaddd(xmm2, k0, xmm2, xmm6, false, Assembler::AVX_512bit); - __ evpaddd(xmm3, k0, xmm3, xmm7, false, Assembler::AVX_512bit); - // rplus in xmm0 + for (int i = 0; i < regCnt; i++) { + __ vpsrad(Tmp1[i], RPlus[i], 31, vector_len); + } + + for (int i = 0; i < regCnt; i++) { + __ vpand(Tmp1[i], Tmp1[i], dilithium_q, vector_len); + } + + for (int i = 0; i < regCnt; i++) { + __ vpaddd(RPlus[i], RPlus[i], Tmp1[i], vector_len); + } + // int quotient = (rplus * barrettMultiplier) >> 22; - __ evpmulld(xmm4, k0, xmm0, barrettMultiplier, false, Assembler::AVX_512bit); - __ evpmulld(xmm5, k0, xmm1, barrettMultiplier, false, Assembler::AVX_512bit); - __ evpmulld(xmm6, k0, xmm2, barrettMultiplier, false, Assembler::AVX_512bit); - __ evpmulld(xmm7, k0, xmm3, barrettMultiplier, false, Assembler::AVX_512bit); - - __ evpsrad(xmm4, k0, xmm4, 22, false, Assembler::AVX_512bit); - __ evpsrad(xmm5, k0, xmm5, 22, false, Assembler::AVX_512bit); - __ evpsrad(xmm6, k0, xmm6, 22, false, Assembler::AVX_512bit); - __ evpsrad(xmm7, k0, xmm7, 22, false, Assembler::AVX_512bit); - // quotient in xmm4 + for (int i = 0; i < regCnt; i++) { + __ vpmulld(Quotient[i], RPlus[i], barrettMultiplier, vector_len); + } + + for (int i = 0; i < regCnt; i++) { + __ vpsrad(Quotient[i], Quotient[i], 22, vector_len); + } + // int r0 = rplus - quotient * twoGamma2; - __ evpmulld(xmm8, k0, xmm4, twoGamma2, false, Assembler::AVX_512bit); - __ evpmulld(xmm9, k0, xmm5, twoGamma2, false, Assembler::AVX_512bit); - __ evpmulld(xmm10, k0, xmm6, twoGamma2, false, Assembler::AVX_512bit); - __ evpmulld(xmm11, k0, xmm7, twoGamma2, false, Assembler::AVX_512bit); - - __ evpsubd(xmm8, k0, xmm0, xmm8, false, Assembler::AVX_512bit); - __ evpsubd(xmm9, k0, xmm1, xmm9, false, Assembler::AVX_512bit); - __ evpsubd(xmm10, k0, xmm2, xmm10, false, Assembler::AVX_512bit); - __ evpsubd(xmm11, k0, xmm3, xmm11, false, Assembler::AVX_512bit); - // r0 in xmm8 + for (int i = 0; i < regCnt; i++) { + __ vpmulld(R0[i], Quotient[i], twoGamma2, vector_len); + } + + for (int i = 0; i < regCnt; i++) { + __ vpsubd(R0[i], RPlus[i], R0[i], vector_len); + } + // int mask = (twoGamma2 - r0) >> 22; - __ evpsubd(xmm12, k0, twoGamma2, xmm8, false, Assembler::AVX_512bit); - __ evpsubd(xmm13, k0, twoGamma2, xmm9, false, Assembler::AVX_512bit); - __ evpsubd(xmm14, k0, twoGamma2, xmm10, false, Assembler::AVX_512bit); - __ evpsubd(xmm15, k0, twoGamma2, xmm11, false, Assembler::AVX_512bit); - - __ evpsrad(xmm12, k0, xmm12, 22, false, Assembler::AVX_512bit); - __ evpsrad(xmm13, k0, xmm13, 22, false, Assembler::AVX_512bit); - __ evpsrad(xmm14, k0, xmm14, 22, false, Assembler::AVX_512bit); - __ evpsrad(xmm15, k0, xmm15, 22, false, Assembler::AVX_512bit); - // mask in xmm12 + for (int i = 0; i < regCnt; i++) { + __ vpsubd(Mask[i], twoGamma2, R0[i], vector_len); + } + + for (int i = 0; i < regCnt; i++) { + __ vpsrad(Mask[i], Mask[i], 22, vector_len); + } + // r0 -= (mask & twoGamma2); - __ evpandd(xmm16, k0, xmm12, twoGamma2, false, Assembler::AVX_512bit); - __ evpandd(xmm17, k0, xmm13, twoGamma2, false, Assembler::AVX_512bit); - __ evpandd(xmm18, k0, xmm14, twoGamma2, false, Assembler::AVX_512bit); - __ evpandd(xmm19, k0, xmm15, twoGamma2, false, Assembler::AVX_512bit); - - __ evpsubd(xmm8, k0, xmm8, xmm16, false, Assembler::AVX_512bit); - __ evpsubd(xmm9, k0, xmm9, xmm17, false, Assembler::AVX_512bit); - __ evpsubd(xmm10, k0, xmm10, xmm18, false, Assembler::AVX_512bit); - __ evpsubd(xmm11, k0, xmm11, xmm19, false, Assembler::AVX_512bit); - // r0 in xmm8 + for (int i = 0; i < regCnt; i++) { + __ vpand(Tmp1[i], Mask[i], twoGamma2, vector_len); + } + + for (int i = 0; i < regCnt; i++) { + __ vpsubd(R0[i], R0[i], Tmp1[i], vector_len); + } + // quotient += (mask & 1); - __ evpandd(xmm16, k0, xmm12, one, false, Assembler::AVX_512bit); - __ evpandd(xmm17, k0, xmm13, one, false, Assembler::AVX_512bit); - __ evpandd(xmm18, k0, xmm14, one, false, Assembler::AVX_512bit); - __ evpandd(xmm19, k0, xmm15, one, false, Assembler::AVX_512bit); + for (int i = 0; i < regCnt; i++) { + __ vpand(Tmp1[i], Mask[i], one, vector_len); + } - __ evpaddd(xmm4, k0, xmm4, xmm16, false, Assembler::AVX_512bit); - __ evpaddd(xmm5, k0, xmm5, xmm17, false, Assembler::AVX_512bit); - __ evpaddd(xmm6, k0, xmm6, xmm18, false, Assembler::AVX_512bit); - __ evpaddd(xmm7, k0, xmm7, xmm19, false, Assembler::AVX_512bit); + for (int i = 0; i < regCnt; i++) { + __ vpaddd(Quotient[i], Quotient[i], Tmp1[i], vector_len); + } // mask = (twoGamma2 / 2 - r0) >> 31; - __ evpsubd(xmm12, k0, gamma2, xmm8, false, Assembler::AVX_512bit); - __ evpsubd(xmm13, k0, gamma2, xmm9, false, Assembler::AVX_512bit); - __ evpsubd(xmm14, k0, gamma2, xmm10, false, Assembler::AVX_512bit); - __ evpsubd(xmm15, k0, gamma2, xmm11, false, Assembler::AVX_512bit); + for (int i = 0; i < regCnt; i++) { + __ vpsubd(Mask[i], gamma2, R0[i], vector_len); + } - __ evpsrad(xmm12, k0, xmm12, 31, false, Assembler::AVX_512bit); - __ evpsrad(xmm13, k0, xmm13, 31, false, Assembler::AVX_512bit); - __ evpsrad(xmm14, k0, xmm14, 31, false, Assembler::AVX_512bit); - __ evpsrad(xmm15, k0, xmm15, 31, false, Assembler::AVX_512bit); + for (int i = 0; i < regCnt; i++) { + __ vpsrad(Mask[i], Mask[i], 31, vector_len); + } // r0 -= (mask & twoGamma2); - __ evpandd(xmm16, k0, xmm12, twoGamma2, false, Assembler::AVX_512bit); - __ evpandd(xmm17, k0, xmm13, twoGamma2, false, Assembler::AVX_512bit); - __ evpandd(xmm18, k0, xmm14, twoGamma2, false, Assembler::AVX_512bit); - __ evpandd(xmm19, k0, xmm15, twoGamma2, false, Assembler::AVX_512bit); - - __ evpsubd(xmm8, k0, xmm8, xmm16, false, Assembler::AVX_512bit); - __ evpsubd(xmm9, k0, xmm9, xmm17, false, Assembler::AVX_512bit); - __ evpsubd(xmm10, k0, xmm10, xmm18, false, Assembler::AVX_512bit); - __ evpsubd(xmm11, k0, xmm11, xmm19, false, Assembler::AVX_512bit); - // r0 in xmm8 + for (int i = 0; i < regCnt; i++) { + __ vpand(Tmp1[i], Mask[i], twoGamma2, vector_len); + } + + for (int i = 0; i < regCnt; i++) { + __ vpsubd(R0[i], R0[i], Tmp1[i], vector_len); + } + // quotient += (mask & 1); - __ evpandd(xmm16, k0, xmm12, one, false, Assembler::AVX_512bit); - __ evpandd(xmm17, k0, xmm13, one, false, Assembler::AVX_512bit); - __ evpandd(xmm18, k0, xmm14, one, false, Assembler::AVX_512bit); - __ evpandd(xmm19, k0, xmm15, one, false, Assembler::AVX_512bit); - - __ evpaddd(xmm4, k0, xmm4, xmm16, false, Assembler::AVX_512bit); - __ evpaddd(xmm5, k0, xmm5, xmm17, false, Assembler::AVX_512bit); - __ evpaddd(xmm6, k0, xmm6, xmm18, false, Assembler::AVX_512bit); - __ evpaddd(xmm7, k0, xmm7, xmm19, false, Assembler::AVX_512bit); - // quotient in xmm4 + for (int i = 0; i < regCnt; i++) { + __ vpand(Tmp1[i], Mask[i], one, vector_len); + } + + for (int i = 0; i < regCnt; i++) { + __ vpaddd(Quotient[i], Quotient[i], Tmp1[i], vector_len); + } + // r1 in RPlus // int r1 = rplus - r0 - (dilithium_q - 1); - __ evpsubd(xmm16, k0, xmm0, xmm8, false, Assembler::AVX_512bit); - __ evpsubd(xmm17, k0, xmm1, xmm9, false, Assembler::AVX_512bit); - __ evpsubd(xmm18, k0, xmm2, xmm10, false, Assembler::AVX_512bit); - __ evpsubd(xmm19, k0, xmm3, xmm11, false, Assembler::AVX_512bit); - - __ evpsubd(xmm16, k0, xmm16, xmm26, false, Assembler::AVX_512bit); - __ evpsubd(xmm17, k0, xmm17, xmm26, false, Assembler::AVX_512bit); - __ evpsubd(xmm18, k0, xmm18, xmm26, false, Assembler::AVX_512bit); - __ evpsubd(xmm19, k0, xmm19, xmm26, false, Assembler::AVX_512bit); - // r1 in xmm16 // r1 = (r1 | (-r1)) >> 31; // 0 if rplus - r0 == (dilithium_q - 1), -1 otherwise - __ evpsubd(xmm20, k0, zero, xmm16, false, Assembler::AVX_512bit); - __ evpsubd(xmm21, k0, zero, xmm17, false, Assembler::AVX_512bit); - __ evpsubd(xmm22, k0, zero, xmm18, false, Assembler::AVX_512bit); - __ evpsubd(xmm23, k0, zero, xmm19, false, Assembler::AVX_512bit); - - __ evporq(xmm16, k0, xmm16, xmm20, false, Assembler::AVX_512bit); - __ evporq(xmm17, k0, xmm17, xmm21, false, Assembler::AVX_512bit); - __ evporq(xmm18, k0, xmm18, xmm22, false, Assembler::AVX_512bit); - __ evporq(xmm19, k0, xmm19, xmm23, false, Assembler::AVX_512bit); - - __ evpsubd(xmm12, k0, zero, one, false, Assembler::AVX_512bit); // -1 - - __ evpsrad(xmm0, k0, xmm16, 31, false, Assembler::AVX_512bit); - __ evpsrad(xmm1, k0, xmm17, 31, false, Assembler::AVX_512bit); - __ evpsrad(xmm2, k0, xmm18, 31, false, Assembler::AVX_512bit); - __ evpsrad(xmm3, k0, xmm19, 31, false, Assembler::AVX_512bit); - // r1 in xmm0 - // r0 += ~r1; - __ evpxorq(xmm20, k0, xmm0, xmm12, false, Assembler::AVX_512bit); - __ evpxorq(xmm21, k0, xmm1, xmm12, false, Assembler::AVX_512bit); - __ evpxorq(xmm22, k0, xmm2, xmm12, false, Assembler::AVX_512bit); - __ evpxorq(xmm23, k0, xmm3, xmm12, false, Assembler::AVX_512bit); - - __ evpaddd(xmm8, k0, xmm8, xmm20, false, Assembler::AVX_512bit); - __ evpaddd(xmm9, k0, xmm9, xmm21, false, Assembler::AVX_512bit); - __ evpaddd(xmm10, k0, xmm10, xmm22, false, Assembler::AVX_512bit); - __ evpaddd(xmm11, k0, xmm11, xmm23, false, Assembler::AVX_512bit); - // r0 in xmm8 - // r1 = r1 & quotient; - __ evpandd(xmm0, k0, xmm4, xmm0, false, Assembler::AVX_512bit); - __ evpandd(xmm1, k0, xmm5, xmm1, false, Assembler::AVX_512bit); - __ evpandd(xmm2, k0, xmm6, xmm2, false, Assembler::AVX_512bit); - __ evpandd(xmm3, k0, xmm7, xmm3, false, Assembler::AVX_512bit); - // r1 in xmm0 + for (int i = 0; i < regCnt; i++) { + __ vpsubd(RPlus[i], RPlus[i], R0[i], vector_len); + } + + if (vector_len == Assembler::AVX_512bit) { + KRegister EqMsk[] = {k1, k2, k3, k4}; + for (int i = 0; i < regCnt; i++) { + __ evpcmpeqd(EqMsk[i], k0, RPlus[i], qMinus1, vector_len); + } + + // r0 += ~r1; // add -1 or keep as is, using EqMsk as filter + for (int i = 0; i < regCnt; i++) { + __ evpaddd(R0[i], EqMsk[i], R0[i], minusOne, true, vector_len); + } + + // r1 in Quotient + // r1 = r1 & quotient; // copy 0 or keep as is, using EqMsk as filter + for (int i = 0; i < regCnt; i++) { + __ evpandd(Quotient[i], EqMsk[i], Quotient[i], zero, true, vector_len); + } + } else { + const XMMRegister qMinus1 = Tmp1[0]; + __ vpsubd(qMinus1, dilithium_q, one, vector_len); // q - 1 + + for (int i = 0; i < regCnt; i++) { + __ vpcmpeqd(Mask[i], RPlus[i], qMinus1, vector_len); + } + + // r0 += ~r1; + // Mask already negated + for (int i = 0; i < regCnt; i++) { + __ vpaddd(R0[i], R0[i], Mask[i], vector_len); + } + + // r1 in Quotient + // r1 = r1 & quotient; + for (int i = 0; i < regCnt; i++) { + __ vpandn(Quotient[i], Mask[i], Quotient[i], vector_len); + } + } + + // r1 in Quotient // lowPart[m] = r0; // highPart[m] = r1; - store4Xmms(highPart, 0, xmm0_3, _masm); - store4Xmms(lowPart, 0, xmm8_11, _masm); + storeXmms(highPart, 0, Quotient, vector_len, _masm); + storeXmms(lowPart, 0, R0, vector_len, _masm); - __ addptr(highPart, 4 * XMMBYTES); - __ addptr(lowPart, 4 * XMMBYTES); - __ subl(len, 4 * XMMBYTES); + __ addptr(highPart, memStep); + __ addptr(lowPart, memStep); + __ subl(len, memStep); __ jcc(Assembler::notEqual, L_loop); __ leave(); // required for proper stackwalking of RuntimeStub frame @@ -1018,17 +1322,21 @@ static address generate_dilithiumDecomposePoly_avx512(StubGenerator *stubgen, } void StubGenerator::generate_dilithium_stubs() { + int vector_len = Assembler::AVX_256bit; + if (VM_Version::supports_evex() && VM_Version::supports_avx512bw()) { + vector_len = Assembler::AVX_512bit; + } // Generate Dilithium intrinsics code if (UseDilithiumIntrinsics) { - StubRoutines::_dilithiumAlmostNtt = - generate_dilithiumAlmostNtt_avx512(this, _masm); - StubRoutines::_dilithiumAlmostInverseNtt = - generate_dilithiumAlmostInverseNtt_avx512(this, _masm); - StubRoutines::_dilithiumNttMult = - generate_dilithiumNttMult_avx512(this, _masm); - StubRoutines::_dilithiumMontMulByConstant = - generate_dilithiumMontMulByConstant_avx512(this, _masm); - StubRoutines::_dilithiumDecomposePoly = - generate_dilithiumDecomposePoly_avx512(this, _masm); + StubRoutines::_dilithiumAlmostNtt = + generate_dilithiumAlmostNtt_avx(this, vector_len, _masm); + StubRoutines::_dilithiumAlmostInverseNtt = + generate_dilithiumAlmostInverseNtt_avx(this, vector_len, _masm); + StubRoutines::_dilithiumNttMult = + generate_dilithiumNttMult_avx(this, vector_len, _masm); + StubRoutines::_dilithiumMontMulByConstant = + generate_dilithiumMontMulByConstant_avx(this, vector_len, _masm); + StubRoutines::_dilithiumDecomposePoly = + generate_dilithiumDecomposePoly_avx(this, vector_len, _masm); } } diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 4961aed61c3..747daefd51d 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -1271,8 +1271,7 @@ void VM_Version::get_processor_features() { } // Dilithium Intrinsics - // Currently we only have them for AVX512 - if (supports_evex() && supports_avx512bw()) { + if (UseAVX > 1) { if (FLAG_IS_DEFAULT(UseDilithiumIntrinsics)) { UseDilithiumIntrinsics = true; } diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index a9748617e1f..be9889b0a99 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -2767,21 +2767,11 @@ class HandlerImpl { public: - static int emit_exception_handler(C2_MacroAssembler *masm); static int emit_deopt_handler(C2_MacroAssembler* masm); - static uint size_exception_handler() { - // NativeCall instruction size is the same as NativeJump. - // exception handler starts out as jump and can be patched to - // a call be deoptimization. (4932387) - // Note that this value is also credited (in output.cpp) to - // the size of the code section. - return NativeJump::instruction_size; - } - static uint size_deopt_handler() { - // three 5 byte instructions plus one move for unreachable address. - return 15+3; + // one call and one jmp. + return 7; } }; @@ -2873,24 +2863,6 @@ int MachNode::compute_padding(int current_offset) const { } } -// Emit exception handler code. -// Stuff framesize into a register and call a VM stub routine. -int HandlerImpl::emit_exception_handler(C2_MacroAssembler* masm) { - - // Note that the code buffer's insts_mark is always relative to insts. - // That's why we must use the macroassembler to generate a handler. - address base = __ start_a_stub(size_exception_handler()); - if (base == nullptr) { - ciEnv::current()->record_failure("CodeCache is full"); - return 0; // CodeBuffer::expand failed - } - int offset = __ offset(); - __ jump(RuntimeAddress(OptoRuntime::exception_blob()->entry_point())); - assert(__ offset() - offset <= (int) size_exception_handler(), "overflow"); - __ end_a_stub(); - return offset; -} - // Emit deopt handler code. int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm) { @@ -2903,21 +2875,20 @@ int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm) { } int offset = __ offset(); - address the_pc = (address) __ pc(); - Label next; - // push a "the_pc" on the stack without destroying any registers - // as they all may be live. + Label start; + __ bind(start); + + __ call(RuntimeAddress(SharedRuntime::deopt_blob()->unpack())); - // push address of "next" - __ call(next, relocInfo::none); // reloc none is fine since it is a disp32 - __ bind(next); - // adjust it so it matches "the_pc" - __ subptr(Address(rsp, 0), __ offset() - offset); + int entry_offset = __ offset(); + + __ jmp(start); - __ jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack())); assert(__ offset() - offset <= (int) size_deopt_handler(), "overflow %d", (__ offset() - offset)); + assert(__ offset() - entry_offset >= NativePostCallNop::first_check_size, + "out of bounds read in post-call NOP check"); __ end_a_stub(); - return offset; + return entry_offset; } static Assembler::Width widthForType(BasicType bt) { diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index 5f81912c0d6..48bd5e05816 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -1747,6 +1747,9 @@ size_t os::pd_pretouch_memory(void* first, void* last, size_t page_size) { return page_size; } +void os::numa_set_thread_affinity(Thread *thread, int node) { +} + void os::numa_make_global(char *addr, size_t bytes) { } diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 3e5fa8b84e1..0b37cb100f6 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -1581,6 +1581,9 @@ size_t os::pd_pretouch_memory(void* first, void* last, size_t page_size) { return page_size; } +void os::numa_set_thread_affinity(Thread *thread, int node) { +} + void os::numa_make_global(char *addr, size_t bytes) { } diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp index 8aeb64ef18c..f556bc57f26 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp @@ -209,14 +209,14 @@ class CgroupV1Subsystem: public CgroupSubsystem { bool pids_max(uint64_t& result) override; bool pids_current(uint64_t& result) override; - bool is_containerized(); + bool is_containerized() override; - const char * container_type() { + const char * container_type() override { return "cgroupv1"; } - CachingCgroupController* memory_controller() { return _memory; } - CachingCgroupController* cpu_controller() { return _cpu; } - CgroupCpuacctController* cpuacct_controller() { return _cpuacct; } + CachingCgroupController* memory_controller() override { return _memory; } + CachingCgroupController* cpu_controller() override { return _cpu; } + CgroupCpuacctController* cpuacct_controller() override { return _cpuacct; } private: /* controllers */ diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index a345663dd5b..efeeec6f484 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -1770,7 +1770,9 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) { {EM_LOONGARCH, EM_LOONGARCH, ELFCLASS64, ELFDATA2LSB, (char*)"LoongArch"}, }; -#if (defined AMD64) +#if (defined IA32) + static Elf32_Half running_arch_code=EM_386; +#elif (defined AMD64) || (defined X32) static Elf32_Half running_arch_code=EM_X86_64; #elif (defined __sparc) && (defined _LP64) static Elf32_Half running_arch_code=EM_SPARCV9; @@ -1804,7 +1806,7 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) { static Elf32_Half running_arch_code=EM_LOONGARCH; #else #error Method os::dll_load requires that one of following is defined:\ - AARCH64, ALPHA, ARM, AMD64, LOONGARCH64, M68K, MIPS, MIPSEL, PARISC, __powerpc__, __powerpc64__, RISCV, S390, SH, __sparc + AARCH64, ALPHA, ARM, AMD64, IA32, LOONGARCH64, M68K, MIPS, MIPSEL, PARISC, __powerpc__, __powerpc64__, RISCV, S390, SH, __sparc #endif // Identify compatibility class for VM's architecture and library's architecture @@ -1866,6 +1868,7 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) { } void * os::Linux::dlopen_helper(const char *filename, char *ebuf, int ebuflen) { +#ifndef IA32 bool ieee_handling = IEEE_subnormal_handling_OK(); if (!ieee_handling) { Events::log_dll_message(nullptr, "IEEE subnormal handling check failed before loading %s", filename); @@ -1888,9 +1891,14 @@ void * os::Linux::dlopen_helper(const char *filename, char *ebuf, int ebuflen) { // numerical "accuracy", but we need to protect Java semantics first // and foremost. See JDK-8295159. + // This workaround is ineffective on IA32 systems because the MXCSR + // register (which controls flush-to-zero mode) is not stored in the + // legacy fenv. + fenv_t default_fenv; int rtn = fegetenv(&default_fenv); assert(rtn == 0, "fegetenv must succeed"); +#endif // IA32 void* result; JFR_ONLY(NativeLibraryLoadEvent load_event(filename, &result);) @@ -1910,6 +1918,7 @@ void * os::Linux::dlopen_helper(const char *filename, char *ebuf, int ebuflen) { } else { Events::log_dll_message(nullptr, "Loaded shared library %s", filename); log_info(os)("shared library load of %s was successful", filename); +#ifndef IA32 // Quickly test to make sure subnormals are correctly handled. if (! IEEE_subnormal_handling_OK()) { // We just dlopen()ed a library that mangled the floating-point flags. @@ -1935,6 +1944,7 @@ void * os::Linux::dlopen_helper(const char *filename, char *ebuf, int ebuflen) { assert(false, "fesetenv didn't work"); } } +#endif // IA32 } return result; } @@ -2433,6 +2443,7 @@ void os::Linux::print_uptime_info(outputStream* st) { if (ret == 0) { os::print_dhm(st, "OS uptime:", (long) sinfo.uptime); } + assert(ret == 0, "sysinfo failed: %s", os::strerror(errno)); } bool os::Linux::print_container_info(outputStream* st) { @@ -2597,7 +2608,8 @@ void os::print_memory_info(outputStream* st) { // values in struct sysinfo are "unsigned long" struct sysinfo si; - sysinfo(&si); + int ret = sysinfo(&si); + assert(ret == 0, "sysinfo failed: %s", os::strerror(errno)); physical_memory_size_type phys_mem = physical_memory(); st->print(", physical " PHYS_MEM_TYPE_FORMAT "k", phys_mem >> 10); @@ -2605,10 +2617,12 @@ void os::print_memory_info(outputStream* st) { (void)os::available_memory(avail_mem); st->print("(" PHYS_MEM_TYPE_FORMAT "k free)", avail_mem >> 10); - st->print(", swap " UINT64_FORMAT "k", - ((jlong)si.totalswap * si.mem_unit) >> 10); - st->print("(" UINT64_FORMAT "k free)", - ((jlong)si.freeswap * si.mem_unit) >> 10); + if (ret == 0) { + st->print(", swap " UINT64_FORMAT "k", + ((jlong)si.totalswap * si.mem_unit) >> 10); + st->print("(" UINT64_FORMAT "k free)", + ((jlong)si.freeswap * si.mem_unit) >> 10); + } st->cr(); st->print("Page Sizes: "); _page_sizes.print_on(st); @@ -2991,6 +3005,10 @@ size_t os::pd_pretouch_memory(void* first, void* last, size_t page_size) { return page_size; } +void os::numa_set_thread_affinity(Thread* thread, int node) { + Linux::numa_set_thread_affinity(thread->osthread()->thread_id(), node); +} + void os::numa_make_global(char *addr, size_t bytes) { Linux::numa_interleave_memory(addr, bytes); } @@ -3173,6 +3191,8 @@ bool os::Linux::libnuma_init() { libnuma_dlsym(handle, "numa_set_bind_policy"))); set_numa_bitmask_isbitset(CAST_TO_FN_PTR(numa_bitmask_isbitset_func_t, libnuma_dlsym(handle, "numa_bitmask_isbitset"))); + set_numa_bitmask_clearbit(CAST_TO_FN_PTR(numa_bitmask_clearbit_func_t, + libnuma_dlsym(handle, "numa_bitmask_clearbit"))); set_numa_bitmask_equal(CAST_TO_FN_PTR(numa_bitmask_equal_func_t, libnuma_dlsym(handle, "numa_bitmask_equal"))); set_numa_distance(CAST_TO_FN_PTR(numa_distance_func_t, @@ -3187,20 +3207,32 @@ bool os::Linux::libnuma_init() { libnuma_dlsym(handle, "numa_set_preferred"))); set_numa_get_run_node_mask(CAST_TO_FN_PTR(numa_get_run_node_mask_func_t, libnuma_v2_dlsym(handle, "numa_get_run_node_mask"))); + set_numa_sched_setaffinity(CAST_TO_FN_PTR(numa_sched_setaffinity_func_t, + libnuma_v2_dlsym(handle, "numa_sched_setaffinity"))); + set_numa_allocate_cpumask(CAST_TO_FN_PTR(numa_allocate_cpumask_func_t, + libnuma_v2_dlsym(handle, "numa_allocate_cpumask"))); if (numa_available() != -1) { set_numa_all_nodes((unsigned long*)libnuma_dlsym(handle, "numa_all_nodes")); set_numa_all_nodes_ptr((struct bitmask **)libnuma_dlsym(handle, "numa_all_nodes_ptr")); set_numa_nodes_ptr((struct bitmask **)libnuma_dlsym(handle, "numa_nodes_ptr")); + set_numa_all_cpus_ptr((struct bitmask **)libnuma_dlsym(handle, "numa_all_cpus_ptr")); set_numa_interleave_bitmask(_numa_get_interleave_mask()); set_numa_membind_bitmask(_numa_get_membind()); set_numa_cpunodebind_bitmask(_numa_get_run_node_mask()); + // Create an index -> node mapping, since nodes are not always consecutive _nindex_to_node = new (mtInternal) GrowableArray(0, mtInternal); rebuild_nindex_to_node_map(); + // Create a cpu -> node mapping _cpu_to_node = new (mtInternal) GrowableArray(0, mtInternal); rebuild_cpu_to_node_map(); + + // Create a node -> CPUs mapping + _numa_affinity_masks = new (mtInternal) GrowableArray(0, mtInternal); + build_numa_affinity_masks(); + return true; } } @@ -3236,6 +3268,42 @@ size_t os::Linux::default_guard_size(os::ThreadType thr_type) { return ((thr_type == java_thread || thr_type == compiler_thread) ? 0 : os::vm_page_size()); } +void os::Linux::build_numa_affinity_masks() { + // We only build the affinity masks if running libnuma v2 (_numa_node_to_cpus_v2 + // is available) and we have the affinity mask of the process when it started. + if (_numa_node_to_cpus_v2 == nullptr || _numa_all_cpus_ptr == nullptr) { + return; + } + + // It's important that we respect any user configuration by removing the + // CPUs we're not allowed to run on from the affinity mask. For example, + // if the user runs the JVM with "numactl -C 0-1,4-5" on a machine with + // the following NUMA setup: + // NUMA 0: CPUs 0-3, NUMA 1: CPUs 4-7 + // We expect to get the following affinity masks: + // Affinity masks: idx 0 = (0, 1), idx 1 = (4, 5) + + const int num_nodes = get_existing_num_nodes(); + const unsigned num_cpus = (unsigned)os::processor_count(); + + for (int i = 0; i < num_nodes; i++) { + struct bitmask* affinity_mask = _numa_allocate_cpumask(); + + // Fill the affinity mask with all CPUs belonging to NUMA node i + _numa_node_to_cpus_v2(i, affinity_mask); + + // Clear the bits of all CPUs that the process is not allowed to + // execute tasks on + for (unsigned j = 0; j < num_cpus; j++) { + if (!_numa_bitmask_isbitset(_numa_all_cpus_ptr, j)) { + _numa_bitmask_clearbit(affinity_mask, j); + } + } + + _numa_affinity_masks->push(affinity_mask); + } +} + void os::Linux::rebuild_nindex_to_node_map() { int highest_node_number = Linux::numa_max_node(); @@ -3351,6 +3419,25 @@ int os::Linux::numa_node_to_cpus(int node, unsigned long *buffer, int bufferlen) return -1; } +void os::Linux::numa_set_thread_affinity(pid_t tid, int node) { + // We only set affinity if running libnuma v2 (_numa_sched_setaffinity + // is available) and we have all affinity mask + if (_numa_sched_setaffinity == nullptr || + _numa_all_cpus_ptr == nullptr || + _numa_affinity_masks->is_empty()) { + return; + } + + if (node == -1) { + // If the node is -1, the affinity is reverted to the original affinity + // of the thread when the VM was started + _numa_sched_setaffinity(tid, _numa_all_cpus_ptr); + } else { + // Normal case, set the affinity to the corresponding affinity mask + _numa_sched_setaffinity(tid, _numa_affinity_masks->at(node)); + } +} + int os::Linux::get_node_by_cpu(int cpu_id) { if (cpu_to_node() != nullptr && cpu_id >= 0 && cpu_id < cpu_to_node()->length()) { return cpu_to_node()->at(cpu_id); @@ -3360,6 +3447,7 @@ int os::Linux::get_node_by_cpu(int cpu_id) { GrowableArray* os::Linux::_cpu_to_node; GrowableArray* os::Linux::_nindex_to_node; +GrowableArray* os::Linux::_numa_affinity_masks; os::Linux::sched_getcpu_func_t os::Linux::_sched_getcpu; os::Linux::numa_node_to_cpus_func_t os::Linux::_numa_node_to_cpus; os::Linux::numa_node_to_cpus_v2_func_t os::Linux::_numa_node_to_cpus_v2; @@ -3371,17 +3459,21 @@ os::Linux::numa_interleave_memory_func_t os::Linux::_numa_interleave_memory; os::Linux::numa_interleave_memory_v2_func_t os::Linux::_numa_interleave_memory_v2; os::Linux::numa_set_bind_policy_func_t os::Linux::_numa_set_bind_policy; os::Linux::numa_bitmask_isbitset_func_t os::Linux::_numa_bitmask_isbitset; +os::Linux::numa_bitmask_clearbit_func_t os::Linux::_numa_bitmask_clearbit; os::Linux::numa_bitmask_equal_func_t os::Linux::_numa_bitmask_equal; os::Linux::numa_distance_func_t os::Linux::_numa_distance; os::Linux::numa_get_membind_func_t os::Linux::_numa_get_membind; os::Linux::numa_get_interleave_mask_func_t os::Linux::_numa_get_interleave_mask; os::Linux::numa_get_run_node_mask_func_t os::Linux::_numa_get_run_node_mask; +os::Linux::numa_sched_setaffinity_func_t os::Linux::_numa_sched_setaffinity; +os::Linux::numa_allocate_cpumask_func_t os::Linux::_numa_allocate_cpumask; os::Linux::numa_move_pages_func_t os::Linux::_numa_move_pages; os::Linux::numa_set_preferred_func_t os::Linux::_numa_set_preferred; os::Linux::NumaAllocationPolicy os::Linux::_current_numa_policy; unsigned long* os::Linux::_numa_all_nodes; struct bitmask* os::Linux::_numa_all_nodes_ptr; struct bitmask* os::Linux::_numa_nodes_ptr; +struct bitmask* os::Linux::_numa_all_cpus_ptr; struct bitmask* os::Linux::_numa_interleave_bitmask; struct bitmask* os::Linux::_numa_membind_bitmask; struct bitmask* os::Linux::_numa_cpunodebind_bitmask; @@ -5071,7 +5163,7 @@ int os::get_core_path(char* buffer, size_t bufferSize) { if (core_pattern[0] == '|') { written = jio_snprintf(buffer, bufferSize, - "\"%s\" (or dumping to %s/core.%d)", + "\"%s\" (alternatively, falling back to %s/core.%d)", &core_pattern[1], p, current_process_id()); } else if (pid_pos != nullptr) { *pid_pos = '\0'; diff --git a/src/hotspot/os/linux/os_linux.hpp b/src/hotspot/os/linux/os_linux.hpp index df96a17d8e9..9c0b6723b38 100644 --- a/src/hotspot/os/linux/os_linux.hpp +++ b/src/hotspot/os/linux/os_linux.hpp @@ -45,6 +45,10 @@ class os::Linux { static GrowableArray* _cpu_to_node; static GrowableArray* _nindex_to_node; + static GrowableArray* _numa_affinity_masks; + + static void build_numa_affinity_masks(); + protected: static physical_memory_size_type _physical_memory; @@ -230,8 +234,11 @@ class os::Linux { typedef void (*numa_set_preferred_func_t)(int node); typedef void (*numa_set_bind_policy_func_t)(int policy); typedef int (*numa_bitmask_isbitset_func_t)(struct bitmask *bmp, unsigned int n); + typedef int (*numa_bitmask_clearbit_func_t)(struct bitmask *bmp, unsigned int n); typedef int (*numa_bitmask_equal_func_t)(struct bitmask *bmp1, struct bitmask *bmp2); typedef int (*numa_distance_func_t)(int node1, int node2); + typedef int (*numa_sched_setaffinity_func_t)(pid_t pid, struct bitmask* mask); + typedef struct bitmask* (*numa_allocate_cpumask_func_t)(void); static sched_getcpu_func_t _sched_getcpu; static numa_node_to_cpus_func_t _numa_node_to_cpus; @@ -244,6 +251,7 @@ class os::Linux { static numa_interleave_memory_v2_func_t _numa_interleave_memory_v2; static numa_set_bind_policy_func_t _numa_set_bind_policy; static numa_bitmask_isbitset_func_t _numa_bitmask_isbitset; + static numa_bitmask_clearbit_func_t _numa_bitmask_clearbit; static numa_bitmask_equal_func_t _numa_bitmask_equal; static numa_distance_func_t _numa_distance; static numa_get_membind_func_t _numa_get_membind; @@ -251,9 +259,12 @@ class os::Linux { static numa_get_interleave_mask_func_t _numa_get_interleave_mask; static numa_move_pages_func_t _numa_move_pages; static numa_set_preferred_func_t _numa_set_preferred; + static numa_sched_setaffinity_func_t _numa_sched_setaffinity; + static numa_allocate_cpumask_func_t _numa_allocate_cpumask; static unsigned long* _numa_all_nodes; static struct bitmask* _numa_all_nodes_ptr; static struct bitmask* _numa_nodes_ptr; + static struct bitmask* _numa_all_cpus_ptr; static struct bitmask* _numa_interleave_bitmask; static struct bitmask* _numa_membind_bitmask; static struct bitmask* _numa_cpunodebind_bitmask; @@ -269,6 +280,7 @@ class os::Linux { static void set_numa_interleave_memory_v2(numa_interleave_memory_v2_func_t func) { _numa_interleave_memory_v2 = func; } static void set_numa_set_bind_policy(numa_set_bind_policy_func_t func) { _numa_set_bind_policy = func; } static void set_numa_bitmask_isbitset(numa_bitmask_isbitset_func_t func) { _numa_bitmask_isbitset = func; } + static void set_numa_bitmask_clearbit(numa_bitmask_clearbit_func_t func) { _numa_bitmask_clearbit = func; } static void set_numa_bitmask_equal(numa_bitmask_equal_func_t func) { _numa_bitmask_equal = func; } static void set_numa_distance(numa_distance_func_t func) { _numa_distance = func; } static void set_numa_get_membind(numa_get_membind_func_t func) { _numa_get_membind = func; } @@ -279,9 +291,12 @@ class os::Linux { static void set_numa_all_nodes(unsigned long* ptr) { _numa_all_nodes = ptr; } static void set_numa_all_nodes_ptr(struct bitmask **ptr) { _numa_all_nodes_ptr = (ptr == nullptr ? nullptr : *ptr); } static void set_numa_nodes_ptr(struct bitmask **ptr) { _numa_nodes_ptr = (ptr == nullptr ? nullptr : *ptr); } + static void set_numa_all_cpus_ptr(struct bitmask **ptr) { _numa_all_cpus_ptr = (ptr == nullptr ? nullptr : *ptr); } static void set_numa_interleave_bitmask(struct bitmask* ptr) { _numa_interleave_bitmask = ptr ; } static void set_numa_membind_bitmask(struct bitmask* ptr) { _numa_membind_bitmask = ptr ; } static void set_numa_cpunodebind_bitmask(struct bitmask* ptr) { _numa_cpunodebind_bitmask = ptr ; } + static void set_numa_sched_setaffinity(numa_sched_setaffinity_func_t func) { _numa_sched_setaffinity = func; } + static void set_numa_allocate_cpumask(numa_allocate_cpumask_func_t func) { _numa_allocate_cpumask = func; } static int sched_getcpu_syscall(void); enum NumaAllocationPolicy{ @@ -292,6 +307,8 @@ class os::Linux { static NumaAllocationPolicy _current_numa_policy; public: + static void numa_set_thread_affinity(pid_t tid, int node); + static int sched_getcpu() { return _sched_getcpu != nullptr ? _sched_getcpu() : -1; } static int numa_node_to_cpus(int node, unsigned long *buffer, int bufferlen); static int numa_max_node() { return _numa_max_node != nullptr ? _numa_max_node() : -1; } diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index 1a04cbba0de..8f1f07dd055 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -108,41 +108,60 @@ size_t os::_os_min_stack_allowed = PTHREAD_STACK_MIN; // Check core dump limit and report possible place where core can be found void os::check_core_dump_prerequisites(char* buffer, size_t bufferSize, bool check_only) { + stringStream buf(buffer, bufferSize); if (!FLAG_IS_DEFAULT(CreateCoredumpOnCrash) && !CreateCoredumpOnCrash) { - jio_snprintf(buffer, bufferSize, "CreateCoredumpOnCrash is disabled from command line"); - VMError::record_coredump_status(buffer, false); + buf.print("CreateCoredumpOnCrash is disabled from command line"); + VMError::record_coredump_status(buf.freeze(), false); } else { struct rlimit rlim; bool success = true; bool warn = true; char core_path[PATH_MAX]; if (get_core_path(core_path, PATH_MAX) <= 0) { - jio_snprintf(buffer, bufferSize, "core.%d (may not exist)", current_process_id()); + // In the warning message, let the user know. + if (check_only) { + buf.print("the core path couldn't be determined. It commonly defaults to "); + } + buf.print("core.%d%s", current_process_id(), check_only ? "" : " (may not exist)"); #ifdef LINUX } else if (core_path[0] == '"') { // redirect to user process - jio_snprintf(buffer, bufferSize, "Core dumps may be processed with %s", core_path); + if (check_only) { + buf.print("core dumps may be further processed by the following: "); + } else { + buf.print("Determined by the following: "); + } + buf.print("%s", core_path); #endif } else if (getrlimit(RLIMIT_CORE, &rlim) != 0) { - jio_snprintf(buffer, bufferSize, "%s (may not exist)", core_path); + if (check_only) { + buf.print("the rlimit couldn't be determined. If resource limits permit, the core dump will be located at "); + } + buf.print("%s%s", core_path, check_only ? "" : " (may not exist)"); } else { switch(rlim.rlim_cur) { case RLIM_INFINITY: - jio_snprintf(buffer, bufferSize, "%s", core_path); + buf.print("%s", core_path); warn = false; break; case 0: - jio_snprintf(buffer, bufferSize, "Core dumps have been disabled. To enable core dumping, try \"ulimit -c unlimited\" before starting Java again"); + buf.print("%s dumps have been disabled. To enable core dumping, try \"ulimit -c unlimited\" before starting Java again", check_only ? "core" : "Core"); success = false; break; default: - jio_snprintf(buffer, bufferSize, "%s (max size " UINT64_FORMAT " k). To ensure a full core dump, try \"ulimit -c unlimited\" before starting Java again", core_path, uint64_t(rlim.rlim_cur) / K); + if (check_only) { + buf.print("core dumps are constrained "); + } else { + buf.print( "%s ", core_path); + } + buf.print( "(max size " UINT64_FORMAT " k). To ensure a full core dump, try \"ulimit -c unlimited\" before starting Java again", uint64_t(rlim.rlim_cur) / K); break; } } + const char* result = buf.freeze(); if (!check_only) { - VMError::record_coredump_status(buffer, success); + VMError::record_coredump_status(result, success); } else if (warn) { - warning("CreateCoredumpOnCrash specified, but %s", buffer); + warning("CreateCoredumpOnCrash specified, but %s", result); } } } diff --git a/src/hotspot/os/posix/signals_posix.cpp b/src/hotspot/os/posix/signals_posix.cpp index 5833e324070..625eb63445a 100644 --- a/src/hotspot/os/posix/signals_posix.cpp +++ b/src/hotspot/os/posix/signals_posix.cpp @@ -621,7 +621,7 @@ int JVM_HANDLE_XXX_SIGNAL(int sig, siginfo_t* info, if (cb != nullptr && cb->is_nmethod()) { nmethod* nm = cb->as_nmethod(); assert(nm->insts_contains_inclusive(pc), ""); - address deopt = nm->deopt_handler_begin(); + address deopt = nm->deopt_handler_entry(); assert(deopt != nullptr, ""); frame fr = os::fetch_frame_from_context(uc); diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index ce2baeaf46c..8a450a291d3 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -2795,7 +2795,7 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) { if (cb != nullptr && cb->is_nmethod()) { nmethod* nm = cb->as_nmethod(); frame fr = os::fetch_frame_from_context((void*)exceptionInfo->ContextRecord); - address deopt = nm->deopt_handler_begin(); + address deopt = nm->deopt_handler_entry(); assert(nm->insts_contains_inclusive(pc), ""); nm->set_original_pc(&fr, pc); // Set pc to handler @@ -3752,6 +3752,7 @@ size_t os::pd_pretouch_memory(void* first, void* last, size_t page_size) { return page_size; } +void os::numa_set_thread_affinity(Thread *thread, int node) { } void os::numa_make_global(char *addr, size_t bytes) { } void os::numa_make_local(char *addr, size_t bytes, int lgrp_hint) { } size_t os::numa_get_groups_num() { return MAX2(numa_node_list_holder.get_count(), 1); } diff --git a/src/hotspot/os_cpu/linux_arm/macroAssembler_linux_arm_32.cpp b/src/hotspot/os_cpu/linux_arm/macroAssembler_linux_arm_32.cpp index e74daaa6d66..e4737191cfc 100644 --- a/src/hotspot/os_cpu/linux_arm/macroAssembler_linux_arm_32.cpp +++ b/src/hotspot/os_cpu/linux_arm/macroAssembler_linux_arm_32.cpp @@ -246,9 +246,9 @@ void MacroAssembler::atomic_cas64(Register memval_lo, Register memval_hi, Regist Label loop; assert_different_registers(memval_lo, memval_hi, result, oldval_lo, oldval_hi, newval_lo, newval_hi, base); - assert(memval_hi == memval_lo + 1 && memval_lo < R9, "cmpxchg_long: illegal registers"); - assert(oldval_hi == oldval_lo + 1 && oldval_lo < R9, "cmpxchg_long: illegal registers"); - assert(newval_hi == newval_lo + 1 && newval_lo < R9, "cmpxchg_long: illegal registers"); + assert(memval_hi == as_Register(memval_lo->encoding() + 1) && memval_lo->encoding() < R9->encoding(), "cmpxchg_long: illegal registers"); + assert(oldval_hi == as_Register(oldval_lo->encoding() + 1) && oldval_lo->encoding() < R9->encoding(), "cmpxchg_long: illegal registers"); + assert(newval_hi == as_Register(newval_lo->encoding() + 1) && newval_lo->encoding() < R9->encoding(), "cmpxchg_long: illegal registers"); assert(result != R10, "cmpxchg_long: illegal registers"); assert(base != R10, "cmpxchg_long: illegal registers"); 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 0799de014a9..35cbb75e8ff 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 @@ -104,11 +104,15 @@ uint32_t VM_Version::cpu_vector_length() { } void VM_Version::RVExtFeatureValue::log_enabled() { - log_debug(os, cpu)("Enabled RV64 feature \"%s\"", pretty()); + log_info(os, cpu)("Enabled RV64 feature \"%s\"", pretty()); +} + +void VM_Version::RVExtFeatureValue::log_disabled(const char* reason) { + log_info(os, cpu)("Disabled RV64 feature \"%s\" (%s)", pretty(), reason); } void VM_Version::RVNonExtFeatureValue::log_enabled() { - log_debug(os, cpu)("Enabled RV64 feature \"%s\" (%ld)", pretty(), value()); + log_info(os, cpu)("Enabled RV64 feature \"%s\" (%ld)", pretty(), value()); } void VM_Version::setup_cpu_available_features() { @@ -193,7 +197,7 @@ void VM_Version::setup_cpu_available_features() { // via PR_RISCV_SCOPE_PER_THREAD, i.e. on VM attach/deattach. int ret = prctl(PR_RISCV_SET_ICACHE_FLUSH_CTX, PR_RISCV_CTX_SW_FENCEI_ON, PR_RISCV_SCOPE_PER_PROCESS); if (ret == 0) { - log_debug(os, cpu)("UseCtxFencei (PR_RISCV_CTX_SW_FENCEI_ON) enabled."); + log_info(os, cpu)("UseCtxFencei (PR_RISCV_CTX_SW_FENCEI_ON) enabled."); } else { FLAG_SET_ERGO(UseCtxFencei, false); log_info(os, cpu)("UseCtxFencei (PR_RISCV_CTX_SW_FENCEI_ON) disabled, unsupported by kernel."); diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.cpp b/src/hotspot/share/cds/aotConstantPoolResolver.cpp index ddf7d32ed70..c4bb26f6fb1 100644 --- a/src/hotspot/share/cds/aotConstantPoolResolver.cpp +++ b/src/hotspot/share/cds/aotConstantPoolResolver.cpp @@ -449,7 +449,7 @@ bool AOTConstantPoolResolver::check_lambda_metafactory_signature(ConstantPool* c } bool AOTConstantPoolResolver::check_lambda_metafactory_methodtype_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i) { - int mt_index = cp->bsm_attribute_entry(bsms_attribute_index)->argument_index(arg_i); + int mt_index = cp->bsm_attribute_entry(bsms_attribute_index)->argument(arg_i); if (!cp->tag_at(mt_index).is_method_type()) { // malformed class? return false; @@ -465,7 +465,7 @@ bool AOTConstantPoolResolver::check_lambda_metafactory_methodtype_arg(ConstantPo } bool AOTConstantPoolResolver::check_lambda_metafactory_methodhandle_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i) { - int mh_index = cp->bsm_attribute_entry(bsms_attribute_index)->argument_index(arg_i); + int mh_index = cp->bsm_attribute_entry(bsms_attribute_index)->argument(arg_i); if (!cp->tag_at(mh_index).is_method_handle()) { // malformed class? return false; diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index 42d41e6ae89..f56050d4d31 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -114,6 +114,7 @@ intx AOTMetaspace::_relocation_delta; char* AOTMetaspace::_requested_base_address; Array* AOTMetaspace::_archived_method_handle_intrinsics = nullptr; bool AOTMetaspace::_use_optimized_module_handling = true; +int volatile AOTMetaspace::_preimage_static_archive_dumped = 0; FileMapInfo* AOTMetaspace::_output_mapinfo = nullptr; // The CDS archive is divided into the following regions: @@ -1056,7 +1057,21 @@ void AOTMetaspace::exercise_runtime_cds_code(TRAPS) { CDSProtectionDomain::to_file_URL("dummy.jar", Handle(), CHECK); } +bool AOTMetaspace::preimage_static_archive_dumped() { + assert(CDSConfig::is_dumping_preimage_static_archive(), "Required"); + return AtomicAccess::load_acquire(&_preimage_static_archive_dumped) == 1; +} + void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS) { + if (CDSConfig::is_dumping_preimage_static_archive()) { + // When dumping to the AOT configuration file ensure this function is only executed once. + // Multiple invocations may happen via JCmd, during VM exit or other means (in the future) + // from different threads and possibly concurrently. + if (AtomicAccess::cmpxchg(&_preimage_static_archive_dumped, 0, 1) != 0) { + return; + } + } + if (CDSConfig::is_dumping_classic_static_archive()) { // We are running with -Xshare:dump load_classes(CHECK); @@ -1355,8 +1370,11 @@ bool AOTMetaspace::try_link_class(JavaThread* current, InstanceKlass* ik) { ik->link_class(THREAD); if (HAS_PENDING_EXCEPTION) { ResourceMark rm(THREAD); - aot_log_warning(aot)("Preload Warning: Verification failed for %s", - ik->external_name()); + oop message = java_lang_Throwable::message(current->pending_exception()); + aot_log_warning(aot)("Preload Warning: Verification failed for %s because a %s was thrown: %s", + ik->external_name(), + current->pending_exception()->klass()->external_name(), + message == nullptr ? "(no message)" : java_lang_String::as_utf8_string(message)); CLEAR_PENDING_EXCEPTION; SystemDictionaryShared::set_class_has_failed_verification(ik); } else { diff --git a/src/hotspot/share/cds/aotMetaspace.hpp b/src/hotspot/share/cds/aotMetaspace.hpp index 1712a7865ad..ab78787288f 100644 --- a/src/hotspot/share/cds/aotMetaspace.hpp +++ b/src/hotspot/share/cds/aotMetaspace.hpp @@ -60,6 +60,7 @@ class AOTMetaspace : AllStatic { static char* _requested_base_address; static bool _use_optimized_module_handling; static Array* _archived_method_handle_intrinsics; + static int volatile _preimage_static_archive_dumped; static FileMapInfo* _output_mapinfo; public: @@ -115,6 +116,8 @@ class AOTMetaspace : AllStatic { // inside the metaspace of the dynamic static CDS archive static bool in_aot_cache_dynamic_region(void* p) NOT_CDS_RETURN_(false); + static bool preimage_static_archive_dumped() NOT_CDS_RETURN_(false); + static void unrecoverable_loading_error(const char* message = "unrecoverable error"); static void report_loading_error(const char* format, ...) ATTRIBUTE_PRINTF(1, 0); static void unrecoverable_writing_error(const char* message = nullptr); diff --git a/src/hotspot/share/ci/ciEnv.cpp b/src/hotspot/share/ci/ciEnv.cpp index 79ab881e7f6..92bacc4c2c3 100644 --- a/src/hotspot/share/ci/ciEnv.cpp +++ b/src/hotspot/share/ci/ciEnv.cpp @@ -1057,7 +1057,9 @@ void ciEnv::register_method(ciMethod* target, } assert(offsets->value(CodeOffsets::Deopt) != -1, "must have deopt entry"); - assert(offsets->value(CodeOffsets::Exceptions) != -1, "must have exception entry"); + + assert(compiler->type() == compiler_c2 || + offsets->value(CodeOffsets::Exceptions) != -1, "must have exception entry"); nm = nmethod::new_nmethod(method, compile_id(), diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index eb8a2a389b9..68890775051 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -47,6 +47,7 @@ #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/annotations.hpp" +#include "oops/bsmAttribute.inline.hpp" #include "oops/constantPool.inline.hpp" #include "oops/fieldInfo.hpp" #include "oops/fieldStreams.inline.hpp" @@ -3298,8 +3299,9 @@ void ClassFileParser::parse_classfile_bootstrap_methods_attribute(const ClassFil TRAPS) { assert(cfs != nullptr, "invariant"); assert(cp != nullptr, "invariant"); + const int cp_size = cp->length(); - const u1* const current_start = cfs->current(); + const u1* const current_before_parsing = cfs->current(); guarantee_property(attribute_byte_length >= sizeof(u2), "Invalid BootstrapMethods attribute length %u in class file %s", @@ -3308,57 +3310,40 @@ void ClassFileParser::parse_classfile_bootstrap_methods_attribute(const ClassFil cfs->guarantee_more(attribute_byte_length, CHECK); - const int attribute_array_length = cfs->get_u2_fast(); + const int num_bootstrap_methods = cfs->get_u2_fast(); - guarantee_property(_max_bootstrap_specifier_index < attribute_array_length, + guarantee_property(_max_bootstrap_specifier_index < num_bootstrap_methods, "Short length on BootstrapMethods in class file %s", CHECK); + const u4 bootstrap_methods_u2_len = (attribute_byte_length - sizeof(u2)) / sizeof(u2); - // The attribute contains a counted array of counted tuples of shorts, - // represending bootstrap specifiers: - // length*{bootstrap_method_index, argument_count*{argument_index}} - const unsigned int operand_count = (attribute_byte_length - (unsigned)sizeof(u2)) / (unsigned)sizeof(u2); - // operand_count = number of shorts in attr, except for leading length - - // The attribute is copied into a short[] array. - // The array begins with a series of short[2] pairs, one for each tuple. - const int index_size = (attribute_array_length * 2); - - Array* const operands = - MetadataFactory::new_array(_loader_data, index_size + operand_count, CHECK); - - // Eagerly assign operands so they will be deallocated with the constant + // Eagerly assign the arrays so that they will be deallocated with the constant // pool if there is an error. - cp->set_operands(operands); - - int operand_fill_index = index_size; - const int cp_size = cp->length(); - - for (int n = 0; n < attribute_array_length; n++) { - // Store a 32-bit offset into the header of the operand array. - ConstantPool::operand_offset_at_put(operands, n, operand_fill_index); + BSMAttributeEntries::InsertionIterator iter = + cp->bsm_entries().start_extension(num_bootstrap_methods, + bootstrap_methods_u2_len, + _loader_data, + CHECK); - // Read a bootstrap specifier. + for (int i = 0; i < num_bootstrap_methods; i++) { cfs->guarantee_more(sizeof(u2) * 2, CHECK); // bsm, argc - const u2 bootstrap_method_index = cfs->get_u2_fast(); - const u2 argument_count = cfs->get_u2_fast(); + u2 bootstrap_method_ref = cfs->get_u2_fast(); + u2 num_bootstrap_arguments = cfs->get_u2_fast(); guarantee_property( - valid_cp_range(bootstrap_method_index, cp_size) && - cp->tag_at(bootstrap_method_index).is_method_handle(), - "bootstrap_method_index %u has bad constant type in class file %s", - bootstrap_method_index, - CHECK); - - guarantee_property((operand_fill_index + 1 + argument_count) < operands->length(), - "Invalid BootstrapMethods num_bootstrap_methods or num_bootstrap_arguments value in class file %s", - CHECK); - - operands->at_put(operand_fill_index++, bootstrap_method_index); - operands->at_put(operand_fill_index++, argument_count); - - cfs->guarantee_more(sizeof(u2) * argument_count, CHECK); // argv[argc] - for (int j = 0; j < argument_count; j++) { + valid_cp_range(bootstrap_method_ref, cp_size) && + cp->tag_at(bootstrap_method_ref).is_method_handle(), + "bootstrap_method_index %u has bad constant type in class file %s", + bootstrap_method_ref, + CHECK); + cfs->guarantee_more(sizeof(u2) * num_bootstrap_arguments, CHECK); // argv[argc] + + BSMAttributeEntry* entry = iter.reserve_new_entry(bootstrap_method_ref, num_bootstrap_arguments); + guarantee_property(entry != nullptr, + "Invalid BootstrapMethods num_bootstrap_methods." + " The total amount of space reserved for the BootstrapMethod attribute was not sufficient", CHECK); + + for (int argi = 0; argi < num_bootstrap_arguments; argi++) { const u2 argument_index = cfs->get_u2_fast(); guarantee_property( valid_cp_range(argument_index, cp_size) && @@ -3366,10 +3351,11 @@ void ClassFileParser::parse_classfile_bootstrap_methods_attribute(const ClassFil "argument_index %u has bad constant type in class file %s", argument_index, CHECK); - operands->at_put(operand_fill_index++, argument_index); + entry->set_argument(argi, argument_index); } } - guarantee_property(current_start + attribute_byte_length == cfs->current(), + cp->bsm_entries().end_extension(iter, _loader_data, CHECK); + guarantee_property(current_before_parsing + attribute_byte_length == cfs->current(), "Bad length on BootstrapMethods in class file %s", CHECK); } diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp index 082c745f4c3..12fbda899b9 100644 --- a/src/hotspot/share/classfile/classLoader.cpp +++ b/src/hotspot/share/classfile/classLoader.cpp @@ -412,31 +412,30 @@ ClassFileStream* ClassPathImageEntry::open_stream(JavaThread* current, const cha // ClassFileStream* ClassPathImageEntry::open_stream_for_loader(JavaThread* current, const char* name, ClassLoaderData* loader_data) { jlong size; - JImageLocationRef location = (*JImageFindResource)(jimage_non_null(), "", get_jimage_version_string(), name, &size); + JImageLocationRef location = 0; - if (location == 0) { - TempNewSymbol class_name = SymbolTable::new_symbol(name); - TempNewSymbol pkg_name = ClassLoader::package_from_class_name(class_name); + TempNewSymbol class_name = SymbolTable::new_symbol(name); + TempNewSymbol pkg_name = ClassLoader::package_from_class_name(class_name); - if (pkg_name != nullptr) { - if (!Universe::is_module_initialized()) { - location = (*JImageFindResource)(jimage_non_null(), JAVA_BASE_NAME, get_jimage_version_string(), name, &size); - } else { - PackageEntry* package_entry = ClassLoader::get_package_entry(pkg_name, loader_data); - if (package_entry != nullptr) { - ResourceMark rm(current); - // Get the module name - ModuleEntry* module = package_entry->module(); - assert(module != nullptr, "Boot classLoader package missing module"); - assert(module->is_named(), "Boot classLoader package is in unnamed module"); - const char* module_name = module->name()->as_C_string(); - if (module_name != nullptr) { - location = (*JImageFindResource)(jimage_non_null(), module_name, get_jimage_version_string(), name, &size); - } + if (pkg_name != nullptr) { + if (!Universe::is_module_initialized()) { + location = (*JImageFindResource)(jimage_non_null(), JAVA_BASE_NAME, get_jimage_version_string(), name, &size); + } else { + PackageEntry* package_entry = ClassLoader::get_package_entry(pkg_name, loader_data); + if (package_entry != nullptr) { + ResourceMark rm(current); + // Get the module name + ModuleEntry* module = package_entry->module(); + assert(module != nullptr, "Boot classLoader package missing module"); + assert(module->is_named(), "Boot classLoader package is in unnamed module"); + const char* module_name = module->name()->as_C_string(); + if (module_name != nullptr) { + location = (*JImageFindResource)(jimage_non_null(), module_name, get_jimage_version_string(), name, &size); } } } } + if (location != 0) { if (UsePerfData) { ClassLoader::perf_sys_classfile_bytes_read()->inc(size); diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index d91af9b4991..c2f8b46f00e 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -1302,7 +1302,7 @@ nmethod::nmethod( } // Native wrappers do not have deopt handlers. Make the values // something that will never match a pc like the nmethod vtable entry - _deopt_handler_offset = 0; + _deopt_handler_entry_offset = 0; _unwind_handler_offset = 0; CHECKED_CAST(_oops_size, uint16_t, align_up(code_buffer->total_oop_size(), oopSize)); @@ -1442,7 +1442,7 @@ nmethod::nmethod(const nmethod &nm) : CodeBlob(nm._name, nm._kind, nm._size, nm. _skipped_instructions_size = nm._skipped_instructions_size; _stub_offset = nm._stub_offset; _exception_offset = nm._exception_offset; - _deopt_handler_offset = nm._deopt_handler_offset; + _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; @@ -1704,19 +1704,26 @@ nmethod::nmethod( _exception_offset = -1; } if (offsets->value(CodeOffsets::Deopt) != -1) { - _deopt_handler_offset = code_offset() + offsets->value(CodeOffsets::Deopt); + _deopt_handler_entry_offset = code_offset() + offsets->value(CodeOffsets::Deopt); } else { - _deopt_handler_offset = -1; + _deopt_handler_entry_offset = -1; } } else #endif { // Exception handler and deopt handler are in the stub section - assert(offsets->value(CodeOffsets::Exceptions) != -1, "must be set"); assert(offsets->value(CodeOffsets::Deopt ) != -1, "must be set"); - _exception_offset = _stub_offset + offsets->value(CodeOffsets::Exceptions); - _deopt_handler_offset = _stub_offset + offsets->value(CodeOffsets::Deopt); + bool has_exception_handler = (offsets->value(CodeOffsets::Exceptions) != -1); + assert(has_exception_handler == (compiler->type() != compiler_c2), + "C2 compiler doesn't provide exception handler stub code."); + if (has_exception_handler) { + _exception_offset = _stub_offset + offsets->value(CodeOffsets::Exceptions); + } else { + _exception_offset = -1; + } + + _deopt_handler_entry_offset = _stub_offset + offsets->value(CodeOffsets::Deopt); } if (offsets->value(CodeOffsets::UnwindHandler) != -1) { // C1 generates UnwindHandler at the end of instructions section. @@ -4024,7 +4031,7 @@ const char* nmethod::nmethod_section_label(address pos) const { // Check stub_code before checking exception_handler or deopt_handler. if (pos == this->stub_begin()) label = "[Stub Code]"; if (JVMCI_ONLY(_exception_offset >= 0 &&) pos == exception_begin()) label = "[Exception Handler]"; - if (JVMCI_ONLY(_deopt_handler_offset != -1 &&) pos == deopt_handler_begin()) label = "[Deopt Handler Code]"; + if (JVMCI_ONLY(_deopt_handler_entry_offset != -1 &&) pos == deopt_handler_entry()) label = "[Deopt Handler Entry Point]"; return label; } diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp index 34accf428b6..0fa9d7fda9e 100644 --- a/src/hotspot/share/code/nmethod.hpp +++ b/src/hotspot/share/code/nmethod.hpp @@ -229,7 +229,7 @@ class nmethod : public CodeBlob { int _exception_offset; // All deoptee's will resume execution at this location described by // this offset. - int _deopt_handler_offset; + int _deopt_handler_entry_offset; // Offset (from insts_end) of the unwind handler if it exists int16_t _unwind_handler_offset; // Number of arguments passed on the stack @@ -617,7 +617,7 @@ class nmethod : public CodeBlob { address stub_begin () const { return header_begin() + _stub_offset ; } address stub_end () const { return code_end() ; } address exception_begin () const { return header_begin() + _exception_offset ; } - address deopt_handler_begin () const { return header_begin() + _deopt_handler_offset ; } + address deopt_handler_entry () const { return header_begin() + _deopt_handler_entry_offset ; } address unwind_handler_begin () const { return _unwind_handler_offset != -1 ? (insts_end() - _unwind_handler_offset) : nullptr; } oop* oops_begin () const { return (oop*) data_begin(); } oop* oops_end () const { return (oop*) data_end(); } diff --git a/src/hotspot/share/code/nmethod.inline.hpp b/src/hotspot/share/code/nmethod.inline.hpp index 44331db669c..ecee3c0c31a 100644 --- a/src/hotspot/share/code/nmethod.inline.hpp +++ b/src/hotspot/share/code/nmethod.inline.hpp @@ -34,7 +34,7 @@ inline bool nmethod::is_deopt_pc(address pc) { return is_deopt_entry(pc); } inline bool nmethod::is_deopt_entry(address pc) { - return pc == deopt_handler_begin(); + return pc == deopt_handler_entry(); } // class ExceptionCache methods diff --git a/src/hotspot/share/compiler/compilationMemoryStatistic.cpp b/src/hotspot/share/compiler/compilationMemoryStatistic.cpp index d1e2f6f34a0..1951fd066fc 100644 --- a/src/hotspot/share/compiler/compilationMemoryStatistic.cpp +++ b/src/hotspot/share/compiler/compilationMemoryStatistic.cpp @@ -1010,8 +1010,10 @@ void CompilationMemoryStatistic::print_error_report(outputStream* st) { oom_stats->print_peak_state_on(st); st->cr(); } - st->print_cr("Compiler Memory Statistic, 10 most expensive compilations:"); - print_all_by_size(st, false, false, 0, 10); + if (Thread::current_or_null_safe() != nullptr) { + st->print_cr("Compiler Memory Statistic, 10 most expensive compilations:"); + print_all_by_size(st, false, false, 0, 10); + } } void CompilationMemoryStatistic::print_final_report(outputStream* st) { diff --git a/src/hotspot/share/cppstdlib/new.hpp b/src/hotspot/share/cppstdlib/new.hpp index 3536ac13288..ea9d6c88c87 100644 --- a/src/hotspot/share/cppstdlib/new.hpp +++ b/src/hotspot/share/cppstdlib/new.hpp @@ -79,11 +79,10 @@ class [[deprecated]] bad_array_new_length; // version to decide whether to redeclare deprecated. #if defined(__clang__) -#if __clang_major__ >= 19 -// clang18 and earlier may accept the declaration but go wrong with uses. -// Different warnings and link-time failures are both possible. -#define CAN_DEPRECATE_HARDWARE_INTERFERENCE_SIZES 1 -#endif // restrict clang version +// Some versions of clang with some stdlibs reject the declaration. Others may +// accept the declaration but go wrong with uses. Different warnings and +// link-time failures are both possible. +// Known to have problems at least through clang19. #elif defined(__GNUC__) #if (__GNUC__ > 13) || (__GNUC__ == 13 && __GNUC_MINOR__ >= 2) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index d18f61ff507..061241c24e2 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -478,11 +478,6 @@ HeapWord* G1CollectedHeap::attempt_allocation_slow(uint node_index, size_t word_ log_trace(gc, alloc)("%s: Unsuccessfully scheduled collection allocating %zu words", Thread::current()->name(), word_size); - if (is_shutting_down()) { - stall_for_vm_shutdown(); - return nullptr; - } - // Has the gc overhead limit been reached in the meantime? If so, this mutator // should receive null even when unsuccessfully scheduling a collection as well // for global consistency. @@ -738,11 +733,6 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) { log_trace(gc, alloc)("%s: Unsuccessfully scheduled collection allocating %zu", Thread::current()->name(), word_size); - if (is_shutting_down()) { - stall_for_vm_shutdown(); - return nullptr; - } - // Has the gc overhead limit been reached in the meantime? If so, this mutator // should receive null even when unsuccessfully scheduling a collection as well // for global consistency. @@ -1645,6 +1635,10 @@ jint G1CollectedHeap::initialize() { return JNI_OK; } +bool G1CollectedHeap::concurrent_mark_is_terminating() const { + return _cm_thread->should_terminate(); +} + void G1CollectedHeap::stop() { // Stop all concurrent threads. We do this to make sure these threads // do not continue to execute and access resources (e.g. logging) @@ -1965,8 +1959,8 @@ bool G1CollectedHeap::try_collect_concurrently(size_t allocation_word_size, } // If VMOp skipped initiating concurrent marking cycle because - // we're terminating, then we're done. - if (is_shutting_down()) { + // we're shutting down, then we're done. + if (op.is_shutting_down()) { LOG_COLLECT_CONCURRENTLY(cause, "skipped: terminating"); return false; } @@ -2361,7 +2355,8 @@ static void print_region_type(outputStream* st, const char* type, uint count, bo } void G1CollectedHeap::print_heap_on(outputStream* st) const { - size_t heap_used = Heap_lock->owned_by_self() ? used() : used_unlocked(); + size_t heap_used = (Thread::current_or_null_safe() != nullptr && + Heap_lock->owned_by_self()) ? used() : used_unlocked(); st->print("%-20s", "garbage-first heap"); st->print(" total reserved %zuK, committed %zuK, used %zuK", _hrm.reserved().byte_size()/K, capacity()/K, heap_used/K); diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 5dccf41e909..aff7166d391 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -917,6 +917,9 @@ class G1CollectedHeap : public CollectedHeap { // specified by the policy object. jint initialize() override; + // Returns whether concurrent mark threads (and the VM) are about to terminate. + bool concurrent_mark_is_terminating() const; + void safepoint_synchronize_begin() override; void safepoint_synchronize_end() override; diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp index 47340fad768..d71108d4d0e 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp @@ -267,8 +267,6 @@ void G1CollectionSetCandidates::set_candidates_from_marking(G1HeapRegion** candi // the same MixedGC. uint group_limit = p->calc_min_old_cset_length(num_candidates); - uint num_added_to_group = 0; - G1CSetCandidateGroup::reset_next_group_id(); G1CSetCandidateGroup* current = nullptr; @@ -279,7 +277,7 @@ void G1CollectionSetCandidates::set_candidates_from_marking(G1HeapRegion** candi assert(!contains(r), "must not contain region %u", r->hrm_index()); _contains_map[r->hrm_index()] = CandidateOrigin::Marking; - if (num_added_to_group == group_limit) { + if (current->length() == group_limit) { if (group_limit != G1OldCSetGroupSize) { group_limit = G1OldCSetGroupSize; } @@ -287,10 +285,8 @@ void G1CollectionSetCandidates::set_candidates_from_marking(G1HeapRegion** candi _from_marking_groups.append(current); current = new G1CSetCandidateGroup(); - num_added_to_group = 0; } current->add(r); - num_added_to_group++; } _from_marking_groups.append(current); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index d37fe9ea7ba..456d543fa10 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -1883,7 +1883,7 @@ bool G1ConcurrentMark::concurrent_cycle_abort() { // nothing, but this situation should be extremely rare (a full gc after shutdown // has been signalled is already rare), and this work should be negligible compared // to actual full gc work. - if (!cm_thread()->in_progress() && !_g1h->is_shutting_down()) { + if (!cm_thread()->in_progress() && !_g1h->concurrent_mark_is_terminating()) { return false; } diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.cpp b/src/hotspot/share/gc/g1/g1HeapRegion.cpp index b1eeb333d8d..361e19d4be5 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.cpp @@ -307,10 +307,6 @@ void G1HeapRegion::add_code_root(nmethod* nm) { rem_set()->add_code_root(nm); } -void G1HeapRegion::remove_code_root(nmethod* nm) { - rem_set()->remove_code_root(nm); -} - void G1HeapRegion::code_roots_do(NMethodClosure* blk) const { rem_set()->code_roots_do(blk); } diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.hpp b/src/hotspot/share/gc/g1/g1HeapRegion.hpp index 17ec3055b52..fe915b0dafe 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.hpp @@ -543,7 +543,6 @@ class G1HeapRegion : public CHeapObj { // Routines for managing a list of code roots (attached to the // this region's RSet) that point into this heap region. void add_code_root(nmethod* nm); - void remove_code_root(nmethod* nm); // Applies blk->do_nmethod() to each of the entries in // the code roots list for this region diff --git a/src/hotspot/share/gc/g1/g1IHOPControl.cpp b/src/hotspot/share/gc/g1/g1IHOPControl.cpp index 34c8cd0366b..43698e9f12b 100644 --- a/src/hotspot/share/gc/g1/g1IHOPControl.cpp +++ b/src/hotspot/share/gc/g1/g1IHOPControl.cpp @@ -28,81 +28,27 @@ #include "gc/g1/g1Trace.hpp" #include "logging/log.hpp" -G1IHOPControl::G1IHOPControl(double initial_ihop_percent, - G1OldGenAllocationTracker const* old_gen_alloc_tracker) : - _initial_ihop_percent(initial_ihop_percent), - _target_occupancy(0), - _last_allocation_time_s(0.0), - _old_gen_alloc_tracker(old_gen_alloc_tracker) -{ - assert(_initial_ihop_percent >= 0.0 && _initial_ihop_percent <= 100.0, "Initial IHOP value must be between 0 and 100 but is %.3f", initial_ihop_percent); -} - -void G1IHOPControl::update_target_occupancy(size_t new_target_occupancy) { - log_debug(gc, ihop)("Target occupancy update: old: %zuB, new: %zuB", - _target_occupancy, new_target_occupancy); - _target_occupancy = new_target_occupancy; -} +double G1IHOPControl::predict(const TruncatedSeq* seq) const { + assert(_is_adaptive, "precondition"); + assert(_predictor != nullptr, "precondition"); -void G1IHOPControl::report_statistics(G1NewTracer* new_tracer, size_t non_young_occupancy) { - print_log(non_young_occupancy); - send_trace_event(new_tracer, non_young_occupancy); + return _predictor->predict_zero_bounded(seq); } -void G1IHOPControl::update_allocation_info(double allocation_time_s, size_t additional_buffer_size) { - assert(allocation_time_s >= 0.0, "Allocation time must be positive but is %.3f", allocation_time_s); +bool G1IHOPControl::have_enough_data_for_prediction() const { + assert(_is_adaptive, "precondition"); - _last_allocation_time_s = allocation_time_s; -} - -void G1IHOPControl::print_log(size_t non_young_occupancy) { - assert(_target_occupancy > 0, "Target occupancy still not updated yet."); - size_t cur_conc_mark_start_threshold = get_conc_mark_start_threshold(); - log_debug(gc, ihop)("Basic information (value update), threshold: %zuB (%1.2f), target occupancy: %zuB, non-young occupancy: %zuB, " - "recent allocation size: %zuB, recent allocation duration: %1.2fms, recent old gen allocation rate: %1.2fB/s, recent marking phase length: %1.2fms", - cur_conc_mark_start_threshold, - percent_of(cur_conc_mark_start_threshold, _target_occupancy), - _target_occupancy, - non_young_occupancy, - _old_gen_alloc_tracker->last_period_old_gen_bytes(), - _last_allocation_time_s * 1000.0, - _last_allocation_time_s > 0.0 ? _old_gen_alloc_tracker->last_period_old_gen_bytes() / _last_allocation_time_s : 0.0, - last_marking_length_s() * 1000.0); -} - -void G1IHOPControl::send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy) { - assert(_target_occupancy > 0, "Target occupancy still not updated yet."); - tracer->report_basic_ihop_statistics(get_conc_mark_start_threshold(), - _target_occupancy, - non_young_occupancy, - _old_gen_alloc_tracker->last_period_old_gen_bytes(), - _last_allocation_time_s, - last_marking_length_s()); + return ((size_t)_marking_times_s.num() >= G1AdaptiveIHOPNumInitialSamples) && + ((size_t)_allocation_rate_s.num() >= G1AdaptiveIHOPNumInitialSamples); } -G1StaticIHOPControl::G1StaticIHOPControl(double ihop_percent, - G1OldGenAllocationTracker const* old_gen_alloc_tracker) : - G1IHOPControl(ihop_percent, old_gen_alloc_tracker), - _last_marking_length_s(0.0) { +double G1IHOPControl::last_marking_length_s() const { + return _marking_times_s.last(); } -G1AdaptiveIHOPControl::G1AdaptiveIHOPControl(double ihop_percent, - G1OldGenAllocationTracker const* old_gen_alloc_tracker, - G1Predictions const* predictor, - size_t heap_reserve_percent, - size_t heap_waste_percent) : - G1IHOPControl(ihop_percent, old_gen_alloc_tracker), - _heap_reserve_percent(heap_reserve_percent), - _heap_waste_percent(heap_waste_percent), - _predictor(predictor), - _marking_times_s(10, 0.05), - _allocation_rate_s(10, 0.05), - _last_unrestrained_young_size(0) -{ -} +size_t G1IHOPControl::actual_target_threshold() const { + assert(_is_adaptive, "precondition"); -size_t G1AdaptiveIHOPControl::actual_target_threshold() const { - guarantee(_target_occupancy > 0, "Target occupancy still not updated yet."); // The actual target threshold takes the heap reserve and the expected waste in // free space into account. // _heap_reserve is that part of the total heap capacity that is reserved for @@ -110,74 +56,103 @@ size_t G1AdaptiveIHOPControl::actual_target_threshold() const { // _heap_waste is the amount of space will never be reclaimed in any // heap, so can not be used for allocation during marking and must always be // considered. - - double safe_total_heap_percentage = MIN2((double)(_heap_reserve_percent + _heap_waste_percent), 100.0); + double safe_total_heap_percentage = + MIN2((double)(_heap_reserve_percent + _heap_waste_percent), 100.0); return (size_t)MIN2( G1CollectedHeap::heap()->max_capacity() * (100.0 - safe_total_heap_percentage) / 100.0, _target_occupancy * (100.0 - _heap_waste_percent) / 100.0 - ); + ); +} + +G1IHOPControl::G1IHOPControl(double ihop_percent, + const G1OldGenAllocationTracker* old_gen_alloc_tracker, + bool adaptive, + const G1Predictions* predictor, + size_t heap_reserve_percent, + size_t heap_waste_percent) + : _is_adaptive(adaptive), + _initial_ihop_percent(ihop_percent), + _target_occupancy(0), + _heap_reserve_percent(heap_reserve_percent), + _heap_waste_percent(heap_waste_percent), + _last_allocation_time_s(0.0), + _old_gen_alloc_tracker(old_gen_alloc_tracker), + _predictor(predictor), + _marking_times_s(10, 0.05), + _allocation_rate_s(10, 0.05), + _last_unrestrained_young_size(0) { + assert(_initial_ihop_percent >= 0.0 && _initial_ihop_percent <= 100.0, + "IHOP percent out of range: %.3f", ihop_percent); + assert(!_is_adaptive || _predictor != nullptr, "precondition"); +} + +void G1IHOPControl::update_target_occupancy(size_t new_target_occupancy) { + log_debug(gc, ihop)("Target occupancy update: old: %zuB, new: %zuB", + _target_occupancy, new_target_occupancy); + _target_occupancy = new_target_occupancy; } -double G1AdaptiveIHOPControl::predict(TruncatedSeq const* seq) const { - return _predictor->predict_zero_bounded(seq); +void G1IHOPControl::report_statistics(G1NewTracer* new_tracer, size_t non_young_occupancy) { + print_log(non_young_occupancy); + send_trace_event(new_tracer, non_young_occupancy); } -bool G1AdaptiveIHOPControl::have_enough_data_for_prediction() const { - return ((size_t)_marking_times_s.num() >= G1AdaptiveIHOPNumInitialSamples) && - ((size_t)_allocation_rate_s.num() >= G1AdaptiveIHOPNumInitialSamples); +void G1IHOPControl::update_allocation_info(double allocation_time_s, size_t additional_buffer_size) { + assert(allocation_time_s > 0, "Invalid allocation time: %.3f", allocation_time_s); + _last_allocation_time_s = allocation_time_s; + double alloc_rate = _old_gen_alloc_tracker->last_period_old_gen_growth() / allocation_time_s; + _allocation_rate_s.add(alloc_rate); + _last_unrestrained_young_size = additional_buffer_size; } -size_t G1AdaptiveIHOPControl::get_conc_mark_start_threshold() { - if (have_enough_data_for_prediction()) { - double pred_marking_time = predict(&_marking_times_s); - double pred_promotion_rate = predict(&_allocation_rate_s); - size_t pred_promotion_size = (size_t)(pred_marking_time * pred_promotion_rate); - - size_t predicted_needed_bytes_during_marking = - pred_promotion_size + - // In reality we would need the maximum size of the young gen during - // marking. This is a conservative estimate. - _last_unrestrained_young_size; - - size_t internal_threshold = actual_target_threshold(); - size_t predicted_initiating_threshold = predicted_needed_bytes_during_marking < internal_threshold ? - internal_threshold - predicted_needed_bytes_during_marking : - 0; - return predicted_initiating_threshold; - } else { - // Use the initial value. - return (size_t)(_initial_ihop_percent * _target_occupancy / 100.0); - } +void G1IHOPControl::update_marking_length(double marking_length_s) { + assert(marking_length_s >= 0.0, "Invalid marking length: %.3f", marking_length_s); + _marking_times_s.add(marking_length_s); } -double G1AdaptiveIHOPControl::last_mutator_period_old_allocation_rate() const { - assert(_last_allocation_time_s > 0, "This should not be called when the last GC is full"); +size_t G1IHOPControl::get_conc_mark_start_threshold() { + guarantee(_target_occupancy > 0, "Target occupancy must be initialized"); - return _old_gen_alloc_tracker->last_period_old_gen_growth() / _last_allocation_time_s; -} + if (!_is_adaptive || !have_enough_data_for_prediction()) { + return (size_t)(_initial_ihop_percent * _target_occupancy / 100.0); + } -void G1AdaptiveIHOPControl::update_allocation_info(double allocation_time_s, - size_t additional_buffer_size) { - G1IHOPControl::update_allocation_info(allocation_time_s, additional_buffer_size); - _allocation_rate_s.add(last_mutator_period_old_allocation_rate()); + double pred_marking_time = predict(&_marking_times_s); + double pred_rate = predict(&_allocation_rate_s); + size_t pred_bytes = (size_t)(pred_marking_time * pred_rate); + size_t predicted_needed = pred_bytes + _last_unrestrained_young_size; + size_t internal_threshold = actual_target_threshold(); - _last_unrestrained_young_size = additional_buffer_size; + return predicted_needed < internal_threshold + ? internal_threshold - predicted_needed + : 0; } -void G1AdaptiveIHOPControl::update_marking_length(double marking_length_s) { - assert(marking_length_s >= 0.0, "Marking length must be larger than zero but is %.3f", marking_length_s); - _marking_times_s.add(marking_length_s); -} +void G1IHOPControl::print_log(size_t non_young_occupancy) { + assert(_target_occupancy > 0, "Target occupancy still not updated yet."); + size_t cur_conc_mark_start_threshold = get_conc_mark_start_threshold(); + log_debug(gc, ihop)("Basic information (value update), threshold: %zuB (%1.2f), target occupancy: %zuB, non-young occupancy: %zuB, " + "recent allocation size: %zuB, recent allocation duration: %1.2fms, recent old gen allocation rate: %1.2fB/s, recent marking phase length: %1.2fms", + cur_conc_mark_start_threshold, + percent_of(cur_conc_mark_start_threshold, _target_occupancy), + _target_occupancy, + non_young_occupancy, + _old_gen_alloc_tracker->last_period_old_gen_bytes(), + _last_allocation_time_s * 1000.0, + _last_allocation_time_s > 0.0 ? _old_gen_alloc_tracker->last_period_old_gen_bytes() / _last_allocation_time_s : 0.0, + last_marking_length_s() * 1000.0); + + if (!_is_adaptive) { + return; + } -void G1AdaptiveIHOPControl::print_log(size_t non_young_occupancy) { - G1IHOPControl::print_log(non_young_occupancy); size_t actual_threshold = actual_target_threshold(); log_debug(gc, ihop)("Adaptive IHOP information (value update), threshold: %zuB (%1.2f), internal target threshold: %zuB, " "non-young occupancy: %zuB, additional buffer size: %zuB, predicted old gen allocation rate: %1.2fB/s, " "predicted marking phase length: %1.2fms, prediction active: %s", - get_conc_mark_start_threshold(), - percent_of(get_conc_mark_start_threshold(), actual_threshold), + cur_conc_mark_start_threshold, + percent_of(cur_conc_mark_start_threshold, actual_threshold), actual_threshold, non_young_occupancy, _last_unrestrained_young_size, @@ -186,13 +161,22 @@ void G1AdaptiveIHOPControl::print_log(size_t non_young_occupancy) { have_enough_data_for_prediction() ? "true" : "false"); } -void G1AdaptiveIHOPControl::send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy) { - G1IHOPControl::send_trace_event(tracer, non_young_occupancy); - tracer->report_adaptive_ihop_statistics(get_conc_mark_start_threshold(), - actual_target_threshold(), - non_young_occupancy, - _last_unrestrained_young_size, - predict(&_allocation_rate_s), - predict(&_marking_times_s), - have_enough_data_for_prediction()); +void G1IHOPControl::send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy) { + assert(_target_occupancy > 0, "Target occupancy still not updated yet."); + tracer->report_basic_ihop_statistics(get_conc_mark_start_threshold(), + _target_occupancy, + non_young_occupancy, + _old_gen_alloc_tracker->last_period_old_gen_bytes(), + _last_allocation_time_s, + last_marking_length_s()); + + if (_is_adaptive) { + tracer->report_adaptive_ihop_statistics(get_conc_mark_start_threshold(), + actual_target_threshold(), + non_young_occupancy, + _last_unrestrained_young_size, + predict(&_allocation_rate_s), + predict(&_marking_times_s), + have_enough_data_for_prediction()); + } } diff --git a/src/hotspot/share/gc/g1/g1IHOPControl.hpp b/src/hotspot/share/gc/g1/g1IHOPControl.hpp index 392a12a785a..b6e80d9b422 100644 --- a/src/hotspot/share/gc/g1/g1IHOPControl.hpp +++ b/src/hotspot/share/gc/g1/g1IHOPControl.hpp @@ -32,89 +32,32 @@ class G1Predictions; class G1NewTracer; -// Base class for algorithms that calculate the heap occupancy at which -// concurrent marking should start. This heap usage threshold should be relative -// to old gen size. +// Implements two strategies for calculating the concurrent mark starting occupancy threshold: +// - Static mode: Uses a fixed percentage of the target heap occupancy. +// - Adaptive mode: Predicts a threshold based on allocation rates and marking durations +// to ensure the target occupancy is never exceeded during marking. class G1IHOPControl : public CHeapObj { - protected: + private: + const bool _is_adaptive; + // The initial IHOP value relative to the target occupancy. double _initial_ihop_percent; + // The target maximum occupancy of the heap. The target occupancy is the number // of bytes when marking should be finished and reclaim started. size_t _target_occupancy; + // Percentage of maximum heap capacity we should avoid to touch + const size_t _heap_reserve_percent; + + // Percentage of free heap that should be considered as waste. + const size_t _heap_waste_percent; + // Most recent complete mutator allocation period in seconds. double _last_allocation_time_s; - const G1OldGenAllocationTracker* _old_gen_alloc_tracker; - // Initialize an instance with the old gen allocation tracker and the - // initial IHOP value in percent. The target occupancy will be updated - // at the first heap expansion. - G1IHOPControl(double ihop_percent, G1OldGenAllocationTracker const* old_gen_alloc_tracker); - - // Most recent time from the end of the concurrent start to the start of the first - // mixed gc. - virtual double last_marking_length_s() const = 0; - - virtual void print_log(size_t non_young_occupancy); - virtual void send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy); - -public: - virtual ~G1IHOPControl() { } - - // Get the current non-young occupancy at which concurrent marking should start. - virtual size_t get_conc_mark_start_threshold() = 0; - - // Adjust target occupancy. - virtual void update_target_occupancy(size_t new_target_occupancy); - // Update information about time during which allocations in the Java heap occurred, - // how large these allocations were in bytes, and an additional buffer. - // The allocations should contain any amount of space made unusable for further - // allocation, e.g. any waste caused by TLAB allocation, space at the end of - // humongous objects that can not be used for allocation, etc. - // Together with the target occupancy, this additional buffer should contain the - // difference between old gen size and total heap size at the start of reclamation, - // and space required for that reclamation. - virtual void update_allocation_info(double allocation_time_s, size_t additional_buffer_size); - // Update the time spent in the mutator beginning from the end of concurrent start to - // the first mixed gc. - virtual void update_marking_length(double marking_length_s) = 0; - - void report_statistics(G1NewTracer* tracer, size_t non_young_occupancy); -}; - -// The returned concurrent mark starting occupancy threshold is a fixed value -// relative to the maximum heap size. -class G1StaticIHOPControl : public G1IHOPControl { - // Most recent mutator time between the end of concurrent mark to the start of the - // first mixed gc. - double _last_marking_length_s; - protected: - double last_marking_length_s() const { return _last_marking_length_s; } - public: - G1StaticIHOPControl(double ihop_percent, G1OldGenAllocationTracker const* old_gen_alloc_tracker); - - size_t get_conc_mark_start_threshold() { - guarantee(_target_occupancy > 0, "Target occupancy must have been initialized."); - return (size_t) (_initial_ihop_percent * _target_occupancy / 100.0); - } - - virtual void update_marking_length(double marking_length_s) { - assert(marking_length_s > 0.0, "Marking length must be larger than zero but is %.3f", marking_length_s); - _last_marking_length_s = marking_length_s; - } -}; - -// This algorithm tries to return a concurrent mark starting occupancy value that -// makes sure that during marking the given target occupancy is never exceeded, -// based on predictions of current allocation rate and time periods between -// concurrent start and the first mixed gc. -class G1AdaptiveIHOPControl : public G1IHOPControl { - size_t _heap_reserve_percent; // Percentage of maximum heap capacity we should avoid to touch - size_t _heap_waste_percent; // Percentage of free heap that should be considered as waste. - - const G1Predictions * _predictor; + const G1Predictions* _predictor; TruncatedSeq _marking_times_s; TruncatedSeq _allocation_rate_s; @@ -128,35 +71,48 @@ class G1AdaptiveIHOPControl : public G1IHOPControl { size_t _last_unrestrained_young_size; // Get a new prediction bounded below by zero from the given sequence. - double predict(TruncatedSeq const* seq) const; + double predict(const TruncatedSeq* seq) const; bool have_enough_data_for_prediction() const; + double last_marking_length_s() const; // The "actual" target threshold the algorithm wants to keep during and at the // end of marking. This is typically lower than the requested threshold, as the // algorithm needs to consider restrictions by the environment. size_t actual_target_threshold() const; - // This method calculates the old gen allocation rate based on the net survived - // bytes that are allocated in the old generation in the last mutator period. - double last_mutator_period_old_allocation_rate() const; - protected: - virtual double last_marking_length_s() const { return _marking_times_s.last(); } - - virtual void print_log(size_t non_young_occupancy); - virtual void send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy); + void print_log(size_t non_young_occupancy); + void send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy); public: - G1AdaptiveIHOPControl(double ihop_percent, - G1OldGenAllocationTracker const* old_gen_alloc_tracker, - G1Predictions const* predictor, - size_t heap_reserve_percent, // The percentage of total heap capacity that should not be tapped into. - size_t heap_waste_percent); // The percentage of the free space in the heap that we think is not usable for allocation. + G1IHOPControl(double ihop_percent, + const G1OldGenAllocationTracker* old_gen_alloc_tracker, + bool adaptive, + const G1Predictions* predictor, + size_t heap_reserve_percent, + size_t heap_waste_percent); + + // Adjust target occupancy. + void update_target_occupancy(size_t new_target_occupancy); + + // Update information about time during which allocations in the Java heap occurred, + // how large these allocations were in bytes, and an additional buffer. + // The allocations should contain any amount of space made unusable for further + // allocation, e.g. any waste caused by TLAB allocation, space at the end of + // humongous objects that can not be used for allocation, etc. + // Together with the target occupancy, this additional buffer should contain the + // difference between old gen size and total heap size at the start of reclamation, + // and space required for that reclamation. + void update_allocation_info(double allocation_time_s, size_t additional_buffer_size); + + // Update the time spent in the mutator beginning from the end of concurrent start to + // the first mixed gc. + void update_marking_length(double marking_length_s); - virtual size_t get_conc_mark_start_threshold(); + // Get the current non-young occupancy at which concurrent marking should start. + size_t get_conc_mark_start_threshold(); - virtual void update_allocation_info(double allocation_time_s, size_t additional_buffer_size); - virtual void update_marking_length(double marking_length_s); + void report_statistics(G1NewTracer* tracer, size_t non_young_occupancy); }; #endif // SHARE_GC_G1_G1IHOPCONTROL_HPP diff --git a/src/hotspot/share/gc/g1/g1OldGenAllocationTracker.hpp b/src/hotspot/share/gc/g1/g1OldGenAllocationTracker.hpp index 265c7029e14..aa5e3c6c942 100644 --- a/src/hotspot/share/gc/g1/g1OldGenAllocationTracker.hpp +++ b/src/hotspot/share/gc/g1/g1OldGenAllocationTracker.hpp @@ -28,8 +28,6 @@ #include "gc/g1/g1HeapRegion.hpp" #include "memory/allocation.hpp" -class G1AdaptiveIHOPControl; - // Track allocation details in the old generation. class G1OldGenAllocationTracker : public CHeapObj { // Total number of bytes allocated in the old generation at the end diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 19573e11cd7..6eef6cbfa87 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -669,7 +669,6 @@ bool G1Policy::should_retain_evac_failed_region(uint index) const { } void G1Policy::record_pause_start_time() { - assert(!_g1h->is_shutting_down(), "Invariant!"); Ticks now = Ticks::now(); _cur_pause_start_sec = now.seconds(); @@ -1026,15 +1025,12 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar G1IHOPControl* G1Policy::create_ihop_control(const G1OldGenAllocationTracker* old_gen_alloc_tracker, const G1Predictions* predictor) { - if (G1UseAdaptiveIHOP) { - return new G1AdaptiveIHOPControl(InitiatingHeapOccupancyPercent, - old_gen_alloc_tracker, - predictor, - G1ReservePercent, - G1HeapWastePercent); - } else { - return new G1StaticIHOPControl(InitiatingHeapOccupancyPercent, old_gen_alloc_tracker); - } + return new G1IHOPControl(InitiatingHeapOccupancyPercent, + old_gen_alloc_tracker, + G1UseAdaptiveIHOP, + predictor, + G1ReservePercent, + G1HeapWastePercent); } bool G1Policy::update_ihop_prediction(double mutator_time_s, @@ -1280,12 +1276,6 @@ void G1Policy::decide_on_concurrent_start_pause() { // concurrent start pause). assert(!collector_state()->in_concurrent_start_gc(), "pre-condition"); - // We should not be starting a concurrent start pause if the concurrent mark - // thread is terminating. - if (_g1h->is_shutting_down()) { - return; - } - if (collector_state()->initiate_conc_mark_if_possible()) { // We had noticed on a previous pause that the heap occupancy has // gone over the initiating threshold and we should start a diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index f0bacefd71c..d0633466f37 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -992,10 +992,11 @@ class G1MergeHeapRootsTask : public WorkerTask { } }; - // Closure to make sure that the marking bitmap is clear for any old region in - // the collection set. - // This is needed to be able to use the bitmap for evacuation failure handling. - class G1ClearBitmapClosure : public G1HeapRegionClosure { + // Closure to prepare the collection set regions for evacuation failure, i.e. make + // sure that the mark bitmap is clear for any old region in the collection set. + // + // These mark bitmaps record the evacuation failed objects. + class G1PrepareRegionsForEvacFailClosure : public G1HeapRegionClosure { G1CollectedHeap* _g1h; G1RemSetScanState* _scan_state; bool _initial_evacuation; @@ -1018,18 +1019,12 @@ 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. - // - // There is one exception: shutdown might have aborted the Concurrent Cleanup for Next - // Mark phase midway, which might have also left stale marks in old generation regions. - // There might actually have been scheduled multiple collections, but at that point we do - // not care that much about performance and just do the work multiple times if needed. - return (_g1h->collector_state()->clear_bitmap_in_progress() || - _g1h->is_shutting_down()) && - hr->is_old(); + return _g1h->collector_state()->clear_bitmap_in_progress() && + hr->is_old(); } public: - G1ClearBitmapClosure(G1CollectedHeap* g1h, G1RemSetScanState* scan_state, bool initial_evacuation) : + G1PrepareRegionsForEvacFailClosure(G1CollectedHeap* g1h, G1RemSetScanState* scan_state, bool initial_evacuation) : _g1h(g1h), _scan_state(scan_state), _initial_evacuation(initial_evacuation) @@ -1178,8 +1173,8 @@ class G1MergeHeapRootsTask : public WorkerTask { // Preparation for evacuation failure handling. { - G1ClearBitmapClosure clear(g1h, _scan_state, _initial_evacuation); - g1h->collection_set_iterate_increment_from(&clear, &_hr_claimer, worker_id); + G1PrepareRegionsForEvacFailClosure prepare_evac_failure(g1h, _scan_state, _initial_evacuation); + g1h->collection_set_iterate_increment_from(&prepare_evac_failure, &_hr_claimer, worker_id); } } }; diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index 747e2f3228c..3a13d0d0535 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -344,11 +344,6 @@ HeapWord* ParallelScavengeHeap::mem_allocate_work(size_t size, bool is_tlab) { assert(is_in_or_null(op.result()), "result not in heap"); return op.result(); } - - if (is_shutting_down()) { - stall_for_vm_shutdown(); - return nullptr; - } } // Was the gc-overhead reached inside the safepoint? If so, this mutator diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp index 0221fd2a90e..5d8ddbcaaed 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp @@ -202,7 +202,6 @@ class ParallelScavengeHeap : public CollectedHeap { bool requires_barriers(stackChunkOop obj) const override; MemRegion reserved_region() const { return _reserved; } - HeapWord* base() const { return _reserved.start(); } // Memory allocation. HeapWord* mem_allocate(size_t size) override; diff --git a/src/hotspot/share/gc/serial/serialHeap.cpp b/src/hotspot/share/gc/serial/serialHeap.cpp index 00d74e691eb..03ad1282f5f 100644 --- a/src/hotspot/share/gc/serial/serialHeap.cpp +++ b/src/hotspot/share/gc/serial/serialHeap.cpp @@ -337,11 +337,6 @@ HeapWord* SerialHeap::mem_allocate_work(size_t size, bool is_tlab) { break; } - if (is_shutting_down()) { - stall_for_vm_shutdown(); - return nullptr; - } - // Give a warning if we seem to be looping forever. if ((QueuedAllocationWarningCount > 0) && (try_count % QueuedAllocationWarningCount == 0)) { diff --git a/src/hotspot/share/gc/shared/collectedHeap.cpp b/src/hotspot/share/gc/shared/collectedHeap.cpp index c8dd39e72be..a59ea3745ab 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.cpp +++ b/src/hotspot/share/gc/shared/collectedHeap.cpp @@ -62,12 +62,14 @@ class ClassLoaderData; +bool CollectedHeap::_is_shutting_down = false; + size_t CollectedHeap::_lab_alignment_reserve = SIZE_MAX; Klass* CollectedHeap::_filler_object_klass = nullptr; size_t CollectedHeap::_filler_array_max_size = 0; size_t CollectedHeap::_stack_chunk_max_size = 0; -class GCLogMessage : public FormatBuffer<512> {}; +class GCLogMessage : public FormatBuffer<1024> {}; template <> void EventLogBase::print(outputStream* st, GCLogMessage& m) { @@ -377,8 +379,7 @@ MetaWord* CollectedHeap::satisfy_failed_metadata_allocation(ClassLoaderData* loa word_size, mdtype, gc_count, - full_gc_count, - GCCause::_metadata_GC_threshold); + full_gc_count); VMThread::execute(&op); @@ -386,11 +387,6 @@ MetaWord* CollectedHeap::satisfy_failed_metadata_allocation(ClassLoaderData* loa return op.result(); } - if (is_shutting_down()) { - stall_for_vm_shutdown(); - return nullptr; - } - loop_count++; if ((QueuedAllocationWarningCount > 0) && (loop_count % QueuedAllocationWarningCount == 0)) { @@ -605,30 +601,20 @@ void CollectedHeap::post_initialize() { initialize_serviceability(); } -bool CollectedHeap::is_shutting_down() const { - return Universe::is_shutting_down(); +bool CollectedHeap::is_shutting_down() { + assert(Heap_lock->owned_by_self(), "Protected by this lock"); + return _is_shutting_down; } -void CollectedHeap::stall_for_vm_shutdown() { - assert(is_shutting_down(), "Precondition"); - // Stall the thread (2 seconds) instead of an indefinite wait to avoid deadlock - // if the VM shutdown triggers a GC. - // The 2-seconds sleep is: - // - long enough to keep daemon threads stalled, while the shutdown - // sequence completes in the common case. - // - short enough to avoid excessive stall time if the shutdown itself - // triggers a GC. - JavaThread::current()->sleep(2 * MILLIUNITS); - - ResourceMark rm; - log_warning(gc, alloc)("%s: Stall for VM-Shutdown timed out; allocation may fail with OOME", Thread::current()->name()); -} +void CollectedHeap::initiate_shutdown() { + { + // Acquire the Heap_lock to synchronize with VM_Heap_Sync_Operations, + // which may depend on the value of _is_shutting_down flag. + MutexLocker hl(Heap_lock); + _is_shutting_down = true; + } -void CollectedHeap::before_exit() { print_tracing_info(); - - // Stop any on-going concurrent work and prepare for exit. - stop(); } size_t CollectedHeap::bootstrap_max_memory() const { diff --git a/src/hotspot/share/gc/shared/collectedHeap.hpp b/src/hotspot/share/gc/shared/collectedHeap.hpp index 6be0057480d..6f335b1cdf4 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.hpp +++ b/src/hotspot/share/gc/shared/collectedHeap.hpp @@ -96,6 +96,8 @@ class CollectedHeap : public CHeapObj { friend class MemAllocator; private: + static bool _is_shutting_down; + GCHeapLog* _heap_log; GCMetaspaceLog* _metaspace_log; @@ -209,11 +211,10 @@ class CollectedHeap : public CHeapObj { // Default implementation does nothing. virtual void print_tracing_info() const = 0; + public: // Stop any onging concurrent work and prepare for exit. virtual void stop() = 0; - public: - static inline size_t filler_array_max_size() { return _filler_array_max_size; } @@ -245,14 +246,9 @@ class CollectedHeap : public CHeapObj { // This is the correct place to place such initialization methods. virtual void post_initialize(); - bool is_shutting_down() const; - - // If the VM is shutting down, we may have skipped VM_CollectForAllocation. - // In this case, stall the allocation request briefly in the hope that - // the VM shutdown completes before the allocation request returns. - void stall_for_vm_shutdown(); + static bool is_shutting_down(); - void before_exit(); + void initiate_shutdown(); // Stop and resume concurrent GC threads interfering with safepoint operations virtual void safepoint_synchronize_begin() {} diff --git a/src/hotspot/share/gc/shared/freeListAllocator.cpp b/src/hotspot/share/gc/shared/freeListAllocator.cpp index c6801c2be18..990bf88aade 100644 --- a/src/hotspot/share/gc/shared/freeListAllocator.cpp +++ b/src/hotspot/share/gc/shared/freeListAllocator.cpp @@ -41,26 +41,26 @@ FreeListAllocator::PendingList::PendingList() : size_t FreeListAllocator::PendingList::add(FreeNode* node) { assert(node->next() == nullptr, "precondition"); - FreeNode* old_head = AtomicAccess::xchg(&_head, node); + FreeNode* old_head = _head.exchange(node); if (old_head != nullptr) { node->set_next(old_head); } else { assert(_tail == nullptr, "invariant"); _tail = node; } - return AtomicAccess::add(&_count, size_t(1)); + return _count.add_then_fetch(1u); } typename FreeListAllocator::NodeList FreeListAllocator::PendingList::take_all() { - NodeList result{AtomicAccess::load(&_head), _tail, AtomicAccess::load(&_count)}; - AtomicAccess::store(&_head, (FreeNode*)nullptr); + NodeList result{_head.load_relaxed(), _tail, _count.load_relaxed()}; + _head.store_relaxed(nullptr); _tail = nullptr; - AtomicAccess::store(&_count, size_t(0)); + _count.store_relaxed(0u); return result; } size_t FreeListAllocator::PendingList::count() const { - return AtomicAccess::load(&_count); + return _count.load_relaxed(); } FreeListAllocator::FreeListAllocator(const char* name, FreeListConfig* config) : @@ -85,7 +85,7 @@ void FreeListAllocator::delete_list(FreeNode* list) { } FreeListAllocator::~FreeListAllocator() { - uint index = AtomicAccess::load(&_active_pending_list); + uint index = _active_pending_list.load_relaxed(); NodeList pending_list = _pending_lists[index].take_all(); delete_list(pending_list._head); delete_list(_free_list.pop_all()); @@ -93,18 +93,18 @@ FreeListAllocator::~FreeListAllocator() { // Drop existing nodes and reset all counters void FreeListAllocator::reset() { - uint index = AtomicAccess::load(&_active_pending_list); + uint index = _active_pending_list.load_relaxed(); _pending_lists[index].take_all(); _free_list.pop_all(); - _free_count = 0; + _free_count.store_relaxed(0u); } size_t FreeListAllocator::free_count() const { - return AtomicAccess::load(&_free_count); + return _free_count.load_relaxed(); } size_t FreeListAllocator::pending_count() const { - uint index = AtomicAccess::load(&_active_pending_list); + uint index = _active_pending_list.load_relaxed(); return _pending_lists[index].count(); } @@ -124,7 +124,7 @@ void* FreeListAllocator::allocate() { // Decrement count after getting buffer from free list. This, along // with incrementing count before adding to free list, ensures count // never underflows. - size_t count = AtomicAccess::sub(&_free_count, 1u); + size_t count = _free_count.sub_then_fetch(1u); assert((count + 1) != 0, "_free_count underflow"); return node; } else { @@ -149,7 +149,7 @@ void FreeListAllocator::release(void* free_node) { // we're done with what might be the pending list to be transferred. { GlobalCounter::CriticalSection cs(Thread::current()); - uint index = AtomicAccess::load_acquire(&_active_pending_list); + uint index = _active_pending_list.load_acquire(); size_t count = _pending_lists[index].add(node); if (count <= _config->transfer_threshold()) return; } @@ -164,17 +164,17 @@ void FreeListAllocator::release(void* free_node) { // in-progress transfer. bool FreeListAllocator::try_transfer_pending() { // Attempt to claim the lock. - if (AtomicAccess::load(&_transfer_lock) || // Skip CAS if likely to fail. - AtomicAccess::cmpxchg(&_transfer_lock, false, true)) { + if (_transfer_lock.load_relaxed() || // Skip CAS if likely to fail. + _transfer_lock.compare_exchange(false, true)) { return false; } // Have the lock; perform the transfer. // Change which pending list is active. Don't need an atomic RMW since // we have the lock and we're the only writer. - uint index = AtomicAccess::load(&_active_pending_list); + uint index = _active_pending_list.load_relaxed(); uint new_active = (index + 1) % ARRAY_SIZE(_pending_lists); - AtomicAccess::release_store(&_active_pending_list, new_active); + _active_pending_list.release_store(new_active); // Wait for all critical sections in the buffer life-cycle to complete. // This includes _free_list pops and adding to the now inactive pending @@ -186,11 +186,11 @@ bool FreeListAllocator::try_transfer_pending() { size_t count = transfer_list._entry_count; if (count > 0) { // Update count first so no underflow in allocate(). - AtomicAccess::add(&_free_count, count); + _free_count.add_then_fetch(count); _free_list.prepend(*transfer_list._head, *transfer_list._tail); log_trace(gc, freelist) ("Transferred %s pending to free: %zu", name(), count); } - AtomicAccess::release_store(&_transfer_lock, false); + _transfer_lock.release_store(false); return true; } diff --git a/src/hotspot/share/gc/shared/freeListAllocator.hpp b/src/hotspot/share/gc/shared/freeListAllocator.hpp index 07e075a6725..dd163f0fe67 100644 --- a/src/hotspot/share/gc/shared/freeListAllocator.hpp +++ b/src/hotspot/share/gc/shared/freeListAllocator.hpp @@ -27,7 +27,7 @@ #include "memory/allocation.hpp" #include "memory/padded.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/lockFreeStack.hpp" @@ -62,15 +62,15 @@ class FreeListConfig { // to the free list making them available for re-allocation. class FreeListAllocator { struct FreeNode { - FreeNode* volatile _next; + Atomic _next; FreeNode() : _next (nullptr) { } - FreeNode* next() { return AtomicAccess::load(&_next); } + FreeNode* next() { return _next.load_relaxed(); } - FreeNode* volatile* next_addr() { return &_next; } + Atomic* next_addr() { return &_next; } - void set_next(FreeNode* next) { AtomicAccess::store(&_next, next); } + void set_next(FreeNode* next) { _next.store_relaxed(next); } }; struct NodeList { @@ -85,8 +85,8 @@ class FreeListAllocator { class PendingList { FreeNode* _tail; - FreeNode* volatile _head; - volatile size_t _count; + Atomic _head; + Atomic _count; NONCOPYABLE(PendingList); @@ -105,20 +105,20 @@ class FreeListAllocator { NodeList take_all(); }; - static FreeNode* volatile* next_ptr(FreeNode& node) { return node.next_addr(); } - typedef LockFreeStack Stack; + static Atomic* next_ptr(FreeNode& node) { return node.next_addr(); } + using Stack = LockFreeStack; FreeListConfig* _config; char _name[DEFAULT_PADDING_SIZE - sizeof(FreeListConfig*)]; // Use name as padding. #define DECLARE_PADDED_MEMBER(Id, Type, Name) \ Type Name; DEFINE_PAD_MINUS_SIZE(Id, DEFAULT_PADDING_SIZE, sizeof(Type)) - DECLARE_PADDED_MEMBER(1, volatile size_t, _free_count); + DECLARE_PADDED_MEMBER(1, Atomic, _free_count); DECLARE_PADDED_MEMBER(2, Stack, _free_list); - DECLARE_PADDED_MEMBER(3, volatile bool, _transfer_lock); + DECLARE_PADDED_MEMBER(3, Atomic, _transfer_lock); #undef DECLARE_PADDED_MEMBER - volatile uint _active_pending_list; + Atomic _active_pending_list; PendingList _pending_lists[2]; void delete_list(FreeNode* list); diff --git a/src/hotspot/share/gc/shared/gcLogPrecious.cpp b/src/hotspot/share/gc/shared/gcLogPrecious.cpp index 43bd58db1aa..d556eed1b69 100644 --- a/src/hotspot/share/gc/shared/gcLogPrecious.cpp +++ b/src/hotspot/share/gc/shared/gcLogPrecious.cpp @@ -25,6 +25,7 @@ #include "runtime/mutex.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/os.hpp" +#include "runtime/thread.hpp" #include "utilities/ostream.hpp" stringStream* GCLogPrecious::_lines = nullptr; @@ -83,7 +84,8 @@ void GCLogPrecious::print_on_error(outputStream* st) { return; } - if (!_lock->try_lock_without_rank_check()) { + if (Thread::current_or_null_safe() == nullptr || + !_lock->try_lock_without_rank_check()) { st->print_cr("\n"); return; } diff --git a/src/hotspot/share/gc/shared/gcVMOperations.cpp b/src/hotspot/share/gc/shared/gcVMOperations.cpp index 36aa0c9843d..6dbfd56b4e9 100644 --- a/src/hotspot/share/gc/shared/gcVMOperations.cpp +++ b/src/hotspot/share/gc/shared/gcVMOperations.cpp @@ -92,6 +92,22 @@ static bool should_use_gclocker() { return UseSerialGC || UseParallelGC; } +static void block_if_java_thread() { + Thread* thread = Thread::current(); + if (thread->is_Java_thread()) { + // Block here and allow the shutdown to complete + while (true) { + // The call to wait has a few important effects: + // 1) Block forever (minus spurious wake-ups, hence the loop) + // 2) Release the Heap_lock, which is taken by the shutdown code + // 3) Transition to blocked state so that the final VM_Exit operation can be scheduled + Heap_lock->wait(); + } + } else { + assert(thread->is_ConcurrentGC_thread(), "Unexpected thread type"); + } +} + bool VM_GC_Operation::doit_prologue() { assert(_gc_cause != GCCause::_no_gc, "Illegal GCCause"); @@ -110,8 +126,15 @@ bool VM_GC_Operation::doit_prologue() { } VM_Heap_Sync_Operation::doit_prologue(); + _is_shutting_down = CollectedHeap::is_shutting_down(); + if (_is_shutting_down) { + // Block forever if a Java thread is triggering a GC after + // the GC has started to shut down. + block_if_java_thread(); + } + // Check invocations - if (skip_operation() || Universe::is_shutting_down()) { + if (skip_operation() || _is_shutting_down) { // skip collection Heap_lock->unlock(); if (should_use_gclocker()) { @@ -197,9 +220,8 @@ VM_CollectForMetadataAllocation::VM_CollectForMetadataAllocation(ClassLoaderData size_t size, Metaspace::MetadataType mdtype, uint gc_count_before, - uint full_gc_count_before, - GCCause::Cause gc_cause) - : VM_GC_Collect_Operation(gc_count_before, gc_cause, full_gc_count_before, true), + uint full_gc_count_before) + : VM_GC_Collect_Operation(gc_count_before, GCCause::_metadata_GC_threshold, full_gc_count_before, true), _result(nullptr), _size(size), _mdtype(mdtype), _loader_data(loader_data) { assert(_size != 0, "An allocation should always be requested with this operation."); AllocTracer::send_allocation_requiring_gc_event(_size * HeapWordSize, GCId::peek()); @@ -208,8 +230,11 @@ VM_CollectForMetadataAllocation::VM_CollectForMetadataAllocation(ClassLoaderData void VM_CollectForMetadataAllocation::doit() { SvcGCMarker sgcm(SvcGCMarker::FULL); - CollectedHeap* heap = Universe::heap(); - GCCauseSetter gccs(heap, _gc_cause); + // Note: GCCauseSetter is intentionally not used here. + // The specific GC cause is set directly in downstream calls that initiate + // collections, allowing us to accurately reflect different situations: + // - A typical metadata allocation failure triggers a collection. + // - As a last resort, a collection clears soft references if prior attempts fail. // Check again if the space is available. Another thread // may have similarly failed a metadata allocation and induced @@ -232,8 +257,10 @@ void VM_CollectForMetadataAllocation::doit() { } #endif + CollectedHeap* heap = Universe::heap(); + // Don't clear the soft refs yet. - heap->collect_as_vm_thread(GCCause::_metadata_GC_threshold); + heap->collect_as_vm_thread(_gc_cause); // After a GC try to allocate without expanding. Could fail // and expansion will be tried below. _result = _loader_data->metaspace_non_null()->allocate(_size, _mdtype); diff --git a/src/hotspot/share/gc/shared/gcVMOperations.hpp b/src/hotspot/share/gc/shared/gcVMOperations.hpp index 5048bc3c1ed..a9aee2faf5d 100644 --- a/src/hotspot/share/gc/shared/gcVMOperations.hpp +++ b/src/hotspot/share/gc/shared/gcVMOperations.hpp @@ -110,23 +110,23 @@ class VM_GC_Operation: public VM_Heap_Sync_Operation { uint _full_gc_count_before; // full gc count before acquiring the Heap_lock bool _full; // whether a "full" collection bool _prologue_succeeded; // whether doit_prologue succeeded + bool _is_shutting_down; // whether the operation found that the GC is shutting down GCCause::Cause _gc_cause; // the putative cause for this gc op virtual bool skip_operation() const; public: VM_GC_Operation(uint gc_count_before, - GCCause::Cause _cause, + GCCause::Cause cause, uint full_gc_count_before, - bool full) : VM_Heap_Sync_Operation() { - _full = full; - _prologue_succeeded = false; - _gc_count_before = gc_count_before; - - _gc_cause = _cause; - - _full_gc_count_before = full_gc_count_before; - } + bool full) + : VM_Heap_Sync_Operation(), + _gc_count_before(gc_count_before), + _full_gc_count_before(full_gc_count_before), + _full(full), + _prologue_succeeded(false), + _is_shutting_down(false), + _gc_cause(cause) {} virtual const char* cause() const; @@ -139,6 +139,14 @@ class VM_GC_Operation: public VM_Heap_Sync_Operation { virtual bool allow_nested_vm_operations() const { return true; } virtual bool gc_succeeded() const { return _prologue_succeeded; } + // This function returns the value of CollectedHeap::is_shutting_down() that + // was recorded in the prologue. Unlike CollectedHeap::is_shutting_down(), + // this function can be called without acquiring the Heap_lock. + // + // This function exists so that code that tries to schedule a GC operation + // can check if it was refused because the JVM is about to shut down. + bool is_shutting_down() const { return _is_shutting_down; } + static void notify_gc_begin(bool full = false); static void notify_gc_end(); }; @@ -214,8 +222,7 @@ class VM_CollectForMetadataAllocation: public VM_GC_Collect_Operation { size_t size, Metaspace::MetadataType mdtype, uint gc_count_before, - uint full_gc_count_before, - GCCause::Cause gc_cause); + uint full_gc_count_before); virtual VMOp_Type type() const { return VMOp_CollectForMetadataAllocation; } virtual void doit(); diff --git a/src/hotspot/share/gc/shared/partialArrayState.cpp b/src/hotspot/share/gc/shared/partialArrayState.cpp index 39c1fe4fc78..6f714d48a35 100644 --- a/src/hotspot/share/gc/shared/partialArrayState.cpp +++ b/src/hotspot/share/gc/shared/partialArrayState.cpp @@ -47,7 +47,7 @@ PartialArrayState::PartialArrayState(oop src, oop dst, } void PartialArrayState::add_references(size_t count) { - size_t new_count = AtomicAccess::add(&_refcount, count, memory_order_relaxed); + size_t new_count = _refcount.add_then_fetch(count, memory_order_relaxed); assert(new_count >= count, "reference count overflow"); } @@ -92,7 +92,7 @@ PartialArrayState* PartialArrayStateAllocator::allocate(oop src, oop dst, } void PartialArrayStateAllocator::release(PartialArrayState* state) { - size_t refcount = AtomicAccess::sub(&state->_refcount, size_t(1), memory_order_release); + size_t refcount = state->_refcount.sub_then_fetch(1u, memory_order_release); if (refcount != 0) { assert(refcount + 1 != 0, "refcount underflow"); } else { @@ -116,25 +116,25 @@ PartialArrayStateManager::~PartialArrayStateManager() { } Arena* PartialArrayStateManager::register_allocator() { - uint idx = AtomicAccess::fetch_then_add(&_registered_allocators, 1u, memory_order_relaxed); + uint idx = _registered_allocators.fetch_then_add(1u, memory_order_relaxed); assert(idx < _max_allocators, "exceeded configured max number of allocators"); return ::new (&_arenas[idx]) Arena(mtGC); } #ifdef ASSERT void PartialArrayStateManager::release_allocator() { - uint old = AtomicAccess::fetch_then_add(&_released_allocators, 1u, memory_order_relaxed); - assert(old < AtomicAccess::load(&_registered_allocators), "too many releases"); + uint old = _released_allocators.fetch_then_add(1u, memory_order_relaxed); + assert(old < _registered_allocators.load_relaxed(), "too many releases"); } #endif // ASSERT void PartialArrayStateManager::reset() { - uint count = AtomicAccess::load(&_registered_allocators); - assert(count == AtomicAccess::load(&_released_allocators), + uint count = _registered_allocators.load_relaxed(); + assert(count == _released_allocators.load_relaxed(), "some allocators still active"); for (uint i = 0; i < count; ++i) { _arenas[i].~Arena(); } - AtomicAccess::store(&_registered_allocators, 0u); - DEBUG_ONLY(AtomicAccess::store(&_released_allocators, 0u);) + _registered_allocators.store_relaxed(0u); + DEBUG_ONLY(_released_allocators.store_relaxed(0u);) } diff --git a/src/hotspot/share/gc/shared/partialArrayState.hpp b/src/hotspot/share/gc/shared/partialArrayState.hpp index 3208c6d6807..3dafeb0f14c 100644 --- a/src/hotspot/share/gc/shared/partialArrayState.hpp +++ b/src/hotspot/share/gc/shared/partialArrayState.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -27,6 +27,7 @@ #include "memory/allocation.hpp" #include "oops/oopsHierarchy.hpp" +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" @@ -60,8 +61,8 @@ class PartialArrayState { oop _source; oop _destination; size_t _length; - volatile size_t _index; - volatile size_t _refcount; + Atomic _index; + Atomic _refcount; friend class PartialArrayStateAllocator; @@ -90,7 +91,7 @@ class PartialArrayState { // A pointer to the start index for the next segment to process, for atomic // update. - volatile size_t* index_addr() { return &_index; } + Atomic* index_addr() { return &_index; } }; // This class provides memory management for PartialArrayStates. @@ -178,8 +179,8 @@ class PartialArrayStateManager : public CHeapObj { // The number of allocators that have been registered/released. // Atomic to support concurrent registration, and concurrent release. // Phasing restriction forbids registration concurrent with release. - volatile uint _registered_allocators; - DEBUG_ONLY(volatile uint _released_allocators;) + Atomic _registered_allocators; + DEBUG_ONLY(Atomic _released_allocators;) // These are all for sole use of the befriended allocator class. Arena* register_allocator(); diff --git a/src/hotspot/share/gc/shared/partialArrayTaskStepper.hpp b/src/hotspot/share/gc/shared/partialArrayTaskStepper.hpp index a68d9bd3612..11499ca2ffe 100644 --- a/src/hotspot/share/gc/shared/partialArrayTaskStepper.hpp +++ b/src/hotspot/share/gc/shared/partialArrayTaskStepper.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -26,6 +26,7 @@ #define SHARE_GC_SHARED_PARTIALARRAYTASKSTEPPER_HPP #include "oops/arrayOop.hpp" +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" class PartialArrayState; @@ -73,7 +74,7 @@ class PartialArrayTaskStepper { uint _task_fanout; // For unit tests. - inline Step next_impl(size_t length, volatile size_t* index_addr) const; + inline Step next_impl(size_t length, Atomic* index_addr) const; }; #endif // SHARE_GC_SHARED_PARTIALARRAYTASKSTEPPER_HPP diff --git a/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp b/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp index 3693abaf8cf..aaa86e2de16 100644 --- a/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp +++ b/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp @@ -46,15 +46,13 @@ PartialArrayTaskStepper::start(size_t length) const { } PartialArrayTaskStepper::Step -PartialArrayTaskStepper::next_impl(size_t length, volatile size_t* index_addr) const { +PartialArrayTaskStepper::next_impl(size_t length, Atomic* index_addr) const { // The start of the next task is in the state's index. // Atomically increment by the chunk size to claim the associated chunk. // Because we limit the number of enqueued tasks to being no more than the // number of remaining chunks to process, we can use an atomic add for the // claim, rather than a CAS loop. - size_t start = AtomicAccess::fetch_then_add(index_addr, - _chunk_size, - memory_order_relaxed); + size_t start = index_addr->fetch_then_add(_chunk_size, memory_order_relaxed); assert(start < length, "invariant: start %zu, length %zu", start, length); assert(((length - start) % _chunk_size) == 0, diff --git a/src/hotspot/share/gc/shared/taskqueue.hpp b/src/hotspot/share/gc/shared/taskqueue.hpp index 1c36e18894a..3a751852ab6 100644 --- a/src/hotspot/share/gc/shared/taskqueue.hpp +++ b/src/hotspot/share/gc/shared/taskqueue.hpp @@ -25,13 +25,16 @@ #ifndef SHARE_GC_SHARED_TASKQUEUE_HPP #define SHARE_GC_SHARED_TASKQUEUE_HPP +#include "cppstdlib/type_traits.hpp" #include "memory/allocation.hpp" #include "memory/padded.hpp" +#include "metaprogramming/primitiveConversions.hpp" #include "oops/oopsHierarchy.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/ostream.hpp" +#include "utilities/powerOfTwo.hpp" #include "utilities/stack.hpp" #if TASKQUEUE_STATS @@ -100,76 +103,92 @@ void TaskQueueStats::reset() { } #endif // TASKQUEUE_STATS +// Helper for TaskQueueSuper, encoding {queue index, tag} pair in a form that +// supports atomic access to the pair. +class TaskQueueAge { + friend struct PrimitiveConversions::Translate; + +public: + // Internal type used for indexing the queue, and for the tag. + using idx_t = NOT_LP64(uint16_t) LP64_ONLY(uint32_t); + + explicit TaskQueueAge(size_t data = 0) : _data{data} {} + TaskQueueAge(idx_t top, idx_t tag) : _fields{top, tag} {} + + idx_t top() const { return _fields._top; } + idx_t tag() const { return _fields._tag; } + + bool operator==(const TaskQueueAge& other) const { return _data == other._data; } + +private: + struct Fields { + idx_t _top; + idx_t _tag; + }; + union { + size_t _data; // Provides access to _fields as a single integral value. + Fields _fields; + }; + // _data must be able to hold combined _fields. Must be equal to ensure + // there isn't any padding that could be uninitialized by 2-arg ctor. + static_assert(sizeof(_data) == sizeof(_fields)); +}; + +// Support for Atomic. +template<> +struct PrimitiveConversions::Translate : public std::true_type { + using Value = TaskQueueAge; + using Decayed = decltype(TaskQueueAge::_data); + + static Decayed decay(Value x) { return x._data; } + static Value recover(Decayed x) { return Value(x); } +}; + // TaskQueueSuper collects functionality common to all GenericTaskQueue instances. template class TaskQueueSuper: public CHeapObj { protected: - // Internal type for indexing the queue; also used for the tag. - typedef NOT_LP64(uint16_t) LP64_ONLY(uint32_t) idx_t; - STATIC_ASSERT(N == idx_t(N)); // Ensure N fits in an idx_t. + using Age = TaskQueueAge; + using idx_t = Age::idx_t; + static_assert(N == idx_t(N)); // Ensure N fits in an idx_t. // N must be a power of 2 for computing modulo via masking. // N must be >= 2 for the algorithm to work at all, though larger is better. - STATIC_ASSERT(N >= 2); - STATIC_ASSERT(is_power_of_2(N)); + static_assert(N >= 2); + static_assert(is_power_of_2(N)); static const uint MOD_N_MASK = N - 1; - class Age { - friend class TaskQueueSuper; - - public: - explicit Age(size_t data = 0) : _data(data) {} - Age(idx_t top, idx_t tag) { _fields._top = top; _fields._tag = tag; } - - idx_t top() const { return _fields._top; } - idx_t tag() const { return _fields._tag; } - - bool operator ==(const Age& other) const { return _data == other._data; } - - private: - struct fields { - idx_t _top; - idx_t _tag; - }; - union { - size_t _data; - fields _fields; - }; - STATIC_ASSERT(sizeof(size_t) >= sizeof(fields)); - }; - uint bottom_relaxed() const { - return AtomicAccess::load(&_bottom); + return _bottom.load_relaxed(); } uint bottom_acquire() const { - return AtomicAccess::load_acquire(&_bottom); + return _bottom.load_acquire(); } void set_bottom_relaxed(uint new_bottom) { - AtomicAccess::store(&_bottom, new_bottom); + _bottom.store_relaxed(new_bottom); } void release_set_bottom(uint new_bottom) { - AtomicAccess::release_store(&_bottom, new_bottom); + _bottom.release_store(new_bottom); } Age age_relaxed() const { - return Age(AtomicAccess::load(&_age._data)); + return _age.load_relaxed(); } void set_age_relaxed(Age new_age) { - AtomicAccess::store(&_age._data, new_age._data); + _age.store_relaxed(new_age); } Age cmpxchg_age(Age old_age, Age new_age) { - return Age(AtomicAccess::cmpxchg(&_age._data, old_age._data, new_age._data)); + return _age.compare_exchange(old_age, new_age); } idx_t age_top_relaxed() const { - // Atomically accessing a subfield of an "atomic" member. - return AtomicAccess::load(&_age._fields._top); + return _age.load_relaxed().top(); } // These both operate mod N. @@ -222,16 +241,16 @@ class TaskQueueSuper: public CHeapObj { DEFINE_PAD_MINUS_SIZE(0, DEFAULT_PADDING_SIZE, 0); // Index of the first free element after the last one pushed (mod N). - volatile uint _bottom; - DEFINE_PAD_MINUS_SIZE(1, DEFAULT_PADDING_SIZE, sizeof(uint)); + Atomic _bottom; + DEFINE_PAD_MINUS_SIZE(1, DEFAULT_PADDING_SIZE, sizeof(_bottom)); // top() is the index of the oldest pushed element (mod N), and tag() // is the associated epoch, to distinguish different modifications of // the age. There is no available element if top() == _bottom or // (_bottom - top()) mod N == N-1; the latter indicates underflow // during concurrent pop_local/pop_global. - volatile Age _age; - DEFINE_PAD_MINUS_SIZE(2, DEFAULT_PADDING_SIZE, sizeof(Age)); + Atomic _age; + DEFINE_PAD_MINUS_SIZE(2, DEFAULT_PADDING_SIZE, sizeof(_age)); NONCOPYABLE(TaskQueueSuper); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAllocRequest.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAllocRequest.hpp index 78ae78f4c24..05ecfb254a2 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAllocRequest.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAllocRequest.hpp @@ -31,15 +31,37 @@ class ShenandoahAllocRequest : StackObj { public: - enum Type { - _alloc_shared, // Allocate common, outside of TLAB - _alloc_shared_gc, // Allocate common, outside of GCLAB/PLAB - _alloc_cds, // Allocate for CDS - _alloc_tlab, // Allocate TLAB - _alloc_gclab, // Allocate GCLAB - _alloc_plab, // Allocate PLAB - _ALLOC_LIMIT - }; + // Alloc type is an int value with encoded bits in scheme as: + // [x|xx|xx|xx] + // ^---- Requester: + // 00 -- mutator + // 10 -- mutator (CDS) + // 01 -- GC + // ^------- Purpose: + // 00 -- shared + // 01 -- TLAB/GCLAB + // 11 -- PLAB + // ^---------- Affiliation: + // 00 -- YOUNG + // 01 -- OLD + // 11 -- OLD, promotion + typedef int Type; + + static constexpr int bit_gc_alloc = 1 << 0; + static constexpr int bit_cds_alloc = 1 << 1; + static constexpr int bit_lab_alloc = 1 << 2; + static constexpr int bit_plab_alloc = 1 << 3; + static constexpr int bit_old_alloc = 1 << 4; + static constexpr int bit_promotion_alloc = 1 << 5; + + static constexpr Type _alloc_shared = 0; + static constexpr Type _alloc_tlab = bit_lab_alloc; + static constexpr Type _alloc_cds = bit_cds_alloc; + static constexpr Type _alloc_shared_gc = bit_gc_alloc; + static constexpr Type _alloc_shared_gc_old = bit_gc_alloc | bit_old_alloc; + static constexpr Type _alloc_shared_gc_promotion = bit_gc_alloc | bit_old_alloc | bit_promotion_alloc; + static constexpr Type _alloc_gclab = bit_gc_alloc | bit_lab_alloc; + static constexpr Type _alloc_plab = bit_gc_alloc | bit_lab_alloc | bit_plab_alloc | bit_old_alloc; static const char* alloc_type_to_string(Type type) { switch (type) { @@ -47,6 +69,10 @@ class ShenandoahAllocRequest : StackObj { return "Shared"; case _alloc_shared_gc: return "Shared GC"; + case _alloc_shared_gc_old: + return "Shared GC Old"; + case _alloc_shared_gc_promotion: + return "Shared GC Promotion"; case _alloc_cds: return "CDS"; case _alloc_tlab: @@ -80,20 +106,14 @@ class ShenandoahAllocRequest : StackObj { // This is the type of the request. Type _alloc_type; - // This is the generation which the request is targeting. - ShenandoahAffiliation const _affiliation; - - // True if this request is trying to copy any object from young to old (promote). - bool _is_promotion; - #ifdef ASSERT // Check that this is set before being read. bool _actual_size_set; #endif - ShenandoahAllocRequest(size_t _min_size, size_t _requested_size, Type _alloc_type, ShenandoahAffiliation affiliation, bool is_promotion = false) : + ShenandoahAllocRequest(size_t _min_size, size_t _requested_size, Type _alloc_type) : _min_size(_min_size), _requested_size(_requested_size), - _actual_size(0), _waste(0), _alloc_type(_alloc_type), _affiliation(affiliation), _is_promotion(is_promotion) + _actual_size(0), _waste(0), _alloc_type(_alloc_type) #ifdef ASSERT , _actual_size_set(false) #endif @@ -101,31 +121,34 @@ class ShenandoahAllocRequest : StackObj { public: static inline ShenandoahAllocRequest for_tlab(size_t min_size, size_t requested_size) { - return ShenandoahAllocRequest(min_size, requested_size, _alloc_tlab, ShenandoahAffiliation::YOUNG_GENERATION); + return ShenandoahAllocRequest(min_size, requested_size, _alloc_tlab); } static inline ShenandoahAllocRequest for_gclab(size_t min_size, size_t requested_size) { - return ShenandoahAllocRequest(min_size, requested_size, _alloc_gclab, ShenandoahAffiliation::YOUNG_GENERATION); + return ShenandoahAllocRequest(min_size, requested_size, _alloc_gclab); } static inline ShenandoahAllocRequest for_plab(size_t min_size, size_t requested_size) { - return ShenandoahAllocRequest(min_size, requested_size, _alloc_plab, ShenandoahAffiliation::OLD_GENERATION); + return ShenandoahAllocRequest(min_size, requested_size, _alloc_plab); } static inline ShenandoahAllocRequest for_shared_gc(size_t requested_size, ShenandoahAffiliation affiliation, bool is_promotion = false) { if (is_promotion) { - assert(affiliation == ShenandoahAffiliation::OLD_GENERATION, "Should only promote to old generation"); - return ShenandoahAllocRequest(0, requested_size, _alloc_shared_gc, affiliation, true); + assert(affiliation == OLD_GENERATION, "Should only promote to old generation"); + return ShenandoahAllocRequest(0, requested_size, _alloc_shared_gc_promotion); } - return ShenandoahAllocRequest(0, requested_size, _alloc_shared_gc, affiliation); + if (affiliation == OLD_GENERATION) { + return ShenandoahAllocRequest(0, requested_size, _alloc_shared_gc_old); + } + return ShenandoahAllocRequest(0, requested_size, _alloc_shared_gc); } static inline ShenandoahAllocRequest for_shared(size_t requested_size) { - return ShenandoahAllocRequest(0, requested_size, _alloc_shared, ShenandoahAffiliation::YOUNG_GENERATION); + return ShenandoahAllocRequest(0, requested_size, _alloc_shared); } static inline ShenandoahAllocRequest for_cds(size_t requested_size) { - return ShenandoahAllocRequest(0, requested_size, _alloc_cds, ShenandoahAffiliation::YOUNG_GENERATION); + return ShenandoahAllocRequest(0, requested_size, _alloc_cds); } inline size_t size() const { @@ -167,71 +190,35 @@ class ShenandoahAllocRequest : StackObj { } inline bool is_mutator_alloc() const { - switch (_alloc_type) { - case _alloc_tlab: - case _alloc_shared: - case _alloc_cds: - return true; - case _alloc_gclab: - case _alloc_plab: - case _alloc_shared_gc: - return false; - default: - ShouldNotReachHere(); - return false; - } + return (_alloc_type & bit_gc_alloc) == 0; } inline bool is_gc_alloc() const { - switch (_alloc_type) { - case _alloc_tlab: - case _alloc_shared: - case _alloc_cds: - return false; - case _alloc_gclab: - case _alloc_plab: - case _alloc_shared_gc: - return true; - default: - ShouldNotReachHere(); - return false; - } + return (_alloc_type & bit_gc_alloc) != 0; } inline bool is_lab_alloc() const { - switch (_alloc_type) { - case _alloc_tlab: - case _alloc_gclab: - case _alloc_plab: - return true; - case _alloc_shared: - case _alloc_shared_gc: - case _alloc_cds: - return false; - default: - ShouldNotReachHere(); - return false; - } + return (_alloc_type & bit_lab_alloc) != 0; } - bool is_old() const { - return _affiliation == OLD_GENERATION; + inline bool is_old() const { + return (_alloc_type & bit_old_alloc) != 0; } - bool is_young() const { - return _affiliation == YOUNG_GENERATION; + inline bool is_young() const { + return (_alloc_type & bit_old_alloc) == 0; } - ShenandoahAffiliation affiliation() const { - return _affiliation; + inline ShenandoahAffiliation affiliation() const { + return (_alloc_type & bit_old_alloc) == 0 ? YOUNG_GENERATION : OLD_GENERATION ; } const char* affiliation_name() const { - return shenandoah_affiliation_name(_affiliation); + return shenandoah_affiliation_name(affiliation()); } - bool is_promotion() const { - return _is_promotion; + inline bool is_promotion() const { + return (_alloc_type & bit_promotion_alloc) != 0; } }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp index a7cf8e638dd..c1fa4b964b7 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp @@ -37,6 +37,7 @@ #include "runtime/globals_extension.hpp" #include "runtime/java.hpp" #include "utilities/defaultStream.hpp" +#include "utilities/powerOfTwo.hpp" void ShenandoahArguments::initialize() { #if !(defined AARCH64 || defined AMD64 || defined PPC64 || defined RISCV64) @@ -205,7 +206,7 @@ void ShenandoahArguments::initialize() { } size_t ShenandoahArguments::conservative_max_heap_alignment() { - size_t align = ShenandoahMaxRegionSize; + size_t align = next_power_of_2(ShenandoahMaxRegionSize); if (UseLargePages) { align = MAX2(align, os::large_page_size()); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp index 0d38cc757f4..2b5bc766a46 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp @@ -129,7 +129,7 @@ class ShenandoahBarrierSet: public BarrierSet { private: template - inline void arraycopy_marking(T* src, T* dst, size_t count, bool is_old_marking); + inline void arraycopy_marking(T* dst, size_t count); template inline void arraycopy_evacuation(T* src, size_t count); template diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp index b176446452a..adeea8ebf96 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp @@ -387,13 +387,11 @@ template void ShenandoahBarrierSet::arraycopy_work(T* src, size_t count) { // Young cycles are allowed to run when old marking is in progress. When old marking is in progress, // this barrier will be called with ENQUEUE=true and HAS_FWD=false, even though the young generation - // may have forwarded objects. In this case, the `arraycopy_work` is first called with HAS_FWD=true and - // ENQUEUE=false. - assert(HAS_FWD == _heap->has_forwarded_objects() || _heap->is_concurrent_old_mark_in_progress(), - "Forwarded object status is sane"); + // may have forwarded objects. + assert(HAS_FWD == _heap->has_forwarded_objects() || _heap->is_concurrent_old_mark_in_progress(), "Forwarded object status is sane"); // This function cannot be called to handle marking and evacuation at the same time (they operate on // different sides of the copy). - assert((HAS_FWD || EVAC) != ENQUEUE, "Cannot evacuate and mark both sides of copy."); + static_assert((HAS_FWD || EVAC) != ENQUEUE, "Cannot evacuate and mark both sides of copy."); Thread* thread = Thread::current(); SATBMarkQueue& queue = ShenandoahThreadLocalData::satb_mark_queue(thread); @@ -412,7 +410,7 @@ void ShenandoahBarrierSet::arraycopy_work(T* src, size_t count) { shenandoah_assert_forwarded_except(elem_ptr, obj, _heap->cancelled_gc()); ShenandoahHeap::atomic_update_oop(fwd, elem_ptr, o); } - if (ENQUEUE && !ctx->is_marked_strong_or_old(obj)) { + if (ENQUEUE && !ctx->is_marked_strong(obj)) { _satb_mark_queue_set.enqueue_known_active(queue, obj); } } @@ -426,68 +424,29 @@ void ShenandoahBarrierSet::arraycopy_barrier(T* src, T* dst, size_t count) { return; } - char gc_state = ShenandoahThreadLocalData::gc_state(Thread::current()); + const char gc_state = ShenandoahThreadLocalData::gc_state(Thread::current()); + if ((gc_state & ShenandoahHeap::MARKING) != 0) { + // If marking old or young, we must evaluate the SATB barrier. This will be the only + // action if we are not marking old. If we are marking old, we must still evaluate the + // load reference barrier for a young collection. + arraycopy_marking(dst, count); + } + if ((gc_state & ShenandoahHeap::EVACUATION) != 0) { + assert((gc_state & ShenandoahHeap::YOUNG_MARKING) == 0, "Cannot be marking young during evacuation"); arraycopy_evacuation(src, count); } else if ((gc_state & ShenandoahHeap::UPDATE_REFS) != 0) { + assert((gc_state & ShenandoahHeap::YOUNG_MARKING) == 0, "Cannot be marking young during update-refs"); arraycopy_update(src, count); } - - if (_heap->mode()->is_generational()) { - assert(ShenandoahSATBBarrier, "Generational mode assumes SATB mode"); - if ((gc_state & ShenandoahHeap::YOUNG_MARKING) != 0) { - arraycopy_marking(src, dst, count, false); - } - if ((gc_state & ShenandoahHeap::OLD_MARKING) != 0) { - arraycopy_marking(src, dst, count, true); - } - } else if ((gc_state & ShenandoahHeap::MARKING) != 0) { - arraycopy_marking(src, dst, count, false); - } } template -void ShenandoahBarrierSet::arraycopy_marking(T* src, T* dst, size_t count, bool is_old_marking) { +void ShenandoahBarrierSet::arraycopy_marking(T* dst, size_t count) { assert(_heap->is_concurrent_mark_in_progress(), "only during marking"); - /* - * Note that an old-gen object is considered live if it is live at the start of OLD marking or if it is promoted - * following the start of OLD marking. - * - * 1. Every object promoted following the start of OLD marking will be above TAMS within its old-gen region - * 2. Every object live at the start of OLD marking will be referenced from a "root" or it will be referenced from - * another live OLD-gen object. With regards to old-gen, roots include stack locations and all of live young-gen. - * All root references to old-gen are identified during a bootstrap young collection. All references from other - * old-gen objects will be marked during the traversal of all old objects, or will be marked by the SATB barrier. - * - * During old-gen marking (which is interleaved with young-gen collections), call arraycopy_work() if: - * - * 1. The overwritten array resides in old-gen and it is below TAMS within its old-gen region - * 2. Do not call arraycopy_work for any array residing in young-gen because young-gen collection is idle at this time - * - * During young-gen marking, call arraycopy_work() if: - * - * 1. The overwritten array resides in young-gen and is below TAMS within its young-gen region - * 2. Additionally, if array resides in old-gen, regardless of its relationship to TAMS because this old-gen array - * may hold references to young-gen - */ if (ShenandoahSATBBarrier) { - T* array = dst; - HeapWord* array_addr = reinterpret_cast(array); - ShenandoahHeapRegion* r = _heap->heap_region_containing(array_addr); - if (is_old_marking) { - // Generational, old marking - assert(_heap->mode()->is_generational(), "Invariant"); - if (r->is_old() && (array_addr < _heap->marking_context()->top_at_mark_start(r))) { - arraycopy_work(array, count); - } - } else if (_heap->mode()->is_generational()) { - // Generational, young marking - if (r->is_old() || (array_addr < _heap->marking_context()->top_at_mark_start(r))) { - arraycopy_work(array, count); - } - } else if (array_addr < _heap->marking_context()->top_at_mark_start(r)) { - // Non-generational, marking - arraycopy_work(array, count); + if (!_heap->marking_context()->allocated_after_mark_start(reinterpret_cast(dst))) { + arraycopy_work(dst, count); } } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp index 0deb3b5ba4c..ab7985b3d34 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp @@ -1311,19 +1311,11 @@ HeapWord* ShenandoahFreeSet::allocate_single(ShenandoahAllocRequest& req, bool& // Overwrite with non-zero (non-null) values only if necessary for allocation bookkeeping. - switch (req.type()) { - case ShenandoahAllocRequest::_alloc_tlab: - case ShenandoahAllocRequest::_alloc_shared: - case ShenandoahAllocRequest::_alloc_cds: - return allocate_for_mutator(req, in_new_region); - case ShenandoahAllocRequest::_alloc_gclab: - case ShenandoahAllocRequest::_alloc_plab: - case ShenandoahAllocRequest::_alloc_shared_gc: - return allocate_for_collector(req, in_new_region); - default: - ShouldNotReachHere(); + if (req.is_mutator_alloc()) { + return allocate_for_mutator(req, in_new_region); + } else { + return allocate_for_collector(req, in_new_region); } - return nullptr; } HeapWord* ShenandoahFreeSet::allocate_for_mutator(ShenandoahAllocRequest &req, bool &in_new_region) { @@ -1619,21 +1611,13 @@ HeapWord* ShenandoahFreeSet::try_allocate_in(ShenandoahHeapRegion* r, Shenandoah if (req.is_mutator_alloc()) { request_generation = _heap->mode()->is_generational()? _heap->young_generation(): _heap->global_generation(); orig_partition = ShenandoahFreeSetPartitionId::Mutator; - } else if (req.type() == ShenandoahAllocRequest::_alloc_gclab) { - request_generation = _heap->mode()->is_generational()? _heap->young_generation(): _heap->global_generation(); - orig_partition = ShenandoahFreeSetPartitionId::Collector; - } else if (req.type() == ShenandoahAllocRequest::_alloc_plab) { + } else if (req.is_old()) { request_generation = _heap->old_generation(); orig_partition = ShenandoahFreeSetPartitionId::OldCollector; } else { - assert(req.type() == ShenandoahAllocRequest::_alloc_shared_gc, "Unexpected allocation type"); - if (req.is_old()) { - request_generation = _heap->old_generation(); - orig_partition = ShenandoahFreeSetPartitionId::OldCollector; - } else { - request_generation = _heap->mode()->is_generational()? _heap->young_generation(): _heap->global_generation(); - orig_partition = ShenandoahFreeSetPartitionId::Collector; - } + // Not old collector alloc, so this is a young collector gclab or shared allocation + request_generation = _heap->mode()->is_generational()? _heap->young_generation(): _heap->global_generation(); + orig_partition = ShenandoahFreeSetPartitionId::Collector; } if (alloc_capacity(r) < PLAB::min_size() * HeapWordSize) { // Regardless of whether this allocation succeeded, if the remaining memory is less than PLAB:min_size(), retire this region. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 22a59b5ac18..5c4d10ca8a5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -1015,7 +1015,7 @@ HeapWord* ShenandoahHeap::allocate_memory_under_lock(ShenandoahAllocRequest& req // Record the plab configuration for this result and register the object. if (result != nullptr && req.is_old()) { old_generation()->configure_plab_for_current_thread(req); - if (req.type() == ShenandoahAllocRequest::_alloc_shared_gc) { + if (!req.is_lab_alloc()) { // Register the newly allocated object while we're holding the global lock since there's no synchronization // built in to the implementation of register_object(). There are potential races when multiple independent // threads are allocating objects, some of which might span the same card region. For example, consider diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp index cad9dc0e932..636f65e2553 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp @@ -129,6 +129,8 @@ inline void ShenandoahHeapRegion::adjust_alloc_metadata(ShenandoahAllocRequest:: switch (type) { case ShenandoahAllocRequest::_alloc_shared: case ShenandoahAllocRequest::_alloc_shared_gc: + case ShenandoahAllocRequest::_alloc_shared_gc_old: + case ShenandoahAllocRequest::_alloc_shared_gc_promotion: case ShenandoahAllocRequest::_alloc_cds: // Counted implicitly by tlab/gclab allocs break; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp index 34713898fc6..44064dbd1a9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp @@ -250,6 +250,8 @@ HeapWord* ShenandoahCardCluster::first_object_start(const size_t card_index, con HeapWord* right = MIN2(region->top(), end_range_of_interest); HeapWord* end_of_search_next = MIN2(right, tams); + // Since end_range_of_interest may not align on a card boundary, last_relevant_card_index is conservative. Not all of the + // memory within the last relevant card's span is < right. size_t last_relevant_card_index; if (end_range_of_interest == _end_of_heap) { last_relevant_card_index = _rs->card_index_for_addr(end_range_of_interest - 1); @@ -352,9 +354,8 @@ HeapWord* ShenandoahCardCluster::first_object_start(const size_t card_index, con return nullptr; } } while (!starts_object(following_card_index)); - assert(_rs->addr_for_card_index(following_card_index) + get_first_start(following_card_index), - "Result must precede right"); - return _rs->addr_for_card_index(following_card_index) + get_first_start(following_card_index); + HeapWord* result_candidate = _rs->addr_for_card_index(following_card_index) + get_first_start(following_card_index); + return (result_candidate >= right)? nullptr: result_candidate; } } } diff --git a/src/hotspot/share/gc/z/zBarrier.inline.hpp b/src/hotspot/share/gc/z/zBarrier.inline.hpp index b5923f01628..766a6eb8e4c 100644 --- a/src/hotspot/share/gc/z/zBarrier.inline.hpp +++ b/src/hotspot/share/gc/z/zBarrier.inline.hpp @@ -86,10 +86,6 @@ inline void ZBarrier::self_heal(ZBarrierFastPath fast_path, volatile zpointer* p assert(ZPointer::is_remapped(heal_ptr), "invariant"); for (;;) { - if (ptr == zpointer::null) { - assert(!ZVerifyOops || !ZHeap::heap()->is_in(uintptr_t(p)) || !ZHeap::heap()->is_old(p), "No raw null in old"); - } - assert_transition_monotonicity(ptr, heal_ptr); // Heal diff --git a/src/hotspot/share/gc/z/zBarrierSet.cpp b/src/hotspot/share/gc/z/zBarrierSet.cpp index 87f93043bdf..643eba1947e 100644 --- a/src/hotspot/share/gc/z/zBarrierSet.cpp +++ b/src/hotspot/share/gc/z/zBarrierSet.cpp @@ -223,27 +223,7 @@ void ZBarrierSet::on_slowpath_allocation_exit(JavaThread* thread, oop new_obj) { // breaks that promise. Take a few steps in the interpreter instead, which has // no such assumptions about where an object resides. deoptimize_allocation(thread); - return; } - - if (!ZGeneration::young()->is_phase_mark_complete()) { - return; - } - - if (!page->is_relocatable()) { - return; - } - - if (ZRelocate::compute_to_age(age) != ZPageAge::old) { - return; - } - - // If the object is young, we have to still be careful that it isn't racingly - // about to get promoted to the old generation. That causes issues when null - // pointers are supposed to be coloured, but the JIT is a bit sloppy and - // reinitializes memory with raw nulls. We detect this situation and detune - // rather than relying on the JIT to never be sloppy with redundant initialization. - deoptimize_allocation(thread); } void ZBarrierSet::print_on(outputStream* st) const { diff --git a/src/hotspot/share/gc/z/zGeneration.cpp b/src/hotspot/share/gc/z/zGeneration.cpp index d1680b6c336..2b632ef29a9 100644 --- a/src/hotspot/share/gc/z/zGeneration.cpp +++ b/src/hotspot/share/gc/z/zGeneration.cpp @@ -111,6 +111,16 @@ static const ZStatSampler ZSamplerJavaThreads("System", "Java Threads", ZStatUni ZGenerationYoung* ZGeneration::_young; ZGenerationOld* ZGeneration::_old; +class ZRendezvousHandshakeClosure : public HandshakeClosure { +public: + ZRendezvousHandshakeClosure() + : HandshakeClosure("ZRendezvous") {} + + void do_thread(Thread* thread) { + // Does nothing + } +}; + ZGeneration::ZGeneration(ZGenerationId id, ZPageTable* page_table, ZPageAllocator* page_allocator) : _id(id), _page_allocator(page_allocator), @@ -168,11 +178,19 @@ void ZGeneration::free_empty_pages(ZRelocationSetSelector* selector, int bulk) { } void ZGeneration::flip_age_pages(const ZRelocationSetSelector* selector) { - if (is_young()) { - _relocate.flip_age_pages(selector->not_selected_small()); - _relocate.flip_age_pages(selector->not_selected_medium()); - _relocate.flip_age_pages(selector->not_selected_large()); - } + _relocate.flip_age_pages(selector->not_selected_small()); + _relocate.flip_age_pages(selector->not_selected_medium()); + _relocate.flip_age_pages(selector->not_selected_large()); + + // Perform a handshake between flip promotion and running the promotion barrier. This ensures + // that ZBarrierSet::on_slowpath_allocation_exit() observing a young page that was then racingly + // flip promoted, will run any stores without barriers to completion before responding to the + // handshake at the subsequent safepoint poll. This ensures that the flip promotion barriers always + // run after compiled code missing barriers, but before relocate start. + ZRendezvousHandshakeClosure cl; + Handshake::execute(&cl); + + _relocate.barrier_flip_promoted_pages(_relocation_set.flip_promoted_pages()); } static double fragmentation_limit(ZGenerationId generation) { @@ -235,7 +253,9 @@ void ZGeneration::select_relocation_set(bool promote_all) { _relocation_set.install(&selector); // Flip age young pages that were not selected - flip_age_pages(&selector); + if (is_young()) { + flip_age_pages(&selector); + } // Setup forwarding table ZRelocationSetIterator rs_iter(&_relocation_set); @@ -1280,16 +1300,6 @@ bool ZGenerationOld::uses_clear_all_soft_reference_policy() const { return _reference_processor.uses_clear_all_soft_reference_policy(); } -class ZRendezvousHandshakeClosure : public HandshakeClosure { -public: - ZRendezvousHandshakeClosure() - : HandshakeClosure("ZRendezvous") {} - - void do_thread(Thread* thread) { - // Does nothing - } -}; - class ZRendezvousGCThreads: public VM_Operation { public: VMOp_Type type() const { return VMOp_ZRendezvousGCThreads; } diff --git a/src/hotspot/share/gc/z/zRelocate.cpp b/src/hotspot/share/gc/z/zRelocate.cpp index 69233da6f54..180ce22b041 100644 --- a/src/hotspot/share/gc/z/zRelocate.cpp +++ b/src/hotspot/share/gc/z/zRelocate.cpp @@ -1322,7 +1322,7 @@ class ZFlipAgePagesTask : public ZTask { public: ZFlipAgePagesTask(const ZArray* pages) - : ZTask("ZPromotePagesTask"), + : ZTask("ZFlipAgePagesTask"), _iter(pages) {} virtual void work() { @@ -1337,16 +1337,6 @@ class ZFlipAgePagesTask : public ZTask { // Figure out if this is proper promotion const bool promotion = to_age == ZPageAge::old; - if (promotion) { - // Before promoting an object (and before relocate start), we must ensure that all - // contained zpointers are store good. The marking code ensures that for non-null - // pointers, but null pointers are ignored. This code ensures that even null pointers - // are made store good, for the promoted objects. - prev_page->object_iterate([&](oop obj) { - ZIterator::basic_oop_iterate_safe(obj, ZBarrier::promote_barrier_on_young_oop_field); - }); - } - // Logging prev_page->log_msg(promotion ? " (flip promoted)" : " (flip survived)"); @@ -1360,7 +1350,7 @@ class ZFlipAgePagesTask : public ZTask { if (promotion) { ZGeneration::young()->flip_promote(prev_page, new_page); - // Defer promoted page registration times the lock is taken + // Defer promoted page registration promoted_pages.push(prev_page); } @@ -1371,11 +1361,42 @@ class ZFlipAgePagesTask : public ZTask { } }; +class ZPromoteBarrierTask : public ZTask { +private: + ZArrayParallelIterator _iter; + +public: + ZPromoteBarrierTask(const ZArray* pages) + : ZTask("ZPromoteBarrierTask"), + _iter(pages) {} + + virtual void work() { + SuspendibleThreadSetJoiner sts_joiner; + + for (ZPage* page; _iter.next(&page);) { + // When promoting an object (and before relocate start), we must ensure that all + // contained zpointers are store good. The marking code ensures that for non-null + // pointers, but null pointers are ignored. This code ensures that even null pointers + // are made store good, for the promoted objects. + page->object_iterate([&](oop obj) { + ZIterator::basic_oop_iterate_safe(obj, ZBarrier::promote_barrier_on_young_oop_field); + }); + + SuspendibleThreadSet::yield(); + } + } +}; + void ZRelocate::flip_age_pages(const ZArray* pages) { ZFlipAgePagesTask flip_age_task(pages); workers()->run(&flip_age_task); } +void ZRelocate::barrier_flip_promoted_pages(const ZArray* pages) { + ZPromoteBarrierTask promote_barrier_task(pages); + workers()->run(&promote_barrier_task); +} + void ZRelocate::synchronize() { _queue.synchronize(); } diff --git a/src/hotspot/share/gc/z/zRelocate.hpp b/src/hotspot/share/gc/z/zRelocate.hpp index d0ddf7deecf..50111f24ee5 100644 --- a/src/hotspot/share/gc/z/zRelocate.hpp +++ b/src/hotspot/share/gc/z/zRelocate.hpp @@ -119,6 +119,7 @@ class ZRelocate { void relocate(ZRelocationSet* relocation_set); void flip_age_pages(const ZArray* pages); + void barrier_flip_promoted_pages(const ZArray* pages); void synchronize(); void desynchronize(); diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.cpp index f7a725fce6d..534c9996cfe 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.cpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.cpp @@ -217,7 +217,8 @@ static bool compute_top_frame(const JfrSampleRequest& request, frame& top_frame, const PcDesc* const pc_desc = get_pc_desc(sampled_nm, sampled_pc); if (is_valid(pc_desc)) { intptr_t* const synthetic_sp = sender_sp - sampled_nm->frame_size(); - top_frame = frame(synthetic_sp, synthetic_sp, sender_sp, pc_desc->real_pc(sampled_nm), sampled_nm); + intptr_t* const synthetic_fp = sender_sp AARCH64_ONLY( - frame::sender_sp_offset); + top_frame = frame(synthetic_sp, synthetic_sp, synthetic_fp, pc_desc->real_pc(sampled_nm), sampled_nm); in_continuation = is_in_continuation(top_frame, jt); return true; } diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp index d389fe81806..4d2897be5eb 100644 --- a/src/hotspot/share/memory/universe.cpp +++ b/src/hotspot/share/memory/universe.cpp @@ -182,7 +182,6 @@ int Universe::_base_vtable_size = 0; bool Universe::_bootstrapping = false; bool Universe::_module_initialized = false; bool Universe::_fully_initialized = false; -volatile bool Universe::_is_shutting_down = false; OopStorage* Universe::_vm_weak = nullptr; OopStorage* Universe::_vm_global = nullptr; @@ -1374,15 +1373,14 @@ static void log_cpu_time() { } void Universe::before_exit() { - { - // Acquire the Heap_lock to synchronize with VM_Heap_Sync_Operations, - // which may depend on the value of _is_shutting_down flag. - MutexLocker hl(Heap_lock); - log_cpu_time(); - AtomicAccess::release_store(&_is_shutting_down, true); - } + // Tell the GC that it is time to shutdown and to block requests for new GC pauses. + heap()->initiate_shutdown(); + + // Log CPU time statistics before stopping the GC threads. + log_cpu_time(); - heap()->before_exit(); + // Stop the GC threads. + heap()->stop(); // Print GC/heap related information. Log(gc, exit) log; diff --git a/src/hotspot/share/memory/universe.hpp b/src/hotspot/share/memory/universe.hpp index df2c1d66d3c..b2325c67ca0 100644 --- a/src/hotspot/share/memory/universe.hpp +++ b/src/hotspot/share/memory/universe.hpp @@ -128,9 +128,6 @@ class Universe: AllStatic { static bool _module_initialized; // true after call_initPhase2 called static bool _fully_initialized; // true after universe_init and initialize_vtables called - // Shutdown - static volatile bool _is_shutting_down; - // the array of preallocated errors with backtraces static objArrayOop preallocated_out_of_memory_errors(); @@ -328,8 +325,6 @@ class Universe: AllStatic { static bool is_module_initialized() { return _module_initialized; } static bool is_fully_initialized() { return _fully_initialized; } - static bool is_shutting_down() { return AtomicAccess::load_acquire(&_is_shutting_down); } - static bool on_page_boundary(void* addr); static bool should_fill_in_stack_trace(Handle throwable); static void check_alignment(uintx size, uintx alignment, const char* name); diff --git a/src/hotspot/share/oops/bsmAttribute.hpp b/src/hotspot/share/oops/bsmAttribute.hpp new file mode 100644 index 00000000000..a28d2757fb0 --- /dev/null +++ b/src/hotspot/share/oops/bsmAttribute.hpp @@ -0,0 +1,170 @@ +/* + * Copyright (c) 1997, 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_OOPS_BSMATTRIBUTE_HPP +#define SHARE_OOPS_BSMATTRIBUTE_HPP + +#include "oops/array.hpp" +#include "utilities/checkedCast.hpp" +#include "utilities/globalDefinitions.hpp" + +class ClassLoaderData; + +class BSMAttributeEntry { + friend class ConstantPool; + friend class BSMAttributeEntries; + + u2 _bootstrap_method_index; + u2 _argument_count; + + // The argument indexes are stored right after the object, in a contiguous array. + // [ bsmi_0 argc_0 arg_00 arg_01 ... arg_0N bsmi_1 argc_1 arg_10 ... arg_1N ... ] + // So in order to find the argument array, jump over ourselves. + const u2* argument_indexes() const { + return reinterpret_cast(this + 1); + } + u2* argument_indexes() { + return reinterpret_cast(this + 1); + } + // These are overlays on top of the BSMAttributeEntries data array, do not construct. + BSMAttributeEntry() = delete; + NONCOPYABLE(BSMAttributeEntry); + + void copy_args_into(BSMAttributeEntry* entry) const; + +public: + // Offsets for SA + enum { + _bsmi_offset = 0, + _argc_offset = 1, + _argv_offset = 2 + }; + + int bootstrap_method_index() const { + return _bootstrap_method_index; + } + int argument_count() const { + return _argument_count; + } + int argument(int n) const { + assert(checked_cast(n) < _argument_count, "oob"); + return argument_indexes()[n]; + } + + void set_argument(int index, u2 value) { + assert(index >= 0 && index < argument_count(), "invariant"); + argument_indexes()[index] = value; + } + + // How many u2s are required to store a BSM entry with argc arguments? + static int u2s_required (u2 argc) { + return 1 /* index */ + 1 /* argc */ + argc /* argv */; + } +}; + +// The BSMAttributeEntries stores the state of the BootstrapMethods attribute. +class BSMAttributeEntries { + friend class VMStructs; + friend class JVMCIVMStructs; + +public: + class InsertionIterator { + friend BSMAttributeEntries; + BSMAttributeEntries* _insert_into; + // Current unused offset into BSMAEs offset array. + int _cur_offset; + // Current unused offset into BSMAEs bsm-data array. + int _cur_array; + public: + InsertionIterator() : _insert_into(nullptr), _cur_offset(-1), _cur_array(-1) {} + InsertionIterator(BSMAttributeEntries* insert_into, int cur_offset, int cur_array) + : _insert_into(insert_into), + _cur_offset(cur_offset), + _cur_array(cur_array) {} + InsertionIterator(const InsertionIterator&) = default; + InsertionIterator& operator=(const InsertionIterator&) = default; + + int current_offset() const { return _cur_offset; } + // Add a new BSMAE, reserving the necessary memory for filling the argument vector. + // Returns null if there isn't enough space. + inline BSMAttributeEntry* reserve_new_entry(u2 bsmi, u2 argc); + }; + +private: + // Each bootstrap method has a variable-sized array associated with it. + // We want constant-time lookup of the Nth BSM. Therefore, we use an offset table, + // such that the Nth BSM is located at _bootstrap_methods[_offsets[N]]. + Array* _offsets; + Array* _bootstrap_methods; + + // Copy the first num_entries into iter. + void copy_into(InsertionIterator& iter, int num_entries) const; + +public: + BSMAttributeEntries() : _offsets(nullptr), _bootstrap_methods(nullptr) {} + BSMAttributeEntries(Array* offsets, Array* bootstrap_methods) + : _offsets(offsets), + _bootstrap_methods(bootstrap_methods) {} + + bool is_empty() const { + return _offsets == nullptr && _bootstrap_methods == nullptr; + } + + Array*& offsets() { return _offsets; } + const Array* const& offsets() const { return _offsets; } + Array*& bootstrap_methods() { return _bootstrap_methods; } + const Array* const& bootstrap_methods() const { return _bootstrap_methods; } + + BSMAttributeEntry* entry(int bsms_attribute_index) { + return reinterpret_cast(_bootstrap_methods->adr_at(_offsets->at(bsms_attribute_index))); + } + const BSMAttributeEntry* entry(int bsms_attribute_index) const { + return reinterpret_cast(_bootstrap_methods->adr_at(_offsets->at(bsms_attribute_index))); + } + + int number_of_entries() const { + return _offsets == nullptr ? 0 : _offsets->length(); + } + + // The number of U2s the BSM data consists of. + int array_length() const { + return _bootstrap_methods == nullptr ? 0 : _bootstrap_methods->length(); + } + + void deallocate_contents(ClassLoaderData* loader_data); + + // Extend to have the space for both this BSMAEntries and other's. + // Does not copy in the other's BSMAEntrys, that must be done via the InsertionIterator. + // This starts an insertion iterator. Any call to start_extension must have a matching end_extension call. + InsertionIterator start_extension(const BSMAttributeEntries& other, ClassLoaderData* loader_data, TRAPS); + // Extend the BSMAEntries with an additional number_of_entries with a total data_size. + InsertionIterator start_extension(int number_of_entries, int data_size, ClassLoaderData* loader_data, TRAPS); + // Reallocates the underlying memory to fit the limits of the InsertionIterator precisely. + // This ends an insertion iteration. The memory is truncated to fit exactly the data used. + void end_extension(InsertionIterator& iter, ClassLoaderData* loader_data, TRAPS); + // Append all of the BSMAEs in other into this. + void append(const BSMAttributeEntries& other, ClassLoaderData* loader_data, TRAPS); +}; + +#endif // SHARE_OOPS_BSMATTRIBUTE_HPP diff --git a/src/hotspot/share/oops/bsmAttribute.inline.hpp b/src/hotspot/share/oops/bsmAttribute.inline.hpp new file mode 100644 index 00000000000..e678c280c26 --- /dev/null +++ b/src/hotspot/share/oops/bsmAttribute.inline.hpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018, 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_OOPS_BSMATTRIBUTE_INLINE_HPP +#define SHARE_OOPS_BSMATTRIBUTE_INLINE_HPP + +#include "oops/bsmAttribute.hpp" + +inline BSMAttributeEntry* BSMAttributeEntries::InsertionIterator::reserve_new_entry(u2 bsmi, u2 argc) { + assert(_insert_into->offsets() != nullptr, "must"); + assert(_insert_into->bootstrap_methods() != nullptr, "must"); + + if (_cur_offset + 1 > _insert_into->offsets()->length() || + _cur_array + BSMAttributeEntry::u2s_required(argc) > _insert_into->bootstrap_methods()->length()) { + return nullptr; + } + _insert_into->offsets()->at_put(_cur_offset, _cur_array); + BSMAttributeEntry* e = _insert_into->entry(_cur_offset); + e->_bootstrap_method_index = bsmi; + e->_argument_count = argc; + + _cur_array += 1 + 1 + argc; + _cur_offset += 1; + return e; +} + +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++) { + entry->set_argument(i, this->argument(i)); + } +} + +#endif // SHARE_OOPS_BSMATTRIBUTE_INLINE_HPP diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp index 95a43b07bd7..640b2f2460f 100644 --- a/src/hotspot/share/oops/constantPool.cpp +++ b/src/hotspot/share/oops/constantPool.cpp @@ -131,8 +131,7 @@ void ConstantPool::deallocate_contents(ClassLoaderData* loader_data) { MetadataFactory::free_array(loader_data, resolved_klasses()); set_resolved_klasses(nullptr); - MetadataFactory::free_array(loader_data, operands()); - set_operands(nullptr); + bsm_entries().deallocate_contents(loader_data); release_C_heap_structures(); @@ -152,7 +151,8 @@ void ConstantPool::metaspace_pointers_do(MetaspaceClosure* it) { it->push(&_tags, MetaspaceClosure::_writable); it->push(&_cache); it->push(&_pool_holder); - it->push(&_operands); + it->push(&bsm_entries().offsets()); + it->push(&bsm_entries().bootstrap_methods()); it->push(&_resolved_klasses, MetaspaceClosure::_writable); for (int i = 0; i < length(); i++) { @@ -761,7 +761,7 @@ Method* ConstantPool::method_at_if_loaded(const constantPoolHandle& cpool, if (cpool->cache() == nullptr) return nullptr; // nothing to load yet if (!(which >= 0 && which < cpool->resolved_method_entries_length())) { // FIXME: should be an assert - log_debug(class, resolve)("bad operand %d in:", which); cpool->print(); + log_debug(class, resolve)("bad BSM %d in:", which); cpool->print(); return nullptr; } return cpool->cache()->method_if_resolved(which); @@ -1562,8 +1562,8 @@ bool ConstantPool::compare_entry_to(int index1, const constantPoolHandle& cp2, int i1 = bootstrap_methods_attribute_index(index1); int i2 = cp2->bootstrap_methods_attribute_index(index2); bool match_entry = compare_entry_to(k1, cp2, k2); - bool match_operand = compare_operand_to(i1, cp2, i2); - return (match_entry && match_operand); + bool match_bsm = compare_bootstrap_entry_to(i1, cp2, i2); + return (match_entry && match_bsm); } break; case JVM_CONSTANT_InvokeDynamic: @@ -1573,8 +1573,8 @@ bool ConstantPool::compare_entry_to(int index1, const constantPoolHandle& cp2, int i1 = bootstrap_methods_attribute_index(index1); int i2 = cp2->bootstrap_methods_attribute_index(index2); bool match_entry = compare_entry_to(k1, cp2, k2); - bool match_operand = compare_operand_to(i1, cp2, i2); - return (match_entry && match_operand); + bool match_bsm = compare_bootstrap_entry_to(i1, cp2, i2); + return (match_entry && match_bsm); } break; case JVM_CONSTANT_String: @@ -1608,140 +1608,29 @@ bool ConstantPool::compare_entry_to(int index1, const constantPoolHandle& cp2, return false; } // end compare_entry_to() - -// Resize the operands array with delta_len and delta_size. -// Used in RedefineClasses for CP merge. -void ConstantPool::resize_operands(int delta_len, int delta_size, TRAPS) { - int old_len = operand_array_length(operands()); - int new_len = old_len + delta_len; - int min_len = (delta_len > 0) ? old_len : new_len; - - int old_size = operands()->length(); - int new_size = old_size + delta_size; - int min_size = (delta_size > 0) ? old_size : new_size; - - ClassLoaderData* loader_data = pool_holder()->class_loader_data(); - Array* new_ops = MetadataFactory::new_array(loader_data, new_size, CHECK); - - // Set index in the resized array for existing elements only - for (int idx = 0; idx < min_len; idx++) { - int offset = operand_offset_at(idx); // offset in original array - operand_offset_at_put(new_ops, idx, offset + 2*delta_len); // offset in resized array - } - // Copy the bootstrap specifiers only - Copy::conjoint_memory_atomic(operands()->adr_at(2*old_len), - new_ops->adr_at(2*new_len), - (min_size - 2*min_len) * sizeof(u2)); - // Explicitly deallocate old operands array. - // Note, it is not needed for 7u backport. - if ( operands() != nullptr) { // the safety check - MetadataFactory::free_array(loader_data, operands()); - } - set_operands(new_ops); -} // end resize_operands() - - -// Extend the operands array with the length and size of the ext_cp operands. +// Extend the BSMAttributeEntries with the length and size of the ext_cp BSMAttributeEntries. // Used in RedefineClasses for CP merge. -void ConstantPool::extend_operands(const constantPoolHandle& ext_cp, TRAPS) { - int delta_len = operand_array_length(ext_cp->operands()); - if (delta_len == 0) { - return; // nothing to do - } - int delta_size = ext_cp->operands()->length(); - - assert(delta_len > 0 && delta_size > 0, "extended operands array must be bigger"); - - if (operand_array_length(operands()) == 0) { - ClassLoaderData* loader_data = pool_holder()->class_loader_data(); - Array* new_ops = MetadataFactory::new_array(loader_data, delta_size, CHECK); - // The first element index defines the offset of second part - operand_offset_at_put(new_ops, 0, 2*delta_len); // offset in new array - set_operands(new_ops); - } else { - resize_operands(delta_len, delta_size, CHECK); - } +BSMAttributeEntries::InsertionIterator +ConstantPool::start_extension(const constantPoolHandle& ext_cp, TRAPS) { + BSMAttributeEntries::InsertionIterator iter = + bsm_entries().start_extension(ext_cp->bsm_entries(), pool_holder()->class_loader_data(), + CHECK_(BSMAttributeEntries::InsertionIterator())); + return iter; +} -} // end extend_operands() +void ConstantPool::end_extension(BSMAttributeEntries::InsertionIterator iter, TRAPS) { + bsm_entries().end_extension(iter, pool_holder()->class_loader_data(), THREAD); +} -// Shrink the operands array to a smaller array with new_len length. -// Used in RedefineClasses for CP merge. -void ConstantPool::shrink_operands(int new_len, TRAPS) { - int old_len = operand_array_length(operands()); - if (new_len == old_len) { - return; // nothing to do - } - assert(new_len < old_len, "shrunken operands array must be smaller"); - - int free_base = operand_next_offset_at(new_len - 1); - int delta_len = new_len - old_len; - int delta_size = 2*delta_len + free_base - operands()->length(); - - resize_operands(delta_len, delta_size, CHECK); - -} // end shrink_operands() - - -void ConstantPool::copy_operands(const constantPoolHandle& from_cp, - const constantPoolHandle& to_cp, - TRAPS) { - - int from_oplen = operand_array_length(from_cp->operands()); - int old_oplen = operand_array_length(to_cp->operands()); - if (from_oplen != 0) { - ClassLoaderData* loader_data = to_cp->pool_holder()->class_loader_data(); - // append my operands to the target's operands array - if (old_oplen == 0) { - // Can't just reuse from_cp's operand list because of deallocation issues - int len = from_cp->operands()->length(); - Array* new_ops = MetadataFactory::new_array(loader_data, len, CHECK); - Copy::conjoint_memory_atomic( - from_cp->operands()->adr_at(0), new_ops->adr_at(0), len * sizeof(u2)); - to_cp->set_operands(new_ops); - } else { - int old_len = to_cp->operands()->length(); - int from_len = from_cp->operands()->length(); - int old_off = old_oplen * sizeof(u2); - int from_off = from_oplen * sizeof(u2); - // Use the metaspace for the destination constant pool - Array* new_operands = MetadataFactory::new_array(loader_data, old_len + from_len, CHECK); - int fillp = 0, len = 0; - // first part of dest - Copy::conjoint_memory_atomic(to_cp->operands()->adr_at(0), - new_operands->adr_at(fillp), - (len = old_off) * sizeof(u2)); - fillp += len; - // first part of src - Copy::conjoint_memory_atomic(from_cp->operands()->adr_at(0), - new_operands->adr_at(fillp), - (len = from_off) * sizeof(u2)); - fillp += len; - // second part of dest - Copy::conjoint_memory_atomic(to_cp->operands()->adr_at(old_off), - new_operands->adr_at(fillp), - (len = old_len - old_off) * sizeof(u2)); - fillp += len; - // second part of src - Copy::conjoint_memory_atomic(from_cp->operands()->adr_at(from_off), - new_operands->adr_at(fillp), - (len = from_len - from_off) * sizeof(u2)); - fillp += len; - assert(fillp == new_operands->length(), ""); - - // Adjust indexes in the first part of the copied operands array. - for (int j = 0; j < from_oplen; j++) { - int offset = operand_offset_at(new_operands, old_oplen + j); - assert(offset == operand_offset_at(from_cp->operands(), j), "correct copy"); - offset += old_len; // every new tuple is preceded by old_len extra u2's - operand_offset_at_put(new_operands, old_oplen + j, offset); - } - // replace target operands array with combined array - to_cp->set_operands(new_operands); - } - } -} // end copy_operands() +void ConstantPool::copy_bsm_entries(const constantPoolHandle& from_cp, + const constantPoolHandle& to_cp, + TRAPS) { + to_cp->bsm_entries().append(from_cp->bsm_entries(), + to_cp->pool_holder()->class_loader_data(), + THREAD); +} // Copy this constant pool's entries at start_i to end_i (inclusive) @@ -1771,7 +1660,7 @@ void ConstantPool::copy_cp_to_impl(const constantPoolHandle& from_cp, int start_ break; } } - copy_operands(from_cp, to_cp, CHECK); + copy_bsm_entries(from_cp, to_cp, THREAD); } // end copy_cp_to_impl() @@ -1895,7 +1784,7 @@ void ConstantPool::copy_entry_to(const constantPoolHandle& from_cp, int from_i, { int k1 = from_cp->bootstrap_methods_attribute_index(from_i); int k2 = from_cp->bootstrap_name_and_type_ref_index_at(from_i); - k1 += operand_array_length(to_cp->operands()); // to_cp might already have operands + k1 += to_cp->bsm_entries().array_length(); // to_cp might already have a BSM attribute to_cp->dynamic_constant_at_put(to_i, k1, k2); } break; @@ -1903,7 +1792,7 @@ void ConstantPool::copy_entry_to(const constantPoolHandle& from_cp, int from_i, { int k1 = from_cp->bootstrap_methods_attribute_index(from_i); int k2 = from_cp->bootstrap_name_and_type_ref_index_at(from_i); - k1 += operand_array_length(to_cp->operands()); // to_cp might already have operands + k1 += to_cp->bsm_entries().array_length(); // to_cp might already have a BSM attribute to_cp->invoke_dynamic_at_put(to_i, k1, k2); } break; @@ -1939,9 +1828,9 @@ int ConstantPool::find_matching_entry(int pattern_i, // Compare this constant pool's bootstrap specifier at idx1 to the constant pool // cp2's bootstrap specifier at idx2. -bool ConstantPool::compare_operand_to(int idx1, const constantPoolHandle& cp2, int idx2) { - BSMAttributeEntry* e1 = bsm_attribute_entry(idx1); - BSMAttributeEntry* e2 = cp2->bsm_attribute_entry(idx2); +bool ConstantPool::compare_bootstrap_entry_to(int idx1, const constantPoolHandle& cp2, int idx2) { + const BSMAttributeEntry* const e1 = bsm_attribute_entry(idx1); + const BSMAttributeEntry* const e2 = cp2->bsm_attribute_entry(idx2); int k1 = e1->bootstrap_method_index(); int k2 = e2->bootstrap_method_index(); bool match = compare_entry_to(k1, cp2, k2); @@ -1949,34 +1838,37 @@ bool ConstantPool::compare_operand_to(int idx1, const constantPoolHandle& cp2, i if (!match) { return false; } - int argc = e1->argument_count(); - if (argc == e2->argument_count()) { - for (int j = 0; j < argc; j++) { - k1 = e1->argument_index(j); - k2 = e2->argument_index(j); - match = compare_entry_to(k1, cp2, k2); - if (!match) { - return false; - } + + const int argc = e1->argument_count(); + if (argc != e2->argument_count()) { + return false; + } + + for (int j = 0; j < argc; j++) { + k1 = e1->argument(j); + k2 = e2->argument(j); + match = compare_entry_to(k1, cp2, k2); + if (!match) { + return false; } - return true; // got through loop; all elements equal } - return false; -} // end compare_operand_to() + + return true; // got through loop; all elements equal +} // end compare_bootstrap_entry_to() // Search constant pool search_cp for a bootstrap specifier that matches // this constant pool's bootstrap specifier data at pattern_i index. // Return the index of a matching bootstrap attribute record or (-1) if there is no match. -int ConstantPool::find_matching_operand(int pattern_i, - const constantPoolHandle& search_cp, int search_len) { - for (int i = 0; i < search_len; i++) { - bool found = compare_operand_to(pattern_i, search_cp, i); +int ConstantPool::find_matching_bsm_entry(int pattern_i, + const constantPoolHandle& search_cp, int offset_limit) { + for (int i = 0; i < offset_limit; i++) { + bool found = compare_bootstrap_entry_to(pattern_i, search_cp, i); if (found) { return i; } } return -1; // bootstrap specifier data not found; return unused index (-1) -} // end find_matching_operand() +} // end find_matching_bsm_entry() #ifndef PRODUCT @@ -2411,7 +2303,7 @@ void ConstantPool::print_value_on(outputStream* st) const { assert(is_constantPool(), "must be constantPool"); st->print("constant pool [%d]", length()); if (has_preresolution()) st->print("/preresolution"); - if (operands() != nullptr) st->print("/operands[%d]", operands()->length()); + if (!bsm_entries().is_empty()) st->print("/BSMs[%d]", bsm_entries().bootstrap_methods()->length()); print_address_on(st); if (pool_holder() != nullptr) { st->print(" for "); @@ -2446,3 +2338,87 @@ void ConstantPool::verify_on(outputStream* st) { guarantee(pool_holder()->is_klass(), "should be klass"); } } + +void BSMAttributeEntries::deallocate_contents(ClassLoaderData* loader_data) { + MetadataFactory::free_array(loader_data, this->_offsets); + MetadataFactory::free_array(loader_data, this->_bootstrap_methods); + this->_offsets = nullptr; + this->_bootstrap_methods = nullptr; +} + +void BSMAttributeEntries::copy_into(InsertionIterator& iter, int num_entries) const { + assert(num_entries + iter._cur_offset <= iter._insert_into->_offsets->length(), "must"); + for (int i = 0; i < num_entries; i++) { + const BSMAttributeEntry* e = entry(i); + BSMAttributeEntry* e_new = iter.reserve_new_entry(e->bootstrap_method_index(), e->argument_count()); + assert(e_new != nullptr, "must be"); + e->copy_args_into(e_new); + } +} + +BSMAttributeEntries::InsertionIterator +BSMAttributeEntries::start_extension(const BSMAttributeEntries& other, ClassLoaderData* loader_data, TRAPS) { + InsertionIterator iter = start_extension(other.number_of_entries(), other.array_length(), + loader_data, CHECK_(BSMAttributeEntries::InsertionIterator())); + return iter; +} + +BSMAttributeEntries::InsertionIterator +BSMAttributeEntries::start_extension(int number_of_entries, int array_length, + ClassLoaderData* loader_data, TRAPS) { + InsertionIterator extension_iterator(this, this->number_of_entries(), this->array_length()); + int new_number_of_entries = this->number_of_entries() + number_of_entries; + int new_array_length = this->array_length() + array_length; + int invalid_index = new_array_length; + + Array* new_offsets = + MetadataFactory::new_array(loader_data, new_number_of_entries, invalid_index, CHECK_(InsertionIterator())); + Array* new_array = MetadataFactory::new_array(loader_data, new_array_length, CHECK_(InsertionIterator())); + { // Copy over all the old BSMAEntry's and their respective offsets + BSMAttributeEntries carrier(new_offsets, new_array); + InsertionIterator copy_iter(&carrier, 0, 0); + copy_into(copy_iter, this->number_of_entries()); + } + // Replace content + deallocate_contents(loader_data); + _offsets = new_offsets; + _bootstrap_methods = new_array; + return extension_iterator; +} + + +void BSMAttributeEntries::append(const BSMAttributeEntries& other, ClassLoaderData* loader_data, TRAPS) { + if (other.number_of_entries() == 0) { + return; // Done! + } + InsertionIterator iter = start_extension(other, loader_data, CHECK); + other.copy_into(iter, other.number_of_entries()); + end_extension(iter, loader_data, THREAD); +} + +void BSMAttributeEntries::end_extension(InsertionIterator& iter, ClassLoaderData* loader_data, TRAPS) { + assert(iter._insert_into == this, "must be"); + assert(iter._cur_offset <= this->_offsets->length(), "must be"); + assert(iter._cur_array <= this->_bootstrap_methods->length(), "must be"); + + // Did we fill up all of the available space? If so, do nothing. + if (iter._cur_offset == this->_offsets->length() && + iter._cur_array == this->_bootstrap_methods->length()) { + return; + } + + // We used less, truncate by allocating new arrays + Array* new_offsets = + MetadataFactory::new_array(loader_data, iter._cur_offset, 0, CHECK); + Array* new_array = + MetadataFactory::new_array(loader_data, iter._cur_array, CHECK); + { // Copy over the constructed BSMAEntry's + BSMAttributeEntries carrier(new_offsets, new_array); + InsertionIterator copy_iter(&carrier, 0, 0); + copy_into(copy_iter, iter._cur_offset); + } + + deallocate_contents(loader_data); + _offsets = new_offsets; + _bootstrap_methods = new_array; +} diff --git a/src/hotspot/share/oops/constantPool.hpp b/src/hotspot/share/oops/constantPool.hpp index 9cbeb1245be..6c519945f4d 100644 --- a/src/hotspot/share/oops/constantPool.hpp +++ b/src/hotspot/share/oops/constantPool.hpp @@ -27,6 +27,7 @@ #include "memory/allocation.hpp" #include "oops/arrayOop.hpp" +#include "oops/bsmAttribute.inline.hpp" #include "oops/cpCache.hpp" #include "oops/objArrayOop.hpp" #include "oops/oopHandle.hpp" @@ -77,43 +78,6 @@ class CPKlassSlot { } }; -class BSMAttributeEntry { - friend class ConstantPool; - u2 _bootstrap_method_index; - u2 _argument_count; - - // The argument indexes are stored right after the object, in a contiguous array. - // [ bsmi_0 argc_0 arg_00 arg_01 ... arg_0N bsmi_1 argc_1 arg_10 ... arg_1N ... ] - // So in order to find the argument array, jump over ourselves. - const u2* argument_indexes() const { - return reinterpret_cast(this + 1); - } - u2* argument_indexes() { - return reinterpret_cast(this + 1); - } - // These are overlays on top of the operands array. Do not construct. - BSMAttributeEntry() = delete; - -public: - // Offsets for SA - enum { - _bsmi_offset = 0, - _argc_offset = 1, - _argv_offset = 2 - }; - - int bootstrap_method_index() const { - return _bootstrap_method_index; - } - int argument_count() const { - return _argument_count; - } - int argument_index(int n) const { - assert(checked_cast(n) < _argument_count, "oob"); - return argument_indexes()[n]; - } -}; - class ConstantPool : public Metadata { friend class VMStructs; friend class JVMCIVMStructs; @@ -126,7 +90,8 @@ class ConstantPool : public Metadata { Array* _tags; // the tag array describing the constant pool's contents ConstantPoolCache* _cache; // the cache holding interpreter runtime information InstanceKlass* _pool_holder; // the corresponding class - Array* _operands; // for variable-sized (InvokeDynamic) nodes, usually empty + + BSMAttributeEntries _bsm_entries; // Consider using an array of compressed klass pointers to // save space on 64-bit platforms. @@ -167,8 +132,6 @@ class ConstantPool : public Metadata { u1* tag_addr_at(int cp_index) const { return tags()->adr_at(cp_index); } - void set_operands(Array* operands) { _operands = operands; } - u2 flags() const { return _flags; } void set_flags(u2 f) { _flags = f; } @@ -208,7 +171,13 @@ class ConstantPool : public Metadata { virtual bool is_constantPool() const { return true; } Array* tags() const { return _tags; } - Array* operands() const { return _operands; } + + BSMAttributeEntries& bsm_entries() { + return _bsm_entries; + } + const BSMAttributeEntries& bsm_entries() const { + return _bsm_entries; + } bool has_preresolution() const { return (_flags & _has_preresolution) != 0; } void set_has_preresolution() { @@ -556,76 +525,21 @@ class ConstantPool : public Metadata { assert(tag_at(cp_index).has_bootstrap(), "Corrupted constant pool"); return extract_low_short_from_int(*int_at_addr(cp_index)); } - // The first part of the operands array consists of an index into the second part. - // Extract a 32-bit index value from the first part. - static int operand_offset_at(Array* operands, int bsms_attribute_index) { - int n = (bsms_attribute_index * 2); - assert(n >= 0 && n+2 <= operands->length(), "oob"); - // The first 32-bit index points to the beginning of the second part - // of the operands array. Make sure this index is in the first part. - DEBUG_ONLY(int second_part = build_int_from_shorts(operands->at(0), - operands->at(1))); - assert(second_part == 0 || n+2 <= second_part, "oob (2)"); - int offset = build_int_from_shorts(operands->at(n+0), - operands->at(n+1)); - // The offset itself must point into the second part of the array. - assert(offset == 0 || (offset >= second_part && offset <= operands->length()), "oob (3)"); - return offset; - } - static void operand_offset_at_put(Array* operands, int bsms_attribute_index, int offset) { - int n = bsms_attribute_index * 2; - assert(n >= 0 && n+2 <= operands->length(), "oob"); - operands->at_put(n+0, extract_low_short_from_int(offset)); - operands->at_put(n+1, extract_high_short_from_int(offset)); - } - static int operand_array_length(Array* operands) { - if (operands == nullptr || operands->length() == 0) return 0; - int second_part = operand_offset_at(operands, 0); - return (second_part / 2); - } - -#ifdef ASSERT - // operand tuples fit together exactly, end to end - static int operand_limit_at(Array* operands, int bsms_attribute_index) { - int nextidx = bsms_attribute_index + 1; - if (nextidx == operand_array_length(operands)) - return operands->length(); - else - return operand_offset_at(operands, nextidx); - } -#endif //ASSERT - - // These functions are used in RedefineClasses for CP merge - int operand_offset_at(int bsms_attribute_index) { - assert(0 <= bsms_attribute_index && - bsms_attribute_index < operand_array_length(operands()), - "Corrupted CP operands"); - return operand_offset_at(operands(), bsms_attribute_index); - } BSMAttributeEntry* bsm_attribute_entry(int bsms_attribute_index) { - int offset = operand_offset_at(bsms_attribute_index); - return reinterpret_cast(operands()->adr_at(offset)); - } - - int operand_next_offset_at(int bsms_attribute_index) { - BSMAttributeEntry* bsme = bsm_attribute_entry(bsms_attribute_index); - u2* argv_start = bsme->argument_indexes(); - int offset = argv_start - operands()->data(); - return offset + bsme->argument_count(); - } - // Compare a bootstrap specifier data in the operands arrays - bool compare_operand_to(int bsms_attribute_index1, const constantPoolHandle& cp2, - int bsms_attribute_index2); - // Find a bootstrap specifier data in the operands array - int find_matching_operand(int bsms_attribute_index, const constantPoolHandle& search_cp, - int operands_cur_len); - // Resize the operands array with delta_len and delta_size - void resize_operands(int delta_len, int delta_size, TRAPS); - // Extend the operands array with the length and size of the ext_cp operands - void extend_operands(const constantPoolHandle& ext_cp, TRAPS); - // Shrink the operands array to a smaller array with new_len length - void shrink_operands(int new_len, TRAPS); + return _bsm_entries.entry(bsms_attribute_index); + } + + bool compare_bootstrap_entry_to(int bsms_attribute_index1, const constantPoolHandle& cp2, + int bsms_attribute_index2); + // Find a BSM entry in search_cp that matches the BSM at bsm_attribute_index. + // Return -1 if not found. + int find_matching_bsm_entry(int bsms_attribute_index, const constantPoolHandle& search_cp, + int offset_limit); + // Extend the BSM attribute storage to fit both the current data and the BSM data in ext_cp. + // Use the returned InsertionIterator to fill out the newly allocated space. + BSMAttributeEntries::InsertionIterator start_extension(const constantPoolHandle& ext_cp, TRAPS); + void end_extension(BSMAttributeEntries::InsertionIterator iter, TRAPS); u2 bootstrap_method_ref_index_at(int cp_index) { assert(tag_at(cp_index).has_bootstrap(), "Corrupted constant pool"); @@ -641,7 +555,7 @@ class ConstantPool : public Metadata { 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_index(j); + return bsm_attribute_entry(bsmai)->argument(j); } // The following methods (name/signature/klass_ref_at, klass_ref_at_noresolve, @@ -848,7 +762,7 @@ class ConstantPool : public Metadata { } static void copy_cp_to_impl(const constantPoolHandle& from_cp, int start_cpi, int end_cpi, const constantPoolHandle& to_cp, int to_cpi, TRAPS); static void copy_entry_to(const constantPoolHandle& from_cp, int from_cpi, const constantPoolHandle& to_cp, int to_cpi); - static void copy_operands(const constantPoolHandle& from_cp, const constantPoolHandle& to_cp, TRAPS); + static void copy_bsm_entries(const constantPoolHandle& from_cp, const constantPoolHandle& to_cp, TRAPS); int find_matching_entry(int pattern_i, const constantPoolHandle& search_cp); int version() const { return _saved._version; } void set_version(int version) { _saved._version = version; } diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 0a4f231c49b..2b2b4db47b1 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -428,7 +428,7 @@ "0=print nothing except PhasePrintLevel directives, " \ "6=all details printed. " \ "Level of detail of printouts can be set on a per-method level " \ - "as well by using CompileCommand=PrintPhaseLevel.") \ + "as well by using CompileCommand=PhasePrintLevel.") \ range(-1, 6) \ \ develop(bool, PrintIdealGraph, false, \ diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 6babc13e1b3..89b5e36b120 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -5233,7 +5233,7 @@ void Compile::end_method() { #ifndef PRODUCT bool Compile::should_print_phase(const int level) const { - return PrintPhaseLevel > 0 && directive()->PhasePrintLevelOption >= level && + return PrintPhaseLevel >= 0 && directive()->PhasePrintLevelOption >= level && _method != nullptr; // Do not print phases for stubs. } diff --git a/src/hotspot/share/opto/idealGraphPrinter.cpp b/src/hotspot/share/opto/idealGraphPrinter.cpp index 6a738878a1b..b28949e27c2 100644 --- a/src/hotspot/share/opto/idealGraphPrinter.cpp +++ b/src/hotspot/share/opto/idealGraphPrinter.cpp @@ -35,6 +35,96 @@ #ifndef PRODUCT +// Support for printing properties +class PrintProperties +{ +private: + IdealGraphPrinter* _printer; + +public: + PrintProperties(IdealGraphPrinter* printer) : _printer(printer) {} + void print_node_properties(Node* node); + void print_lrg_properties(const LRG& lrg, const char* buffer); + void print_property(int flag, const char* name); + void print_property(int flag, const char* name, const char* val); + void print_property(int flag, const char* name, int val); +}; + +void PrintProperties::print_node_properties(Node* node) { + const jushort flags = node->flags(); + print_property((flags & Node::Flag_is_Copy), "is_copy"); + print_property((flags & Node::Flag_rematerialize), "rematerialize"); + print_property((flags & Node::Flag_needs_anti_dependence_check), "needs_anti_dependence_check"); + print_property((flags & Node::Flag_is_macro), "is_macro"); + print_property((flags & Node::Flag_is_Con), "is_con"); + print_property((flags & Node::Flag_is_cisc_alternate), "is_cisc_alternate"); + print_property((flags & Node::Flag_is_dead_loop_safe), "is_dead_loop_safe"); + print_property((flags & Node::Flag_may_be_short_branch), "may_be_short_branch"); + print_property((flags & Node::Flag_has_call), "has_call"); + print_property((flags & Node::Flag_has_swapped_edges), "has_swapped_edges"); + Matcher* matcher = _printer->C->matcher(); + if (matcher != nullptr) { + print_property(matcher->is_shared(node),"is_shared"); + print_property(!(matcher->is_shared(node)), "is_shared", IdealGraphPrinter::FALSE_VALUE); + print_property(matcher->is_dontcare(node), "is_dontcare"); + print_property(!(matcher->is_dontcare(node)),"is_dontcare", IdealGraphPrinter::FALSE_VALUE); + Node* old = matcher->find_old_node(node); + if (old != nullptr) { + print_property(true, "old_node_idx", old->_idx); + } + } +} + +void PrintProperties::print_lrg_properties(const LRG &lrg, const char *buffer) { + print_property(true, "mask", buffer); + print_property(true, "mask_size", lrg.mask_size()); + if (lrg._degree_valid) { + print_property(true, "degree", lrg.degree()); + } + print_property(true, "num_regs", lrg.num_regs()); + print_property(true, "reg_pressure", lrg.reg_pressure()); + print_property(true, "cost", lrg._cost); + print_property(true, "area", lrg._area); + print_property(true, "score", lrg.score()); + print_property((lrg._risk_bias != 0), "risk_bias", lrg._risk_bias); + print_property((lrg._copy_bias != 0), "copy_bias", lrg._copy_bias); + print_property(lrg.is_singledef(), "is_singledef"); + print_property(lrg.is_multidef(), "is_multidef"); + print_property(lrg._is_oop, "is_oop"); + print_property(lrg._is_float, "is_float"); + print_property(lrg._is_vector, "is_vector"); + print_property(lrg._is_predicate, "is_predicate"); + print_property(lrg._is_scalable, "is_scalable"); + print_property(lrg._was_spilled1, "was_spilled1"); + print_property(lrg._was_spilled2, "was_spilled2"); + print_property(lrg._direct_conflict, "direct_conflict"); + print_property(lrg._fat_proj, "fat_proj"); + print_property(lrg._was_lo, "_was_lo"); + print_property(lrg._has_copy, "has_copy"); + print_property(lrg._at_risk, "at_risk"); + print_property(lrg._must_spill, "must_spill"); + print_property(lrg._is_bound, "is_bound"); + print_property((lrg._msize_valid && lrg._degree_valid && lrg.lo_degree()), "trivial"); +} + +void PrintProperties::print_property(int flag, const char* name) { + if (flag != 0) { + _printer->print_prop(name, IdealGraphPrinter::TRUE_VALUE); + } +} + +void PrintProperties::print_property(int flag, const char* name, const char* val) { + if (flag != 0) { + _printer->print_prop(name, val); + } +} + +void PrintProperties::print_property(int flag, const char* name, int val) { + if (flag != 0) { + _printer->print_prop(name, val); + } +} + // Constants // Keep consistent with Java constants const char *IdealGraphPrinter::INDENT = " "; @@ -522,54 +612,8 @@ void IdealGraphPrinter::visit_node(Node* n, bool edges) { print_prop("jvms", buffer); } - const jushort flags = node->flags(); - if (flags & Node::Flag_is_Copy) { - print_prop("is_copy", "true"); - } - if (flags & Node::Flag_rematerialize) { - print_prop("rematerialize", "true"); - } - if (flags & Node::Flag_needs_anti_dependence_check) { - print_prop("needs_anti_dependence_check", "true"); - } - if (flags & Node::Flag_is_macro) { - print_prop("is_macro", "true"); - } - if (flags & Node::Flag_is_Con) { - print_prop("is_con", "true"); - } - if (flags & Node::Flag_is_cisc_alternate) { - print_prop("is_cisc_alternate", "true"); - } - if (flags & Node::Flag_is_dead_loop_safe) { - print_prop("is_dead_loop_safe", "true"); - } - if (flags & Node::Flag_may_be_short_branch) { - print_prop("may_be_short_branch", "true"); - } - if (flags & Node::Flag_has_call) { - print_prop("has_call", "true"); - } - if (flags & Node::Flag_has_swapped_edges) { - print_prop("has_swapped_edges", "true"); - } - - if (C->matcher() != nullptr) { - if (C->matcher()->is_shared(node)) { - print_prop("is_shared", "true"); - } else { - print_prop("is_shared", "false"); - } - if (C->matcher()->is_dontcare(node)) { - print_prop("is_dontcare", "true"); - } else { - print_prop("is_dontcare", "false"); - } - Node* old = C->matcher()->find_old_node(node); - if (old != nullptr) { - print_prop("old_node_idx", old->_idx); - } - } + PrintProperties print_node(this); + print_node.print_node_properties(node); if (node->is_Proj()) { print_prop("con", (int)node->as_Proj()->_con); @@ -1145,73 +1189,10 @@ void IdealGraphPrinter::print(const char* name, Node* node, GrowableArray { - private: + friend class PrintProperties; +private: static const char *INDENT; static const char *TOP_ELEMENT; static const char *GROUP_ELEMENT; diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 31d1cbe0443..5c65103677b 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -1411,7 +1411,6 @@ void PhaseIdealLoop::insert_pre_post_loops(IdealLoopTree *loop, Node_List &old_n C->print_method(PHASE_BEFORE_PRE_MAIN_POST, 4, main_head); - Node *pre_header= main_head->in(LoopNode::EntryControl); Node *init = main_head->init_trip(); Node *incr = main_end ->incr(); Node *limit = main_end ->limit(); diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index dfff7ef96a5..03cc5cbcff6 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -1162,13 +1162,16 @@ bool PhaseIdealLoop::create_loop_nest(IdealLoopTree* loop, Node_List &old_new) { class CloneShortLoopPredicateVisitor : public PredicateVisitor { ClonePredicateToTargetLoop _clone_predicate_to_loop; PhaseIdealLoop* const _phase; + Node* const _new_init; public: CloneShortLoopPredicateVisitor(LoopNode* target_loop_head, + Node* new_init, const NodeInSingleLoopBody &node_in_loop_body, PhaseIdealLoop* phase) : _clone_predicate_to_loop(target_loop_head, node_in_loop_body, phase), - _phase(phase) { + _phase(phase), + _new_init(new_init) { } NONCOPYABLE(CloneShortLoopPredicateVisitor); @@ -1180,11 +1183,32 @@ class CloneShortLoopPredicateVisitor : public PredicateVisitor { } void visit(const TemplateAssertionPredicate& template_assertion_predicate) override { - _clone_predicate_to_loop.clone_template_assertion_predicate(template_assertion_predicate); + _clone_predicate_to_loop.clone_template_assertion_predicate_and_replace_init(template_assertion_predicate, _new_init); template_assertion_predicate.kill(_phase->igvn()); } }; +// For an int counted loop, try_make_short_running_loop() transforms the loop from: +// for (int = start; i < stop; i+= stride) { ... } +// to +// for (int = 0; i < stop - start; i+= stride) { ... } +// Template Assertion Predicates added so far were with an init value of start. They need to be updated with the new +// init value of 0 (otherwise when a template assertion predicate is turned into an initialized assertion predicate, it +// performs an incorrect check): +// zero +// init | +// | ===> OpaqueLoopInit init +// OpaqueLoopInit \ / +// AddI +// +Node* PhaseIdealLoop::new_assertion_predicate_opaque_init(Node* entry_control, Node* init, Node* int_zero) { + OpaqueLoopInitNode* new_opaque_init = new OpaqueLoopInitNode(C, int_zero); + register_new_node(new_opaque_init, entry_control); + Node* new_init = new AddINode(new_opaque_init, init); + register_new_node(new_init, entry_control); + return new_init; +} + // If the loop is either statically known to run for a small enough number of iterations or if profile data indicates // that, we don't want an outer loop because the overhead of having an outer loop whose backedge is never taken, has a // measurable cost. Furthermore, creating the loop nest usually causes one iteration of the loop to be peeled so @@ -1236,6 +1260,7 @@ bool PhaseIdealLoop::try_make_short_running_loop(IdealLoopTree* loop, jint strid } register_new_node(new_limit, entry_control); + Node* int_zero = intcon(0); PhiNode* phi = head->phi()->as_Phi(); if (profile_short_running_loop) { // Add a Short Running Long Loop Predicate. It's the first predicate in the predicate chain before entering a loop @@ -1261,9 +1286,11 @@ bool PhaseIdealLoop::try_make_short_running_loop(IdealLoopTree* loop, jint strid if (!short_running_long_loop_predicate_block->has_parse_predicate()) { // already trapped return false; } + Node* new_init = new_assertion_predicate_opaque_init(entry_control, init, int_zero); + PredicateIterator predicate_iterator(entry_control); NodeInSingleLoopBody node_in_short_loop_body(this, loop); - CloneShortLoopPredicateVisitor clone_short_loop_predicates_visitor(head, node_in_short_loop_body, this); + CloneShortLoopPredicateVisitor clone_short_loop_predicates_visitor(head, new_init, node_in_short_loop_body, this); predicate_iterator.for_each(clone_short_loop_predicates_visitor); entry_control = head->skip_strip_mined()->in(LoopNode::EntryControl); @@ -1311,6 +1338,10 @@ bool PhaseIdealLoop::try_make_short_running_loop(IdealLoopTree* loop, jint strid register_new_node(new_limit, predicates.entry()); } else { assert(bt == T_INT && known_short_running_loop, "only CountedLoop statically known to be short running"); + PredicateIterator predicate_iterator(entry_control); + Node* new_init = new_assertion_predicate_opaque_init(entry_control, init, int_zero); + UpdateInitForTemplateAssertionPredicates update_init_for_template_assertion_predicates(new_init, this); + predicate_iterator.for_each(update_init_for_template_assertion_predicates); } IfNode* exit_test = head->loopexit(); @@ -1320,7 +1351,6 @@ bool PhaseIdealLoop::try_make_short_running_loop(IdealLoopTree* loop, jint strid register_new_node(new_limit, entry_control); } - Node* int_zero = intcon(0); if (stride_con < 0) { new_limit = new SubINode(int_zero, new_limit); register_new_node(new_limit, entry_control); diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 1e34331f213..3b97d76773f 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -1969,6 +1969,8 @@ class PhaseIdealLoop : public PhaseTransform { Node* ensure_node_and_inputs_are_above_pre_end(CountedLoopEndNode* pre_end, Node* node); + Node* new_assertion_predicate_opaque_init(Node* entry_control, Node* init, Node* int_zero); + bool try_make_short_running_loop(IdealLoopTree* loop, jint stride_con, const Node_List& range_checks, const uint iters_limit); ConINode* intcon(jint i); diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index 3ef6a085b1c..ee3f138b8af 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -4180,6 +4180,33 @@ bool PhaseIdealLoop::partial_peel( IdealLoopTree *loop, Node_List &old_new ) { return true; } +#ifdef ASSERT + +// Moves Template Assertion Predicates to a target loop by cloning and killing the old ones. The target loop is the +// original, not-cloned loop. This is currently only used with StressLoopBackedge which is a develop flag only and +// false with product builds. We can therefore guard it with an ifdef. More details can be found at the use-site. +class MoveAssertionPredicatesVisitor : public PredicateVisitor { + ClonePredicateToTargetLoop _clone_predicate_to_loop; + PhaseIdealLoop* const _phase; + +public: + MoveAssertionPredicatesVisitor(LoopNode* target_loop_head, + const NodeInSingleLoopBody &node_in_loop_body, + PhaseIdealLoop* phase) + : _clone_predicate_to_loop(target_loop_head, node_in_loop_body, phase), + _phase(phase) { + } + NONCOPYABLE(MoveAssertionPredicatesVisitor); + + using PredicateVisitor::visit; + + void visit(const TemplateAssertionPredicate& template_assertion_predicate) override { + _clone_predicate_to_loop.clone_template_assertion_predicate(template_assertion_predicate); + template_assertion_predicate.kill(_phase->igvn()); + } +}; +#endif // ASSERT + // Transform: // // loop<-----------------+ @@ -4248,6 +4275,7 @@ bool PhaseIdealLoop::duplicate_loop_backedge(IdealLoopTree *loop, Node_List &old IfNode* exit_test = nullptr; uint inner; float f; +#ifdef ASSERT if (StressDuplicateBackedge) { if (head->is_strip_mined()) { return false; @@ -4266,7 +4294,9 @@ bool PhaseIdealLoop::duplicate_loop_backedge(IdealLoopTree *loop, Node_List &old } inner = 1; - } else { + } else +#endif //ASSERT + { // Is the shape of the loop that of a counted loop... Node* back_control = loop_exit_control(head, loop); if (back_control == nullptr) { @@ -4457,6 +4487,19 @@ bool PhaseIdealLoop::duplicate_loop_backedge(IdealLoopTree *loop, Node_List &old } } +#ifdef ASSERT + if (StressDuplicateBackedge && head->is_CountedLoop()) { + // The Template Assertion Predicates from the old counted loop are now at the new outer loop - clone them to + // the inner counted loop and kill the old ones. We only need to do this with debug builds because + // StressDuplicateBackedge is a devlop flag and false by default. Without StressDuplicateBackedge 'head' will be a + // non-counted loop, and thus we have no Template Assertion Predicates above the old loop to move down. + PredicateIterator predicate_iterator(outer_head->in(LoopNode::EntryControl)); + NodeInSingleLoopBody node_in_body(this, loop); + MoveAssertionPredicatesVisitor move_assertion_predicates_visitor(head, node_in_body, this); + predicate_iterator.for_each(move_assertion_predicates_visitor); + } +#endif // ASSERT + C->set_major_progress(); C->print_method(PHASE_AFTER_DUPLICATE_LOOP_BACKEDGE, 4, outer_head); diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 6067bcbac8d..ec80fb6a0ab 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -2176,7 +2176,10 @@ class BFSActions : public StackObj { virtual bool is_target_node(Node* node) const = 0; // Defines an action that should be taken when we visit a target node in the BFS traversal. - virtual void target_node_action(Node* target_node) = 0; + // To give more freedom, we pass the direct child node to the target node such that + // child->in(i) == target node. This allows to also directly replace the target node instead + // of only updating its inputs. + virtual void target_node_action(Node* child, uint i) = 0; }; // Class to perform a BFS traversal on the data nodes from a given start node. The provided BFSActions guide which @@ -2198,7 +2201,7 @@ class DataNodeBFS : public StackObj { Node* input = next->in(j); if (_bfs_actions.is_target_node(input)) { assert(_bfs_actions.should_visit(input), "must also pass node filter"); - _bfs_actions.target_node_action(input); + _bfs_actions.target_node_action(next, j); } else if (_bfs_actions.should_visit(input)) { _nodes_to_visit.push(input); } diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp index 84c01c68e38..136fc8ac864 100644 --- a/src/hotspot/share/opto/output.cpp +++ b/src/hotspot/share/opto/output.cpp @@ -1347,20 +1347,18 @@ CodeBuffer* PhaseOutput::init_buffer() { // nmethod and CodeBuffer count stubs & constants as part of method's code. // class HandlerImpl is platform-specific and defined in the *.ad files. - int exception_handler_req = HandlerImpl::size_exception_handler() + MAX_stubs_size; // add marginal slop for handler int deopt_handler_req = HandlerImpl::size_deopt_handler() + MAX_stubs_size; // add marginal slop for handler stub_req += MAX_stubs_size; // ensure per-stub margin code_req += MAX_inst_size; // ensure per-instruction margin if (StressCodeBuffers) - code_req = const_req = stub_req = exception_handler_req = deopt_handler_req = 0x10; // force expansion + code_req = const_req = stub_req = deopt_handler_req = 0x10; // force expansion int total_req = const_req + code_req + pad_req + stub_req + - exception_handler_req + deopt_handler_req; // deopt handler CodeBuffer* cb = code_buffer(); @@ -1789,8 +1787,6 @@ void PhaseOutput::fill_buffer(C2_MacroAssembler* masm, uint* blk_starts) { // Only java methods have exception handlers and deopt handlers // class HandlerImpl is platform-specific and defined in the *.ad files. if (C->method()) { - // Emit the exception handler code. - _code_offsets.set_value(CodeOffsets::Exceptions, HandlerImpl::emit_exception_handler(masm)); if (C->failing()) { return; // CodeBuffer::expand failed } diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 1fe911aa7ac..4a0933b89f2 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -1132,7 +1132,7 @@ void PhaseIterGVN::verify_empty_worklist(Node* node) { // (1) Integer "widen" changes, but the range is the same. // (2) LoadNode performs deep traversals. Load is not notified for changes far away. // (3) CmpPNode performs deep traversals if it compares oopptr. CmpP is not notified for changes far away. -bool PhaseIterGVN::verify_Value_for(Node* n) { +bool PhaseIterGVN::verify_Value_for(Node* n, bool strict) { // If we assert inside type(n), because the type is still a null, then maybe // the node never went through gvn.transform, which would be a bug. const Type* told = type(n); @@ -1152,7 +1152,7 @@ bool PhaseIterGVN::verify_Value_for(Node* n) { } // Exception (2) // LoadNode performs deep traversals. Load is not notified for changes far away. - if (n->is_Load() && !told->singleton()) { + if (!strict && n->is_Load() && !told->singleton()) { // MemNode::can_see_stored_value looks up through many memory nodes, // which means we would need to notify modifications from far up in // the inputs all the way down to the LoadNode. We don't do that. @@ -1160,7 +1160,7 @@ bool PhaseIterGVN::verify_Value_for(Node* n) { } // Exception (3) // CmpPNode performs deep traversals if it compares oopptr. CmpP is not notified for changes far away. - if (n->Opcode() == Op_CmpP && type(n->in(1))->isa_oopptr() && type(n->in(2))->isa_oopptr()) { + if (!strict && n->Opcode() == Op_CmpP && type(n->in(1))->isa_oopptr() && type(n->in(2))->isa_oopptr()) { // SubNode::Value // CmpPNode::sub // MemNode::detect_ptr_independence @@ -2799,6 +2799,7 @@ void PhaseCCP::analyze() { // Compile is over. The local arena gets de-allocated at the end of its scope. ResourceArea local_arena(mtCompiler); Unique_Node_List worklist(&local_arena); + Unique_Node_List worklist_revisit(&local_arena); DEBUG_ONLY(Unique_Node_List worklist_verify(&local_arena);) // Push root onto worklist @@ -2807,45 +2808,86 @@ void PhaseCCP::analyze() { assert(_root_and_safepoints.size() == 0, "must be empty (unused)"); _root_and_safepoints.push(C->root()); - // Pull from worklist; compute new value; push changes out. - // This loop is the meat of CCP. + // This is the meat of CCP: pull from worklist; compute new value; push changes out. + + // Do the first round. Since all initial types are TOP, this will visit all alive nodes. while (worklist.size() != 0) { Node* n = fetch_next_node(worklist); DEBUG_ONLY(worklist_verify.push(n);) + if (needs_revisit(n)) { + worklist_revisit.push(n); + } if (n->is_SafePoint()) { // Make sure safepoints are processed by PhaseCCP::transform even if they are // not reachable from the bottom. Otherwise, infinite loops would be removed. _root_and_safepoints.push(n); } - const Type* new_type = n->Value(this); - if (new_type != type(n)) { - DEBUG_ONLY(verify_type(n, new_type, type(n));) - dump_type_and_node(n, new_type); - set_type(n, new_type); - push_child_nodes_to_worklist(worklist, n); + analyze_step(worklist, n); + } + + // More rounds to catch updates far in the graph. + // Revisit nodes that might be able to refine their types at the end of the round. + // If so, process these nodes. If there is remaining work, start another round. + do { + while (worklist.size() != 0) { + Node* n = fetch_next_node(worklist); + analyze_step(worklist, n); } - if (KillPathsReachableByDeadTypeNode && n->is_Type() && new_type == Type::TOP) { - // Keep track of Type nodes to kill CFG paths that use Type - // nodes that become dead. - _maybe_top_type_nodes.push(n); + for (uint t = 0; t < worklist_revisit.size(); t++) { + Node* n = worklist_revisit.at(t); + analyze_step(worklist, n); } - } + } while (worklist.size() != 0); + DEBUG_ONLY(verify_analyze(worklist_verify);) } +void PhaseCCP::analyze_step(Unique_Node_List& worklist, Node* n) { + const Type* new_type = n->Value(this); + if (new_type != type(n)) { + DEBUG_ONLY(verify_type(n, new_type, type(n));) + dump_type_and_node(n, new_type); + set_type(n, new_type); + push_child_nodes_to_worklist(worklist, n); + } + if (KillPathsReachableByDeadTypeNode && n->is_Type() && new_type == Type::TOP) { + // Keep track of Type nodes to kill CFG paths that use Type + // nodes that become dead. + _maybe_top_type_nodes.push(n); + } +} + +// Some nodes can refine their types due to type change somewhere deep +// in the graph. We will need to revisit them before claiming convergence. +// Add nodes here if particular *Node::Value is doing deep graph traversals +// not handled by PhaseCCP::push_more_uses(). +bool PhaseCCP::needs_revisit(Node* n) const { + // LoadNode performs deep traversals. Load is not notified for changes far away. + if (n->is_Load()) { + return true; + } + // CmpPNode performs deep traversals if it compares oopptr. CmpP is not notified for changes far away. + if (n->Opcode() == Op_CmpP && type(n->in(1))->isa_oopptr() && type(n->in(2))->isa_oopptr()) { + return true; + } + return false; +} + #ifdef ASSERT // For every node n on verify list, check if type(n) == n->Value() -// We have a list of exceptions, see comments in verify_Value_for. +// Note for CCP the non-convergence can lead to unsound analysis and mis-compilation. +// Therefore, we are verifying Value convergence strictly. void PhaseCCP::verify_analyze(Unique_Node_List& worklist_verify) { bool failure = false; while (worklist_verify.size()) { Node* n = worklist_verify.pop(); - failure |= verify_Value_for(n); + failure |= verify_Value_for(n, /* strict = */ true); } // If we get this assert, check why the reported nodes were not processed again in CCP. // We should either make sure that these nodes are properly added back to the CCP worklist - // in PhaseCCP::push_child_nodes_to_worklist() to update their type or add an exception - // in the verification code above if that is not possible for some reason (like Load nodes). + // in PhaseCCP::push_child_nodes_to_worklist() to update their type in the same round, + // or that they are added in PhaseCCP::needs_revisit() so that analysis revisits + // them at the end of the round. assert(!failure, "PhaseCCP not at fixpoint: analysis result may be unsound."); } #endif diff --git a/src/hotspot/share/opto/phaseX.hpp b/src/hotspot/share/opto/phaseX.hpp index 083e77bf6d9..473231e6af5 100644 --- a/src/hotspot/share/opto/phaseX.hpp +++ b/src/hotspot/share/opto/phaseX.hpp @@ -490,7 +490,7 @@ class PhaseIterGVN : public PhaseGVN { void optimize(); #ifdef ASSERT void verify_optimize(); - bool verify_Value_for(Node* n); + bool verify_Value_for(Node* n, bool strict = false); bool verify_Ideal_for(Node* n, bool can_reshape); bool verify_Identity_for(Node* n); void verify_empty_worklist(Node* n); @@ -659,6 +659,8 @@ class PhaseCCP : public PhaseIterGVN { // Worklist algorithm identifies constants void analyze(); + void analyze_step(Unique_Node_List& worklist, Node* n); + bool needs_revisit(Node* n) const; #ifdef ASSERT void verify_type(Node* n, const Type* tnew, const Type* told); // For every node n on verify list, check if type(n) == n->Value() diff --git a/src/hotspot/share/opto/predicates.cpp b/src/hotspot/share/opto/predicates.cpp index 208bd6583c5..2489ff563a9 100644 --- a/src/hotspot/share/opto/predicates.cpp +++ b/src/hotspot/share/opto/predicates.cpp @@ -198,12 +198,21 @@ TemplateAssertionPredicate TemplateAssertionPredicate::clone_and_replace_opaque_ Node* new_opaque_input, CountedLoopNode* new_loop_node, PhaseIdealLoop* phase) const { - DEBUG_ONLY(verify();) OpaqueLoopInitNode* new_opaque_init = new OpaqueLoopInitNode(phase->C, new_opaque_input); phase->register_new_node(new_opaque_init, new_control); + return clone_and_replace_init(new_control, new_opaque_init, new_loop_node, phase); +} + +// Clone this Template Assertion Predicate and replace the old OpaqueLoopInit node with 'new_init'. +// Note: 'new_init' could also have the 'OpaqueLoopInit` as parent node further up. +TemplateAssertionPredicate TemplateAssertionPredicate::clone_and_replace_init(Node* new_control, + Node* new_init, + CountedLoopNode* new_loop_node, + PhaseIdealLoop* phase) const { + DEBUG_ONLY(verify();) TemplateAssertionExpression template_assertion_expression(opaque_node(), phase); OpaqueTemplateAssertionPredicateNode* new_opaque_node = - template_assertion_expression.clone_and_replace_init(new_control, new_opaque_init, new_loop_node); + template_assertion_expression.clone_and_replace_init(new_control, new_init, new_loop_node); AssertionPredicateIfCreator assertion_predicate_if_creator(phase); IfTrueNode* success_proj = assertion_predicate_if_creator.create_for_template(new_control, _if_node->Opcode(), new_opaque_node, @@ -238,8 +247,40 @@ class ReplaceOpaqueStrideInput : public BFSActions { return node->is_OpaqueLoopStride(); } - void target_node_action(Node* target_node) override { - _igvn.replace_input_of(target_node, 1, _new_opaque_stride_input); + void target_node_action(Node* child, uint i) override { + assert(child->in(i)->is_OpaqueLoopStride(), "must be OpaqueLoopStride"); + _igvn.replace_input_of(child->in(i), 1, _new_opaque_stride_input); + } +}; + +// This class is used to replace the OpaqueLoopInitNode with a new node while leaving the other nodes +// unchanged. +class ReplaceOpaqueInitNode : public BFSActions { + Node* _new_opaque_init_node; + PhaseIterGVN& _igvn; + + public: + ReplaceOpaqueInitNode(Node* new_opaque_init_node, PhaseIterGVN& igvn) + : _new_opaque_init_node(new_opaque_init_node), + _igvn(igvn) {} + NONCOPYABLE(ReplaceOpaqueInitNode); + + void replace_for(OpaqueTemplateAssertionPredicateNode* opaque_node) { + DataNodeBFS bfs(*this); + bfs.run(opaque_node); + } + + bool should_visit(Node* node) const override { + return TemplateAssertionExpressionNode::is_maybe_in_expression(node); + } + + bool is_target_node(Node* node) const override { + return node->is_OpaqueLoopInit(); + } + + void target_node_action(Node* child, uint i) override { + assert(child->in(i)->is_OpaqueLoopInit(), "must be old OpaqueLoopInit"); + _igvn.replace_input_of(child, i, _new_opaque_init_node); } }; @@ -250,6 +291,13 @@ void TemplateAssertionPredicate::replace_opaque_stride_input(Node* new_stride, P replace_opaque_stride_input.replace_for(opaque_node()); } +// Replace the OpaqueLoopInitNode with 'new_init' and leave the other nodes unchanged. +void TemplateAssertionPredicate::replace_opaque_init_node(Node* new_init, PhaseIterGVN& igvn) const { + DEBUG_ONLY(verify();) + ReplaceOpaqueInitNode replace_opaque_init_node(new_init, igvn); + replace_opaque_init_node.replace_for(opaque_node()); +} + // Create a new Initialized Assertion Predicate from this template at the template success projection. InitializedAssertionPredicate TemplateAssertionPredicate::initialize(PhaseIdealLoop* phase) const { DEBUG_ONLY(verify();) @@ -308,7 +356,8 @@ class OpaqueLoopNodesVerifier : public BFSActions { return node->is_Opaque1(); } - void target_node_action(Node* target_node) override { + void target_node_action(Node* child, uint i) override { + Node* target_node = child->in(i); if (target_node->is_OpaqueLoopInit()) { assert(!_found_init, "should only find one OpaqueLoopInitNode"); _found_init = true; @@ -1094,6 +1143,18 @@ void ClonePredicateToTargetLoop::clone_template_assertion_predicate( _target_loop_predicate_chain.insert_predicate(cloned_template_assertion_predicate); } +// Clones the provided Template Assertion Predicate to the head of the current predicate chain at the target loop and +// replaces the current OpaqueLoopInit with 'new_init'. +// Note: 'new_init' could also have the 'OpaqueLoopInit` as parent node further up. +void ClonePredicateToTargetLoop::clone_template_assertion_predicate_and_replace_init( + const TemplateAssertionPredicate& template_assertion_predicate, Node* new_init) { + TemplateAssertionPredicate cloned_template_assertion_predicate = + template_assertion_predicate.clone_and_replace_init(_old_target_loop_entry, new_init, _target_loop_head->as_CountedLoop(), _phase); + template_assertion_predicate.rewire_loop_data_dependencies(cloned_template_assertion_predicate.tail(), + _node_in_loop_body, _phase); + _target_loop_predicate_chain.insert_predicate(cloned_template_assertion_predicate); +} + CloneUnswitchedLoopPredicatesVisitor::CloneUnswitchedLoopPredicatesVisitor( LoopNode* true_path_loop_head, LoopNode* false_path_loop_head, const NodeInOriginalLoopBody& node_in_true_path_loop_body, const NodeInClonedLoopBody& node_in_false_path_loop_body, @@ -1182,6 +1243,10 @@ void UpdateStrideForAssertionPredicates::connect_initialized_assertion_predicate } } +void UpdateInitForTemplateAssertionPredicates::visit(const TemplateAssertionPredicate& template_assertion_predicate) { + template_assertion_predicate.replace_opaque_init_node(_new_init, _phase->igvn()); +} + // Do the following to find and eliminate useless Parse and Template Assertion Predicates: // 1. Mark all Parse and Template Assertion Predicates "maybe useful". // 2. Walk through the loop tree and iterate over all Predicates above each loop head. All found Parse and Template diff --git a/src/hotspot/share/opto/predicates.hpp b/src/hotspot/share/opto/predicates.hpp index 32b1c1cd3c4..cd0832cc062 100644 --- a/src/hotspot/share/opto/predicates.hpp +++ b/src/hotspot/share/opto/predicates.hpp @@ -438,7 +438,10 @@ class TemplateAssertionPredicate : public Predicate { TemplateAssertionPredicate clone(Node* new_control, CountedLoopNode* new_loop_node, PhaseIdealLoop* phase) const; TemplateAssertionPredicate clone_and_replace_opaque_input(Node* new_control, Node* new_opaque_input, CountedLoopNode* new_loop_node, PhaseIdealLoop* phase) const; + TemplateAssertionPredicate clone_and_replace_init(Node* new_control, Node* new_input, + CountedLoopNode* new_loop_node, PhaseIdealLoop* phase) const; void replace_opaque_stride_input(Node* new_stride, PhaseIterGVN& igvn) const; + void replace_opaque_init_node(Node* new_init, PhaseIterGVN& igvn) const; InitializedAssertionPredicate initialize(PhaseIdealLoop* phase) const; void rewire_loop_data_dependencies(IfTrueNode* target_predicate, const NodeInLoopBody& data_in_loop_body, const PhaseIdealLoop* phase) const; @@ -1228,6 +1231,7 @@ class ClonePredicateToTargetLoop : public StackObj { } void clone_template_assertion_predicate(const TemplateAssertionPredicate& template_assertion_predicate); + void clone_template_assertion_predicate_and_replace_init(const TemplateAssertionPredicate& template_assertion_predicate, Node* new_init); }; // Visitor to clone Parse and Template Assertion Predicates from a loop to its unswitched true and false path loop. @@ -1300,6 +1304,22 @@ class UpdateStrideForAssertionPredicates : public PredicateVisitor { void visit(const InitializedAssertionPredicate& initialized_assertion_predicate) override; }; +// This visitor replaces the OpaqueLoopInitNode for an Assertion Predicate with the expression passed as input. +class UpdateInitForTemplateAssertionPredicates : public PredicateVisitor { + Node* const _new_init; + PhaseIdealLoop* const _phase; + +public: + UpdateInitForTemplateAssertionPredicates(Node* const new_init, PhaseIdealLoop* phase) + : _new_init(new_init), + _phase(phase) {} + NONCOPYABLE(UpdateInitForTemplateAssertionPredicates); + + using PredicateVisitor::visit; + + void visit(const TemplateAssertionPredicate& template_assertion_predicate) override; +}; + // Eliminate all useless Parse and Template Assertion Predicates. They become useless when they can no longer be found // from a loop head. We mark these useless to clean them up later during IGVN. A Predicate that is marked useless will // no longer be visited by a PredicateVisitor. diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 96fee925e5d..ecb8c2c1cd8 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -45,6 +45,8 @@ #include "opto/type.hpp" #include "runtime/stubRoutines.hpp" #include "utilities/checkedCast.hpp" +#include "utilities/debug.hpp" +#include "utilities/ostream.hpp" #include "utilities/powerOfTwo.hpp" #include "utilities/stringUtils.hpp" @@ -2979,15 +2981,22 @@ const char *const TypePtr::ptr_msg[TypePtr::lastPTR] = { #ifndef PRODUCT void TypePtr::dump2( Dict &d, uint depth, outputStream *st ) const { - if( _ptr == Null ) st->print("null"); - else st->print("%s *", ptr_msg[_ptr]); - if( _offset == OffsetTop ) st->print("+top"); - else if( _offset == OffsetBot ) st->print("+bot"); - else if( _offset ) st->print("+%d", _offset); + st->print("ptr:%s", ptr_msg[_ptr]); + dump_offset(st); dump_inline_depth(st); dump_speculative(st); } +void TypePtr::dump_offset(outputStream* st) const { + if (_offset == OffsetBot) { + st->print("+bot"); + } else if (_offset == OffsetTop) { + st->print("+top"); + } else { + st->print("+%d", _offset); + } +} + /** *dump the speculative part of the type */ @@ -3159,11 +3168,12 @@ uint TypeRawPtr::hash(void) const { //------------------------------dump2------------------------------------------ #ifndef PRODUCT -void TypeRawPtr::dump2( Dict &d, uint depth, outputStream *st ) const { - if( _ptr == Constant ) - st->print(INTPTR_FORMAT, p2i(_bits)); - else +void TypeRawPtr::dump2(Dict& d, uint depth, outputStream* st) const { + if (_ptr == Constant) { + st->print("rawptr:Constant:" INTPTR_FORMAT, p2i(_bits)); + } else { st->print("rawptr:%s", ptr_msg[_ptr]); + } } #endif @@ -3798,24 +3808,29 @@ uint TypeOopPtr::hash(void) const { //------------------------------dump2------------------------------------------ #ifndef PRODUCT -void TypeOopPtr::dump2( Dict &d, uint depth, outputStream *st ) const { +void TypeOopPtr::dump2(Dict& d, uint depth, outputStream* st) const { st->print("oopptr:%s", ptr_msg[_ptr]); - if( _klass_is_exact ) st->print(":exact"); - if( const_oop() ) st->print(INTPTR_FORMAT, p2i(const_oop())); - switch( _offset ) { - case OffsetTop: st->print("+top"); break; - case OffsetBot: st->print("+any"); break; - case 0: break; - default: st->print("+%d",_offset); break; - } - if (_instance_id == InstanceTop) - st->print(",iid=top"); - else if (_instance_id != InstanceBot) - st->print(",iid=%d",_instance_id); - + if (_klass_is_exact) { + st->print(":exact"); + } + if (const_oop() != nullptr) { + st->print(":" INTPTR_FORMAT, p2i(const_oop())); + } + dump_offset(st); + dump_instance_id(st); dump_inline_depth(st); dump_speculative(st); } + +void TypeOopPtr::dump_instance_id(outputStream* st) const { + if (_instance_id == InstanceTop) { + st->print(",iid=top"); + } else if (_instance_id == InstanceBot) { + st->print(",iid=bot"); + } else { + st->print(",iid=%d", _instance_id); + } +} #endif //------------------------------singleton-------------------------------------- @@ -4453,50 +4468,30 @@ bool TypeInstPtr::maybe_java_subtype_of_helper(const TypeOopPtr* other, bool thi #ifndef PRODUCT void TypeInstPtr::dump2(Dict &d, uint depth, outputStream* st) const { // Print the name of the klass. + st->print("instptr:"); klass()->print_name_on(st); _interfaces->dump(st); - switch( _ptr ) { - case Constant: - if (WizardMode || Verbose) { - ResourceMark rm; - stringStream ss; - - st->print(" "); - const_oop()->print_oop(&ss); - // 'const_oop->print_oop()' may emit newlines('\n') into ss. - // suppress newlines from it so -XX:+Verbose -XX:+PrintIdeal dumps one-liner for each node. - char* buf = ss.as_string(/* c_heap= */false); - StringUtils::replace_no_expand(buf, "\n", ""); - st->print_raw(buf); - } - case BotPTR: - if (!WizardMode && !Verbose) { - if( _klass_is_exact ) st->print(":exact"); - break; - } - case TopPTR: - case AnyNull: - case NotNull: - st->print(":%s", ptr_msg[_ptr]); - if( _klass_is_exact ) st->print(":exact"); - break; - default: - break; - } + if (_ptr == Constant && (WizardMode || Verbose)) { + ResourceMark rm; + stringStream ss; - if( _offset ) { // Dump offset, if any - if( _offset == OffsetBot ) st->print("+any"); - else if( _offset == OffsetTop ) st->print("+unknown"); - else st->print("+%d", _offset); + st->print(" "); + const_oop()->print_oop(&ss); + // 'const_oop->print_oop()' may emit newlines('\n') into ss. + // suppress newlines from it so -XX:+Verbose -XX:+PrintIdeal dumps one-liner for each node. + char* buf = ss.as_string(/* c_heap= */false); + StringUtils::replace_no_expand(buf, "\n", ""); + st->print_raw(buf); } - st->print(" *"); - if (_instance_id == InstanceTop) - st->print(",iid=top"); - else if (_instance_id != InstanceBot) - st->print(",iid=%d",_instance_id); + st->print(":%s", ptr_msg[_ptr]); + if (_klass_is_exact) { + st->print(":exact"); + } + dump_offset(st); + dump_instance_id(st); dump_inline_depth(st); dump_speculative(st); } @@ -5089,26 +5084,17 @@ const Type *TypeAryPtr::xdual() const { //------------------------------dump2------------------------------------------ #ifndef PRODUCT void TypeAryPtr::dump2( Dict &d, uint depth, outputStream *st ) const { - _ary->dump2(d,depth,st); + st->print("aryptr:"); + _ary->dump2(d, depth, st); _interfaces->dump(st); - switch( _ptr ) { - case Constant: + if (_ptr == Constant) { const_oop()->print(st); - break; - case BotPTR: - if (!WizardMode && !Verbose) { - if( _klass_is_exact ) st->print(":exact"); - break; - } - case TopPTR: - case AnyNull: - case NotNull: - st->print(":%s", ptr_msg[_ptr]); - if( _klass_is_exact ) st->print(":exact"); - break; - default: - break; + } + + st->print(":%s", ptr_msg[_ptr]); + if (_klass_is_exact) { + st->print(":exact"); } if( _offset != 0 ) { @@ -5126,12 +5112,8 @@ void TypeAryPtr::dump2( Dict &d, uint depth, outputStream *st ) const { } } } - st->print(" *"); - if (_instance_id == InstanceTop) - st->print(",iid=top"); - else if (_instance_id != InstanceBot) - st->print(",iid=%d",_instance_id); + dump_instance_id(st); dump_inline_depth(st); dump_speculative(st); } @@ -5490,13 +5472,10 @@ const Type *TypeMetadataPtr::xdual() const { #ifndef PRODUCT void TypeMetadataPtr::dump2( Dict &d, uint depth, outputStream *st ) const { st->print("metadataptr:%s", ptr_msg[_ptr]); - if( metadata() ) st->print(INTPTR_FORMAT, p2i(metadata())); - switch( _offset ) { - case OffsetTop: st->print("+top"); break; - case OffsetBot: st->print("+any"); break; - case 0: break; - default: st->print("+%d",_offset); break; + if (metadata() != nullptr) { + st->print(":" INTPTR_FORMAT, p2i(metadata())); } + dump_offset(st); } #endif @@ -5644,44 +5623,6 @@ intptr_t TypeKlassPtr::get_con() const { return (intptr_t)k->constant_encoding(); } -//------------------------------dump2------------------------------------------ -// Dump Klass Type -#ifndef PRODUCT -void TypeKlassPtr::dump2(Dict & d, uint depth, outputStream *st) const { - switch(_ptr) { - case Constant: - st->print("precise "); - case NotNull: - { - const char *name = klass()->name()->as_utf8(); - if (name) { - st->print("%s: " INTPTR_FORMAT, name, p2i(klass())); - } else { - ShouldNotReachHere(); - } - _interfaces->dump(st); - } - case BotPTR: - if (!WizardMode && !Verbose && _ptr != Constant) break; - case TopPTR: - case AnyNull: - st->print(":%s", ptr_msg[_ptr]); - if (_ptr == Constant) st->print(":exact"); - break; - default: - break; - } - - if (_offset) { // Dump offset, if any - if (_offset == OffsetBot) { st->print("+any"); } - else if (_offset == OffsetTop) { st->print("+unknown"); } - else { st->print("+%d", _offset); } - } - - st->print(" *"); -} -#endif - //============================================================================= // Convenience common pre-built types. @@ -6036,6 +5977,15 @@ const TypeKlassPtr* TypeInstKlassPtr::try_improve() const { return this; } +#ifndef PRODUCT +void TypeInstKlassPtr::dump2(Dict& d, uint depth, outputStream* st) const { + st->print("instklassptr:"); + klass()->print_name_on(st); + _interfaces->dump(st); + st->print(":%s", ptr_msg[_ptr]); + dump_offset(st); +} +#endif // PRODUCT const TypeAryKlassPtr *TypeAryKlassPtr::make(PTR ptr, const Type* elem, ciKlass* k, int offset) { return (TypeAryKlassPtr*)(new TypeAryKlassPtr(ptr, elem, k, offset))->hashcons(); @@ -6507,34 +6457,11 @@ ciKlass* TypeAryKlassPtr::klass() const { // Dump Klass Type #ifndef PRODUCT void TypeAryKlassPtr::dump2( Dict & d, uint depth, outputStream *st ) const { - switch( _ptr ) { - case Constant: - st->print("precise "); - case NotNull: - { - st->print("["); - _elem->dump2(d, depth, st); - _interfaces->dump(st); - st->print(": "); - } - case BotPTR: - if( !WizardMode && !Verbose && _ptr != Constant ) break; - case TopPTR: - case AnyNull: - st->print(":%s", ptr_msg[_ptr]); - if( _ptr == Constant ) st->print(":exact"); - break; - default: - break; - } - - if( _offset ) { // Dump offset, if any - if( _offset == OffsetBot ) { st->print("+any"); } - else if( _offset == OffsetTop ) { st->print("+unknown"); } - else { st->print("+%d", _offset); } - } - - st->print(" *"); + st->print("aryklassptr:["); + _elem->dump2(d, depth, st); + _interfaces->dump(st); + st->print(":%s", ptr_msg[_ptr]); + dump_offset(st); } #endif diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index c61c2a64278..4666cfbcf2d 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -1176,15 +1176,15 @@ class TypePtr : public Type { int hash_speculative() const; const TypePtr* add_offset_speculative(intptr_t offset) const; const TypePtr* with_offset_speculative(intptr_t offset) const; -#ifndef PRODUCT - void dump_speculative(outputStream *st) const; -#endif // utility methods to work on the inline depth of the type int dual_inline_depth() const; int meet_inline_depth(int depth) const; + #ifndef PRODUCT - void dump_inline_depth(outputStream *st) const; + void dump_speculative(outputStream* st) const; + void dump_inline_depth(outputStream* st) const; + void dump_offset(outputStream* st) const; #endif // TypeInstPtr (TypeAryPtr resp.) and TypeInstKlassPtr (TypeAryKlassPtr resp.) implement very similar meet logic. @@ -1364,6 +1364,10 @@ class TypeOopPtr : public TypePtr { virtual ciKlass* exact_klass_helper() const { return nullptr; } virtual ciKlass* klass() const { return _klass; } +#ifndef PRODUCT + void dump_instance_id(outputStream* st) const; +#endif // PRODUCT + public: bool is_java_subtype_of(const TypeOopPtr* other) const { @@ -1832,9 +1836,6 @@ class TypeKlassPtr : public TypePtr { virtual const TypeKlassPtr* try_improve() const { return this; } -#ifndef PRODUCT - virtual void dump2( Dict &d, uint depth, outputStream *st ) const; // Specialized per-Type dumping -#endif private: virtual bool is_meet_subtype_of(const TypePtr* other) const { return is_meet_subtype_of_helper(other->is_klassptr(), klass_is_exact(), other->is_klassptr()->klass_is_exact()); @@ -1914,6 +1915,11 @@ class TypeInstKlassPtr : public TypeKlassPtr { // Convenience common pre-built types. static const TypeInstKlassPtr* OBJECT; // Not-null object klass or below static const TypeInstKlassPtr* OBJECT_OR_NULL; // Maybe-null version of same + +#ifndef PRODUCT + virtual void dump2(Dict& d, uint depth, outputStream* st) const; +#endif // PRODUCT + private: virtual bool is_meet_subtype_of_helper(const TypeKlassPtr* other, bool this_xk, bool other_xk) const; }; diff --git a/src/hotspot/share/opto/vectorization.cpp b/src/hotspot/share/opto/vectorization.cpp index 98f3d79c9f5..15b2df663b6 100644 --- a/src/hotspot/share/opto/vectorization.cpp +++ b/src/hotspot/share/opto/vectorization.cpp @@ -1022,27 +1022,39 @@ bool VPointer::can_make_speculative_aliasing_check_with(const VPointer& other) c // or at the multiversion_if. That is before the pre-loop. From the construction of // VPointer, we already know that all its variables (except iv) are pre-loop invariant. // - // For the computation of main_init, we also need the pre_limit, and so we need - // to check that this value is pre-loop invariant. In the case of non-equal iv_scales, - // we also need the main_limit in the aliasing check, and so this value must then - // also be pre-loop invariant. + // In VPointer::make_speculative_aliasing_check_with we compute main_init in all + // cases. For this, we require pre_init and pre_limit. These values must be available + // for the speculative check, i.e. their control must dominate the speculative check. + // Further, "if vp1.iv_scale() != vp2.iv_scale()" we additionally need to have + // main_limit available for the speculative check. + // Note: no matter if the speculative check is inserted as a predicate or at the + // multiversion if, the speculative check happens before (dominates) the + // pre-loop. + Node* pre_init = _vloop.pre_loop_end()->init_trip(); Opaque1Node* pre_limit_opaq = _vloop.pre_loop_end()->limit()->as_Opaque1(); Node* pre_limit = pre_limit_opaq->in(1); Node* main_limit = _vloop.cl()->limit(); - - if (!_vloop.is_pre_loop_invariant(pre_limit)) { + if (!_vloop.is_available_for_speculative_check(pre_init)) { +#ifdef ASSERT + if (_vloop.is_trace_speculative_aliasing_analysis()) { + tty->print_cr("VPointer::can_make_speculative_aliasing_check_with: pre_limit is not available at speculative check!"); + } +#endif + return false; + } + if (!_vloop.is_available_for_speculative_check(pre_limit)) { #ifdef ASSERT if (_vloop.is_trace_speculative_aliasing_analysis()) { - tty->print_cr("VPointer::can_make_speculative_aliasing_check_with: pre_limit is not pre-loop independent!"); + tty->print_cr("VPointer::can_make_speculative_aliasing_check_with: pre_limit is not available at speculative check!"); } #endif return false; } - if (vp1.iv_scale() != vp2.iv_scale() && !_vloop.is_pre_loop_invariant(main_limit)) { + if (vp1.iv_scale() != vp2.iv_scale() && !_vloop.is_available_for_speculative_check(main_limit)) { #ifdef ASSERT if (_vloop.is_trace_speculative_aliasing_analysis()) { - tty->print_cr("VPointer::can_make_speculative_aliasing_check_with: main_limit is not pre-loop independent!"); + tty->print_cr("VPointer::can_make_speculative_aliasing_check_with: main_limit is not available at speculative check!"); } #endif return false; @@ -1119,6 +1131,8 @@ BoolNode* VPointer::make_speculative_aliasing_check_with(const VPointer& other, Node* pre_limit = pre_limit_opaq->in(1); assert(_vloop.is_pre_loop_invariant(pre_init), "needed for aliasing check before pre-loop"); assert(_vloop.is_pre_loop_invariant(pre_limit), "needed for aliasing check before pre-loop"); + assert(_vloop.is_available_for_speculative_check(pre_init), "ctrl must be early enough to avoid cycles"); + assert(_vloop.is_available_for_speculative_check(pre_limit), "ctrl must be early enough to avoid cycles"); Node* pre_initL = new ConvI2LNode(pre_init); Node* pre_limitL = new ConvI2LNode(pre_limit); @@ -1180,6 +1194,7 @@ BoolNode* VPointer::make_speculative_aliasing_check_with(const VPointer& other, jint main_iv_stride = _vloop.iv_stride(); Node* main_limit = _vloop.cl()->limit(); assert(_vloop.is_pre_loop_invariant(main_limit), "needed for aliasing check before pre-loop"); + assert(_vloop.is_available_for_speculative_check(main_limit), "ctrl must be early enough to avoid cycles"); Node* main_limitL = new ConvI2LNode(main_limit); phase->register_new_node_with_ctrl_of(main_limitL, pre_init); diff --git a/src/hotspot/share/opto/vectorization.hpp b/src/hotspot/share/opto/vectorization.hpp index f7099b5b7c0..aacd406f798 100644 --- a/src/hotspot/share/opto/vectorization.hpp +++ b/src/hotspot/share/opto/vectorization.hpp @@ -236,6 +236,8 @@ class VLoop : public StackObj { // Some nodes must be pre-loop invariant, so that they can be used for conditions // before or inside the pre-loop. For example, alignment of main-loop vector // memops must be achieved in the pre-loop, via the exit check in the pre-loop. + // Note: this condition is NOT strong enough for speculative checks, those happen + // before the pre-loop. See is_available_for_speculative_check bool is_pre_loop_invariant(Node* n) const { // Must be in the main-loop, otherwise we can't access the pre-loop. // This fails during SuperWord::unrolling_analysis, but that is ok. @@ -257,6 +259,28 @@ class VLoop : public StackObj { return is_before_pre_loop(early); } + // Nodes that are to be used in speculative checks must be available early enough. + // Note: the speculative check happens before the pre-loop, either at the auto + // vectorization predicate or the multiversion if. This is before the + // pre-loop, and thus the condition here is stronger then the one from + // is_pre_loop_invariant. + bool is_available_for_speculative_check(Node* n) const { + assert(are_speculative_checks_possible(), "meaningless without speculative check"); + ParsePredicateSuccessProj* parse_predicate_proj = auto_vectorization_parse_predicate_proj(); + // Find the control of the predicate: + ProjNode* proj = (parse_predicate_proj != nullptr) ? parse_predicate_proj : multiversioning_fast_proj(); + Node* check_ctrl = proj->in(0)->as_If()->in(0); + + // Often, the control of n already dominates that of the predicate. + Node* n_ctrl = phase()->get_ctrl(n); + if (phase()->is_dominator(n_ctrl, check_ctrl)) { return true; } + + // But in some cases, the ctrl of n is after that of the predicate, + // but the early ctrl is before the predicate. + Node* n_early = phase()->compute_early_ctrl(n, n_ctrl); + return phase()->is_dominator(n_early, check_ctrl); + } + // Check if the loop passes some basic preconditions for vectorization. // Return indicates if analysis succeeded. bool check_preconditions(); diff --git a/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp b/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp index a441d405f8d..5077a1743b9 100644 --- a/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp +++ b/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp @@ -25,6 +25,7 @@ #include "classfile/symbolTable.hpp" #include "interpreter/bytecodeStream.hpp" #include "memory/universe.hpp" +#include "oops/bsmAttribute.inline.hpp" #include "oops/constantPool.inline.hpp" #include "oops/fieldStreams.inline.hpp" #include "oops/instanceKlass.inline.hpp" @@ -389,20 +390,13 @@ void JvmtiClassFileReconstituter::write_annotations_attribute(const char* attr_n // } bootstrap_methods[num_bootstrap_methods]; // } void JvmtiClassFileReconstituter::write_bootstrapmethod_attribute() { - Array* operands = cpool()->operands(); write_attribute_name_index("BootstrapMethods"); - int num_bootstrap_methods = ConstantPool::operand_array_length(operands); - - // calculate length of attribute - u4 length = sizeof(u2); // num_bootstrap_methods - for (int n = 0; n < num_bootstrap_methods; n++) { - u2 num_bootstrap_arguments = cpool()->bsm_attribute_entry(n)->argument_count(); - length += sizeof(u2); // bootstrap_method_ref - length += sizeof(u2); // num_bootstrap_arguments - length += (u4)sizeof(u2) * num_bootstrap_arguments; // bootstrap_arguments[num_bootstrap_arguments] - } + u4 length = sizeof(u2) + // Size of num_bootstrap_methods + // The rest of the data for the attribute is exactly the u2s in the data array. + sizeof(u2) * cpool()->bsm_entries().array_length(); write_u4(length); + int num_bootstrap_methods = cpool()->bsm_entries().number_of_entries(); // write attribute write_u2(checked_cast(num_bootstrap_methods)); for (int n = 0; n < num_bootstrap_methods; n++) { @@ -411,7 +405,7 @@ void JvmtiClassFileReconstituter::write_bootstrapmethod_attribute() { write_u2(bsme->bootstrap_method_index()); write_u2(num_bootstrap_arguments); for (int arg = 0; arg < num_bootstrap_arguments; arg++) { - u2 bootstrap_argument = bsme->argument_index(arg); + u2 bootstrap_argument = bsme->argument(arg); write_u2(bootstrap_argument); } } @@ -798,7 +792,7 @@ void JvmtiClassFileReconstituter::write_class_attributes() { if (type_anno != nullptr) { ++attr_count; // has RuntimeVisibleTypeAnnotations attribute } - if (cpool()->operands() != nullptr) { + if (!cpool()->bsm_entries().is_empty()) { ++attr_count; } if (ik()->nest_host_index() != 0) { @@ -843,7 +837,7 @@ void JvmtiClassFileReconstituter::write_class_attributes() { if (ik()->record_components() != nullptr) { write_record_attribute(); } - if (cpool()->operands() != nullptr) { + if (!cpool()->bsm_entries().is_empty()) { write_bootstrapmethod_attribute(); } if (inner_classes_length > 0) { diff --git a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp index ef8875d582e..13b239b4df0 100644 --- a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp +++ b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp @@ -45,7 +45,8 @@ #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/annotations.hpp" -#include "oops/constantPool.hpp" +#include "oops/bsmAttribute.inline.hpp" +#include "oops/constantPool.inline.hpp" #include "oops/fieldStreams.inline.hpp" #include "oops/klass.inline.hpp" #include "oops/klassVtable.hpp" @@ -573,9 +574,9 @@ void VM_RedefineClasses::append_entry(const constantPoolHandle& scratch_cp, case JVM_CONSTANT_Dynamic: // fall through case JVM_CONSTANT_InvokeDynamic: { - // Index of the bootstrap specifier in the operands array + // Index of the bootstrap specifier in the BSM array int old_bs_i = scratch_cp->bootstrap_methods_attribute_index(scratch_i); - int new_bs_i = find_or_append_operand(scratch_cp, old_bs_i, merge_cp_p, + int new_bs_i = find_or_append_bsm_entry(scratch_cp, old_bs_i, merge_cp_p, merge_cp_length_p); // The bootstrap method NameAndType_info index int old_ref_i = scratch_cp->bootstrap_name_and_type_ref_index_at(scratch_i); @@ -591,10 +592,11 @@ void VM_RedefineClasses::append_entry(const constantPoolHandle& scratch_cp, ("Dynamic entry@%d name_and_type_index change: %d to %d", *merge_cp_length_p, old_ref_i, new_ref_i); } - if (scratch_cp->tag_at(scratch_i).is_dynamic_constant()) + if (scratch_cp->tag_at(scratch_i).is_dynamic_constant()) { (*merge_cp_p)->dynamic_constant_at_put(*merge_cp_length_p, new_bs_i, new_ref_i); - else + } else { (*merge_cp_p)->invoke_dynamic_at_put(*merge_cp_length_p, new_bs_i, new_ref_i); + } if (scratch_i != *merge_cp_length_p) { // The new entry in *merge_cp_p is at a different index than // the new entry in scratch_cp so we need to map the index values. @@ -660,10 +662,10 @@ u2 VM_RedefineClasses::find_or_append_indirect_entry(const constantPoolHandle& s } // end find_or_append_indirect_entry() -// Append a bootstrap specifier into the merge_cp operands that is semantically equal -// to the scratch_cp operands bootstrap specifier passed by the old_bs_i index. +// Append a bootstrap specifier into the merge_cp BSM entries that is semantically equal +// to the scratch_cp BSM entries' bootstrap specifier passed by the old_bs_i index. // Recursively append new merge_cp entries referenced by the new bootstrap specifier. -void VM_RedefineClasses::append_operand(const constantPoolHandle& scratch_cp, const int old_bs_i, +int VM_RedefineClasses::append_bsm_entry(const constantPoolHandle& scratch_cp, const int old_bs_i, constantPoolHandle *merge_cp_p, int *merge_cp_length_p) { BSMAttributeEntry* old_bsme = scratch_cp->bsm_attribute_entry(old_bs_i); @@ -672,90 +674,82 @@ void VM_RedefineClasses::append_operand(const constantPoolHandle& scratch_cp, co merge_cp_length_p); if (new_ref_i != old_ref_i) { log_trace(redefine, class, constantpool) - ("operands entry@%d bootstrap method ref_index change: %d to %d", _operands_cur_length, old_ref_i, new_ref_i); + ("BSM attribute entry@%d bootstrap method ref_index change: %d to %d", _bsmae_iter.current_offset() - 1, old_ref_i, new_ref_i); } - Array* merge_ops = (*merge_cp_p)->operands(); - int new_bs_i = _operands_cur_length; - // We have _operands_cur_length == 0 when the merge_cp operands is empty yet. - // However, the operand_offset_at(0) was set in the extend_operands() call. - int new_base = (new_bs_i == 0) ? (*merge_cp_p)->operand_offset_at(0) - : (*merge_cp_p)->operand_next_offset_at(new_bs_i - 1); - u2 argc = old_bsme->argument_count(); - - ConstantPool::operand_offset_at_put(merge_ops, _operands_cur_length, new_base); - merge_ops->at_put(new_base++, new_ref_i); - merge_ops->at_put(new_base++, argc); - - for (int i = 0; i < argc; i++) { - u2 old_arg_ref_i = old_bsme->argument_index(i); + const int new_bs_i = _bsmae_iter.current_offset(); + BSMAttributeEntry* new_bsme = + _bsmae_iter.reserve_new_entry(new_ref_i, old_bsme->argument_count()); + assert(new_bsme != nullptr, "must be"); + for (int i = 0; i < new_bsme->argument_count(); i++) { + u2 old_arg_ref_i = old_bsme->argument(i); u2 new_arg_ref_i = find_or_append_indirect_entry(scratch_cp, old_arg_ref_i, merge_cp_p, merge_cp_length_p); - merge_ops->at_put(new_base++, new_arg_ref_i); + new_bsme->set_argument(i, new_arg_ref_i); + if (new_arg_ref_i != old_arg_ref_i) { log_trace(redefine, class, constantpool) - ("operands entry@%d bootstrap method argument ref_index change: %d to %d", - _operands_cur_length, old_arg_ref_i, new_arg_ref_i); + ("BSM attribute entry@%d bootstrap method argument ref_index change: %d to %d", + _bsmae_iter.current_offset() - 1, old_arg_ref_i, new_arg_ref_i); } } - if (old_bs_i != _operands_cur_length) { - // The bootstrap specifier in *merge_cp_p is at a different index than - // that in scratch_cp so we need to map the index values. - map_operand_index(old_bs_i, new_bs_i); - } - _operands_cur_length++; -} // end append_operand() + // This is only for the logging + map_bsm_index(old_bs_i, new_bs_i); + return new_bs_i; +} // end append_bsm_entry() -int VM_RedefineClasses::find_or_append_operand(const constantPoolHandle& scratch_cp, +int VM_RedefineClasses::find_or_append_bsm_entry(const constantPoolHandle& scratch_cp, int old_bs_i, constantPoolHandle *merge_cp_p, int *merge_cp_length_p) { + const int max_offset_in_merge = _bsmae_iter.current_offset(); int new_bs_i = old_bs_i; // bootstrap specifier index - bool match = (old_bs_i < _operands_cur_length) && - scratch_cp->compare_operand_to(old_bs_i, *merge_cp_p, old_bs_i); + // Has the old_bs_i index been used already? Check if it's the same so we know + // whether or not a remapping is required. + bool match = (old_bs_i < max_offset_in_merge) && + scratch_cp->compare_bootstrap_entry_to(old_bs_i, *merge_cp_p, old_bs_i); if (!match) { // forward reference in *merge_cp_p or not a direct match - int found_i = scratch_cp->find_matching_operand(old_bs_i, *merge_cp_p, - _operands_cur_length); + int found_i = scratch_cp->find_matching_bsm_entry(old_bs_i, *merge_cp_p, + max_offset_in_merge); if (found_i != -1) { - guarantee(found_i != old_bs_i, "compare_operand_to() and find_matching_operand() disagree"); - // found a matching operand somewhere else in *merge_cp_p so just need a mapping + guarantee(found_i != old_bs_i, "compare_bootstrap_entry_to() and find_matching_bsm_entry() disagree"); + // found a matching BSM entry somewhere else in *merge_cp_p so just need a mapping new_bs_i = found_i; - map_operand_index(old_bs_i, found_i); + map_bsm_index(old_bs_i, found_i); } else { // no match found so we have to append this bootstrap specifier to *merge_cp_p - append_operand(scratch_cp, old_bs_i, merge_cp_p, merge_cp_length_p); - new_bs_i = _operands_cur_length - 1; + new_bs_i = append_bsm_entry(scratch_cp, old_bs_i, merge_cp_p, merge_cp_length_p); } } return new_bs_i; -} // end find_or_append_operand() +} // end find_or_append_bsm_entry() -void VM_RedefineClasses::finalize_operands_merge(const constantPoolHandle& merge_cp, TRAPS) { - if (merge_cp->operands() == nullptr) { +void VM_RedefineClasses::finalize_bsm_entries_merge(const constantPoolHandle& merge_cp, TRAPS) { + if (merge_cp->bsm_entries().number_of_entries() == 0) { return; } - // Shrink the merge_cp operands - merge_cp->shrink_operands(_operands_cur_length, CHECK); + // Finished extending the BSMAEs + merge_cp->end_extension(_bsmae_iter, CHECK); if (log_is_enabled(Trace, redefine, class, constantpool)) { // don't want to loop unless we are tracing int count = 0; - for (int i = 1; i < _operands_index_map_p->length(); i++) { - int value = _operands_index_map_p->at(i); + for (int i = 1; i < _bsm_index_map_p->length(); i++) { + int value = _bsm_index_map_p->at(i); if (value != -1) { - log_trace(redefine, class, constantpool)("operands_index_map[%d]: old=%d new=%d", count, i, value); + log_trace(redefine, class, constantpool)("bsm_index_map[%d]: old=%d new=%d", count, i, value); count++; } } } // Clean-up - _operands_index_map_p = nullptr; - _operands_cur_length = 0; - _operands_index_map_count = 0; -} // end finalize_operands_merge() + _bsm_index_map_p = nullptr; + _bsm_index_map_count = 0; + _bsmae_iter = BSMAttributeEntries::InsertionIterator(); +} // end finalize_bsmentries_merge() // Symbol* comparator for qsort // The caller must have an active ResourceMark. @@ -1272,26 +1266,26 @@ u2 VM_RedefineClasses::find_new_index(int old_index) { // Find new bootstrap specifier index value for old bootstrap specifier index // value by searching the index map. Returns unused index (-1) if there is // no mapped value for the old bootstrap specifier index. -int VM_RedefineClasses::find_new_operand_index(int old_index) { - if (_operands_index_map_count == 0) { +int VM_RedefineClasses::find_new_bsm_index(int old_index) { + if (_bsm_index_map_count == 0) { // map is empty so nothing can be found return -1; } - if (old_index == -1 || old_index >= _operands_index_map_p->length()) { + if (old_index == -1 || old_index >= _bsm_index_map_p->length()) { // The old_index is out of range so it is not mapped. // This should not happen in regular constant pool merging use. return -1; } - int value = _operands_index_map_p->at(old_index); + int value = _bsm_index_map_p->at(old_index); if (value == -1) { // the old_index is not mapped return -1; } return value; -} // end find_new_operand_index() +} // end find_new_bsm_index() // The bug 6214132 caused the verification to fail. @@ -1560,22 +1554,15 @@ void VM_RedefineClasses::map_index(const constantPoolHandle& scratch_cp, // Map old_index to new_index as needed. -void VM_RedefineClasses::map_operand_index(int old_index, int new_index) { - if (find_new_operand_index(old_index) != -1) { - // old_index is already mapped - return; - } - +void VM_RedefineClasses::map_bsm_index(int old_index, int new_index) { if (old_index == new_index) { // no mapping is needed return; } - - _operands_index_map_p->at_put(old_index, new_index); - _operands_index_map_count++; - + _bsm_index_map_p->at_put(old_index, new_index); + _bsm_index_map_count++; log_trace(redefine, class, constantpool)("mapped bootstrap specifier at index %d to %d", old_index, new_index); -} // end map_index() +} // end map_bsm_index() // Merge old_cp and scratch_cp and return the results of the merge via @@ -1639,8 +1626,8 @@ bool VM_RedefineClasses::merge_constant_pools(const constantPoolHandle& old_cp, } } // end for each old_cp entry - ConstantPool::copy_operands(old_cp, merge_cp_p, CHECK_false); - merge_cp_p->extend_operands(scratch_cp, CHECK_false); + ConstantPool::copy_bsm_entries(old_cp, merge_cp_p, CHECK_false); + _bsmae_iter = merge_cp_p->start_extension(scratch_cp, CHECK_false); // We don't need to sanity check that *merge_cp_length_p is within // *merge_cp_p bounds since we have the minimum on-entry check above. @@ -1737,7 +1724,7 @@ bool VM_RedefineClasses::merge_constant_pools(const constantPoolHandle& old_cp, ("after pass 1b: merge_cp_len=%d, scratch_i=%d, index_map_len=%d", merge_cp_length_p, scratch_i, _index_map_count); } - finalize_operands_merge(merge_cp_p, CHECK_false); + finalize_bsm_entries_merge(merge_cp_p, CHECK_false); return true; } // end merge_constant_pools() @@ -1807,12 +1794,11 @@ jvmtiError VM_RedefineClasses::merge_cp_and_rewrite( _index_map_count = 0; _index_map_p = new intArray(scratch_cp->length(), scratch_cp->length(), -1); - _operands_cur_length = ConstantPool::operand_array_length(old_cp->operands()); - _operands_index_map_count = 0; - int operands_index_map_len = ConstantPool::operand_array_length(scratch_cp->operands()); - _operands_index_map_p = new intArray(operands_index_map_len, operands_index_map_len, -1); + _bsm_index_map_count = 0; + int bsm_data_len = scratch_cp->bsm_entries().array_length(); + _bsm_index_map_p = new intArray(bsm_data_len, bsm_data_len, -1); - // reference to the cp holder is needed for copy_operands() + // reference to the cp holder is needed for reallocating the BSM attribute merge_cp->set_pool_holder(scratch_class); bool result = merge_constant_pools(old_cp, scratch_cp, merge_cp, merge_cp_length, THREAD); @@ -3500,7 +3486,7 @@ void VM_RedefineClasses::set_new_constant_pool( smaller_cp->set_version(version); // attach klass to new constant pool - // reference to the cp holder is needed for copy_operands() + // reference to the cp holder is needed for reallocating the BSM attribute smaller_cp->set_pool_holder(scratch_class); smaller_cp->copy_fields(scratch_cp()); diff --git a/src/hotspot/share/prims/jvmtiRedefineClasses.hpp b/src/hotspot/share/prims/jvmtiRedefineClasses.hpp index d2eda1f3eed..3f1b555b175 100644 --- a/src/hotspot/share/prims/jvmtiRedefineClasses.hpp +++ b/src/hotspot/share/prims/jvmtiRedefineClasses.hpp @@ -363,11 +363,16 @@ class VM_RedefineClasses: public VM_Operation { int _index_map_count; intArray * _index_map_p; - // _operands_index_map_count is just an optimization for knowing if - // _operands_index_map_p contains any entries. - int _operands_cur_length; - int _operands_index_map_count; - intArray * _operands_index_map_p; + // _bsm_index_map_count is just an optimization for knowing if + // _bsm_index_map_p contains any entries. + int _bsm_index_map_count; + intArray * _bsm_index_map_p; + + // After merge_constant_pools "Pass 0", the BSMAttribute entries of merge_cp_p will have been expanded to fit + // scratch_cp's BSMAttribute entries as well. + // However, the newly acquired space will not have been filled in yet. + // To append to this new space, the iterator is used. + BSMAttributeEntries::InsertionIterator _bsmae_iter; // ptr to _class_count scratch_classes InstanceKlass** _scratch_classes; @@ -429,17 +434,18 @@ class VM_RedefineClasses: public VM_Operation { // Support for constant pool merging (these routines are in alpha order): void append_entry(const constantPoolHandle& scratch_cp, int scratch_i, constantPoolHandle *merge_cp_p, int *merge_cp_length_p); - void append_operand(const constantPoolHandle& scratch_cp, int scratch_bootstrap_spec_index, + // Returns the index of the appended BSM + int append_bsm_entry(const constantPoolHandle& scratch_cp, int scratch_bootstrap_spec_index, constantPoolHandle *merge_cp_p, int *merge_cp_length_p); - void finalize_operands_merge(const constantPoolHandle& merge_cp, TRAPS); + void finalize_bsm_entries_merge(const constantPoolHandle& merge_cp, TRAPS); u2 find_or_append_indirect_entry(const constantPoolHandle& scratch_cp, int scratch_i, constantPoolHandle *merge_cp_p, int *merge_cp_length_p); - int find_or_append_operand(const constantPoolHandle& scratch_cp, int scratch_bootstrap_spec_index, + int find_or_append_bsm_entry(const constantPoolHandle& scratch_cp, int scratch_bootstrap_spec_index, constantPoolHandle *merge_cp_p, int *merge_cp_length_p); u2 find_new_index(int old_index); - int find_new_operand_index(int old_bootstrap_spec_index); + int find_new_bsm_index(int old_bootstrap_spec_index); void map_index(const constantPoolHandle& scratch_cp, int old_index, int new_index); - void map_operand_index(int old_bootstrap_spec_index, int new_bootstrap_spec_index); + void map_bsm_index(int old_bootstrap_spec_index, int new_bootstrap_spec_index); bool merge_constant_pools(const constantPoolHandle& old_cp, const constantPoolHandle& scratch_cp, constantPoolHandle& merge_cp_p, int& merge_cp_length_p, TRAPS); diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 55ee7641a5f..ff1de899bdd 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -1482,6 +1482,7 @@ void Arguments::set_conservative_max_heap_alignment() { os::vm_allocation_granularity(), os::max_page_size(), GCArguments::compute_heap_alignment()); + assert(is_power_of_2(_conservative_max_heap_alignment), "Expected to be a power-of-2"); } jint Arguments::set_ergonomics_flags() { diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index 0aa7b392b17..e2029a26d37 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -498,6 +498,9 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread RegisterMap::WalkContinuation::skip); // Now get the deoptee with a valid map frame deoptee = stub_frame.sender(&map); + if (exec_mode == Unpack_deopt) { + assert(deoptee.is_deoptimized_frame(), "frame is not marked for deoptimization"); + } // Set the deoptee nmethod assert(current->deopt_compiled_method() == nullptr, "Pending deopt!"); nmethod* nm = deoptee.cb()->as_nmethod_or_null(); diff --git a/src/hotspot/share/runtime/frame.cpp b/src/hotspot/share/runtime/frame.cpp index b5cd4acc75d..8f969600ba8 100644 --- a/src/hotspot/share/runtime/frame.cpp +++ b/src/hotspot/share/runtime/frame.cpp @@ -206,7 +206,7 @@ address frame::raw_pc() const { if (is_deoptimized_frame()) { nmethod* nm = cb()->as_nmethod_or_null(); assert(nm != nullptr, "only nmethod is expected here"); - return nm->deopt_handler_begin() - pc_return_offset; + return nm->deopt_handler_entry() - pc_return_offset; } else { return (pc() - pc_return_offset); } @@ -355,7 +355,7 @@ void frame::deoptimize(JavaThread* thread) { // If the call site is a MethodHandle call site use the MH deopt handler. nmethod* nm = _cb->as_nmethod(); - address deopt = nm->deopt_handler_begin(); + address deopt = nm->deopt_handler_entry(); NativePostCallNop* inst = nativePostCallNop_at(pc()); diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index e008f29eecc..b65bf643cbf 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -534,6 +534,7 @@ class os: AllStatic { static void realign_memory(char *addr, size_t bytes, size_t alignment_hint); // NUMA-specific interface + static void numa_set_thread_affinity(Thread* thread, int node); static void numa_make_local(char *addr, size_t bytes, int lgrp_hint); static void numa_make_global(char *addr, size_t bytes); static size_t numa_get_groups_num(); diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 79c7c0b32b4..e277e1fb569 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -87,6 +87,9 @@ #ifdef COMPILER1 #include "c1/c1_Runtime1.hpp" #endif +#ifdef COMPILER2 +#include "opto/runtime.hpp" +#endif #if INCLUDE_JFR #include "jfr/jfr.inline.hpp" #endif @@ -601,6 +604,11 @@ address SharedRuntime::raw_exception_handler_for_return_address(JavaThread* curr // The deferred StackWatermarkSet::after_unwind check will be performed in // * OptoRuntime::handle_exception_C_helper for C2 code // * exception_handler_for_pc_helper via Runtime1::handle_exception_from_callee_id for C1 code +#ifdef COMPILER2 + if (nm->compiler_type() == compiler_c2) { + return OptoRuntime::exception_blob()->entry_point(); + } +#endif // COMPILER2 return nm->exception_begin(); } } diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index a75e67e9b56..25a99c2d758 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -54,6 +54,7 @@ #include "oops/array.hpp" #include "oops/arrayKlass.hpp" #include "oops/arrayOop.hpp" +#include "oops/bsmAttribute.hpp" #include "oops/constantPool.hpp" #include "oops/constMethod.hpp" #include "oops/cpCache.hpp" @@ -166,10 +167,12 @@ nonstatic_field(ArrayKlass, _dimension, int) \ volatile_nonstatic_field(ArrayKlass, _higher_dimension, ObjArrayKlass*) \ volatile_nonstatic_field(ArrayKlass, _lower_dimension, ArrayKlass*) \ + nonstatic_field(BSMAttributeEntries, _offsets, Array*) \ + nonstatic_field(BSMAttributeEntries, _bootstrap_methods, Array*) \ + nonstatic_field(ConstantPool, _bsm_entries, BSMAttributeEntries) \ nonstatic_field(ConstantPool, _tags, Array*) \ nonstatic_field(ConstantPool, _cache, ConstantPoolCache*) \ nonstatic_field(ConstantPool, _pool_holder, InstanceKlass*) \ - nonstatic_field(ConstantPool, _operands, Array*) \ nonstatic_field(ConstantPool, _resolved_klasses, Array*) \ nonstatic_field(ConstantPool, _length, int) \ nonstatic_field(ConstantPool, _minor_version, u2) \ @@ -534,7 +537,7 @@ nonstatic_field(nmethod, _osr_link, nmethod*) \ nonstatic_field(nmethod, _state, volatile signed char) \ nonstatic_field(nmethod, _exception_offset, int) \ - nonstatic_field(nmethod, _deopt_handler_offset, int) \ + nonstatic_field(nmethod, _deopt_handler_entry_offset, int) \ nonstatic_field(nmethod, _orig_pc_offset, int) \ nonstatic_field(nmethod, _stub_offset, int) \ nonstatic_field(nmethod, _immutable_data_ref_count_offset, int) \ @@ -733,6 +736,7 @@ unchecked_nonstatic_field(Array, _data, sizeof(int)) \ unchecked_nonstatic_field(Array, _data, sizeof(u1)) \ unchecked_nonstatic_field(Array, _data, sizeof(u2)) \ + unchecked_nonstatic_field(Array, _data, sizeof(u4)) \ unchecked_nonstatic_field(Array, _data, sizeof(Method*)) \ unchecked_nonstatic_field(Array, _data, sizeof(Klass*)) \ unchecked_nonstatic_field(Array, _data, sizeof(ResolvedFieldEntry)) \ @@ -964,6 +968,7 @@ declare_toplevel_type(volatile Metadata*) \ \ declare_toplevel_type(DataLayout) \ + declare_toplevel_type(BSMAttributeEntries) \ \ /********/ \ /* Oops */ \ diff --git a/src/hotspot/share/services/cpuTimeUsage.cpp b/src/hotspot/share/services/cpuTimeUsage.cpp index 0c7ecfdb655..27b5e90fbaf 100644 --- a/src/hotspot/share/services/cpuTimeUsage.cpp +++ b/src/hotspot/share/services/cpuTimeUsage.cpp @@ -36,7 +36,6 @@ volatile bool CPUTimeUsage::Error::_has_error = false; static inline jlong thread_cpu_time_or_zero(Thread* thread) { - assert(!Universe::is_shutting_down(), "Should not query during shutdown"); jlong cpu_time = os::thread_cpu_time(thread); if (cpu_time == -1) { CPUTimeUsage::Error::mark_error(); diff --git a/src/hotspot/share/services/diagnosticCommand.cpp b/src/hotspot/share/services/diagnosticCommand.cpp index 5bef650891d..91b23904676 100644 --- a/src/hotspot/share/services/diagnosticCommand.cpp +++ b/src/hotspot/share/services/diagnosticCommand.cpp @@ -22,6 +22,7 @@ * */ +#include "cds/aotMetaspace.hpp" #include "cds/cds_globals.hpp" #include "cds/cdsConfig.hpp" #include "classfile/classLoaderDataGraph.hpp" @@ -165,6 +166,7 @@ void DCmd::register_dcmds(){ #if INCLUDE_CDS DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); #endif // INCLUDE_CDS DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); @@ -986,6 +988,28 @@ void ClassesDCmd::execute(DCmdSource source, TRAPS) { VMThread::execute(&vmop); } +#if INCLUDE_CDS +void AOTEndRecordingDCmd::execute(DCmdSource source, TRAPS) { + if (!CDSConfig::is_dumping_preimage_static_archive()) { + output()->print_cr("AOT.end_recording is unsupported when VM flags -XX:AOTMode=record or -XX:AOTCacheOutput= are missing."); + return; + } + + if (AOTMetaspace::preimage_static_archive_dumped()) { + output()->print_cr("Recording has already ended."); + return; + } + + AOTMetaspace::dump_static_archive(THREAD); + if (!AOTMetaspace::preimage_static_archive_dumped()) { + output()->print_cr("Error: Failed to end recording."); + return; + } + + output()->print_cr("Recording ended successfully."); +} +#endif // INCLUDE_CDS + #if INCLUDE_CDS #define DEFAULT_CDS_ARCHIVE_FILENAME "java_pid%p_.jsa" diff --git a/src/hotspot/share/services/diagnosticCommand.hpp b/src/hotspot/share/services/diagnosticCommand.hpp index 2364b0ce4cd..c41e7bf2e2e 100644 --- a/src/hotspot/share/services/diagnosticCommand.hpp +++ b/src/hotspot/share/services/diagnosticCommand.hpp @@ -325,6 +325,21 @@ class ClassHierarchyDCmd : public DCmdWithParser { virtual void execute(DCmdSource source, TRAPS); }; +#if INCLUDE_CDS +class AOTEndRecordingDCmd : public DCmd { +public: + AOTEndRecordingDCmd(outputStream* output, bool heap) : DCmd(output, heap) { } + static const char* name() { return "AOT.end_recording"; } + static const char* description() { + return "End AOT recording."; + } + static const char* impact() { + return "Medium: Pause time depends on number of loaded classes"; + } + virtual void execute(DCmdSource source, TRAPS); +}; +#endif // INCLUDE_CDS + #if INCLUDE_CDS class DumpSharedArchiveDCmd: public DCmdWithParser { protected: diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index e0cbb60c744..a290602e0be 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -664,6 +664,7 @@ void VMError::report(outputStream* st, bool _verbose) { BEGIN if (MemTracker::enabled() && NmtVirtualMemory_lock != nullptr && + _thread != nullptr && NmtVirtualMemory_lock->owned_by_self()) { // Manually unlock to avoid reentrancy due to mallocs in detailed mode. NmtVirtualMemory_lock->unlock(); @@ -1305,7 +1306,7 @@ void VMError::report(outputStream* st, bool _verbose) { os::print_signal_handlers(st, buf, sizeof(buf)); st->cr(); - STEP_IF("Native Memory Tracking", _verbose) + STEP_IF("Native Memory Tracking", _verbose && _thread != nullptr) MemTracker::error_report(st); st->cr(); diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DHKEM.java b/src/java.base/share/classes/com/sun/crypto/provider/DHKEM.java index b27320ed24b..c7372a4c2c8 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DHKEM.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DHKEM.java @@ -26,26 +26,51 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.Serial; import java.math.BigInteger; -import java.security.*; -import java.security.interfaces.ECKey; +import java.security.AsymmetricKey; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.ProviderException; +import java.security.PublicKey; +import java.security.SecureRandom; import java.security.interfaces.ECPublicKey; -import java.security.interfaces.XECKey; import java.security.interfaces.XECPublicKey; -import java.security.spec.*; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.NamedParameterSpec; +import java.security.spec.XECPrivateKeySpec; +import java.security.spec.XECPublicKeySpec; import java.util.Arrays; import java.util.Objects; -import javax.crypto.*; -import javax.crypto.spec.SecretKeySpec; +import javax.crypto.DecapsulateException; +import javax.crypto.KDF; +import javax.crypto.KEM; +import javax.crypto.KEMSpi; +import javax.crypto.KeyAgreement; +import javax.crypto.SecretKey; import javax.crypto.spec.HKDFParameterSpec; +import javax.crypto.spec.SecretKeySpec; import sun.security.jca.JCAUtil; -import sun.security.util.*; - -import jdk.internal.access.SharedSecrets; +import sun.security.util.ArrayUtil; +import sun.security.util.CurveDB; +import sun.security.util.ECUtil; +import sun.security.util.InternalPrivateKey; +import sun.security.util.NamedCurve; +import sun.security.util.SliceableSecretKey; // Implementing DHKEM defined inside https://www.rfc-editor.org/rfc/rfc9180.html, -// without the AuthEncap and AuthDecap functions public class DHKEM implements KEMSpi { private static final byte[] KEM = new byte[] @@ -65,80 +90,86 @@ public class DHKEM implements KEMSpi { private static final byte[] EMPTY = new byte[0]; private record Handler(Params params, SecureRandom secureRandom, - PrivateKey skR, PublicKey pkR) + PrivateKey skS, PublicKey pkS, // sender keys + PrivateKey skR, PublicKey pkR) // receiver keys implements EncapsulatorSpi, DecapsulatorSpi { @Override public KEM.Encapsulated engineEncapsulate(int from, int to, String algorithm) { - Objects.checkFromToIndex(from, to, params.Nsecret); + Objects.checkFromToIndex(from, to, params.nsecret); Objects.requireNonNull(algorithm, "null algorithm"); KeyPair kpE = params.generateKeyPair(secureRandom); PrivateKey skE = kpE.getPrivate(); PublicKey pkE = kpE.getPublic(); - byte[] pkEm = params.SerializePublicKey(pkE); - byte[] pkRm = params.SerializePublicKey(pkR); - byte[] kem_context = concat(pkEm, pkRm); - byte[] key = null; + byte[] pkEm = params.serializePublicKey(pkE); + byte[] pkRm = params.serializePublicKey(pkR); try { - byte[] dh = params.DH(skE, pkR); - key = params.ExtractAndExpand(dh, kem_context); - return new KEM.Encapsulated( - new SecretKeySpec(key, from, to - from, algorithm), - pkEm, null); + SecretKey key; + if (skS == null) { + byte[] kem_context = concat(pkEm, pkRm); + key = params.deriveKey(algorithm, from, to, kem_context, + params.dh(skE, pkR)); + } else { + byte[] pkSm = params.serializePublicKey(pkS); + byte[] kem_context = concat(pkEm, pkRm, pkSm); + key = params.deriveKey(algorithm, from, to, kem_context, + params.dh(skE, pkR), params.dh(skS, pkR)); + } + return new KEM.Encapsulated(key, pkEm, null); + } catch (UnsupportedOperationException e) { + throw e; } catch (Exception e) { throw new ProviderException("internal error", e); - } finally { - // `key` has been cloned into the `SecretKeySpec` within the - // returned `KEM.Encapsulated`, so it can now be cleared. - if (key != null) { - Arrays.fill(key, (byte)0); - } } } @Override public SecretKey engineDecapsulate(byte[] encapsulation, int from, int to, String algorithm) throws DecapsulateException { - Objects.checkFromToIndex(from, to, params.Nsecret); + Objects.checkFromToIndex(from, to, params.nsecret); Objects.requireNonNull(algorithm, "null algorithm"); Objects.requireNonNull(encapsulation, "null encapsulation"); - if (encapsulation.length != params.Npk) { + if (encapsulation.length != params.npk) { throw new DecapsulateException("incorrect encapsulation size"); } - byte[] key = null; try { - PublicKey pkE = params.DeserializePublicKey(encapsulation); - byte[] dh = params.DH(skR, pkE); - byte[] pkRm = params.SerializePublicKey(pkR); - byte[] kem_context = concat(encapsulation, pkRm); - key = params.ExtractAndExpand(dh, kem_context); - return new SecretKeySpec(key, from, to - from, algorithm); + PublicKey pkE = params.deserializePublicKey(encapsulation); + byte[] pkRm = params.serializePublicKey(pkR); + if (pkS == null) { + byte[] kem_context = concat(encapsulation, pkRm); + return params.deriveKey(algorithm, from, to, kem_context, + params.dh(skR, pkE)); + } else { + byte[] pkSm = params.serializePublicKey(pkS); + byte[] kem_context = concat(encapsulation, pkRm, pkSm); + return params.deriveKey(algorithm, from, to, kem_context, + params.dh(skR, pkE), params.dh(skR, pkS)); + } + } catch (UnsupportedOperationException e) { + throw e; } catch (IOException | InvalidKeyException e) { throw new DecapsulateException("Cannot decapsulate", e); } catch (Exception e) { throw new ProviderException("internal error", e); - } finally { - if (key != null) { - Arrays.fill(key, (byte)0); - } } } @Override public int engineSecretSize() { - return params.Nsecret; + return params.nsecret; } @Override public int engineEncapsulationSize() { - return params.Npk; + return params.npk; } } // Not really a random. For KAT test only. It generates key pair from ikm. public static class RFC9180DeriveKeyPairSR extends SecureRandom { - static final long serialVersionUID = 0L; + @Serial + private static final long serialVersionUID = 0L; private final byte[] ikm; @@ -147,7 +178,7 @@ public RFC9180DeriveKeyPairSR(byte[] ikm) { this.ikm = ikm; } - public KeyPair derive(Params params) { + private KeyPair derive(Params params) { try { return params.deriveKeyPair(ikm); } catch (Exception e) { @@ -183,9 +214,9 @@ private enum Params { ; private final int kem_id; - private final int Nsecret; - private final int Nsk; - private final int Npk; + private final int nsecret; + private final int nsk; + private final int npk; private final String kaAlgorithm; private final String keyAlgorithm; private final AlgorithmParameterSpec spec; @@ -193,18 +224,18 @@ private enum Params { private final byte[] suiteId; - Params(int kem_id, int Nsecret, int Nsk, int Npk, + Params(int kem_id, int nsecret, int nsk, int npk, String kaAlgorithm, String keyAlgorithm, AlgorithmParameterSpec spec, String hkdfAlgorithm) { this.kem_id = kem_id; this.spec = spec; - this.Nsecret = Nsecret; - this.Nsk = Nsk; - this.Npk = Npk; + this.nsecret = nsecret; + this.nsk = nsk; + this.npk = npk; this.kaAlgorithm = kaAlgorithm; this.keyAlgorithm = keyAlgorithm; this.hkdfAlgorithm = hkdfAlgorithm; - suiteId = concat(KEM, I2OSP(kem_id, 2)); + suiteId = concat(KEM, i2OSP(kem_id, 2)); } private boolean isEC() { @@ -224,18 +255,18 @@ private KeyPair generateKeyPair(SecureRandom sr) { } } - private byte[] SerializePublicKey(PublicKey k) { + private byte[] serializePublicKey(PublicKey k) { if (isEC()) { ECPoint w = ((ECPublicKey) k).getW(); return ECUtil.encodePoint(w, ((NamedCurve) spec).getCurve()); } else { byte[] uArray = ((XECPublicKey) k).getU().toByteArray(); ArrayUtil.reverse(uArray); - return Arrays.copyOf(uArray, Npk); + return Arrays.copyOf(uArray, npk); } } - private PublicKey DeserializePublicKey(byte[] data) + private PublicKey deserializePublicKey(byte[] data) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { KeySpec keySpec; if (isEC()) { @@ -251,29 +282,59 @@ private PublicKey DeserializePublicKey(byte[] data) return KeyFactory.getInstance(keyAlgorithm).generatePublic(keySpec); } - private byte[] DH(PrivateKey skE, PublicKey pkR) + private SecretKey dh(PrivateKey skE, PublicKey pkR) throws NoSuchAlgorithmException, InvalidKeyException { KeyAgreement ka = KeyAgreement.getInstance(kaAlgorithm); ka.init(skE); ka.doPhase(pkR, true); - return ka.generateSecret(); + return ka.generateSecret("Generic"); } - private byte[] ExtractAndExpand(byte[] dh, byte[] kem_context) - throws NoSuchAlgorithmException, InvalidKeyException { - KDF hkdf = KDF.getInstance(hkdfAlgorithm); - SecretKey eae_prk = LabeledExtract(hkdf, suiteId, EAE_PRK, dh); - try { - return LabeledExpand(hkdf, suiteId, eae_prk, SHARED_SECRET, - kem_context, Nsecret); - } finally { - if (eae_prk instanceof SecretKeySpec s) { - SharedSecrets.getJavaxCryptoSpecAccess() - .clearSecretKeySpec(s); + // The final shared secret derivation of either the encapsulator + // or the decapsulator. The key slicing is implemented inside. + // Throws UOE if a slice of the key cannot be found. + private SecretKey deriveKey(String alg, int from, int to, + byte[] kem_context, SecretKey... dhs) + throws NoSuchAlgorithmException { + if (from == 0 && to == nsecret) { + return extractAndExpand(kem_context, alg, dhs); + } else { + // First get shared secrets in "Generic" and then get a slice + // of it in the requested algorithm. + var fullKey = extractAndExpand(kem_context, "Generic", dhs); + if ("RAW".equalsIgnoreCase(fullKey.getFormat())) { + byte[] km = fullKey.getEncoded(); + if (km == null) { + // Should not happen if format is "RAW" + throw new UnsupportedOperationException("Key extract failed"); + } else { + try { + return new SecretKeySpec(km, from, to - from, alg); + } finally { + Arrays.fill(km, (byte)0); + } + } + } else if (fullKey instanceof SliceableSecretKey ssk) { + return ssk.slice(alg, from, to); + } else { + throw new UnsupportedOperationException("Cannot extract key"); } } } + private SecretKey extractAndExpand(byte[] kem_context, String alg, SecretKey... dhs) + throws NoSuchAlgorithmException { + var kdf = KDF.getInstance(hkdfAlgorithm); + var builder = labeledExtract(suiteId, EAE_PRK); + for (var dh : dhs) builder.addIKM(dh); + try { + return kdf.deriveKey(alg, + labeledExpand(builder, suiteId, SHARED_SECRET, kem_context, nsecret)); + } catch (InvalidAlgorithmParameterException e) { + throw new ProviderException(e); + } + } + private PublicKey getPublicKey(PrivateKey sk) throws InvalidKeyException { if (!(sk instanceof InternalPrivateKey)) { @@ -298,45 +359,37 @@ private PublicKey getPublicKey(PrivateKey sk) // For KAT tests only. See RFC9180DeriveKeyPairSR. public KeyPair deriveKeyPair(byte[] ikm) throws Exception { - KDF hkdf = KDF.getInstance(hkdfAlgorithm); - SecretKey dkp_prk = LabeledExtract(hkdf, suiteId, DKP_PRK, ikm); - try { - if (isEC()) { - NamedCurve curve = (NamedCurve) spec; - BigInteger sk = BigInteger.ZERO; - int counter = 0; - while (sk.signum() == 0 || - sk.compareTo(curve.getOrder()) >= 0) { - if (counter > 255) { - throw new RuntimeException(); - } - byte[] bytes = LabeledExpand(hkdf, suiteId, dkp_prk, - CANDIDATE, I2OSP(counter, 1), Nsk); - // bitmask is defined to be 0xFF for P-256 and P-384, - // and 0x01 for P-521 - if (this == Params.P521) { - bytes[0] = (byte) (bytes[0] & 0x01); - } - sk = new BigInteger(1, (bytes)); - counter = counter + 1; + var kdf = KDF.getInstance(hkdfAlgorithm); + var builder = labeledExtract(suiteId, DKP_PRK).addIKM(ikm); + if (isEC()) { + NamedCurve curve = (NamedCurve) spec; + BigInteger sk = BigInteger.ZERO; + int counter = 0; + while (sk.signum() == 0 || sk.compareTo(curve.getOrder()) >= 0) { + if (counter > 255) { + // So unlucky and should not happen + throw new ProviderException("DeriveKeyPairError"); } - PrivateKey k = DeserializePrivateKey(sk.toByteArray()); - return new KeyPair(getPublicKey(k), k); - } else { - byte[] sk = LabeledExpand(hkdf, suiteId, dkp_prk, SK, EMPTY, - Nsk); - PrivateKey k = DeserializePrivateKey(sk); - return new KeyPair(getPublicKey(k), k); - } - } finally { - if (dkp_prk instanceof SecretKeySpec s) { - SharedSecrets.getJavaxCryptoSpecAccess() - .clearSecretKeySpec(s); + byte[] bytes = kdf.deriveData(labeledExpand(builder, + suiteId, CANDIDATE, i2OSP(counter, 1), nsk)); + // bitmask is defined to be 0xFF for P-256 and P-384, and 0x01 for P-521 + if (this == Params.P521) { + bytes[0] = (byte) (bytes[0] & 0x01); + } + sk = new BigInteger(1, (bytes)); + counter = counter + 1; } + PrivateKey k = deserializePrivateKey(sk.toByteArray()); + return new KeyPair(getPublicKey(k), k); + } else { + byte[] sk = kdf.deriveData(labeledExpand(builder, + suiteId, SK, EMPTY, nsk)); + PrivateKey k = deserializePrivateKey(sk); + return new KeyPair(getPublicKey(k), k); } } - private PrivateKey DeserializePrivateKey(byte[] data) throws Exception { + private PrivateKey deserializePrivateKey(byte[] data) throws Exception { KeySpec keySpec = isEC() ? new ECPrivateKeySpec(new BigInteger(1, (data)), (NamedCurve) spec) : new XECPrivateKeySpec(spec, data); @@ -359,7 +412,22 @@ public EncapsulatorSpi engineNewEncapsulator( throw new InvalidAlgorithmParameterException("no spec needed"); } Params params = paramsFromKey(pk); - return new Handler(params, getSecureRandom(secureRandom), null, pk); + return new Handler(params, getSecureRandom(secureRandom), null, null, null, pk); + } + + // AuthEncap is not public KEM API + public EncapsulatorSpi engineNewAuthEncapsulator(PublicKey pkR, PrivateKey skS, + AlgorithmParameterSpec spec, SecureRandom secureRandom) + throws InvalidAlgorithmParameterException, InvalidKeyException { + if (pkR == null || skS == null) { + throw new InvalidKeyException("input key is null"); + } + if (spec != null) { + throw new InvalidAlgorithmParameterException("no spec needed"); + } + Params params = paramsFromKey(pkR); + return new Handler(params, getSecureRandom(secureRandom), + skS, params.getPublicKey(skS), null, pkR); } @Override @@ -372,20 +440,34 @@ public DecapsulatorSpi engineNewDecapsulator(PrivateKey sk, AlgorithmParameterSp throw new InvalidAlgorithmParameterException("no spec needed"); } Params params = paramsFromKey(sk); - return new Handler(params, null, sk, params.getPublicKey(sk)); + return new Handler(params, null, null, null, sk, params.getPublicKey(sk)); + } + + // AuthDecap is not public KEM API + public DecapsulatorSpi engineNewAuthDecapsulator( + PrivateKey skR, PublicKey pkS, AlgorithmParameterSpec spec) + throws InvalidAlgorithmParameterException, InvalidKeyException { + if (skR == null || pkS == null) { + throw new InvalidKeyException("input key is null"); + } + if (spec != null) { + throw new InvalidAlgorithmParameterException("no spec needed"); + } + Params params = paramsFromKey(skR); + return new Handler(params, null, null, pkS, skR, params.getPublicKey(skR)); } - private Params paramsFromKey(Key k) throws InvalidKeyException { - if (k instanceof ECKey eckey) { - if (ECUtil.equals(eckey.getParams(), CurveDB.P_256)) { + private Params paramsFromKey(AsymmetricKey k) throws InvalidKeyException { + var p = k.getParams(); + if (p instanceof ECParameterSpec ecp) { + if (ECUtil.equals(ecp, CurveDB.P_256)) { return Params.P256; - } else if (ECUtil.equals(eckey.getParams(), CurveDB.P_384)) { + } else if (ECUtil.equals(ecp, CurveDB.P_384)) { return Params.P384; - } else if (ECUtil.equals(eckey.getParams(), CurveDB.P_521)) { + } else if (ECUtil.equals(ecp, CurveDB.P_521)) { return Params.P521; } - } else if (k instanceof XECKey xkey - && xkey.getParams() instanceof NamedParameterSpec ns) { + } else if (p instanceof NamedParameterSpec ns) { if (ns.getName().equalsIgnoreCase("X25519")) { return Params.X25519; } else if (ns.getName().equalsIgnoreCase("X448")) { @@ -401,8 +483,11 @@ private static byte[] concat(byte[]... inputs) { return o.toByteArray(); } - private static byte[] I2OSP(int n, int w) { - assert n < 256; + // I2OSP(n, w) as defined in RFC 9180 Section 3. + // In DHKEM and HPKE, number is always <65536 + // and converted to at most 2 bytes. + public static byte[] i2OSP(int n, int w) { + assert n < 65536; assert w == 1 || w == 2; if (w == 1) { return new byte[] { (byte) n }; @@ -411,32 +496,32 @@ private static byte[] I2OSP(int n, int w) { } } - private static SecretKey LabeledExtract(KDF hkdf, byte[] suite_id, - byte[] label, byte[] ikm) throws InvalidKeyException { - SecretKeySpec s = new SecretKeySpec(concat(HPKE_V1, suite_id, label, - ikm), "IKM"); - try { - HKDFParameterSpec spec = - HKDFParameterSpec.ofExtract().addIKM(s).extractOnly(); - return hkdf.deriveKey("Generic", spec); - } catch (InvalidAlgorithmParameterException | - NoSuchAlgorithmException e) { - throw new InvalidKeyException(e.getMessage(), e); - } finally { - SharedSecrets.getJavaxCryptoSpecAccess().clearSecretKeySpec(s); - } + // Create a LabeledExtract builder with labels. + // You can add more IKM and salt into the result. + public static HKDFParameterSpec.Builder labeledExtract( + byte[] suiteId, byte[] label) { + return HKDFParameterSpec.ofExtract() + .addIKM(HPKE_V1).addIKM(suiteId).addIKM(label); } - private static byte[] LabeledExpand(KDF hkdf, byte[] suite_id, - SecretKey prk, byte[] label, byte[] info, int L) - throws InvalidKeyException { - byte[] labeled_info = concat(I2OSP(L, 2), HPKE_V1, suite_id, label, - info); - try { - return hkdf.deriveData(HKDFParameterSpec.expandOnly( - prk, labeled_info, L)); - } catch (InvalidAlgorithmParameterException iape) { - throw new InvalidKeyException(iape.getMessage(), iape); - } + // Create a labeled info from info and labels + private static byte[] labeledInfo( + byte[] suiteId, byte[] label, byte[] info, int length) { + return concat(i2OSP(length, 2), HPKE_V1, suiteId, label, info); + } + + // LabeledExpand from a builder + public static HKDFParameterSpec labeledExpand( + HKDFParameterSpec.Builder builder, + byte[] suiteId, byte[] label, byte[] info, int length) { + return builder.thenExpand( + labeledInfo(suiteId, label, info, length), length); + } + + // LabeledExpand from a prk + public static HKDFParameterSpec labeledExpand( + SecretKey prk, byte[] suiteId, byte[] label, byte[] info, int length) { + return HKDFParameterSpec.expandOnly( + prk, labeledInfo(suiteId, label, info, length), length); } } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/HPKE.java b/src/java.base/share/classes/com/sun/crypto/provider/HPKE.java new file mode 100644 index 00000000000..eee5f59cc75 --- /dev/null +++ b/src/java.base/share/classes/com/sun/crypto/provider/HPKE.java @@ -0,0 +1,588 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.crypto.provider; + +import sun.security.util.CurveDB; +import sun.security.util.ECUtil; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.CipherSpi; +import javax.crypto.DecapsulateException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KDF; +import javax.crypto.KEM; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.HPKEParameterSpec; +import javax.crypto.spec.IvParameterSpec; +import java.io.ByteArrayOutputStream; +import java.nio.ByteBuffer; +import java.security.AlgorithmParameters; +import java.security.AsymmetricKey; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.ProviderException; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.NamedParameterSpec; +import java.util.Arrays; + +public class HPKE extends CipherSpi { + + private static final byte[] HPKE = new byte[] + {'H', 'P', 'K', 'E'}; + private static final byte[] SEC = new byte[] + {'s', 'e', 'c'}; + private static final byte[] PSK_ID_HASH = new byte[] + {'p', 's', 'k', '_', 'i', 'd', '_', 'h', 'a', 's', 'h'}; + private static final byte[] INFO_HASH = new byte[] + {'i', 'n', 'f', 'o', '_', 'h', 'a', 's', 'h'}; + private static final byte[] SECRET = new byte[] + {'s', 'e', 'c', 'r', 'e', 't'}; + private static final byte[] EXP = new byte[] + {'e', 'x', 'p'}; + private static final byte[] KEY = new byte[] + {'k', 'e', 'y'}; + private static final byte[] BASE_NONCE = new byte[] + {'b', 'a', 's', 'e', '_', 'n', 'o', 'n', 'c', 'e'}; + + private static final int BEGIN = 1; + private static final int EXPORT_ONLY = 2; // init done with aead_id == 65535 + private static final int ENCRYPT_AND_EXPORT = 3; // int done with AEAD + private static final int AFTER_FINAL = 4; // after doFinal, need reinit internal cipher + + private int state = BEGIN; + private Impl impl; + + @Override + protected void engineSetMode(String mode) throws NoSuchAlgorithmException { + throw new NoSuchAlgorithmException(mode); + } + + @Override + protected void engineSetPadding(String padding) throws NoSuchPaddingException { + throw new NoSuchPaddingException(padding); + } + + @Override + protected int engineGetBlockSize() { + if (state == ENCRYPT_AND_EXPORT || state == AFTER_FINAL) { + return impl.aead.cipher.getBlockSize(); + } else { + return 0; + } + } + + @Override + protected int engineGetOutputSize(int inputLen) { + if (state == ENCRYPT_AND_EXPORT || state == AFTER_FINAL) { + return impl.aead.cipher.getOutputSize(inputLen); + } else { + return 0; + } + } + + @Override + protected byte[] engineGetIV() { + return (state == BEGIN || impl.kemEncaps == null) + ? null : impl.kemEncaps.clone(); + } + + @Override + protected AlgorithmParameters engineGetParameters() { + return null; + } + + @Override + protected void engineInit(int opmode, Key key, SecureRandom random) + throws InvalidKeyException { + throw new InvalidKeyException("HPKEParameterSpec must be provided"); + } + + @Override + protected void engineInit(int opmode, Key key, + AlgorithmParameterSpec params, SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + impl = new Impl(opmode); + if (!(key instanceof AsymmetricKey ak)) { + throw new InvalidKeyException("Not an asymmetric key"); + } + if (params == null) { + throw new InvalidAlgorithmParameterException( + "HPKEParameterSpec must be provided"); + } else if (params instanceof HPKEParameterSpec hps) { + impl.init(ak, hps, random); + } else { + throw new InvalidAlgorithmParameterException( + "Unsupported params type: " + params.getClass()); + } + if (impl.hasEncrypt()) { + impl.aead.start(impl.opmode, impl.context.k, impl.context.computeNonce()); + state = ENCRYPT_AND_EXPORT; + } else { + state = EXPORT_ONLY; + } + } + + @Override + protected void engineInit(int opmode, Key key, + AlgorithmParameters params, SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + throw new InvalidKeyException("HPKEParameterSpec must be provided"); + } + + // state is ENCRYPT_AND_EXPORT after this call succeeds + private void maybeReinitInternalCipher() { + if (state == BEGIN) { + throw new IllegalStateException("Illegal state: " + state); + } + if (state == EXPORT_ONLY) { + throw new UnsupportedOperationException(); + } + if (state == AFTER_FINAL) { + impl.aead.start(impl.opmode, impl.context.k, impl.context.computeNonce()); + state = ENCRYPT_AND_EXPORT; + } + } + + @Override + protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { + maybeReinitInternalCipher(); + return impl.aead.cipher.update(input, inputOffset, inputLen); + } + + @Override + protected int engineUpdate(byte[] input, int inputOffset, int inputLen, + byte[] output, int outputOffset) throws ShortBufferException { + maybeReinitInternalCipher(); + return impl.aead.cipher.update( + input, inputOffset, inputLen, output, outputOffset); + } + + @Override + protected void engineUpdateAAD(byte[] src, int offset, int len) { + maybeReinitInternalCipher(); + impl.aead.cipher.updateAAD(src, offset, len); + } + + @Override + protected void engineUpdateAAD(ByteBuffer src) { + maybeReinitInternalCipher(); + impl.aead.cipher.updateAAD(src); + } + + @Override + protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) + throws IllegalBlockSizeException, BadPaddingException { + maybeReinitInternalCipher(); + impl.context.IncrementSeq(); + state = AFTER_FINAL; + if (input == null) { // a bug in doFinal(null, ?, ?) + return impl.aead.cipher.doFinal(); + } else { + return impl.aead.cipher.doFinal(input, inputOffset, inputLen); + } + } + + @Override + protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, + byte[] output, int outputOffset) throws ShortBufferException, + IllegalBlockSizeException, BadPaddingException { + maybeReinitInternalCipher(); + impl.context.IncrementSeq(); + state = AFTER_FINAL; + return impl.aead.cipher.doFinal( + input, inputOffset, inputLen, output, outputOffset); + } + + //@Override + protected SecretKey engineExportKey(String algorithm, byte[] context, int length) { + if (state == BEGIN) { + throw new IllegalStateException("State: " + state); + } else { + return impl.context.exportKey(algorithm, context, length); + } + } + + //@Override + protected byte[] engineExportData(byte[] context, int length) { + if (state == BEGIN) { + throw new IllegalStateException("State: " + state); + } else { + return impl.context.exportData(context, length); + } + } + + private static class AEAD { + final Cipher cipher; + final int nk, nn, nt; + final int id; + public AEAD(int id) throws InvalidAlgorithmParameterException { + this.id = id; + try { + switch (id) { + case HPKEParameterSpec.AEAD_AES_128_GCM -> { + cipher = Cipher.getInstance("AES/GCM/NoPadding"); + nk = 16; + } + case HPKEParameterSpec.AEAD_AES_256_GCM -> { + cipher = Cipher.getInstance("AES/GCM/NoPadding"); + nk = 32; + } + case HPKEParameterSpec.AEAD_CHACHA20_POLY1305 -> { + cipher = Cipher.getInstance("ChaCha20-Poly1305"); + nk = 32; + } + case HPKEParameterSpec.EXPORT_ONLY -> { + cipher = null; + nk = -1; + } + default -> throw new InvalidAlgorithmParameterException( + "Unknown aead_id: " + id); + } + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new ProviderException("Internal error", e); + } + nn = 12; nt = 16; + } + + void start(int opmode, SecretKey key, byte[] nonce) { + try { + if (id == HPKEParameterSpec.AEAD_CHACHA20_POLY1305) { + cipher.init(opmode, key, new IvParameterSpec(nonce)); + } else { + cipher.init(opmode, key, new GCMParameterSpec(nt * 8, nonce)); + } + } catch (InvalidAlgorithmParameterException | InvalidKeyException e) { + throw new ProviderException("Internal error", e); + } + } + } + + private static class Impl { + + final int opmode; + + HPKEParameterSpec params; + Context context; + AEAD aead; + + byte[] suite_id; + String kdfAlg; + int kdfNh; + + // only used on sender side + byte[] kemEncaps; + + class Context { + final SecretKey k; // null if only export + final byte[] base_nonce; + final SecretKey exporter_secret; + + byte[] seq = new byte[aead.nn]; + + public Context(SecretKey sk, byte[] base_nonce, + SecretKey exporter_secret) { + this.k = sk; + this.base_nonce = base_nonce; + this.exporter_secret = exporter_secret; + } + + SecretKey exportKey(String algorithm, byte[] exporter_context, int length) { + if (exporter_context == null) { + throw new IllegalArgumentException("Null exporter_context"); + } + try { + var kdf = KDF.getInstance(kdfAlg); + return kdf.deriveKey(algorithm, DHKEM.labeledExpand( + exporter_secret, suite_id, SEC, exporter_context, length)); + } catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException e) { + // algorithm not accepted by HKDF, length too big or too small + throw new IllegalArgumentException("Invalid input", e); + } + } + + byte[] exportData(byte[] exporter_context, int length) { + if (exporter_context == null) { + throw new IllegalArgumentException("Null exporter_context"); + } + try { + var kdf = KDF.getInstance(kdfAlg); + return kdf.deriveData(DHKEM.labeledExpand( + exporter_secret, suite_id, SEC, exporter_context, length)); + } catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException e) { + // algorithm not accepted by HKDF, length too big or too small + throw new IllegalArgumentException("Invalid input", e); + } + } + + private byte[] computeNonce() { + var result = new byte[aead.nn]; + for (var i = 0; i < result.length; i++) { + result[i] = (byte)(seq[i] ^ base_nonce[i]); + } + return result; + } + + private void IncrementSeq() { + for (var i = seq.length - 1; i >= 0; i--) { + if ((seq[i] & 0xff) == 0xff) { + seq[i] = 0; + } else { + seq[i]++; + return; + } + } + // seq >= (1 << (8*aead.Nn)) - 1 when this method is called + throw new ProviderException("MessageLimitReachedError"); + } + } + + public Impl(int opmode) { + this.opmode = opmode; + } + + public boolean hasEncrypt() { + return params.aead_id() != 65535; + } + + // Section 7.2.1 of RFC 9180 has restrictions on size of psk, psk_id, + // info, and exporter_context (~2^61 for HMAC-SHA256 and ~2^125 for + // HMAC-SHA384 and HMAC-SHA512). This method does not pose any + // restrictions. + public void init(AsymmetricKey key, HPKEParameterSpec p, SecureRandom rand) + throws InvalidKeyException, InvalidAlgorithmParameterException { + if (opmode != Cipher.ENCRYPT_MODE && opmode != Cipher.DECRYPT_MODE) { + throw new UnsupportedOperationException( + "Can only be used for encryption and decryption"); + } + setParams(p); + SecretKey shared_secret; + if (opmode == Cipher.ENCRYPT_MODE) { + if (!(key instanceof PublicKey pk)) { + throw new InvalidKeyException( + "Cannot encrypt with private key"); + } + if (p.encapsulation() != null) { + throw new InvalidAlgorithmParameterException( + "Must not provide key encapsulation message on sender side"); + } + checkMatch(false, pk, params.kem_id()); + KEM.Encapsulated enc; + switch (p.authKey()) { + case null -> { + var e = kem().newEncapsulator(pk, rand); + enc = e.encapsulate(); + } + case PrivateKey skS -> { + checkMatch(true, skS, params.kem_id()); + // AuthEncap not public KEM API but it's internally supported + var e = new DHKEM().engineNewAuthEncapsulator(pk, skS, null, rand); + enc = e.engineEncapsulate(0, e.engineSecretSize(), "Generic"); + } + default -> throw new InvalidAlgorithmParameterException( + "Cannot auth with public key"); + } + kemEncaps = enc.encapsulation(); + shared_secret = enc.key(); + } else { + if (!(key instanceof PrivateKey sk)) { + throw new InvalidKeyException("Cannot decrypt with public key"); + } + checkMatch(false, sk, params.kem_id()); + try { + var encap = p.encapsulation(); + if (encap == null) { + throw new InvalidAlgorithmParameterException( + "Must provide key encapsulation message on recipient side"); + } + switch (p.authKey()) { + case null -> { + var d = kem().newDecapsulator(sk); + shared_secret = d.decapsulate(encap); + } + case PublicKey pkS -> { + checkMatch(true, pkS, params.kem_id()); + // AuthDecap not public KEM API but it's internally supported + var d = new DHKEM().engineNewAuthDecapsulator(sk, pkS, null); + shared_secret = d.engineDecapsulate( + encap, 0, d.engineSecretSize(), "Generic"); + } + default -> throw new InvalidAlgorithmParameterException( + "Cannot auth with private key"); + } + } catch (DecapsulateException e) { + throw new InvalidAlgorithmParameterException(e); + } + } + + var usePSK = usePSK(params.psk()); + int mode = params.authKey() == null ? (usePSK ? 1 : 0) : (usePSK ? 3 : 2); + context = keySchedule(mode, shared_secret, + params.info(), + params.psk(), + params.psk_id()); + } + + private static void checkMatch(boolean inSpec, AsymmetricKey k, int kem_id) + throws InvalidKeyException, InvalidAlgorithmParameterException { + var p = k.getParams(); + switch (p) { + case ECParameterSpec ecp -> { + if ((!ECUtil.equals(ecp, CurveDB.P_256) + || kem_id != HPKEParameterSpec.KEM_DHKEM_P_256_HKDF_SHA256) + && (!ECUtil.equals(ecp, CurveDB.P_384) + || kem_id != HPKEParameterSpec.KEM_DHKEM_P_384_HKDF_SHA384) + && (!ECUtil.equals(ecp, CurveDB.P_521) + || kem_id != HPKEParameterSpec.KEM_DHKEM_P_521_HKDF_SHA512)) { + var name = ECUtil.getCurveName(ecp); + throw new InvalidAlgorithmParameterException( + name + " does not match " + kem_id); + } + } + case NamedParameterSpec ns -> { + var name = ns.getName(); + if ((!name.equalsIgnoreCase("x25519") + || kem_id != HPKEParameterSpec.KEM_DHKEM_X25519_HKDF_SHA256) + && (!name.equalsIgnoreCase("x448") + || kem_id != HPKEParameterSpec.KEM_DHKEM_X448_HKDF_SHA512)) { + throw new InvalidAlgorithmParameterException( + name + " does not match " + kem_id); + } + } + case null, default -> { + var msg = k.getClass() + " does not match " + kem_id; + if (inSpec) { + throw new InvalidAlgorithmParameterException(msg); + } else { + throw new InvalidKeyException(msg); + } + } + } + } + + private KEM kem() { + try { + return KEM.getInstance("DHKEM"); + } catch (NoSuchAlgorithmException e) { + throw new ProviderException("Internal error", e); + } + } + + private void setParams(HPKEParameterSpec p) + throws InvalidAlgorithmParameterException { + params = p; + suite_id = concat( + HPKE, + DHKEM.i2OSP(params.kem_id(), 2), + DHKEM.i2OSP(params.kdf_id(), 2), + DHKEM.i2OSP(params.aead_id(), 2)); + switch (params.kdf_id()) { + case HPKEParameterSpec.KDF_HKDF_SHA256 -> { + kdfAlg = "HKDF-SHA256"; + kdfNh = 32; + } + case HPKEParameterSpec.KDF_HKDF_SHA384 -> { + kdfAlg = "HKDF-SHA384"; + kdfNh = 48; + } + case HPKEParameterSpec.KDF_HKDF_SHA512 -> { + kdfAlg = "HKDF-SHA512"; + kdfNh = 64; + } + default -> throw new InvalidAlgorithmParameterException( + "Unsupported kdf_id: " + params.kdf_id()); + } + aead = new AEAD(params.aead_id()); + } + + private Context keySchedule(int mode, + SecretKey shared_secret, + byte[] info, + SecretKey psk, + byte[] psk_id) { + try { + var psk_id_hash_x = DHKEM.labeledExtract(suite_id, PSK_ID_HASH) + .addIKM(psk_id).extractOnly(); + var info_hash_x = DHKEM.labeledExtract(suite_id, INFO_HASH) + .addIKM(info).extractOnly(); + + // deriveData must and can be called because all info to + // thw builder are just byte arrays. Any KDF impl can handle this. + var kdf = KDF.getInstance(kdfAlg); + var key_schedule_context = concat(new byte[]{(byte) mode}, + kdf.deriveData(psk_id_hash_x), + kdf.deriveData(info_hash_x)); + + var secret_x_builder = DHKEM.labeledExtract(suite_id, SECRET); + if (psk != null) { + secret_x_builder.addIKM(psk); + } + secret_x_builder.addSalt(shared_secret); + var secret_x = kdf.deriveKey("Generic", secret_x_builder.extractOnly()); + + // A new KDF object must be created because secret_x_builder + // might contain provider-specific keys which the previous + // KDF (provider already chosen) cannot handle. + kdf = KDF.getInstance(kdfAlg); + var exporter_secret = kdf.deriveKey("Generic", DHKEM.labeledExpand( + secret_x, suite_id, EXP, key_schedule_context, kdfNh)); + + if (hasEncrypt()) { + // ChaCha20-Poly1305 does not care about algorithm name + var key = kdf.deriveKey("AES", DHKEM.labeledExpand(secret_x, + suite_id, KEY, key_schedule_context, aead.nk)); + // deriveData must be called because we need to increment nonce + var base_nonce = kdf.deriveData(DHKEM.labeledExpand(secret_x, + suite_id, BASE_NONCE, key_schedule_context, aead.nn)); + return new Context(key, base_nonce, exporter_secret); + } else { + return new Context(null, null, exporter_secret); + } + } catch (InvalidAlgorithmParameterException + | NoSuchAlgorithmException | UnsupportedOperationException e) { + throw new ProviderException("Internal error", e); + } + } + } + + private static boolean usePSK(SecretKey psk) { + return psk != null; + } + + private static byte[] concat(byte[]... inputs) { + var o = new ByteArrayOutputStream(); + Arrays.stream(inputs).forEach(o::writeBytes); + return o.toByteArray(); + } +} diff --git a/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java b/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java index ec8e0f3757d..ad98653b9c2 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java @@ -661,6 +661,10 @@ public void engineStore(OutputStream stream, char[] password) dos.close(); } } + + if (debug != null) { + emitWeakKeyStoreWarning(); + } } } @@ -862,6 +866,10 @@ public void engineLoad(InputStream stream, char[] password) secretKeyCount); } + if (debug != null) { + emitWeakKeyStoreWarning(); + } + /* * If a password has been provided, we check the keyed digest * at the end. If this check fails, the store has been tampered @@ -978,4 +986,12 @@ public DeserializationChecker(int fullLength) { return Status.UNDECIDED; } } + + private void emitWeakKeyStoreWarning() { + debug.println("WARNING: JCEKS uses outdated cryptographic " + + "algorithms and will be removed in a future " + + "release. Migrate to PKCS12 using:"); + debug.println("keytool -importkeystore -srckeystore " + + "-destkeystore -deststoretype pkcs12"); + } } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java b/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java index 22d5f17c6e0..4b38bd55809 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java @@ -371,6 +371,8 @@ void putEntries() { ps("Cipher", "PBEWithHmacSHA512/256AndAES_256", "com.sun.crypto.provider.PBES2Core$HmacSHA512_256AndAES_256"); + ps("Cipher", "HPKE", "com.sun.crypto.provider.HPKE"); + /* * Key(pair) Generator engines */ diff --git a/src/java.base/share/classes/java/lang/Character.java b/src/java.base/share/classes/java/lang/Character.java index d866202909c..b71849eaee7 100644 --- a/src/java.base/share/classes/java/lang/Character.java +++ b/src/java.base/share/classes/java/lang/Character.java @@ -743,7 +743,8 @@ public final String toString() { */ public static final class UnicodeBlock extends Subset { /** - * NUM_ENTITIES should match the total number of UnicodeBlocks. + * NUM_ENTITIES should match the total number of UnicodeBlock identifier + * names plus their aliases. * It should be adjusted whenever the Unicode Character Database * is upgraded. */ diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index cfd2fc82235..eab1993a2b4 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -43,6 +43,7 @@ import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; import java.lang.reflect.GenericDeclaration; +import java.lang.reflect.GenericSignatureFormatError; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; import java.lang.reflect.Method; @@ -159,6 +160,10 @@ * other members are the classes and interfaces whose declarations are * enclosed within the top-level class declaration. * + *

Unless otherwise specified, methods in this class throw a + * {@link NullPointerException} when they are called with {@code null} + * or an array that contains {@code null} as an argument. + * *

Hidden Classes

* A class or interface created by the invocation of * {@link java.lang.invoke.MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOption...) @@ -529,7 +534,8 @@ private static Class forName(String className, Class caller) * (which implies linking). See Section {@jls * 12.4} of The Java Language * Specification. - * @param loader class loader from which the class must be loaded + * @param loader class loader from which the class must be loaded, + * may be {@code null} * @return class object representing the desired class * * @throws LinkageError if the linkage fails @@ -588,8 +594,6 @@ private static native Class forName0(String name, boolean initialize, * @return {@code Class} object of the given name defined in the given module; * {@code null} if not found. * - * @throws NullPointerException if the given module or name is {@code null} - * * @throws LinkageError if the linkage fails * * @jls 12.2 Loading of Classes and Interfaces @@ -619,8 +623,6 @@ public static Class forName(Module module, String name) { * * @param primitiveName the name of the primitive type to find * - * @throws NullPointerException if the argument is {@code null} - * * @jls 4.2 Primitive Types and Values * @jls 15.8.2 Class Literals * @since 22 @@ -756,7 +758,7 @@ public T newInstance() * this {@code Class} object represents a primitive type, this method * returns {@code false}. * - * @param obj the object to check + * @param obj the object to check, may be {@code null} * @return true if {@code obj} is an instance of this class * * @since 1.1 @@ -786,8 +788,6 @@ public T newInstance() * @param cls the {@code Class} object to be checked * @return the {@code boolean} value indicating whether objects of the * type {@code cls} can be assigned to objects of this class - * @throws NullPointerException if the specified Class parameter is - * null. * @since 1.1 */ @IntrinsicCandidate @@ -1445,7 +1445,6 @@ public Method getEnclosingMethod() { if (!enclosingInfo.isMethod()) return null; - // Descriptor already validated by VM List> types = BytecodeDescriptor.parseMethod(enclosingInfo.getDescriptor(), getClassLoader()); Class returnType = types.removeLast(); Class[] parameterClasses = types.toArray(EMPTY_CLASS_ARRAY); @@ -1533,8 +1532,15 @@ boolean isPartial() { String getName() { return name; } - String getDescriptor() { return descriptor; } - + String getDescriptor() { + // hotspot validates this descriptor to be either a field or method + // descriptor as the "type" in a NameAndType in verification. + // So this can still be a field descriptor + if (descriptor.isEmpty() || descriptor.charAt(0) != '(') { + throw new GenericSignatureFormatError("Bad method signature: " + descriptor); + } + return descriptor; + } } private static Class toClass(Type o) { @@ -1567,7 +1573,6 @@ public Constructor getEnclosingConstructor() { if (!enclosingInfo.isConstructor()) return null; - // Descriptor already validated by VM List> types = BytecodeDescriptor.parseMethod(enclosingInfo.getDescriptor(), getClassLoader()); types.removeLast(); Class[] parameterClasses = types.toArray(EMPTY_CLASS_ARRAY); @@ -2051,7 +2056,6 @@ public Constructor[] getConstructors() { * {@code name} * @throws NoSuchFieldException if a field with the specified name is * not found. - * @throws NullPointerException if {@code name} is {@code null} * * @since 1.1 * @jls 8.2 Class Members @@ -2142,13 +2146,13 @@ public Field getField(String name) throws NoSuchFieldException { * overriding method as it would have a more specific return type. * * @param name the name of the method - * @param parameterTypes the list of parameters + * @param parameterTypes the list of parameters, may be {@code null} * @return the {@code Method} object that matches the specified * {@code name} and {@code parameterTypes} - * @throws NoSuchMethodException if a matching method is not found + * @throws NoSuchMethodException if a matching method is not found, + * if {@code parameterTypes} contains {@code null}, * or if the name is {@value ConstantDescs#INIT_NAME} or - * {@value ConstantDescs#CLASS_INIT_NAME}. - * @throws NullPointerException if {@code name} is {@code null} + * {@value ConstantDescs#CLASS_INIT_NAME} * * @jls 8.2 Class Members * @jls 8.4 Method Declarations @@ -2179,12 +2183,13 @@ public Method getMethod(String name, Class... parameterTypes) * represented by this {@code Class} object whose formal parameter * types match those specified by {@code parameterTypes}. * - * @param parameterTypes the parameter array + * @param parameterTypes the parameter array, may be {@code null} * @return the {@code Constructor} object of the public constructor that * matches the specified {@code parameterTypes} * @throws NoSuchMethodException if a matching constructor is not found, - * including when this {@code Class} object represents - * an interface, a primitive type, an array class, or void. + * if this {@code Class} object represents an interface, a primitive + * type, an array class, or void, or if {@code parameterTypes} + * contains {@code null} * * @see #getDeclaredConstructor(Class[]) * @since 1.1 @@ -2365,7 +2370,6 @@ public Constructor[] getDeclaredConstructors() { * class * @throws NoSuchFieldException if a field with the specified name is * not found. - * @throws NullPointerException if {@code name} is {@code null} * * @since 1.1 * @jls 8.2 Class Members @@ -2400,11 +2404,13 @@ public Field getDeclaredField(String name) throws NoSuchFieldException { * method does not find the {@code clone()} method. * * @param name the name of the method - * @param parameterTypes the parameter array - * @return the {@code Method} object for the method of this class - * matching the specified name and parameters - * @throws NoSuchMethodException if a matching method is not found. - * @throws NullPointerException if {@code name} is {@code null} + * @param parameterTypes the parameter array, may be {@code null} + * @return the {@code Method} object for the method of this class + * matching the specified name and parameters + * @throws NoSuchMethodException if a matching method is not found, + * if {@code parameterTypes} contains {@code null}, + * or if the name is {@value ConstantDescs#INIT_NAME} or + * {@value ConstantDescs#CLASS_INIT_NAME} * * @jls 8.2 Class Members * @jls 8.4 Method Declarations @@ -2471,12 +2477,13 @@ Method findMethod(boolean publicOnly, String name, Class... parameterTypes) { * declared in a non-static context, the formal parameter types * include the explicit enclosing instance as the first parameter. * - * @param parameterTypes the parameter array + * @param parameterTypes the parameter array, may be {@code null} * @return The {@code Constructor} object for the constructor with the * specified parameter list * @throws NoSuchMethodException if a matching constructor is not found, - * including when this {@code Class} object represents - * an interface, a primitive type, an array class, or void. + * if this {@code Class} object represents an interface, a + * primitive type, an array class, or void, or if + * {@code parameterTypes} contains {@code null} * * @see #getConstructor(Class[]) * @since 1.1 @@ -2535,7 +2542,6 @@ public Constructor getDeclaredConstructor(Class... parameterTypes) * resource with this name is found, or the resource is in a package * that is not {@linkplain Module#isOpen(String, Module) open} to at * least the caller module. - * @throws NullPointerException If {@code name} is {@code null} * * @see Module#getResourceAsStream(String) * @since 1.1 @@ -2631,7 +2637,6 @@ public InputStream getResourceAsStream(String name) { * resource is in a package that is not * {@linkplain Module#isOpen(String, Module) open} to at least the caller * module. - * @throws NullPointerException If {@code name} is {@code null} * @since 1.1 */ @CallerSensitive @@ -3473,7 +3478,7 @@ Map enumConstantDirectory() { * Casts an object to the class or interface represented * by this {@code Class} object. * - * @param obj the object to be cast + * @param obj the object to be cast, may be {@code null} * @return the object after casting, or null if obj is null * * @throws ClassCastException if the object is not @@ -3528,7 +3533,6 @@ public Class asSubclass(Class clazz) { *

Note that any annotation returned by this method is a * declaration annotation. * - * @throws NullPointerException {@inheritDoc} * @since 1.5 */ @Override @@ -3541,7 +3545,6 @@ public A getAnnotation(Class annotationClass) { /** * {@inheritDoc} - * @throws NullPointerException {@inheritDoc} * @since 1.5 */ @Override @@ -3554,7 +3557,6 @@ public boolean isAnnotationPresent(Class annotationClass) *

Note that any annotations returned by this method are * declaration annotations. * - * @throws NullPointerException {@inheritDoc} * @since 1.8 */ @Override @@ -3584,7 +3586,6 @@ public Annotation[] getAnnotations() { *

Note that any annotation returned by this method is a * declaration annotation. * - * @throws NullPointerException {@inheritDoc} * @since 1.8 */ @Override @@ -3600,7 +3601,6 @@ public A getDeclaredAnnotation(Class annotationClass) *

Note that any annotations returned by this method are * declaration annotations. * - * @throws NullPointerException {@inheritDoc} * @since 1.8 */ @Override @@ -3831,6 +3831,7 @@ public Class getNestHost() { * @since 11 */ public boolean isNestmateOf(Class c) { + Objects.requireNonNull(c); if (this == c) { return true; } diff --git a/src/java.base/share/classes/java/lang/LazyConstant.java b/src/java.base/share/classes/java/lang/LazyConstant.java index 34f3d754a10..703d67b8abf 100644 --- a/src/java.base/share/classes/java/lang/LazyConstant.java +++ b/src/java.base/share/classes/java/lang/LazyConstant.java @@ -39,12 +39,16 @@ *

* A lazy constant is created using the factory method * {@linkplain LazyConstant#of(Supplier) LazyConstant.of({@code })}. + *

* When created, the lazy constant is not initialized, meaning it has no contents. + *

* The lazy constant (of type {@code T}) can then be initialized * (and its contents retrieved) by calling {@linkplain #get() get()}. The first time * {@linkplain #get() get()} is called, the underlying computing function * (provided at construction) will be invoked and the result will be used to initialize - * the constant. Once a lazy constant is initialized, its contents can never change + * the constant. + *

+ * Once a lazy constant is initialized, its contents can never change * and will be retrieved over and over again upon subsequent {@linkplain #get() get()} * invocations. *

@@ -64,7 +68,7 @@ * // ... * } * } - *} + * } *

* Initially, the lazy constant is not initialized. When {@code logger.get()} * is first invoked, it evaluates the computing function and initializes the constant to @@ -121,7 +125,7 @@ * } * * } - *} + * } * Calling {@code BAR.get()} will create the {@code Bar} singleton if it is not already * created. Upon such a creation, a dependent {@code Foo} will first be created if * the {@code Foo} does not already exist. @@ -238,10 +242,11 @@ public sealed interface LazyConstant // Object methods /** - * {@return if this lazy constant is the same as the provided {@code obj}} + * {@return {@code true} if this lazy constant is the same instance as + * the provided {@code obj}, otherwise {@code false}} *

* In other words, equals compares the identity of this lazy constant and {@code obj} - * to determine equality. Hence, two lazy constants with the same contents are + * to determine equality. Hence, two distinct lazy constants with the same contents are * not equal. *

* This method never triggers initialization of this lazy constant. diff --git a/src/java.base/share/classes/java/lang/ThreadBuilders.java b/src/java.base/share/classes/java/lang/ThreadBuilders.java index 62c29651d11..033c237877c 100644 --- a/src/java.base/share/classes/java/lang/ThreadBuilders.java +++ b/src/java.base/share/classes/java/lang/ThreadBuilders.java @@ -309,7 +309,7 @@ UncaughtExceptionHandler uncaughtExceptionHandler() { String nextThreadName() { if (hasCounter) { - return name + (long) COUNT.getAndAdd(this, 1); + return name + (long) COUNT.getAndAdd(this, 1L); } else { return name; } diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/package-info.java b/src/java.base/share/classes/java/lang/classfile/attribute/package-info.java index 1ddd0511d85..43bf1984a00 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/package-info.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/package-info.java @@ -32,9 +32,8 @@ * including {@link Attribute}, {@link AttributedElement}, {@link AttributeMapper}, and {@link CustomAttribute}, which * do not reside in this package. *

- * Unless otherwise specified, passing {@code null} or an array or collection containing a {@code null} element as an - * argument to a constructor or method of any Class-File API class or interface will cause a {@link NullPointerException} - * to be thrown. + * APIs in this package perform {@linkplain java.lang.classfile##checks null and unrepresentable argument checks}, + * unless otherwise noted. * *

Reading Attributes

* The general way to obtain attributes is through {@link AttributedElement}. In addition to that, many attributes diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/package-info.java b/src/java.base/share/classes/java/lang/classfile/constantpool/package-info.java index 66e72496d3a..0828e04ae22 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/package-info.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/package-info.java @@ -30,9 +30,8 @@ * {@code class} file format. Constant pool entries are low-level models to faithfully represent the exact structure * of a {@code class} file. *

- * Unless otherwise specified, passing {@code null} or an array or collection containing a {@code null} element as an - * argument to a constructor or method of any Class-File API class or interface will cause a {@link NullPointerException} - * to be thrown. + * APIs in this package perform {@linkplain java.lang.classfile##checks null and unrepresentable argument checks}, + * unless otherwise noted. * *

Reading the constant pool entries

* When read from {@code class} files, the pool entries are lazily inflated; the contents of these entries, besides the diff --git a/src/java.base/share/classes/java/lang/classfile/instruction/package-info.java b/src/java.base/share/classes/java/lang/classfile/instruction/package-info.java index e732aadf1ec..990731721a8 100644 --- a/src/java.base/share/classes/java/lang/classfile/instruction/package-info.java +++ b/src/java.base/share/classes/java/lang/classfile/instruction/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -29,9 +29,8 @@ * The {@code java.lang.classfile.instruction} package contains interfaces describing code instructions. * Implementations of these interfaces are immutable. *

- * Unless otherwise specified, passing {@code null} or an array or collection containing a {@code null} element as an - * argument to a constructor or method of any Class-File API class or interface will cause a {@link NullPointerException} - * to be thrown. + * APIs in this package perform {@linkplain java.lang.classfile##checks null and unrepresentable argument checks}, + * unless otherwise noted. * *

Reading of instructions

* Instructions and pseudo-instructions are usually accessed from a {@link CodeModel}, such as {@link CodeModel#forEach diff --git a/src/java.base/share/classes/java/lang/classfile/package-info.java b/src/java.base/share/classes/java/lang/classfile/package-info.java index da9ad7fbf0d..460f6699e7b 100644 --- a/src/java.base/share/classes/java/lang/classfile/package-info.java +++ b/src/java.base/share/classes/java/lang/classfile/package-info.java @@ -250,12 +250,6 @@ * the convenience method {@code CodeBuilder.invoke}, which in turn behaves * as if it calls method {@code CodeBuilder.with}. This composing of method calls on the * builder enables the composing of transforms (as described later). - *

- * Unless otherwise noted, passing a {@code null} argument to a constructor - * or method of any Class-File API class or interface will cause a {@link - * NullPointerException} to be thrown. Additionally, - * invoking a method with an array or collection containing a {@code null} element - * will cause a {@code NullPointerException}, unless otherwise specified. * *

Symbolic information

* To describe symbolic information for classes and types, the API uses the @@ -272,14 +266,22 @@ * symbolic information, one accepting nominal descriptors, and the other * accepting constant pool entries. * - *

Consistency checks, syntax checks and verification

- * The Class-File API performs checks to ensure arguments are representable in - * the {@code class} file format. A value that is lost when it is built to a - * {@code class} file and re-parsed to a model is rejected with an {@link - * IllegalArgumentException}. For example, a negative value or a value over - * {@code 65535} is lost when built to a {@link ##u2 u2} item, with - * the range {@code [0, 65535]}. In particular, any variable-sized table - * exceeding its maximum representable size is rejected. + *

Consistency checks, syntax checks and verification

+ * The Class-File API performs checks to ensure arguments to construct {@code + * class} file structures are representable in the {@code class} file format. + * An argument value that cannot be representable by its data type is rejected + * with an {@link IllegalArgumentException}. For example, an {@code int} value + * cannot be out of the range of its {@linkplain java.lang.classfile##data-types + * data type}; a {@code List} cannot exceed the maximum representable size of + * its table data type, or contain an unrepresentable element. Restrictions + * based on underlying data type, such as the {@code int} and {@code List} ones + * before, are specified on the corresponding APIs. Unless otherwise noted, in + * all structures, a {@code String} cannot exceed {@code 65535} bytes when + * represented in modified UTF-8 format. + *

+ * Unless otherwise noted, passing null or an array or collection that contains + * null as an element to a constructor or method of any Class-File API class or + * interface will cause a {@link NullPointerException} to be thrown. *

* No consistency checks are performed while building or transforming classfiles * (except for null and representable arguments checks). All builders and diff --git a/src/java.base/share/classes/java/lang/invoke/MethodType.java b/src/java.base/share/classes/java/lang/invoke/MethodType.java index 3d15ce68710..afbe63709fd 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodType.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodType.java @@ -1194,10 +1194,6 @@ public static MethodType fromMethodDescriptorString(String descriptor, ClassLoad static MethodType fromDescriptor(String descriptor, ClassLoader loader) throws IllegalArgumentException, TypeNotPresentException { - if (!descriptor.startsWith("(") || // also generates NPE if needed - descriptor.indexOf(')') < 0 || - descriptor.indexOf('.') >= 0) - throw newIllegalArgumentException("not a method descriptor: "+descriptor); List> types = BytecodeDescriptor.parseMethod(descriptor, loader); Class rtype = types.remove(types.size() - 1); Class[] ptypes = listToArray(types); diff --git a/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java b/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java index 1637d26b571..54000b916e2 100644 --- a/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java +++ b/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java @@ -97,6 +97,8 @@ public class AccessibleObject implements AnnotatedElement { * objects in the array * @throws SecurityException if an element in the array is a constructor for {@code * java.lang.Class} + * @throws NullPointerException if {@code array} or any of its elements is + * {@code null} */ @CallerSensitive public static void setAccessible(AccessibleObject[] array, boolean flag) { diff --git a/src/java.base/share/classes/java/lang/reflect/Array.java b/src/java.base/share/classes/java/lang/reflect/Array.java index 2fbad52c374..4e676c050d7 100644 --- a/src/java.base/share/classes/java/lang/reflect/Array.java +++ b/src/java.base/share/classes/java/lang/reflect/Array.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -98,8 +98,8 @@ public static Object newInstance(Class componentType, int length) * @param dimensions an array of {@code int} representing the dimensions of * the new array * @return the new array - * @throws NullPointerException if the specified - * {@code componentType} argument is null + * @throws NullPointerException if any of the specified + * {@code componentType} or {@code dimensions} arguments is null * @throws IllegalArgumentException if the specified {@code dimensions} * argument is a zero-dimensional array, if componentType is {@link * Void#TYPE}, or if the number of dimensions of the requested array @@ -117,8 +117,8 @@ public static Object newInstance(Class componentType, int... dimensions) * * @param array the array * @return the length of the array - * @throws IllegalArgumentException if the object argument is not - * an array + * @throws NullPointerException if {@code array} is {@code null} + * @throws IllegalArgumentException if {@code array} is not an array */ @IntrinsicCandidate public static native int getLength(Object array) diff --git a/src/java.base/share/classes/java/lang/reflect/ClassFileFormatVersion.java b/src/java.base/share/classes/java/lang/reflect/ClassFileFormatVersion.java index 4a63dd157f8..16fe000091c 100644 --- a/src/java.base/share/classes/java/lang/reflect/ClassFileFormatVersion.java +++ b/src/java.base/share/classes/java/lang/reflect/ClassFileFormatVersion.java @@ -433,6 +433,7 @@ public int major() { * ClassFileFormatVersion.valueOf(Runtime.Version.parse("17"))} * * @param rv runtime version to map to a class file format version + * @throws NullPointerException if {@code rv} is {@code null} * @throws IllegalArgumentException if the feature of version * argument is greater than the feature of the platform version. */ diff --git a/src/java.base/share/classes/java/lang/reflect/InaccessibleObjectException.java b/src/java.base/share/classes/java/lang/reflect/InaccessibleObjectException.java index 7bc9ea33b2b..29d6eb13602 100644 --- a/src/java.base/share/classes/java/lang/reflect/InaccessibleObjectException.java +++ b/src/java.base/share/classes/java/lang/reflect/InaccessibleObjectException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -47,7 +47,7 @@ public InaccessibleObjectException() { * message. * * @param msg - * The detail message + * The detail message, may be {@code null} */ public InaccessibleObjectException(String msg) { super(msg); diff --git a/src/java.base/share/classes/java/lang/reflect/InvocationTargetException.java b/src/java.base/share/classes/java/lang/reflect/InvocationTargetException.java index a6ef4fa289c..efdca9bcbd1 100644 --- a/src/java.base/share/classes/java/lang/reflect/InvocationTargetException.java +++ b/src/java.base/share/classes/java/lang/reflect/InvocationTargetException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -62,7 +62,7 @@ protected InvocationTargetException() { /** * Constructs a InvocationTargetException with a target exception. * - * @param target the target exception + * @param target the target exception, may be {@code null} */ public InvocationTargetException(Throwable target) { super((Throwable)null); // Disallow initCause @@ -73,8 +73,8 @@ public InvocationTargetException(Throwable target) { * Constructs a InvocationTargetException with a target exception * and a detail message. * - * @param target the target exception - * @param s the detail message + * @param target the target exception, may be {@code null} + * @param s the detail message, may be {@code null} */ public InvocationTargetException(Throwable target, String s) { super(s, null); // Disallow initCause diff --git a/src/java.base/share/classes/java/lang/reflect/MalformedParametersException.java b/src/java.base/share/classes/java/lang/reflect/MalformedParametersException.java index ae2da8c9279..fa754be5474 100644 --- a/src/java.base/share/classes/java/lang/reflect/MalformedParametersException.java +++ b/src/java.base/share/classes/java/lang/reflect/MalformedParametersException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -65,7 +65,7 @@ public MalformedParametersException() {} /** * Create a {@code MalformedParametersException}. * - * @param reason The reason for the exception. + * @param reason The reason for the exception, may be {@code null} */ public MalformedParametersException(String reason) { super(reason); diff --git a/src/java.base/share/classes/java/lang/reflect/Proxy.java b/src/java.base/share/classes/java/lang/reflect/Proxy.java index dc512e86590..b811deb863d 100644 --- a/src/java.base/share/classes/java/lang/reflect/Proxy.java +++ b/src/java.base/share/classes/java/lang/reflect/Proxy.java @@ -897,7 +897,8 @@ private static Module getDynamicModule(ClassLoader loader) { * of interfaces but in a different order will result in two distinct * proxy classes. * - * @param loader the class loader to define the proxy class + * @param loader the class loader to define the proxy class, may be + * {@code null} to represent the bootstrap class loader * @param interfaces the list of interfaces for the proxy class * to implement * @param h the invocation handler to dispatch method invocations to diff --git a/src/java.base/share/classes/java/lang/reflect/UndeclaredThrowableException.java b/src/java.base/share/classes/java/lang/reflect/UndeclaredThrowableException.java index 0b086330de2..25d8affa474 100644 --- a/src/java.base/share/classes/java/lang/reflect/UndeclaredThrowableException.java +++ b/src/java.base/share/classes/java/lang/reflect/UndeclaredThrowableException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -61,7 +61,7 @@ public class UndeclaredThrowableException extends RuntimeException { * specified {@code Throwable}. * * @param undeclaredThrowable the undeclared checked exception - * that was thrown + * that was thrown, may be {@code null} */ public UndeclaredThrowableException(Throwable undeclaredThrowable) { super(null, undeclaredThrowable); // Disallow initCause @@ -72,8 +72,8 @@ public UndeclaredThrowableException(Throwable undeclaredThrowable) { * specified {@code Throwable} and a detail message. * * @param undeclaredThrowable the undeclared checked exception - * that was thrown - * @param s the detail message + * that was thrown, may be {@code null} + * @param s the detail message, may be {@code null} */ public UndeclaredThrowableException(Throwable undeclaredThrowable, String s) diff --git a/src/java.base/share/classes/java/net/HostPortrange.java b/src/java.base/share/classes/java/net/HostPortrange.java index 289846841c9..f7a5b911633 100644 --- a/src/java.base/share/classes/java/net/HostPortrange.java +++ b/src/java.base/share/classes/java/net/HostPortrange.java @@ -60,6 +60,11 @@ public int hashCode() { } HostPortrange(String scheme, String host) { + // Defensive validation first + if (host == null || host.isEmpty()) { + throw new IllegalArgumentException("Invalid URL authority"); + } + // Parse the host name. A name has up to three components, the // hostname, a port number, or two numbers representing a port // range. "www.example.com:8080-9090" is a valid host name. diff --git a/src/java.base/share/classes/java/net/URLPermission.java b/src/java.base/share/classes/java/net/URLPermission.java index bf87cad8077..ac51d6c60a1 100644 --- a/src/java.base/share/classes/java/net/URLPermission.java +++ b/src/java.base/share/classes/java/net/URLPermission.java @@ -527,6 +527,9 @@ static class Authority { HostPortrange p; Authority(String scheme, String authority) { + if (authority == null || authority.isEmpty()) { + throw new IllegalArgumentException("Invalid URL: authority is empty"); + } int at = authority.indexOf('@'); if (at == -1) { p = new HostPortrange(scheme, authority); diff --git a/src/java.base/share/classes/java/time/MonthDay.java b/src/java.base/share/classes/java/time/MonthDay.java index a25c9beec95..26ca69bbe0d 100644 --- a/src/java.base/share/classes/java/time/MonthDay.java +++ b/src/java.base/share/classes/java/time/MonthDay.java @@ -88,6 +88,8 @@ import java.time.temporal.ValueRange; import java.util.Objects; +import jdk.internal.util.DecimalDigits; + /** * A month-day in the ISO-8601 calendar system, such as {@code --12-03}. *

@@ -764,10 +766,12 @@ public int hashCode() { */ @Override public String toString() { - return new StringBuilder(10).append("--") - .append(month < 10 ? "0" : "").append(month) - .append(day < 10 ? "-0" : "-").append(day) - .toString(); + StringBuilder buf = new StringBuilder(10); + buf.append("--"); + DecimalDigits.appendPair(buf, month); + buf.append('-'); + DecimalDigits.appendPair(buf, day); + return buf.toString(); } //----------------------------------------------------------------------- diff --git a/src/java.base/share/classes/java/time/YearMonth.java b/src/java.base/share/classes/java/time/YearMonth.java index b3d1aff7bc2..665c8c85544 100644 --- a/src/java.base/share/classes/java/time/YearMonth.java +++ b/src/java.base/share/classes/java/time/YearMonth.java @@ -101,6 +101,8 @@ import java.time.temporal.ValueRange; import java.util.Objects; +import jdk.internal.util.DecimalDigits; + /** * A year-month in the ISO-8601 calendar system, such as {@code 2007-12}. *

@@ -1213,18 +1215,17 @@ public int hashCode() { public String toString() { int absYear = Math.abs(year); StringBuilder buf = new StringBuilder(9); - if (absYear < 1000) { + if (absYear < 10000) { if (year < 0) { - buf.append(year - 10000).deleteCharAt(1); - } else { - buf.append(year + 10000).deleteCharAt(0); + buf.append('-'); } + DecimalDigits.appendQuad(buf, absYear); } else { buf.append(year); } - return buf.append(month < 10 ? "-0" : "-") - .append(month) - .toString(); + buf.append('-'); + DecimalDigits.appendPair(buf, month); + return buf.toString(); } //----------------------------------------------------------------------- diff --git a/src/java.base/share/classes/java/time/ZoneOffset.java b/src/java.base/share/classes/java/time/ZoneOffset.java index 4199d17735c..2a45e7cbf82 100644 --- a/src/java.base/share/classes/java/time/ZoneOffset.java +++ b/src/java.base/share/classes/java/time/ZoneOffset.java @@ -88,6 +88,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicReferenceArray; +import jdk.internal.util.DecimalDigits; import jdk.internal.vm.annotation.Stable; /** @@ -465,12 +466,14 @@ private static String buildId(int totalSeconds) { StringBuilder buf = new StringBuilder(); int absHours = absTotalSeconds / SECONDS_PER_HOUR; int absMinutes = (absTotalSeconds / SECONDS_PER_MINUTE) % MINUTES_PER_HOUR; - buf.append(totalSeconds < 0 ? "-" : "+") - .append(absHours < 10 ? "0" : "").append(absHours) - .append(absMinutes < 10 ? ":0" : ":").append(absMinutes); + buf.append(totalSeconds < 0 ? '-' : '+'); + DecimalDigits.appendPair(buf, absHours); + buf.append(':'); + DecimalDigits.appendPair(buf, absMinutes); int absSeconds = absTotalSeconds % SECONDS_PER_MINUTE; if (absSeconds != 0) { - buf.append(absSeconds < 10 ? ":0" : ":").append(absSeconds); + buf.append(':'); + DecimalDigits.appendPair(buf, absSeconds); } return buf.toString(); } diff --git a/src/java.base/share/classes/java/time/chrono/ChronoLocalDateImpl.java b/src/java.base/share/classes/java/time/chrono/ChronoLocalDateImpl.java index ca226b70d24..67f08c5cb4f 100644 --- a/src/java.base/share/classes/java/time/chrono/ChronoLocalDateImpl.java +++ b/src/java.base/share/classes/java/time/chrono/ChronoLocalDateImpl.java @@ -74,6 +74,8 @@ import java.time.temporal.ValueRange; import java.util.Objects; +import jdk.internal.util.DecimalDigits; + /** * A date expressed in terms of a standard year-month-day calendar system. *

@@ -426,18 +428,22 @@ public int hashCode() { @Override public String toString() { - // getLong() reduces chances of exceptions in toString() - long yoe = getLong(YEAR_OF_ERA); - long moy = getLong(MONTH_OF_YEAR); - long dom = getLong(DAY_OF_MONTH); + // Using get() instead of getLong() for performance reasons, + // as the values of YEAR_OF_ERA, MONTH_OF_YEAR, and DAY_OF_MONTH + // are guaranteed to be within the int range for all chronologies. + int yoe = get(YEAR_OF_ERA); + int moy = get(MONTH_OF_YEAR); + int dom = get(DAY_OF_MONTH); StringBuilder buf = new StringBuilder(30); buf.append(getChronology().toString()) .append(" ") .append(getEra()) .append(" ") .append(yoe) - .append(moy < 10 ? "-0" : "-").append(moy) - .append(dom < 10 ? "-0" : "-").append(dom); + .append('-'); + DecimalDigits.appendPair(buf, moy); + buf.append('-'); + DecimalDigits.appendPair(buf, dom); return buf.toString(); } diff --git a/src/java.base/share/classes/java/util/List.java b/src/java.base/share/classes/java/util/List.java index 6ab80b83ef8..43408de292a 100644 --- a/src/java.base/share/classes/java/util/List.java +++ b/src/java.base/share/classes/java/util/List.java @@ -1207,7 +1207,7 @@ static List copyOf(Collection coll) { * The provided computing function is guaranteed to be successfully * invoked at most once per list index, even in a multi-threaded environment. * Competing threads accessing an element already under computation will block until - * an element is computed or the computing function completes abnormally + * an element is computed or the computing function completes abnormally. *

* If invoking the provided computing function throws an exception, it is rethrown * to the initial caller and no value for the element is recorded. @@ -1235,7 +1235,7 @@ static List copyOf(Collection coll) { * one or more lazy elements. *

* The returned lazy list strongly references its computing - * function used to compute elements at least so long as there are uninitialized + * function used to compute elements at least as long as there are uninitialized * elements. *

* The returned List is not {@linkplain Serializable}. diff --git a/src/java.base/share/classes/java/util/Map.java b/src/java.base/share/classes/java/util/Map.java index abee819069f..177f0522b1b 100644 --- a/src/java.base/share/classes/java/util/Map.java +++ b/src/java.base/share/classes/java/util/Map.java @@ -1788,7 +1788,7 @@ static Map copyOf(Map map) { * one or more lazy elements. *

* The returned lazy map strongly references its underlying - * computing function used to compute values at least so long as there are + * computing function used to compute values at least as long as there are * uncomputed values. *

* The returned Map is not {@linkplain Serializable}. diff --git a/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java b/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java index 4cb7048a798..9295f0deb59 100644 --- a/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java +++ b/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java @@ -1372,12 +1372,20 @@ public boolean equals(Object o) { Node[] t; int f = (t = table) == null ? 0 : t.length; Traverser it = new Traverser(t, f, 0, f); - for (Node p; (p = it.advance()) != null; ) { - V val = p.val; - Object v = m.get(p.key); - if (v == null || (v != val && !v.equals(val))) - return false; + + try { + for (Node p; (p = it.advance()) != null; ) { + V val = p.val; + Object v = m.get(p.key); + if (v == null || (v != val && !v.equals(val))) + return false; + } + } catch (ClassCastException | NullPointerException _) { + // m.get(p.key) is contractually allowed to throw CCE or NPE + // but CHM doesn't allow null keys, so NPE shouldn't occur in practice + return false; } + for (Map.Entry e : m.entrySet()) { Object mk, mv, v; if ((mk = e.getKey()) == null || diff --git a/src/java.base/share/classes/java/util/concurrent/DelayScheduler.java b/src/java.base/share/classes/java/util/concurrent/DelayScheduler.java index d1f8283489b..1ded0211932 100644 --- a/src/java.base/share/classes/java/util/concurrent/DelayScheduler.java +++ b/src/java.base/share/classes/java/util/concurrent/DelayScheduler.java @@ -360,31 +360,36 @@ private static int replace(ScheduledForkJoinTask[] h, int k, int n) { u.heapIndex = -1; } } - if (t != null) { // sift down - for (int cs; (cs = (k << 2) + 1) < n; ) { - ScheduledForkJoinTask leastChild = null, c; + if (t != null) { + while (k > 0) { // sift up if replaced with smaller value + ScheduledForkJoinTask parent; int pk; + if ((parent = h[pk = (k - 1) >>> 2]) == null || + parent.when <= d) + break; + parent.heapIndex = k; + h[k] = parent; + k = pk; + } + for (int cs; (cs = (k << 2) + 1) < n; ) { // sift down + ScheduledForkJoinTask leastChild = null; int leastIndex = 0; - long leastValue = Long.MAX_VALUE; - for (int ck = cs, j = 4;;) { // at most 4 children - if ((c = h[ck]) == null) - break; - long cd = c.when; - if (c.status < 0 && alsoReplace < 0) { - alsoReplace = ck; // at most once per pass - c.heapIndex = -1; - } - else if (leastChild == null || cd < leastValue) { + long leastValue = d; // at most 4 children + for (int ck, j = 0; j < 4 && (ck = j + cs) < n; ++j) { + ScheduledForkJoinTask c; long cd; + if ((c = h[ck]) != null && (cd = c.when) < leastValue) { leastValue = cd; leastIndex = ck; leastChild = c; } - if (--j == 0 || ++ck >= n) - break; } - if (leastChild == null || d <= leastValue) + if (leastChild == null) // already ordered break; - leastChild.heapIndex = k; - h[k] = leastChild; + if ((h[k] = leastChild).status >= 0 || alsoReplace >= 0) + leastChild.heapIndex = k; + else { + leastChild.heapIndex = -1; + alsoReplace = k; + } k = leastIndex; } t.heapIndex = k; @@ -393,6 +398,7 @@ else if (leastChild == null || cd < leastValue) { k = alsoReplace; } } + assert checkHeap(h, n); return n; } @@ -451,6 +457,33 @@ private static void cancelAll(ScheduledForkJoinTask[] h, int n) { } } + /** + * Invariant checks + */ + private static boolean checkHeap(ScheduledForkJoinTask[] h, int n) { + for (int i = 0; i < h.length; ++i) { + ScheduledForkJoinTask t = h[i]; + if (t == null) { // unused slots all null + if (i < n) + return false; + } + else { + long v = t.when; + int x = t.heapIndex; + if (x != i && x >= 0) // valid index unless removing + return false; + if (i > 0 && h[(i - 1) >>> 2].when > v) // ordered wrt parent + return false; + int cs = (i << 2) + 1; // ordered wrt children + for (int ck, j = 0; j < 4 && (ck = cs + j) < n; ++j) { + if (h[ck].when < v) + return false; + } + } + } + return true; + } + /** * Task class for DelayScheduler operations */ diff --git a/src/java.base/share/classes/java/util/stream/SortedOps.java b/src/java.base/share/classes/java/util/stream/SortedOps.java index 91892eba44f..1286735ebd9 100644 --- a/src/java.base/share/classes/java/util/stream/SortedOps.java +++ b/src/java.base/share/classes/java/util/stream/SortedOps.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -108,13 +108,10 @@ private static final class OfRef extends ReferencePipeline.StatefulOp { * {@code Comparable}. */ OfRef(AbstractPipeline upstream) { - super(upstream, StreamShape.REFERENCE, - StreamOpFlag.IS_ORDERED | StreamOpFlag.IS_SORTED); - this.isNaturalSort = true; // Will throw CCE when we try to sort if T is not Comparable @SuppressWarnings("unchecked") Comparator comp = (Comparator) Comparator.naturalOrder(); - this.comparator = comp; + this(upstream, comp); } /** @@ -123,10 +120,13 @@ private static final class OfRef extends ReferencePipeline.StatefulOp { * @param comparator The comparator to be used to evaluate ordering. */ OfRef(AbstractPipeline upstream, Comparator comparator) { + Objects.requireNonNull(comparator); + boolean isNaturalSort = Comparator.naturalOrder().equals(comparator); + this.comparator = comparator; + this.isNaturalSort = isNaturalSort; super(upstream, StreamShape.REFERENCE, - StreamOpFlag.IS_ORDERED | StreamOpFlag.NOT_SORTED); - this.isNaturalSort = false; - this.comparator = Objects.requireNonNull(comparator); + StreamOpFlag.IS_ORDERED | + (isNaturalSort ? StreamOpFlag.IS_SORTED : StreamOpFlag.NOT_SORTED)); } @Override diff --git a/src/java.base/share/classes/java/util/stream/StreamOpFlag.java b/src/java.base/share/classes/java/util/stream/StreamOpFlag.java index df98125cbc0..0e7b2f6607b 100644 --- a/src/java.base/share/classes/java/util/stream/StreamOpFlag.java +++ b/src/java.base/share/classes/java/util/stream/StreamOpFlag.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -24,6 +24,7 @@ */ package java.util.stream; +import java.util.Comparator; import java.util.EnumMap; import java.util.Map; import java.util.Spliterator; @@ -738,9 +739,9 @@ static int toCharacteristics(int streamFlags) { * * @implSpec * If the spliterator is naturally {@code SORTED} (the associated - * {@code Comparator} is {@code null}) then the characteristic is converted - * to the {@link #SORTED} flag, otherwise the characteristic is not - * converted. + * {@code Comparator} is {@code null} or {@code Comparator.naturalOrder()}) then + * the characteristic is converted to the {@link #SORTED} flag, otherwise + * the characteristic is not converted. * * @param spliterator the spliterator from which to obtain characteristic * bit set. @@ -748,14 +749,15 @@ static int toCharacteristics(int streamFlags) { */ static int fromCharacteristics(Spliterator spliterator) { int characteristics = spliterator.characteristics(); - if ((characteristics & Spliterator.SORTED) != 0 && spliterator.getComparator() != null) { - // Do not propagate the SORTED characteristic if it does not correspond - // to a natural sort order - return characteristics & SPLITERATOR_CHARACTERISTICS_MASK & ~Spliterator.SORTED; - } - else { - return characteristics & SPLITERATOR_CHARACTERISTICS_MASK; + if ((characteristics & Spliterator.SORTED) != 0) { + Comparator comparator = spliterator.getComparator(); + if (comparator != null && !Comparator.naturalOrder().equals(comparator)) { + // Do not propagate the SORTED characteristic if it does not correspond + // to a natural sort order + return characteristics & SPLITERATOR_CHARACTERISTICS_MASK & ~Spliterator.SORTED; + } } + return characteristics & SPLITERATOR_CHARACTERISTICS_MASK; } /** diff --git a/src/java.base/share/classes/javax/crypto/spec/HPKEParameterSpec.java b/src/java.base/share/classes/javax/crypto/spec/HPKEParameterSpec.java new file mode 100644 index 00000000000..6776ddcdb75 --- /dev/null +++ b/src/java.base/share/classes/javax/crypto/spec/HPKEParameterSpec.java @@ -0,0 +1,443 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.crypto.spec; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import java.nio.charset.StandardCharsets; +import java.security.AsymmetricKey; +import java.security.Key; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Arrays; +import java.util.HexFormat; +import java.util.Objects; + +/** + * This immutable class specifies the set of parameters used with a {@code Cipher} for the + * Hybrid Public Key Encryption + * (HPKE) algorithm. HPKE is a public key encryption scheme for encrypting + * arbitrary-sized plaintexts with a recipient's public key. It combines a key + * encapsulation mechanism (KEM), a key derivation function (KDF), and an + * authenticated encryption with additional data (AEAD) cipher. + *

+ * The + * standard algorithm name for the cipher is "HPKE". Unlike most other + * ciphers, HPKE is not expressed as a transformation string of the form + * "algorithm/mode/padding". Therefore, the argument to {@code Cipher.getInstance} + * must be the single algorithm name "HPKE". + *

+ * In HPKE, the sender's {@code Cipher} is always initialized with the + * recipient's public key in {@linkplain Cipher#ENCRYPT_MODE encrypt mode}, + * while the recipient's {@code Cipher} object is initialized with its own + * private key in {@linkplain Cipher#DECRYPT_MODE decrypt mode}. + *

+ * An {@code HPKEParameterSpec} object must be provided at HPKE + * {@linkplain Cipher#init(int, Key, AlgorithmParameterSpec) cipher initialization}. + *

+ * The {@link #of(int, int, int)} static method returns an {@code HPKEParameterSpec} + * object with the specified KEM, KDF, and AEAD algorithm identifiers. + * The terms "KEM algorithm identifiers", "KDF algorithm identifiers", and + * "AEAD algorithm identifiers" refer to their respective numeric values + * (specifically, {@code kem_id}, {@code kdf_id}, and {@code aead_id}) as + * defined in Section 7 + * of RFC 9180 and maintained on the + * IANA HPKE page. + *

+ * Once an {@code HPKEParameterSpec} object is created, additional methods + * are available to generate new {@code HPKEParameterSpec} objects with + * different features: + *

    + *
  • + * Application-supplied information can be provided using the + * {@link #withInfo(byte[])} method by both sides. + *
  • + * To authenticate using a pre-shared key ({@code mode_psk}), the + * pre-shared key and its identifier must be provided using the + * {@link #withPsk(SecretKey, byte[])} method by both sides. + *
  • + * To authenticate using an asymmetric key ({@code mode_auth}), + * the asymmetric keys must be provided using the {@link #withAuthKey(AsymmetricKey)} + * method. Precisely, the sender must call this method with its own private key + * and the recipient must call it with the sender's public key. + *
  • + * To authenticate using both a PSK and an asymmetric key + * ({@code mode_auth_psk}), both {@link #withAuthKey(AsymmetricKey)} and + * {@link #withPsk(SecretKey, byte[])} methods must be called as described above. + *
  • + * In HPKE, a shared secret is negotiated during the KEM step and a key + * encapsulation message must be transmitted from the sender to the recipient + * so that the recipient can recover the shared secret. On the sender side, + * after the cipher is initialized, the key encapsulation message can be + * retrieved using the {@link Cipher#getIV()} method. On the recipient side, + * this message must be supplied as part of an {@code HPKEParameterSpec} + * object obtained from the {@link #withEncapsulation(byte[])} method. + *
+ * For successful interoperability, both sides need to have identical algorithm + * identifiers, and supply identical + * {@code info}, {@code psk}, and {@code psk_id} or matching authentication + * keys if provided. For details about HPKE modes, refer to + * Section 5 + * of RFC 9180. + *

+ * If an HPKE cipher is {@linkplain Cipher#init(int, Key) initialized without + * parameters}, an {@code InvalidKeyException} is thrown. + *

+ * At HPKE cipher initialization, if no HPKE implementation supports the + * provided key type, an {@code InvalidKeyException} is thrown. If the provided + * {@code HPKEParameterSpec} is not accepted by any HPKE implementation, + * an {@code InvalidAlgorithmParameterException} is thrown. For example: + *

    + *
  • An algorithm identifier is unsupported or does not match the provided key type. + *
  • A key encapsulation message is provided on the sender side. + *
  • A key encapsulation message is not provided on the recipient side. + *
  • An attempt to use {@code withAuthKey(key)} is made with an incompatible key. + *
  • An attempt to use {@code withAuthKey(key)} is made but {@code mode_auth} + * or {@code mode_auth_psk} is not supported by the KEM algorithm used. + *
+ * After initialization, both the sender and recipient can process multiple + * messages in sequence with repeated {@code doFinal} calls, optionally preceded + * by one or more {@code updateAAD} and {@code update}. Each {@code doFinal} + * performs a complete HPKE encryption or decryption operation using a distinct + * IV derived from an internal sequence counter, as specified in + * Section 5.2 + * of RFC 9180. On the recipient side, each {@code doFinal} call must correspond + * to exactly one complete ciphertext, and the number and order of calls must + * match those on the sender side. This differs from the direct use of an AEAD + * cipher, where the caller must provide a fresh IV and reinitialize the cipher + * for each message. By managing IVs internally, HPKE allows a single + * initialization to support multiple messages while still ensuring IV + * uniqueness and preserving AEAD security guarantees. + *

+ * This example shows a sender and a recipient using HPKE to securely exchange + * messages with an X25519 key pair. + * {@snippet lang=java class="PackageSnippets" region="hpke-spec-example"} + * + * @implNote This class defines constants for some of the standard algorithm + * identifiers such as {@link #KEM_DHKEM_P_256_HKDF_SHA256}, + * {@link #KDF_HKDF_SHA256}, and {@link #AEAD_AES_128_GCM}. An HPKE {@code Cipher} + * implementation may support all, some, or none of the algorithm identifiers + * defined here. An implementation may also support additional identifiers not + * listed here, including private or experimental values. + * + * @spec https://www.rfc-editor.org/info/rfc9180 + * RFC 9180: Hybrid Public Key Encryption + * @spec security/standard-names.html + * Java Security Standard Algorithm Names + * @since 26 + */ +public final class HPKEParameterSpec implements AlgorithmParameterSpec { + + /** + * KEM algorithm identifier for DHKEM(P-256, HKDF-SHA256) as defined in RFC 9180. + */ + public static final int KEM_DHKEM_P_256_HKDF_SHA256 = 0x10; + + /** + * KEM algorithm identifier for DHKEM(P-384, HKDF-SHA384) as defined in RFC 9180. + */ + public static final int KEM_DHKEM_P_384_HKDF_SHA384 = 0x11; + + /** + * KEM algorithm identifier for DHKEM(P-521, HKDF-SHA512) as defined in RFC 9180. + */ + public static final int KEM_DHKEM_P_521_HKDF_SHA512 = 0x12; + + /** + * KEM algorithm identifier for DHKEM(X25519, HKDF-SHA256) as defined in RFC 9180. + */ + public static final int KEM_DHKEM_X25519_HKDF_SHA256 = 0x20; + + /** + * KEM algorithm identifier for DHKEM(X448, HKDF-SHA512) as defined in RFC 9180. + */ + public static final int KEM_DHKEM_X448_HKDF_SHA512 = 0x21; + + /** + * KDF algorithm identifier for HKDF-SHA256 as defined in RFC 9180. + */ + public static final int KDF_HKDF_SHA256 = 0x1; + + /** + * KDF algorithm identifier for HKDF-SHA384 as defined in RFC 9180. + */ + public static final int KDF_HKDF_SHA384 = 0x2; + + /** + * KDF algorithm identifier for HKDF-SHA512 as defined in RFC 9180. + */ + public static final int KDF_HKDF_SHA512 = 0x3; + + /** + * AEAD algorithm identifier for AES-128-GCM as defined in RFC 9180. + */ + public static final int AEAD_AES_128_GCM = 0x1; + + /** + * AEAD algorithm identifier for AES-256-GCM as defined in RFC 9180. + */ + public static final int AEAD_AES_256_GCM = 0x2; + + /** + * AEAD algorithm identifier for ChaCha20Poly1305 as defined in RFC 9180. + */ + public static final int AEAD_CHACHA20_POLY1305 = 0x3; + + /** + * AEAD algorithm identifier for Export-only as defined in RFC 9180. + */ + public static final int EXPORT_ONLY = 0xffff; + + private final int kem_id; + private final int kdf_id; + private final int aead_id; + private final byte[] info; // never null, can be empty + private final SecretKey psk; // null if not used + private final byte[] psk_id; // never null, can be empty + private final AsymmetricKey kS; // null if not used + private final byte[] encapsulation; // null if none + + // Note: this constructor does not clone array arguments. + private HPKEParameterSpec(int kem_id, int kdf_id, int aead_id, byte[] info, + SecretKey psk, byte[] psk_id, AsymmetricKey kS, byte[] encapsulation) { + this.kem_id = kem_id; + this.kdf_id = kdf_id; + this.aead_id = aead_id; + this.info = info; + this.psk = psk; + this.psk_id = psk_id; + this.kS = kS; + this.encapsulation = encapsulation; + } + + /** + * A factory method to create a new {@code HPKEParameterSpec} object with + * specified KEM, KDF, and AEAD algorithm identifiers in {@code mode_base} + * mode with an empty {@code info}. + * + * @param kem_id algorithm identifier for KEM, must be between 0 and 65535 (inclusive) + * @param kdf_id algorithm identifier for KDF, must be between 0 and 65535 (inclusive) + * @param aead_id algorithm identifier for AEAD, must be between 0 and 65535 (inclusive) + * @return a new {@code HPKEParameterSpec} object + * @throws IllegalArgumentException if any input value + * is out of range (must be between 0 and 65535, inclusive). + */ + public static HPKEParameterSpec of(int kem_id, int kdf_id, int aead_id) { + if (kem_id < 0 || kem_id > 65535) { + throw new IllegalArgumentException("Invalid kem_id: " + kem_id); + } + if (kdf_id < 0 || kdf_id > 65535) { + throw new IllegalArgumentException("Invalid kdf_id: " + kdf_id); + } + if (aead_id < 0 || aead_id > 65535) { + throw new IllegalArgumentException("Invalid aead_id: " + aead_id); + } + return new HPKEParameterSpec(kem_id, kdf_id, aead_id, + new byte[0], null, new byte[0], null, null); + } + + /** + * Creates a new {@code HPKEParameterSpec} object with the specified + * {@code info} value. + *

+ * For interoperability, RFC 9180 Section 7.2.1 recommends limiting + * this value to a maximum of 64 bytes. + * + * @param info application-supplied information. + * The contents of the array are copied to protect + * against subsequent modification. + * @return a new {@code HPKEParameterSpec} object + * @throws NullPointerException if {@code info} is {@code null} + * @throws IllegalArgumentException if {@code info} is empty. + */ + public HPKEParameterSpec withInfo(byte[] info) { + Objects.requireNonNull(info); + if (info.length == 0) { + throw new IllegalArgumentException("info is empty"); + } + return new HPKEParameterSpec(kem_id, kdf_id, aead_id, + info.clone(), psk, psk_id, kS, encapsulation); + } + + /** + * Creates a new {@code HPKEParameterSpec} object with the specified + * {@code psk} and {@code psk_id} values. + *

+ * RFC 9180 Section 5.1.2 requires the PSK MUST have at least 32 bytes + * of entropy. For interoperability, RFC 9180 Section 7.2.1 recommends + * limiting the key size and identifier length to a maximum of 64 bytes. + * + * @param psk pre-shared key + * @param psk_id identifier for PSK. The contents of the array are copied + * to protect against subsequent modification. + * @return a new {@code HPKEParameterSpec} object + * @throws NullPointerException if {@code psk} or {@code psk_id} is {@code null} + * @throws IllegalArgumentException if {@code psk} is shorter than 32 bytes + * or {@code psk_id} is empty + */ + public HPKEParameterSpec withPsk(SecretKey psk, byte[] psk_id) { + Objects.requireNonNull(psk); + Objects.requireNonNull(psk_id); + if (psk_id.length == 0) { + throw new IllegalArgumentException("psk_id is empty"); + } + if ("RAW".equalsIgnoreCase(psk.getFormat())) { + // We can only check when psk is extractable. We can only + // check the length and not the real entropy size + var keyBytes = psk.getEncoded(); + assert keyBytes != null; + Arrays.fill(keyBytes, (byte)0); + if (keyBytes.length < 32) { + throw new IllegalArgumentException("psk is too short"); + } + } + return new HPKEParameterSpec(kem_id, kdf_id, aead_id, + info, psk, psk_id.clone(), kS, encapsulation); + } + + /** + * Creates a new {@code HPKEParameterSpec} object with the specified + * key encapsulation message value that will be used by the recipient. + * + * @param encapsulation the key encapsulation message. + * The contents of the array are copied to protect against + * subsequent modification. + * + * @return a new {@code HPKEParameterSpec} object + * @throws NullPointerException if {@code encapsulation} is {@code null} + */ + public HPKEParameterSpec withEncapsulation(byte[] encapsulation) { + return new HPKEParameterSpec(kem_id, kdf_id, aead_id, + info, psk, psk_id, kS, + Objects.requireNonNull(encapsulation).clone()); + } + + /** + * Creates a new {@code HPKEParameterSpec} object with the specified + * authentication key value. + *

+ * Note: this method does not check whether the KEM algorithm supports + * {@code mode_auth} or {@code mode_auth_psk}. If the resulting object is + * used to initialize an HPKE cipher with an unsupported mode, an + * {@code InvalidAlgorithmParameterException} will be thrown at that time. + * + * @param kS the authentication key + * @return a new {@code HPKEParameterSpec} object + * @throws NullPointerException if {@code kS} is {@code null} + */ + public HPKEParameterSpec withAuthKey(AsymmetricKey kS) { + return new HPKEParameterSpec(kem_id, kdf_id, aead_id, + info, psk, psk_id, + Objects.requireNonNull(kS), + encapsulation); + } + + /** + * {@return the algorithm identifier for KEM } + */ + public int kem_id() { + return kem_id; + } + + /** + * {@return the algorithm identifier for KDF } + */ + public int kdf_id() { + return kdf_id; + } + + /** + * {@return the algorithm identifier for AEAD } + */ + public int aead_id() { + return aead_id; + } + + /** + * {@return a copy of the application-supplied information, empty if none} + */ + public byte[] info() { + return info.clone(); + } + + /** + * {@return pre-shared key, {@code null} if none} + */ + public SecretKey psk() { + return psk; + } + + /** + * {@return a copy of the identifier for PSK, empty if none} + */ + public byte[] psk_id() { + return psk_id.clone(); + } + + /** + * {@return the key for authentication, {@code null} if none} + */ + public AsymmetricKey authKey() { + return kS; + } + + /** + * {@return a copy of the key encapsulation message, {@code null} if none} + */ + public byte[] encapsulation() { + return encapsulation == null ? null : encapsulation.clone(); + } + + @Override + public String toString() { + return "HPKEParameterSpec{" + + "kem_id=" + kem_id + + ", kdf_id=" + kdf_id + + ", aead_id=" + aead_id + + ", info=" + bytesToString(info) + + ", " + (psk == null + ? (kS == null ? "mode_base" : "mode_auth") + : (kS == null ? "mode_psk" : "mode_auth_psk")) + "}"; + } + + // Returns a human-readable representation of a byte array. + private static String bytesToString(byte[] input) { + if (input.length == 0) { + return "(empty)"; + } else { + for (byte b : input) { + if (b < 0x20 || b > 0x7E || b == '"') { + // Non-ASCII or control characters are hard to read, and + // `"` requires character escaping. If any of these are + // present, return only the HEX representation. + return HexFormat.of().formatHex(input); + } + } + // Otherwise, all characters are printable and safe. + // Return both HEX and ASCII representations. + return HexFormat.of().formatHex(input) + + " (\"" + new String(input, StandardCharsets.US_ASCII) + "\")"; + } + } +} diff --git a/src/java.base/share/classes/javax/crypto/spec/snippet-files/PackageSnippets.java b/src/java.base/share/classes/javax/crypto/spec/snippet-files/PackageSnippets.java new file mode 100644 index 00000000000..e4074c1c4a9 --- /dev/null +++ b/src/java.base/share/classes/javax/crypto/spec/snippet-files/PackageSnippets.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +import javax.crypto.Cipher; +import javax.crypto.spec.HPKEParameterSpec; +import java.nio.charset.StandardCharsets; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.util.Arrays; +import java.util.HexFormat; + +class PackageSnippets { + public static void main(String[] args) throws Exception { + + // @start region="hpke-spec-example" + // Recipient key pair generation + KeyPairGenerator g = KeyPairGenerator.getInstance("X25519"); + KeyPair kp = g.generateKeyPair(); + + // The HPKE sender cipher is initialized with the recipient's public + // key and an HPKEParameterSpec using specified algorithm identifiers + // and application-supplied info. + Cipher senderCipher = Cipher.getInstance("HPKE"); + HPKEParameterSpec ps = HPKEParameterSpec.of( + HPKEParameterSpec.KEM_DHKEM_X25519_HKDF_SHA256, + HPKEParameterSpec.KDF_HKDF_SHA256, + HPKEParameterSpec.AEAD_AES_128_GCM) + .withInfo(HexFormat.of().parseHex("010203040506")); + senderCipher.init(Cipher.ENCRYPT_MODE, kp.getPublic(), ps); + + // Retrieve the key encapsulation message (from the KEM step) from + // the sender. + byte[] kemEncap = senderCipher.getIV(); + + // The HPKE recipient cipher is initialized with its own private key, + // an HPKEParameterSpec using the same algorithm identifiers as used by + // the sender, and the key encapsulation message from the sender. + Cipher recipientCipher = Cipher.getInstance("HPKE"); + HPKEParameterSpec pr = HPKEParameterSpec.of( + HPKEParameterSpec.KEM_DHKEM_X25519_HKDF_SHA256, + HPKEParameterSpec.KDF_HKDF_SHA256, + HPKEParameterSpec.AEAD_AES_128_GCM) + .withInfo(HexFormat.of().parseHex("010203040506")) + .withEncapsulation(kemEncap); + recipientCipher.init(Cipher.DECRYPT_MODE, kp.getPrivate(), pr); + + // Encryption and decryption + byte[] msg = "Hello World".getBytes(StandardCharsets.UTF_8); + byte[] ct = senderCipher.doFinal(msg); + byte[] pt = recipientCipher.doFinal(ct); + + assert Arrays.equals(msg, pt); + // @end + } +} diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java index 962a2057585..ec747d06c21 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java @@ -35,6 +35,7 @@ import jdk.internal.constant.ClassOrInterfaceDescImpl; import jdk.internal.constant.PrimitiveClassDescImpl; import jdk.internal.util.ArraysSupport; +import jdk.internal.util.ModifiedUtf; import jdk.internal.vm.annotation.Stable; import static java.util.Objects.requireNonNull; @@ -141,7 +142,7 @@ enum State { RAW, BYTE, CHAR, STRING } @Stable TypeDescriptor typeSym; Utf8EntryImpl(ConstantPool cpm, int index, - byte[] rawBytes, int offset, int rawLen) { + byte[] rawBytes, int offset, int rawLen) { super(cpm, index, 0); this.rawBytes = rawBytes; this.offset = offset; @@ -154,6 +155,10 @@ enum State { RAW, BYTE, CHAR, STRING } } Utf8EntryImpl(ConstantPool cpm, int index, String s, int contentHash) { + // Prevent creation of unwritable entries + if (!ModifiedUtf.isValidLengthInConstantPool(s)) { + throw new IllegalArgumentException("utf8 length out of range of u2: " + ModifiedUtf.utfLen(s)); + } super(cpm, index, 0); this.rawBytes = null; this.offset = 0; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java index b30592a4ebd..6daef5cab9a 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java @@ -277,10 +277,9 @@ void writeUtfEntry(String str) { int strlen = str.length(); int countNonZeroAscii = JLA.countNonZeroAscii(str); long utflenLong = utfLen(str, countNonZeroAscii); - if (!ExactConversionsSupport.isLongToCharExact(utflenLong)) { - throw new IllegalArgumentException("utf8 length out of range of u2: " + utflenLong); - } - int utflen = (int)utflenLong; + // Utf8Entry should always be writable + assert ExactConversionsSupport.isLongToCharExact(utflenLong) : utflenLong; + int utflen = (int) utflenLong; reserveSpace(utflen + 3); int offset = this.offset; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java index 5be14f42baa..e4e67afb17f 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -25,12 +25,11 @@ */ package jdk.internal.classfile.impl; -import java.io.BufferedInputStream; -import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; import java.lang.classfile.ClassHierarchyResolver; +import java.lang.classfile.constantpool.ClassEntry; import java.lang.constant.ClassDesc; import java.util.Collection; import java.util.HashMap; @@ -38,7 +37,6 @@ import java.util.function.Function; import static java.lang.classfile.ClassFile.ACC_INTERFACE; -import static java.lang.classfile.constantpool.PoolEntry.*; import static java.lang.constant.ConstantDescs.CD_Object; import static java.util.Objects.requireNonNull; import static jdk.internal.constant.ConstantUtils.referenceClassDesc; @@ -164,31 +162,12 @@ public ResourceParsingClassHierarchyResolver(Function cl public ClassHierarchyResolver.ClassHierarchyInfo getClassInfo(ClassDesc classDesc) { var ci = streamProvider.apply(classDesc); if (ci == null) return null; - try (var in = new DataInputStream(new BufferedInputStream(ci))) { - in.skipBytes(8); - int cpLength = in.readUnsignedShort(); - String[] cpStrings = new String[cpLength]; - int[] cpClasses = new int[cpLength]; - for (int i = 1; i < cpLength; i++) { - int tag; - switch (tag = in.readUnsignedByte()) { - case TAG_UTF8 -> cpStrings[i] = in.readUTF(); - case TAG_CLASS -> cpClasses[i] = in.readUnsignedShort(); - case TAG_STRING, TAG_METHOD_TYPE, TAG_MODULE, TAG_PACKAGE -> in.skipBytes(2); - case TAG_METHOD_HANDLE -> in.skipBytes(3); - case TAG_INTEGER, TAG_FLOAT, TAG_FIELDREF, TAG_METHODREF, TAG_INTERFACE_METHODREF, - TAG_NAME_AND_TYPE, TAG_DYNAMIC, TAG_INVOKE_DYNAMIC -> in.skipBytes(4); - case TAG_LONG, TAG_DOUBLE -> { - in.skipBytes(8); - i++; - } - default -> throw new IllegalStateException("Bad tag (" + tag + ") at index (" + i + ")"); - } - } - boolean isInterface = (in.readUnsignedShort() & ACC_INTERFACE) != 0; - in.skipBytes(2); - int superIndex = in.readUnsignedShort(); - var superClass = superIndex > 0 ? ClassDesc.ofInternalName(cpStrings[cpClasses[superIndex]]) : null; + try (ci) { + var reader = new ClassReaderImpl(ci.readAllBytes(), ClassFileImpl.DEFAULT_CONTEXT); + boolean isInterface = (reader.flags() & ACC_INTERFACE) != 0; + ClassDesc superClass = reader.superclassEntry() + .map(ClassEntry::asSymbol) + .orElse(null); return new ClassHierarchyInfoImpl(superClass, isInterface); } catch (IOException ioe) { throw new UncheckedIOException(ioe); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/TransformImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/TransformImpl.java index 23387fcb098..4cb0517ec76 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/TransformImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/TransformImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -120,9 +120,9 @@ public ResolvedTransform resolve(ClassBuilder builder) { @Override public ClassTransform andThen(ClassTransform next) { - if (next instanceof ClassMethodTransform cmt) - return new ClassMethodTransform(transform.andThen(cmt.transform), - mm -> filter.test(mm) && cmt.filter.test(mm)); + // Optimized for shared _ -> true filter in ClassTransform.transformingMethods(MethodTransform) + if (next instanceof ClassMethodTransform(var nextTransform, var nextFilter) && filter == nextFilter) + return new ClassMethodTransform(transform.andThen(nextTransform), filter); else return UnresolvedClassTransform.super.andThen(next); } @@ -143,9 +143,9 @@ public ResolvedTransform resolve(ClassBuilder builder) { @Override public ClassTransform andThen(ClassTransform next) { - if (next instanceof ClassFieldTransform cft) - return new ClassFieldTransform(transform.andThen(cft.transform), - mm -> filter.test(mm) && cft.filter.test(mm)); + // Optimized for shared _ -> true filter in ClassTransform.transformingFields(FieldTransform) + if (next instanceof ClassFieldTransform(var nextTransform, var nextFilter) && filter == nextFilter) + return new ClassFieldTransform(transform.andThen(nextTransform), filter); else return UnresolvedClassTransform.super.andThen(next); } @@ -208,8 +208,8 @@ public ResolvedTransform resolve(MethodBuilder builder) { @Override public MethodTransform andThen(MethodTransform next) { - return (next instanceof TransformImpl.MethodCodeTransform mct) - ? new TransformImpl.MethodCodeTransform(xform.andThen(mct.xform)) + return (next instanceof MethodCodeTransform(var nextXform)) + ? new TransformImpl.MethodCodeTransform(xform.andThen(nextXform)) : UnresolvedMethodTransform.super.andThen(next); } diff --git a/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java b/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java index 3edcac2e44c..728ee235547 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java +++ b/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java @@ -212,7 +212,9 @@ private static long allocateNativeInternal(long byteSize, long byteAlignment, Me allocationBase = allocateMemoryWrapper(allocationSize); result = Utils.alignUp(allocationBase, byteAlignment); } else { - allocationSize = alignedSize; + // always allocate at least 'byteAlignment' bytes, so that malloc is guaranteed to + // return a pointer aligned to that alignment, for cases where byteAlignment > alignedSize + allocationSize = Math.max(alignedSize, byteAlignment); if (shouldReserve) { AbstractMemorySegmentImpl.NIO_ACCESS.reserveMemory(allocationSize, byteSize); } diff --git a/src/java.base/share/classes/jdk/internal/util/DateTimeHelper.java b/src/java.base/share/classes/jdk/internal/util/DateTimeHelper.java index bbb0b6738d1..4d9d560fded 100644 --- a/src/java.base/share/classes/jdk/internal/util/DateTimeHelper.java +++ b/src/java.base/share/classes/jdk/internal/util/DateTimeHelper.java @@ -49,24 +49,23 @@ public static void formatTo(StringBuilder buf, LocalDateTime dateTime) { * Requires extra capacity of 10 to avoid StringBuilder reallocation. */ public static void formatTo(StringBuilder buf, LocalDate date) { - int year = date.getYear(), - month = date.getMonthValue(), - day = date.getDayOfMonth(); - int absYear = Math.abs(year); - if (absYear < 1000) { + int year = date.getYear(), + absYear = Math.abs(year); + if (absYear < 10000) { if (year < 0) { buf.append('-'); } - buf.repeat('0', absYear < 10 ? 3 : absYear < 100 ? 2 : 1); - buf.append(absYear); + DecimalDigits.appendQuad(buf, absYear); } else { if (year > 9999) { buf.append('+'); } buf.append(year); } - buf.append(month < 10 ? "-0" : "-").append(month) - .append(day < 10 ? "-0" : "-").append(day); + buf.append('-'); + DecimalDigits.appendPair(buf, date.getMonthValue()); + buf.append('-'); + DecimalDigits.appendPair(buf, date.getDayOfMonth()); } /** @@ -74,14 +73,14 @@ public static void formatTo(StringBuilder buf, LocalDate date) { * Requires extra capacity of 18 to avoid StringBuilder reallocation. */ public static void formatTo(StringBuilder buf, LocalTime time) { - int hour = time.getHour(), - minute = time.getMinute(), - second = time.getSecond(), + DecimalDigits.appendPair(buf, time.getHour()); + buf.append(':'); + DecimalDigits.appendPair(buf, time.getMinute()); + int second = time.getSecond(), nano = time.getNano(); - buf.append(hour < 10 ? "0" : "").append(hour) - .append(minute < 10 ? ":0" : ":").append(minute); if ((second | nano) > 0) { - buf.append(second < 10 ? ":0" : ":").append(second); + buf.append(':'); + DecimalDigits.appendPair(buf, second); if (nano > 0) { buf.append('.'); int zeros = 9 - DecimalDigits.stringSize(nano); diff --git a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java index 6c0c745651e..b55b6ce63b0 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -25,6 +25,8 @@ package jdk.internal.util; +import jdk.internal.access.JavaLangAccess; +import jdk.internal.access.SharedSecrets; import jdk.internal.misc.Unsafe; import jdk.internal.vm.annotation.Stable; @@ -36,6 +38,7 @@ * @since 21 */ public final class DecimalDigits { + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); private static final Unsafe UNSAFE = Unsafe.getUnsafe(); /** @@ -443,4 +446,56 @@ private static void uncheckedPutCharUTF16(byte[] buf, int charPos, int c) { assert charPos >= 0 && charPos < (buf.length >> 1); UNSAFE.putCharUnaligned(buf, ARRAY_BYTE_BASE_OFFSET + ((long) charPos << 1), (char) c); } + + /** + * Appends the two-digit string representation of the {@code int} + * argument to the given {@code StringBuilder}. + *

+ * The integer {@code v} is formatted as two decimal digits. + * Values from 0 to 9 are formatted with a leading zero (e.g., 5 becomes "05"), + * and values from 10 to 99 are formatted as regular two-digit numbers. + * If the value is outside the range 0-99, the behavior is unspecified. + * + * @param buf the {@code StringBuilder} to append to. + * @param v the {@code int} value (should be between 0 and 99 inclusive). + */ + public static void appendPair(StringBuilder buf, int v) { + // The & 0x7f operation keeps the index within the safe range [0, 127] for the DIGITS array, + // which allows the JIT compiler to eliminate array bounds checks for performance. + int packed = DIGITS[v & 0x7f]; + // The temporary String and byte[] objects created here are typically eliminated + // by the JVM's escape analysis and scalar replacement optimizations during + // runtime compilation, avoiding actual heap allocations in optimized code. + buf.append( + JLA.uncheckedNewStringWithLatin1Bytes( + new byte[] {(byte) packed, (byte) (packed >> 8)})); + } + + /** + * Appends the four-digit string representation of the {@code int} + * argument to the given {@code StringBuilder}. + *

+ * The integer {@code v} is formatted as four decimal digits. + * Values from 0 to 9 are formatted with leading zeros (e.g., 5 becomes "0005"), + * values from 10 to 99 add two leading zeros (e.g., 25 becomes "0025"), + * values from 100 to 999 add one leading zero (e.g., 123 becomes "0123"), + * and values from 1000 to 9999 have no leading zeros. + * If the value is outside the range 0-9999, the behavior is unspecified. + * + * @param buf the {@code StringBuilder} to append to. + * @param v the {@code int} value (should be between 0 and 9999 inclusive). + */ + public static void appendQuad(StringBuilder buf, int v) { + // The & 0x7f operation keeps the index within the safe range [0, 127] for the DIGITS array, + // which allows the JIT compiler to eliminate array bounds checks for performance. + int packedHigh = DIGITS[(v / 100) & 0x7f]; + int packedLow = DIGITS[(v % 100) & 0x7f]; + // The temporary String and byte[] objects created here are typically eliminated + // by the JVM's escape analysis and scalar replacement optimizations during + // runtime compilation, avoiding actual heap allocations in optimized code. + buf.append( + JLA.uncheckedNewStringWithLatin1Bytes( + new byte[] {(byte) packedHigh, (byte) (packedHigh >> 8), + (byte) packedLow, (byte) (packedLow >> 8)})); + } } diff --git a/src/java.base/share/classes/jdk/internal/util/ModifiedUtf.java b/src/java.base/share/classes/jdk/internal/util/ModifiedUtf.java index 46885e12adf..4221ec22de3 100644 --- a/src/java.base/share/classes/jdk/internal/util/ModifiedUtf.java +++ b/src/java.base/share/classes/jdk/internal/util/ModifiedUtf.java @@ -28,19 +28,17 @@ import jdk.internal.vm.annotation.ForceInline; -/** - * Helper to JDK UTF putChar and Calculate length - * - * @since 24 - */ -public abstract class ModifiedUtf { - // Maximum number of bytes allowed for a Modified UTF-8 encoded string - // in a ClassFile constant pool entry (CONSTANT_Utf8_info). +/// Utilities for string encoding and decoding with the +/// [Modified UTF-8][java.io.DataInput##modified-utf-8] format. +public final class ModifiedUtf { + /// Maximum number of bytes allowed for a Modified UTF-8 encoded string + /// in a [java.lang.classfile.constantpool.Utf8Entry] or a hotspot `Symbol`. public static final int CONSTANT_POOL_UTF8_MAX_BYTES = 65535; private ModifiedUtf() { } + /// Writes a char to the pre-sized modified UTF buffer. @ForceInline public static int putChar(byte[] buf, int offset, char c) { if (c != 0 && c < 0x80) { @@ -58,11 +56,23 @@ public static int putChar(byte[] buf, int offset, char c) { return offset; } - /** - * Calculate the utf length of a string - * @param str input string - * @param countNonZeroAscii the number of non-zero ascii characters in the prefix calculated by JLA.countNonZeroAscii(str) - */ + /// Calculate the encoded length of an input String. + /// For many workloads that have fast paths for ASCII-only prefixes, + /// [#utfLen(String, int)] skips scanning that prefix. + /// + /// @param str input string + public static long utfLen(String str) { + return utfLen(str, 0); + } + + /// Calculate the encoded length of trailing parts of an input String, + /// after [jdk.internal.access.JavaLangAccess#countNonZeroAscii(String)] + /// calculates the number of contiguous single-byte characters in the + /// beginning of the string. + /// + /// @param str input string + /// @param countNonZeroAscii the number of non-zero ascii characters in the + /// prefix calculated by JLA.countNonZeroAscii(str) @ForceInline public static long utfLen(String str, int countNonZeroAscii) { long utflen = str.length(); @@ -74,11 +84,11 @@ public static long utfLen(String str, int countNonZeroAscii) { return utflen; } - /** - * Checks whether the Modified UTF-8 encoded length of the given string - * fits within the ClassFile constant pool limit (u2 length = 65535 bytes). - * @param str the string to check - */ + /// Checks whether an input String can be encoded in a + /// [java.lang.classfile.constantpool.Utf8Entry], or represented as a + /// hotspot `Symbol` (which has the same length limit). + /// + /// @param str input string @ForceInline public static boolean isValidLengthInConstantPool(String str) { // Quick approximation: each char can be at most 3 bytes in Modified UTF-8. @@ -91,7 +101,7 @@ public static boolean isValidLengthInConstantPool(String str) { return false; } // Check exact Modified UTF-8 length. - long utfLen = utfLen(str, 0); + long utfLen = utfLen(str); return utfLen <= CONSTANT_POOL_UTF8_MAX_BYTES; } } diff --git a/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java b/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java index bd5ea6d7635..b3671d19333 100644 --- a/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java +++ b/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java @@ -63,33 +63,24 @@ public static Class parseClass(String descriptor, ClassLoader loader) { /// @throws TypeNotPresentException if a reference type cannot be found by /// the loader (before the descriptor is found invalid) public static List> parseMethod(String descriptor, ClassLoader loader) { - return parseMethod(descriptor, 0, descriptor.length(), loader); - } - - /** - * @param loader the class loader in which to look up the types (null means - * bootstrap class loader) - */ - static List> parseMethod(String bytecodeSignature, - int start, int end, ClassLoader loader) { - String str = bytecodeSignature; - int[] i = {start}; + int end = descriptor.length(); // implicit null check + int[] i = {0}; var ptypes = new ArrayList>(); - if (i[0] < end && str.charAt(i[0]) == '(') { + if (i[0] < end && descriptor.charAt(i[0]) == '(') { ++i[0]; // skip '(' - while (i[0] < end && str.charAt(i[0]) != ')') { - Class pt = parseSig(str, i, end, loader); + while (i[0] < end && descriptor.charAt(i[0]) != ')') { + Class pt = parseSig(descriptor, i, end, loader); if (pt == null || pt == void.class) - parseError(str, "bad argument type"); + parseError(descriptor, "bad argument type"); ptypes.add(pt); } ++i[0]; // skip ')' } else { - parseError(str, "not a method type"); + parseError(descriptor, "not a method type"); } - Class rtype = parseSig(str, i, end, loader); + Class rtype = parseSig(descriptor, i, end, loader); if (rtype == null || i[0] != end) - parseError(str, "bad return type"); + parseError(descriptor, "bad return type"); ptypes.add(rtype); return ptypes; } @@ -115,8 +106,22 @@ private static Class parseSig(String str, int[] i, int end, ClassLoader loade if (i[0] == end) return null; char c = str.charAt(i[0]++); if (c == 'L') { - int begc = i[0], endc = str.indexOf(';', begc); - if (endc < 0) return null; + int begc = i[0]; + int identifierStart = begc; + int endc; + while (true) { + int next = nextNonIdentifier(str, identifierStart, end); + if (identifierStart == next || next >= end) return null; // Empty name segment, or the end + char ch = str.charAt(next); + if (ch == ';') { + endc = next; + break; + } else if (ch == '/') { + identifierStart = next + 1; // Next name segment + } else { + return null; // Bad char [ or . + } + } i[0] = endc+1; String name = str.substring(begc, endc).replace('/', '.'); try { @@ -148,6 +153,23 @@ private static Class parseSig(String str, int[] i, int end, ClassLoader loade } } + private static final int CHECK_OFFSET = 32; + private static final long NON_IDENTIFIER_MASK = (1L << ('.' - CHECK_OFFSET)) + | (1L << ('/' - CHECK_OFFSET)) + | (1L << (';' - CHECK_OFFSET)) + | (1L << ('[' - CHECK_OFFSET)); + + private static int nextNonIdentifier(String str, int index, int end) { + while (index < end) { + int check = str.charAt(index) - CHECK_OFFSET; + if ((check & -Long.SIZE) == 0 && (NON_IDENTIFIER_MASK & (1L << check)) != 0) { + break; + } + index++; + } + return index; + } + public static String unparse(Class type) { if (type == Object.class) { return "Ljava/lang/Object;"; diff --git a/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java b/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java index 3e771c015f4..73ca0c6bf16 100644 --- a/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java +++ b/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -627,6 +627,10 @@ public void engineStore(OutputStream stream, char[] password) dos.write(digest); dos.flush(); + + if (debug != null) { + emitWeakKeyStoreWarning(); + } } } @@ -790,6 +794,10 @@ public void engineLoad(InputStream stream, char[] password) privateKeyCount + ". trusted key count: " + trustedKeyCount); } + if (debug != null) { + emitWeakKeyStoreWarning(); + } + /* * If a password has been provided, we check the keyed digest * at the end. If this check fails, the store has been tampered @@ -838,4 +846,16 @@ private byte[] convertToBytes(char[] password) { } return passwdBytes; } + + private void emitWeakKeyStoreWarning() { + String type = this.getClass().getSimpleName(). + toUpperCase(Locale.ROOT); + if (type.equals("JKS")){ + debug.println("WARNING: JKS uses outdated cryptographic " + + "algorithms and will be removed in a future " + + "release. Migrate to PKCS12 using:"); + debug.println("keytool -importkeystore -srckeystore " + + "-destkeystore -deststoretype pkcs12"); + } + } } diff --git a/src/java.base/share/classes/sun/security/ssl/Alert.java b/src/java.base/share/classes/sun/security/ssl/Alert.java index ed5e079bf44..d172206326f 100644 --- a/src/java.base/share/classes/sun/security/ssl/Alert.java +++ b/src/java.base/share/classes/sun/security/ssl/Alert.java @@ -238,7 +238,7 @@ public void consume(ConnectionContext context, TransportContext tc = (TransportContext)context; AlertMessage am = new AlertMessage(tc, m); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("Received alert message", am); } diff --git a/src/java.base/share/classes/sun/security/ssl/AlpnExtension.java b/src/java.base/share/classes/sun/security/ssl/AlpnExtension.java index aa5933ddab0..f03a65c8410 100644 --- a/src/java.base/share/classes/sun/security/ssl/AlpnExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/AlpnExtension.java @@ -157,7 +157,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(SSLExtension.CH_ALPN)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.info( "Ignore client unavailable extension: " + SSLExtension.CH_ALPN.name); @@ -170,7 +170,7 @@ public byte[] produce(ConnectionContext context, String[] laps = chc.sslConfig.applicationProtocols; if ((laps == null) || (laps.length == 0)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.info( "No available application protocols"); } @@ -183,7 +183,7 @@ public byte[] produce(ConnectionContext context, int length = ap.getBytes(alpnCharset).length; if (length == 0) { // log the configuration problem - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.severe( "Application protocol name cannot be empty"); } @@ -197,7 +197,7 @@ public byte[] produce(ConnectionContext context, listLength += (length + 1); } else { // log the configuration problem - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.severe( "Application protocol name (" + ap + ") exceeds the size limit (" + @@ -212,7 +212,7 @@ public byte[] produce(ConnectionContext context, if (listLength > MAX_AP_LIST_LENGTH) { // log the configuration problem - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.severe( "The configured application protocols (" + Arrays.toString(laps) + @@ -266,7 +266,7 @@ public void consume(ConnectionContext context, if (!shc.sslConfig.isAvailable(SSLExtension.CH_ALPN)) { shc.applicationProtocol = ""; shc.conContext.applicationProtocol = ""; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.info( "Ignore server unavailable extension: " + SSLExtension.CH_ALPN.name); @@ -288,7 +288,7 @@ public void consume(ConnectionContext context, if (noAPSelector && noAlpnProtocols) { shc.applicationProtocol = ""; shc.conContext.applicationProtocol = ""; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore server unenabled extension: " + SSLExtension.CH_ALPN.name); @@ -378,7 +378,7 @@ public byte[] produce(ConnectionContext context, (AlpnSpec)shc.handshakeExtensions.get(SSLExtension.CH_ALPN); if (requestedAlps == null) { // Ignore, this extension was not requested and accepted. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extension: " + SSLExtension.SH_ALPN.name); @@ -423,7 +423,7 @@ public byte[] produce(ConnectionContext context, // Ignore, no negotiated application layer protocol. shc.applicationProtocol = ""; shc.conContext.applicationProtocol = ""; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore, no negotiated application layer protocol"); } diff --git a/src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java b/src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java index 2125a148162..2d03d5fef98 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java @@ -94,7 +94,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable( SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable " + "signature_algorithms_cert extension"); @@ -144,7 +144,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable( SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable " + "signature_algorithms_cert extension"); @@ -235,7 +235,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable( SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable " + "signature_algorithms_cert extension"); @@ -283,7 +283,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable( SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable " + "signature_algorithms_cert extension"); diff --git a/src/java.base/share/classes/sun/security/ssl/CertStatusExtension.java b/src/java.base/share/classes/sun/security/ssl/CertStatusExtension.java index 49713b6db11..d6c1cec5735 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertStatusExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/CertStatusExtension.java @@ -144,7 +144,7 @@ private CertStatusRequestSpec(HandshakeContext hc, if (statusType == CertStatusRequestType.OCSP.id) { this.statusRequest = new OCSPStatusRequest(statusType, encoded); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.info( "Unknown certificate status request " + "(status type: " + statusType + ")"); @@ -196,7 +196,7 @@ private CertStatusResponseSpec(HandshakeContext hc, if (type == CertStatusRequestType.OCSP.id) { this.statusResponse = new OCSPStatusResponse(type, respData); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.info( "Unknown certificate status response " + "(status type: " + type + ")"); @@ -557,7 +557,7 @@ public byte[] produce(ConnectionContext context, } if (!chc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extension: " + SSLExtension.CH_STATUS_REQUEST.name); @@ -598,7 +598,7 @@ public void consume(ConnectionContext context, ServerHandshakeContext shc = (ServerHandshakeContext)context; if (!shc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Ignore unavailable extension: " + SSLExtension.CH_STATUS_REQUEST.name); } @@ -656,7 +656,7 @@ public byte[] produce(ConnectionContext context, shc.handshakeExtensions.get(SSLExtension.CH_STATUS_REQUEST); if (spec == null) { // Ignore, no status_request extension requested. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest("Ignore unavailable extension: " + SSLExtension.CH_STATUS_REQUEST.name); } @@ -666,7 +666,7 @@ public byte[] produce(ConnectionContext context, // Is it a session resuming? if (shc.isResumption) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "No status_request response for session resuming"); } @@ -839,7 +839,7 @@ private CertStatusRequestV2Spec(HandshakeContext hc, statusRequests.add( new OCSPStatusRequest(statusType, encoded)); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.info( "Unknown certificate status request " + "(status type: " + statusType + ")"); @@ -915,7 +915,7 @@ public byte[] produce(ConnectionContext context, } if (!chc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST_V2)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "Ignore unavailable status_request_v2 extension"); } @@ -957,7 +957,7 @@ public void consume(ConnectionContext context, ServerHandshakeContext shc = (ServerHandshakeContext)context; if (!shc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST_V2)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "Ignore unavailable status_request_v2 extension"); } @@ -1017,7 +1017,7 @@ public byte[] produce(ConnectionContext context, shc.handshakeExtensions.get(SSLExtension.CH_STATUS_REQUEST_V2); if (spec == null) { // Ignore, no status_request_v2 extension requested. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "Ignore unavailable status_request_v2 extension"); } @@ -1027,7 +1027,7 @@ public byte[] produce(ConnectionContext context, // Is it a session resuming? if (shc.isResumption) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "No status_request_v2 response for session resumption"); } @@ -1112,7 +1112,7 @@ public byte[] produce(ConnectionContext context, // Stapling needs to be active and have valid data to proceed if (shc.stapleParams == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "Stapling is disabled for this connection"); } @@ -1121,7 +1121,7 @@ public byte[] produce(ConnectionContext context, // There needs to be a non-null CertificateEntry to proceed if (shc.currentCertEntry == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest("Found null CertificateEntry in context"); } return null; @@ -1139,7 +1139,7 @@ public byte[] produce(ConnectionContext context, byte[] respBytes = shc.stapleParams.responseMap.get(x509Cert); if (respBytes == null) { // We're done with this entry. Clear it from the context - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest("No status response found for " + x509Cert.getSubjectX500Principal()); @@ -1149,7 +1149,7 @@ public byte[] produce(ConnectionContext context, } // Build a proper response buffer from the stapling information - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest("Found status response for " + x509Cert.getSubjectX500Principal() + ", response length: " + respBytes.length); @@ -1208,7 +1208,7 @@ public void consume(ConnectionContext context, respList.add(spec.statusResponse.encodedResponse); chc.handshakeSession.setStatusResponses(respList); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Ignoring stapled data on resumed session"); } diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateAuthoritiesExtension.java b/src/java.base/share/classes/sun/security/ssl/CertificateAuthoritiesExtension.java index 43bac16f0ea..cc513eb30ba 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertificateAuthoritiesExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateAuthoritiesExtension.java @@ -192,7 +192,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable( SSLExtension.CH_CERTIFICATE_AUTHORITIES)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable " + "certificate_authorities extension"); @@ -205,7 +205,7 @@ public byte[] produce(ConnectionContext context, X509Certificate[] caCerts = chc.sslContext.getX509TrustManager().getAcceptedIssuers(); if (caCerts.length == 0) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "No available certificate authorities"); } @@ -216,7 +216,7 @@ public byte[] produce(ConnectionContext context, List encodedCAs = CertificateAuthoritiesSpec.getEncodedAuthorities(caCerts); if (encodedCAs.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "The number of CAs exceeds the maximum size " + "of the certificate_authorities extension"); @@ -270,7 +270,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable( SSLExtension.CH_CERTIFICATE_AUTHORITIES)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable " + "certificate_authorities extension"); @@ -319,7 +319,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable( SSLExtension.CR_CERTIFICATE_AUTHORITIES)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable " + "certificate_authorities extension"); @@ -332,7 +332,7 @@ public byte[] produce(ConnectionContext context, X509Certificate[] caCerts = shc.sslContext.getX509TrustManager().getAcceptedIssuers(); if (caCerts.length == 0) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "No available certificate authorities"); } @@ -343,7 +343,7 @@ public byte[] produce(ConnectionContext context, List encodedCAs = CertificateAuthoritiesSpec.getEncodedAuthorities(caCerts); if (encodedCAs.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Too many certificate authorities to use " + "the certificate_authorities extension"); @@ -397,7 +397,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable( SSLExtension.CR_CERTIFICATE_AUTHORITIES)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable " + "certificate_authorities extension"); diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java index d4587d35ae9..2a2db34cab9 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java @@ -265,7 +265,7 @@ private byte[] onProduceCertificate(ServerHandshakeContext shc, shc.handshakeSession.setLocalCertificates(x509Possession.popCerts); T12CertificateMessage cm = new T12CertificateMessage(shc, x509Possession.popCerts); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced server Certificate handshake message", cm); } @@ -293,7 +293,7 @@ private byte[] onProduceCertificate(ClientHandshakeContext chc, // an empty cert chain instead. if (x509Possession == null) { if (chc.negotiatedProtocol.useTLS10PlusSpec()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "No X.509 certificate for client authentication, " + "use empty Certificate message instead"); @@ -302,7 +302,7 @@ private byte[] onProduceCertificate(ClientHandshakeContext chc, x509Possession = new X509Possession(null, new X509Certificate[0]); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "No X.509 certificate for client authentication, " + "send a no_certificate alert"); @@ -324,7 +324,7 @@ private byte[] onProduceCertificate(ClientHandshakeContext chc, } T12CertificateMessage cm = new T12CertificateMessage(chc, x509Possession.popCerts); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced client Certificate handshake message", cm); } @@ -360,13 +360,13 @@ public void consume(ConnectionContext context, T12CertificateMessage cm = new T12CertificateMessage(hc, message); if (hc.sslConfig.isClientMode) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming server Certificate handshake message", cm); } onCertificate((ClientHandshakeContext)context, cm); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming client Certificate handshake message", cm); } @@ -501,7 +501,7 @@ private static boolean isIdentityEquivalent(X509Certificate thisCert, try { thisSubjectAltNames = thisCert.getSubjectAlternativeNames(); } catch (CertificateParsingException cpe) { - if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("handshake")) { SSLLogger.fine( "Attempt to obtain subjectAltNames extension failed!"); } @@ -511,7 +511,7 @@ private static boolean isIdentityEquivalent(X509Certificate thisCert, try { prevSubjectAltNames = prevCert.getSubjectAlternativeNames(); } catch (CertificateParsingException cpe) { - if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("handshake")) { SSLLogger.fine( "Attempt to obtain subjectAltNames extension failed!"); } @@ -980,7 +980,7 @@ private byte[] onProduceCertificate(ServerHandshakeContext shc, certEnt.extensions.produce(shc, enabledCTExts); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced server Certificate message", cm); } @@ -997,7 +997,7 @@ private static SSLPossession choosePossession( ClientHelloMessage clientHello) { if (hc.peerRequestedCertSignSchemes == null || hc.peerRequestedCertSignSchemes.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "No signature_algorithms(_cert) in ClientHello"); } @@ -1021,7 +1021,7 @@ private static SSLPossession choosePossession( SSLPossession pos = X509Authentication .createPossession(hc, supportedKeyTypes); if (pos == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("No available authentication scheme"); } } @@ -1034,14 +1034,14 @@ private byte[] onProduceCertificate(ClientHandshakeContext chc, SSLPossession pos = choosePossession(chc, clientHello); X509Certificate[] localCerts; if (pos == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("No available client authentication scheme"); } localCerts = new X509Certificate[0]; } else { chc.handshakePossessions.add(pos); if (!(pos instanceof X509Possession x509Possession)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "No X.509 certificate for client authentication"); } @@ -1067,7 +1067,7 @@ private byte[] onProduceCertificate(ClientHandshakeContext chc, throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, "Failed to produce client Certificate message", ce); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced client Certificate message", cm); } @@ -1108,13 +1108,13 @@ public void consume(ConnectionContext context, T13CertificateMessage cm = new T13CertificateMessage(hc, message); if (hc.sslConfig.isClientMode) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming server Certificate handshake message", cm); } onConsumeCertificate((ClientHandshakeContext)context, cm); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming client Certificate handshake message", cm); } diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java index 66b8c048703..a297d9d21b2 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java @@ -297,7 +297,7 @@ public byte[] produce(ConnectionContext context, shc.sslContext.getX509TrustManager().getAcceptedIssuers(); T10CertificateRequestMessage crm = new T10CertificateRequestMessage( shc, caCerts, shc.negotiatedCipherSuite.keyExchange); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced CertificateRequest handshake message", crm); } @@ -360,7 +360,7 @@ public void consume(ConnectionContext context, T10CertificateRequestMessage crm = new T10CertificateRequestMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming CertificateRequest handshake message", crm); } @@ -400,7 +400,7 @@ public void consume(ConnectionContext context, } if (clientAlias == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("No available client authentication"); } return; @@ -408,7 +408,7 @@ public void consume(ConnectionContext context, PrivateKey clientPrivateKey = km.getPrivateKey(clientAlias); if (clientPrivateKey == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("No available client private key"); } return; @@ -416,7 +416,7 @@ public void consume(ConnectionContext context, X509Certificate[] clientCerts = km.getCertificateChain(clientAlias); if ((clientCerts == null) || (clientCerts.length == 0)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("No available client certificate"); } return; @@ -655,7 +655,7 @@ public byte[] produce(ConnectionContext context, T12CertificateRequestMessage crm = new T12CertificateRequestMessage( shc, caCerts, shc.negotiatedCipherSuite.keyExchange, certReqSignAlgs); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced CertificateRequest handshake message", crm); } @@ -717,7 +717,7 @@ public void consume(ConnectionContext context, T12CertificateRequestMessage crm = new T12CertificateRequestMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming CertificateRequest handshake message", crm); } @@ -784,7 +784,7 @@ private static SSLPossession choosePossession(HandshakeContext hc, T12CertificateRequestMessage crm) { if (hc.peerRequestedCertSignSchemes == null || hc.peerRequestedCertSignSchemes.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("No signature and hash algorithms " + "in CertificateRequest"); } @@ -823,7 +823,7 @@ private static SSLPossession choosePossession(HandshakeContext hc, SSLPossession pos = X509Authentication .createPossession(hc, supportedKeyTypes); if (pos == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("No available authentication scheme"); } } @@ -933,7 +933,7 @@ public byte[] produce(ConnectionContext context, SSLExtension[] extTypes = shc.sslConfig.getEnabledExtensions( SSLHandshake.CERTIFICATE_REQUEST, shc.negotiatedProtocol); crm.extensions.produce(shc, extTypes); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced CertificateRequest message", crm); } @@ -985,7 +985,7 @@ public void consume(ConnectionContext context, T13CertificateRequestMessage crm = new T13CertificateRequestMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming CertificateRequest handshake message", crm); } diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateStatus.java b/src/java.base/share/classes/sun/security/ssl/CertificateStatus.java index 11b2c5e587d..a1048e423d1 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertificateStatus.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateStatus.java @@ -281,7 +281,7 @@ public void consume(ConnectionContext context, new CertificateStatusMessage(chc, message); // Log the message - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming server CertificateStatus handshake message", cst); @@ -325,7 +325,7 @@ public byte[] produce(ConnectionContext context, // Create the CertificateStatus message from info in the CertificateStatusMessage csm = new CertificateStatusMessage(shc); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced server CertificateStatus handshake message", csm); } @@ -358,7 +358,7 @@ public void absent(ConnectionContext context, // status_request[_v2] extension. 2) The CertificateStatus // message was not sent. This means that cert path checking // was deferred, but must happen immediately. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Server did not send CertificateStatus, " + "checking cert chain without status info."); } diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java b/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java index 09d07d8e62d..18ea2b9c3de 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java @@ -248,7 +248,7 @@ public byte[] produce(ConnectionContext context, if (x509Possession == null || x509Possession.popPrivateKey == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "No X.509 credentials negotiated for CertificateVerify"); } @@ -258,7 +258,7 @@ public byte[] produce(ConnectionContext context, S30CertificateVerifyMessage cvm = new S30CertificateVerifyMessage(chc, x509Possession); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced CertificateVerify handshake message", cvm); } @@ -300,7 +300,7 @@ public void consume(ConnectionContext context, S30CertificateVerifyMessage cvm = new S30CertificateVerifyMessage(shc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming CertificateVerify handshake message", cvm); } @@ -503,7 +503,7 @@ public byte[] produce(ConnectionContext context, if (x509Possession == null || x509Possession.popPrivateKey == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "No X.509 credentials negotiated for CertificateVerify"); } @@ -513,7 +513,7 @@ public byte[] produce(ConnectionContext context, T10CertificateVerifyMessage cvm = new T10CertificateVerifyMessage(chc, x509Possession); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced CertificateVerify handshake message", cvm); } @@ -555,7 +555,7 @@ public void consume(ConnectionContext context, T10CertificateVerifyMessage cvm = new T10CertificateVerifyMessage(shc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming CertificateVerify handshake message", cvm); } @@ -754,7 +754,7 @@ public byte[] produce(ConnectionContext context, if (x509Possession == null || x509Possession.popPrivateKey == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "No X.509 credentials negotiated for CertificateVerify"); } @@ -764,7 +764,7 @@ public byte[] produce(ConnectionContext context, T12CertificateVerifyMessage cvm = new T12CertificateVerifyMessage(chc, x509Possession); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced CertificateVerify handshake message", cvm); } @@ -806,7 +806,7 @@ public void consume(ConnectionContext context, T12CertificateVerifyMessage cvm = new T12CertificateVerifyMessage(shc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming CertificateVerify handshake message", cvm); } @@ -1092,7 +1092,7 @@ public byte[] produce(ConnectionContext context, if (x509Possession == null || x509Possession.popPrivateKey == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "No X.509 credentials negotiated for CertificateVerify"); } @@ -1113,7 +1113,7 @@ private byte[] onProduceCertificateVerify(ServerHandshakeContext shc, X509Possession x509Possession) throws IOException { T13CertificateVerifyMessage cvm = new T13CertificateVerifyMessage(shc, x509Possession); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced server CertificateVerify handshake message", cvm); } @@ -1130,7 +1130,7 @@ private byte[] onProduceCertificateVerify(ClientHandshakeContext chc, X509Possession x509Possession) throws IOException { T13CertificateVerifyMessage cvm = new T13CertificateVerifyMessage(chc, x509Possession); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced client CertificateVerify handshake message", cvm); } @@ -1173,7 +1173,7 @@ public void consume(ConnectionContext context, T13CertificateVerifyMessage cvm = new T13CertificateVerifyMessage(hc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming CertificateVerify handshake message", cvm); } diff --git a/src/java.base/share/classes/sun/security/ssl/ChangeCipherSpec.java b/src/java.base/share/classes/sun/security/ssl/ChangeCipherSpec.java index 4ea61161c1d..d3eac8f13af 100644 --- a/src/java.base/share/classes/sun/security/ssl/ChangeCipherSpec.java +++ b/src/java.base/share/classes/sun/security/ssl/ChangeCipherSpec.java @@ -108,7 +108,7 @@ public byte[] produce(ConnectionContext context, ") and protocol version (" + hc.negotiatedProtocol + ")"); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced ChangeCipherSpec message"); } @@ -142,7 +142,7 @@ public void consume(ConnectionContext context, throw tc.fatal(Alert.UNEXPECTED_MESSAGE, "Malformed or unexpected ChangeCipherSpec message"); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Consuming ChangeCipherSpec message"); } @@ -237,7 +237,7 @@ public void consume(ConnectionContext context, throw tc.fatal(Alert.UNEXPECTED_MESSAGE, "Malformed or unexpected ChangeCipherSpec message"); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Consuming ChangeCipherSpec message"); } diff --git a/src/java.base/share/classes/sun/security/ssl/ClientHello.java b/src/java.base/share/classes/sun/security/ssl/ClientHello.java index c9432ea3979..421673d625d 100644 --- a/src/java.base/share/classes/sun/security/ssl/ClientHello.java +++ b/src/java.base/share/classes/sun/security/ssl/ClientHello.java @@ -430,7 +430,7 @@ public byte[] produce(ConnectionContext context) throws IOException { if (!session.isRejoinable()) { session = null; - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, the session is not rejoinable"); @@ -443,7 +443,7 @@ public byte[] produce(ConnectionContext context) throws IOException { sessionSuite = session.getSuite(); if (!chc.isNegotiable(sessionSuite)) { session = null; - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, unavailable session cipher suite"); @@ -456,7 +456,7 @@ public byte[] produce(ConnectionContext context) throws IOException { sessionVersion = session.getProtocolVersion(); if (!chc.isNegotiable(sessionVersion)) { session = null; - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, unavailable protocol version"); @@ -513,7 +513,7 @@ public byte[] produce(ConnectionContext context) throws IOException { String sessionIdentityAlg = session.getIdentificationProtocol(); if (!identityAlg.equalsIgnoreCase(sessionIdentityAlg)) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest("Can't resume, endpoint id" + " algorithm does not match, requested: " + @@ -524,7 +524,7 @@ public byte[] produce(ConnectionContext context) throws IOException { } if (session != null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest("Try resuming session", session); } @@ -547,7 +547,7 @@ public byte[] produce(ConnectionContext context) throws IOException { cipherSuites = List.of(sessionSuite); } - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "No new session is allowed, so try to resume " + @@ -634,7 +634,7 @@ public byte[] produce(ConnectionContext context) throws IOException { SSLHandshake.CLIENT_HELLO, chc.activeProtocols); chm.extensions.produce(chc, extTypes); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced ClientHello handshake message", chm); } @@ -700,7 +700,7 @@ public byte[] produce(ConnectionContext context, // // The HelloVerifyRequest consumer should have updated the // ClientHello handshake message with cookie. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced ClientHello(cookie) handshake message", chc.initialClientHelloMsg); @@ -734,7 +734,7 @@ public byte[] produce(ConnectionContext context, // TLS 1.3 // The HelloRetryRequest consumer should have updated the // ClientHello handshake message with cookie. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced ClientHello(HRR) handshake message", chc.initialClientHelloMsg); @@ -790,7 +790,7 @@ public void consume(ConnectionContext context, ClientHelloMessage chm = new ClientHelloMessage(shc, message, enabledExtensions); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Consuming ClientHello handshake message", chm); } @@ -820,7 +820,7 @@ private void onClientHello(ServerHandshakeContext context, negotiateProtocol(context, clientHello.clientVersion); } context.negotiatedProtocol = negotiatedProtocol; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Negotiated protocol version: " + negotiatedProtocol.name); } @@ -980,7 +980,7 @@ public void consume(ConnectionContext context, boolean resumingSession = (previous != null) && previous.isRejoinable(); if (!resumingSession) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, " + @@ -993,7 +993,7 @@ public void consume(ConnectionContext context, previous.getProtocolVersion(); if (sessionProtocol != shc.negotiatedProtocol) { resumingSession = false; - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, not the same protocol version"); @@ -1008,7 +1008,7 @@ public void consume(ConnectionContext context, previous.getPeerPrincipal(); } catch (SSLPeerUnverifiedException e) { resumingSession = false; - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, " + @@ -1023,7 +1023,7 @@ public void consume(ConnectionContext context, if ((!shc.isNegotiable(suite)) || (!clientHello.cipherSuites.contains(suite))) { resumingSession = false; - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, " + @@ -1039,7 +1039,7 @@ public void consume(ConnectionContext context, String sessionIdentityAlg = previous.getIdentificationProtocol(); if (!identityAlg.equalsIgnoreCase(sessionIdentityAlg)) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest("Can't resume, endpoint id" + " algorithm does not match, requested: " + @@ -1054,7 +1054,7 @@ public void consume(ConnectionContext context, shc.isResumption = resumingSession; shc.resumingSession = resumingSession ? previous : null; - if (!resumingSession && SSLLogger.isOn && + if (!resumingSession && SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Session not resumed."); } @@ -1321,7 +1321,7 @@ public void consume(ConnectionContext context, boolean resumingSession = (previous != null) && previous.isRejoinable(); if (!resumingSession) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, " + @@ -1334,7 +1334,7 @@ public void consume(ConnectionContext context, previous.getProtocolVersion(); if (sessionProtocol != shc.negotiatedProtocol) { resumingSession = false; - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, not the same protocol version"); @@ -1350,7 +1350,7 @@ public void consume(ConnectionContext context, previous.getPeerPrincipal(); } catch (SSLPeerUnverifiedException e) { resumingSession = false; - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, " + @@ -1365,7 +1365,7 @@ public void consume(ConnectionContext context, if ((!shc.isNegotiable(suite)) || (!clientHello.cipherSuites.contains(suite))) { resumingSession = false; - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, " + diff --git a/src/java.base/share/classes/sun/security/ssl/CookieExtension.java b/src/java.base/share/classes/sun/security/ssl/CookieExtension.java index d54a1a3e63d..2c22dd121ba 100644 --- a/src/java.base/share/classes/sun/security/ssl/CookieExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/CookieExtension.java @@ -117,7 +117,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(SSLExtension.CH_COOKIE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable cookie extension"); } @@ -154,7 +154,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(SSLExtension.CH_COOKIE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable cookie extension"); } @@ -218,7 +218,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(SSLExtension.HRR_COOKIE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable cookie extension"); } @@ -253,7 +253,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(SSLExtension.HRR_COOKIE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable cookie extension"); } @@ -280,7 +280,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(SSLExtension.HRR_COOKIE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable cookie extension"); } diff --git a/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java index fb5d6feef55..53f9896a3e4 100644 --- a/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java @@ -187,7 +187,7 @@ public byte[] produce(ConnectionContext context, chc.handshakePossessions.add(dhePossession); DHClientKeyExchangeMessage ckem = new DHClientKeyExchangeMessage(chc); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced DH ClientKeyExchange handshake message", ckem); } @@ -268,7 +268,7 @@ public void consume(ConnectionContext context, DHClientKeyExchangeMessage ckem = new DHClientKeyExchangeMessage(shc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming DH ClientKeyExchange handshake message", ckem); } diff --git a/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java index 2df62d50fb8..744ff59f402 100644 --- a/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java @@ -481,7 +481,7 @@ public byte[] produce(ConnectionContext context, ServerHandshakeContext shc = (ServerHandshakeContext)context; DHServerKeyExchangeMessage skem = new DHServerKeyExchangeMessage(shc); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced DH ServerKeyExchange handshake message", skem); } @@ -512,7 +512,7 @@ public void consume(ConnectionContext context, DHServerKeyExchangeMessage skem = new DHServerKeyExchangeMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming DH ServerKeyExchange handshake message", skem); } diff --git a/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java b/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java index 4e82fd25a7b..e880f36e846 100644 --- a/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java +++ b/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java @@ -125,7 +125,7 @@ Plaintext[] decode(ByteBuffer packet) throws SSLProtocolException { return null; } - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine("Raw read", packet); } @@ -150,7 +150,7 @@ Plaintext[] decode(ByteBuffer packet) throws SSLProtocolException { int contentLen = ((packet.get() & 0xFF) << 8) | (packet.get() & 0xFF); // pos: 11, 12 - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine("READ: " + ProtocolVersion.nameOf(majorVersion, minorVersion) + " " + ContentType.nameOf(contentType) + ", length = " + @@ -162,7 +162,7 @@ Plaintext[] decode(ByteBuffer packet) throws SSLProtocolException { if (this.readEpoch > recordEpoch) { // Reset the position of the packet buffer. packet.position(recLim); - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine("READ: discard this old record", recordEnS); } return null; @@ -181,7 +181,7 @@ Plaintext[] decode(ByteBuffer packet) throws SSLProtocolException { packet.position(recLim); - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("Premature record (epoch), discard it."); } @@ -223,7 +223,7 @@ Plaintext[] decode(ByteBuffer packet) throws SSLProtocolException { plaintextFragment = plaintext.fragment; contentType = plaintext.contentType; } catch (GeneralSecurityException gse) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("Discard invalid record: " + gse); } @@ -241,7 +241,7 @@ Plaintext[] decode(ByteBuffer packet) throws SSLProtocolException { // Cleanup the handshake reassembler if necessary. if ((reassembler != null) && (reassembler.handshakeEpoch < recordEpoch)) { - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("Cleanup the handshake reassembler"); } @@ -273,7 +273,7 @@ Plaintext[] decode(ByteBuffer packet) throws SSLProtocolException { if (hsFrag == null) { // invalid, discard this record - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Invalid handshake message, discard it."); } @@ -296,7 +296,7 @@ Plaintext[] decode(ByteBuffer packet) throws SSLProtocolException { return pt == null ? null : new Plaintext[] { pt }; } - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("The reassembler is not initialized yet."); } @@ -356,7 +356,7 @@ private static HandshakeFragment parseHandshakeMessage( int remaining = plaintextFragment.remaining(); if (remaining < handshakeHeaderSize) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("Discard invalid record: " + "too small record to hold a handshake fragment"); } @@ -368,7 +368,7 @@ private static HandshakeFragment parseHandshakeMessage( // Fail fast for unknown handshake message. byte handshakeType = plaintextFragment.get(); // pos: 0 if (!SSLHandshake.isKnown(handshakeType)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("Discard invalid record: " + "unknown handshake type size, Handshake.msg_type = " + (handshakeType & 0xFF)); @@ -404,7 +404,7 @@ private static HandshakeFragment parseHandshakeMessage( ((plaintextFragment.get() & 0xFF) << 8) | (plaintextFragment.get() & 0xFF); // pos: 9-11 if ((remaining - handshakeHeaderSize) < fragmentLength) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("Discard invalid record: " + "not a complete handshake fragment in the record"); } @@ -748,7 +748,7 @@ void queueUpHandshake(HandshakeFragment hsf) throws SSLProtocolException { // It's OK to discard retransmission as the handshake hash // is computed as if each handshake message had been sent // as a single fragment. - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("Have got the full message, discard it."); } @@ -769,7 +769,7 @@ void queueUpHandshake(HandshakeFragment hsf) throws SSLProtocolException { // The ranges SHOULD NOT overlap. if (hole.offset > hsf.fragmentOffset || hole.limit < fragmentLimit) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("Discard invalid record: " + "handshake fragment ranges are overlapping"); } @@ -837,7 +837,7 @@ private HandshakeFragment valHello(HandshakeFragment hsf, } // Read the random (32 bytes) if (fragmentData.remaining() < 32) { - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("Rejected client hello fragment (bad random len) " + "fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength); } @@ -861,7 +861,7 @@ private HandshakeFragment valHello(HandshakeFragment hsf, // Cookie byte[] cookie = Record.getBytes8(fragmentData); if (firstHello && cookie.length != 0) { - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("Rejected initial client hello fragment (bad cookie len) " + "fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength); } @@ -897,7 +897,7 @@ private HandshakeFragment valHello(HandshakeFragment hsf, } } } catch (IOException ioe) { - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("Rejected client hello fragment " + "fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength); } @@ -1029,7 +1029,7 @@ private boolean isDesirable(RecordFragment rf) throws SSLProtocolException { int previousEpoch = nextRecordEpoch - 1; if (rf.recordEpoch < previousEpoch) { // Too old to use, discard this record. - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Too old epoch to use this record, discard it."); } @@ -1075,7 +1075,7 @@ private boolean isDesirable(RecordFragment rf) throws SSLProtocolException { if (!isDesired) { // Too old to use, discard this retransmitted record - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Too old retransmission to use, discard it."); } @@ -1088,7 +1088,7 @@ private boolean isDesirable(RecordFragment rf) throws SSLProtocolException { // Previously disordered record for the current epoch. // // Should have been retransmitted. Discard this record. - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Lagging behind record (sequence), discard it."); } @@ -1126,7 +1126,7 @@ private boolean isEmpty() { Plaintext acquirePlaintext() throws SSLProtocolException { if (bufferedFragments.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("No received handshake messages"); } return null; @@ -1147,7 +1147,7 @@ Plaintext acquirePlaintext() throws SSLProtocolException { // Reset the next handshake flight. resetHandshakeFlight(precedingFlight); - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("Received a retransmission flight."); } @@ -1159,7 +1159,7 @@ Plaintext acquirePlaintext() throws SSLProtocolException { } if (!flightIsReady) { - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "The handshake flight is not ready to use: " + handshakeFlight.handshakeType); @@ -1244,7 +1244,7 @@ private Plaintext acquireCachedMessage() throws SSLProtocolException { if (readEpoch != rFrag.recordEpoch) { if (readEpoch > rFrag.recordEpoch) { // discard old records - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Discard old buffered ciphertext fragments."); } @@ -1256,7 +1256,7 @@ private Plaintext acquireCachedMessage() throws SSLProtocolException { flightIsReady = false; } - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Not yet ready to decrypt the cached fragments."); } @@ -1273,7 +1273,7 @@ private Plaintext acquireCachedMessage() throws SSLProtocolException { plaintextFragment = plaintext.fragment; rFrag.contentType = plaintext.contentType; } catch (GeneralSecurityException gse) { - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("Discard invalid record: ", gse); } @@ -1295,7 +1295,7 @@ private Plaintext acquireCachedMessage() throws SSLProtocolException { if (hsFrag == null) { // invalid, discard this record - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Invalid handshake fragment, discard it", plaintextFragment); @@ -1446,7 +1446,7 @@ boolean flightIsReady() { if (expectCCSFlight) { // Have the ChangeCipherSpec/Finished flight been received? boolean isReady = hasFinishedMessage(); - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Has the final flight been received? " + isReady); } @@ -1454,7 +1454,7 @@ boolean flightIsReady() { return isReady; } - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("No flight is received yet."); } @@ -1467,7 +1467,7 @@ boolean flightIsReady() { // single handshake message flight boolean isReady = hasCompleted(flightType); - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Is the handshake message completed? " + isReady); } @@ -1481,7 +1481,7 @@ boolean flightIsReady() { if (flightType == SSLHandshake.SERVER_HELLO.id) { // Firstly, check the first flight handshake message. if (!hasCompleted(flightType)) { - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "The ServerHello message is not completed yet."); } @@ -1493,7 +1493,7 @@ boolean flightIsReady() { // an abbreviated handshake // if (hasFinishedMessage()) { - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("It's an abbreviated handshake."); } @@ -1507,7 +1507,7 @@ boolean flightIsReady() { SSLHandshake.SERVER_HELLO_DONE.id); if ((holes == null) || !holes.isEmpty()) { // Not yet got the final message of the flight. - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Not yet got the ServerHelloDone message"); } @@ -1519,7 +1519,7 @@ boolean flightIsReady() { boolean isReady = hasCompleted(bufferedFragments, handshakeFlight.minMessageSeq, handshakeFlight.maxMessageSeq); - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Is the ServerHello flight (message " + handshakeFlight.minMessageSeq + "-" + @@ -1542,7 +1542,7 @@ boolean flightIsReady() { // Firstly, check the first flight handshake message. if (!hasCompleted(flightType)) { - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "The ClientKeyExchange or client Certificate " + "message is not completed yet."); @@ -1556,7 +1556,7 @@ boolean flightIsReady() { if (needClientVerify(bufferedFragments) && !hasCompleted(SSLHandshake.CERTIFICATE_VERIFY.id)) { - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Not yet have the CertificateVerify message"); } @@ -1567,7 +1567,7 @@ boolean flightIsReady() { if (!hasFinishedMessage()) { // not yet have the ChangeCipherSpec/Finished messages - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Not yet have the ChangeCipherSpec and " + "Finished messages"); @@ -1580,7 +1580,7 @@ boolean flightIsReady() { boolean isReady = hasCompleted(bufferedFragments, handshakeFlight.minMessageSeq, handshakeFlight.maxMessageSeq); - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Is the ClientKeyExchange flight (message " + handshakeFlight.minMessageSeq + "-" + @@ -1594,7 +1594,7 @@ boolean flightIsReady() { // // Otherwise, need to receive more handshake messages. // - if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("Need to receive more handshake messages"); } diff --git a/src/java.base/share/classes/sun/security/ssl/DTLSOutputRecord.java b/src/java.base/share/classes/sun/security/ssl/DTLSOutputRecord.java index 691ac32c26b..162dbb58eec 100644 --- a/src/java.base/share/classes/sun/security/ssl/DTLSOutputRecord.java +++ b/src/java.base/share/classes/sun/security/ssl/DTLSOutputRecord.java @@ -92,7 +92,7 @@ void finishHandshake() { void changeWriteCiphers(SSLWriteCipher writeCipher, boolean useChangeCipherSpec) { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "change_cipher_spec message"); } @@ -120,7 +120,7 @@ void changeWriteCiphers(SSLWriteCipher writeCipher, @Override void encodeAlert(byte level, byte description) { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "alert message: " + Alert.nameOf(description)); } @@ -137,7 +137,7 @@ void encodeAlert(byte level, byte description) { @Override void encodeChangeCipherSpec() { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "change_cipher_spec message"); } @@ -154,7 +154,7 @@ void encodeChangeCipherSpec() { void encodeHandshake(byte[] source, int offset, int length) { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "handshake message", ByteBuffer.wrap(source, offset, length)); @@ -179,14 +179,14 @@ Ciphertext encode( ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException { if (isClosed) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "application data or cached messages"); } return null; } else if (isCloseWaiting) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "application data"); } @@ -201,7 +201,7 @@ private Ciphertext encode(ByteBuffer[] sources, int offset, int length, ByteBuffer destination) throws IOException { if (writeCipher.authenticator.seqNumOverflow()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine( "sequence number extremely close to overflow " + "(2^64-1 packets). Closing connection."); @@ -269,7 +269,7 @@ private Ciphertext encode(ByteBuffer[] sources, int offset, int length, destination.limit(destination.position()); destination.position(dstContent); - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "WRITE: " + protocolVersion.name + " " + ContentType.APPLICATION_DATA.name + @@ -282,7 +282,7 @@ private Ciphertext encode(ByteBuffer[] sources, int offset, int length, dstPos, dstLim, headerSize, protocolVersion); - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { ByteBuffer temporary = destination.duplicate(); temporary.limit(temporary.position()); temporary.position(dstPos); @@ -497,7 +497,7 @@ Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException { dstBuf.limit(dstBuf.position()); dstBuf.position(dstContent); - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "WRITE: " + protocolVersion.name + " " + ContentType.nameOf(memo.contentType) + @@ -511,7 +511,7 @@ Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException { ProtocolVersion.valueOf(memo.majorVersion, memo.minorVersion)); - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { ByteBuffer temporary = dstBuf.duplicate(); temporary.limit(temporary.position()); temporary.position(dstPos); diff --git a/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java index e1c1b1377ad..a626f6f34d0 100644 --- a/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java @@ -199,7 +199,7 @@ public byte[] produce(ConnectionContext context, ECDHClientKeyExchangeMessage cke = new ECDHClientKeyExchangeMessage( chc, sslPossession.encode()); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced ECDH ClientKeyExchange handshake message", cke); } @@ -308,7 +308,7 @@ public void consume(ConnectionContext context, // parse either handshake message containing either EC/XEC. ECDHClientKeyExchangeMessage cke = new ECDHClientKeyExchangeMessage(shc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming ECDH ClientKeyExchange handshake message", cke); } @@ -397,7 +397,7 @@ public byte[] produce(ConnectionContext context, new ECDHClientKeyExchangeMessage( chc, sslPossession.encode()); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced ECDHE ClientKeyExchange handshake message", cke); } @@ -490,7 +490,7 @@ public void consume(ConnectionContext context, // parse the EC/XEC handshake message ECDHClientKeyExchangeMessage cke = new ECDHClientKeyExchangeMessage(shc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming ECDHE ClientKeyExchange handshake message", cke); } diff --git a/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java index b31c0ba9cb9..a02a8438163 100644 --- a/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java @@ -489,7 +489,7 @@ public byte[] produce(ConnectionContext context, ServerHandshakeContext shc = (ServerHandshakeContext)context; ECDHServerKeyExchangeMessage skem = new ECDHServerKeyExchangeMessage(shc); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced ECDH ServerKeyExchange handshake message", skem); } @@ -522,7 +522,7 @@ public void consume(ConnectionContext context, // AlgorithmConstraints are checked during decoding ECDHServerKeyExchangeMessage skem = new ECDHServerKeyExchangeMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming ECDH ServerKeyExchange handshake message", skem); } diff --git a/src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java b/src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java index 580e1d416de..72b7c950374 100644 --- a/src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java @@ -171,7 +171,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(CH_EC_POINT_FORMATS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable ec_point_formats extension"); } @@ -193,7 +193,7 @@ public byte[] produce(ConnectionContext context, return extData; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Need no ec_point_formats extension"); } @@ -221,7 +221,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(CH_EC_POINT_FORMATS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable ec_point_formats extension"); } diff --git a/src/java.base/share/classes/sun/security/ssl/EncryptedExtensions.java b/src/java.base/share/classes/sun/security/ssl/EncryptedExtensions.java index d1975b5caa4..b5be927f0aa 100644 --- a/src/java.base/share/classes/sun/security/ssl/EncryptedExtensions.java +++ b/src/java.base/share/classes/sun/security/ssl/EncryptedExtensions.java @@ -134,7 +134,7 @@ public byte[] produce(ConnectionContext context, SSLHandshake.ENCRYPTED_EXTENSIONS, shc.negotiatedProtocol); eem.extensions.produce(shc, extTypes); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced EncryptedExtensions message", eem); } @@ -168,7 +168,7 @@ public void consume(ConnectionContext context, EncryptedExtensionsMessage eem = new EncryptedExtensionsMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming EncryptedExtensions handshake message", eem); } diff --git a/src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java b/src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java index ff4694c8c7c..6bacbfbd1d8 100644 --- a/src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java @@ -119,7 +119,7 @@ public byte[] produce(ConnectionContext context, if (!chc.sslConfig.isAvailable(CH_EXTENDED_MASTER_SECRET) || !SSLConfiguration.useExtendedMasterSecret || !chc.conContext.protocolVersion.useTLS10PlusSpec()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extended_master_secret extension"); } @@ -162,7 +162,7 @@ public void consume(ConnectionContext context, if (!shc.sslConfig.isAvailable(CH_EXTENDED_MASTER_SECRET) || !SSLConfiguration.useExtendedMasterSecret || !shc.negotiatedProtocol.useTLS10PlusSpec()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Ignore unavailable extension: " + CH_EXTENDED_MASTER_SECRET.name); } @@ -182,7 +182,7 @@ public void consume(ConnectionContext context, // with a full handshake. shc.isResumption = false; shc.resumingSession = null; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "abort session resumption which did not use " + "Extended Master Secret extension"); @@ -213,7 +213,7 @@ public void absent(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(CH_EXTENDED_MASTER_SECRET) || !SSLConfiguration.useExtendedMasterSecret) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Ignore unavailable extension: " + CH_EXTENDED_MASTER_SECRET.name); } @@ -252,7 +252,7 @@ public void absent(ConnectionContext context, } else { // Otherwise, continue with a full handshake. shc.isResumption = false; shc.resumingSession = null; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "abort session resumption, " + "missing Extended Master Secret extension"); diff --git a/src/java.base/share/classes/sun/security/ssl/Finished.java b/src/java.base/share/classes/sun/security/ssl/Finished.java index 04fe61760d0..4238ced8f01 100644 --- a/src/java.base/share/classes/sun/security/ssl/Finished.java +++ b/src/java.base/share/classes/sun/security/ssl/Finished.java @@ -390,7 +390,7 @@ private byte[] onProduceFinished(ClientHandshakeContext chc, // Change write cipher and delivery ChangeCipherSpec message. ChangeCipherSpec.t10Producer.produce(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced client Finished handshake message", fm); } @@ -453,7 +453,7 @@ private byte[] onProduceFinished(ServerHandshakeContext shc, // Change write cipher and delivery ChangeCipherSpec message. ChangeCipherSpec.t10Producer.produce(shc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced server Finished handshake message", fm); } @@ -542,7 +542,7 @@ public void consume(ConnectionContext context, private void onConsumeFinished(ClientHandshakeContext chc, ByteBuffer message) throws IOException { FinishedMessage fm = new FinishedMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming server Finished handshake message", fm); } @@ -602,7 +602,7 @@ private void onConsumeFinished(ServerHandshakeContext shc, } FinishedMessage fm = new FinishedMessage(shc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming client Finished handshake message", fm); } @@ -681,7 +681,7 @@ private byte[] onProduceFinished(ClientHandshakeContext chc, chc.handshakeHash.update(); FinishedMessage fm = new FinishedMessage(chc); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced client Finished handshake message", fm); } @@ -778,7 +778,7 @@ private byte[] onProduceFinished(ServerHandshakeContext shc, shc.handshakeHash.update(); FinishedMessage fm = new FinishedMessage(shc); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced server Finished handshake message", fm); } @@ -930,7 +930,7 @@ private void onConsumeFinished(ClientHandshakeContext chc, } FinishedMessage fm = new FinishedMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming server Finished handshake message", fm); } @@ -1073,7 +1073,7 @@ private void onConsumeFinished(ServerHandshakeContext shc, } FinishedMessage fm = new FinishedMessage(shc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming client Finished handshake message", fm); } diff --git a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java index 8455ddfc65d..a5f340d5203 100644 --- a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java +++ b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java @@ -284,14 +284,14 @@ private static List getActiveProtocols( found = true; break; } - } else if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + } else if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "Ignore unsupported cipher suite: " + suite + " for " + protocol.name); } } - if (!found && (SSLLogger.isOn) && SSLLogger.isOn("handshake")) { + if (!found && (SSLLogger.isOn()) && SSLLogger.isOn("handshake")) { SSLLogger.fine( "No available cipher suite for " + protocol.name); } @@ -335,7 +335,7 @@ private static List getActiveCipherSuites( } if (!isSupported && - SSLLogger.isOn && SSLLogger.isOn("verbose")) { + SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.finest( "Ignore unsupported cipher suite: " + suite); } @@ -556,7 +556,7 @@ private static boolean isActivatable( cachedStatus.put(groupType, groupAvailable); if (!groupAvailable && - SSLLogger.isOn && SSLLogger.isOn("verbose")) { + SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine( "No activated named group in " + groupType); } @@ -570,13 +570,13 @@ private static boolean isActivatable( } } - if (!retval && SSLLogger.isOn && SSLLogger.isOn("verbose")) { + if (!retval && SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("No active named group(s), ignore " + suite); } return retval; - } else if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { + } else if (SSLLogger.isOn() && SSLLogger.isOn("verbose")) { SSLLogger.fine("Ignore disabled cipher suite: " + suite); } diff --git a/src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java b/src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java index 61936442502..2a05881180d 100644 --- a/src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java +++ b/src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java @@ -61,7 +61,7 @@ void complete() throws IOException { if (!outputRecord.isClosed()) { outputRecord.encodeHandshake(buf, 0, count); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "handshake messages", ByteBuffer.wrap(buf, 0, count)); } diff --git a/src/java.base/share/classes/sun/security/ssl/HelloRequest.java b/src/java.base/share/classes/sun/security/ssl/HelloRequest.java index f4da66b5dd3..39464992db5 100644 --- a/src/java.base/share/classes/sun/security/ssl/HelloRequest.java +++ b/src/java.base/share/classes/sun/security/ssl/HelloRequest.java @@ -101,7 +101,7 @@ public byte[] produce(ConnectionContext context) throws IOException { ServerHandshakeContext shc = (ServerHandshakeContext)context; HelloRequestMessage hrm = new HelloRequestMessage(shc); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced HelloRequest handshake message", hrm); } @@ -137,7 +137,7 @@ public byte[] produce(ConnectionContext context, ServerHandshakeContext shc = (ServerHandshakeContext)context; HelloRequestMessage hrm = new HelloRequestMessage(shc); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced HelloRequest handshake message", hrm); } @@ -177,7 +177,7 @@ public void consume(ConnectionContext context, // be sent by the server at any time. Please don't clean up this // handshake consumer. HelloRequestMessage hrm = new HelloRequestMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming HelloRequest handshake message", hrm); } @@ -190,7 +190,7 @@ public void consume(ConnectionContext context, } if (!chc.conContext.secureRenegotiation) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Continue with insecure renegotiation"); } @@ -206,7 +206,7 @@ public void consume(ConnectionContext context, // SSLHandshake.CLIENT_HELLO.produce(context, hrm); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore HelloRequest, handshaking is in progress"); } diff --git a/src/java.base/share/classes/sun/security/ssl/HelloVerifyRequest.java b/src/java.base/share/classes/sun/security/ssl/HelloVerifyRequest.java index f28ae16de88..8d3f1048c91 100644 --- a/src/java.base/share/classes/sun/security/ssl/HelloVerifyRequest.java +++ b/src/java.base/share/classes/sun/security/ssl/HelloVerifyRequest.java @@ -140,7 +140,7 @@ public byte[] produce(ConnectionContext context, HelloVerifyRequestMessage hvrm = new HelloVerifyRequestMessage(shc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced HelloVerifyRequest handshake message", hvrm); } @@ -197,7 +197,7 @@ public void consume(ConnectionContext context, HelloVerifyRequestMessage hvrm = new HelloVerifyRequestMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming HelloVerifyRequest handshake message", hvrm); } diff --git a/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java b/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java index 98e4693e917..8d785f7515a 100644 --- a/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java @@ -90,7 +90,7 @@ private byte[] getEncoded() { Record.putInt16(m, namedGroupId); Record.putBytes16(m, keyExchange); } catch (IOException ioe) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Unlikely IOException", ioe); } @@ -222,7 +222,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(SSLExtension.CH_KEY_SHARE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable key_share extension"); } @@ -237,7 +237,7 @@ public byte[] produce(ConnectionContext context, namedGroups = chc.clientRequestedNamedGroups; if (namedGroups == null || namedGroups.isEmpty()) { // No supported groups. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore key_share extension, no supported groups"); } @@ -287,7 +287,7 @@ private static byte[] getShare(ClientHandshakeContext chc, NamedGroup ng) { SSLKeyExchange ke = SSLKeyExchange.valueOf(ng); if (ke == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "No key exchange for named group " + ng.name); } @@ -323,7 +323,7 @@ public void consume(ConnectionContext context, ServerHandshakeContext shc = (ServerHandshakeContext)context; if (shc.handshakeExtensions.containsKey(SSLExtension.CH_KEY_SHARE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "The key_share extension has been loaded"); } @@ -332,7 +332,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(SSLExtension.CH_KEY_SHARE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable key_share extension"); } @@ -346,7 +346,7 @@ public void consume(ConnectionContext context, NamedGroup ng = NamedGroup.valueOf(entry.namedGroupId); if (ng == null || !NamedGroup.isActivatable(shc.sslConfig, shc.algorithmConstraints, ng)) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unsupported named group: " + @@ -364,7 +364,7 @@ public void consume(ConnectionContext context, if (!shc.algorithmConstraints.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), namedGroupCredentials.getPublicKey())) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "key share entry of " + ng + " does not " + @@ -379,7 +379,7 @@ public void consume(ConnectionContext context, credentials.add(kaCred); } } catch (GeneralSecurityException ex) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Cannot decode named group: " + NamedGroup.nameOf(entry.namedGroupId)); @@ -522,7 +522,7 @@ public byte[] produce(ConnectionContext context, SSLExtension.CH_KEY_SHARE); if (kss == null) { // Unlikely, no key_share extension requested. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore, no client key_share extension"); } @@ -531,7 +531,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(SSLExtension.SH_KEY_SHARE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore, no available server key_share extension"); } @@ -542,7 +542,7 @@ public byte[] produce(ConnectionContext context, if ((shc.handshakeCredentials == null) || shc.handshakeCredentials.isEmpty()) { // Unlikely, HelloRetryRequest should be used earlier. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "No available client key share entries"); } @@ -562,7 +562,7 @@ public byte[] produce(ConnectionContext context, SSLKeyExchange ke = SSLKeyExchange.valueOf(ng); if (ke == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "No key exchange for named group " + ng.name); } @@ -597,7 +597,7 @@ public byte[] produce(ConnectionContext context, if (keyShare == null) { // Unlikely, HelloRetryRequest should be used instead earlier. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "No available server key_share extension"); } @@ -708,7 +708,7 @@ public void absent(ConnectionContext context, ClientHandshakeContext chc = (ClientHandshakeContext)context; // Cannot use the previous requested key shares anymore. - if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("handshake")) { SSLLogger.fine( "No key_share extension in ServerHello, " + "cleanup the key shares if necessary"); @@ -801,7 +801,7 @@ public byte[] produce(ConnectionContext context, for (NamedGroup ng : shc.clientRequestedNamedGroups) { if (NamedGroup.isActivatable(shc.sslConfig, shc.algorithmConstraints, ng)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "HelloRetryRequest selected named group: " + ng.name); diff --git a/src/java.base/share/classes/sun/security/ssl/KeyUpdate.java b/src/java.base/share/classes/sun/security/ssl/KeyUpdate.java index 2b17c7406a3..4e5a9683079 100644 --- a/src/java.base/share/classes/sun/security/ssl/KeyUpdate.java +++ b/src/java.base/share/classes/sun/security/ssl/KeyUpdate.java @@ -191,7 +191,7 @@ public void consume(ConnectionContext context, // The consuming happens in client side only. PostHandshakeContext hc = (PostHandshakeContext)context; KeyUpdateMessage km = new KeyUpdateMessage(hc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming KeyUpdate post-handshake message", km); } @@ -235,7 +235,7 @@ public void consume(ConnectionContext context, rc.baseSecret = nplus1; hc.conContext.inputRecord.changeReadCiphers(rc); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("KeyUpdate: read key updated"); } } catch (GeneralSecurityException gse) { @@ -276,7 +276,7 @@ public byte[] produce(ConnectionContext context, return null; } KeyUpdateMessage km = (KeyUpdateMessage)message; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced KeyUpdate post-handshake message", km); } @@ -328,7 +328,7 @@ public byte[] produce(ConnectionContext context, // changeWriteCiphers() implementation. wc.baseSecret = nplus1; hc.conContext.outputRecord.changeWriteCiphers(wc, km.status.id); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("KeyUpdate: write key updated"); } diff --git a/src/java.base/share/classes/sun/security/ssl/MaxFragExtension.java b/src/java.base/share/classes/sun/security/ssl/MaxFragExtension.java index a07e81be914..25500c7ac57 100644 --- a/src/java.base/share/classes/sun/security/ssl/MaxFragExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/MaxFragExtension.java @@ -176,7 +176,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(CH_MAX_FRAGMENT_LENGTH)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable max_fragment_length extension"); } @@ -213,7 +213,7 @@ public byte[] produce(ConnectionContext context, } else { // log and ignore, no MFL extension. chc.maxFragmentLength = -1; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "No available max_fragment_length extension can " + "be used for fragment size of " + @@ -243,7 +243,7 @@ public void consume(ConnectionContext context, ServerHandshakeContext shc = (ServerHandshakeContext)context; if (!shc.sslConfig.isAvailable(CH_MAX_FRAGMENT_LENGTH)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable max_fragment_length extension"); } @@ -288,7 +288,7 @@ public byte[] produce(ConnectionContext context, MaxFragLenSpec spec = (MaxFragLenSpec) shc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH); if (spec == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "Ignore unavailable max_fragment_length extension"); } @@ -305,7 +305,7 @@ public byte[] produce(ConnectionContext context, // For better interoperability, abort the maximum // fragment length negotiation, rather than terminate // the connection with a fatal alert. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Abort the maximum fragment length negotiation, " + "may overflow the maximum packet size limit."); @@ -413,7 +413,7 @@ public void consume(ConnectionContext context, // For better interoperability, abort the maximum // fragment length negotiation, rather than terminate // the connection with a fatal alert. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Abort the maximum fragment length negotiation, " + "may overflow the maximum packet size limit."); @@ -455,7 +455,7 @@ public byte[] produce(ConnectionContext context, MaxFragLenSpec spec = (MaxFragLenSpec) shc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH); if (spec == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "Ignore unavailable max_fragment_length extension"); } @@ -472,7 +472,7 @@ public byte[] produce(ConnectionContext context, // For better interoperability, abort the maximum // fragment length negotiation, rather than terminate // the connection with a fatal alert. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Abort the maximum fragment length negotiation, " + "may overflow the maximum packet size limit."); @@ -578,7 +578,7 @@ public void consume(ConnectionContext context, // For better interoperability, abort the maximum // fragment length negotiation, rather than terminate // the connection with a fatal alert. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Abort the maximum fragment length negotiation, " + "may overflow the maximum packet size limit."); diff --git a/src/java.base/share/classes/sun/security/ssl/NamedGroup.java b/src/java.base/share/classes/sun/security/ssl/NamedGroup.java index 46280a05355..0c708b194cb 100644 --- a/src/java.base/share/classes/sun/security/ssl/NamedGroup.java +++ b/src/java.base/share/classes/sun/security/ssl/NamedGroup.java @@ -273,7 +273,7 @@ enum NamedGroup { | NoSuchAlgorithmException exp) { if (namedGroupSpec != NamedGroupSpec.NAMED_GROUP_XDH) { mediator = false; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "No AlgorithmParameters for " + name, exp); } @@ -294,7 +294,7 @@ enum NamedGroup { // AlgorithmParameters.getInstance(name); } catch (NoSuchAlgorithmException nsae) { mediator = false; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "No AlgorithmParameters for " + name, nsae); } @@ -382,7 +382,7 @@ public static List namesOf(String[] namedGroups) { for (String ss : namedGroups) { NamedGroup ng = NamedGroup.nameOf(ss); if (ng == null || !ng.isAvailable) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Ignore the named group (" + ss @@ -811,7 +811,7 @@ static final class SupportedGroups { } if (groupList.isEmpty() && - SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("No default named groups"); } } diff --git a/src/java.base/share/classes/sun/security/ssl/NewSessionTicket.java b/src/java.base/share/classes/sun/security/ssl/NewSessionTicket.java index 4c879e0dc4d..89b0a72bb32 100644 --- a/src/java.base/share/classes/sun/security/ssl/NewSessionTicket.java +++ b/src/java.base/share/classes/sun/security/ssl/NewSessionTicket.java @@ -202,7 +202,7 @@ static final class T13NewSessionTicketMessage extends NewSessionTicketMessage { this.ticket = Record.getBytes16(m); if (ticket.length == 0) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "No ticket in the NewSessionTicket handshake message"); } @@ -329,7 +329,7 @@ public byte[] produce(ConnectionContext context) throws IOException { if (hc instanceof ServerHandshakeContext) { // Is this session resumable? if (!hc.handshakeSession.isRejoinable()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("No session ticket produced: " + "session is not resumable"); } @@ -347,7 +347,7 @@ public byte[] produce(ConnectionContext context) throws IOException { SSLExtension.PSK_KEY_EXCHANGE_MODES); if (pkemSpec == null || !pkemSpec.contains(PskKeyExchangeMode.PSK_DHE_KE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("No session ticket produced: " + "client does not support psk_dhe_ke"); } @@ -358,7 +358,7 @@ public byte[] produce(ConnectionContext context) throws IOException { // Check if we have sent a PSK already, then we know it is // using an allowable PSK exchange key mode. if (!hc.handshakeSession.isPSKable()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("No session ticket produced: " + "No session ticket allowed in this session"); } @@ -372,7 +372,7 @@ public byte[] produce(ConnectionContext context) throws IOException { hc.sslContext.engineGetServerSessionContext(); int sessionTimeoutSeconds = sessionCache.getSessionTimeout(); if (sessionTimeoutSeconds > MAX_TICKET_LIFETIME) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("No session ticket produced: " + "session timeout is too long"); } @@ -459,7 +459,7 @@ private NewSessionTicketMessage generateNST(HandshakeContext hc, if (!nstm.isValid()) { hc.statelessResumption = false; } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced NewSessionTicket stateless " + "post-handshake message", nstm); } @@ -474,7 +474,7 @@ private NewSessionTicketMessage generateNST(HandshakeContext hc, sessionCache.getSessionTimeout(), hc.sslContext.getSecureRandom(), nonce, newId.getId()); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced NewSessionTicket " + "post-handshake message", nstm); } @@ -488,7 +488,7 @@ private NewSessionTicketMessage generateNST(HandshakeContext hc, return nstm; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("No NewSessionTicket created"); } @@ -526,7 +526,7 @@ public byte[] produce(ConnectionContext context, shc.sslContext.engineGetServerSessionContext(); int sessionTimeoutSeconds = sessionCache.getSessionTimeout(); if (sessionTimeoutSeconds > MAX_TICKET_LIFETIME) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Session timeout is too long. No ticket sent."); } @@ -540,7 +540,7 @@ public byte[] produce(ConnectionContext context, NewSessionTicketMessage nstm = new T12NewSessionTicketMessage(shc, sessionTimeoutSeconds, new SessionTicketSpec().encrypt(shc, sessionCopy)); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced NewSessionTicket stateless handshake message", nstm); @@ -579,7 +579,7 @@ public void consume(ConnectionContext context, HandshakeContext hc = (HandshakeContext)context; NewSessionTicketMessage nstm = new T13NewSessionTicketMessage(hc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming NewSessionTicket message", nstm); } @@ -590,7 +590,7 @@ public void consume(ConnectionContext context, // discard tickets with timeout 0 if (nstm.ticketLifetime <= 0 || nstm.ticketLifetime > MAX_TICKET_LIFETIME) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Discarding NewSessionTicket with lifetime " + nstm.ticketLifetime, nstm); @@ -599,7 +599,7 @@ public void consume(ConnectionContext context, } if (sessionCache.getSessionTimeout() > MAX_TICKET_LIFETIME) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Session cache lifetime is too long. " + "Discarding ticket."); @@ -611,7 +611,7 @@ public void consume(ConnectionContext context, SecretKey resumptionMasterSecret = sessionToSave.getResumptionMasterSecret(); if (resumptionMasterSecret == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Session has no resumption master secret. " + "Ignoring ticket."); @@ -637,7 +637,7 @@ public void consume(ConnectionContext context, sessionCopy.setPskIdentity(nstm.ticket); sessionCache.put(sessionCopy, sessionCopy.isPSK()); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("MultiNST PSK (Server): " + Utilities.toHexString(Arrays.copyOf(nstm.ticket, 16))); } @@ -665,7 +665,7 @@ public void consume(ConnectionContext context, NewSessionTicketMessage nstm = new T12NewSessionTicketMessage(hc, message); if (nstm.ticket.length == 0) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("NewSessionTicket ticket was empty"); } return; @@ -674,7 +674,7 @@ public void consume(ConnectionContext context, // discard tickets with timeout 0 if (nstm.ticketLifetime <= 0 || nstm.ticketLifetime > MAX_TICKET_LIFETIME) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Discarding NewSessionTicket with lifetime " + nstm.ticketLifetime, nstm); @@ -686,7 +686,7 @@ public void consume(ConnectionContext context, hc.sslContext.engineGetClientSessionContext(); if (sessionCache.getSessionTimeout() > MAX_TICKET_LIFETIME) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Session cache lifetime is too long. " + "Discarding ticket."); @@ -695,7 +695,7 @@ public void consume(ConnectionContext context, } hc.handshakeSession.setPskIdentity(nstm.ticket); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Consuming NewSessionTicket\n" + nstm); } } diff --git a/src/java.base/share/classes/sun/security/ssl/OutputRecord.java b/src/java.base/share/classes/sun/security/ssl/OutputRecord.java index f2c30b3ff72..416d5d1b5ef 100644 --- a/src/java.base/share/classes/sun/security/ssl/OutputRecord.java +++ b/src/java.base/share/classes/sun/security/ssl/OutputRecord.java @@ -188,7 +188,7 @@ void changeWriteCiphers(SSLWriteCipher writeCipher, recordLock.lock(); try { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "change_cipher_spec message"); } @@ -222,7 +222,7 @@ void changeWriteCiphers(SSLWriteCipher writeCipher, recordLock.lock(); try { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "key_update handshake message"); } diff --git a/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java b/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java index 819fdd589cb..b99c0175838 100644 --- a/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java @@ -341,7 +341,7 @@ public void consume(ConnectionContext context, ServerHandshakeContext shc = (ServerHandshakeContext)context; // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(SSLExtension.CH_PRE_SHARED_KEY)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable pre_shared_key extension"); } @@ -393,7 +393,7 @@ public void consume(ConnectionContext context, } } if (b == null || s == null) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Stateless session ticket invalid"); @@ -402,7 +402,7 @@ public void consume(ConnectionContext context, } if (s != null && canRejoin(clientHello, shc, s)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Resuming session: ", s); } @@ -435,7 +435,7 @@ private static boolean canRejoin(ClientHelloMessage clientHello, // Check protocol version if (result && s.getProtocolVersion() != shc.negotiatedProtocol) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest("Can't resume, incorrect protocol version"); @@ -449,7 +449,7 @@ private static boolean canRejoin(ClientHelloMessage clientHello, try { s.getPeerPrincipal(); } catch (SSLPeerUnverifiedException e) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, " + @@ -466,7 +466,7 @@ private static boolean canRejoin(ClientHelloMessage clientHello, if (result && !shc.localSupportedCertSignAlgs.containsAll(sessionSigAlgs)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Can't resume. Session uses different " + "signature algorithms"); } @@ -480,7 +480,7 @@ private static boolean canRejoin(ClientHelloMessage clientHello, if (result && identityAlg != null) { String sessionIdentityAlg = s.getIdentificationProtocol(); if (!identityAlg.equalsIgnoreCase(sessionIdentityAlg)) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest("Can't resume, endpoint id" + @@ -494,7 +494,7 @@ private static boolean canRejoin(ClientHelloMessage clientHello, // Ensure cipher suite can be negotiated if (result && (!shc.isNegotiable(s.getSuite()) || !clientHello.cipherSuites.contains(s.getSuite()))) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Can't resume, unavailable session cipher suite"); @@ -653,7 +653,7 @@ public byte[] produce(ConnectionContext context, // The producing happens in client side only. ClientHandshakeContext chc = (ClientHandshakeContext)context; if (!chc.isResumption || chc.resumingSession == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("No session to resume."); } return null; @@ -663,7 +663,7 @@ public byte[] produce(ConnectionContext context, Collection sessionSigAlgs = chc.resumingSession.getLocalSupportedSignatureSchemes(); if (!chc.localSupportedCertSignAlgs.containsAll(sessionSigAlgs)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Existing session uses different " + "signature algorithms"); } @@ -673,7 +673,7 @@ public byte[] produce(ConnectionContext context, // The session must have a pre-shared key SecretKey psk = chc.resumingSession.getPreSharedKey(); if (psk == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Existing session has no PSK."); } return null; @@ -687,7 +687,7 @@ public byte[] produce(ConnectionContext context, } if (chc.pskIdentity == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "PSK has no identity, or identity was already used"); } @@ -699,7 +699,7 @@ public byte[] produce(ConnectionContext context, chc.sslContext.engineGetClientSessionContext(); sessionCache.remove(chc.resumingSession.getSessionId(), true); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Found resumable session. Preparing PSK message."); SSLLogger.fine( @@ -836,7 +836,7 @@ class CHPreSharedKeyOnLoadAbsence implements HandshakeAbsence { public void absent(ConnectionContext context, HandshakeMessage message) throws IOException { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Handling pre_shared_key absence."); } @@ -901,7 +901,7 @@ public void consume(ConnectionContext context, } SHPreSharedKeySpec shPsk = new SHPreSharedKeySpec(chc, buffer); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Received pre_shared_key extension: ", shPsk); } @@ -911,7 +911,7 @@ public void consume(ConnectionContext context, "Selected identity index is not in correct range."); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Resuming session: ", chc.resumingSession); } @@ -925,7 +925,7 @@ public void absent(ConnectionContext context, HandshakeMessage message) throws IOException { ClientHandshakeContext chc = (ClientHandshakeContext)context; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Handling pre_shared_key absence."); } diff --git a/src/java.base/share/classes/sun/security/ssl/PredefinedDHParameterSpecs.java b/src/java.base/share/classes/sun/security/ssl/PredefinedDHParameterSpecs.java index e510fe92b0e..c826d9c89e3 100644 --- a/src/java.base/share/classes/sun/security/ssl/PredefinedDHParameterSpecs.java +++ b/src/java.base/share/classes/sun/security/ssl/PredefinedDHParameterSpecs.java @@ -246,7 +246,7 @@ final class PredefinedDHParameterSpecs { Matcher spacesMatcher = spacesPattern.matcher(property); property = spacesMatcher.replaceAll(""); - if (SSLLogger.isOn && SSLLogger.isOn("sslctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("sslctx")) { SSLLogger.fine( "The Security Property " + PROPERTY_NAME + ": " + property); @@ -262,7 +262,7 @@ final class PredefinedDHParameterSpecs { String primeModulus = paramsFinder.group(1); BigInteger p = new BigInteger(primeModulus, 16); if (!p.isProbablePrime(PRIME_CERTAINTY)) { - if (SSLLogger.isOn && SSLLogger.isOn("sslctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("sslctx")) { SSLLogger.fine( "Prime modulus p in Security Property, " + PROPERTY_NAME + ", is not a prime: " + @@ -279,7 +279,7 @@ final class PredefinedDHParameterSpecs { DHParameterSpec spec = new DHParameterSpec(p, g); defaultParams.put(primeLen, spec); } - } else if (SSLLogger.isOn && SSLLogger.isOn("sslctx")) { + } else if (SSLLogger.isOn() && SSLLogger.isOn("sslctx")) { SSLLogger.fine("Invalid Security Property, " + PROPERTY_NAME + ", definition"); } diff --git a/src/java.base/share/classes/sun/security/ssl/PskKeyExchangeModesExtension.java b/src/java.base/share/classes/sun/security/ssl/PskKeyExchangeModesExtension.java index 07707c5163a..a4f343ccb06 100644 --- a/src/java.base/share/classes/sun/security/ssl/PskKeyExchangeModesExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/PskKeyExchangeModesExtension.java @@ -184,7 +184,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable( SSLExtension.PSK_KEY_EXCHANGE_MODES)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable psk_key_exchange_modes extension"); } @@ -216,7 +216,7 @@ public void consume(ConnectionContext context, if (!spec.contains(PskKeyExchangeMode.PSK_DHE_KE)) { shc.isResumption = false; shc.resumingSession = null; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "abort session resumption, " + "no supported psk_dhe_ke PSK key exchange mode"); @@ -247,7 +247,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable( SSLExtension.PSK_KEY_EXCHANGE_MODES)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore unavailable psk_key_exchange_modes extension"); } @@ -287,7 +287,7 @@ public void absent(ConnectionContext context, if (shc.isResumption) { // resumingSession may not be set shc.isResumption = false; shc.resumingSession = null; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "abort session resumption, " + "no supported psk_dhe_ke PSK key exchange mode"); diff --git a/src/java.base/share/classes/sun/security/ssl/QuicEngineOutputRecord.java b/src/java.base/share/classes/sun/security/ssl/QuicEngineOutputRecord.java index 893eb282116..7e307ba9d27 100644 --- a/src/java.base/share/classes/sun/security/ssl/QuicEngineOutputRecord.java +++ b/src/java.base/share/classes/sun/security/ssl/QuicEngineOutputRecord.java @@ -75,14 +75,14 @@ void encodeAlert(byte level, byte description) throws IOException { recordLock.lock(); try { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "alert message: " + Alert.nameOf(description)); } return; } if (level == Alert.Level.WARNING.level) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("Suppressing warning-level " + "alert message: " + Alert.nameOf(description)); } @@ -90,7 +90,7 @@ void encodeAlert(byte level, byte description) throws IOException { } if (alert != null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("Suppressing subsequent alert: " + description + ", original: " + alert.id); } @@ -109,7 +109,7 @@ void encodeHandshake(byte[] source, recordLock.lock(); try { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "handshake message", ByteBuffer.wrap(source, offset, length)); diff --git a/src/java.base/share/classes/sun/security/ssl/QuicKeyManager.java b/src/java.base/share/classes/sun/security/ssl/QuicKeyManager.java index fb9077af022..4613dcf96ff 100644 --- a/src/java.base/share/classes/sun/security/ssl/QuicKeyManager.java +++ b/src/java.base/share/classes/sun/security/ssl/QuicKeyManager.java @@ -244,7 +244,7 @@ void discardKeys() { if (toDiscard == null) { return; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("discarding keys (keyphase=" + toDiscard.writeCipher.getKeyPhase() + ") of " + this.keySpace + " key space"); @@ -389,7 +389,7 @@ void discardKeys() { if (toDiscard == null) { return; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("discarding keys (keyphase=" + toDiscard.writeCipher.getKeyPhase() + ") of " + this.keySpace + " key space"); @@ -570,7 +570,7 @@ void discardKeys() { if (series == null) { return; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("discarding key (series) of " + this.keySpace + " key space"); } @@ -611,7 +611,7 @@ void decryptPacket(final long packetNumber, final int keyPhase, if (series.canUseOldDecryptKey(packetNumber)) { final QuicReadCipher oldReadCipher = series.old; assert oldReadCipher != null : "old key is unexpectedly null"; - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("using old read key to decrypt packet: " + packetNumber + ", with incoming key phase: " + keyPhase + ", current key phase: " + @@ -633,7 +633,7 @@ void decryptPacket(final long packetNumber, final int keyPhase, // KEY_UPDATE_ERROR. This indicates that a peer has // received and acknowledged a packet that initiates a key // update, but has not updated keys in response. - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("peer used incorrect key, was" + " expected to use updated key of" + " key phase: " + currentKeyPhase + @@ -646,7 +646,7 @@ void decryptPacket(final long packetNumber, final int keyPhase, } return; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("detected ONE_RTT key update, current key " + "phase: " + currentKeyPhase + ", incoming key phase: " + keyPhase @@ -717,7 +717,7 @@ private boolean maybeInitiateKeyUpdate(final KeySeries currentSeries, } final long numEncrypted = cipher.getNumEncrypted(); if (numEncrypted >= 0.8 * confidentialityLimit) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("about to reach confidentiality limit, " + "attempting to initiate a 1-RTT key update," + " packet number: " + @@ -732,7 +732,7 @@ private boolean maybeInitiateKeyUpdate(final KeySeries currentSeries, : "key phase of updated key unexpectedly matches " + "the key phase " + cipher.getKeyPhase() + " of current keys"; - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest( "1-RTT key update initiated, new key phase: " + newKeyPhase); @@ -755,7 +755,7 @@ private boolean initiateKeyUpdate(final KeySeries series) { // current key phase. This ensures that keys are // available to both peers before // another key update can be initiated. - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest( "skipping key update initiation because peer " + "hasn't yet sent us a packet encrypted with " + @@ -803,7 +803,7 @@ private void decryptUsingNextKeys( // (we avoid timing attacks by not generating // keys during decryption, our key generation // only happens during encryption) - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("next keys unavailable," + " won't decrypt a packet which appears to be" + " a key update"); @@ -815,7 +815,7 @@ private void decryptUsingNextKeys( // use the next keys to attempt decrypting currentKeySeries.next.readCipher.decryptPacket(packetNumber, packet, headerLength, output); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest( "decrypted using next keys for peer-initiated" + " key update; will now switch to new key phase: " + @@ -1025,14 +1025,14 @@ private KeySeries rolloverKeys(final QuicVersion version, // update the key series this.keySeries = newSeries; if (oldReadCipher != null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest( "discarding old read key of key phase: " + oldReadCipher.getKeyPhase()); } oldReadCipher.discard(false); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("discarding write key of key phase: " + writeCipherToDiscard.getKeyPhase()); } diff --git a/src/java.base/share/classes/sun/security/ssl/QuicTLSEngineImpl.java b/src/java.base/share/classes/sun/security/ssl/QuicTLSEngineImpl.java index 6765f554fcc..18790a58c11 100644 --- a/src/java.base/share/classes/sun/security/ssl/QuicTLSEngineImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/QuicTLSEngineImpl.java @@ -560,7 +560,7 @@ public void consumeHandshakeBytes(KeySpace keySpace, ByteBuffer payload) // incoming crypto buffer is null. Validate message type, // check if size is available byte messageType = payload.get(payload.position()); - if (SSLLogger.isOn) { + if (SSLLogger.isOn()) { SSLLogger.fine("Received message of type 0x" + Integer.toHexString(messageType & 0xFF)); } @@ -835,7 +835,7 @@ public boolean tryMarkHandshakeDone() { final boolean confirmed = HANDSHAKE_STATE_HANDLE.compareAndSet(this, NEED_SEND_HANDSHAKE_DONE, HANDSHAKE_CONFIRMED); if (confirmed) { - if (SSLLogger.isOn) { + if (SSLLogger.isOn()) { SSLLogger.fine("QuicTLSEngine (server) marked handshake " + "state as HANDSHAKE_CONFIRMED"); } @@ -853,7 +853,7 @@ public boolean tryReceiveHandshakeDone() { final boolean confirmed = HANDSHAKE_STATE_HANDLE.compareAndSet(this, NEED_RECV_HANDSHAKE_DONE, HANDSHAKE_CONFIRMED); if (confirmed) { - if (SSLLogger.isOn) { + if (SSLLogger.isOn()) { SSLLogger.fine( "QuicTLSEngine (client) received HANDSHAKE_DONE," + " marking state as HANDSHAKE_DONE"); diff --git a/src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java index 701ba35174e..ec91cb4509a 100644 --- a/src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java @@ -190,7 +190,7 @@ public byte[] produce(ConnectionContext context, throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, "Cannot generate RSA premaster secret", gse); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced RSA ClientKeyExchange handshake message", ckem); } @@ -270,7 +270,7 @@ public void consume(ConnectionContext context, RSAClientKeyExchangeMessage ckem = new RSAClientKeyExchangeMessage(shc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming RSA ClientKeyExchange handshake message", ckem); } diff --git a/src/java.base/share/classes/sun/security/ssl/RSAKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/RSAKeyExchange.java index 311ac97e744..d176d7311d0 100644 --- a/src/java.base/share/classes/sun/security/ssl/RSAKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/RSAKeyExchange.java @@ -35,7 +35,6 @@ import java.security.PublicKey; import java.security.SecureRandom; import java.security.interfaces.RSAPublicKey; -import java.security.spec.AlgorithmParameterSpec; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; @@ -150,7 +149,7 @@ static RSAPremasterSecret decode(ServerHandshakeContext shc, needFailover = !KeyUtil.isOracleJCEProvider( cipher.getProvider().getName()); } catch (InvalidKeyException | UnsupportedOperationException iue) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("The Cipher provider " + safeProviderName(cipher) + " caused exception: " + iue.getMessage()); @@ -197,7 +196,7 @@ private static String safeProviderName(Cipher cipher) { try { return cipher.getProvider().toString(); } catch (Exception e) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Retrieving The Cipher provider name" + " caused exception ", e); } @@ -205,7 +204,7 @@ private static String safeProviderName(Cipher cipher) { try { return cipher.toString() + " (provider name not available)"; } catch (Exception e) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Retrieving The Cipher name" + " caused exception ", e); } @@ -220,7 +219,7 @@ private static SecretKey generatePremasterSecret( int clientVersion, int serverVersion, byte[] encodedSecret, SecureRandom generator) throws GeneralSecurityException { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Generating a premaster secret"); } @@ -235,7 +234,7 @@ private static SecretKey generatePremasterSecret( } catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException iae) { // unlikely to happen, otherwise, must be a provider exception - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("RSA premaster secret generation error", iae); } diff --git a/src/java.base/share/classes/sun/security/ssl/RSAServerKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/RSAServerKeyExchange.java index 8633b9458ce..43f2ef95ba8 100644 --- a/src/java.base/share/classes/sun/security/ssl/RSAServerKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/RSAServerKeyExchange.java @@ -264,7 +264,7 @@ public byte[] produce(ConnectionContext context, RSAServerKeyExchangeMessage skem = new RSAServerKeyExchangeMessage( shc, x509Possession, rsaPossession); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced RSA ServerKeyExchange handshake message", skem); } @@ -296,7 +296,7 @@ public void consume(ConnectionContext context, RSAServerKeyExchangeMessage skem = new RSAServerKeyExchangeMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming RSA ServerKeyExchange handshake message", skem); } diff --git a/src/java.base/share/classes/sun/security/ssl/RenegoInfoExtension.java b/src/java.base/share/classes/sun/security/ssl/RenegoInfoExtension.java index e1348badd30..90b9e999925 100644 --- a/src/java.base/share/classes/sun/security/ssl/RenegoInfoExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/RenegoInfoExtension.java @@ -138,7 +138,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(CH_RENEGOTIATION_INFO)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable renegotiation_info extension"); } @@ -182,7 +182,7 @@ public byte[] produce(ConnectionContext context, return extData; } else { // not secure renegotiation if (HandshakeContext.allowUnsafeRenegotiation) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("Using insecure renegotiation"); } @@ -216,7 +216,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(CH_RENEGOTIATION_INFO)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Ignore unavailable extension: " + CH_RENEGOTIATION_INFO.name); } @@ -280,7 +280,7 @@ public void absent(ConnectionContext context, for (int id : clientHello.cipherSuiteIds) { if (id == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV.id) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "Safe renegotiation, using the SCSV signaling"); } @@ -294,7 +294,7 @@ public void absent(ConnectionContext context, "Failed to negotiate the use of secure renegotiation"); } // otherwise, allow legacy hello message - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("Warning: No renegotiation " + "indication in ClientHello, allow legacy ClientHello"); } @@ -306,13 +306,13 @@ public void absent(ConnectionContext context, "Inconsistent secure renegotiation indication"); } else { // renegotiation, not secure if (HandshakeContext.allowUnsafeRenegotiation) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("Using insecure renegotiation"); } } else { // Unsafe renegotiation should have been aborted in // earlier processes. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Terminate insecure renegotiation"); } throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, @@ -345,7 +345,7 @@ public byte[] produce(ConnectionContext context, if (requestedSpec == null && !shc.conContext.secureRenegotiation) { // Ignore, no renegotiation_info extension or SCSV signaling // requested. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "Ignore unavailable renegotiation_info extension"); } @@ -354,7 +354,7 @@ public byte[] produce(ConnectionContext context, if (!shc.conContext.secureRenegotiation) { // Ignore, no secure renegotiation is negotiated. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "No secure renegotiation has been negotiated"); } @@ -515,7 +515,7 @@ public void absent(ConnectionContext context, "Failed to negotiate the use of secure renegotiation"); } // otherwise, allow legacy hello message - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("Warning: No renegotiation " + "indication in ServerHello, allow legacy ServerHello"); } @@ -527,13 +527,13 @@ public void absent(ConnectionContext context, "Inconsistent secure renegotiation indication"); } else { // renegotiation, not secure if (HandshakeContext.allowUnsafeRenegotiation) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("Using insecure renegotiation"); } } else { // Unsafe renegotiation should have been aborted in // earlier processes. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Terminate insecure renegotiation"); } throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, diff --git a/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java b/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java index 1d5a4c4e73d..594766ea0fd 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java @@ -454,7 +454,7 @@ private boolean checkRsaSsaPssParams( .equalsIgnoreCase(paramDigestAlg)); } catch (InvalidParameterSpecException e) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("Invalid AlgorithmParameters: " + parameters + "; Error: " + e.getMessage()); } diff --git a/src/java.base/share/classes/sun/security/ssl/SSLCipher.java b/src/java.base/share/classes/sun/security/ssl/SSLCipher.java index 4a52a2ea583..5dfa5be3420 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLCipher.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLCipher.java @@ -392,7 +392,7 @@ enum SSLCipher { if (values[1].contains(tag[0])) { index = 0; } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("jdk.tls.keyLimits: Unknown action: " + entry); } @@ -413,13 +413,13 @@ enum SSLCipher { "Length exceeded limits"); } } catch (NumberFormatException e) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("jdk.tls.keyLimits: " + e.getMessage() + ": " + entry); } continue; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("jdk.tls.keyLimits: entry = " + entry + ". " + values[0] + ":" + tag[index] + " = " + size); } @@ -468,7 +468,7 @@ private static boolean isTransformationAvailable(String transformation) { Cipher.getInstance(transformation); return true; } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("Transformation " + transformation + " is" + " not available."); } @@ -860,7 +860,7 @@ public Plaintext decrypt(byte contentType, ByteBuffer bb, "JCE provider " + cipher.getProvider().getName(), sbe); } pt.position(pos); - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Plaintext after DECRYPTION", pt.duplicate()); } @@ -930,7 +930,7 @@ public int encrypt(byte contentType, ByteBuffer bb) { authenticator.increaseSequenceNumber(); } - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.finest( "Padded plaintext before ENCRYPTION", bb.duplicate()); } @@ -1050,7 +1050,7 @@ public Plaintext decrypt(byte contentType, ByteBuffer bb, "JCE provider " + cipher.getProvider().getName(), sbe); } - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Padded plaintext after DECRYPTION", pt.duplicate().position(pos)); @@ -1182,7 +1182,7 @@ public int encrypt(byte contentType, ByteBuffer bb) { int len = addPadding(bb, blockSize); bb.position(pos); - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Padded plaintext before ENCRYPTION", bb.duplicate()); @@ -1326,7 +1326,7 @@ public Plaintext decrypt(byte contentType, ByteBuffer bb, "JCE provider " + cipher.getProvider().getName(), sbe); } - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine("Padded plaintext after DECRYPTION", pt.duplicate().position(pos)); } @@ -1478,7 +1478,7 @@ public int encrypt(byte contentType, ByteBuffer bb) { int len = addPadding(bb, blockSize); bb.position(pos); - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Padded plaintext before ENCRYPTION", bb.duplicate()); @@ -1650,7 +1650,7 @@ public Plaintext decrypt(byte contentType, ByteBuffer bb, pt.position(pos); pt.limit(pos + len); - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Plaintext after DECRYPTION", pt.duplicate()); } @@ -1737,7 +1737,7 @@ public int encrypt(byte contentType, // DON'T encrypt the nonce for AEAD mode. int len, pos = bb.position(); - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Plaintext before ENCRYPTION", bb.duplicate()); @@ -1823,7 +1823,7 @@ static final class GcmReadCipher extends SSLReadCipher { keyLimitCountdown = cipherLimits.getOrDefault( algorithm.toUpperCase(Locale.ENGLISH) + ":" + tag[0], 0L); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("KeyLimit read side: algorithm = " + algorithm + ":" + tag[0] + "\ncountdown value = " + keyLimitCountdown); @@ -1932,7 +1932,7 @@ public Plaintext decrypt(byte contentType, ByteBuffer bb, contentType = pt.get(i); pt.limit(i); - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Plaintext after DECRYPTION", pt.duplicate()); } @@ -1984,7 +1984,7 @@ private static final class GcmWriteCipher extends SSLWriteCipher { keyLimitCountdown = cipherLimits.getOrDefault( algorithm.toUpperCase(Locale.ENGLISH) + ":" + tag[0], 0L); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("KeyLimit write side: algorithm = " + algorithm + ":" + tag[0] + "\ncountdown value = " + keyLimitCountdown); @@ -2026,7 +2026,7 @@ public int encrypt(byte contentType, cipher.updateAAD(aad); int len, pos = bb.position(); - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Plaintext before ENCRYPTION", bb.duplicate()); @@ -2182,7 +2182,7 @@ public Plaintext decrypt(byte contentType, ByteBuffer bb, pt.position(pos); pt.limit(pos + len); - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Plaintext after DECRYPTION", pt.duplicate()); } @@ -2231,7 +2231,7 @@ private static final class CC20P1305WriteCipher extends SSLWriteCipher { keyLimitCountdown = cipherLimits.getOrDefault( algorithm.toUpperCase(Locale.ENGLISH) + ":" + tag[0], 0L); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("algorithm = " + algorithm + ":" + tag[0] + "\ncountdown value = " + keyLimitCountdown); @@ -2273,7 +2273,7 @@ public int encrypt(byte contentType, // DON'T encrypt the nonce for AEAD mode. int pos = bb.position(); - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Plaintext before ENCRYPTION", bb.duplicate()); @@ -2450,7 +2450,7 @@ public Plaintext decrypt(byte contentType, ByteBuffer bb, contentType = pt.get(i); pt.limit(i); - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Plaintext after DECRYPTION", pt.duplicate()); } @@ -2499,7 +2499,7 @@ private static final class CC20P1305WriteCipher extends SSLWriteCipher { keyLimitCountdown = cipherLimits.getOrDefault( algorithm.toUpperCase(Locale.ENGLISH) + ":" + tag[0], 0L); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("algorithm = " + algorithm + ":" + tag[0] + "\ncountdown value = " + keyLimitCountdown); @@ -2541,7 +2541,7 @@ public int encrypt(byte contentType, cipher.updateAAD(aad); int pos = bb.position(); - if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + if (SSLLogger.isOn() && SSLLogger.isOn("plaintext")) { SSLLogger.fine( "Plaintext before ENCRYPTION", bb.duplicate()); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java index 6d1834ad2b7..ace60e41af9 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java @@ -204,7 +204,7 @@ final class SSLConfiguration implements Cloneable { if (nstServerCount == null || nstServerCount < 0 || nstServerCount > 10) { serverNewSessionTicketCount = SERVER_NST_DEFAULT; - if (nstServerCount != null && SSLLogger.isOn && + if (nstServerCount != null && SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "jdk.tls.server.newSessionTicketCount defaults to " + @@ -213,7 +213,7 @@ final class SSLConfiguration implements Cloneable { } } else { serverNewSessionTicketCount = nstServerCount; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "jdk.tls.server.newSessionTicketCount set to " + serverNewSessionTicketCount); @@ -586,7 +586,7 @@ private static String[] getCustomizedSignatureScheme(String propertyName) { String property = System.getProperty(propertyName); // this method is called from class initializer; logging here // will occasionally pin threads and deadlock if called from a virtual thread - if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx") + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx") && !Thread.currentThread().isVirtual()) { SSLLogger.fine( "System property " + propertyName + " is set to '" + @@ -615,7 +615,7 @@ private static String[] getCustomizedSignatureScheme(String propertyName) { if (scheme != null && scheme.isAvailable) { signatureSchemes.add(schemeName); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx") + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx") && !Thread.currentThread().isVirtual()) { SSLLogger.fine( "The current installed providers do not " + diff --git a/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java index d50a9a10b76..be324eb0949 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java @@ -104,11 +104,11 @@ protected void engineInit(KeyManager[] km, TrustManager[] tm, * first connection to time out and fail. Make sure it is * primed and ready by getting some initial output from it. */ - if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx")) { SSLLogger.finest("trigger seeding of SecureRandom"); } secureRandom.nextInt(); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx")) { SSLLogger.finest("done seeding of SecureRandom"); } @@ -143,7 +143,7 @@ private X509ExtendedKeyManager chooseKeyManager(KeyManager[] kms) { return (X509ExtendedKeyManager)km; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx")) { SSLLogger.warning( "X509KeyManager passed to SSLContext.init(): need an " + "X509ExtendedKeyManager for SSLEngine use"); @@ -246,7 +246,7 @@ StatusResponseManager getStatusResponseManager() { contextLock.lock(); try { if (statusResponseManager == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx")) { SSLLogger.finest( "Initializing StatusResponseManager"); } @@ -383,7 +383,7 @@ private static List getApplicableCipherSuites( suite.name, null)) { suites.add(suite); isSupported = true; - } else if (SSLLogger.isOn && + } else if (SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx,verbose")) { SSLLogger.fine( "Ignore disabled cipher suite: " + suite.name); @@ -392,7 +392,7 @@ private static List getApplicableCipherSuites( break; } - if (!isSupported && SSLLogger.isOn && + if (!isSupported && SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx,verbose")) { SSLLogger.finest( "Ignore unsupported cipher suite: " + suite); @@ -410,7 +410,7 @@ private static Collection getCustomizedCipherSuites( String propertyName) { String property = System.getProperty(propertyName); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx")) { SSLLogger.fine( "System property " + propertyName + " is set to '" + property + "'"); @@ -437,7 +437,7 @@ private static Collection getCustomizedCipherSuites( try { suite = CipherSuite.nameOf(cipherSuiteNames[i]); } catch (IllegalArgumentException iae) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx")) { SSLLogger.fine( "Unknown or unsupported cipher suite name: " + cipherSuiteNames[i]); @@ -449,7 +449,7 @@ private static Collection getCustomizedCipherSuites( if (suite != null && suite.isAvailable()) { cipherSuites.add(suite); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx")) { SSLLogger.fine( "The current installed providers do not " + "support cipher suite: " + cipherSuiteNames[i]); @@ -907,7 +907,7 @@ private static final class DefaultManagersHolder { tmMediator = getTrustManagers(); } catch (Exception e) { reserved = e; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,defaultctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,defaultctx")) { SSLLogger.warning( "Failed to load default trust managers", e); } @@ -919,7 +919,7 @@ private static final class DefaultManagersHolder { kmMediator = getKeyManagers(); } catch (Exception e) { reserved = e; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,defaultctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,defaultctx")) { SSLLogger.warning( "Failed to load default key managers", e); } @@ -977,7 +977,7 @@ private static KeyManager[] getKeyManagers() throws Exception { String defaultKeyStore = props.get("keyStore"); String defaultKeyStoreType = props.get("keyStoreType"); String defaultKeyStoreProvider = props.get("keyStoreProvider"); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,defaultctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,defaultctx")) { SSLLogger.fine("keyStore is : " + defaultKeyStore); SSLLogger.fine("keyStore type is : " + defaultKeyStoreType); @@ -1007,7 +1007,7 @@ private static KeyManager[] getKeyManagers() throws Exception { // Try to initialize key store. if ((defaultKeyStoreType.length()) != 0) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,defaultctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,defaultctx")) { SSLLogger.finest("init keystore"); } if (defaultKeyStoreProvider.isEmpty()) { @@ -1030,7 +1030,7 @@ private static KeyManager[] getKeyManagers() throws Exception { /* * Try to initialize key manager. */ - if (SSLLogger.isOn && SSLLogger.isOn("ssl,defaultctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,defaultctx")) { SSLLogger.fine("init keymanager of type " + KeyManagerFactory.getDefaultAlgorithm()); } @@ -1068,7 +1068,7 @@ private static final class DefaultSSLContextHolder { // exception object, which may be not garbage collection // friendly as 'reservedException' is a static filed. reserved = new KeyManagementException(e.getMessage()); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,defaultctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,defaultctx")) { SSLLogger.warning( "Failed to load default SSLContext", e); } @@ -1097,7 +1097,7 @@ public DefaultSSLContext() throws Exception { super.engineInit(DefaultManagersHolder.keyManagers, DefaultManagersHolder.trustManagers, null); } catch (Exception e) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,defaultctx")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,defaultctx")) { SSLLogger.fine("default context init failed: ", e); } throw e; diff --git a/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java index 4b19f5a9d7b..5e23e6ee37b 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -330,7 +330,7 @@ private Ciphertext encode( // application data may be discarded accordingly. As could // be an issue for some applications. This impact can be // mitigated by sending the last flight twice. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,verbose")) { SSLLogger.finest("retransmit the last flight messages"); } @@ -397,7 +397,7 @@ private HandshakeStatus tryKeyUpdate( if ((conContext.handshakeContext == null) && !conContext.isOutboundClosed() && !conContext.isBroken) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("trigger key update"); } beginHandshake(); @@ -419,7 +419,7 @@ private HandshakeStatus tryNewSessionTicket( !conContext.isOutboundClosed() && !conContext.isInboundClosed() && !conContext.isBroken) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("trigger NST"); } conContext.conSession.updateNST = false; @@ -612,7 +612,7 @@ private SSLEngineResult readRecord( } catch (SSLException ssle) { // Need to discard invalid records for DTLS protocols. if (sslContext.isDTLS()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,verbose")) { SSLLogger.finest("Discard invalid DTLS records", ssle); } @@ -780,7 +780,7 @@ public void closeInbound() throws SSLException { return; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("Closing inbound of SSLEngine"); } @@ -819,7 +819,7 @@ public void closeOutbound() { return; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("Closing outbound of SSLEngine"); } diff --git a/src/java.base/share/classes/sun/security/ssl/SSLEngineInputRecord.java b/src/java.base/share/classes/sun/security/ssl/SSLEngineInputRecord.java index 1bfbd9f51bf..6e08fc71664 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLEngineInputRecord.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLEngineInputRecord.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -172,7 +172,7 @@ private Plaintext[] decode(ByteBuffer packet) return null; } - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine("Raw read", packet); } @@ -209,7 +209,7 @@ private Plaintext[] decodeInputRecord(ByteBuffer packet) byte minorVersion = packet.get(); // pos: 2 int contentLen = Record.getInt16(packet); // pos: 3, 4 - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "READ: " + ProtocolVersion.nameOf(majorVersion, minorVersion) + @@ -388,7 +388,7 @@ private Plaintext[] handleUnknownRecord(ByteBuffer packet) * error message, one that's treated as fatal by * clients (Otherwise we'll hang.) */ - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "Requested to negotiate unsupported SSLv2!"); } @@ -410,7 +410,7 @@ private Plaintext[] handleUnknownRecord(ByteBuffer packet) ByteBuffer converted = convertToClientHello(packet); - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine( "[Converted] ClientHello", converted); } diff --git a/src/java.base/share/classes/sun/security/ssl/SSLEngineOutputRecord.java b/src/java.base/share/classes/sun/security/ssl/SSLEngineOutputRecord.java index 4a689a84d5f..1c8751e66fe 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLEngineOutputRecord.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLEngineOutputRecord.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -73,7 +73,7 @@ boolean isClosed() { @Override void encodeAlert(byte level, byte description) { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "alert message: " + Alert.nameOf(description)); } @@ -91,7 +91,7 @@ void encodeAlert(byte level, byte description) { void encodeHandshake(byte[] source, int offset, int length) { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "handshake message", ByteBuffer.wrap(source, offset, length)); @@ -138,7 +138,7 @@ void encodeHandshake(byte[] source, @Override void encodeChangeCipherSpec() { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "change_cipher_spec message"); } @@ -171,14 +171,14 @@ Ciphertext encode( ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException { if (isClosed) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "application data or cached messages"); } return null; } else if (isCloseWaiting) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "application data"); } @@ -193,7 +193,7 @@ private Ciphertext encode(ByteBuffer[] sources, int offset, int length, ByteBuffer destination) throws IOException { if (writeCipher.authenticator.seqNumOverflow()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine( "sequence number extremely close to overflow " + "(2^64-1 packets). Closing connection."); @@ -275,7 +275,7 @@ private Ciphertext encode(ByteBuffer[] sources, int offset, int length, destination.limit(destination.position()); destination.position(dstContent); - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "WRITE: " + protocolVersion.name + " " + ContentType.APPLICATION_DATA.name + @@ -288,7 +288,7 @@ private Ciphertext encode(ByteBuffer[] sources, int offset, int length, dstPos, dstLim, headerSize, protocolVersion); - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { ByteBuffer temporary = destination.duplicate(); temporary.limit(temporary.position()); temporary.position(dstPos); @@ -317,7 +317,7 @@ private Ciphertext acquireCiphertext( // // Please don't change the limit of the destination buffer. destination.put(SSLRecord.v2NoCipher); - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine("Raw write", SSLRecord.v2NoCipher); } @@ -331,7 +331,7 @@ private Ciphertext acquireCiphertext( // deliver the SSLv2 format ClientHello message // // Please don't change the limit of the destination buffer. - if (SSLLogger.isOn) { + if (SSLLogger.isOn()) { if (SSLLogger.isOn("record")) { SSLLogger.fine(Thread.currentThread().getName() + ", WRITE: SSLv2 ClientHello message" + @@ -525,7 +525,7 @@ Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException { dstBuf.limit(dstBuf.position()); dstBuf.position(dstContent); - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "WRITE: " + protocolVersion.name + " " + ContentType.nameOf(memo.contentType) + @@ -543,7 +543,7 @@ Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException { memo.encodeCipher.dispose(); } - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { ByteBuffer temporary = dstBuf.duplicate(); temporary.limit(temporary.position()); temporary.position(dstPos); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLExtension.java b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java index fb0490d70f1..47a0d0b0e44 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java @@ -844,7 +844,7 @@ private static Collection getDisabledExtensions( String property = System.getProperty(propertyName); // this method is called from class initializer; logging here // will occasionally pin threads and deadlock if called from a virtual thread - if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx") + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,sslctx") && !Thread.currentThread().isVirtual()) { SSLLogger.fine( "System property " + propertyName + " is set to '" + diff --git a/src/java.base/share/classes/sun/security/ssl/SSLExtensions.java b/src/java.base/share/classes/sun/security/ssl/SSLExtensions.java index 5ad93cfc836..66f6293302e 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLExtensions.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLExtensions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -43,7 +43,7 @@ final class SSLExtensions { // Extension map for debug logging private final Map logMap = - SSLLogger.isOn ? new LinkedHashMap<>() : null; + SSLLogger.isOn() ? new LinkedHashMap<>() : null; SSLExtensions(HandshakeMessage handshakeMessage) { this.handshakeMessage = handshakeMessage; @@ -93,7 +93,7 @@ final class SSLExtensions { // However, the implementation of the limit is complicated // and inefficient, and may not worthy the maintenance. isSupported = false; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Received buggy supported_groups extension " + "in the ServerHello handshake message"); @@ -143,7 +143,7 @@ final class SSLExtensions { m.get(extData); logMap.put(extId, extData); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unknown or unsupported extension", toString(extId, extData)); @@ -171,7 +171,7 @@ void consumeOnLoad(HandshakeContext context, for (SSLExtension extension : extensions) { if (context.negotiatedProtocol != null && !extension.isAvailable(context.negotiatedProtocol)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unsupported extension: " + extension.name); } @@ -181,7 +181,7 @@ void consumeOnLoad(HandshakeContext context, if (!extMap.containsKey(extension)) { if (extension.onLoadAbsence != null) { extension.absentOnLoad(context, handshakeMessage); - } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + } else if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extension: " + extension.name); } @@ -190,7 +190,7 @@ void consumeOnLoad(HandshakeContext context, if (extension.onLoadConsumer == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore unsupported extension: " + extension.name); } @@ -200,7 +200,7 @@ void consumeOnLoad(HandshakeContext context, ByteBuffer m = ByteBuffer.wrap(extMap.get(extension)); extension.consumeOnLoad(context, handshakeMessage, m); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Consumed extension: " + extension.name); } } @@ -215,7 +215,7 @@ void consumeOnTrade(HandshakeContext context, if (!extMap.containsKey(extension)) { if (extension.onTradeAbsence != null) { extension.absentOnTrade(context, handshakeMessage); - } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + } else if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extension: " + extension.name); } @@ -223,7 +223,7 @@ void consumeOnTrade(HandshakeContext context, } if (extension.onTradeConsumer == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore impact of unsupported extension: " + extension.name); @@ -232,7 +232,7 @@ void consumeOnTrade(HandshakeContext context, } extension.consumeOnTrade(context, handshakeMessage); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Populated with extension: " + extension.name); } } @@ -245,7 +245,7 @@ void produce(HandshakeContext context, SSLExtension[] extensions) throws IOException { for (SSLExtension extension : extensions) { if (extMap.containsKey(extension)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore, duplicated extension: " + extension.name); @@ -254,7 +254,7 @@ void produce(HandshakeContext context, } if (extension.networkProducer == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore, no extension producer defined: " + extension.name); @@ -267,7 +267,7 @@ void produce(HandshakeContext context, extMap.put(extension, encoded); encodedLength += encoded.length + 4; // extension_type (2) // extension_data length(2) - } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + } else if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { // The extension is not available in the context. SSLLogger.fine( "Ignore, context unavailable extension: " + @@ -284,7 +284,7 @@ void reproduce(HandshakeContext context, SSLExtension[] extensions) throws IOException { for (SSLExtension extension : extensions) { if (extension.networkProducer == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore, no extension producer defined: " + extension.name); @@ -305,7 +305,7 @@ void reproduce(HandshakeContext context, encodedLength += encoded.length + 4; // extension_type (2) // extension_data length(2) - } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + } else if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { // The extension is not available in the context. SSLLogger.fine( "Ignore, context unavailable extension: " + diff --git a/src/java.base/share/classes/sun/security/ssl/SSLLogger.java b/src/java.base/share/classes/sun/security/ssl/SSLLogger.java index f55ab27d297..7fa6fbf91b5 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLLogger.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLLogger.java @@ -29,8 +29,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; -import java.lang.System.Logger; -import java.lang.System.Logger.Level; import java.nio.ByteBuffer; import java.security.cert.Certificate; import java.security.cert.Extension; @@ -41,6 +39,7 @@ import java.time.format.DateTimeFormatter; import java.util.*; +import jdk.internal.vm.annotation.ForceInline; import sun.security.util.HexDumpEncoder; import sun.security.util.Debug; import sun.security.x509.*; @@ -58,10 +57,13 @@ * logging mechanisms. If the system property "javax.net.debug" is defined * and non-empty, a private debug logger implemented in this class is used. */ -public final class SSLLogger { +public final class SSLLogger implements System.Logger { private static final System.Logger logger; private static final String property; - public static final boolean isOn; + private static final boolean isOn; + + private final String loggerName; + private final boolean useCompactFormat; static { @@ -76,7 +78,7 @@ public final class SSLLogger { help(); } - logger = new SSLConsoleLogger("javax.net.ssl", p); + logger = new SSLLogger("javax.net.ssl", p); } isOn = true; } else { @@ -86,35 +88,10 @@ public final class SSLLogger { } } - private static void help() { - System.err.println(); - System.err.println("help print the help messages"); - System.err.println("expand expand debugging information"); - System.err.println(); - System.err.println("all turn on all debugging"); - System.err.println("ssl turn on ssl debugging"); - System.err.println(); - System.err.println("The following can be used with ssl:"); - System.err.println("\trecord enable per-record tracing"); - System.err.println("\thandshake print each handshake message"); - System.err.println("\tkeygen print key generation data"); - System.err.println("\tsession print session activity"); - System.err.println("\tdefaultctx print default SSL initialization"); - System.err.println("\tsslctx print SSLContext tracing"); - System.err.println("\tsessioncache print session cache tracing"); - System.err.println("\tkeymanager print key manager tracing"); - System.err.println("\ttrustmanager print trust manager tracing"); - System.err.println("\tpluggability print pluggability tracing"); - System.err.println(); - System.err.println("\thandshake debugging can be widened with:"); - System.err.println("\tdata hex dump of each handshake message"); - System.err.println("\tverbose verbose handshake message printing"); - System.err.println(); - System.err.println("\trecord debugging can be widened with:"); - System.err.println("\tplaintext hex dump of record plaintext"); - System.err.println("\tpacket print raw SSL/TLS packets"); - System.err.println(); - System.exit(0); + private SSLLogger(String loggerName, String options) { + this.loggerName = loggerName; + options = options.toLowerCase(Locale.ENGLISH); + this.useCompactFormat = !options.contains("expand"); } /** @@ -139,6 +116,11 @@ public static boolean isOn(String checkPoints) { return true; } + @ForceInline + public static boolean isOn() { + return isOn; + } + private static boolean hasOption(String option) { option = option.toLowerCase(Locale.ENGLISH); if (property.contains("all")) { @@ -161,30 +143,30 @@ private static boolean hasOption(String option) { } public static void severe(String msg, Object... params) { - SSLLogger.log(Level.ERROR, msg, params); + SSLLogger.log0(Level.ERROR, msg, params); } public static void warning(String msg, Object... params) { - SSLLogger.log(Level.WARNING, msg, params); + SSLLogger.log0(Level.WARNING, msg, params); } public static void info(String msg, Object... params) { - SSLLogger.log(Level.INFO, msg, params); + SSLLogger.log0(Level.INFO, msg, params); } public static void fine(String msg, Object... params) { - SSLLogger.log(Level.DEBUG, msg, params); + SSLLogger.log0(Level.DEBUG, msg, params); } public static void finer(String msg, Object... params) { - SSLLogger.log(Level.TRACE, msg, params); + SSLLogger.log0(Level.TRACE, msg, params); } public static void finest(String msg, Object... params) { - SSLLogger.log(Level.TRACE, msg, params); + SSLLogger.log0(Level.TRACE, msg, params); } - private static void log(Level level, String msg, Object... params) { + private static void log0(Level level, String msg, Object... params) { if (logger != null && logger.isLoggable(level)) { if (params == null || params.length == 0) { logger.log(level, msg); @@ -192,8 +174,8 @@ private static void log(Level level, String msg, Object... params) { try { String formatted = SSLSimpleFormatter.formatParameters(params); - // use the customized log method for SSLConsoleLogger - if (logger instanceof SSLConsoleLogger) { + // use the customized log method for SSLLogger + if (logger instanceof SSLLogger) { logger.log(level, msg, formatted); } else { logger.log(level, msg + ":" + LINE_SEP + formatted); @@ -205,6 +187,37 @@ private static void log(Level level, String msg, Object... params) { } } + private static void help() { + System.err.println(); + System.err.println("help print the help messages"); + System.err.println("expand expand debugging information"); + System.err.println(); + System.err.println("all turn on all debugging"); + System.err.println("ssl turn on ssl debugging"); + System.err.println(); + System.err.println("The following can be used with ssl:"); + System.err.println("\trecord enable per-record tracing"); + System.err.println("\thandshake print each handshake message"); + System.err.println("\tkeygen print key generation data"); + System.err.println("\tsession print session activity"); + System.err.println("\tdefaultctx print default SSL initialization"); + System.err.println("\tsslctx print SSLContext tracing"); + System.err.println("\tsessioncache print session cache tracing"); + System.err.println("\tkeymanager print key manager tracing"); + System.err.println("\ttrustmanager print trust manager tracing"); + System.err.println("\tpluggability print pluggability tracing"); + System.err.println(); + System.err.println("\thandshake debugging can be widened with:"); + System.err.println("\tdata hex dump of each handshake message"); + System.err.println("\tverbose verbose handshake message printing"); + System.err.println(); + System.err.println("\trecord debugging can be widened with:"); + System.err.println("\tplaintext hex dump of record plaintext"); + System.err.println("\tpacket print raw SSL/TLS packets"); + System.err.println(); + System.exit(0); + } + static String toString(Object... params) { try { return SSLSimpleFormatter.formatParameters(params); @@ -216,65 +229,55 @@ static String toString(Object... params) { // Logs a warning message and always returns false. This method // can be used as an OR Predicate to add a log in a stream filter. public static boolean logWarning(String option, String s) { - if (SSLLogger.isOn && SSLLogger.isOn(option)) { + if (SSLLogger.isOn() && SSLLogger.isOn(option)) { SSLLogger.warning(s); } return false; } - private static class SSLConsoleLogger implements Logger { - private final String loggerName; - private final boolean useCompactFormat; - - SSLConsoleLogger(String loggerName, String options) { - this.loggerName = loggerName; - options = options.toLowerCase(Locale.ENGLISH); - this.useCompactFormat = !options.contains("expand"); - } - - @Override - public String getName() { - return loggerName; - } + @Override + public String getName() { + return loggerName; + } - @Override - public boolean isLoggable(Level level) { - return level != Level.OFF; - } + @Override + public boolean isLoggable(Level level) { + return level != Level.OFF; + } - @Override - public void log(Level level, - ResourceBundle rb, String message, Throwable thrwbl) { - if (isLoggable(level)) { - try { - String formatted = + @Override + public void log(Level level, + ResourceBundle rb, String message, Throwable thrwbl) { + if (isLoggable(level)) { + try { + String formatted = SSLSimpleFormatter.format(this, level, message, thrwbl); - System.err.write(formatted.getBytes(UTF_8)); - } catch (Exception exp) { - // ignore it, just for debugging. - } + System.err.write(formatted.getBytes(UTF_8)); + } catch (Exception exp) { + // ignore it, just for debugging. } } + } - @Override - public void log(Level level, - ResourceBundle rb, String message, Object... params) { - if (isLoggable(level)) { - try { - String formatted = + @Override + public void log(Level level, + ResourceBundle rb, String message, Object... params) { + if (isLoggable(level)) { + try { + String formatted = SSLSimpleFormatter.format(this, level, message, params); - System.err.write(formatted.getBytes(UTF_8)); - } catch (Exception exp) { - // ignore it, just for debugging. - } + System.err.write(formatted.getBytes(UTF_8)); + } catch (Exception exp) { + // ignore it, just for debugging. } } } private static class SSLSimpleFormatter { private static final String PATTERN = "yyyy-MM-dd kk:mm:ss.SSS z"; - private static final DateTimeFormatter dateTimeFormat = DateTimeFormatter.ofPattern(PATTERN, Locale.ENGLISH) - .withZone(ZoneId.systemDefault()); + private static final DateTimeFormatter dateTimeFormat = + DateTimeFormatter.ofPattern(PATTERN, Locale.ENGLISH) + .withZone(ZoneId.systemDefault()); private static final MessageFormat basicCertFormat = new MessageFormat( """ @@ -290,68 +293,68 @@ private static class SSLSimpleFormatter { Locale.ENGLISH); private static final MessageFormat extendedCertFormat = - new MessageFormat( - """ - "version" : "v{0}", - "serial number" : "{1}", - "signature algorithm": "{2}", - "issuer" : "{3}", - "not before" : "{4}", - "not after" : "{5}", - "subject" : "{6}", - "subject public key" : "{7}", - "extensions" : [ - {8} - ] - """, - Locale.ENGLISH); + new MessageFormat( + """ + "version" : "v{0}", + "serial number" : "{1}", + "signature algorithm": "{2}", + "issuer" : "{3}", + "not before" : "{4}", + "not after" : "{5}", + "subject" : "{6}", + "subject public key" : "{7}", + "extensions" : [ + {8} + ] + """, + Locale.ENGLISH); private static final MessageFormat messageFormatNoParas = - new MessageFormat( - """ - '{' - "logger" : "{0}", - "level" : "{1}", - "thread id" : "{2}", - "thread name" : "{3}", - "time" : "{4}", - "caller" : "{5}", - "message" : "{6}" - '}' - """, - Locale.ENGLISH); + new MessageFormat( + """ + '{' + "logger" : "{0}", + "level" : "{1}", + "thread id" : "{2}", + "thread name" : "{3}", + "time" : "{4}", + "caller" : "{5}", + "message" : "{6}" + '}' + """, + Locale.ENGLISH); private static final MessageFormat messageCompactFormatNoParas = - new MessageFormat( - "{0}|{1}|{2}|{3}|{4}|{5}|{6}" + LINE_SEP, - Locale.ENGLISH); + new MessageFormat( + "{0}|{1}|{2}|{3}|{4}|{5}|{6}" + LINE_SEP, + Locale.ENGLISH); private static final MessageFormat messageFormatWithParas = - new MessageFormat( - """ - '{' - "logger" : "{0}", - "level" : "{1}", - "thread id" : "{2}", - "thread name" : "{3}", - "time" : "{4}", - "caller" : "{5}", - "message" : "{6}", - "specifics" : [ - {7} - ] - '}' - """, - Locale.ENGLISH); + new MessageFormat( + """ + '{' + "logger" : "{0}", + "level" : "{1}", + "thread id" : "{2}", + "thread name" : "{3}", + "time" : "{4}", + "caller" : "{5}", + "message" : "{6}", + "specifics" : [ + {7} + ] + '}' + """, + Locale.ENGLISH); private static final MessageFormat messageCompactFormatWithParas = - new MessageFormat( - """ - {0}|{1}|{2}|{3}|{4}|{5}|{6} ( - {7} - ) - """, - Locale.ENGLISH); + new MessageFormat( + """ + {0}|{1}|{2}|{3}|{4}|{5}|{6} ( + {7} + ) + """, + Locale.ENGLISH); private static final MessageFormat keyObjectFormat = new MessageFormat( """ @@ -364,8 +367,8 @@ private static class SSLSimpleFormatter { // log message // log message // ... - private static String format(SSLConsoleLogger logger, Level level, - String message, Object ... parameters) { + private static String format(SSLLogger logger, Level level, + String message, Object... parameters) { if (parameters == null || parameters.length == 0) { Object[] messageFields = { @@ -394,9 +397,9 @@ private static String format(SSLConsoleLogger logger, Level level, formatCaller(), message, (logger.useCompactFormat ? - formatParameters(parameters) : - Utilities.indent(formatParameters(parameters))) - }; + formatParameters(parameters) : + Utilities.indent(formatParameters(parameters))) + }; if (logger.useCompactFormat) { return messageCompactFormatWithParas.format(messageFields); @@ -414,7 +417,7 @@ private static String formatCaller() { .findFirst().orElse("unknown caller")); } - private static String formatParameters(Object ... parameters) { + private static String formatParameters(Object... parameters) { StringBuilder builder = new StringBuilder(512); boolean isFirst = true; for (Object parameter : parameters) { @@ -425,21 +428,21 @@ private static String formatParameters(Object ... parameters) { } if (parameter instanceof Throwable) { - builder.append(formatThrowable((Throwable)parameter)); + builder.append(formatThrowable((Throwable) parameter)); } else if (parameter instanceof Certificate) { - builder.append(formatCertificate((Certificate)parameter)); + builder.append(formatCertificate((Certificate) parameter)); } else if (parameter instanceof ByteArrayInputStream) { builder.append(formatByteArrayInputStream( - (ByteArrayInputStream)parameter)); + (ByteArrayInputStream) parameter)); } else if (parameter instanceof ByteBuffer) { - builder.append(formatByteBuffer((ByteBuffer)parameter)); + builder.append(formatByteBuffer((ByteBuffer) parameter)); } else if (parameter instanceof byte[]) { builder.append(formatByteArrayInputStream( - new ByteArrayInputStream((byte[])parameter))); + new ByteArrayInputStream((byte[]) parameter))); } else if (parameter instanceof Map.Entry) { @SuppressWarnings("unchecked") Map.Entry mapParameter = - (Map.Entry)parameter; + (Map.Entry) parameter; builder.append(formatMapEntry(mapParameter)); } else { builder.append(formatObject(parameter)); @@ -462,7 +465,7 @@ private static String formatThrowable(Throwable throwable) { Object[] fields = { "throwable", builder.toString() - }; + }; return keyObjectFormat.format(fields); } @@ -479,7 +482,7 @@ private static String formatCertificate(Certificate certificate) { StringBuilder builder = new StringBuilder(512); try { X509CertImpl x509 = - X509CertImpl.toImpl((X509Certificate)certificate); + X509CertImpl.toImpl((X509Certificate) certificate); X509CertInfo certInfo = x509.getInfo(); CertificateExtensions certExts = certInfo.getExtensions(); if (certExts == null) { @@ -528,7 +531,7 @@ private static String formatCertificate(Certificate certificate) { Object[] fields = { "certificate", builder.toString() - }; + }; return Utilities.indent(keyObjectFormat.format(fields)); } @@ -591,13 +594,13 @@ private static String formatMapEntry(Map.Entry entry) { formatted = builder.toString(); } else if (value instanceof byte[]) { formatted = "\"" + key + "\": \"" + - Utilities.toHexString((byte[])value) + "\""; + Utilities.toHexString((byte[]) value) + "\""; } else if (value instanceof Byte) { formatted = "\"" + key + "\": \"" + - HexFormat.of().toHexDigits((byte)value) + "\""; + HexFormat.of().toHexDigits((byte) value) + "\""; } else { formatted = "\"" + key + "\": " + - "\"" + value.toString() + "\""; + "\"" + value.toString() + "\""; } return Utilities.indent(formatted); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLMasterKeyDerivation.java b/src/java.base/share/classes/sun/security/ssl/SSLMasterKeyDerivation.java index db5887c5e8e..4de29b7570a 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLMasterKeyDerivation.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLMasterKeyDerivation.java @@ -29,7 +29,6 @@ import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.security.ProviderException; -import java.security.spec.AlgorithmParameterSpec; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import sun.security.internal.spec.TlsMasterSecretParameterSpec; @@ -152,7 +151,7 @@ public SecretKey deriveKey(String typeNotUsed) throws IOException { // // For RSA premaster secrets, do not signal a protocol error // due to the Bleichenbacher attack. See comments further down. - if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("handshake")) { SSLLogger.fine("RSA master secret generation error.", iae); } throw new ProviderException(iae); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java index 4baa3304fee..f713f723ea0 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java @@ -339,7 +339,7 @@ private int getDefaults(boolean server) { if (t < 0 || t > NewSessionTicket.MAX_TICKET_LIFETIME) { timeout = DEFAULT_SESSION_TIMEOUT; - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("Invalid timeout given " + "jdk.tls.server.sessionTicketTimeout: " + t + ". Set to default value " + timeout); @@ -349,7 +349,7 @@ private int getDefaults(boolean server) { } } catch (NumberFormatException e) { setSessionTimeout(DEFAULT_SESSION_TIMEOUT); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("Invalid timeout for " + "jdk.tls.server.sessionTicketTimeout: " + s + ". Set to default value " + timeout); @@ -363,7 +363,7 @@ private int getDefaults(boolean server) { if (defaultCacheLimit >= 0) { return defaultCacheLimit; - } else if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + } else if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning( "invalid System Property javax.net.ssl.sessionCacheSize, " + "use the default session cache size (" + @@ -371,7 +371,7 @@ private int getDefaults(boolean server) { } } catch (Exception e) { // unlikely, log it for safe - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning( "the System Property javax.net.ssl.sessionCacheSize is " + "not available, use the default value (" + diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java index 1bf561c47e6..f3a4b964158 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java @@ -223,7 +223,7 @@ final class SSLSessionImpl extends ExtendedSSLSession { this.identificationProtocol = hc.sslConfig.identificationProtocol; this.boundValues = new ConcurrentHashMap<>(); - if (SSLLogger.isOn && SSLLogger.isOn("session")) { + if (SSLLogger.isOn() && SSLLogger.isOn("session")) { SSLLogger.finest("Session initialized: " + this); } } @@ -256,7 +256,7 @@ final class SSLSessionImpl extends ExtendedSSLSession { this.maximumPacketSize = baseSession.maximumPacketSize; this.boundValues = baseSession.boundValues; - if (SSLLogger.isOn && SSLLogger.isOn("session")) { + if (SSLLogger.isOn() && SSLLogger.isOn("session")) { SSLLogger.finest("Session initialized: " + this); } } @@ -455,7 +455,7 @@ final class SSLSessionImpl extends ExtendedSSLSession { if (same) { this.localCerts = ((X509Possession) pos).popCerts; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,session")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,session")) { SSLLogger.fine("Restored " + len + " local certificates from session ticket" + " for algorithms " + Arrays.toString(certAlgs)); @@ -463,7 +463,7 @@ final class SSLSessionImpl extends ExtendedSSLSession { } else { this.localCerts = null; this.invalidated = true; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,session")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,session")) { SSLLogger.warning("Local certificates can not be restored " + "from session ticket " + "for algorithms " + Arrays.toString(certAlgs)); @@ -482,7 +482,7 @@ boolean isStatelessable() { // If there is no getMasterSecret with TLS1.2 or under, do not resume. if (!protocolVersion.useTLS13PlusSpec() && getMasterSecret().getEncoded() == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest("No MasterSecret, cannot make stateless" + " ticket"); } @@ -490,7 +490,7 @@ boolean isStatelessable() { } if (boundValues != null && boundValues.size() > 0) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest("There are boundValues, cannot make" + " stateless ticket"); } @@ -862,7 +862,7 @@ CipherSuite getSuite() { void setSuite(CipherSuite suite) { cipherSuite = suite; - if (SSLLogger.isOn && SSLLogger.isOn("session")) { + if (SSLLogger.isOn() && SSLLogger.isOn("session")) { SSLLogger.finest("Negotiating session: " + this); } } @@ -1132,7 +1132,7 @@ public void invalidate() { return; } invalidated = true; - if (SSLLogger.isOn && SSLLogger.isOn("session")) { + if (SSLLogger.isOn() && SSLLogger.isOn("session")) { SSLLogger.finest("Invalidated session: " + this); } for (SSLSessionImpl child : childSessions) { diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java index be95a09006f..ab01a9d85be 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -370,7 +370,7 @@ public SSLSession getSession() { // start handshaking, if failed, the connection will be closed. ensureNegotiated(false); } catch (IOException ioe) { - if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("handshake")) { SSLLogger.severe("handshake failed", ioe); } @@ -573,7 +573,7 @@ public void close() throws IOException { return; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("duplex close of SSLSocket"); } @@ -591,7 +591,7 @@ public void close() throws IOException { } } catch (IOException ioe) { // ignore the exception - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("SSLSocket duplex close failed. Debug info only. Exception details:", ioe); } } finally { @@ -601,7 +601,7 @@ public void close() throws IOException { closeSocket(false); } catch (IOException ioe) { // ignore the exception - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("SSLSocket close failed. Debug info only. Exception details:", ioe); } } finally { @@ -696,7 +696,7 @@ void closeNotify(boolean useUserCanceled) throws IOException { "close_notify message cannot be sent."); } else { super.shutdownOutput(); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning( "SSLSocket output duplex close failed: " + "SO_LINGER timeout, " + @@ -717,7 +717,7 @@ void closeNotify(boolean useUserCanceled) throws IOException { // failed to send the close_notify message. // conContext.conSession.invalidate(); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning( "Invalidate the session: SO_LINGER timeout, " + "close_notify message cannot be sent."); @@ -832,7 +832,7 @@ private void shutdownInput( return; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("close inbound of SSLSocket"); } @@ -868,7 +868,7 @@ public void shutdownOutput() throws IOException { return; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("close outbound of SSLSocket"); } conContext.closeOutbound(); @@ -1027,7 +1027,7 @@ public int read(byte[] b, int off, int len) throws IOException { // filed is checked here, in case the closing process is // still in progress. if (hasDepleted) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("The input stream has been depleted"); } @@ -1048,7 +1048,7 @@ public int read(byte[] b, int off, int len) throws IOException { // Double check if the input stream has been depleted. if (hasDepleted) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("The input stream is closing"); } @@ -1134,7 +1134,7 @@ public long skip(long n) throws IOException { @Override public void close() throws IOException { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("Closing input stream"); } @@ -1142,7 +1142,7 @@ public void close() throws IOException { SSLSocketImpl.this.close(); } catch (IOException ioe) { // ignore the exception - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("input stream close failed. Debug info only. Exception details:", ioe); } } @@ -1218,7 +1218,7 @@ private void readLockedDeplete() { socketInputRecord.deplete( conContext.isNegotiated && (getSoTimeout() > 0)); } catch (Exception ex) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning( "input stream close depletion failed", ex); } @@ -1327,7 +1327,7 @@ public void write(byte[] b, @Override public void close() throws IOException { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("Closing output stream"); } @@ -1335,7 +1335,7 @@ public void close() throws IOException { SSLSocketImpl.this.close(); } catch (IOException ioe) { // ignore the exception - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("output stream close failed. Debug info only. Exception details:", ioe); } } @@ -1543,7 +1543,7 @@ private void tryKeyUpdate() throws IOException { if ((conContext.handshakeContext == null) && !conContext.isOutboundClosed() && !conContext.isBroken) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("trigger key update"); } startHandshake(); @@ -1562,7 +1562,7 @@ private void tryNewSessionTicket() throws IOException { !conContext.isOutboundClosed() && !conContext.isInboundClosed() && !conContext.isBroken) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("trigger new session ticket"); } conContext.conSession.updateNST = false; @@ -1670,7 +1670,7 @@ public void setHost(String host) { * This method never returns normally, it always throws an IOException. */ private void handleException(Exception cause) throws IOException { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("handling exception", cause); } @@ -1747,7 +1747,7 @@ public boolean useDelegatedTask() { @Override public void shutdown() throws IOException { if (!isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("close the underlying socket"); } @@ -1773,7 +1773,7 @@ public String toString() { } private void closeSocket(boolean selfInitiated) throws IOException { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("close the SSL connection " + (selfInitiated ? "(initiative)" : "(passive)")); } @@ -1828,7 +1828,7 @@ private void closeSocket(boolean selfInitiated) throws IOException { * transport without waiting for the responding close_notify. */ private void waitForClose() throws IOException { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("wait for close_notify or alert"); } @@ -1838,7 +1838,7 @@ private void waitForClose() throws IOException { try { Plaintext plainText = decode(null); // discard and continue - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest( "discard plaintext while waiting for close", plainText); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java b/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java index 09223a4485d..ce7ab630730 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, Azul Systems, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -210,7 +210,7 @@ private Plaintext[] decodeInputRecord() throws IOException, BadPaddingException int contentLen = ((header[3] & 0xFF) << 8) + (header[4] & 0xFF); // pos: 3, 4 - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "READ: " + ProtocolVersion.nameOf(majorVersion, minorVersion) + @@ -243,7 +243,7 @@ private Plaintext[] decodeInputRecord() throws IOException, BadPaddingException readFully(contentLen); recordBody.flip(); - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "READ: " + ProtocolVersion.nameOf(majorVersion, minorVersion) + @@ -406,7 +406,7 @@ private Plaintext[] handleUnknownRecord() throws IOException { */ os.write(SSLRecord.v2NoCipher); // SSLv2Hello - if (SSLLogger.isOn) { + if (SSLLogger.isOn()) { if (SSLLogger.isOn("record")) { SSLLogger.fine( "Requested to negotiate unsupported SSLv2!"); @@ -445,7 +445,7 @@ private Plaintext[] handleUnknownRecord() throws IOException { ByteBuffer converted = convertToClientHello(recordBody); - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine( "[Converted] ClientHello", converted); } @@ -488,13 +488,13 @@ private int readHeader() throws IOException { private static int read(InputStream is, byte[] buf, int off, int len) throws IOException { int readLen = is.read(buf, off, len); if (readLen < 0) { - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine("Raw read: EOF"); } throw new EOFException("SSL peer shut down incorrectly"); } - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { ByteBuffer bb = ByteBuffer.wrap(buf, off, readLen); SSLLogger.fine("Raw read", bb); } diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSocketOutputRecord.java b/src/java.base/share/classes/sun/security/ssl/SSLSocketOutputRecord.java index a7809754ed0..e83ad15db22 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSocketOutputRecord.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketOutputRecord.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -55,7 +55,7 @@ void encodeAlert(byte level, byte description) throws IOException { recordLock.lock(); try { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "alert message: " + Alert.nameOf(description)); } @@ -67,7 +67,7 @@ void encodeAlert(byte level, byte description) throws IOException { write(level); write(description); - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine("WRITE: " + protocolVersion.name + " " + ContentType.ALERT.name + "(" + Alert.nameOf(description) + ")" + @@ -81,7 +81,7 @@ void encodeAlert(byte level, byte description) throws IOException { deliverStream.write(buf, 0, count); // may throw IOException deliverStream.flush(); // may throw IOException - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine("Raw write", (new ByteArrayInputStream(buf, 0, count))); } @@ -99,7 +99,7 @@ void encodeHandshake(byte[] source, recordLock.lock(); try { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "handshake message", ByteBuffer.wrap(source, offset, length)); @@ -127,7 +127,7 @@ void encodeHandshake(byte[] source, int limit = v2ClientHello.limit(); handshakeHash.deliver(record, 2, (limit - 2)); - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "WRITE: SSLv2 ClientHello message" + ", length = " + limit); @@ -141,7 +141,7 @@ void encodeHandshake(byte[] source, deliverStream.write(record, 0, limit); deliverStream.flush(); - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine("Raw write", (new ByteArrayInputStream(record, 0, limit))); } @@ -177,7 +177,7 @@ void encodeHandshake(byte[] source, return; } - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "WRITE: " + protocolVersion.name + " " + ContentType.HANDSHAKE.name + @@ -191,7 +191,7 @@ void encodeHandshake(byte[] source, deliverStream.write(buf, 0, count); // may throw IOException deliverStream.flush(); // may throw IOException - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine("Raw write", (new ByteArrayInputStream(buf, 0, count))); } @@ -212,7 +212,7 @@ void encodeChangeCipherSpec() throws IOException { recordLock.lock(); try { if (isClosed()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound has closed, ignore outbound " + "change_cipher_spec message"); } @@ -231,7 +231,7 @@ void encodeChangeCipherSpec() throws IOException { deliverStream.write(buf, 0, count); // may throw IOException // deliverStream.flush(); // flush in Finished - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine("Raw write", (new ByteArrayInputStream(buf, 0, count))); } @@ -257,7 +257,7 @@ public void flush() throws IOException { return; } - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "WRITE: " + protocolVersion.name + " " + ContentType.HANDSHAKE.name + @@ -271,7 +271,7 @@ public void flush() throws IOException { deliverStream.write(buf, 0, count); // may throw IOException deliverStream.flush(); // may throw IOException - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine("Raw write", (new ByteArrayInputStream(buf, 0, count))); } @@ -293,7 +293,7 @@ void deliver(byte[] source, int offset, int length) throws IOException { } if (writeCipher.authenticator.seqNumOverflow()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine( "sequence number extremely close to overflow " + "(2^64-1 packets). Closing connection."); @@ -330,7 +330,7 @@ void deliver(byte[] source, int offset, int length) throws IOException { count = position; write(source, offset, fragLen); - if (SSLLogger.isOn && SSLLogger.isOn("record")) { + if (SSLLogger.isOn() && SSLLogger.isOn("record")) { SSLLogger.fine( "WRITE: " + protocolVersion.name + " " + ContentType.APPLICATION_DATA.name + @@ -345,7 +345,7 @@ void deliver(byte[] source, int offset, int length) throws IOException { deliverStream.write(buf, 0, count); // may throw IOException deliverStream.flush(); // may throw IOException - if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + if (SSLLogger.isOn() && SSLLogger.isOn("packet")) { SSLLogger.fine("Raw write", (new ByteArrayInputStream(buf, 0, count))); } diff --git a/src/java.base/share/classes/sun/security/ssl/SSLTransport.java b/src/java.base/share/classes/sun/security/ssl/SSLTransport.java index c248b48fb0a..9298e016f63 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLTransport.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLTransport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -113,7 +113,7 @@ static Plaintext decode(TransportContext context, // Code to deliver SSLv2 error message for SSL/TLS connections. if (!context.sslContext.isDTLS()) { context.outputRecord.encodeV2NoCipher(); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("may be talking to SSLv2"); } } @@ -161,7 +161,7 @@ static Plaintext decode(TransportContext context, if (context.handshakeContext != null && context.handshakeContext.sslConfig.enableRetransmissions && context.sslContext.isDTLS()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,verbose")) { SSLLogger.finest("retransmitted handshake flight"); } @@ -181,7 +181,7 @@ static Plaintext decode(TransportContext context, // Note that JDK does not support 0-RTT yet. Otherwise, it is // needed to check early_data. if (!context.isNegotiated) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,verbose")) { SSLLogger.warning("unexpected application data " + "before handshake completion"); } diff --git a/src/java.base/share/classes/sun/security/ssl/ServerHello.java b/src/java.base/share/classes/sun/security/ssl/ServerHello.java index 1d2faa5351f..76c266a628a 100644 --- a/src/java.base/share/classes/sun/security/ssl/ServerHello.java +++ b/src/java.base/share/classes/sun/security/ssl/ServerHello.java @@ -365,7 +365,7 @@ public byte[] produce(ConnectionContext context, shc.sslConfig.getEnabledExtensions( SSLHandshake.SERVER_HELLO, shc.negotiatedProtocol); shm.extensions.produce(shc, serverHelloExtensions); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced ServerHello handshake message", shm); } @@ -440,7 +440,7 @@ private static KeyExchangeProperties chooseCipherSuite( } // The cipher suite has been negotiated. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("use cipher suite " + cs.name); } @@ -453,7 +453,7 @@ private static KeyExchangeProperties chooseCipherSuite( if (ke != null) { SSLPossession[] hcds = ke.createPossessions(shc); if ((hcds != null) && (hcds.length != 0)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "use legacy cipher suite " + cs.name); } @@ -570,7 +570,7 @@ public byte[] produce(ConnectionContext context, shc.sslConfig.getEnabledExtensions( SSLHandshake.SERVER_HELLO, shc.negotiatedProtocol); shm.extensions.produce(shc, serverHelloExtensions); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced ServerHello handshake message", shm); } @@ -723,14 +723,14 @@ private static CipherSuite chooseCipherSuite( } // The cipher suite has been negotiated. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("use cipher suite " + cs.name); } return cs; } if (legacySuite != null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "use legacy cipher suite " + legacySuite.name); } @@ -783,7 +783,7 @@ public byte[] produce(ConnectionContext context, shc.sslConfig.getEnabledExtensions( SSLHandshake.HELLO_RETRY_REQUEST, shc.negotiatedProtocol); hhrm.extensions.produce(shc, serverHelloExtensions); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced HelloRetryRequest handshake message", hhrm); } @@ -845,7 +845,7 @@ public byte[] produce(ConnectionContext context, shc.sslConfig.getEnabledExtensions( SSLHandshake.MESSAGE_HASH, shc.negotiatedProtocol); hhrm.extensions.produce(shc, serverHelloExtensions); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Reproduced HelloRetryRequest handshake message", hhrm); } @@ -886,7 +886,7 @@ public void consume(ConnectionContext context, } ServerHelloMessage shm = new ServerHelloMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Consuming ServerHello handshake message", shm); } @@ -931,7 +931,7 @@ private void onHelloRetryRequest(ClientHandshakeContext chc, } chc.negotiatedProtocol = serverVersion; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Negotiated protocol version: " + serverVersion.name); } @@ -986,7 +986,7 @@ private void onServerHello(ClientHandshakeContext chc, chc.conContext.protocolVersion = chc.negotiatedProtocol; chc.conContext.outputRecord.setVersion(chc.negotiatedProtocol); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Negotiated protocol version: " + serverVersion.name); } @@ -1132,7 +1132,7 @@ public void consume(ConnectionContext context, chc.handshakeSession = new SSLSessionImpl(chc, chc.negotiatedCipherSuite, newId); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Locally assigned Session Id: " + newId.toString()); } @@ -1204,7 +1204,7 @@ public void consume(ConnectionContext context, private static void setUpPskKD(HandshakeContext hc, SecretKey psk) throws SSLHandshakeException { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Using PSK to derive early secret"); } diff --git a/src/java.base/share/classes/sun/security/ssl/ServerHelloDone.java b/src/java.base/share/classes/sun/security/ssl/ServerHelloDone.java index 7136b36ffc2..a86257ab3d0 100644 --- a/src/java.base/share/classes/sun/security/ssl/ServerHelloDone.java +++ b/src/java.base/share/classes/sun/security/ssl/ServerHelloDone.java @@ -93,7 +93,7 @@ public byte[] produce(ConnectionContext context, ServerHandshakeContext shc = (ServerHandshakeContext)context; ServerHelloDoneMessage shdm = new ServerHelloDoneMessage(shc); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced ServerHelloDone handshake message", shdm); } @@ -147,7 +147,7 @@ public void consume(ConnectionContext context, ServerHelloDoneMessage shdm = new ServerHelloDoneMessage(chc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming ServerHelloDone handshake message", shdm); } diff --git a/src/java.base/share/classes/sun/security/ssl/ServerNameExtension.java b/src/java.base/share/classes/sun/security/ssl/ServerNameExtension.java index 96c3fe2fa6a..1b3c8ec3eba 100644 --- a/src/java.base/share/classes/sun/security/ssl/ServerNameExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/ServerNameExtension.java @@ -216,7 +216,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(CH_SERVER_NAME)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore unavailable server_name extension"); } @@ -261,7 +261,7 @@ public byte[] produce(ConnectionContext context, return extData; } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("Unable to indicate server name"); } return null; @@ -287,7 +287,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(CH_SERVER_NAME)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extension: " + CH_SERVER_NAME.name); } @@ -305,7 +305,7 @@ public void consume(ConnectionContext context, if (!shc.sslConfig.sniMatchers.isEmpty()) { sni = chooseSni(shc.sslConfig.sniMatchers, spec.serverNames); if (sni != null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "server name indication (" + sni + ") is accepted"); @@ -322,7 +322,7 @@ public void consume(ConnectionContext context, // connection with a "missing_extension" alert. // // We do not reject client without SNI extension currently. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "no server name matchers, " + "ignore server name indication"); @@ -347,7 +347,7 @@ public void consume(ConnectionContext context, // so don't include the pre-shared key in the // ServerHello handshake message shc.handshakeExtensions.remove(SH_PRE_SHARED_KEY); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "abort session resumption, " + "different server name indication used"); @@ -441,7 +441,7 @@ public byte[] produce(ConnectionContext context, CHServerNamesSpec spec = (CHServerNamesSpec) shc.handshakeExtensions.get(CH_SERVER_NAME); if (spec == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "Ignore unavailable extension: " + SH_SERVER_NAME.name); } @@ -451,7 +451,7 @@ public byte[] produce(ConnectionContext context, // When resuming a session, the server MUST NOT include a // server_name extension in the server hello. if (shc.isResumption || shc.negotiatedServerName == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "No expected server name indication response"); } @@ -528,7 +528,7 @@ public byte[] produce(ConnectionContext context, CHServerNamesSpec spec = (CHServerNamesSpec) shc.handshakeExtensions.get(CH_SERVER_NAME); if (spec == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "Ignore unavailable extension: " + EE_SERVER_NAME.name); } @@ -538,7 +538,7 @@ public byte[] produce(ConnectionContext context, // When resuming a session, the server MUST NOT include a // server_name extension in the server hello. if (shc.isResumption || shc.negotiatedServerName == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( "No expected server name indication response"); } diff --git a/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java b/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java index 9a84bbad8fd..1a0283cf859 100644 --- a/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java @@ -93,7 +93,7 @@ final class SessionTicketExtension { kt = Integer.parseInt(s) * 1000; // change to ms if (kt < 0 || kt > NewSessionTicket.MAX_TICKET_LIFETIME) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("Invalid timeout for " + "jdk.tls.server.statelessKeyTimeout: " + kt + ". Set to default value " + @@ -103,7 +103,7 @@ final class SessionTicketExtension { } } catch (NumberFormatException e) { kt = TIMEOUT_DEFAULT; - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("Invalid timeout for " + "jdk.tls.server.statelessKeyTimeout: " + s + ". Set to default value " + TIMEOUT_DEFAULT + @@ -252,7 +252,7 @@ byte[] encrypt(HandshakeContext hc, SSLSessionImpl session) { Integer.BYTES + iv.length + 1, encrypted.length); return result; } catch (Exception e) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Encryption failed." + e); } return new byte[0]; @@ -294,7 +294,7 @@ ByteBuffer decrypt(HandshakeContext hc) { return out; } catch (Exception e) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Decryption failed." + e); } } @@ -309,7 +309,7 @@ private static byte[] compress(byte[] input) throws IOException { gos.write(input, 0, decompressedLen); gos.finish(); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("decompressed bytes: " + decompressedLen + "; compressed bytes: " + baos.size()); } @@ -328,7 +328,7 @@ private static ByteBuffer decompress(ByteBuffer input) new ByteArrayInputStream(bytes))) { byte[] out = gis.readAllBytes(); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("compressed bytes: " + compressedLen + "; decompressed bytes: " + out.length); } @@ -394,7 +394,7 @@ public byte[] produce(ConnectionContext context, // If the context does not allow stateless tickets, exit if (!((SSLSessionContextImpl)chc.sslContext. engineGetClientSessionContext()).statelessEnabled()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Stateless resumption not supported"); } return null; @@ -406,7 +406,7 @@ public byte[] produce(ConnectionContext context, if (!chc.isResumption || chc.resumingSession == null || chc.resumingSession.getPskIdentity() == null || chc.resumingSession.getProtocolVersion().useTLS13PlusSpec()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Stateless resumption supported"); } return new byte[0]; @@ -450,7 +450,7 @@ public void consume(ConnectionContext context, shc.statelessResumption = true; if (buffer.remaining() == 0) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Client accepts session tickets."); } return; @@ -462,11 +462,11 @@ public void consume(ConnectionContext context, if (b != null) { shc.resumingSession = new SSLSessionImpl(shc, b); shc.isResumption = true; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Valid stateless session ticket found"); } } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Invalid stateless session ticket found"); } } @@ -546,7 +546,7 @@ public void absent(ConnectionContext context, // Disable stateless resumption if server doesn't send the extension. if (chc.statelessResumption) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.info( "Server doesn't support stateless resumption"); } diff --git a/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java b/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java index b298da05e9a..dddeb523516 100644 --- a/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java @@ -182,7 +182,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable( SSLExtension.CH_SIGNATURE_ALGORITHMS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable signature_algorithms extension"); } @@ -218,7 +218,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable( SSLExtension.CH_SIGNATURE_ALGORITHMS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable signature_algorithms extension"); } diff --git a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java index 5a8f103082b..b91fc17fd29 100644 --- a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java +++ b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java @@ -204,7 +204,7 @@ enum SigAlgParamSpec { // support RSASSA-PSS only now NoSuchAlgorithmException | RuntimeException exp) { // Signature.getParameters() may throw RuntimeException. mediator = false; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "RSASSA-PSS signature with " + hash + " is not supported by the underlying providers", exp); @@ -297,7 +297,7 @@ enum SigAlgParamSpec { // support RSASSA-PSS only now Signature.getInstance(algorithm); } catch (Exception e) { mediator = false; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Signature algorithm, " + algorithm + ", is not supported by the underlying providers"); @@ -432,7 +432,7 @@ private static List getSupportedAlgorithms( for (SignatureScheme ss: schemesToCheck) { if (!ss.isAvailable) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Ignore unsupported signature scheme: " + ss.name); @@ -451,12 +451,12 @@ private static List getSupportedAlgorithms( if (isMatch) { if (ss.isPermitted(constraints, scopes)) { supported.add(ss); - } else if (SSLLogger.isOn && + } else if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Ignore disabled signature scheme: " + ss.name); } - } else if (SSLLogger.isOn && + } else if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Ignore inactive signature scheme: " + ss.name); @@ -476,7 +476,7 @@ static List getSupportedAlgorithms( for (int ssid : algorithmIds) { SignatureScheme ss = SignatureScheme.valueOf(ssid); if (ss == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Unsupported signature scheme: " + SignatureScheme.nameOf(ssid)); @@ -486,7 +486,7 @@ static List getSupportedAlgorithms( && ss.isAllowed(constraints, protocolVersion, scopes)) { supported.add(ss); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Unsupported signature scheme: " + ss.name); } @@ -545,7 +545,7 @@ static Map.Entry getSignerOfPreferableAlgorithm( } } - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Ignore the signature algorithm (" + ss + @@ -574,7 +574,7 @@ static Map.Entry getSignerOfPreferableAlgorithm( } } - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Ignore the legacy signature algorithm (" + ss + @@ -660,7 +660,7 @@ private Signature getSigner(PrivateKey privateKey) { return signer; } catch (NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException nsae) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Ignore unsupported signature algorithm (" + diff --git a/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java b/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java index ec200c6e495..d8c4a8ccc3e 100644 --- a/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java +++ b/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java @@ -119,13 +119,13 @@ URI getURI(X509Certificate cert) { if (cert.getExtensionValue( PKIXExtensions.OCSPNoCheck_Id.toString()) != null) { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "OCSP NoCheck extension found. OCSP will be skipped"); } return null; } else if (defaultResponder != null && respOverride) { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Responder override: URI is " + defaultResponder); } @@ -165,7 +165,7 @@ Map get(CertStatusRequestType type, Map responseMap = new HashMap<>(); List requestList = new ArrayList<>(); - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Beginning check: Type = " + type + ", Chain length = " + chain.length); @@ -192,7 +192,7 @@ Map get(CertStatusRequestType type, requestList.add(new OCSPFetchCall(sInfo, ocspReq)); } } catch (IOException exc) { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Exception during CertId creation: ", exc); } @@ -219,14 +219,14 @@ Map get(CertStatusRequestType type, requestList.add(new OCSPFetchCall(sInfo, ocspReq)); } } catch (IOException exc) { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Exception during CertId creation: ", exc); } } } } else { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine("Unsupported status request type: " + type); } } @@ -257,7 +257,7 @@ Map get(CertStatusRequestType type, // that, otherwise just log the ExecutionException Throwable cause = Optional.ofNullable( exc.getCause()).orElse(exc); - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine("Exception during OCSP fetch: " + cause); } @@ -266,13 +266,13 @@ Map get(CertStatusRequestType type, if (info != null && info.responseData != null) { responseMap.put(info.cert, info.responseData.ocspBytes); - } else if (SSLLogger.isOn && + } else if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Completed task had no response data"); } } else { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine("Found cancelled task"); } } @@ -280,7 +280,7 @@ Map get(CertStatusRequestType type, } catch (InterruptedException intex) { // Log and reset the interrupted state Thread.currentThread().interrupt(); - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine("Interrupt occurred while fetching: " + intex); } @@ -308,7 +308,7 @@ private ResponseCacheEntry getFromCache(CertId cid, for (Extension ext : ocspRequest.extensions) { if (ext.getId().equals( PKIXExtensions.OCSPNonce_Id.toString())) { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Nonce extension found, skipping cache check"); } @@ -323,14 +323,14 @@ private ResponseCacheEntry getFromCache(CertId cid, // and do not return it as a cache hit. if (respEntry != null && respEntry.nextUpdate != null && respEntry.nextUpdate.before(new Date())) { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "nextUpdate threshold exceeded, purging from cache"); } respEntry = null; } - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Check cache for SN" + Debug.toString(cid.getSerialNumber()) + ": " + (respEntry != null ? "HIT" : "MISS")); @@ -493,7 +493,7 @@ public OCSPFetchCall(StatusInfo info, OCSPStatusRequest request) { */ @Override public StatusInfo call() { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Starting fetch for SN " + Debug.toString(statInfo.cid.getSerialNumber())); @@ -505,13 +505,13 @@ public StatusInfo call() { if (statInfo.responder == null) { // If we have no URI then there's nothing to do // but return. - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Null URI detected, OCSP fetch aborted"); } return statInfo; } else { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Attempting fetch from " + statInfo.responder); } @@ -541,7 +541,7 @@ public StatusInfo call() { statInfo.cid); // Get the response status and act on it appropriately - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine("OCSP Status: " + cacheEntry.status + " (" + respBytes.length + " bytes)"); } @@ -554,7 +554,7 @@ public StatusInfo call() { addToCache(statInfo.cid, cacheEntry); } } catch (IOException ioe) { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine("Caught exception: ", ioe); } } @@ -573,12 +573,12 @@ private void addToCache(CertId certId, ResponseCacheEntry entry) { // If no cache lifetime has been set on entries then // don't cache this response if there is no nextUpdate field if (entry.nextUpdate == null && cacheLifetime == 0) { - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine("Not caching this OCSP response"); } } else { responseCache.put(certId, entry); - if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + if (SSLLogger.isOn() && SSLLogger.isOn("respmgr")) { SSLLogger.fine( "Added response for SN " + Debug.toString(certId.getSerialNumber()) + @@ -600,7 +600,7 @@ static StaplingParameters processStapling(ServerHandshakeContext shc) { // is necessary. Also, we will only staple if we're doing a full // handshake. if (!shc.sslContext.isStaplingEnabled(false) || shc.isResumption) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Staping disabled or is a resumed session"); } return null; @@ -623,7 +623,7 @@ static StaplingParameters processStapling(ServerHandshakeContext shc) { // selection yet, only accept a request if the ResponderId field // is empty. Finally, we'll only do this in (D)TLS 1.2 or earlier. if (statReqV2 != null && !shc.negotiatedProtocol.useTLS13PlusSpec()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.fine("SH Processing status_request_v2 extension"); } // RFC 6961 stapling @@ -660,7 +660,7 @@ static StaplingParameters processStapling(ServerHandshakeContext shc) { req = reqItems[ocspIdx]; type = CertStatusRequestType.valueOf(req.statusType); } else { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest("Warning: No suitable request " + "found in the status_request_v2 extension."); @@ -678,7 +678,7 @@ static StaplingParameters processStapling(ServerHandshakeContext shc) { // we will try processing an asserted status_request. if ((statReq != null) && (ext == null || type == null || req == null)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.fine("SH Processing status_request extension"); } ext = SSLExtension.CH_STATUS_REQUEST; @@ -692,7 +692,7 @@ static StaplingParameters processStapling(ServerHandshakeContext shc) { if (ocspReq.responderIds.isEmpty()) { req = ocspReq; } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest("Warning: No suitable request " + "found in the status_request extension."); } @@ -704,7 +704,7 @@ static StaplingParameters processStapling(ServerHandshakeContext shc) { // find a suitable StatusRequest, then stapling is disabled. // The ext, type and req variables must have been set to continue. if (type == null || req == null || ext == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("No suitable status_request or " + "status_request_v2, stapling is disabled"); } @@ -721,7 +721,7 @@ static StaplingParameters processStapling(ServerHandshakeContext shc) { } if (x509Possession == null) { // unlikely - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest("Warning: no X.509 certificates found. " + "Stapling is disabled."); } @@ -743,7 +743,7 @@ static StaplingParameters processStapling(ServerHandshakeContext shc) { responses = statRespMgr.get(fetchType, req, certs, shc.statusRespTimeout, TimeUnit.MILLISECONDS); if (!responses.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest("Response manager returned " + responses.size() + " entries."); } @@ -752,7 +752,7 @@ static StaplingParameters processStapling(ServerHandshakeContext shc) { if (type == CertStatusRequestType.OCSP) { byte[] respDER = responses.get(certs[0]); if (respDER == null || respDER.length == 0) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest("Warning: Null or zero-length " + "response found for leaf certificate. " + @@ -763,7 +763,7 @@ static StaplingParameters processStapling(ServerHandshakeContext shc) { } params = new StaplingParameters(ext, type, req, responses); } else { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest("Warning: no OCSP responses obtained. " + "Stapling is disabled."); } @@ -771,7 +771,7 @@ static StaplingParameters processStapling(ServerHandshakeContext shc) { } else { // This should not happen, but if lazy initialization of the // StatusResponseManager doesn't occur we should turn off stapling. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest("Warning: lazy initialization " + "of the StatusResponseManager failed. " + "Stapling is disabled."); diff --git a/src/java.base/share/classes/sun/security/ssl/SunX509KeyManagerImpl.java b/src/java.base/share/classes/sun/security/ssl/SunX509KeyManagerImpl.java index 6bf138f4e45..1fa2356d1de 100644 --- a/src/java.base/share/classes/sun/security/ssl/SunX509KeyManagerImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SunX509KeyManagerImpl.java @@ -129,7 +129,7 @@ private static class X509Credentials { X509Credentials cred = new X509Credentials((PrivateKey) key, (X509Certificate[]) certs); credentialsMap.put(alias, cred); - if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("keymanager")) { SSLLogger.fine("found key for : " + alias, (Object[]) certs); } } @@ -315,7 +315,7 @@ private String[] getAliases(List keyTypes, Principal[] issuers, } if (results == null) { - if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("keymanager")) { SSLLogger.fine("KeyMgr: no matching key found"); } return null; diff --git a/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java b/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java index 57e5f8c9093..67cb37988a1 100644 --- a/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java @@ -164,7 +164,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(CH_SUPPORTED_GROUPS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable supported_groups extension"); } @@ -177,7 +177,7 @@ public byte[] produce(ConnectionContext context, for (String name : chc.sslConfig.namedGroups) { NamedGroup ng = NamedGroup.nameOf(name); if (ng == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unspecified named group: " + name); } @@ -193,14 +193,14 @@ public byte[] produce(ConnectionContext context, ng.isSupported(chc.activeCipherSuites) && ng.isPermitted(chc.algorithmConstraints)) { namedGroups.add(ng); - } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + } else if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore inactive or disabled named group: " + ng.name); } } if (namedGroups.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("no available named group"); } @@ -244,7 +244,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(CH_SUPPORTED_GROUPS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable supported_groups extension"); } @@ -319,7 +319,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(EE_SUPPORTED_GROUPS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable supported_groups extension"); } @@ -335,7 +335,7 @@ public byte[] produce(ConnectionContext context, for (String name : shc.sslConfig.namedGroups) { NamedGroup ng = NamedGroup.nameOf(name); if (ng == null) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unspecified named group: " + name); @@ -352,14 +352,14 @@ public byte[] produce(ConnectionContext context, ng.isSupported(shc.activeCipherSuites) && ng.isPermitted(shc.algorithmConstraints)) { namedGroups.add(ng); - } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + } else if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore inactive or disabled named group: " + ng.name); } } if (namedGroups.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("no available named group"); } @@ -399,7 +399,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(EE_SUPPORTED_GROUPS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable supported_groups extension"); } diff --git a/src/java.base/share/classes/sun/security/ssl/SupportedVersionsExtension.java b/src/java.base/share/classes/sun/security/ssl/SupportedVersionsExtension.java index 6efdcef0d29..58597c3008c 100644 --- a/src/java.base/share/classes/sun/security/ssl/SupportedVersionsExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SupportedVersionsExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -168,7 +168,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(CH_SUPPORTED_VERSIONS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extension: " + CH_SUPPORTED_VERSIONS.name); @@ -216,7 +216,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(CH_SUPPORTED_VERSIONS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extension: " + CH_SUPPORTED_VERSIONS.name); @@ -308,7 +308,7 @@ public byte[] produce(ConnectionContext context, shc.handshakeExtensions.get(CH_SUPPORTED_VERSIONS); if (svs == null) { // Unlikely, no key_share extension requested. - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( "Ignore unavailable supported_versions extension"); } @@ -317,7 +317,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(SH_SUPPORTED_VERSIONS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extension: " + SH_SUPPORTED_VERSIONS.name); @@ -356,7 +356,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(SH_SUPPORTED_VERSIONS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extension: " + SH_SUPPORTED_VERSIONS.name); @@ -399,7 +399,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(HRR_SUPPORTED_VERSIONS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extension: " + HRR_SUPPORTED_VERSIONS.name); @@ -441,7 +441,7 @@ public void consume(ConnectionContext context, // Is it a supported and enabled extension? if (!chc.sslConfig.isAvailable(HRR_SUPPORTED_VERSIONS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Ignore unavailable extension: " + HRR_SUPPORTED_VERSIONS.name); @@ -483,7 +483,7 @@ public byte[] produce(ConnectionContext context, // Is it a supported and enabled extension? if (!shc.sslConfig.isAvailable(HRR_SUPPORTED_VERSIONS)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "[Reproduce] Ignore unavailable extension: " + HRR_SUPPORTED_VERSIONS.name); diff --git a/src/java.base/share/classes/sun/security/ssl/TransportContext.java b/src/java.base/share/classes/sun/security/ssl/TransportContext.java index 9595dc8b3fd..980d9c4a6ce 100644 --- a/src/java.base/share/classes/sun/security/ssl/TransportContext.java +++ b/src/java.base/share/classes/sun/security/ssl/TransportContext.java @@ -270,7 +270,7 @@ void warning(Alert alert) { try { outputRecord.encodeAlert(Alert.Level.WARNING.level, alert.id); } catch (IOException ioe) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning( "Warning: failed to send warning alert " + alert, ioe); } @@ -330,7 +330,7 @@ SSLException fatal(Alert alert, String diagnostic, // so we'll do it here. if (closeReason != null) { if (cause == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning( "Closed transport, general or untracked problem"); } @@ -341,7 +341,7 @@ SSLException fatal(Alert alert, String diagnostic, if (cause instanceof SSLException) { throw (SSLException)cause; } else { // unlikely, but just in case. - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning( "Closed transport, unexpected rethrowing", cause); } @@ -364,7 +364,7 @@ SSLException fatal(Alert alert, String diagnostic, } // shutdown the transport - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.severe("Fatal (" + alert + "): " + diagnostic, cause); } @@ -380,7 +380,7 @@ SSLException fatal(Alert alert, String diagnostic, try { inputRecord.close(); } catch (IOException ioe) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("Fatal: input record closure failed", ioe); } @@ -411,7 +411,7 @@ SSLException fatal(Alert alert, String diagnostic, try { outputRecord.encodeAlert(Alert.Level.FATAL.level, alert.id); } catch (IOException ioe) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning( "Fatal: failed to send fatal alert " + alert, ioe); } @@ -424,7 +424,7 @@ SSLException fatal(Alert alert, String diagnostic, try { outputRecord.close(); } catch (IOException ioe) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("Fatal: output record closure failed", ioe); } @@ -440,7 +440,7 @@ SSLException fatal(Alert alert, String diagnostic, try { transport.shutdown(); } catch (IOException ioe) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("Fatal: transport closure failed", ioe); } @@ -526,7 +526,7 @@ void closeInbound() { passiveInboundClose(); } } catch (IOException ioe) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("inbound closure failed", ioe); } } @@ -583,7 +583,7 @@ void closeOutbound() { try { initiateOutboundClose(); } catch (IOException ioe) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning("outbound closure failed", ioe); } } diff --git a/src/java.base/share/classes/sun/security/ssl/TrustManagerFactoryImpl.java b/src/java.base/share/classes/sun/security/ssl/TrustManagerFactoryImpl.java index bac7735de07..8b89fecefa8 100644 --- a/src/java.base/share/classes/sun/security/ssl/TrustManagerFactoryImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/TrustManagerFactoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -48,24 +48,24 @@ protected void engineInit(KeyStore ks) throws KeyStoreException { trustManager = getInstance(TrustStoreManager.getTrustedCerts()); } catch (SecurityException se) { // eat security exceptions but report other throwables - if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine( "SunX509: skip default keystore", se); } } catch (Error err) { - if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine( "SunX509: skip default keystore", err); } throw err; } catch (RuntimeException re) { - if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine( "SunX509: skip default keystore", re); } throw re; } catch (Exception e) { - if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine( "SunX509: skip default keystore", e); } diff --git a/src/java.base/share/classes/sun/security/ssl/TrustStoreManager.java b/src/java.base/share/classes/sun/security/ssl/TrustStoreManager.java index e2c353847c7..a44f3a4e32d 100644 --- a/src/java.base/share/classes/sun/security/ssl/TrustStoreManager.java +++ b/src/java.base/share/classes/sun/security/ssl/TrustStoreManager.java @@ -108,7 +108,7 @@ private TrustStoreDescriptor(String storeName, String storeType, this.storeFile = storeFile; this.lastModified = lastModified; - if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine( "trustStore is: " + storeName + "\n" + "trustStore type is: " + storeType + "\n" + @@ -151,7 +151,7 @@ static TrustStoreDescriptor createInstance() { } // Not break, the file is inaccessible. - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine( "Inaccessible trust store: " + @@ -267,7 +267,7 @@ KeyStore getKeyStore( } // Reload a new key store. - if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine("Reload the trust store"); } @@ -321,7 +321,7 @@ Set getTrustedCerts( // Reload the trust store if needed. if (ks == null) { - if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine("Reload the trust store"); } ks = loadKeyStore(descriptor); @@ -329,12 +329,12 @@ Set getTrustedCerts( } // Reload trust certs from the key store. - if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine("Reload trust certs"); } certs = loadTrustedCerts(ks); - if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine("Reloaded " + certs.size() + " trust certs"); } @@ -355,7 +355,7 @@ private static KeyStore loadKeyStore( descriptor.storeFile == null) { // No file available, no KeyStore available. - if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine("No available key store"); } @@ -382,7 +382,7 @@ private static KeyStore loadKeyStore( ks.load(bis, password); } catch (FileNotFoundException fnfe) { // No file available, no KeyStore available. - if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("trustmanager")) { SSLLogger.fine( "Not available key store: " + descriptor.storeName); } diff --git a/src/java.base/share/classes/sun/security/ssl/Utilities.java b/src/java.base/share/classes/sun/security/ssl/Utilities.java index 50cd3bee751..458551b9d8a 100644 --- a/src/java.base/share/classes/sun/security/ssl/Utilities.java +++ b/src/java.base/share/classes/sun/security/ssl/Utilities.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -70,7 +70,7 @@ static List addToSNIServerNameList( SNIServerName serverName = sniList.get(i); if (serverName.getType() == StandardConstants.SNI_HOST_NAME) { sniList.set(i, sniHostName); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine( "the previous server name in SNI (" + serverName + ") was replaced with (" + sniHostName + ")"); @@ -116,7 +116,7 @@ private static SNIHostName rawToSNIHostName(String hostname) { return new SNIHostName(hostname); } catch (IllegalArgumentException iae) { // don't bother to handle illegal host_name - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine(hostname + "\" " + "is not a legal HostName for server name indication"); } diff --git a/src/java.base/share/classes/sun/security/ssl/X509Authentication.java b/src/java.base/share/classes/sun/security/ssl/X509Authentication.java index 5abc2cb1bf4..6aedff02c34 100644 --- a/src/java.base/share/classes/sun/security/ssl/X509Authentication.java +++ b/src/java.base/share/classes/sun/security/ssl/X509Authentication.java @@ -201,7 +201,7 @@ public static SSLPossession createPossession( private static SSLPossession createClientPossession( ClientHandshakeContext chc, String[] keyTypes) { X509ExtendedKeyManager km = chc.sslContext.getX509KeyManager(); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("X509KeyManager class: " + km.getClass().getName()); } @@ -243,7 +243,7 @@ private static SSLPossession createClientPossession( } if (clientAlias == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("No X.509 cert selected for " + Arrays.toString(keyTypes)); } @@ -252,7 +252,7 @@ private static SSLPossession createClientPossession( PrivateKey clientPrivateKey = km.getPrivateKey(clientAlias); if (clientPrivateKey == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest( clientAlias + " is not a private key entry"); } @@ -261,7 +261,7 @@ private static SSLPossession createClientPossession( X509Certificate[] clientCerts = km.getCertificateChain(clientAlias); if ((clientCerts == null) || (clientCerts.length == 0)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest(clientAlias + " is a private key entry with no cert chain stored"); } @@ -270,7 +270,7 @@ private static SSLPossession createClientPossession( String privateKeyAlgorithm = clientPrivateKey.getAlgorithm(); if (!Arrays.asList(keyTypes).contains(privateKeyAlgorithm)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine( clientAlias + " private key algorithm " + privateKeyAlgorithm + " not in request list"); @@ -280,7 +280,7 @@ private static SSLPossession createClientPossession( String publicKeyAlgorithm = clientCerts[0].getPublicKey().getAlgorithm(); if (!privateKeyAlgorithm.equals(publicKeyAlgorithm)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine( clientAlias + " private or public key is not of " + "same algorithm: " + @@ -296,7 +296,7 @@ private static SSLPossession createClientPossession( private static SSLPossession createServerPossession( ServerHandshakeContext shc, String[] keyTypes) { X509ExtendedKeyManager km = shc.sslContext.getX509KeyManager(); - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("X509KeyManager class: " + km.getClass().getName()); } @@ -337,7 +337,7 @@ private static SSLPossession createServerPossession( } if (serverAlias == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest("No X.509 cert selected for " + keyType); } continue; @@ -345,7 +345,7 @@ private static SSLPossession createServerPossession( PrivateKey serverPrivateKey = km.getPrivateKey(serverAlias); if (serverPrivateKey == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest( serverAlias + " is not a private key entry"); } @@ -354,7 +354,7 @@ private static SSLPossession createServerPossession( X509Certificate[] serverCerts = km.getCertificateChain(serverAlias); if ((serverCerts == null) || (serverCerts.length == 0)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.finest( serverAlias + " is not a certificate entry"); } @@ -364,7 +364,7 @@ private static SSLPossession createServerPossession( PublicKey serverPublicKey = serverCerts[0].getPublicKey(); if ((!serverPrivateKey.getAlgorithm().equals(keyType)) || (!serverPublicKey.getAlgorithm().equals(keyType))) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine( serverAlias + " private or public key is not of " + keyType + " algorithm"); @@ -379,7 +379,7 @@ private static SSLPossession createServerPossession( if (!shc.negotiatedProtocol.useTLS13PlusSpec() && keyType.equals("EC")) { if (!(serverPublicKey instanceof ECPublicKey)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning(serverAlias + " public key is not an instance of ECPublicKey"); } @@ -398,7 +398,7 @@ private static SSLPossession createServerPossession( ((shc.clientRequestedNamedGroups != null) && !shc.clientRequestedNamedGroups.contains(namedGroup))) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.warning( "Unsupported named group (" + namedGroup + ") used in the " + serverAlias + " certificate"); diff --git a/src/java.base/share/classes/sun/security/ssl/X509KeyManagerCertChecking.java b/src/java.base/share/classes/sun/security/ssl/X509KeyManagerCertChecking.java index 9484ab4f830..2a1f01273bd 100644 --- a/src/java.base/share/classes/sun/security/ssl/X509KeyManagerCertChecking.java +++ b/src/java.base/share/classes/sun/security/ssl/X509KeyManagerCertChecking.java @@ -116,7 +116,7 @@ protected EntryStatus checkAlias(int keyStoreIndex, String alias, } if (keyIndex == -1) { - if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("keymanager")) { SSLLogger.fine("Ignore alias " + alias + ": key algorithm does not match"); } @@ -134,7 +134,7 @@ protected EntryStatus checkAlias(int keyStoreIndex, String alias, } } if (!found) { - if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("keymanager")) { SSLLogger.fine( "Ignore alias " + alias + ": issuers do not match"); @@ -150,7 +150,7 @@ protected EntryStatus checkAlias(int keyStoreIndex, String alias, !conformsToAlgorithmConstraints(constraints, chain, checkType.getValidator())) { - if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("keymanager")) { SSLLogger.fine("Ignore alias " + alias + ": certificate chain does not conform to " + "algorithm constraints"); @@ -219,7 +219,7 @@ private boolean conformsToAlgorithmConstraints( checker.init(false); } catch (CertPathValidatorException cpve) { // unlikely to happen - if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("keymanager")) { SSLLogger.fine( "Cannot initialize algorithm constraints checker", cpve); @@ -235,7 +235,7 @@ private boolean conformsToAlgorithmConstraints( // We don't care about the unresolved critical extensions. checker.check(cert, Collections.emptySet()); } catch (CertPathValidatorException cpve) { - if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("keymanager")) { SSLLogger.fine("Certificate does not conform to " + "algorithm constraints", cert, cpve); } @@ -392,7 +392,7 @@ CheckResult check(X509Certificate cert, Date date, serverName.getEncoded()); } catch (IllegalArgumentException iae) { // unlikely to happen, just in case ... - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("keymanager")) { SSLLogger.fine("Illegal server name: " + serverName); @@ -408,7 +408,7 @@ CheckResult check(X509Certificate cert, Date date, X509TrustManagerImpl.checkIdentity(hostname, cert, idAlgorithm); } catch (CertificateException e) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("keymanager")) { SSLLogger.fine( "Certificate identity does not match " diff --git a/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java b/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java index c607fe0f25d..72f079db175 100644 --- a/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java @@ -228,7 +228,7 @@ private PrivateKeyEntry getEntry(String alias) { || (secondDot - firstDot < 2) || (alias.length() - secondDot < 2)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,keymanager")) { SSLLogger.warning("Invalid alias format: " + alias); } return null; @@ -255,7 +255,7 @@ private PrivateKeyEntry getEntry(String alias) { NoSuchAlgorithmException | IndexOutOfBoundsException e) { // ignore and only log exception - if (SSLLogger.isOn && SSLLogger.isOn("ssl,keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,keymanager")) { SSLLogger.warning("Exception thrown while getting an alias " + alias + ": " + e); } @@ -295,7 +295,7 @@ private String chooseAlias(List keyTypeList, Principal[] issuers, if (results != null) { for (EntryStatus status : results) { if (status.checkResult == CheckResult.OK) { - if (SSLLogger.isOn + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,keymanager")) { SSLLogger.fine("Choosing key: " + status); } @@ -312,13 +312,13 @@ private String chooseAlias(List keyTypeList, Principal[] issuers, } } if (allResults == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,keymanager")) { SSLLogger.fine("No matching key found"); } return null; } Collections.sort(allResults); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,keymanager")) { SSLLogger.fine( "No good matching key found, " + "returning best match out of", allResults); @@ -358,13 +358,13 @@ private String[] getAliases( } } if (allResults == null || allResults.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,keymanager")) { SSLLogger.fine("No matching alias found"); } return null; } Collections.sort(allResults); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,keymanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,keymanager")) { SSLLogger.fine("Getting aliases", allResults); } return toAliases(allResults); diff --git a/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java b/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java index d82b94a1d7d..1bbe0bfb9c7 100644 --- a/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java @@ -81,7 +81,7 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager this.trustedCerts = trustedCerts; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,trustmanager")) { SSLLogger.fine("adding as trusted certificates", (Object[])trustedCerts.toArray(new X509Certificate[0])); } @@ -98,7 +98,7 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager trustedCerts = v.getTrustedCertificates(); serverValidator = v; - if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,trustmanager")) { SSLLogger.fine("adding as trusted certificates", (Object[])trustedCerts.toArray(new X509Certificate[0])); } @@ -242,7 +242,7 @@ private void checkTrusted(X509Certificate[] chain, null, checkClientTrusted ? null : authType); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,trustmanager")) { SSLLogger.fine("Found trusted certificate", trustedChain[trustedChain.length - 1]); } @@ -288,7 +288,7 @@ private void checkTrusted(X509Certificate[] chain, null, checkClientTrusted ? null : authType); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,trustmanager")) { SSLLogger.fine("Found trusted certificate", trustedChain[trustedChain.length - 1]); } @@ -331,7 +331,7 @@ private void checkTrusted(X509Certificate[] chain, null, checkClientTrusted ? null : authType); } - if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,trustmanager")) { SSLLogger.fine("Found trusted certificate", trustedChain[trustedChain.length - 1]); } @@ -365,7 +365,7 @@ private static String getHostNameInSNI(List sniNames) { hostname = new SNIHostName(sniName.getEncoded()); } catch (IllegalArgumentException iae) { // unlikely to happen, just in case ... - if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,trustmanager")) { SSLLogger.fine("Illegal server name: " + sniName); } } diff --git a/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool.properties b/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool.properties index fb03573a7d5..751e3af9c9c 100644 --- a/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool.properties +++ b/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool.properties @@ -321,7 +321,8 @@ Unable.to.parse.denyAfter.string.in.exception.message=Unable to parse denyAfter whose.sigalg.weak=%1$s uses the %2$s signature algorithm which is considered a security risk. whose.key.disabled=%1$s uses a %2$s which is considered a security risk and is disabled. whose.key.weak=%1$s uses a %2$s which is considered a security risk. It will be disabled in a future update. -jks.storetype.warning=The %1$s keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format using "keytool -importkeystore -srckeystore %2$s -destkeystore %2$s -deststoretype pkcs12". +jks.storetype.warning=%1$s uses outdated cryptographic algorithms and will be removed in a future release. Migrate to PKCS12 using:\n\ +keytool -importkeystore -srckeystore %2$s -destkeystore %2$s -deststoretype pkcs12 migrate.keystore.warning=Migrated "%1$s" to %4$s. The %2$s keystore is backed up as "%3$s". backup.keystore.warning=The original keystore "%1$s" is backed up as "%3$s"... importing.keystore.status=Importing keystore %1$s to %2$s... diff --git a/src/java.base/share/classes/sun/security/util/DomainName.java b/src/java.base/share/classes/sun/security/util/DomainName.java index 4f577f1114c..465c155ab87 100644 --- a/src/java.base/share/classes/sun/security/util/DomainName.java +++ b/src/java.base/share/classes/sun/security/util/DomainName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -45,7 +45,6 @@ import static java.nio.charset.StandardCharsets.UTF_8; -import jdk.internal.util.StaticProperty; import sun.security.ssl.SSLLogger; /** @@ -193,7 +192,7 @@ private static Rules createRules(String tld) { } return getRules(tld, new ZipInputStream(pubSuffixStream)); } catch (IOException e) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine( "cannot parse public suffix data for " + tld + ": " + e.getMessage()); @@ -210,7 +209,7 @@ private static InputStream getPubSuffixStream() { is = new FileInputStream(f); } catch (FileNotFoundException e) { } if (is == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl") && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl") && SSLLogger.isOn("trustmanager")) { SSLLogger.fine( "lib/security/public_suffix_list.dat not found"); @@ -231,7 +230,7 @@ private static Rules getRules(String tld, } } if (!found) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl")) { SSLLogger.fine("Domain " + tld + " not found"); } return null; diff --git a/src/java.base/share/classes/sun/security/util/HostnameChecker.java b/src/java.base/share/classes/sun/security/util/HostnameChecker.java index 1374bc6d535..65115c9aeaf 100644 --- a/src/java.base/share/classes/sun/security/util/HostnameChecker.java +++ b/src/java.base/share/classes/sun/security/util/HostnameChecker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -271,7 +271,7 @@ private boolean isMatched(String name, String template, name = IDN.toUnicode(IDN.toASCII(name)); template = IDN.toUnicode(IDN.toASCII(template)); } catch (RuntimeException re) { - if (SSLLogger.isOn) { + if (SSLLogger.isOn()) { SSLLogger.fine("Failed to normalize to Unicode: " + re); } @@ -308,7 +308,7 @@ private static boolean hasIllegalWildcard( String template, boolean chainsToPublicCA) { // not ok if it is a single wildcard character or "*." if (template.equals("*") || template.equals("*.")) { - if (SSLLogger.isOn) { + if (SSLLogger.isOn()) { SSLLogger.fine( "Certificate domain name has illegal single " + "wildcard character: " + template); @@ -328,7 +328,7 @@ private static boolean hasIllegalWildcard( // not ok if there is no dot after wildcard (ex: "*com") if (firstDotIndex == -1) { - if (SSLLogger.isOn) { + if (SSLLogger.isOn()) { SSLLogger.fine( "Certificate domain name has illegal wildcard, " + "no dot after wildcard character: " + template); @@ -353,7 +353,7 @@ private static boolean hasIllegalWildcard( // Is it a top-level domain? if (wildcardedDomain.equalsIgnoreCase(templateDomainSuffix)) { - if (SSLLogger.isOn) { + if (SSLLogger.isOn()) { SSLLogger.fine( "Certificate domain name has illegal " + "wildcard for top-level public suffix: " + template); diff --git a/src/java.base/share/classes/sun/security/util/SliceableSecretKey.java b/src/java.base/share/classes/sun/security/util/SliceableSecretKey.java new file mode 100644 index 00000000000..4dc3fe0a3e8 --- /dev/null +++ b/src/java.base/share/classes/sun/security/util/SliceableSecretKey.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.util; + +import javax.crypto.SecretKey; + +/** + * An interface for SecretKeys that support using its slice as a new + * SecretKey. + *

+ * This is mainly used by PKCS #11 implementations that support the + * EXTRACT_KEY_FROM_KEY mechanism even if the key itself is sensitive + * and non-extractable. + */ +public interface SliceableSecretKey { + + /** + * Returns a slice as a new SecretKey. + * + * @param alg the new algorithm name + * @param from the byte offset of the new key in the full key + * @param to the to offset (exclusive) of the new key in the full key + * @return the new key + * @throws ArrayIndexOutOfBoundsException for improper from + * and to values + * @throws UnsupportedOperationException if slicing is not supported + */ + SecretKey slice(String alg, int from, int to); +} diff --git a/src/java.base/share/native/libjimage/jimage.cpp b/src/java.base/share/native/libjimage/jimage.cpp index 10e85eb2520..91a86f992e6 100644 --- a/src/java.base/share/native/libjimage/jimage.cpp +++ b/src/java.base/share/native/libjimage/jimage.cpp @@ -110,7 +110,7 @@ JIMAGE_FindResource(JImageFile* image, size_t nameLen = strlen(name); size_t index; - // TBD: assert(moduleNameLen > 0 && "module name must be non-empty"); + assert(moduleNameLen > 0 && "module name must be non-empty"); assert(nameLen > 0 && "name must non-empty"); // If the concatenated string is too long for the buffer, return not found diff --git a/src/java.base/unix/native/libjava/java_props_md.c b/src/java.base/unix/native/libjava/java_props_md.c index 000eb4b953e..1cbc3665ebc 100644 --- a/src/java.base/unix/native/libjava/java_props_md.c +++ b/src/java.base/unix/native/libjava/java_props_md.c @@ -388,8 +388,12 @@ GetJavaProperties(JNIEnv *env) /* supported instruction sets */ { char list[258]; - sysinfo(SI_ISALIST, list, sizeof(list)); - sprops.cpu_isalist = strdup(list); + int ret = sysinfo(SI_ISALIST, list, sizeof(list)); + if (ret == 0) { + sprops.cpu_isalist = strdup(list); + } else { + sprops.cpu_isalist = NULL; + } } #else sprops.cpu_isalist = NULL; @@ -438,7 +442,7 @@ GetJavaProperties(JNIEnv *env) /* Determine the language, country, variant, and encoding from the host, * and store these in the user.language, user.country, user.variant and - * file.encoding system properties. */ + * native.encoding system properties. */ setlocale(LC_ALL, ""); if (ParseLocale(env, LC_CTYPE, &(sprops.format_language), diff --git a/src/java.base/windows/native/libjava/java_props_md.c b/src/java.base/windows/native/libjava/java_props_md.c index 75587a09d0b..e152dbe9bef 100644 --- a/src/java.base/windows/native/libjava/java_props_md.c +++ b/src/java.base/windows/native/libjava/java_props_md.c @@ -569,7 +569,7 @@ GetJavaProperties(JNIEnv* env) /* * user.language * user.script, user.country, user.variant (if user's environment specifies them) - * file.encoding + * native.encoding */ { /* diff --git a/src/java.desktop/share/classes/java/awt/image/Raster.java b/src/java.desktop/share/classes/java/awt/image/Raster.java index 9f346cb304a..053e7c1eec5 100644 --- a/src/java.desktop/share/classes/java/awt/image/Raster.java +++ b/src/java.desktop/share/classes/java/awt/image/Raster.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -265,9 +265,15 @@ public static WritableRaster createInterleavedRaster(int dataType, * {@code location.x + w} or * {@code location.y + h} results in integer overflow * @throws IllegalArgumentException if {@code scanlineStride} - * is less than 0 - * @throws IllegalArgumentException if {@code pixelStride} is less than 0 + * is less than or equal to 0 + * @throws IllegalArgumentException if {@code pixelStride} is less than or equal to 0 + * @throws IllegalArgumentException if {@code w * pixelStride} is greater + * than {@code scanlineStride} + * @throws IllegalArgumentException if the data size need to store all + * lines of the image is greater than {@code Integer.MAX_VALUE} * @throws NullPointerException if {@code bandOffsets} is null + * @throws IllegalArgumentException if any element of {@code bandOffsets} is greater + * than {@code pixelStride} or the {@code scanlineStride} */ public static WritableRaster createInterleavedRaster(int dataType, int w, int h, @@ -285,14 +291,25 @@ public static WritableRaster createInterleavedRaster(int dataType, throw new IllegalArgumentException("Dimensions (width="+w+ " height="+h+") are too large"); } - if (pixelStride < 0) { - throw new IllegalArgumentException("pixelStride is < 0"); + if (pixelStride <= 0) { + throw new IllegalArgumentException("pixelStride is <= 0"); } - if (scanlineStride < 0) { - throw new IllegalArgumentException("scanlineStride is < 0"); + if (scanlineStride <= 0) { + throw new IllegalArgumentException("scanlineStride is <= 0"); + } + if (bandOffsets == null) { + throw new NullPointerException("bandOffsets is null"); } - int size = scanlineStride * (h - 1) + // first (h - 1) scans - pixelStride * w; // last scan + lsz = (long)w * pixelStride; + if (lsz > scanlineStride) { + throw new IllegalArgumentException("w * pixelStride is too large"); + } + lsz = (long)scanlineStride * (long)(h - 1) + // first (h - 1) scans + (long)pixelStride * (long)w; // last scan + if (lsz > Integer.MAX_VALUE) { + throw new IllegalArgumentException("size too large to store image"); + } + int size = (int)lsz; if (location == null) { location = new Point(0, 0); @@ -415,6 +432,10 @@ public static WritableRaster createBandedRaster(int dataType, * is less than 0 * @throws ArrayIndexOutOfBoundsException if {@code bankIndices} * is {@code null} + * @throws IllegalArgumentException if the lengths of {@code bankIndices} + * and {@code bandOffsets} are different. + * @throws IllegalArgumentException if the data size need to store all + * lines of a bank of the image is greater than {@code Integer.MAX_VALUE} * @throws NullPointerException if {@code bandOffsets} is {@code null} */ public static WritableRaster createBandedRaster(int dataType, @@ -423,9 +444,6 @@ public static WritableRaster createBandedRaster(int dataType, int[] bankIndices, int[] bandOffsets, Point location) { - DataBuffer d; - int bands = bandOffsets.length; - if (w <= 0 || h <= 0) { throw new IllegalArgumentException("w and h must be positive"); } @@ -440,7 +458,11 @@ public static WritableRaster createBandedRaster(int dataType, } if (bandOffsets == null) { throw new - ArrayIndexOutOfBoundsException("Band offsets array is null"); + NullPointerException("Band offsets array is null"); + } + if (bandOffsets.length != bankIndices.length) { + throw new IllegalArgumentException( + "bankIndices.length != bandOffsets.length"); } if (location != null) { if ((w + location.getX() > Integer.MAX_VALUE) || @@ -451,6 +473,9 @@ public static WritableRaster createBandedRaster(int dataType, } } + DataBuffer d; + int bands = bandOffsets.length; + // Figure out the #banks and the largest band offset int maxBank = bankIndices[0]; int maxBandOff = bandOffsets[0]; @@ -463,9 +488,15 @@ public static WritableRaster createBandedRaster(int dataType, } } int banks = maxBank + 1; - int size = maxBandOff + - scanlineStride * (h - 1) + // first (h - 1) scans - w; // last scan + + lsz = (long) maxBandOff + + (long)scanlineStride * (h - 1) + // first (h - 1) scans + w; // last scan + if (lsz > Integer.MAX_VALUE) { + throw new IllegalArgumentException("storage size is too large"); + } + + int size = (int)lsz; switch(dataType) { case DataBuffer.TYPE_BYTE: @@ -508,11 +539,14 @@ public static WritableRaster createBandedRaster(int dataType, * @param location the upper-left corner of the {@code Raster} * @return a WritableRaster object with the specified data type, * width, height, and band masks. - * @throws RasterFormatException if {@code w} or {@code h} - * is less than or equal to zero, or computing either + * @throws NullPointerException if {@code bandMasks} is null + * @throws IllegalArgumentException if {@code w} and {@code h} + * are not both greater than 0 + * @throws IllegalArgumentException if the product of {@code w} + * and {@code h} is greater than {@code Integer.MAX_VALUE} + * @throws RasterFormatException if computing either * {@code location.x + w} or - * {@code location.y + h} results in integer - * overflow + * {@code location.y + h} results in integer overflow * @throws IllegalArgumentException if {@code dataType} is not * one of the supported data types, which are * {@code DataBuffer.TYPE_BYTE}, @@ -525,6 +559,24 @@ public static WritableRaster createPackedRaster(int dataType, Point location) { DataBuffer d; + if (w <= 0 || h <= 0) { + throw new IllegalArgumentException("w and h must be positive"); + } + long lsz = (long)w * h; + if (lsz > Integer.MAX_VALUE) { + throw new IllegalArgumentException("Dimensions (width="+w+ + " height="+h+") are too large"); + } + + if (location != null) { + if ((w + location.getX() > Integer.MAX_VALUE) || + (h + location.getY() > Integer.MAX_VALUE)) { + throw new RasterFormatException( + "location.x + w and location.y + h " + + " cannot exceed Integer.MAX_VALUE"); + } + } + switch(dataType) { case DataBuffer.TYPE_BYTE: d = new DataBufferByte(w*h); @@ -573,17 +625,19 @@ public static WritableRaster createPackedRaster(int dataType, * @param location the upper-left corner of the {@code Raster} * @return a WritableRaster object with the specified data type, * width, height, number of bands, and bits per band. - * @throws RasterFormatException if {@code w} or {@code h} - * is less than or equal to zero, or computing either - * {@code location.x + w} or - * {@code location.y + h} results in integer - * overflow + * @throws IllegalArgumentException if {@code bitsPerBand} or + * {@code bands} is not greater than zero * @throws IllegalArgumentException if the product of * {@code bitsPerBand} and {@code bands} is * greater than the number of bits held by * {@code dataType} - * @throws IllegalArgumentException if {@code bitsPerBand} or - * {@code bands} is not greater than zero + * @throws IllegalArgumentException if {@code w} and {@code h} + * are not both greater than 0 + * @throws IllegalArgumentException if the product of {@code w} + * and {@code h} is greater than {@code Integer.MAX_VALUE} + * @throws RasterFormatException if computing either + * {@code location.x + w} or + * {@code location.y + h} results in integer overflow * @throws IllegalArgumentException if {@code dataType} is not * one of the supported data types, which are * {@code DataBuffer.TYPE_BYTE}, @@ -607,18 +661,37 @@ public static WritableRaster createPackedRaster(int dataType, ") must be greater than 0"); } + if (w <= 0 || h <= 0) { + throw new IllegalArgumentException("w and h must be positive"); + } + long lsz = (long)w * h; + if (lsz > Integer.MAX_VALUE) { + throw new IllegalArgumentException("Dimensions (width="+w+ + " height="+h+") are too large"); + } + + if (location != null) { + if ((w + location.getX() > Integer.MAX_VALUE) || + (h + location.getY() > Integer.MAX_VALUE)) { + throw new RasterFormatException( + "location.x + w and location.y + h " + + " cannot exceed Integer.MAX_VALUE"); + } + } + + int shift = (bands-1)*bitsPerBand; + + /* Make sure the total mask size will fit in the data type */ + if (shift+bitsPerBand > DataBuffer.getDataTypeSize(dataType)) { + throw new IllegalArgumentException("bitsPerBand("+ + bitsPerBand+") * bands is "+ + " greater than data type "+ + "size."); + } if (bands != 1) { int[] masks = new int[bands]; int mask = (1 << bitsPerBand) - 1; - int shift = (bands-1)*bitsPerBand; - - /* Make sure the total mask size will fit in the data type */ - if (shift+bitsPerBand > DataBuffer.getDataTypeSize(dataType)) { - throw new IllegalArgumentException("bitsPerBand("+ - bitsPerBand+") * bands is "+ - " greater than data type "+ - "size."); - } + switch(dataType) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: @@ -693,6 +766,7 @@ public static WritableRaster createPackedRaster(int dataType, * {@code DataBuffer.TYPE_USHORT}. * @throws RasterFormatException if {@code dataBuffer} has more * than one bank. + * @throws RasterFormatException if {@code dataBuffer} is too small. * @throws IllegalArgumentException if {@code w} and {@code h} are not * both > 0 * @throws IllegalArgumentException if the product of {@code w} @@ -704,6 +778,8 @@ public static WritableRaster createPackedRaster(int dataType, * is less than 0 * @throws IllegalArgumentException if {@code pixelStride} is less than 0 * @throws NullPointerException if {@code bandOffsets} is null + * @throws IllegalArgumentException if any element of {@code bandOffsets} is greater + * than {@code pixelStride} or the {@code scanlineStride} */ public static WritableRaster createInterleavedRaster(DataBuffer dataBuffer, @@ -779,6 +855,10 @@ public static WritableRaster createInterleavedRaster(DataBuffer dataBuffer, * bank indices and band offsets. * @throws NullPointerException if {@code dataBuffer} is null, * or {@code bankIndices} is null, or {@code bandOffsets} is null + * @throws IllegalArgumentException if the lengths of {@code bankIndices} + * and {@code bandOffsets} are different. + * @throws ArrayIndexOutOfBoundsException if any element of {@code bankIndices} + * is greater or equal to the number of bands in {@code dataBuffer} * @throws IllegalArgumentException if {@code dataType} is not * one of the supported data types, which are * {@code DataBuffer.TYPE_BYTE}, @@ -883,11 +963,14 @@ public static WritableRaster createBandedRaster(DataBuffer dataBuffer, * @return a WritableRaster object with the specified * {@code DataBuffer}, width, height, scanline stride, * and band masks. - * @throws RasterFormatException if {@code w} or {@code h} - * is less than or equal to zero, or computing either + * @throws NullPointerException if {@code bandMasks} is null + * @throws IllegalArgumentException if {@code w} and {@code h} + * are not both greater than 0 + * @throws IllegalArgumentException if the product of {@code w} + * and {@code h} is greater than {@code Integer.MAX_VALUE} + * @throws RasterFormatException if computing either * {@code location.x + w} or - * {@code location.y + h} results in integer - * overflow + * {@code location.y + h} results in integer overflow * @throws IllegalArgumentException if {@code dataBuffer} is not * one of the supported data types, which are * {@code DataBuffer.TYPE_BYTE}, @@ -906,6 +989,25 @@ public static WritableRaster createPackedRaster(DataBuffer dataBuffer, if (dataBuffer == null) { throw new NullPointerException("DataBuffer cannot be null"); } + + if (w <= 0 || h <= 0) { + throw new IllegalArgumentException("w and h must be positive"); + } + long lsz = (long)w * h; + if (lsz > Integer.MAX_VALUE) { + throw new IllegalArgumentException("Dimensions (width="+w+ + " height="+h+") are too large"); + } + + if (location != null) { + if ((w + location.getX() > Integer.MAX_VALUE) || + (h + location.getY() > Integer.MAX_VALUE)) { + throw new RasterFormatException( + "location.x + w and location.y + h " + + " cannot exceed Integer.MAX_VALUE"); + } + } + if (location == null) { location = new Point(0,0); } @@ -960,11 +1062,13 @@ public static WritableRaster createPackedRaster(DataBuffer dataBuffer, * @return a WritableRaster object with the specified * {@code DataBuffer}, width, height, and * bits per pixel. - * @throws RasterFormatException if {@code w} or {@code h} - * is less than or equal to zero, or computing either + * @throws IllegalArgumentException if {@code w} and {@code h} + * are not both greater than 0 + * @throws IllegalArgumentException if the product of {@code w} + * and {@code h} is greater than {@code Integer.MAX_VALUE} + * @throws RasterFormatException if computing either * {@code location.x + w} or - * {@code location.y + h} results in integer - * overflow + * {@code location.y + h} results in integer overflow * @throws IllegalArgumentException if {@code dataType} is not * one of the supported data types, which are * {@code DataBuffer.TYPE_BYTE}, @@ -972,6 +1076,8 @@ public static WritableRaster createPackedRaster(DataBuffer dataBuffer, * or {@code DataBuffer.TYPE_INT} * @throws RasterFormatException if {@code dataBuffer} has more * than one bank. + * @throws RasterFormatException if {@code bitsPixel} is less than 1 or + * not a power of 2 or exceeds the {@code dataBuffer} element size. * @throws NullPointerException if {@code dataBuffer} is null */ public static WritableRaster createPackedRaster(DataBuffer dataBuffer, @@ -982,6 +1088,25 @@ public static WritableRaster createPackedRaster(DataBuffer dataBuffer, if (dataBuffer == null) { throw new NullPointerException("DataBuffer cannot be null"); } + if (w <= 0 || h <= 0) { + throw new IllegalArgumentException("w and h must be positive"); + } + long lsz = (long)w * h; + + if (lsz > Integer.MAX_VALUE) { + throw new IllegalArgumentException("Dimensions (width="+w+ + " height="+h+") are too large"); + } + + if (location != null) { + if ((w + location.getX() > Integer.MAX_VALUE) || + (h + location.getY() > Integer.MAX_VALUE)) { + throw new RasterFormatException( + "location.x + w and location.y + h " + + " cannot exceed Integer.MAX_VALUE"); + } + } + if (location == null) { location = new Point(0,0); } @@ -1000,6 +1125,12 @@ public static WritableRaster createPackedRaster(DataBuffer dataBuffer, " must only have 1 bank."); } + if ((bitsPerPixel < 1) || (bitsPerPixel > DataBuffer.getDataTypeSize(dataType))) { + // NB MPPSM checks power of 2 condition + throw new + RasterFormatException("bitsPerPixel must be > 0 and a power of 2 that " + + "does not exceed data buffer element size"); + } MultiPixelPackedSampleModel mppsm = new MultiPixelPackedSampleModel(dataType, w, h, bitsPerPixel); diff --git a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalButtonUI.java b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalButtonUI.java index 0bbbab7b45f..c940b4dcead 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalButtonUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -217,6 +217,13 @@ protected void paintFocus(Graphics g, AbstractButton b, // If there is an icon and no text else if ( isIcon ) { focusRect.setBounds( iconRect ); + } else { + Rectangle emptyRect = new Rectangle(); + emptyRect.x = 5; + emptyRect.y = 5; + emptyRect.width = b.getWidth() - emptyRect.x * 2; + emptyRect.height = b.getHeight() - emptyRect.y * 2; + focusRect.setBounds(emptyRect); } g.setColor(getFocusColor()); diff --git a/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMS.java b/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMS.java index 63a1359323f..24b50003e3c 100644 --- a/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMS.java +++ b/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMS.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -136,8 +136,7 @@ public ColorTransform createTransform(int renderingIntent, static native void colorConvert(long trans, int width, int height, int srcOffset, int srcNextRowOffset, int dstOffset, int dstNextRowOffset, - Object srcData, Object dstData, - int srcType, int dstType); + Object srcData, Object dstData); private LCMS() {} diff --git a/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSImageLayout.java b/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSImageLayout.java index 663e11ff172..c7bae875282 100644 --- a/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSImageLayout.java +++ b/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSImageLayout.java @@ -31,7 +31,6 @@ import java.awt.image.ComponentColorModel; import java.awt.image.ComponentSampleModel; import java.awt.image.Raster; -import java.lang.annotation.Native; import java.nio.ByteOrder; import sun.awt.image.ByteComponentRaster; @@ -69,14 +68,7 @@ private static int CHANNELS_SH(int x) { // private static final int PT_BGRA_8 = PT_ABGR_8 | SWAPFIRST; private static final int SWAP_ENDIAN = ByteOrder.nativeOrder() == LITTLE_ENDIAN ? DOSWAP : 0; - @Native - private static final int DT_BYTE = 0; - @Native - private static final int DT_SHORT = 1; - @Native - private static final int DT_INT = 2; int pixelType; - int dataType; int width; int height; int nextRowOffset; @@ -93,12 +85,10 @@ private static int CHANNELS_SH(int x) { * @param data the storage of pixels: {@code byte[], short[] or int[]} * @param length the length of the data array * @param nc the number of color components - * @param dt the type of data array: DT_BYTE, DT_SHORT or DT_INT * @param size the size of one color component in bytes */ - private LCMSImageLayout(Object data, int length, int nc, int dt, int size) { + private LCMSImageLayout(Object data, int length, int nc, int size) { dataArray = data; - dataType = dt; dataArrayLength = length * size; pixelType = CHANNELS_SH(nc) | BYTES_SH(size); width = length / nc; @@ -109,11 +99,11 @@ private LCMSImageLayout(Object data, int length, int nc, int dt, int size) { } LCMSImageLayout(byte[] data, int nc) { - this(data, data.length, nc, DT_BYTE, Byte.BYTES); + this(data, data.length, nc, Byte.BYTES); } LCMSImageLayout(short[] data, int nc) { - this(data, data.length, nc, DT_SHORT, Short.BYTES); + this(data, data.length, nc, Short.BYTES); } private LCMSImageLayout() { @@ -186,7 +176,6 @@ static LCMSImageLayout createImageLayout(BufferedImage image) { l.offset = safeMult(4, intRaster.getDataOffset(0)); l.dataArray = intRaster.getDataStorage(); l.dataArrayLength = 4 * intRaster.getDataStorage().length; - l.dataType = DT_INT; } case BufferedImage.TYPE_BYTE_GRAY, BufferedImage.TYPE_3BYTE_BGR, BufferedImage.TYPE_4BYTE_ABGR, @@ -201,7 +190,6 @@ static LCMSImageLayout createImageLayout(BufferedImage image) { l.offset = byteRaster.getDataOffset(firstBand); l.dataArray = byteRaster.getDataStorage(); l.dataArrayLength = byteRaster.getDataStorage().length; - l.dataType = DT_BYTE; } case BufferedImage.TYPE_USHORT_GRAY -> { if (!(raster instanceof ShortComponentRaster shortRaster)) { @@ -212,7 +200,6 @@ static LCMSImageLayout createImageLayout(BufferedImage image) { l.offset = safeMult(2, shortRaster.getDataOffset(0)); l.dataArray = shortRaster.getDataStorage(); l.dataArrayLength = 2 * shortRaster.getDataStorage().length; - l.dataType = DT_SHORT; } default -> { return null; @@ -319,7 +306,6 @@ static LCMSImageLayout createImageLayout(Raster r, ColorModel cm) { l.nextPixelOffset = br.getPixelStride(); l.offset = br.getDataOffset(firstBand); - l.dataType = DT_BYTE; byte[] data = br.getDataStorage(); l.dataArray = data; l.dataArrayLength = data.length; diff --git a/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java b/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java index 881a6556ace..3f4f510cf94 100644 --- a/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java +++ b/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java @@ -121,8 +121,7 @@ private void doTransform(LCMSImageLayout in, LCMSImageLayout out) { } LCMS.colorConvert(tfm.ID, in.width, in.height, in.offset, in.nextRowOffset, out.offset, out.nextRowOffset, - in.dataArray, out.dataArray, - in.dataType, out.dataType); + in.dataArray, out.dataArray); Reference.reachabilityFence(tfm); // prevent deallocation of "tfm.ID" } diff --git a/src/java.desktop/share/native/liblcms/LCMS.c b/src/java.desktop/share/native/liblcms/LCMS.c index 5cf7ee6c436..5d4ace7a624 100644 --- a/src/java.desktop/share/native/liblcms/LCMS.c +++ b/src/java.desktop/share/native/liblcms/LCMS.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -27,7 +27,6 @@ #include #include #include "sun_java2d_cmm_lcms_LCMS.h" -#include "sun_java2d_cmm_lcms_LCMSImageLayout.h" #include "jni_util.h" #include "Trace.h" #include "Disposer.h" @@ -46,10 +45,6 @@ #define SigHead TagIdConst('h','e','a','d') -#define DT_BYTE sun_java2d_cmm_lcms_LCMSImageLayout_DT_BYTE -#define DT_SHORT sun_java2d_cmm_lcms_LCMSImageLayout_DT_SHORT -#define DT_INT sun_java2d_cmm_lcms_LCMSImageLayout_DT_INT - /* Default temp profile list size */ #define DF_ICC_BUF_SIZE 32 @@ -464,46 +459,17 @@ JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_setTagDataNative } } -static void *getILData(JNIEnv *env, jobject data, jint type) { - switch (type) { - case DT_BYTE: - return (*env)->GetByteArrayElements(env, data, 0); - case DT_SHORT: - return (*env)->GetShortArrayElements(env, data, 0); - case DT_INT: - return (*env)->GetIntArrayElements(env, data, 0); - default: - return NULL; - } -} - -static void releaseILData(JNIEnv *env, void *pData, jint type, jobject data, - jint mode) { - switch (type) { - case DT_BYTE: - (*env)->ReleaseByteArrayElements(env, data, (jbyte *) pData, mode); - break; - case DT_SHORT: - (*env)->ReleaseShortArrayElements(env, data, (jshort *) pData, mode); - break; - case DT_INT: - (*env)->ReleaseIntArrayElements(env, data, (jint *) pData, mode); - break; - } -} - /* * Class: sun_java2d_cmm_lcms_LCMS * Method: colorConvert - * Signature: (JIIIIIIZZLjava/lang/Object;Ljava/lang/Object;)V + * Signature: (JIIIIIILjava/lang/Object;Ljava/lang/Object;)V */ JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_colorConvert (JNIEnv *env, jclass cls, jlong ID, jint width, jint height, jint srcOffset, jint srcNextRowOffset, jint dstOffset, jint dstNextRowOffset, - jobject srcData, jobject dstData, jint srcDType, jint dstDType) + jobject srcData, jobject dstData) { cmsHTRANSFORM sTrans = jlong_to_ptr(ID); - if (sTrans == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "LCMS_colorConvert: transform == NULL"); JNU_ThrowByName(env, "java/awt/color/CMMException", @@ -511,28 +477,22 @@ JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_colorConvert return; } - void *inputBuffer = getILData(env, srcData, srcDType); + void *inputBuffer = (*env)->GetPrimitiveArrayCritical(env, srcData, NULL); if (inputBuffer == NULL) { - J2dRlsTraceLn(J2D_TRACE_ERROR, ""); // An exception should have already been thrown. return; } + void *outputBuffer = (*env)->GetPrimitiveArrayCritical(env, dstData, NULL); + if (outputBuffer != NULL) { + char *input = (char *) inputBuffer + srcOffset; + char *output = (char *) outputBuffer + dstOffset; - void *outputBuffer = getILData(env, dstData, dstDType); - if (outputBuffer == NULL) { - releaseILData(env, inputBuffer, srcDType, srcData, JNI_ABORT); - // An exception should have already been thrown. - return; - } + cmsDoTransformLineStride(sTrans, input, output, width, height, + srcNextRowOffset, dstNextRowOffset, 0, 0); - char *input = (char *) inputBuffer + srcOffset; - char *output = (char *) outputBuffer + dstOffset; - - cmsDoTransformLineStride(sTrans, input, output, width, height, - srcNextRowOffset, dstNextRowOffset, 0, 0); - - releaseILData(env, inputBuffer, srcDType, srcData, JNI_ABORT); - releaseILData(env, outputBuffer, dstDType, dstData, 0); + (*env)->ReleasePrimitiveArrayCritical(env, dstData, outputBuffer, 0); + } + (*env)->ReleasePrimitiveArrayCritical(env, srcData, inputBuffer, JNI_ABORT); } static cmsBool _getHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize) diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java b/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java index c50a4922e80..64ff093152f 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java @@ -27,11 +27,9 @@ import java.io.IOException; import java.net.ProtocolException; -import java.net.http.HttpClient.Version; import java.time.Duration; import java.util.Optional; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @@ -708,12 +706,10 @@ HttpResponse.BodySubscriber ignoreBody(HttpResponse.ResponseInfo hdrs) { if (s == null) { // s can be null if an exception occurred // asynchronously while sending the preface. - Throwable t = c.getRecordedCause(); + final Http2TerminationCause tc = c.getTerminationCause(); IOException ioe; - if (t != null) { - if (!cached) - c.close(); - ioe = new IOException("Can't get stream 1: " + t, t); + if (tc != null) { + ioe = new IOException("Can't get stream 1", tc.getCloseCause()); } else { ioe = new IOException("Can't get stream 1"); } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Http2ClientImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/Http2ClientImpl.java index cc8a2a7142b..6121a0c1673 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Http2ClientImpl.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Http2ClientImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -25,7 +25,6 @@ package jdk.internal.net.http; -import java.io.EOFException; import java.io.IOException; import java.io.UncheckedIOException; import java.util.Base64; @@ -234,11 +233,8 @@ void removeFromPool(Http2Connection c) { } } - private EOFException STOPPED; void stop() { if (debug.on()) debug.log("stopping"); - STOPPED = new EOFException("HTTP/2 client stopped"); - STOPPED.setStackTrace(new StackTraceElement[0]); connectionPoolLock.lock(); try { stopping = true; @@ -253,10 +249,7 @@ void stop() { private boolean close(Http2Connection h2c) { // close all streams try { h2c.closeAllStreams(); } catch (Throwable t) {} - // send GOAWAY try { h2c.close(); } catch (Throwable t) {} - // attempt graceful shutdown - try { h2c.shutdown(STOPPED); } catch (Throwable t) {} // double check and close any new streams try { h2c.closeAllStreams(); } catch (Throwable t) {} // Allows for use of removeIf in stop() diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java b/src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java index 63889fa6af2..94b8505da47 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java @@ -25,21 +25,20 @@ package jdk.internal.net.http; +import java.io.Closeable; import java.io.EOFException; import java.io.IOException; import java.io.UncheckedIOException; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.VarHandle; import java.net.InetSocketAddress; import java.net.ProtocolException; import java.net.http.HttpClient; import java.net.http.HttpClient.Version; import java.net.http.HttpHeaders; import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.net.http.HttpConnectTimeoutException; import java.time.Duration; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Locale; @@ -49,6 +48,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Flow; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; @@ -56,6 +56,7 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; import java.util.function.Supplier; + import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; @@ -130,7 +131,7 @@ * and incoming stream creation (Server push). Incoming frames destined for a * stream are provided by calling Stream.incoming(). */ -class Http2Connection { +class Http2Connection implements Closeable { final Logger debug = Utils.getDebugLogger(this::dbgString); static final Logger DEBUG_LOGGER = @@ -143,6 +144,8 @@ class Http2Connection { private static final int MAX_SERVER_STREAM_ID = Integer.MAX_VALUE - 1; // 2147483646 // may be null; must be accessed/updated with the stateLock held private IdleConnectionTimeoutEvent idleConnectionTimeoutEvent; + private final AtomicBoolean goAwaySent = new AtomicBoolean(); + private final AtomicBoolean goAwayRecvd = new AtomicBoolean(); /** * Flag set when no more streams to be opened on this connection. @@ -215,7 +218,7 @@ final class IdleConnectionTimeoutEvent extends TimeoutEvent { } /** - * {@link #shutdown(Throwable) Shuts down} the connection, unless this event is + * {@link #close(Http2TerminationCause) Closes} the connection, unless this event is * {@link #cancelled} */ @Override @@ -228,26 +231,20 @@ public void handle() { try { if (cancelled) { if (debug.on()) { - debug.log("Not initiating idle connection shutdown"); - } - return; - } - if (!markIdleShutdownInitiated()) { - if (debug.on()) { - debug.log("Unexpected state %s, skipping idle connection shutdown", - describeClosedState(closedState)); + debug.log("Idle timeout event already cancelled, not initiating idle connection close"); } return; } + // the connection has been idle long enough, we now + // mark a state indicating that the connection is chosen + // for idle termination and should not be handed out (from the pool) + // for newer requests. + connTerminator.markForIdleTermination(); } finally { stateLock.unlock(); } - if (debug.on()) { - debug.log("Initiating shutdown of HTTP connection which is idle for too long"); - } - HttpConnectTimeoutException hte = new HttpConnectTimeoutException( - "HTTP connection idle, no active streams. Shutting down."); - shutdown(hte); + // terminate the connection due to being idle long enough + connTerminator.idleTimedOut(); } /** @@ -256,7 +253,7 @@ public void handle() { void cancel() { assert stateLock.isHeldByCurrentThread() : "Current thread doesn't hold " + stateLock; // mark as cancelled to prevent potentially already triggered event from actually - // doing the shutdown + // doing the close this.cancelled = true; // cancel the timer to prevent the event from being triggered (if it hasn't already) client().cancelTimer(this); @@ -376,16 +373,7 @@ public void onMaxHeaderListSizeReached(long size, int maxHeaderListSize) throws } - private static final int HALF_CLOSED_LOCAL = 1; - private static final int HALF_CLOSED_REMOTE = 2; - private static final int SHUTDOWN_REQUESTED = 4; - // state when idle connection management initiates a shutdown of the connection, after - // which the connection will go into SHUTDOWN_REQUESTED state - private static final int IDLE_SHUTDOWN_INITIATED = 8; private final ReentrantLock stateLock = new ReentrantLock(); - private volatile int closedState; - - //------------------------------------- final HttpConnection connection; private final Http2ClientImpl client2; private final ConcurrentHashMap> streams = new ConcurrentHashMap<>(); @@ -403,7 +391,9 @@ public void onMaxHeaderListSizeReached(long size, int maxHeaderListSize) throws private final Decoder hpackIn; final SettingsFrame clientSettings; private volatile SettingsFrame serverSettings; + private record PushContinuationState(PushPromiseDecoder pushContDecoder, PushPromiseFrame pushContFrame) {} + private volatile PushContinuationState pushContinuationState; private final String key; // for HttpClientImpl.connections map private final FramesDecoder framesDecoder; @@ -418,7 +408,7 @@ private record PushContinuationState(PushPromiseDecoder pushContDecoder, PushPro private final FramesController framesController = new FramesController(); private final Http2TubeSubscriber subscriber; final ConnectionWindowUpdateSender windowUpdater; - private final AtomicReference cause = new AtomicReference<>(); + private final Terminator connTerminator = new Terminator(); private volatile Supplier initial; private volatile Stream initialStream; @@ -640,35 +630,31 @@ private boolean reserveStream0(boolean clientInitiated, boolean pushEnabled) thr } void abandonStream() { - boolean shouldClose = false; stateLock.lock(); try { long reserved = --numReservedClientStreams; assert reserved >= 0; - if (finalStream && reserved == 0 && streams.isEmpty()) { - shouldClose = true; - } } catch (Throwable t) { - shutdown(t); // in case the assert fires... + close(Http2TerminationCause.forException(t)); // in case the assert fires... } finally { stateLock.unlock(); } - // We should close the connection here if - // it's not pooled. If it's not pooled it will - // be marked final stream, reserved will be 0 - // after decrementing it by one, and there should - // be no active request-response streams. - if (shouldClose) { - shutdown(new IOException("HTTP/2 connection abandoned")); + // if the connection is eligible to be closed, we close it here + if (shouldClose()) { + close(Http2TerminationCause.noErrorTermination()); } - } - boolean shouldClose() { + /* + * return true if the connection is marked as "final stream" and there + * are no active streams on that connection and the connection isn't + * reserved for a new stream. + */ + final boolean shouldClose() { stateLock.lock(); try { - return finalStream() && streams.isEmpty(); + return finalStream() && streams.isEmpty() && numReservedClientStreams == 0; } finally { stateLock.unlock(); } @@ -840,22 +826,8 @@ final int maxConcurrentServerInitiatedStreams() { return clientSettings.getParameter(MAX_CONCURRENT_STREAMS); } - void close() { - if (markHalfClosedLocal()) { - // we send a GOAWAY frame only if the remote side hasn't already indicated - // the intention to close the connection by previously sending a GOAWAY of its own - if (connection.channel().isOpen() && !isMarked(closedState, HALF_CLOSED_REMOTE)) { - Log.logTrace("Closing HTTP/2 connection: to {0}", connection.address()); - GoAwayFrame f = new GoAwayFrame(0, - ErrorFrame.NO_ERROR, - "Requested by user".getBytes(UTF_8)); - // TODO: set last stream. For now zero ok. - sendFrame(f); - } - } - } - long count; + final void asyncReceive(ByteBuffer buffer) { // We don't need to read anything and // we don't want to send anything back to the server @@ -904,44 +876,26 @@ final void asyncReceive(ByteBuffer buffer) { } catch (Throwable e) { String msg = Utils.stackTrace(e); Log.logTrace(msg); - shutdown(e); + close(Http2TerminationCause.forException(e)); } } - Throwable getRecordedCause() { - return cause.get(); + /** + * Closes the connection normally (with a NO_ERROR termination cause), if not already closed. + */ + @Override + public final void close() { + close(Http2TerminationCause.noErrorTermination()); } - void shutdown(Throwable t) { - int state = closedState; - if (debug.on()) debug.log(() -> "Shutting down h2c (state="+describeClosedState(state)+"): " + t); - stateLock.lock(); - try { - if (!markShutdownRequested()) return; - cause.compareAndSet(null, t); - } finally { - stateLock.unlock(); - } - - if (Log.errors()) { - if (t!= null && (!(t instanceof EOFException) || isActive())) { - Log.logError(t); - } else if (t != null) { - Log.logError("Shutting down connection: {0}", t.getMessage()); - } else { - Log.logError("Shutting down connection"); - } - } - client2.removeFromPool(this); - subscriber.stop(cause.get()); - for (Stream s : streams.values()) { - try { - s.connectionClosing(t); - } catch (Throwable e) { - Log.logError("Failed to close stream {0}: {1}", s.streamid, e); - } - } - connection.close(cause.get()); + /** + * Closes the connection with the given termination cause, if not already closed. + * + * @param tc the termination cause. cannot be null. + */ + final void close(final Http2TerminationCause tc) { + Objects.requireNonNull(tc, "termination cause cannot be null"); + this.connTerminator.terminate(tc); } /** @@ -981,15 +935,14 @@ void processFrame(Http2Frame frame) throws IOException { } else { if (frame instanceof SettingsFrame) { // The stream identifier for a SETTINGS frame MUST be zero - framesDecoder.close( + protocolError(ErrorFrame.PROTOCOL_ERROR, "The stream identifier for a SETTINGS frame MUST be zero"); - protocolError(GoAwayFrame.PROTOCOL_ERROR); return; } if (frame instanceof PushPromiseFrame && !serverPushEnabled()) { String protocolError = "received a PUSH_PROMISE when SETTINGS_ENABLE_PUSH is 0"; - protocolError(ResetFrame.PROTOCOL_ERROR, protocolError); + protocolError(ErrorFrame.PROTOCOL_ERROR, protocolError); return; } @@ -1144,7 +1097,9 @@ final void releaseUnconsumed(DataFrame df) { // Otherwise, if the frame is dropped after having been added to the // inputQ, releaseUnconsumed above should be called. final void dropDataFrame(DataFrame df) { - if (isMarked(closedState, SHUTDOWN_REQUESTED)) return; + if (!isOpen()) { + return; + } if (debug.on()) { debug.log("Dropping data frame for stream %d (%d payload bytes)", df.streamid(), df.payloadLength()); @@ -1154,7 +1109,9 @@ final void dropDataFrame(DataFrame df) { final void ensureWindowUpdated(DataFrame df) { try { - if (isMarked(closedState, SHUTDOWN_REQUESTED)) return; + if (!isOpen()) { + return; + } int length = df.payloadLength(); if (length > 0) { windowUpdater.update(length); @@ -1251,12 +1208,16 @@ private void handleConnectionFrame(Http2Frame frame) case AltSvcFrame.TYPE -> processAltSvcFrame(0, (AltSvcFrame) frame, connection, connection.client()); - default -> protocolError(ErrorFrame.PROTOCOL_ERROR); + default -> protocolError(ErrorFrame.PROTOCOL_ERROR, "unknown frame: " + frame); } } - boolean isOpen() { - return !isMarkedForShutdown() && connection.channel().isOpen(); + /** + * Returns true if this connection hasn't been terminated and the underlying + * {@linkplain NetworkChannel#isOpen() channel is open}. false otherwise. + */ + final boolean isOpen() { + return this.connTerminator.terminationCause.get() == null && connection.channel().isOpen(); } void resetStream(int streamid, int code) { @@ -1295,6 +1256,7 @@ void decrementStreamsCount(int streamid) { } } + private void decrementStreamsCount0(int streamid) { Stream s = streams.get(streamid); if (s == null || !s.deRegister()) @@ -1346,8 +1308,7 @@ void closeStream(int streamid) { // corresponding entry in the window controller. windowController.removeStream(streamid); } - if (finalStream() && streams.isEmpty()) { - // should be only 1 stream, but there might be more if server push + if (shouldClose()) { close(); } else { // Start timer if property present and not already created @@ -1372,9 +1333,7 @@ void closeStream(int streamid) { /** * Increments this connection's send Window by the amount in the given frame. */ - private void handleWindowUpdate(WindowUpdateFrame f) - throws IOException - { + private void handleWindowUpdate(WindowUpdateFrame f) { int amount = f.getUpdate(); if (amount <= 0) { // ## temporarily disable to workaround a bug in Jetty where it @@ -1383,37 +1342,19 @@ private void handleWindowUpdate(WindowUpdateFrame f) } else { boolean success = windowController.increaseConnectionWindow(amount); if (!success) { - protocolError(ErrorFrame.FLOW_CONTROL_ERROR); // overflow + protocolError(ErrorFrame.FLOW_CONTROL_ERROR, null); // overflow } } } - private void protocolError(int errorCode) - throws IOException - { - protocolError(errorCode, null); - } - - private void protocolError(int errorCode, String msg) - throws IOException - { - String protocolError = "protocol error" + (msg == null?"":(": " + msg)); - ProtocolException protocolException = - new ProtocolException(protocolError); - this.cause.compareAndSet(null, protocolException); - if (markHalfClosedLocal()) { - framesDecoder.close(protocolError); - subscriber.stop(protocolException); - if (debug.on()) debug.log("Sending GOAWAY due to " + protocolException); - GoAwayFrame frame = new GoAwayFrame(0, errorCode); - sendFrame(frame); - } - shutdown(protocolException); + private void protocolError(final int errorCode, final String msg) { + final Http2TerminationCause terminationCause = + Http2TerminationCause.forH2Error(errorCode, msg); + framesDecoder.close(terminationCause.getLogMsg()); + close(terminationCause); } - private void handleSettings(SettingsFrame frame) - throws IOException - { + private void handleSettings(SettingsFrame frame) { assert frame.streamid() == 0; if (!frame.getFlag(SettingsFrame.ACK)) { int newWindowSize = frame.getParameter(INITIAL_WINDOW_SIZE); @@ -1430,9 +1371,7 @@ private void handleSettings(SettingsFrame frame) } } - private void handlePing(PingFrame frame) - throws IOException - { + private void handlePing(PingFrame frame) { frame.setFlag(PingFrame.ACK); sendUnorderedFrame(frame); } @@ -1442,7 +1381,7 @@ private void handleGoAway(final GoAwayFrame frame) { assert lastProcessedStream >= 0 : "unexpected last stream id: " + lastProcessedStream + " in GOAWAY frame"; - markHalfClosedRemote(); + goAwayRecvd.set(true); setFinalStream(); // don't allow any new streams on this connection if (debug.on()) { debug.log("processing incoming GOAWAY with last processed stream id:%s in frame %s", @@ -1599,20 +1538,20 @@ boolean tryReserveForPoolCheckout() { // must be done with "stateLock" held to co-ordinate idle connection management stateLock.lock(); try { - cancelIdleShutdownEvent(); - // consider the reservation successful only if the connection's state hasn't moved - // to "being closed" - return isOpen(); + cancelIdleCloseEvent(); + // consider the reservation successful only if the connection is open and + // hasn't been chosen for idle termination + return !this.connTerminator.isMarkedForIdleTermination() && isOpen(); } finally { stateLock.unlock(); } } /** - * Cancels any event that might have been scheduled to shutdown this connection. Must be called + * Cancels any event that might have been scheduled to close this connection. Must be called * with the stateLock held. */ - private void cancelIdleShutdownEvent() { + private void cancelIdleCloseEvent() { assert stateLock.isHeldByCurrentThread() : "Current thread doesn't hold " + stateLock; if (idleConnectionTimeoutEvent == null) { return; @@ -1627,20 +1566,23 @@ void putStream(Stream stream, int streamid) { // the stream is closed. stateLock.lock(); try { - if (!isMarkedForShutdown()) { + if (isOpen() && !this.connTerminator.isMarkedForIdleTermination()) { if (debug.on()) { debug.log("Opened stream %d", streamid); } client().streamReference(); streams.put(streamid, stream); - cancelIdleShutdownEvent(); + // don't consider the connection idle anymore + cancelIdleCloseEvent(); return; } } finally { stateLock.unlock(); } if (debug.on()) debug.log("connection closed: closing stream %d", stream); - stream.cancel(new IOException("Stream " + streamid + " cancelled", cause.get())); + final Http2TerminationCause terminationCause = getTerminationCause(); + assert terminationCause != null : "termination cause is null"; + stream.cancel(new IOException("Stream " + streamid + " cancelled", terminationCause.getCloseCause())); } /** @@ -1743,11 +1685,10 @@ private Stream registerNewStream(OutgoingHeaders> oh) { int streamid = nextstreamid; Throwable cause = null; synchronized (this) { - if (isMarked(closedState, SHUTDOWN_REQUESTED)) { - cause = this.cause.get(); - if (cause == null) { - cause = new IOException("Connection closed"); - } + if (!isOpen()) { + final Http2TerminationCause terminationCause = getTerminationCause(); + assert terminationCause != null : "termination cause is null"; + cause = terminationCause.getCloseCause(); } } if (cause != null) { @@ -1762,7 +1703,7 @@ private Stream registerNewStream(OutgoingHeaders> oh) { return stream; } else { stream.cancelImpl(new IOException("Request cancelled")); - if (finalStream() && streams.isEmpty()) { + if (shouldClose()) { close(); } return null; @@ -1795,14 +1736,12 @@ void sendFrame(Http2Frame frame) { } publisher.signalEnqueued(); } catch (IOException e) { - if (!isMarked(closedState, SHUTDOWN_REQUESTED)) { - if (!client2.stopping()) { - Log.logError(e); - shutdown(e); - } else if (debug.on()) { - debug.log("Failed to send %s while stopping: %s", frame, e); - } + if (!client2.stopping()) { + Log.logError(e); + } else if (debug.on()) { + debug.log("Failed to send %s while stopping: %s", frame, e); } + close(Http2TerminationCause.forException(e)); } } @@ -1817,14 +1756,12 @@ void sendDataFrame(DataFrame frame) { publisher.enqueue(encodeFrame(frame)); publisher.signalEnqueued(); } catch (IOException e) { - if (!isMarked(closedState, SHUTDOWN_REQUESTED)) { - if (!client2.stopping()) { - Log.logError(e); - shutdown(e); - } else if (debug.on()) { - debug.log("Failed to send %s while stopping: %s", frame, e); - } + if (!client2.stopping()) { + Log.logError(e); + } else if (debug.on()) { + debug.log("Failed to send %s while stopping: %s", frame, e); } + close(Http2TerminationCause.forException(e)); } } @@ -1839,10 +1776,12 @@ void sendUnorderedFrame(Http2Frame frame) { publisher.enqueueUnordered(encodeFrame(frame)); publisher.signalEnqueued(); } catch (IOException e) { - if (!isMarked(closedState, SHUTDOWN_REQUESTED)) { + if (!client2.stopping()) { Log.logError(e); - shutdown(e); + } else if (debug.on()) { + debug.log("Failed to send %s while stopping: %s", frame, e); } + close(Http2TerminationCause.forException(e)); } } @@ -1868,6 +1807,7 @@ final void processQueue() { try { while (!queue.isEmpty() && !scheduler.isStopped()) { ByteBuffer buffer = queue.poll(); + assert buffer != null : "null buffer obtained from non-empty queue"; if (debug.on()) debug.log("sending %d to Http2Connection.asyncReceive", buffer.remaining()); @@ -1877,7 +1817,12 @@ final void processQueue() { errorRef.compareAndSet(null, t); } finally { Throwable x = errorRef.get(); - if (x != null) { + // if there was any error or if the TubeSubscriber completed normally, + // then close the connection + if (x != null || completed) { + // although the connection terminator stops the scheduler too, + // we don't want to wait that "long" and instead we should immediately + // stop the scheduler so that we don't enter "processQueue" anymore. scheduler.stop(); if (client2.stopping()) { if (debug.on()) { @@ -1888,7 +1833,11 @@ final void processQueue() { debug.log("Stopping scheduler", x); } } - Http2Connection.this.shutdown(x); + // terminate the connection + final Http2TerminationCause tc = (x != null) + ? Http2TerminationCause.forException(x) + : Http2TerminationCause.noErrorTermination(); + Http2Connection.this.close(tc); } } } @@ -1938,11 +1887,17 @@ public void onError(Throwable throwable) { @Override public void onComplete() { if (completed) return; - String msg = isActive() - ? "EOF reached while reading" - : "Idle connection closed by HTTP/2 peer"; - if (debug.on()) debug.log(msg); - errorRef.compareAndSet(null, new EOFException(msg)); + if (isActive()) { + final String msg = "EOF reached while reading"; + errorRef.compareAndSet(null, new EOFException(msg)); + if (debug.on()) { + debug.log(msg); + } + } else { + if (debug.on()) { + debug.log("HTTP/2 connection (with no active streams) closed by peer"); + } + } completed = true; runOrSchedule(); } @@ -1955,16 +1910,13 @@ public void dropSubscription() { dropped = true; } - void stop(Throwable error) { - if (errorRef.compareAndSet(null, error)) { - completed = true; - scheduler.stop(); - queue.clear(); - if (subscription != null) { - subscription.cancel(); - } - queue.clear(); + private void close() { + scheduler.stop(); + queue.clear(); + if (subscription != null) { + subscription.cancel(); } + queue.clear(); } } @@ -1990,6 +1942,7 @@ final String dbgString() { static final class ConnectionWindowUpdateSender extends WindowUpdateSender { final int initialWindowSize; + public ConnectionWindowUpdateSender(Http2Connection connection, int initialWindowSize) { super(connection, initialWindowSize); @@ -2004,13 +1957,9 @@ int getStreamId() { @Override protected boolean windowSizeExceeded(long received) { if (connection.isOpen()) { - try { - connection.protocolError(ErrorFrame.FLOW_CONTROL_ERROR, - "connection window exceeded (%s > %s)" - .formatted(received, windowSize)); - } catch (IOException io) { - connection.shutdown(io); - } + connection.protocolError(ErrorFrame.FLOW_CONTROL_ERROR, + "connection window exceeded (%s > %s)" + .formatted(received, windowSize)); } return true; } @@ -2033,72 +1982,144 @@ AbstractAsyncSSLConnection getConnection() { } } - private boolean isMarked(int state, int mask) { - return (state & mask) == mask; - } - - private boolean isMarkedForShutdown() { - final int closedSt = closedState; - return isMarked(closedSt, IDLE_SHUTDOWN_INITIATED) - || isMarked(closedSt, SHUTDOWN_REQUESTED); - } - - private boolean markShutdownRequested() { - return markClosedState(SHUTDOWN_REQUESTED); - } - - private boolean markHalfClosedLocal() { - return markClosedState(HALF_CLOSED_LOCAL); - } - - private boolean markHalfClosedRemote() { - return markClosedState(HALF_CLOSED_REMOTE); - } - - private boolean markIdleShutdownInitiated() { - return markClosedState(IDLE_SHUTDOWN_INITIATED); - } - - private boolean markClosedState(int flag) { - int state, desired; - do { - state = desired = closedState; - if ((state & flag) == flag) return false; - desired = state | flag; - } while (!CLOSED_STATE.compareAndSet(this, state, desired)); - return true; + private void sendGoAway(final GoAwayFrame goAway) { + // currently we send a GOAWAY just once irrespective of what value the + // last stream id was in the GOAWAY frame + if (!goAwaySent.compareAndSet(false, true)) { + // already sent + return; + } + if (Log.trace()) { + Log.logTrace("{0} sending GOAWAY {1}", connection, goAway); + } else if (debug.on()) { + debug.log("sending GOAWAY " + goAway); + } + // this merely enqueues the frame + sendFrame(goAway); } - String describeClosedState(int state) { - if (state == 0) return "active"; - String desc = null; - if (isMarked(state, IDLE_SHUTDOWN_INITIATED)) { - desc = "idle-shutdown-initiated"; + /** + * Returns the termination cause if the connection is closed, else returns null. + */ + final Http2TerminationCause getTerminationCause() { + return this.connTerminator.determineTerminationCause(); + } + + // Responsible for doing all the necessary work for closing a Http2Connection + private final class Terminator { + // the cause for closing the connection. Must only be set in the + // Terminator.terminate(Http2TerminationCause) method. + private final AtomicReference terminationCause = new AtomicReference<>(); + // true if it has been decided to terminate the connection due to being idle, + // false otherwise. should be accessed only when holding the stateLock + private boolean chosenForIdleTermination; + + private void terminate(final Http2TerminationCause terminationCause) { + Objects.requireNonNull(terminationCause, "termination cause cannot be null"); + // allow to be terminated only once + stateLock.lock(); + try { + final boolean success = this.terminationCause.compareAndSet(null, terminationCause); + if (!success) { + // already terminated or is being terminated by some other thread + return; + } + // disable the idle timeout event, since we are now going to terminate the + // connection + Http2Connection.this.cancelIdleCloseEvent(); + } finally { + stateLock.unlock(); + } + // do the actual termination + doTerminate(); } - if (isMarked(state, SHUTDOWN_REQUESTED)) { - desc = desc == null ? "shutdown" : desc + "+shutdown"; + + private void doTerminate() { + final Http2TerminationCause tc = terminationCause.get(); + assert tc != null : "missing termination cause"; + // we send a GOAWAY frame only if the remote side hasn't already indicated + // the intention to close the connection by previously sending a GOAWAY of its own + if (!Http2Connection.this.goAwayRecvd.get()) { + final int lastStream = 0; // TODO: set last stream. For now zero is ok. + final String peerVisibleReason = tc.getPeerVisibleReason(); + final GoAwayFrame goAway; + if (peerVisibleReason == null) { + goAway = new GoAwayFrame(lastStream, tc.getCloseCode()); + } else { + goAway = new GoAwayFrame(lastStream, tc.getCloseCode(), + peerVisibleReason.getBytes(UTF_8)); + } + sendGoAway(goAway); + } + // now close the connection + + if (Log.errors() || debug.on()) { + final String stateStr = "Abnormal close=" + tc.isAbnormalClose() + + ", has active streams=" + isActive() + + ", GOAWAY received=" + goAwayRecvd.get() + + ", GOAWAY sent=" + goAwaySent.get(); + if (Log.errors()) { + Log.logError("Closing connection {0} ({1}) due to: {2}", + connection, stateStr, tc); + } else { + debug.log("Closing connection (" + stateStr + ") due to: " + tc); + } + } + // close the TubeSubscriber + subscriber.close(); + client2.removeFromPool(Http2Connection.this); + // notify the HTTP/2 streams of the connection closure + for (final Stream s : streams.values()) { + try { + s.connectionClosing(tc.getCloseCause()); + } catch (Throwable e) { + Log.logError("Failed to close stream {0}: {1}", s.streamid, e); + } + } + // close the underlying connection + connection.close(tc.getCloseCause()); } - if (isMarked(state, HALF_CLOSED_LOCAL | HALF_CLOSED_REMOTE)) { - if (desc == null) return "closed"; - else return desc + "+closed"; + + private void markForIdleTermination() { + assert stateLock.isHeldByCurrentThread() : Thread.currentThread() + + " not holding stateLock"; + this.chosenForIdleTermination = true; } - if (isMarked(state, HALF_CLOSED_LOCAL)) { - if (desc == null) return "half-closed-local"; - else return desc + "+half-closed-local"; + + private boolean isMarkedForIdleTermination() { + assert stateLock.isHeldByCurrentThread() : Thread.currentThread() + + " not holding stateLock"; + return this.chosenForIdleTermination; } - if (isMarked(state, HALF_CLOSED_REMOTE)) { - if (desc == null) return "half-closed-remote"; - else return desc + "+half-closed-remote"; + + private void idleTimedOut() { + if (debug.on()) { + debug.log("closing connection due to being idle"); + } + this.terminate(Http2TerminationCause.idleTimedOut()); } - return "0x" + Integer.toString(state, 16); - } - private static final VarHandle CLOSED_STATE; - static { - try { - CLOSED_STATE = MethodHandles.lookup().findVarHandle(Http2Connection.class, "closedState", int.class); - } catch (Exception x) { - throw new ExceptionInInitializerError(x); + /** + * Returns the termination cause for the connection. This method guarantees that if the + * {@linkplain Http2Connection#isOpen() connection is not open}, when this method is called, + * then it returns a non-null termination cause. Returns null if the connection is open. + */ + private Http2TerminationCause determineTerminationCause() { + final Http2TerminationCause tc = this.terminationCause.get(); + if (tc != null) { + // already terminated, return the cause + return tc; + } + if (!connection.channel().isOpen()) { + // if the underlying SocketChannel isn't open, then terminate the connection. + // that way when Http2Connection.isOpen() returns false in that situation, then this + // getTerminationCause() will return a termination cause. + terminate(Http2TerminationCause.forException(new ClosedChannelException())); + final Http2TerminationCause terminated = this.terminationCause.get(); + assert terminated != null : "missing termination cause"; + return terminated; + } + return null; // connection still open } } } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Http2TerminationCause.java b/src/java.net.http/share/classes/jdk/internal/net/http/Http2TerminationCause.java new file mode 100644 index 00000000000..89b08cf8888 --- /dev/null +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Http2TerminationCause.java @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.internal.net.http; + + +import java.io.IOException; +import java.net.ProtocolException; +import java.util.Objects; + +import jdk.internal.net.http.common.Utils; +import jdk.internal.net.http.frame.ErrorFrame; + +/** + * Termination cause for an {@linkplain Http2Connection HTTP/2 connection} + */ +public abstract sealed class Http2TerminationCause { + private String logMsg; + private String peerVisibleReason; + private final int closeCode; + private final Throwable originalCause; + private final IOException reportedCause; + + private Http2TerminationCause(final int closeCode, final Throwable closeCause) { + this.closeCode = closeCode; + this.originalCause = closeCause; + if (closeCause != null) { + this.logMsg = closeCause.toString(); + } + this.reportedCause = toReportedCause(this.originalCause, this.logMsg); + } + + private Http2TerminationCause(final int closeCode, final String loggedAs) { + this.closeCode = closeCode; + this.originalCause = null; + this.logMsg = loggedAs; + this.reportedCause = toReportedCause(null, this.logMsg); + } + + /** + * Returns the error code (specified for HTTP/2 ErrorFrame) that caused the + * connection termination. + */ + public final int getCloseCode() { + return this.closeCode; + } + + /** + * Returns the {@link IOException} that is considered the cause of the connection termination. + * Even a {@linkplain #isAbnormalClose() normal} termination will have + * an {@code IOException} associated with it, so this method will always return a non-null instance. + */ + public final IOException getCloseCause() { + return this.reportedCause; + } + + /** + * Returns {@code true} if the connection was terminated due to some exception. {@code false} + * otherwise. + * A normal connection termination (for example, the connection idle timing out locally) + * is not considered as an abnormal termination and this method returns {@code false} for + * such cases. + */ + public abstract boolean isAbnormalClose(); + + /** + * Returns the connection termination cause, represented as a string. Unlike the + * {@linkplain #getPeerVisibleReason() peer-visible reason}, this log message will not be + * sent across to the peer and it is thus allowed to include additional details that might + * help debugging a connection termination. + */ + public final String getLogMsg() { + return logMsg; + } + + /** + * Returns the connection termination cause, represented as a string. This represents the + * "debugData" that is sent to the peer in a + * {@linkplain jdk.internal.net.http.frame.GoAwayFrame GOAWAY frame}. + */ + public final String getPeerVisibleReason() { + return this.peerVisibleReason; + } + + /** + * Sets the connection termination cause, represented as a string, which will be sent + * to the peer in a {@linkplain jdk.internal.net.http.frame.GoAwayFrame GOAWAY frame}. + * Unlike the {@link #getLogMsg() log message}, + * it is expected that this peer-visible reason will not contain anything that is not meant + * to be viewed by the peer. + */ + protected final void setPeerVisibleReason(final String reasonPhrase) { + this.peerVisibleReason = reasonPhrase; + } + + /** + * Returns a connection termination cause that represents an + * {@linkplain #isAbnormalClose() abnormal} termination due to the given {@code cause}. + * + * @param cause the termination cause, cannot be null. + */ + public static Http2TerminationCause forException(final Throwable cause) { + Objects.requireNonNull(cause); + if (cause instanceof ProtocolException pe) { + return new ProtocolError(pe); + } + return new InternalError(cause); + } + + /** + * Returns a connection termination cause that represents a + * {@linkplain #isAbnormalClose() normal} termination. + */ + public static Http2TerminationCause noErrorTermination() { + return NoError.INSTANCE; + } + + /** + * Returns a connection termination cause that represents a + * {@linkplain #isAbnormalClose() normal} termination due to the connection + * being idle. + */ + public static Http2TerminationCause idleTimedOut() { + return NoError.IDLE_TIMED_OUT; + } + + /** + * Returns a connection termination cause that represents an + * {@linkplain #isAbnormalClose() abnormal} termination due to the given {@code errorCode}. + * Although this method does no checks for the {@code errorCode}, it is expected to be one + * of the error codes specified by the HTTP/2 RFC for the ErrorFrame. + * + * @param errorCode the error code + * @param loggedAs optional log message to be associated with this termination cause + */ + public static Http2TerminationCause forH2Error(final int errorCode, final String loggedAs) { + if (errorCode == ErrorFrame.PROTOCOL_ERROR) { + return new ProtocolError(loggedAs); + } else if (errorCode == ErrorFrame.FLOW_CONTROL_ERROR) { + // we treat flow control error as a protocol error currently + return new ProtocolError(loggedAs, true); + } + return new H2StandardError(errorCode, loggedAs); + } + + private static IOException toReportedCause(final Throwable original, + final String fallbackExceptionMsg) { + if (original == null) { + return fallbackExceptionMsg == null + ? new IOException("connection terminated") + : new IOException(fallbackExceptionMsg); + } else if (original instanceof IOException ioe) { + return ioe; + } else { + return Utils.toIOException(original); + } + } + + private static final class NoError extends Http2TerminationCause { + private static final IOException NO_ERROR_MARKER = + new IOException("HTTP/2 connection closed normally - no error"); + private static final IOException NO_ERROR_IDLE_TIMED_OUT_MARKER = + new IOException("HTTP/2 connection idle timed out - no error"); + + static { + // remove the stacktrace from the marker exception instances + NO_ERROR_MARKER.setStackTrace(new StackTraceElement[0]); + NO_ERROR_IDLE_TIMED_OUT_MARKER.setStackTrace(new StackTraceElement[0]); + } + + private static final NoError INSTANCE = new NoError(false); + private static final NoError IDLE_TIMED_OUT = new NoError(true); + + private final boolean idleTimedOut; + + private NoError(final boolean idleTimedOut) { + super(ErrorFrame.NO_ERROR, + idleTimedOut ? NO_ERROR_IDLE_TIMED_OUT_MARKER : NO_ERROR_MARKER); + this.idleTimedOut = idleTimedOut; + setPeerVisibleReason(idleTimedOut ? "idle timed out" : "no error"); + } + + @Override + public boolean isAbnormalClose() { + return false; + } + + @Override + public String toString() { + return this.idleTimedOut + ? "No error - idle timed out" + : "No error - normal termination"; + } + } + + private static sealed class H2StandardError extends Http2TerminationCause { + private H2StandardError(final int errCode, final String msg) { + super(errCode, msg); + setPeerVisibleReason(ErrorFrame.stringForCode(errCode)); + } + + private H2StandardError(final int errCode, final Throwable cause) { + super(errCode, cause); + setPeerVisibleReason(ErrorFrame.stringForCode(errCode)); + } + + @Override + public boolean isAbnormalClose() { + return getCloseCode() != ErrorFrame.NO_ERROR; + } + + @Override + public String toString() { + return ErrorFrame.stringForCode(this.getCloseCode()); + } + } + + private static final class ProtocolError extends H2StandardError { + private ProtocolError(final String msg) { + this(msg, false); + } + + private ProtocolError(final String msg, final boolean flowControlError) { + super(flowControlError + ? ErrorFrame.FLOW_CONTROL_ERROR + : ErrorFrame.PROTOCOL_ERROR, + new ProtocolException(msg)); + } + + private ProtocolError(final ProtocolException pe) { + super(ErrorFrame.PROTOCOL_ERROR, pe); + } + + @Override + public boolean isAbnormalClose() { + return true; + } + + @Override + public String toString() { + return "Protocol error - " + this.getLogMsg(); + } + } + + private static final class InternalError extends Http2TerminationCause { + private InternalError(final Throwable cause) { + super(ErrorFrame.INTERNAL_ERROR, cause); + } + + @Override + public boolean isAbnormalClose() { + return true; + } + + @Override + public String toString() { + return "Internal error - " + this.getLogMsg(); + } + } +} diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java index 6ea196a4d1c..af9fd3b96ba 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java @@ -98,6 +98,7 @@ import jdk.internal.net.http.common.OperationTrackers.Tracker; import jdk.internal.net.http.common.Utils.SafeExecutor; import jdk.internal.net.http.common.Utils.SafeExecutorService; +import jdk.internal.net.http.common.Utils.UseVTForSelector; import jdk.internal.net.http.websocket.BuilderImpl; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; @@ -126,6 +127,16 @@ final class HttpClientImpl extends HttpClient implements Trackable { static final long IDLE_CONNECTION_TIMEOUT_H2 = getTimeoutProp("jdk.httpclient.keepalive.timeout.h2", KEEP_ALIVE_TIMEOUT); static final long IDLE_CONNECTION_TIMEOUT_H3 = getTimeoutProp("jdk.httpclient.keepalive.timeout.h3", IDLE_CONNECTION_TIMEOUT_H2); + static final UseVTForSelector USE_VT_FOR_SELECTOR = + Utils.useVTForSelector("jdk.internal.httpclient.tcp.selector.useVirtualThreads", "default"); + private static boolean useVtForSelector() { + return switch (USE_VT_FOR_SELECTOR) { + case ALWAYS -> true; + case NEVER -> false; + default -> true; + }; + } + // Define the default factory as a static inner class // that embeds all the necessary logic to avoid // the risk of using a lambda that might keep a reference on the @@ -293,7 +304,6 @@ static CompletableFuture registerPending(PendingRequest pending, Completa if (pending.cf.isDone()) return res; var client = pending.client; - var cf = pending.cf; var id = pending.id; boolean added = client.pendingRequests.add(pending); // this may immediately remove `pending` from the set is the cf is already completed @@ -342,6 +352,7 @@ static void abortPendingRequests(HttpClientImpl client, Throwable reason) { private final boolean isDefaultExecutor; private final SSLContext sslContext; private final SSLParameters sslParams; + private final Thread selmgrThread; private final SelectorManager selmgr; private final FilterFactory filters; private final Http2ClientImpl client2; @@ -509,7 +520,11 @@ private HttpClientImpl(HttpClientBuilderImpl builder, // unlikely throw new UncheckedIOException(e); } - selmgr.setDaemon(true); + selmgrThread = useVtForSelector() + ? Thread.ofVirtual().name("HttpClient-" + id + "-SelectorManager") + .inheritInheritableThreadLocals(false).unstarted(selmgr) + : Thread.ofPlatform().name("HttpClient-" + id + "-SelectorManager") + .inheritInheritableThreadLocals(false).daemon().unstarted(selmgr); filters = new FilterFactory(); initFilters(); assert facadeRef.get() != null; @@ -528,7 +543,7 @@ void onSubmitFailure(Runnable command, Throwable failure) { private void start() { try { - selmgr.start(); + selmgrThread.start(); } catch (Throwable t) { isStarted.set(true); throw t; @@ -635,7 +650,7 @@ public void shutdownNow() { @Override public boolean awaitTermination(Duration duration) throws InterruptedException { // Implicit NPE will be thrown if duration is null - return selmgr.join(duration); + return selmgrThread.join(duration); } @Override @@ -927,7 +942,7 @@ void eventUpdated(AsyncEvent event) throws ClosedChannelException { } boolean isSelectorThread() { - return Thread.currentThread() == selmgr; + return Thread.currentThread() == selmgrThread; } AltServicesRegistry registry() { return registry; } @@ -1157,7 +1172,7 @@ private static CompletableFuture> translateSendAsyncExecFail } // Main loop for this client's selector - private static final class SelectorManager extends Thread { + private static final class SelectorManager implements Runnable { // For testing purposes we have an internal System property that // can control the frequency at which the selector manager will wake @@ -1196,9 +1211,6 @@ private static final class SelectorManager extends Thread { private final ReentrantLock lock = new ReentrantLock(); SelectorManager(HttpClientImpl ref) throws IOException { - super(null, null, - "HttpClient-" + ref.id + "-SelectorManager", - 0, false); owner = ref; debug = ref.debug; debugtimeout = ref.debugtimeout; @@ -1221,7 +1233,7 @@ IOException selectorClosedException() { } void eventUpdated(AsyncEvent e) throws ClosedChannelException { - if (Thread.currentThread() == this) { + if (owner.isSelectorThread()) { SelectionKey key = e.channel().keyFor(selector); if (key != null && key.isValid()) { SelectorAttachment sa = (SelectorAttachment) key.attachment(); @@ -1315,6 +1327,10 @@ void abort(Throwable t) { if (!inSelectorThread) selector.wakeup(); } + String getName() { + return owner.selmgrThread.getName(); + } + // Only called by the selector manager thread private void shutdown() { try { diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/HttpConnection.java b/src/java.net.http/share/classes/jdk/internal/net/http/HttpConnection.java index 0219b0960d7..115bc56f804 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/HttpConnection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/HttpConnection.java @@ -540,9 +540,7 @@ public List getSNIServerNames() { * Closes this connection due to the given cause. * @param cause the cause for which the connection is closed, may be null */ - void close(Throwable cause) { - close(); - } + abstract void close(Throwable cause); /** * {@return the underlying connection flow, if applicable} diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java b/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java index a02506cff5c..20b0338215c 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java @@ -242,6 +242,15 @@ private static Set getDisallowedRedirectHeaders() { return true; }; + public enum UseVTForSelector { ALWAYS, NEVER, DEFAULT } + + public static UseVTForSelector useVTForSelector(String property, String defval) { + String useVtForSelector = System.getProperty(property, defval); + return Stream.of(UseVTForSelector.values()) + .filter((v) -> v.name().equalsIgnoreCase(useVtForSelector)) + .findFirst().orElse(UseVTForSelector.DEFAULT); + } + public static T addSuppressed(T x, Throwable suppressed) { if (x != suppressed && suppressed != null) { var sup = x.getSuppressed(); diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/ConnectionTerminatorImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/ConnectionTerminatorImpl.java index 150d6233953..0f5aa4cb7ac 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/ConnectionTerminatorImpl.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/ConnectionTerminatorImpl.java @@ -143,8 +143,9 @@ void incomingStatelessReset() { Log.logError("{0}: stateless reset from peer ({1})", connection.logTag(), (peerIsServer ? "server" : "client")); } + var label = "quic:" + connection.uniqueId(); final SilentTermination st = forSilentTermination("stateless reset from peer (" - + (peerIsServer ? "server" : "client") + ")"); + + (peerIsServer ? "server" : "client") + ") on " + label); terminate(st); } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/IdleTimeoutManager.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/IdleTimeoutManager.java index a7469f18ed8..72ce0290038 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/IdleTimeoutManager.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/IdleTimeoutManager.java @@ -346,8 +346,10 @@ private void idleTimedOut() { } } // silently close the connection and discard all its state - final TerminationCause cause = forSilentTermination("connection idle timed out (" - + timeoutMillis + " milli seconds)"); + var type = connection.isClientConnection() ? "client" : "server"; + var label = "quic:" + connection.uniqueId(); + final TerminationCause cause = forSilentTermination(type + " connection idle timed out (" + + timeoutMillis + " milli seconds) on " + label); connection.terminator.terminate(cause); } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/PacketSpaceManager.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/PacketSpaceManager.java index 90031decdb4..487a8a186f6 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/PacketSpaceManager.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/PacketSpaceManager.java @@ -385,10 +385,10 @@ private void handleLoop0() throws IOException, QuicTransportException { } packetEmitter.checkAbort(PacketSpaceManager.this.packetNumberSpace); // Handle is called from within the executor - var nextDeadline = this.nextDeadline; + Deadline newDeadline; Deadline now = now(); - congestionController.updatePacer(now); do { + congestionController.updatePacer(now); transmitNow = false; var closed = !isOpenForTransmission(); if (closed) { @@ -534,16 +534,17 @@ private void handleLoop0() throws IOException, QuicTransportException { packetEmitter.ptoBackoffIncreased(PacketSpaceManager.this, backoff); } - // if nextDeadline is not Deadline.MAX the task will be + // if newDeadline is not Deadline.MAX the task will be // automatically rescheduled. if (debug.on()) debug.log("handle: refreshing deadline"); - nextDeadline = computeNextDeadline(); - } while(!nextDeadline.isAfter(now)); + newDeadline = computeNextDeadline(); + now = now(); + } while(!newDeadline.isAfter(now)); - logNoDeadline(nextDeadline, true); - if (Deadline.MAX.equals(nextDeadline)) return; + logNoDeadline(newDeadline, true); + if (Deadline.MAX.equals(newDeadline)) return; // we have a new deadline - packetEmitter.reschedule(this, nextDeadline); + packetEmitter.reschedule(this, newDeadline); } /** diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicBaseCongestionController.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicBaseCongestionController.java new file mode 100644 index 00000000000..7dd3276f0d3 --- /dev/null +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicBaseCongestionController.java @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.net.http.quic; + +import jdk.internal.net.http.common.Deadline; +import jdk.internal.net.http.common.Log; +import jdk.internal.net.http.common.TimeLine; +import jdk.internal.net.http.common.TimeSource; +import jdk.internal.net.http.common.Utils; +import jdk.internal.net.http.quic.frames.AckFrame; +import jdk.internal.net.http.quic.packets.QuicPacket; + +import java.util.Collection; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Implementation of the common parts of a QUIC congestion controller based on RFC 9002. + * + * This class implements the common parts of a congestion controller: + * - slow start + * - loss recovery + * - cooperation with pacer + * + * Subclasses implement congestion window growth in congestion avoidance phase. + * + * @spec https://www.rfc-editor.org/info/rfc9002 + * RFC 9002: QUIC Loss Detection and Congestion Control + */ +abstract class QuicBaseCongestionController implements QuicCongestionController { + // higher of 14720 and 2*maxDatagramSize; we use fixed maxDatagramSize + private static final int INITIAL_WINDOW = Math.max(14720, 2 * QuicConnectionImpl.DEFAULT_DATAGRAM_SIZE); + private static final int MAX_BYTES_IN_FLIGHT = Math.clamp( + Utils.getLongProperty("jdk.httpclient.quic.maxBytesInFlight", 1 << 24), + 1 << 14, 1 << 24); + final TimeLine timeSource; + final String dbgTag; + final Lock lock = new ReentrantLock(); + long congestionWindow = INITIAL_WINDOW; + int maxDatagramSize = QuicConnectionImpl.DEFAULT_DATAGRAM_SIZE; + int minimumWindow = 2 * maxDatagramSize; + long bytesInFlight; + // maximum bytes in flight seen since the last congestion event + long maxBytesInFlight; + Deadline congestionRecoveryStartTime; + long ssThresh = Long.MAX_VALUE; + + private final QuicPacer pacer; + + QuicBaseCongestionController(String dbgTag, QuicRttEstimator rttEstimator) { + this(dbgTag, TimeSource.source(), rttEstimator); + } + + // Allows to pass a custom timeline for testing + QuicBaseCongestionController(String dbgTag, TimeLine source, QuicRttEstimator rttEstimator) { + this.dbgTag = dbgTag; + this.timeSource = source; + this.pacer = new QuicPacer(rttEstimator, this); + } + + boolean inCongestionRecovery(Deadline sentTime) { + return (congestionRecoveryStartTime != null && + !sentTime.isAfter(congestionRecoveryStartTime)); + } + + abstract void onCongestionEvent(Deadline sentTime); + + private static boolean inFlight(QuicPacket packet) { + // packet is in flight if it contains anything other than a single ACK frame + // specifically, a packet containing padding is considered to be in flight. + return packet.frames().size() != 1 || + !(packet.frames().get(0) instanceof AckFrame); + } + + @Override + public boolean canSendPacket() { + lock.lock(); + try { + if (bytesInFlight >= MAX_BYTES_IN_FLIGHT) { + return false; + } + if (isCwndLimited() || isPacerLimited()) { + return false; + } + return true; + } finally { + lock.unlock(); + } + } + + @Override + public void updateMaxDatagramSize(int newSize) { + lock.lock(); + try { + if (minimumWindow != newSize * 2) { + minimumWindow = newSize * 2; + maxDatagramSize = newSize; + congestionWindow = Math.max(congestionWindow, minimumWindow); + } + } finally { + lock.unlock(); + } + } + + @Override + public void packetSent(int packetBytes) { + lock.lock(); + try { + bytesInFlight += packetBytes; + if (bytesInFlight > maxBytesInFlight) { + maxBytesInFlight = bytesInFlight; + } + pacer.packetSent(packetBytes); + } finally { + lock.unlock(); + } + } + + @Override + public void packetAcked(int packetBytes, Deadline sentTime) { + lock.lock(); + try { + long oldWindow = congestionWindow; + assert oldWindow >= minimumWindow : + "Congestion window lower than minimum: %s < %s".formatted(oldWindow, minimumWindow); + bytesInFlight -= packetBytes; + // RFC 9002 says we should not increase cwnd when application limited. + // The concept itself is poorly defined. + // Here we limit cwnd growth based on the maximum bytes in flight + // observed since the last congestion event + if (inCongestionRecovery(sentTime)) { + if (Log.quicCC() && Log.trace()) { + Log.logQuic(dbgTag + " Acked, in recovery: bytes: " + packetBytes + + ", in flight: " + bytesInFlight); + } + return; + } + boolean isAppLimited; + if (congestionWindow < ssThresh) { + isAppLimited = congestionWindow >= 2 * maxBytesInFlight; + if (!isAppLimited) { + congestionWindow += packetBytes; + } + } else { + isAppLimited = congestionAvoidanceAcked(packetBytes, sentTime); + } + if (Log.quicCC() && Log.trace()) { + if (isAppLimited) { + Log.logQuic(dbgTag + " Acked, not blocked: bytes: " + packetBytes + + ", in flight: " + bytesInFlight); + } else { + Log.logQuic(dbgTag + " Acked, increased: bytes: " + packetBytes + + ", in flight: " + bytesInFlight + + ", new cwnd:" + congestionWindow); + } + } + assert congestionWindow >= oldWindow : + "Window size decreased on ACK: %s to %s".formatted(oldWindow, congestionWindow); + } finally { + lock.unlock(); + } + } + + abstract boolean congestionAvoidanceAcked(int packetBytes, Deadline sentTime); + + @Override + public void packetLost(Collection lostPackets, Deadline sentTime, boolean persistent) { + lock.lock(); + try { + for (QuicPacket packet : lostPackets) { + if (inFlight(packet)) { + bytesInFlight -= packet.size(); + } + } + onCongestionEvent(sentTime); + if (persistent) { + congestionWindow = minimumWindow; + congestionRecoveryStartTime = null; + if (Log.quicCC()) { + Log.logQuic(dbgTag + " Persistent congestion: ssThresh: " + ssThresh + + ", in flight: " + bytesInFlight + + ", cwnd:" + congestionWindow); + } + } + assert congestionWindow >= minimumWindow : + "Congestion window lower than minimum: %s < %s".formatted(congestionWindow, minimumWindow); + } finally { + lock.unlock(); + } + } + + @Override + public void packetDiscarded(Collection discardedPackets) { + lock.lock(); + try { + for (QuicPacket packet : discardedPackets) { + if (inFlight(packet)) { + bytesInFlight -= packet.size(); + } + } + } finally { + lock.unlock(); + } + } + + @Override + public long congestionWindow() { + lock.lock(); + try { + return congestionWindow; + } finally { + lock.unlock(); + } + } + + @Override + public long initialWindow() { + lock.lock(); + try { + return Math.max(14720, 2 * maxDatagramSize); + } finally { + lock.unlock(); + } + } + + @Override + public long maxDatagramSize() { + lock.lock(); + try { + return maxDatagramSize; + } finally { + lock.unlock(); + } + } + + @Override + public boolean isSlowStart() { + lock.lock(); + try { + return congestionWindow < ssThresh; + } finally { + lock.unlock(); + } + } + + @Override + public void updatePacer(Deadline now) { + lock.lock(); + try { + pacer.updateQuota(now); + } finally { + lock.unlock(); + } + } + + @Override + public boolean isPacerLimited() { + lock.lock(); + try { + return !pacer.canSend(); + } finally { + lock.unlock(); + } + } + + @Override + public boolean isCwndLimited() { + lock.lock(); + try { + return congestionWindow - bytesInFlight < maxDatagramSize; + } finally { + lock.unlock(); + } + } + + @Override + public Deadline pacerDeadline() { + lock.lock(); + try { + return pacer.twoPacketDeadline(); + } finally { + lock.unlock(); + } + } + + @Override + public void appLimited() { + lock.lock(); + try { + pacer.appLimited(); + } finally { + lock.unlock(); + } + } +} diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java index 7ebe09e008e..d90ad1b217e 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java @@ -334,7 +334,7 @@ protected QuicConnectionImpl(final QuicVersion firstFlightVersion, this.connectionId = this.endpoint.idFactory().newConnectionId(); this.logTag = logTagFormat.formatted(labelId); this.dbgTag = dbgTag(quicInstance, logTag); - this.congestionController = new QuicRenoCongestionController(dbgTag, rttEstimator); + this.congestionController = createCongestionController(dbgTag, rttEstimator); this.originalVersion = this.quicVersion = firstFlightVersion == null ? QuicVersion.firstFlightVersion(quicInstance.getAvailableVersions()) : firstFlightVersion; @@ -366,6 +366,16 @@ protected QuicConnectionImpl(final QuicVersion firstFlightVersion, if (debug.on()) debug.log("Quic Connection Created"); } + private static QuicCongestionController createCongestionController + (String dbgTag, QuicRttEstimator rttEstimator) { + String algo = System.getProperty("jdk.internal.httpclient.quic.congestionController", "cubic"); + if (algo.equalsIgnoreCase("reno")) { + return new QuicRenoCongestionController(dbgTag, rttEstimator); + } else { + return new QuicCubicCongestionController(dbgTag, rttEstimator); + } + } + @Override public final long uniqueId() { return labelId; diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicCubicCongestionController.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicCubicCongestionController.java new file mode 100644 index 00000000000..a7a1cd0c0bc --- /dev/null +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicCubicCongestionController.java @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.net.http.quic; + +import jdk.internal.net.http.common.Deadline; +import jdk.internal.net.http.common.Log; +import jdk.internal.net.http.common.TimeLine; + +import java.util.concurrent.TimeUnit; + +/** + * Implementation of the CUBIC congestion controller + * based on RFC 9438. + * + * @spec https://www.rfc-editor.org/rfc/rfc9438.html + * RFC 9438: CUBIC for Fast and Long-Distance Networks + */ +public final class QuicCubicCongestionController extends QuicBaseCongestionController { + + public static final double BETA = 0.7; + public static final double ALPHA = 3 * (1 - BETA) / (1 + BETA); + private static final double C = 0.4; + private final QuicRttEstimator rttEstimator; + // Cubic curve inflection point, in bytes + private long wMaxBytes; + // cwnd before the most recent congestion event + private long cwndPriorBytes; + // "t" from RFC 9438 + private long timeNanos; + // "K" from RFC 9438 + private long kNanos; + // estimate for the Reno-friendly congestion window + private long wEstBytes; + // the most recent time when the congestion window was filled + private Deadline lastFullWindow; + + public QuicCubicCongestionController(String dbgTag, QuicRttEstimator rttEstimator) { + super(dbgTag, rttEstimator); + this.rttEstimator = rttEstimator; + } + + // for testing + public QuicCubicCongestionController(TimeLine source, QuicRttEstimator rttEstimator) { + super("TEST", source, rttEstimator); + this.rttEstimator = rttEstimator; + } + + @Override + public void packetSent(int packetBytes) { + lock.lock(); + try { + super.packetSent(packetBytes); + if (isCwndLimited()) { + Deadline now = timeSource.instant(); + if (lastFullWindow == null) { + lastFullWindow = now; + } else { + long timePassedNanos = Deadline.between(lastFullWindow, now).toNanos(); + if (timePassedNanos > 0) { + /* "The elapsed time MUST NOT include periods during which cwnd + has not been updated due to application-limited behavior" + "A flow is application limited if it is currently sending less + than what is allowed by the congestion window." + + We are sending asynchronously; one thread is sending data, + a separate thread is processing the acknowledgements. + We can't rely on cwnd being fully utilized when we process an ack, because + most of the time it won't be. + + Instead, we assume that if we filled the cwnd, we were not application-limited + in the last RTT (which is a pretty good approximation because of pacing), + and acknowledgements for all packets sent prior to filling the cwnd + count towards cwnd increase. + */ + long rttNanos = TimeUnit.MICROSECONDS.toNanos(rttEstimator.state().smoothedRttMicros()); + timeNanos += Math.min(timePassedNanos, rttNanos); + lastFullWindow = now; + } + } + } + } finally { + lock.unlock(); + } + } + + + boolean congestionAvoidanceAcked(int packetBytes, Deadline sentTime) { + boolean isAppLimited = sentTime.isAfter(lastFullWindow); + if (!isAppLimited) { + if (wEstBytes < cwndPriorBytes) { + wEstBytes += Math.max((long) (ALPHA * maxDatagramSize * packetBytes / congestionWindow), 1); + } else { + wEstBytes += Math.max((long)maxDatagramSize * packetBytes / congestionWindow, 1); + } + // target = Wcubic(t + RTT) + long rttNanos = TimeUnit.MICROSECONDS.toNanos(rttEstimator.state().smoothedRttMicros()); + double dblTargetBytes = wCubicBytes(timeNanos + rttNanos); + assert dblTargetBytes > 0 : "Unexpected negative target bytes"; + long targetBytes = (long) Math.min(dblTargetBytes, 1.5 * congestionWindow); + if (targetBytes > congestionWindow) { + long oldWindow = congestionWindow; + congestionWindow += Math.max((targetBytes - congestionWindow) * packetBytes / congestionWindow, 1L); + assert congestionWindow > oldWindow : + "Window size decreased: %s to %s".formatted(oldWindow, congestionWindow); + } + if (wEstBytes > congestionWindow) { + congestionWindow = wEstBytes; + } + } + return isAppLimited; + } + + // Wcubic(t) = C * (t-K [seconds])^3 + Wmax (segments) + private double wCubicBytes(long timeNanos) { + return (C * maxDatagramSize * Math.pow((timeNanos - kNanos) / 1e9, 3)) + wMaxBytes; + } + + void onCongestionEvent(Deadline sentTime) { + if (inCongestionRecovery(sentTime)) { + return; + } + if (congestionWindow < wMaxBytes) { + // fast convergence + wMaxBytes = (long) ((1 + BETA) * congestionWindow / 2); + } else { + wMaxBytes = congestionWindow; + } + cwndPriorBytes = congestionWindow; + congestionRecoveryStartTime = timeSource.instant(); + ssThresh = (long)(congestionWindow * BETA); + wEstBytes = congestionWindow = Math.max(minimumWindow, ssThresh); + maxBytesInFlight = 0; + timeNanos = 0; + // set lastFullWindow to prevent rapid timeNanos growth + lastFullWindow = congestionRecoveryStartTime; + // ((wmax_segments - cwnd_segments) / C) ^ (1/3) seconds + kNanos = (long)(Math.cbrt((wMaxBytes - congestionWindow) / C / maxDatagramSize) * 1_000_000_000); + // kNanos may be negative if we reduced the window below minimum, + // and fast convergence was used. This is acceptable. + if (Log.quicCC()) { + Log.logQuic(dbgTag + " Congestion: ssThresh: " + ssThresh + + ", in flight: " + bytesInFlight + + ", cwnd:" + congestionWindow + + ", K: " + TimeUnit.NANOSECONDS.toMillis(kNanos) + " ms"); + } + } +} diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java index d9bf5fe6dcf..b1de5ef4bfd 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java @@ -62,6 +62,7 @@ import jdk.internal.net.http.common.TimeLine; import jdk.internal.net.http.common.TimeSource; import jdk.internal.net.http.common.Utils; +import jdk.internal.net.http.common.Utils.UseVTForSelector; import jdk.internal.net.http.quic.QuicSelector.QuicNioSelector; import jdk.internal.net.http.quic.QuicSelector.QuicVirtualThreadPoller; import jdk.internal.net.http.quic.packets.QuicPacket.HeadersType; @@ -116,7 +117,6 @@ public abstract sealed class QuicEndpoint implements AutoCloseable static final boolean DGRAM_SEND_ASYNC; static final int MAX_BUFFERED_HIGH; static final int MAX_BUFFERED_LOW; - enum UseVTForSelector { ALWAYS, NEVER, DEFAULT } static final UseVTForSelector USE_VT_FOR_SELECTOR; static { // This default value is the maximum payload size of @@ -144,11 +144,8 @@ enum UseVTForSelector { ALWAYS, NEVER, DEFAULT } if (maxBufferLow >= maxBufferHigh) maxBufferLow = maxBufferHigh >> 1; MAX_BUFFERED_HIGH = maxBufferHigh; MAX_BUFFERED_LOW = maxBufferLow; - String useVtForSelector = - System.getProperty("jdk.internal.httpclient.quic.selector.useVirtualThreads", "default"); - USE_VT_FOR_SELECTOR = Stream.of(UseVTForSelector.values()) - .filter((v) -> v.name().equalsIgnoreCase(useVtForSelector)) - .findFirst().orElse(UseVTForSelector.DEFAULT); + var property = "jdk.internal.httpclient.quic.selector.useVirtualThreads"; + USE_VT_FOR_SELECTOR = Utils.useVTForSelector(property, "default"); } /** diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicPacer.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicPacer.java index 0ba7d78038b..50d8485785f 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicPacer.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicPacer.java @@ -30,10 +30,8 @@ import jdk.internal.net.http.common.Utils; import jdk.internal.util.OperatingSystem; -import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReentrantLock; /** * Implementation of pacing. diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicRenoCongestionController.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicRenoCongestionController.java index ff51aafc131..2594c00055f 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicRenoCongestionController.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicRenoCongestionController.java @@ -27,15 +27,6 @@ import jdk.internal.net.http.common.Deadline; import jdk.internal.net.http.common.Log; -import jdk.internal.net.http.common.TimeLine; -import jdk.internal.net.http.common.TimeSource; -import jdk.internal.net.http.common.Utils; -import jdk.internal.net.http.quic.frames.AckFrame; -import jdk.internal.net.http.quic.packets.QuicPacket; - -import java.util.Collection; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; /** * Implementation of QUIC congestion controller based on RFC 9002. @@ -46,38 +37,20 @@ * @spec https://www.rfc-editor.org/info/rfc9002 * RFC 9002: QUIC Loss Detection and Congestion Control */ -class QuicRenoCongestionController implements QuicCongestionController { - // higher of 14720 and 2*maxDatagramSize; we use fixed maxDatagramSize - private static final int INITIAL_WINDOW = Math.max(14720, 2 * QuicConnectionImpl.DEFAULT_DATAGRAM_SIZE); - private static final int MAX_BYTES_IN_FLIGHT = Math.clamp( - Utils.getLongProperty("jdk.httpclient.quic.maxBytesInFlight", 1 << 24), - 1 << 14, 1 << 24); - private final TimeLine timeSource; - private final String dbgTag; - private final Lock lock = new ReentrantLock(); - private long congestionWindow = INITIAL_WINDOW; - private int maxDatagramSize = QuicConnectionImpl.DEFAULT_DATAGRAM_SIZE; - private int minimumWindow = 2 * maxDatagramSize; - private long bytesInFlight; - // maximum bytes in flight seen since the last congestion event - private long maxBytesInFlight; - private Deadline congestionRecoveryStartTime; - private long ssThresh = Long.MAX_VALUE; - - private final QuicPacer pacer; - +final class QuicRenoCongestionController extends QuicBaseCongestionController { public QuicRenoCongestionController(String dbgTag, QuicRttEstimator rttEstimator) { - this.dbgTag = dbgTag; - this.timeSource = TimeSource.source(); - this.pacer = new QuicPacer(rttEstimator, this); + super(dbgTag, rttEstimator); } - private boolean inCongestionRecovery(Deadline sentTime) { - return (congestionRecoveryStartTime != null && - !sentTime.isAfter(congestionRecoveryStartTime)); + boolean congestionAvoidanceAcked(int packetBytes, Deadline sentTime) { + boolean isAppLimited = congestionWindow > maxBytesInFlight + 2L * maxDatagramSize; + if (!isAppLimited) { + congestionWindow += Math.max((long) maxDatagramSize * packetBytes / congestionWindow, 1L); + } + return isAppLimited; } - private void onCongestionEvent(Deadline sentTime) { + void onCongestionEvent(Deadline sentTime) { if (inCongestionRecovery(sentTime)) { return; } @@ -91,226 +64,4 @@ private void onCongestionEvent(Deadline sentTime) { ", cwnd:" + congestionWindow); } } - - private static boolean inFlight(QuicPacket packet) { - // packet is in flight if it contains anything other than a single ACK frame - // specifically, a packet containing padding is considered to be in flight. - return packet.frames().size() != 1 || - !(packet.frames().get(0) instanceof AckFrame); - } - - @Override - public boolean canSendPacket() { - lock.lock(); - try { - if (bytesInFlight >= MAX_BYTES_IN_FLIGHT) { - return false; - } - if (isCwndLimited() || isPacerLimited()) { - return false; - } - return true; - } finally { - lock.unlock(); - } - } - - @Override - public void updateMaxDatagramSize(int newSize) { - lock.lock(); - try { - if (minimumWindow != newSize * 2) { - minimumWindow = newSize * 2; - maxDatagramSize = newSize; - congestionWindow = Math.max(congestionWindow, minimumWindow); - } - } finally { - lock.unlock(); - } - } - - @Override - public void packetSent(int packetBytes) { - lock.lock(); - try { - bytesInFlight += packetBytes; - if (bytesInFlight > maxBytesInFlight) { - maxBytesInFlight = bytesInFlight; - } - pacer.packetSent(packetBytes); - } finally { - lock.unlock(); - } - } - - @Override - public void packetAcked(int packetBytes, Deadline sentTime) { - lock.lock(); - try { - bytesInFlight -= packetBytes; - // RFC 9002 says we should not increase cwnd when application limited. - // The concept itself is poorly defined. - // Here we limit cwnd growth based on the maximum bytes in flight - // observed since the last congestion event - if (inCongestionRecovery(sentTime)) { - if (Log.quicCC() && Log.trace()) { - Log.logQuic(dbgTag + " Acked, in recovery: bytes: " + packetBytes + - ", in flight: " + bytesInFlight); - } - return; - } - boolean isAppLimited; - if (congestionWindow < ssThresh) { - isAppLimited = congestionWindow >= 2 * maxBytesInFlight; - if (!isAppLimited) { - congestionWindow += packetBytes; - } - } else { - isAppLimited = congestionWindow > maxBytesInFlight + 2L * maxDatagramSize; - if (!isAppLimited) { - congestionWindow += Math.max((long) maxDatagramSize * packetBytes / congestionWindow, 1L); - } - } - if (Log.quicCC() && Log.trace()) { - if (isAppLimited) { - Log.logQuic(dbgTag + " Acked, not blocked: bytes: " + packetBytes + - ", in flight: " + bytesInFlight); - } else { - Log.logQuic(dbgTag + " Acked, increased: bytes: " + packetBytes + - ", in flight: " + bytesInFlight + - ", new cwnd:" + congestionWindow); - } - } - } finally { - lock.unlock(); - } - } - - @Override - public void packetLost(Collection lostPackets, Deadline sentTime, boolean persistent) { - lock.lock(); - try { - for (QuicPacket packet : lostPackets) { - if (inFlight(packet)) { - bytesInFlight -= packet.size(); - } - } - onCongestionEvent(sentTime); - if (persistent) { - congestionWindow = minimumWindow; - congestionRecoveryStartTime = null; - if (Log.quicCC()) { - Log.logQuic(dbgTag + " Persistent congestion: ssThresh: " + ssThresh + - ", in flight: " + bytesInFlight + - ", cwnd:" + congestionWindow); - } - } - } finally { - lock.unlock(); - } - } - - @Override - public void packetDiscarded(Collection discardedPackets) { - lock.lock(); - try { - for (QuicPacket packet : discardedPackets) { - if (inFlight(packet)) { - bytesInFlight -= packet.size(); - } - } - } finally { - lock.unlock(); - } - } - - @Override - public long congestionWindow() { - lock.lock(); - try { - return congestionWindow; - } finally { - lock.unlock(); - } - } - - @Override - public long initialWindow() { - lock.lock(); - try { - return Math.max(14720, 2 * maxDatagramSize); - } finally { - lock.unlock(); - } - } - - @Override - public long maxDatagramSize() { - lock.lock(); - try { - return maxDatagramSize; - } finally { - lock.unlock(); - } - } - - @Override - public boolean isSlowStart() { - lock.lock(); - try { - return congestionWindow < ssThresh; - } finally { - lock.unlock(); - } - } - - @Override - public void updatePacer(Deadline now) { - lock.lock(); - try { - pacer.updateQuota(now); - } finally { - lock.unlock(); - } - } - - @Override - public boolean isPacerLimited() { - lock.lock(); - try { - return !pacer.canSend(); - } finally { - lock.unlock(); - } - } - - @Override - public boolean isCwndLimited() { - lock.lock(); - try { - return congestionWindow - bytesInFlight < maxDatagramSize; - } finally { - lock.unlock(); - } - } - - @Override - public Deadline pacerDeadline() { - lock.lock(); - try { - return pacer.twoPacketDeadline(); - } finally { - lock.unlock(); - } - } - - @Override - public void appLimited() { - lock.lock(); - try { - pacer.appLimited(); - } finally { - lock.unlock(); - } - } } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicSelector.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicSelector.java index 9fa825459ff..02db895d27c 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicSelector.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicSelector.java @@ -45,9 +45,9 @@ import jdk.internal.net.http.common.TimeLine; import jdk.internal.net.http.common.TimeSource; import jdk.internal.net.http.common.Utils; +import jdk.internal.net.http.common.Utils.UseVTForSelector; import jdk.internal.net.http.quic.QuicEndpoint.QuicVirtualThreadedEndpoint; import jdk.internal.net.http.quic.QuicEndpoint.QuicSelectableEndpoint; -import jdk.internal.net.http.quic.QuicEndpoint.UseVTForSelector; import static jdk.internal.net.http.quic.QuicEndpoint.USE_VT_FOR_SELECTOR; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java index 2eca26de838..22ee2393a02 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java @@ -413,7 +413,18 @@ private Symbol attributeDocReference(TreePath path, DCReference ref) { } if (ref.qualifierExpression == null) { - tsym = env.enclClass.sym; + // Resolve target for unqualified reference based on declaring element + tsym = switch (path.getLeaf().getKind()) { + case PACKAGE -> env.toplevel.packge; + case MODULE -> env.toplevel.modle; + case COMPILATION_UNIT -> + // Treat unqualified reference in legacy package.html as package reference. + // Unqualified references in doc-files only need to work locally, so null is fine. + path.getCompilationUnit().getSourceFile().isNameCompatible("package", JavaFileObject.Kind.HTML) + ? env.toplevel.packge + : null; + default -> env.enclClass.sym; // Class or class member reference + }; memberName = (Name) ref.memberName; } else { // Check if qualifierExpression is a type or package, using the methods javac provides. @@ -470,8 +481,15 @@ private Symbol attributeDocReference(TreePath path, DCReference ref) { } } - if (memberName == null) + if (memberName == null) { return tsym; + } else if (tsym == null || tsym.getKind() == ElementKind.PACKAGE || tsym.getKind() == ElementKind.MODULE) { + return null; // Non-null member name in non-class context + } + + if (tsym.type.isPrimitive()) { + return null; + } final List paramTypes; if (ref.paramTypes == null) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index ad41adcc135..3f72ada94e8 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -176,7 +176,7 @@ protected Attr(Context context) { Feature.UNCONDITIONAL_PATTERN_IN_INSTANCEOF.allowedInSource(source); sourceName = source.name; useBeforeDeclarationWarning = options.isSet("useBeforeDeclarationWarning"); - captureMRefReturnType = Source.Feature.ERASE_POLY_SIG_RETURN_TYPE.allowedInSource(source); + captureMRefReturnType = Source.Feature.CAPTURE_MREF_RETURN_TYPE.allowedInSource(source); statInfo = new ResultInfo(KindSelector.NIL, Type.noType); varAssignmentInfo = new ResultInfo(KindSelector.ASG, Type.noType); @@ -377,7 +377,7 @@ private class IdentAttributer extends SimpleTreeVisitor> @Override @DefinedBy(Api.COMPILER_TREE) public Symbol visitMemberSelect(MemberSelectTree node, Env env) { Symbol site = visit(node.getExpression(), env); - if (site.kind == ERR || site.kind == ABSENT_TYP || site.kind == HIDDEN) + if (site == null || site.kind == ERR || site.kind == ABSENT_TYP || site.kind == HIDDEN) return site; Name name = (Name)node.getIdentifier(); if (site.kind == PCK) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java index eea766f57c1..07f2a742bcb 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java @@ -2810,7 +2810,12 @@ Symbol resolveQualifiedMethod(DiagnosticPosition pos, Env env, Symbol resolveQualifiedMethod(DiagnosticPosition pos, Env env, Symbol location, Type site, Name name, List argtypes, List typeargtypes) { - return resolveQualifiedMethod(new MethodResolutionContext(), pos, env, location, site, name, argtypes, typeargtypes); + try { + return resolveQualifiedMethod(new MethodResolutionContext(), pos, env, location, site, name, argtypes, typeargtypes); + } catch (CompletionFailure cf) { + chk.completionError(pos, cf); + return methodNotFound.access(name, site.tsym); + } } private Symbol resolveQualifiedMethod(MethodResolutionContext resolveContext, DiagnosticPosition pos, Env env, diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java index 0436f68b9e3..6501fd5d96c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java @@ -505,7 +505,7 @@ public JCTree getTree() { // for default DiagnosticPosition public int getStartPosition() { - return TreeInfo.getStartPos(this); + return noNoPos(TreeInfo.getStartPos(this)); } // for default DiagnosticPosition @@ -515,7 +515,14 @@ public int getPreferredPosition() { // for default DiagnosticPosition public int getEndPosition(EndPosTable endPosTable) { - return TreeInfo.getEndPos(this, endPosTable); + return noNoPos(TreeInfo.getEndPos(this, endPosTable)); + } + + private int noNoPos(int position) { + if (position == JCDiagnostic.NOPOS) { + return pos; + } + return position; } /** diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java index af823024fab..3f73bfd2296 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java @@ -651,6 +651,11 @@ public static int getEndPos(JCTree tree, EndPosTable endPosTable) { if (tree == null) return Position.NOPOS; + if (endPosTable == null) { + // fall back on limited info in the tree + return endPos(tree); + } + int mapPos = endPosTable.getEndPos(tree); if (mapPos != Position.NOPOS) return mapPos; diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/NMethod.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/NMethod.java index 91302dba0f6..f935c56b536 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/NMethod.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/NMethod.java @@ -48,7 +48,7 @@ public class NMethod extends CodeBlob { /** Offsets for different nmethod parts */ private static CIntegerField exceptionOffsetField; - private static CIntegerField deoptHandlerOffsetField; + private static CIntegerField deoptHandlerEntryOffsetField; private static CIntegerField origPCOffsetField; private static CIntegerField stubOffsetField; private static CIntField handlerTableOffsetField; @@ -86,7 +86,7 @@ private static void initialize(TypeDataBase db) { immutableDataField = type.getAddressField("_immutable_data"); immutableDataSizeField = type.getCIntegerField("_immutable_data_size"); exceptionOffsetField = type.getCIntegerField("_exception_offset"); - deoptHandlerOffsetField = type.getCIntegerField("_deopt_handler_offset"); + deoptHandlerEntryOffsetField = type.getCIntegerField("_deopt_handler_entry_offset"); origPCOffsetField = type.getCIntegerField("_orig_pc_offset"); stubOffsetField = type.getCIntegerField("_stub_offset"); scopesPCsOffsetField = type.getCIntegerField("_scopes_pcs_offset"); @@ -121,16 +121,16 @@ public Method getMethod() { public boolean isOSRMethod() { return getEntryBCI() != VM.getVM().getInvocationEntryBCI(); } /** Boundaries for different parts */ - public Address constantsBegin() { return contentBegin(); } - public Address constantsEnd() { return codeBegin(); } - public Address instsBegin() { return codeBegin(); } - public Address instsEnd() { return headerBegin().addOffsetTo(getStubOffset()); } - public Address exceptionBegin() { return headerBegin().addOffsetTo(getExceptionOffset()); } - public Address deoptHandlerBegin() { return headerBegin().addOffsetTo(getDeoptHandlerOffset()); } - public Address stubBegin() { return headerBegin().addOffsetTo(getStubOffset()); } - public Address stubEnd() { return dataBegin(); } - public Address oopsBegin() { return dataBegin(); } - public Address oopsEnd() { return dataEnd(); } + public Address constantsBegin() { return contentBegin(); } + public Address constantsEnd() { return codeBegin(); } + public Address instsBegin() { return codeBegin(); } + public Address instsEnd() { return headerBegin().addOffsetTo(getStubOffset()); } + public Address exceptionBegin() { return headerBegin().addOffsetTo(getExceptionOffset()); } + public Address deoptHandlerEntry() { return headerBegin().addOffsetTo(getDeoptHandlerEntryOffset()); } + public Address stubBegin() { return headerBegin().addOffsetTo(getStubOffset()); } + public Address stubEnd() { return dataBegin(); } + public Address oopsBegin() { return dataBegin(); } + public Address oopsEnd() { return dataEnd(); } public Address immutableDataBegin() { return immutableDataField.getValue(addr); } public Address immutableDataEnd() { return immutableDataBegin().addOffsetTo(getImmutableDataSize()); } @@ -262,7 +262,7 @@ public NMethod getOSRLink() { // Deopt // Return true is the PC is one would expect if the frame is being deopted. public boolean isDeoptPc (Address pc) { return isDeoptEntry(pc); } - public boolean isDeoptEntry (Address pc) { return pc == deoptHandlerBegin(); } + public boolean isDeoptEntry (Address pc) { return pc == deoptHandlerEntry(); } /** Tells whether frames described by this nmethod can be deoptimized. Note: native wrappers cannot be deoptimized. */ @@ -490,7 +490,7 @@ public String getName() { private int getEntryBCI() { return (int) entryBCIField .getValue(addr); } private int getExceptionOffset() { return (int) exceptionOffsetField .getValue(addr); } - private int getDeoptHandlerOffset() { return (int) deoptHandlerOffsetField .getValue(addr); } + private int getDeoptHandlerEntryOffset() { return (int) deoptHandlerEntryOffsetField .getValue(addr); } private int getStubOffset() { return (int) stubOffsetField .getValue(addr); } private int getScopesDataOffset() { return (int) scopesDataOffsetField .getValue(addr); } private int getScopesPCsOffset() { return (int) scopesPCsOffsetField .getValue(addr); } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ConstantPool.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ConstantPool.java index 563d9d3ac4a..3a4ea5546a1 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ConstantPool.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ConstantPool.java @@ -88,8 +88,11 @@ public void update(Observable o, Object data) { private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { Type type = db.lookupType("ConstantPool"); tags = type.getAddressField("_tags"); - operands = type.getAddressField("_operands"); cache = type.getAddressField("_cache"); + bsm_entries = type.getField("_bsm_entries").getOffset(); + Type bsmae_type = db.lookupType("BSMAttributeEntries"); + bsm_entries_offsets = bsmae_type.getAddressField("_offsets"); + bsm_entries_bootstrap_methods = bsmae_type.getAddressField("_bootstrap_methods"); poolHolder = new MetadataField(type.getAddressField("_pool_holder"), 0); length = new CIntField(type.getCIntegerField("_length"), 0); resolved_klasses = type.getAddressField("_resolved_klasses"); @@ -112,9 +115,11 @@ public ConstantPool(Address addr) { public boolean isConstantPool() { return true; } private static AddressField tags; - private static AddressField operands; private static AddressField cache; private static AddressField resolved_klasses; + private static long bsm_entries; // Offset in the constantpool where the Bsm_Entries are found + private static AddressField bsm_entries_offsets; + private static AddressField bsm_entries_bootstrap_methods; private static MetadataField poolHolder; private static CIntField length; // number of elements in oop private static CIntField majorVersion; @@ -130,10 +135,6 @@ public ConstantPool(Address addr) { private static int INDY_ARGV_OFFSET; public U1Array getTags() { return new U1Array(tags.getValue(getAddress())); } - public U2Array getOperands() { - Address addr = operands.getValue(getAddress()); - return VMObjectFactory.newObject(U2Array.class, addr); - } public ConstantPoolCache getCache() { Address addr = cache.getValue(getAddress()); return VMObjectFactory.newObject(ConstantPoolCache.class, addr); @@ -435,26 +436,23 @@ public int getMethodTypeIndexAt(int i) { return res; } + private U4Array getOffsets() { + Address a = getAddress().addOffsetTo(bsm_entries); + if (a == null) return null; + a = bsm_entries_offsets.getValue(a); + return VMObjectFactory.newObject(U4Array.class, a); + } + private U2Array getBootstrapMethods() { + Address a = getAddress().addOffsetTo(bsm_entries); + if (a == null) return null; + return VMObjectFactory.newObject(U2Array.class, bsm_entries_bootstrap_methods.getValue(a)); + } + public int getBootstrapMethodsCount() { - U2Array operands = getOperands(); + U4Array offsets = getOffsets(); int count = 0; - if (operands != null) { - // Operands array consists of two parts. First part is an array of 32-bit values which denote - // index of the bootstrap method data in the operands array. Note that elements of operands array are of type short. - // So each element of first part occupies two slots in the array. - // Second part is the bootstrap methods data. - // This layout allows us to get BSM count by getting the index of first BSM and dividing it by 2. - // - // The example below shows layout of operands array with 3 bootstrap methods. - // First part has 3 32-bit values indicating the index of the respective bootstrap methods in - // the operands array. - // The first BSM is at index 6. So the count in this case is 6/2=3. - // - // <-----first part----><-------second part-------> - // index: 0 2 4 6 i2 i3 - // operands: | 6 | i2 | i3 | bsm1 | bsm2 | bsm3 | - // - count = getOperandOffsetAt(operands, 0) / 2; + if (offsets != null) { + count = offsets.length(); } if (DEBUG) { System.err.println("ConstantPool.getBootstrapMethodsCount: count = " + count); @@ -463,12 +461,12 @@ public int getBootstrapMethodsCount() { } public int getBootstrapMethodArgsCount(int bsmIndex) { - U2Array operands = getOperands(); + U4Array offs = getOffsets(); + U2Array bsms = getBootstrapMethods(); if (Assert.ASSERTS_ENABLED) { - Assert.that(operands != null, "Operands is not present"); + Assert.that(offs != null && bsms != null, "BSM attribute is not present"); } - int bsmOffset = getOperandOffsetAt(operands, bsmIndex); - int argc = operands.at(bsmOffset + INDY_ARGC_OFFSET); + int argc = bsms.at(offs.at(bsmIndex) + INDY_ARGC_OFFSET); if (DEBUG) { System.err.println("ConstantPool.getBootstrapMethodArgsCount: bsm index = " + bsmIndex + ", args count = " + argc); } @@ -476,15 +474,16 @@ public int getBootstrapMethodArgsCount(int bsmIndex) { } public short[] getBootstrapMethodAt(int bsmIndex) { - U2Array operands = getOperands(); - if (operands == null) return null; // safety first - int basePos = getOperandOffsetAt(operands, bsmIndex); + U4Array offs = getOffsets(); + U2Array bsms = getBootstrapMethods(); + if (offs == null || bsms == null) return null; // safety first + int basePos = offs.at(bsmIndex); int argv = basePos + INDY_ARGV_OFFSET; - int argc = operands.at(basePos + INDY_ARGC_OFFSET); + int argc = getBootstrapMethodArgsCount(bsmIndex); int endPos = argv + argc; short[] values = new short[endPos - basePos]; for (int j = 0; j < values.length; j++) { - values[j] = operands.at(basePos+j); + values[j] = bsms.at(basePos+j); } return values; } @@ -773,8 +772,7 @@ private static int extractLowShortFromInt(int val) { // Return the offset of the requested Bootstrap Method in the operands array private int getOperandOffsetAt(U2Array operands, int bsmIndex) { - return VM.getVM().buildIntFromShorts(operands.at(bsmIndex * 2), - operands.at(bsmIndex * 2 + 1)); + return 0; } } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Frame.java index 27efb631f79..ee9e0ecdafd 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Frame.java @@ -87,7 +87,7 @@ protected void adjustForDeopt() { CodeBlob cb = VM.getVM().getCodeCache().findBlob(pc); if (cb != null && cb.isJavaMethod()) { NMethod nm = (NMethod) cb; - if (pc.equals(nm.deoptHandlerBegin())) { + if (pc.equals(nm.deoptHandlerEntry())) { if (Assert.ASSERTS_ENABLED) { Assert.that(this.getUnextendedSP() != null, "null SP in Java frame"); } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/U4Array.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/U4Array.java new file mode 100644 index 00000000000..9836614d2c9 --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/U4Array.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.jvm.hotspot.utilities; + +import sun.jvm.hotspot.debugger.Address; +import sun.jvm.hotspot.runtime.VM; +import sun.jvm.hotspot.types.Type; +import sun.jvm.hotspot.types.TypeDataBase; +import sun.jvm.hotspot.types.WrongTypeException; +import sun.jvm.hotspot.utilities.Observable; +import sun.jvm.hotspot.utilities.Observer; + +public class U4Array extends GenericArray { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + elemType = db.lookupType("u4"); + Type type = db.lookupType("Array"); + dataFieldOffset = type.getAddressField("_data").getOffset(); + } + + private static long dataFieldOffset; + protected static Type elemType; + + public U4Array(Address addr) { + super(addr, dataFieldOffset); + } + + public int at(int i) { + return (int)getIntegerAt(i); + } + + public Type getElemType() { + return elemType; + } +} diff --git a/src/jdk.httpserver/share/classes/com/sun/net/httpserver/Headers.java b/src/jdk.httpserver/share/classes/com/sun/net/httpserver/Headers.java index 84535027eb4..7d11bd42c94 100644 --- a/src/jdk.httpserver/share/classes/com/sun/net/httpserver/Headers.java +++ b/src/jdk.httpserver/share/classes/com/sun/net/httpserver/Headers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -109,29 +109,69 @@ public Headers(Map> headers) { } /** - * Normalize the key by converting to following form. - * First {@code char} upper case, rest lower case. - * key is presumed to be {@code ASCII}. + * {@return the normalized header name of the following form: the first + * character in upper-case, the rest in lower-case} + * The input header name is assumed to be encoded in ASCII. + * + * @implSpec + * This method is performance-sensitive; update with care. + * + * @param key an ASCII-encoded header name + * @throws NullPointerException on null {@code key} + * @throws IllegalArgumentException if {@code key} contains {@code \r} or {@code \n} */ - private String normalize(String key) { + private static String normalize(String key) { + + // Fast path for the empty key Objects.requireNonNull(key); - int len = key.length(); - if (len == 0) { + int l = key.length(); + if (l == 0) { + return key; + } + + // Find the first non-normalized `char` + int i = 0; + char c = key.charAt(i); + if (!(c == '\r' || c == '\n' || (c >= 'a' && c <= 'z'))) { + i++; + for (; i < l; i++) { + c = key.charAt(i); + if (c == '\r' || c == '\n' || (c >= 'A' && c <= 'Z')) { + break; + } + } + } + + // Fast path for the already normalized key + if (i == l) { return key; } - char[] b = key.toCharArray(); - if (b[0] >= 'a' && b[0] <= 'z') { - b[0] = (char)(b[0] - ('a' - 'A')); - } else if (b[0] == '\r' || b[0] == '\n') - throw new IllegalArgumentException("illegal character in key"); - - for (int i=1; i= 'A' && b[i] <= 'Z') { - b[i] = (char) (b[i] + ('a' - 'A')); - } else if (b[i] == '\r' || b[i] == '\n') - throw new IllegalArgumentException("illegal character in key"); + + // Upper-case the first `char` + char[] cs = key.toCharArray(); + int o = 'a' - 'A'; + if (i == 0) { + if (c == '\r' || c == '\n') { + throw new IllegalArgumentException("illegal character in key at index " + i); + } + if (c >= 'a' && c <= 'z') { + cs[0] = (char) (c - o); + } + i++; } - return new String(b); + + // Lower-case the secondary `char`s + for (; i < l; i++) { + c = cs[i]; + if (c >= 'A' && c <= 'Z') { + cs[i] = (char) (c + o); + } else if (c == '\r' || c == '\n') { + throw new IllegalArgumentException("illegal character in key at index " + i); + } + } + + return new String(cs); + } @Override diff --git a/src/jdk.internal.opt/share/classes/module-info.java b/src/jdk.internal.opt/share/classes/module-info.java index ba6987f1ea9..728c2de500d 100644 --- a/src/jdk.internal.opt/share/classes/module-info.java +++ b/src/jdk.internal.opt/share/classes/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -32,6 +32,7 @@ exports jdk.internal.joptsimple to jdk.jlink, jdk.jshell, + jdk.jpackage, jdk.jdeps; exports jdk.internal.opt to jdk.compiler, diff --git a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java index f2c76058434..12967972a88 100644 --- a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java +++ b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java @@ -167,6 +167,7 @@ public ExitException(int errorCode) { char[] storepass; // keystore password boolean protectedPath; // protected authentication path String storetype; // keystore type + String realStoreType; String providerName; // provider name List providers = null; // list of provider names List providerClasses = null; // list of provider classes @@ -240,6 +241,7 @@ public ExitException(int errorCode) { private boolean signerSelfSigned = false; private boolean allAliasesFound = true; private boolean hasMultipleManifests = false; + private boolean weakKeyStore = false; private Throwable chainNotValidatedReason = null; private Throwable tsaChainNotValidatedReason = null; @@ -1482,6 +1484,12 @@ private void displayMessagesAndResult(boolean isSigning) { warnings.add(rb.getString("external.file.attributes.detected")); } + if (weakKeyStore) { + warnings.add(String.format(rb.getString( + "jks.storetype.warning"), + realStoreType, keystore)); + } + if ((strict) && (!errors.isEmpty())) { result = isSigning ? rb.getString("jar.signed.with.signer.errors.") @@ -2422,6 +2430,23 @@ void loadKeyStore(String keyStoreName, boolean prompt) { is.close(); } } + + File storeFile = new File(keyStoreName); + if (storeFile.exists()) { + // Probe for real type. A JKS can be loaded as PKCS12 because + // DualFormat support, vice versa. + try { + KeyStore keyStore = KeyStore.getInstance(storeFile, storepass); + realStoreType = keyStore.getType(); + if (realStoreType.equalsIgnoreCase("JKS") + || realStoreType.equalsIgnoreCase("JCEKS")) { + weakKeyStore = true; + } + } catch (KeyStoreException e) { + // Probing not supported, therefore cannot be JKS or JCEKS. + // Skip the legacy type warning at all. + } + } } Enumeration aliases = store.aliases(); while (aliases.hasMoreElements()) { diff --git a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/resources/jarsigner.properties b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/resources/jarsigner.properties index b490d386e59..a16420daf8f 100644 --- a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/resources/jarsigner.properties +++ b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/resources/jarsigner.properties @@ -222,3 +222,5 @@ entry.1.is.signed.in.jarinputstream.but.is.not.signed.in.jarfile=Entry %s is sig jar.contains.internal.inconsistencies.result.in.different.contents.via.jarfile.and.jarinputstream=This JAR file contains internal inconsistencies that may result in different contents when reading via JarFile and JarInputStream: signature.verification.failed.on.entry.1.when.reading.via.jarinputstream=Signature verification failed on entry %s when reading via JarInputStream signature.verification.failed.on.entry.1.when.reading.via.jarfile=Signature verification failed on entry %s when reading via JarFile +jks.storetype.warning=%1$s uses outdated cryptographic algorithms and will be removed in a future release. Migrate to PKCS12 using:\n\ +keytool -importkeystore -srckeystore %2$s -destkeystore %2$s -deststoretype pkcs12 diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java index 6896b86279f..1f2c4d97dd3 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java @@ -908,12 +908,13 @@ public Content getTypeParameterLinks(HtmlLinkInfo linkInfo) { * @param refMemName the name of the member being referenced. This should * be null or empty string if no member is being referenced. * @param label the label for the external link. + * @param title the title for the link * @param style optional style for the link. * @param code true if the label should be code font. * @return the link */ public Content getCrossClassLink(TypeElement classElement, String refMemName, - Content label, HtmlStyle style, boolean code) { + Content label, String title, HtmlStyle style, boolean code) { if (classElement != null) { String className = utils.getSimpleName(classElement); PackageElement packageElement = utils.containingPackage(classElement); @@ -931,9 +932,7 @@ class (assuming that it exists). This is definitely a limitation of DocLink link = configuration.extern.getExternalLink(packageElement, pathToRoot, className + ".html", refMemName); return links.createLink(link, - (label == null) || label.isEmpty() ? defaultLabel : label, style, - resources.getText("doclet.Href_Class_Or_Interface_Title", - getLocalizedPackageName(packageElement)), true); + (label == null) || label.isEmpty() ? defaultLabel : label, style, title, true); } } return null; diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlLinkFactory.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlLinkFactory.java index 4dbbd5e172a..cb8b3dbfd40 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlLinkFactory.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlLinkFactory.java @@ -305,7 +305,7 @@ protected Content getClassLink(HtmlLinkInfo linkInfo) { } else { Content crossLink = m_writer.getCrossClassLink( typeElement, linkInfo.getFragment(), - label, linkInfo.getStyle(), true); + label, linkInfo.getTitle(), linkInfo.getStyle(), true); if (crossLink != null) { link.add(crossLink); addSuperscript(link, flags, null, typeElement, previewTarget, restrictedTarget); @@ -361,7 +361,7 @@ private Content getSuperscript(DocPath fileName, TypeElement typeElement, HtmlId if (fileName != null) { return m_writer.links.createLink(fileName.fragment(id.name()), label); } else if (typeElement != null) { - return (m_writer.getCrossClassLink(typeElement, id.name(), label, null, false)); + return (m_writer.getCrossClassLink(typeElement, id.name(), label, null, null, false)); } else { return label; } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Links.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Links.java index 0af29135654..285ab260e0e 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Links.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Links.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -197,7 +197,7 @@ public HtmlTree createLink(DocLink link, Content label, HtmlStyle style, if (style != null) { l.setStyle(style); } - if (title != null && title.length() != 0) { + if (title != null && !title.isEmpty()) { l.put(HtmlAttr.TITLE, title); } if (isExternal) { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties index df3c2fc3a53..4366295477b 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties @@ -51,7 +51,6 @@ doclet.Href_Annotation_Interface_Title=annotation interface in {0} doclet.Href_Enum_Title=enum in {0} doclet.Href_Enum_Class_Title=enum class in {0} doclet.Href_Type_Param_Title=type parameter in {0} -doclet.Href_Class_Or_Interface_Title=class or interface in {0} doclet.Summary=Summary: doclet.Detail=Detail: doclet.Module_Sub_Nav=Module: diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_de.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_de.properties index 2669aa9bdc0..4cbb4b97774 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_de.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_de.properties @@ -51,7 +51,6 @@ doclet.Href_Annotation_Interface_Title=Annotationsschnittstelle in {0} doclet.Href_Enum_Title=Enum in {0} doclet.Href_Enum_Class_Title=Enum-Klasse in {0} doclet.Href_Type_Param_Title=Typparameter in {0} -doclet.Href_Class_Or_Interface_Title=Klasse oder Schnittstelle in {0} doclet.Summary=Übersicht: doclet.Detail=Details: doclet.Module_Sub_Nav=Modul: diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_ja.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_ja.properties index 1694dc980bc..2151b3f4a2e 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_ja.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_ja.properties @@ -51,7 +51,6 @@ doclet.Href_Annotation_Interface_Title={0}内の注釈インタフェース doclet.Href_Enum_Title={0}内の列挙型 doclet.Href_Enum_Class_Title={0}の列挙クラス doclet.Href_Type_Param_Title={0}内の型パラメータ -doclet.Href_Class_Or_Interface_Title={0}内のクラスまたはインタフェース doclet.Summary=概要: doclet.Detail=詳細: doclet.Module_Sub_Nav=モジュール: diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_zh_CN.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_zh_CN.properties index 8881171351e..66620d158bb 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_zh_CN.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_zh_CN.properties @@ -51,7 +51,6 @@ doclet.Href_Annotation_Interface_Title={0} 中的批注接口 doclet.Href_Enum_Title={0}中的枚举 doclet.Href_Enum_Class_Title={0} 中的枚举类 doclet.Href_Type_Param_Title={0}中的类型参数 -doclet.Href_Class_Or_Interface_Title={0}中的类或接口 doclet.Summary=概要: doclet.Detail=详细资料: doclet.Module_Sub_Nav=模块: diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/LinkTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/LinkTaglet.java index 914f70ced47..62b003afd96 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/LinkTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/LinkTaglet.java @@ -50,6 +50,7 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocLink; import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable; import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.HtmlId; import jdk.javadoc.internal.html.HtmlTree; import jdk.javadoc.internal.html.Text; @@ -159,6 +160,10 @@ Content linkSeeReferenceOutput(Element holder, Optional.of(refSignature)); } refFragment = refFragment.substring(1); + if (ref == null && refSignature.startsWith("##")) { + // Unqualified local anchor link in doc-file + return htmlWriter.links.createLink(HtmlId.of(refFragment), labelContent); + } } if (refClass == null) { ModuleElement refModule = ch.getReferencedModule(ref); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclint/Checker.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclint/Checker.java index 3669c800a8e..43405c3ebb0 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclint/Checker.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclint/Checker.java @@ -1004,12 +1004,15 @@ public Void visitProvides(ProvidesTree tree, Void ignore) { @Override @DefinedBy(Api.COMPILER_TREE) public Void visitReference(ReferenceTree tree, Void ignore) { - Element e = env.trees.getElement(getCurrentPath()); - if (e == null) { - reportBadReference(tree); - } else if ((inLink || inSee) - && e.getKind() == ElementKind.CLASS && e.asType().getKind() != TypeKind.DECLARED) { - reportBadReference(tree); + // Exclude same-file anchor links from reference checks + if (!tree.getSignature().startsWith("##")) { + Element e = env.trees.getElement(getCurrentPath()); + if (e == null) { + reportBadReference(tree); + } else if ((inLink || inSee) + && e.getKind() == ElementKind.CLASS && e.asType().getKind() != TypeKind.DECLARED) { + reportBadReference(tree); + } } return super.visitReference(tree, ignore); } diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.c b/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.c index bfeffe85678..491182a583f 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -137,6 +137,9 @@ typedef struct { static DeferredEventModeList deferredEventModes; +static ThreadNode * +insertThread(JNIEnv *env, ThreadList *list, jthread thread); + /* Get the state of the thread direct from JVMTI */ static jvmtiError threadState(jthread thread, jint *pstate) @@ -277,6 +280,35 @@ findThread(ThreadList *list, jthread thread) return node; } +/* Creates a new ThreadNode for a vthread if one is needed. */ +static ThreadNode * +createVThreadNodeIfNeeded(jthread thread) { + ThreadNode *node = findThread(&otherThreads, thread); + if (node != NULL) { + //tty_message("createVThreadNodeIfNeeded: thread is on otherThreads"); + // Don't create a new node if it is on otherThreads list. Also don't return + // the existing node because if it is on otherThreads, it is not running. + return NULL; + } + + // See if we have a vthread that is alive. If we do, create a ThreadNode + // for it. Otherwise just return NULL. + jint vthread_state = 0; + jvmtiError error = threadState(thread, &vthread_state); + if (error != JVMTI_ERROR_NONE) { + EXIT_ERROR(error, "getting vthread state"); + } + if ((vthread_state & JVMTI_THREAD_STATE_ALIVE) == 0) { + return NULL; // Don't create a new ThreadNode if thread is not alive + } + node = insertThread(getEnv(), &runningVThreads, thread); + if (node->suspendCount > 0 && !node->suspendOnStart) { + node->toBeResumed = JNI_TRUE; + } + + return node; +} + /* Search for a running thread, including vthreads. */ static ThreadNode * findRunningThread(jthread thread) @@ -284,6 +316,16 @@ findRunningThread(jthread thread) ThreadNode *node; if (isVThread(thread)) { node = findThread(&runningVThreads, thread); + if (node == NULL && !gdata->includeVThreads) { + // Unlike platform threads, we don't always have a ThreadNode for all vthreads. + // They can be freed if not holding on to any relevant state info. It's also + // possible that the vthread was created before the debugger attached. Also + // in the future we won't be enabling VIRTUAL_THREAD_START events in some + // cases, which means we won't be creating a ThreadNode when the vthread is + // created. If for any of the above reasons the ThreadNode lookup failed, + // we'll create one for the vthread now, but only if really needed. + node = createVThreadNodeIfNeeded(thread); + } } else { node = findThread(&runningThreads, thread); } @@ -370,6 +412,23 @@ insertThread(JNIEnv *env, ThreadList *list, jthread thread) EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"thread table entry"); return NULL; } + +#ifdef DEBUG_THREADNAME + { + /* Set the thread name */ + jvmtiThreadInfo info; + jvmtiError error; + + memset(&info, 0, sizeof(info)); + error = JVMTI_FUNC_PTR(gdata->jvmti,GetThreadInfo) + (gdata->jvmti, node->thread, &info); + if (info.name != NULL) { + strncpy(node->name, info.name, sizeof(node->name) - 1); + jvmtiDeallocate(info.name); + } + } +#endif + if (!is_vthread) { if (threadControl_isDebugThread(node->thread)) { /* Remember if it is a debug thread */ @@ -418,22 +477,6 @@ insertThread(JNIEnv *env, ThreadList *list, jthread thread) node->eventBag = eventBag; addNode(list, node); -#ifdef DEBUG_THREADNAME - { - /* Set the thread name */ - jvmtiThreadInfo info; - jvmtiError error; - - memset(&info, 0, sizeof(info)); - error = JVMTI_FUNC_PTR(gdata->jvmti,GetThreadInfo) - (gdata->jvmti, node->thread, &info); - if (info.name != NULL) { - strncpy(node->name, info.name, sizeof(node->name) - 1); - jvmtiDeallocate(info.name); - } - } -#endif - /* Set thread local storage for quick thread -> node access. * Threads that are not yet started do not allow setting of TLS. These * threads go on the otherThreads list and have their TLS set @@ -491,8 +534,7 @@ removeResumed(JNIEnv *env, ThreadList *list) static void removeVThreads(JNIEnv *env) { - ThreadList *list = &runningVThreads; - ThreadNode *node = list->first; + ThreadNode *node = runningVThreads.first; while (node != NULL) { ThreadNode *temp = node->next; removeNode(node); @@ -501,6 +543,94 @@ removeVThreads(JNIEnv *env) } } +/* + * Free (garbage collect) a vthread node if it is unused. This can only be called when + * locks are held so we don't need to worry about other threads changing the state + * of the ThreadNode while we are looking at it. We also need to make sure the + * ThreadNode is not in use for operations like single stepping or invoking. + */ +static void +freeUnusedVThreadNode(JNIEnv *env, ThreadNode* node) +{ + if (gdata->includeVThreads) { + return; + } + + /* + * node->suspendCount requires special handling to see if it triggers having + * to keep the node around. It's possible for it to be 0 yet we still need to + * keep the node around. Also, it's possbile for it to be non-zero yet we + * don't need to keep the node around. More details in the comments below. + */ + if (node->suspendCount == 0) { + /* + * Normally a suspendCount of 0 does not result in having to keep the + * node. However, if the suspendAllCount is not 0, then we do. Otherwise + * when the node is recreated later it will end up assuming suspendAllCount + * as its suspendCount rather than 0, which would be incorrect. This + * mismatch in suspend counts happens when there is a suspendAll in place, + * and ThreadReference.resume() is used to decrement the thread's + * suspendCount to 0. + */ + if (suspendAllCount > 0) { + return; + } + } else { + /* + * Although at first it might seem that a non-zero suspendCount would require + * keeping the node, we don't have to if node->suspendCount == suspendAllCount, + * because when the node is recreated it will get suspendAllCount assigned to it. + * So we only worry about keeping the node around if the two counts are not equal. + */ + if (node->suspendCount != suspendAllCount) { + return; + } + + } + + // All of the following conditions must be met to free this node. Note + // suspendCount checks were already made above, so are not included below. If + // we got here, then suspendCount checks passed w.r.t. being able to free the node. + // Also note we don't need to check node->toBeResumed. If it is set, that implies + // node->suspendCount > 0, and that will trigger toBeResumed getting set when + // the ThreadNode is recreated. See createVThreadNodeIfNeeded(). + if (!node->suspendOnStart && + node->current_ei == 0 && + node->cleInfo.ei == 0 && + !node->currentInvoke.pending && + !node->currentInvoke.started && + !node->currentInvoke.available && + !node->currentStep.pending && + node->instructionStepMode != JVMTI_ENABLE && + !node->pendingInterrupt && + !node->popFrameEvent && + !node->popFrameProceed && + !node->popFrameThread && + node->pendingStop == NULL) + { + removeNode(node); + clearThread(env, node); + } +} + +/* + * Free (garbage collect) vthread nodes if unused. See freeUnusedVThreadNode() above. + */ +static void +freeUnusedVThreadNodes(JNIEnv *env) +{ + if (gdata->includeVThreads) { + return; + } + + ThreadNode *node = runningVThreads.first; + while (node != NULL) { + ThreadNode *temp = node->next; + freeUnusedVThreadNode(env, node); + node = temp; + } +} + static void moveNode(ThreadList *source, ThreadList *dest, ThreadNode *node) { @@ -1139,6 +1269,7 @@ commonResumeList(JNIEnv *env) /* * This function must be called after preSuspend and before postSuspend. + * Only called for platform threads. */ static jvmtiError commonSuspendList(JNIEnv *env, jint initCount, jthread *initList) @@ -1160,15 +1291,17 @@ commonSuspendList(JNIEnv *env, jint initCount, jthread *initList) */ for (i = 0; i < initCount; i++) { ThreadNode *node; + jthread thread = initList[i]; + JDI_ASSERT(!isVThread(thread)); /* * If the thread is not between its start and end events, we should * still suspend it. To keep track of things, add the thread * to a separate list of threads so that we'll resume it later. */ - node = findThread(&runningThreads, initList[i]); + node = findThread(&runningThreads, thread); if (node == NULL) { - node = insertThread(env, &otherThreads, initList[i]); + node = insertThread(env, &otherThreads, thread); } if (node->isDebugThread) { @@ -1187,7 +1320,7 @@ commonSuspendList(JNIEnv *env, jint initCount, jthread *initList) if (node->suspendCount == 0) { /* thread is not suspended yet so put it on the request list */ - reqList[reqCnt++] = initList[i]; + reqList[reqCnt++] = thread; } } @@ -1255,10 +1388,16 @@ commonResume(jthread thread) ThreadNode *node; /* - * The thread is normally between its start and end events, but if - * not, check the auxiliary list used by threadControl_suspendThread. + * We need to call findRunningThread(thread) here instead of just calling + * findThread(NULL, thread) because it's possible that there is not currently a + * ThreadNode for the thread, and findRunningThread() will create one in that case. */ - node = findThread(NULL, thread); + node = findRunningThread(thread); + if (node == NULL) { + // The thread is normally between its start and end events, but if not, + // check the auxiliary list used by commonSuspend() and commonSuspendList(). + node = findThread(&otherThreads, thread); + } #if 0 tty_message("commonResume: node(%p) suspendCount(%d) %s", node, node->suspendCount, node->name); #endif @@ -1336,24 +1475,9 @@ threadControl_suspendCount(jthread thread, jint *count) } else { /* * If the node is in neither list, the debugger never suspended - * this thread, so the suspend count is 0, unless it is a vthread. + * this thread, so the suspend count is 0. */ - if (isVThread(thread)) { - jint vthread_state = 0; - jvmtiError error = threadState(thread, &vthread_state); - if (error != JVMTI_ERROR_NONE) { - EXIT_ERROR(error, "getting vthread state"); - } - if (vthread_state == 0) { - // If state == 0, then this is a new vthread that has not been started yet. - *count = 0; - } else { - // This is a started vthread that we are not tracking. Use suspendAllCount. - *count = suspendAllCount; - } - } else { - *count = 0; - } + *count = 0; } debugMonitorExit(threadLock); @@ -1406,9 +1530,6 @@ threadControl_suspendAll(void) { jvmtiError error; JNIEnv *env; -#if 0 - tty_message("threadControl_suspendAll: suspendAllCount(%d)", suspendAllCount); -#endif env = getEnv(); @@ -1416,6 +1537,8 @@ threadControl_suspendAll(void) preSuspend(); + //tty_message("threadControl_suspendAll: suspendAllCount(%d)", suspendAllCount); + /* * Get a list of all threads and suspend them. */ @@ -1425,6 +1548,11 @@ threadControl_suspendAll(void) jint count; if (gdata->vthreadsSupported) { + // Now is a good time to garbage collect vthread nodes. We want to do it before + // any suspendAll because it will prevent the suspended nodes from being freed. + if (!gdata->includeVThreads) { + freeUnusedVThreadNodes(env); + } /* Tell JVMTI to suspend all virtual threads. */ if (suspendAllCount == 0) { error = JVMTI_FUNC_PTR(gdata->jvmti, SuspendAllVirtualThreads) @@ -1528,9 +1656,6 @@ threadControl_resumeAll(void) { jvmtiError error; JNIEnv *env; -#if 0 - tty_message("threadControl_resumeAll: suspendAllCount(%d)", suspendAllCount); -#endif env = getEnv(); @@ -1539,6 +1664,8 @@ threadControl_resumeAll(void) eventHandler_lock(); /* for proper lock order */ debugMonitorEnter(threadLock); + //tty_message("threadControl_resumeAll: suspendAllCount(%d)", suspendAllCount); + if (gdata->vthreadsSupported) { if (suspendAllCount == 1) { jint excludeCnt = 0; @@ -2092,7 +2219,7 @@ threadControl_onEventHandlerEntry(jbyte sessionID, EventInfo *evinfo, jobject cu processDeferredEventModes(env, thread, node); } if (ei == EI_THREAD_END) { - // If the node was previously freed, then it was just recreated and we need + // If the node was previously freed and was just now recreated, we need // to mark it as started. node->isStarted = JNI_TRUE; } @@ -2148,17 +2275,30 @@ threadControl_onEventHandlerExit(EventIndex ei, jthread thread, log_debugee_location("threadControl_onEventHandlerExit()", thread, NULL, 0); - if (ei == EI_THREAD_END) { + if (ei == EI_THREAD_END || ei == EI_THREAD_START) { eventHandler_lock(); /* for proper lock order - see removeThread() call below */ debugMonitorEnter(threadLock); node = findRunningThread(thread); if (node == NULL) { EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"thread list corrupted"); } - removeThread(env, node); // Grabs handlerLock, thus the reason for first grabbing it above. - node = NULL; // We exiting the threadLock. No longer safe to access. - debugMonitorExit(threadLock); - eventHandler_unlock(); + // Remove nodes for exiting threads, but also remove nodes for vthreads + // that are just starting to help prevent their accumulation. There can't + // possibly be any state related information in the node at this point. A new + // one will be created later if necessary, such as when a new event arrives. + if (ei == EI_THREAD_END || (node->is_vthread && !gdata->includeVThreads)) { + removeThread(env, node); // Grabs handlerLock, thus the reason for first grabbing it above. + node = NULL; // Node has been freed. No longer safe to access. + } else { + // EI_THREAD_START for a platform thread, or we are tracking vthreads. + // In either case we are not removing the thread node. + // Just clear these two fields. Others are not set yet. Also no need to + // worry about pending tasks like we do below for other event types. + node->eventBag = eventBag; + node->current_ei = 0; + } + debugMonitorExit(threadLock); + eventHandler_unlock(); } else { debugMonitorEnter(threadLock); node = findRunningThread(thread); @@ -2173,7 +2313,7 @@ threadControl_onEventHandlerExit(EventIndex ei, jthread thread, node->pendingStop = NULL; node->eventBag = eventBag; node->current_ei = 0; - node = NULL; // We exiting the threadLock. No longer safe to access. + node = NULL; // We're exiting the threadLock. No longer safe to access. // doPendingTasks() may do an upcall to java, and we don't want to hold any // locks when doing that. Thus we got all our node updates done first // and can now exit the threadLock. @@ -2189,6 +2329,8 @@ threadControl_onEventHandlerExit(EventIndex ei, jthread thread, // until now. Otherwise there are complaints when JNI IsVirtualThread is called. JNI_FUNC_PTR(env,Throw)(env, currentException); } + // Note: Threads that were just created or are about to die don't have pending + // tasks, which is why this is the only code path where we call doPendingTasks(). doPendingTasks(env, thread, pendingInterrupt, pendingStop); if (pendingStop != NULL) { tossGlobalRef(env, &pendingStop); @@ -2207,6 +2349,7 @@ threadControl_applicationThreadStatus(jthread thread, log_debugee_location("threadControl_applicationThreadStatus()", thread, NULL, 0); + eventHandler_lock(); // Needed to safely access HANDLING_EVENT(node) debugMonitorEnter(threadLock); error = threadState(thread, &state); @@ -2229,6 +2372,7 @@ threadControl_applicationThreadStatus(jthread thread, } debugMonitorExit(threadLock); + eventHandler_unlock(); return error; } @@ -2334,6 +2478,7 @@ threadControl_stop(jthread thread, jobject throwable) log_debugee_location("threadControl_stop()", thread, NULL, 0); + eventHandler_lock(); // Needed to safely access HANDLING_EVENT(node) debugMonitorEnter(threadLock); node = findThread(&runningThreads, thread); @@ -2351,6 +2496,7 @@ threadControl_stop(jthread thread, jobject throwable) } debugMonitorExit(threadLock); + eventHandler_unlock(); return error; } @@ -2579,7 +2725,7 @@ threadControl_dumpAllThreads() tty_message("suspendAllCount: %d", suspendAllCount); tty_message("Dumping runningThreads:"); dumpThreadList(&runningThreads); - tty_message("\nDumping runningVThreads:"); + tty_message("\nDumping runningVThreads(numRunningVThreads=%d):", numRunningVThreads); dumpThreadList(&runningVThreads); tty_message("\nDumping otherThreads:"); dumpThreadList(&otherThreads); @@ -2650,9 +2796,11 @@ dumpThread(ThreadNode *node) { // kept small so it doesn't generate too much output. tty_message("\tsuspendCount: %d", node->suspendCount); #if 0 + tty_message("\ttoBeResumed: %d", node->toBeResumed); + tty_message("\tisStarted: %d", node->isStarted); + tty_message("\tsuspendOnStart: %d", node->suspendOnStart); tty_message("\tsuspendAllCount: %d", suspendAllCount); tty_message("\tthreadState: 0x%x", getThreadState(node)); - tty_message("\ttoBeResumed: %d", node->toBeResumed); tty_message("\tis_vthread: %d", node->is_vthread); tty_message("\tpendingInterrupt: %d", node->pendingInterrupt); tty_message("\tcurrentInvoke.pending: %d", node->currentInvoke.pending); diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ReleaseInfoPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ReleaseInfoPlugin.java index b4301ea8e45..25789b4d172 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ReleaseInfoPlugin.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ReleaseInfoPlugin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -25,11 +25,13 @@ package jdk.tools.jlink.internal.plugins; import java.io.ByteArrayOutputStream; -import java.io.FileInputStream; import java.io.IOException; import java.io.PrintWriter; +import java.io.Reader; import java.io.UncheckedIOException; import java.lang.module.ModuleDescriptor; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.EnumSet; import java.util.HashMap; import java.util.Map; @@ -107,8 +109,8 @@ public void configure(Map config) { default: { // --release-info Properties props = new Properties(); - try (FileInputStream fis = new FileInputStream(operation)) { - props.load(fis); + try (Reader reader = Files.newBufferedReader(Path.of(operation))) { + props.load(reader); // Use reader API so as to read in as UTF-8 } catch (IOException exp) { throw new UncheckedIOException(exp); } diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties index a4b780a15c3..e9be0b4e587 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. +# 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 @@ -39,6 +39,7 @@ release-info.argument=|add:=:=:...|del: option is to load release properties from the supplied file.\n\ +\ The specified file is expected to be encoded in UTF-8.\n\ add: is to add properties to the 'release' file.\n\ Any number of = pairs can be passed.\n\ del: is to delete the list of keys in release file. @@ -46,7 +47,8 @@ del: is to delete the list of keys in release file. release-info.usage=\ \ --release-info |add:=:=:...|del:\n\ \ option is to load release properties from\n\ -\ the supplied file.\n\ +\ the supplied file. The specified file is expected\n\ +\ to be encoded in UTF-8.\n\ \ add: is to add properties to the 'release' file.\n\ \ Any number of = pairs can be passed.\n\ \ del: is to delete the list of keys in release file. diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppBundler.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppBundler.java deleted file mode 100644 index fe8d6bcf34f..00000000000 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppBundler.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.util.Optional; - -public class LinuxAppBundler extends AppImageBundler { - public LinuxAppBundler() { - setAppImageSupplier((params, output) -> { - // Order is important! - var app = LinuxFromParams.APPLICATION.fetchFrom(params); - var env = BuildEnvFromParams.BUILD_ENV.fetchFrom(params); - LinuxPackagingPipeline.build(Optional.empty()) - .excludeDirFromCopying(output.getParent()) - .create().execute(BuildEnv.withAppImageDir(env, output), app); - }); - } -} diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxBundlingEnvironment.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxBundlingEnvironment.java new file mode 100644 index 00000000000..cfd8ab391bb --- /dev/null +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxBundlingEnvironment.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal; + +import static java.util.stream.Collectors.toMap; +import static jdk.jpackage.internal.LinuxFromOptions.createLinuxApplication; +import static jdk.jpackage.internal.LinuxPackagingPipeline.APPLICATION_LAYOUT; +import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_LINUX_APP_IMAGE; +import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_LINUX_DEB; +import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_LINUX_RPM; + +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; +import jdk.jpackage.internal.cli.Options; +import jdk.jpackage.internal.cli.StandardBundlingOperation; +import jdk.jpackage.internal.model.BundlingOperationDescriptor; +import jdk.jpackage.internal.model.LinuxPackage; +import jdk.jpackage.internal.model.PackageType; +import jdk.jpackage.internal.util.Result; + +public class LinuxBundlingEnvironment extends DefaultBundlingEnvironment { + + public LinuxBundlingEnvironment() { + super(build() + .defaultOperation(() -> { + return LazyLoad.SYS_ENV.value().map(LinuxSystemEnvironment::nativePackageType).map(DESCRIPTORS::get); + }) + .bundler(CREATE_LINUX_APP_IMAGE, LinuxBundlingEnvironment::createAppImage) + .bundler(CREATE_LINUX_DEB, LazyLoad::debSysEnv, LinuxBundlingEnvironment::createDebPackage) + .bundler(CREATE_LINUX_RPM, LazyLoad::rpmSysEnv, LinuxBundlingEnvironment::createRpmPackage)); + } + + private static void createDebPackage(Options options, LinuxDebSystemEnvironment sysEnv) { + + createNativePackage(options, + LinuxFromOptions::createLinuxDebPackage, + buildEnv()::create, + LinuxBundlingEnvironment::buildPipeline, + (env, pkg, outputDir) -> { + return new LinuxDebPackager(env, pkg, outputDir, sysEnv); + }); + } + + private static void createRpmPackage(Options options, LinuxRpmSystemEnvironment sysEnv) { + + createNativePackage(options, + LinuxFromOptions::createLinuxRpmPackage, + buildEnv()::create, + LinuxBundlingEnvironment::buildPipeline, + (env, pkg, outputDir) -> { + return new LinuxRpmPackager(env, pkg, outputDir, sysEnv); + }); + } + + private static void createAppImage(Options options) { + + final var app = createLinuxApplication(options); + + createApplicationImage(options, app, LinuxPackagingPipeline.build(Optional.empty())); + } + + private static PackagingPipeline.Builder buildPipeline(LinuxPackage pkg) { + return LinuxPackagingPipeline.build(Optional.of(pkg)); + } + + private static BuildEnvFromOptions buildEnv() { + return new BuildEnvFromOptions().predefinedAppImageLayout(APPLICATION_LAYOUT); + } + + private static final class LazyLoad { + + static Result debSysEnv() { + return DEB_SYS_ENV; + } + + static Result rpmSysEnv() { + return RPM_SYS_ENV; + } + + private static final Result SYS_ENV = LinuxSystemEnvironment.create(); + + private static final Result DEB_SYS_ENV = LinuxDebSystemEnvironment.create(SYS_ENV); + + private static final Result RPM_SYS_ENV = LinuxRpmSystemEnvironment.create(SYS_ENV); + } + + private static final Map DESCRIPTORS = Stream.of( + CREATE_LINUX_DEB, + CREATE_LINUX_RPM + ).collect(toMap(StandardBundlingOperation::packageType, StandardBundlingOperation::descriptor)); +} diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java deleted file mode 100644 index 76a08519b48..00000000000 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.nio.file.Path; -import java.util.Map; -import java.util.Optional; -import jdk.jpackage.internal.model.LinuxDebPackage; -import jdk.jpackage.internal.model.PackagerException; -import jdk.jpackage.internal.model.StandardPackageType; -import jdk.jpackage.internal.util.Result; - -public class LinuxDebBundler extends LinuxPackageBundler { - - public LinuxDebBundler() { - super(LinuxFromParams.DEB_PACKAGE); - } - - @Override - public String getName() { - return I18N.getString("deb.bundler.name"); - } - - @Override - public String getID() { - return "deb"; - } - - @Override - public Path execute(Map params, Path outputParentDir) throws PackagerException { - - var pkg = LinuxFromParams.DEB_PACKAGE.fetchFrom(params); - - return Packager.build().outputDir(outputParentDir) - .pkg(pkg) - .env(BuildEnvFromParams.BUILD_ENV.fetchFrom(params)) - .pipelineBuilderMutatorFactory((env, _, outputDir) -> { - return new LinuxDebPackager(env, pkg, outputDir, sysEnv.orElseThrow()); - }).execute(LinuxPackagingPipeline.build(Optional.of(pkg))); - } - - @Override - protected Result sysEnv() { - return sysEnv; - } - - @Override - public boolean isDefault() { - return sysEnv.value() - .map(LinuxSystemEnvironment::nativePackageType) - .map(StandardPackageType.LINUX_DEB::equals) - .orElse(false); - } - - private final Result sysEnv = LinuxDebSystemEnvironment.create(SYS_ENV); -} diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebPackageBuilder.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebPackageBuilder.java index 50e371d5c76..d7b6559abe1 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebPackageBuilder.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebPackageBuilder.java @@ -26,7 +26,6 @@ import java.util.Objects; import java.util.Optional; -import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.LinuxDebPackage; import jdk.jpackage.internal.model.LinuxDebPackageMixin; @@ -36,7 +35,7 @@ final class LinuxDebPackageBuilder { this.pkgBuilder = Objects.requireNonNull(pkgBuilder); } - LinuxDebPackage create() throws ConfigException { + LinuxDebPackage create() { if (pkgBuilder.category().isEmpty()) { pkgBuilder.category(DEFAULTS.category()); } diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.java new file mode 100644 index 00000000000..799c92ce2e1 --- /dev/null +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +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.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; +import static jdk.jpackage.internal.cli.StandardOption.LINUX_PACKAGE_DEPENDENCIES; +import static jdk.jpackage.internal.cli.StandardOption.LINUX_PACKAGE_NAME; +import static jdk.jpackage.internal.cli.StandardOption.LINUX_RELEASE; +import static jdk.jpackage.internal.cli.StandardOption.LINUX_RPM_LICENSE_TYPE; +import static jdk.jpackage.internal.cli.StandardOption.LINUX_SHORTCUT_HINT; +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.Launcher; +import jdk.jpackage.internal.model.LinuxApplication; +import jdk.jpackage.internal.model.LinuxDebPackage; +import jdk.jpackage.internal.model.LinuxLauncher; +import jdk.jpackage.internal.model.LinuxLauncherMixin; +import jdk.jpackage.internal.model.LinuxRpmPackage; +import jdk.jpackage.internal.model.StandardPackageType; + +final class LinuxFromOptions { + + static LinuxApplication createLinuxApplication(Options options) { + + final var launcherFromOptions = new LauncherFromOptions().faWithDefaultDescription(); + + final var appBuilder = buildApplicationBuilder().create(options, launcherOptions -> { + + final var launcher = launcherFromOptions.create(launcherOptions); + + final var shortcut = LINUX_SHORTCUT_HINT.findIn(launcherOptions); + + return LinuxLauncher.create(launcher, new LinuxLauncherMixin.Stub(shortcut)); + + }, (LinuxLauncher linuxLauncher, Launcher launcher) -> { + return LinuxLauncher.create(launcher, linuxLauncher); + }, APPLICATION_LAYOUT); + + appBuilder.launchers().map(LinuxPackagingPipeline::normalizeShortcuts).ifPresent(appBuilder::launchers); + + return LinuxApplication.create(appBuilder.create()); + } + + static LinuxRpmPackage createLinuxRpmPackage(Options options) { + + final var superPkgBuilder = createLinuxPackageBuilder(options, LINUX_RPM); + + final var pkgBuilder = new LinuxRpmPackageBuilder(superPkgBuilder); + + LINUX_RPM_LICENSE_TYPE.ifPresentIn(options, pkgBuilder::licenseType); + + return pkgBuilder.create(); + } + + static LinuxDebPackage createLinuxDebPackage(Options options) { + + final var superPkgBuilder = createLinuxPackageBuilder(options, LINUX_DEB); + + final var pkgBuilder = new LinuxDebPackageBuilder(superPkgBuilder); + + LINUX_DEB_MAINTAINER_EMAIL.ifPresentIn(options, pkgBuilder::maintainerEmail); + + final var pkg = pkgBuilder.create(); + + // Show warning if license file is missing + if (pkg.licenseFile().isEmpty()) { + Log.verbose(I18N.getString("message.debs-like-licenses")); + } + + return pkg; + } + + private static LinuxPackageBuilder createLinuxPackageBuilder(Options options, StandardPackageType type) { + + final var app = createLinuxApplication(options); + + final var superPkgBuilder = createPackageBuilder(options, app, type); + + final var pkgBuilder = new LinuxPackageBuilder(superPkgBuilder); + + LINUX_PACKAGE_DEPENDENCIES.ifPresentIn(options, pkgBuilder::additionalDependencies); + LINUX_APP_CATEGORY.ifPresentIn(options, pkgBuilder::category); + LINUX_MENU_GROUP.ifPresentIn(options, pkgBuilder::menuGroupName); + LINUX_RELEASE.ifPresentIn(options, pkgBuilder::release); + LINUX_PACKAGE_NAME.ifPresentIn(options, pkgBuilder::literalName); + + return pkgBuilder; + } + +} diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromParams.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromParams.java deleted file mode 100644 index e9d1416b5c3..00000000000 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromParams.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.jpackage.internal; - -import static jdk.jpackage.internal.BundlerParamInfo.createStringBundlerParam; -import static jdk.jpackage.internal.FromParams.createApplicationBuilder; -import static jdk.jpackage.internal.FromParams.createApplicationBundlerParam; -import static jdk.jpackage.internal.FromParams.createPackageBuilder; -import static jdk.jpackage.internal.FromParams.createPackageBundlerParam; -import static jdk.jpackage.internal.FromParams.findLauncherShortcut; -import static jdk.jpackage.internal.LinuxPackagingPipeline.APPLICATION_LAYOUT; -import static jdk.jpackage.internal.model.StandardPackageType.LINUX_DEB; -import static jdk.jpackage.internal.model.StandardPackageType.LINUX_RPM; -import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; - -import java.io.IOException; -import java.util.Map; -import jdk.jpackage.internal.model.ConfigException; -import jdk.jpackage.internal.model.LinuxApplication; -import jdk.jpackage.internal.model.LinuxDebPackage; -import jdk.jpackage.internal.model.LinuxLauncher; -import jdk.jpackage.internal.model.LinuxLauncherMixin; -import jdk.jpackage.internal.model.LinuxRpmPackage; -import jdk.jpackage.internal.model.Launcher; -import jdk.jpackage.internal.model.StandardPackageType; - -final class LinuxFromParams { - - private static LinuxApplication createLinuxApplication( - Map params) throws ConfigException, IOException { - final var launcherFromParams = new LauncherFromParams(); - - final var app = createApplicationBuilder(params, toFunction(launcherParams -> { - final var launcher = launcherFromParams.create(launcherParams); - final var shortcut = findLauncherShortcut(LINUX_SHORTCUT_HINT, params, launcherParams); - return LinuxLauncher.create(launcher, new LinuxLauncherMixin.Stub(shortcut)); - }), (LinuxLauncher linuxLauncher, Launcher launcher) -> { - return LinuxLauncher.create(launcher, linuxLauncher); - }, APPLICATION_LAYOUT).create(); - return LinuxApplication.create(app); - } - - private static LinuxPackageBuilder createLinuxPackageBuilder( - Map params, StandardPackageType type) throws ConfigException, IOException { - - final var app = APPLICATION.fetchFrom(params); - - final var superPkgBuilder = createPackageBuilder(params, app, type); - - final var pkgBuilder = new LinuxPackageBuilder(superPkgBuilder); - - LINUX_PACKAGE_DEPENDENCIES.copyInto(params, pkgBuilder::additionalDependencies); - LINUX_CATEGORY.copyInto(params, pkgBuilder::category); - LINUX_MENU_GROUP.copyInto(params, pkgBuilder::menuGroupName); - RELEASE.copyInto(params, pkgBuilder::release); - LINUX_PACKAGE_NAME.copyInto(params, pkgBuilder::literalName); - - return pkgBuilder; - } - - private static LinuxRpmPackage createLinuxRpmPackage( - Map params) throws ConfigException, IOException { - - final var superPkgBuilder = createLinuxPackageBuilder(params, LINUX_RPM); - - final var pkgBuilder = new LinuxRpmPackageBuilder(superPkgBuilder); - - LICENSE_TYPE.copyInto(params, pkgBuilder::licenseType); - - return pkgBuilder.create(); - } - - private static LinuxDebPackage createLinuxDebPackage( - Map params) throws ConfigException, IOException { - - final var superPkgBuilder = createLinuxPackageBuilder(params, LINUX_DEB); - - final var pkgBuilder = new LinuxDebPackageBuilder(superPkgBuilder); - - MAINTAINER_EMAIL.copyInto(params, pkgBuilder::maintainerEmail); - - final var pkg = pkgBuilder.create(); - - // Show warning if license file is missing - if (pkg.licenseFile().isEmpty()) { - Log.verbose(I18N.getString("message.debs-like-licenses")); - } - - return pkg; - } - - static final BundlerParamInfo APPLICATION = createApplicationBundlerParam( - LinuxFromParams::createLinuxApplication); - - static final BundlerParamInfo RPM_PACKAGE = createPackageBundlerParam( - LinuxFromParams::createLinuxRpmPackage); - - static final BundlerParamInfo DEB_PACKAGE = createPackageBundlerParam( - LinuxFromParams::createLinuxDebPackage); - - private static final BundlerParamInfo LINUX_SHORTCUT_HINT = createStringBundlerParam( - Arguments.CLIOptions.LINUX_SHORTCUT_HINT.getId()); - - private static final BundlerParamInfo LINUX_CATEGORY = createStringBundlerParam( - Arguments.CLIOptions.LINUX_CATEGORY.getId()); - - private static final BundlerParamInfo LINUX_PACKAGE_DEPENDENCIES = createStringBundlerParam( - Arguments.CLIOptions.LINUX_PACKAGE_DEPENDENCIES.getId()); - - private static final BundlerParamInfo LINUX_MENU_GROUP = createStringBundlerParam( - Arguments.CLIOptions.LINUX_MENU_GROUP.getId()); - - private static final BundlerParamInfo RELEASE = createStringBundlerParam( - Arguments.CLIOptions.RELEASE.getId()); - - private static final BundlerParamInfo LINUX_PACKAGE_NAME = createStringBundlerParam( - Arguments.CLIOptions.LINUX_BUNDLE_NAME.getId()); - - private static final BundlerParamInfo LICENSE_TYPE = createStringBundlerParam( - Arguments.CLIOptions.LINUX_RPM_LICENSE_TYPE.getId()); - - private static final BundlerParamInfo MAINTAINER_EMAIL = createStringBundlerParam( - Arguments.CLIOptions.LINUX_DEB_MAINTAINER.getId()); -} diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBuilder.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBuilder.java index 4cfb8a26c8f..bc7c301ace2 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBuilder.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBuilder.java @@ -32,12 +32,11 @@ import java.util.regex.Pattern; import jdk.jpackage.internal.model.AppImageLayout; import jdk.jpackage.internal.model.ApplicationLayout; -import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.LinuxApplication; import jdk.jpackage.internal.model.LinuxPackage; -import jdk.jpackage.internal.model.RuntimeLayout; import jdk.jpackage.internal.model.LinuxPackageMixin; import jdk.jpackage.internal.model.Package; +import jdk.jpackage.internal.model.RuntimeLayout; import jdk.jpackage.internal.model.StandardPackageType; final class LinuxPackageBuilder { @@ -46,20 +45,17 @@ final class LinuxPackageBuilder { this.pkgBuilder = Objects.requireNonNull(pkgBuilder); } - LinuxPackage create() throws ConfigException { - if (literalName != null) { - pkgBuilder.name(literalName); - } else { + LinuxPackage create() { + pkgBuilder.name(Optional.ofNullable(literalName).orElseGet(() -> { // Lower case and turn spaces/underscores into dashes - pkgBuilder.name(pkgBuilder.create().packageName().toLowerCase().replaceAll("[ _]", "-")); - } + return pkgBuilder.create().packageName().toLowerCase().replaceAll("[ _]", "-"); + })); final var tmpPkg = pkgBuilder.create(); - final var stdPkgType = tmpPkg.asStandardPackageType(); - if (stdPkgType.isPresent()) { - validatePackageName(tmpPkg.packageName(), stdPkgType.orElseThrow()); - } + tmpPkg.asStandardPackageType().ifPresent(stdPkgType -> { + validatePackageName(tmpPkg.packageName(), stdPkgType); + }); final AppImageLayout relativeInstalledLayout; if (create(tmpPkg).isInstallDirInUsrTree()) { @@ -81,7 +77,7 @@ LinuxPackage create() throws ConfigException { .create()); } - private LinuxPackage create(Package pkg) throws ConfigException { + private LinuxPackage create(Package pkg) { return LinuxPackage.create(pkg, new LinuxPackageMixin.Stub( Optional.ofNullable(menuGroupName).orElseGet(DEFAULTS::menuGroupName), category(), @@ -137,8 +133,7 @@ private static LinuxApplicationLayout usrTreePackageLayout(Path prefix, String p lib.resolve("lib/libapplauncher.so")); } - private static void validatePackageName(String packageName, - StandardPackageType pkgType) throws ConfigException { + private static void validatePackageName(String packageName, StandardPackageType pkgType) { switch (pkgType) { case LINUX_DEB -> { // diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBundler.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBundler.java deleted file mode 100644 index 1f674c0be11..00000000000 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBundler.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.jpackage.internal; - -import java.util.Map; -import java.util.Objects; -import jdk.jpackage.internal.model.ConfigException; -import jdk.jpackage.internal.model.LinuxPackage; -import jdk.jpackage.internal.util.Result; - -abstract class LinuxPackageBundler extends AbstractBundler { - - LinuxPackageBundler(BundlerParamInfo pkgParam) { - this.pkgParam = Objects.requireNonNull(pkgParam); - } - - @Override - public final boolean validate(Map params) - throws ConfigException { - - // Order is important! - pkgParam.fetchFrom(params); - BuildEnvFromParams.BUILD_ENV.fetchFrom(params); - - LinuxSystemEnvironment sysEnv; - try { - sysEnv = sysEnv().orElseThrow(); - } catch (RuntimeException ex) { - throw ConfigException.rethrowConfigException(ex); - } - - if (!isDefault()) { - Log.verbose(I18N.format( - "message.not-default-bundler-no-dependencies-lookup", - getName())); - } else if (!sysEnv.soLookupAvailable()) { - final String advice; - if ("deb".equals(getID())) { - advice = "message.deb-ldd-not-available.advice"; - } else { - advice = "message.rpm-ldd-not-available.advice"; - } - // Let user know package dependencies will not be generated. - Log.error(String.format("%s\n%s", I18N.getString( - "message.ldd-not-available"), I18N.getString(advice))); - } - - return true; - } - - @Override - public final String getBundleType() { - return "INSTALLER"; - } - - @Override - public boolean supported(boolean runtimeInstaller) { - return sysEnv().hasValue(); - } - - protected abstract Result sysEnv(); - - private final BundlerParamInfo pkgParam; - - static final Result SYS_ENV = LinuxSystemEnvironment.create(); -} diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackager.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackager.java index 806592904d1..af7f5288cc5 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackager.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackager.java @@ -40,7 +40,6 @@ import jdk.jpackage.internal.PackagingPipeline.TaskID; import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.LinuxPackage; -import jdk.jpackage.internal.model.PackagerException; abstract class LinuxPackager implements Consumer { @@ -95,7 +94,7 @@ protected final Path outputPackageFile() { protected abstract void initLibProvidersLookup(LibProvidersLookup libProvidersLookup); - private void buildConfigFiles() throws PackagerException, IOException { + private void buildConfigFiles() throws IOException { final var data = createDefaultReplacementData(); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackagingPipeline.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackagingPipeline.java index 6f6013b3091..4b846db3231 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackagingPipeline.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackagingPipeline.java @@ -24,6 +24,7 @@ */ package jdk.jpackage.internal; +import static jdk.jpackage.internal.ApplicationBuilder.normalizeLauncherProperty; import static jdk.jpackage.internal.ApplicationImageUtils.createLauncherIconResource; import java.io.IOException; @@ -36,8 +37,12 @@ import jdk.jpackage.internal.PackagingPipeline.PrimaryTaskID; import jdk.jpackage.internal.PackagingPipeline.TaskID; 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.LauncherShortcut; +import jdk.jpackage.internal.model.LinuxLauncher; +import jdk.jpackage.internal.model.LinuxLauncherMixin; import jdk.jpackage.internal.model.LinuxPackage; import jdk.jpackage.internal.resources.ResourceLocator; @@ -64,6 +69,17 @@ static PackagingPipeline.Builder build(Optional pkg) { return builder; } + static ApplicationLaunchers normalizeShortcuts(ApplicationLaunchers appLaunchers) { + return normalizeLauncherProperty(appLaunchers, launcher -> { + // Return "true" if shortcut is not configured for the launcher. + return launcher.shortcut().isEmpty(); + }, (LinuxLauncher launcher) -> { + return launcher.shortcut().flatMap(LauncherShortcut::startupDirectory); + }, (launcher, shortcut) -> { + return LinuxLauncher.create(launcher, new LinuxLauncherMixin.Stub(Optional.of(new LauncherShortcut(shortcut)))); + }); + } + private static void writeLauncherLib( AppImageBuildEnv env) throws IOException { @@ -90,6 +106,15 @@ private static void writeLauncherIcons( }); } + private static final ApplicationLayout LINUX_APPLICATION_LAYOUT = ApplicationLayout.build() + .launchersDirectory("bin") + .appDirectory("lib/app") + .runtimeDirectory("lib/runtime") + .desktopIntegrationDirectory("lib") + .appModsDirectory("lib/app/mods") + .contentDirectory("lib") + .create(); + static final LinuxApplicationLayout APPLICATION_LAYOUT = LinuxApplicationLayout.create( - ApplicationLayoutUtils.PLATFORM_APPLICATION_LAYOUT, Path.of("lib/libapplauncher.so")); + LINUX_APPLICATION_LAYOUT, Path.of("lib/libapplauncher.so")); } diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmBundler.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmBundler.java deleted file mode 100644 index c134aa91d6a..00000000000 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmBundler.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.nio.file.Path; -import java.util.Map; -import java.util.Optional; -import jdk.jpackage.internal.model.LinuxRpmPackage; -import jdk.jpackage.internal.model.PackagerException; -import jdk.jpackage.internal.model.StandardPackageType; -import jdk.jpackage.internal.util.Result; - - -public class LinuxRpmBundler extends LinuxPackageBundler { - - public LinuxRpmBundler() { - super(LinuxFromParams.RPM_PACKAGE); - } - - @Override - public String getName() { - return I18N.getString("rpm.bundler.name"); - } - - @Override - public String getID() { - return "rpm"; - } - - @Override - public Path execute(Map params, Path outputParentDir) throws PackagerException { - - var pkg = LinuxFromParams.RPM_PACKAGE.fetchFrom(params); - - return Packager.build().outputDir(outputParentDir) - .pkg(pkg) - .env(BuildEnvFromParams.BUILD_ENV.fetchFrom(params)) - .pipelineBuilderMutatorFactory((env, _, outputDir) -> { - return new LinuxRpmPackager(env, pkg, outputDir, sysEnv.orElseThrow()); - }).execute(LinuxPackagingPipeline.build(Optional.of(pkg))); - } - - @Override - protected Result sysEnv() { - return sysEnv; - } - - @Override - public boolean isDefault() { - return sysEnv.value() - .map(LinuxSystemEnvironment::nativePackageType) - .map(StandardPackageType.LINUX_RPM::equals) - .orElse(false); - } - - private final Result sysEnv = LinuxRpmSystemEnvironment.create(SYS_ENV); -} diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmPackageBuilder.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmPackageBuilder.java index bc361eac759..f97a7741e42 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmPackageBuilder.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmPackageBuilder.java @@ -26,7 +26,6 @@ import java.util.Objects; import java.util.Optional; -import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.LinuxRpmPackage; import jdk.jpackage.internal.model.LinuxRpmPackageMixin; @@ -36,7 +35,7 @@ final class LinuxRpmPackageBuilder { this.pkgBuilder = Objects.requireNonNull(pkgBuilder); } - LinuxRpmPackage create() throws ConfigException { + LinuxRpmPackage create() { if (pkgBuilder.release().isEmpty()) { pkgBuilder.release("1"); } diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxLauncher.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxLauncher.java index c84b5e3bbf5..3c654f604c2 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxLauncher.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxLauncher.java @@ -24,6 +24,8 @@ */ package jdk.jpackage.internal.model; +import static jdk.jpackage.internal.cli.StandardAppImageFileOption.LINUX_LAUNCHER_SHORTCUT; + import java.util.HashMap; import java.util.Map; import jdk.jpackage.internal.util.CompositeProxy; @@ -39,7 +41,7 @@ public interface LinuxLauncher extends Launcher, LinuxLauncherMixin { default Map extraAppImageFileData() { Map map = new HashMap<>(); shortcut().ifPresent(shortcut -> { - shortcut.store(SHORTCUT_ID, map::put); + shortcut.store(LINUX_LAUNCHER_SHORTCUT.getName(), map::put); }); return map; } @@ -55,6 +57,4 @@ default Map extraAppImageFileData() { public static LinuxLauncher create(Launcher launcher, LinuxLauncherMixin mixin) { return CompositeProxy.create(LinuxLauncher.class, launcher, mixin); } - - public static final String SHORTCUT_ID = "linux-shortcut"; } diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties index a732d02c7d1..3aabe1f4ba5 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties @@ -23,10 +23,6 @@ # questions. # # -app.bundler.name=Linux Application Image -deb.bundler.name=DEB Bundle -rpm.bundler.name=RPM Bundle - param.license-type.default=Unknown resource.deb-control-file=DEB control file @@ -58,7 +54,6 @@ message.output-to-location=Package (.deb) saved to: {0}. message.debs-like-licenses=Debian packages should specify a license. The absence of a license will cause some linux distributions to complain about the quality of the application. message.outputting-bundle-location=Generating RPM for installer to: {0}. message.output-bundle-location=Package (.rpm) saved to: {0}. -message.creating-association-with-null-extension=Creating association with null extension. message.ldd-not-available=ldd command not found. Package dependencies will not be generated. message.deb-ldd-not-available.advice=Install "libc-bin" DEB package to get ldd. diff --git a/src/jdk.jpackage/linux/classes/module-info.java.extra b/src/jdk.jpackage/linux/classes/module-info.java.extra index d32314b0429..7bef2286214 100644 --- a/src/jdk.jpackage/linux/classes/module-info.java.extra +++ b/src/jdk.jpackage/linux/classes/module-info.java.extra @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -23,8 +23,5 @@ * questions. */ -provides jdk.jpackage.internal.Bundler with - jdk.jpackage.internal.LinuxAppBundler, - jdk.jpackage.internal.LinuxDebBundler, - jdk.jpackage.internal.LinuxRpmBundler; - +provides jdk.jpackage.internal.cli.CliBundlingEnvironment with + jdk.jpackage.internal.LinuxBundlingEnvironment; diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigningConfigBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigningConfigBuilder.java index bb043830f3c..6fc7fe004c2 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigningConfigBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigningConfigBuilder.java @@ -28,7 +28,6 @@ import java.util.Objects; import java.util.Optional; import jdk.jpackage.internal.model.AppImageSigningConfig; -import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.LauncherStartupInfo; final class AppImageSigningConfigBuilder { @@ -62,21 +61,20 @@ AppImageSigningConfigBuilder signingIdentifierPrefix(String v) { return this; } - Optional create() throws ConfigException { - final var identityCfg = signingIdentityBuilder.create(); - if (identityCfg.isEmpty()) { - return Optional.empty(); - } else { + Optional create() { + return signingIdentityBuilder.create().map(cfg -> { final var validatedEntitlements = validatedEntitlements(); - return identityCfg.map(cfg -> { - return new AppImageSigningConfig.Stub(cfg.identity(), signingIdentifierPrefix, - validatedEntitlements, cfg.keychain().map(Keychain::name), - Optional.ofNullable(entitlementsResourceName).orElse("entitlements.plist")); - }); - } + return new AppImageSigningConfig.Stub( + Objects.requireNonNull(cfg.identity()), + Objects.requireNonNull(signingIdentifierPrefix), + validatedEntitlements, + cfg.keychain().map(Keychain::name), + Optional.ofNullable(entitlementsResourceName).orElse("entitlements.plist") + ); + }); } - private Optional validatedEntitlements() throws ConfigException { + private Optional validatedEntitlements() { return Optional.ofNullable(entitlements); } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppBundler.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppBundler.java deleted file mode 100644 index cce35ece117..00000000000 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppBundler.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import static jdk.jpackage.internal.StandardBundlerParam.OUTPUT_DIR; -import static jdk.jpackage.internal.StandardBundlerParam.SIGN_BUNDLE; - -import java.util.Map; -import java.util.Optional; -import jdk.jpackage.internal.model.ConfigException; -import jdk.jpackage.internal.util.function.ExceptionBox; - -public class MacAppBundler extends AppImageBundler { - public MacAppBundler() { - setAppImageSupplier((params, output) -> { - - // Order is important! - final var app = MacFromParams.APPLICATION.fetchFrom(params); - final BuildEnv env; - - if (StandardBundlerParam.hasPredefinedAppImage(params)) { - env = MacBuildEnvFromParams.BUILD_ENV.fetchFrom(params); - final var pkg = MacPackagingPipeline.createSignAppImagePackage(app, env); - MacPackagingPipeline.build(Optional.of(pkg)).create().execute(env, pkg, output); - } else { - env = BuildEnv.withAppImageDir(MacBuildEnvFromParams.BUILD_ENV.fetchFrom(params), output); - MacPackagingPipeline.build(Optional.empty()) - .excludeDirFromCopying(output.getParent()) - .excludeDirFromCopying(OUTPUT_DIR.fetchFrom(params)).create().execute(env, app); - } - - }); - setParamsValidator(MacAppBundler::doValidate); - } - - private static void doValidate(Map params) - throws ConfigException { - - try { - MacFromParams.APPLICATION.fetchFrom(params); - } catch (ExceptionBox ex) { - if (ex.getCause() instanceof ConfigException cfgEx) { - throw cfgEx; - } else { - throw ex; - } - } - - if (StandardBundlerParam.hasPredefinedAppImage(params)) { - if (!Optional.ofNullable( - SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.FALSE)) { - throw new ConfigException( - I18N.getString("error.app-image.mac-sign.required"), - null); - } - } - } -} 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 226bb9e8134..5f126305aed 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java @@ -28,17 +28,15 @@ import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.List; -import java.util.Set; import java.util.Objects; import java.util.Optional; +import java.util.Set; +import jdk.jpackage.internal.model.AppImageLayout; +import jdk.jpackage.internal.model.AppImageSigningConfig; import jdk.jpackage.internal.model.Application; -import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.MacApplication; import jdk.jpackage.internal.model.MacApplicationMixin; -import jdk.jpackage.internal.model.AppImageLayout; -import jdk.jpackage.internal.model.AppImageSigningConfig; final class MacApplicationBuilder { @@ -92,7 +90,7 @@ MacApplicationBuilder signingBuilder(AppImageSigningConfigBuilder v) { return this; } - MacApplication create() throws ConfigException { + MacApplication create() { if (externalInfoPlistFile != null) { return createCopyForExternalInfoPlistFile().create(); } @@ -136,7 +134,7 @@ static boolean isValidBundleIdentifier(String id) { return true; } - private static void validateAppVersion(Application app) throws ConfigException { + private static void validateAppVersion(Application app) { try { CFBundleVersion.of(app.version()); } catch (IllegalArgumentException ex) { @@ -156,7 +154,7 @@ private static void validateAppContentDirs(Application app) { } } - private MacApplicationBuilder createCopyForExternalInfoPlistFile() throws ConfigException { + private MacApplicationBuilder createCopyForExternalInfoPlistFile() { try { final var plistFile = AppImageInfoPListFile.loadFromInfoPList(externalInfoPlistFile); @@ -187,15 +185,11 @@ private MacApplicationBuilder createCopyForExternalInfoPlistFile() throws Config } } - private Optional createSigningConfig() throws ConfigException { - if (signingBuilder != null) { - return signingBuilder.create(); - } else { - return Optional.empty(); - } + private Optional createSigningConfig() { + return Optional.ofNullable(signingBuilder).flatMap(AppImageSigningConfigBuilder::create); } - private String validatedBundleName() throws ConfigException { + private String validatedBundleName() { final var value = Optional.ofNullable(bundleName).orElseGet(() -> { final var appName = app.name(); // Commented out for backward compatibility @@ -214,7 +208,7 @@ private String validatedBundleName() throws ConfigException { return value; } - private String validatedBundleIdentifier() throws ConfigException { + private String validatedBundleIdentifier() { final var value = Optional.ofNullable(bundleIdentifier).orElseGet(() -> { return app.mainLauncher() .flatMap(Launcher::startupInfo) @@ -238,16 +232,12 @@ private String validatedBundleIdentifier() throws ConfigException { return value; } - private String validatedCategory() throws ConfigException { + private String validatedCategory() { return "public.app-category." + Optional.ofNullable(category).orElseGet(DEFAULTS::category); } - private Optional validatedIcon() throws ConfigException { - if (icon != null) { - LauncherBuilder.validateIcon(icon); - } - - return Optional.ofNullable(icon); + private Optional validatedIcon() { + return Optional.ofNullable(icon).map(LauncherBuilder::validateIcon); } private record Defaults(String category) { diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundlingEnvironment.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundlingEnvironment.java new file mode 100644 index 00000000000..371a3c7307a --- /dev/null +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundlingEnvironment.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal; + +import static jdk.jpackage.internal.MacFromOptions.createMacApplication; +import static jdk.jpackage.internal.MacPackagingPipeline.APPLICATION_LAYOUT; +import static jdk.jpackage.internal.MacPackagingPipeline.createSignAppImagePackage; +import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_MAC_APP_IMAGE; +import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_MAC_DMG; +import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_MAC_PKG; +import static jdk.jpackage.internal.cli.StandardBundlingOperation.SIGN_MAC_APP_IMAGE; + +import java.util.Optional; +import jdk.jpackage.internal.cli.Options; +import jdk.jpackage.internal.model.MacPackage; +import jdk.jpackage.internal.model.Package; +import jdk.jpackage.internal.util.Result; + +public class MacBundlingEnvironment extends DefaultBundlingEnvironment { + + public MacBundlingEnvironment() { + super(build() + .defaultOperation(CREATE_MAC_DMG) + .bundler(SIGN_MAC_APP_IMAGE, MacBundlingEnvironment::signAppImage) + .bundler(CREATE_MAC_APP_IMAGE, MacBundlingEnvironment::createAppImage) + .bundler(CREATE_MAC_DMG, LazyLoad::dmgSysEnv, MacBundlingEnvironment::createDmdPackage) + .bundler(CREATE_MAC_PKG, MacBundlingEnvironment::createPkgPackage)); + } + + private static void createDmdPackage(Options options, MacDmgSystemEnvironment sysEnv) { + createNativePackage(options, + MacFromOptions::createMacDmgPackage, + buildEnv()::create, + MacBundlingEnvironment::buildPipeline, + (env, pkg, outputDir) -> { + Log.verbose(I18N.format("message.building-dmg", pkg.app().name())); + return new MacDmgPackager(env, pkg, outputDir, sysEnv); + }); + } + + private static void createPkgPackage(Options options) { + createNativePackage(options, + MacFromOptions::createMacPkgPackage, + buildEnv()::create, + MacBundlingEnvironment::buildPipeline, + (env, pkg, outputDir) -> { + Log.verbose(I18N.format("message.building-pkg", pkg.app().name())); + return new MacPkgPackager(env, pkg, outputDir); + }); + } + + private static void signAppImage(Options options) { + + final var app = createMacApplication(options); + + final var env = buildEnv().create(options, app); + + final var pkg = createSignAppImagePackage(app, env); + + buildPipeline(pkg).create().execute(env, pkg, env.appImageDir()); + } + + private static void createAppImage(Options options) { + + final var app = createMacApplication(options); + + createApplicationImage(options, app, MacPackagingPipeline.build(Optional.empty())); + } + + private static PackagingPipeline.Builder buildPipeline(Package pkg) { + return MacPackagingPipeline.build(Optional.of(pkg)); + } + + private static BuildEnvFromOptions buildEnv() { + return new BuildEnvFromOptions() + .predefinedAppImageLayout(APPLICATION_LAYOUT) + .predefinedRuntimeImageLayout(MacPackage::guessRuntimeLayout); + } + + private static final class LazyLoad { + + static Result dmgSysEnv() { + return DMG_SYS_ENV; + } + + private static final Result DMG_SYS_ENV = MacDmgSystemEnvironment.create(); + } +} diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java deleted file mode 100644 index 0ddb987dbee..00000000000 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.nio.file.Path; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import jdk.jpackage.internal.model.ConfigException; -import jdk.jpackage.internal.model.MacDmgPackage; -import jdk.jpackage.internal.model.PackagerException; -import jdk.jpackage.internal.util.Result; - -public class MacDmgBundler extends MacBaseInstallerBundler { - - @Override - public String getName() { - return I18N.getString("dmg.bundler.name"); - } - - @Override - public String getID() { - return "dmg"; - } - - @Override - public boolean validate(Map params) - throws ConfigException { - try { - Objects.requireNonNull(params); - - MacFromParams.DMG_PACKAGE.fetchFrom(params); - - //run basic validation to ensure requirements are met - //we are not interested in return code, only possible exception - validateAppImageAndBundeler(params); - - return true; - } catch (RuntimeException re) { - if (re.getCause() instanceof ConfigException) { - throw (ConfigException) re.getCause(); - } else { - throw new ConfigException(re); - } - } - } - - @Override - public Path execute(Map params, - Path outputParentDir) throws PackagerException { - - var pkg = MacFromParams.DMG_PACKAGE.fetchFrom(params); - - Log.verbose(I18N.format("message.building-dmg", pkg.app().name())); - - return Packager.build().outputDir(outputParentDir) - .pkg(pkg) - .env(MacBuildEnvFromParams.BUILD_ENV.fetchFrom(params)) - .pipelineBuilderMutatorFactory((env, _, outputDir) -> { - return new MacDmgPackager(env, pkg, outputDir, sysEnv.orElseThrow()); - }).execute(MacPackagingPipeline.build(Optional.of(pkg))); - } - - @Override - public boolean supported(boolean runtimeInstaller) { - return sysEnv.hasValue(); - } - - @Override - public boolean isDefault() { - return true; - } - - private final Result sysEnv = MacDmgSystemEnvironment.create(); -} diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackageBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackageBuilder.java index c2b9c25f327..10754b1f1b6 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackageBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackageBuilder.java @@ -28,7 +28,6 @@ import java.util.List; import java.util.Objects; import java.util.Optional; -import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.MacDmgPackage; import jdk.jpackage.internal.model.MacDmgPackageMixin; @@ -52,7 +51,7 @@ List validatedDmgContent() { return Optional.ofNullable(dmgContent).orElseGet(List::of); } - MacDmgPackage create() throws ConfigException { + MacDmgPackage create() { final var pkg = pkgBuilder.create(); return MacDmgPackage.create(pkg, new MacDmgPackageMixin.Stub( diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFileAssociationBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFileAssociationBuilder.java index c86b3a15cbb..7b9e5478f74 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFileAssociationBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFileAssociationBuilder.java @@ -27,15 +27,13 @@ import java.util.List; import java.util.Objects; import java.util.Optional; - -import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.FileAssociation; import jdk.jpackage.internal.model.MacFileAssociation; import jdk.jpackage.internal.model.MacFileAssociationMixin; final class MacFileAssociationBuilder { - MacFileAssociation create(FileAssociation fa) throws ConfigException { + MacFileAssociation create(FileAssociation fa) { Objects.requireNonNull(fa); final var mixin = new MacFileAssociationMixin.Stub( diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java new file mode 100644 index 00000000000..074014dede0 --- /dev/null +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +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.cli.StandardBundlingOperation.SIGN_MAC_APP_IMAGE; +import static jdk.jpackage.internal.cli.StandardOption.ICON; +import static jdk.jpackage.internal.cli.StandardOption.APPCLASS; +import static jdk.jpackage.internal.cli.StandardOption.MAC_APP_CATEGORY; +import static jdk.jpackage.internal.cli.StandardOption.MAC_APP_IMAGE_SIGN_IDENTITY; +import static jdk.jpackage.internal.cli.StandardOption.MAC_APP_STORE; +import static jdk.jpackage.internal.cli.StandardOption.MAC_BUNDLE_IDENTIFIER; +import static jdk.jpackage.internal.cli.StandardOption.MAC_BUNDLE_NAME; +import static jdk.jpackage.internal.cli.StandardOption.MAC_BUNDLE_SIGNING_PREFIX; +import static jdk.jpackage.internal.cli.StandardOption.MAC_DMG_CONTENT; +import static jdk.jpackage.internal.cli.StandardOption.MAC_ENTITLEMENTS; +import static jdk.jpackage.internal.cli.StandardOption.MAC_INSTALLER_SIGN_IDENTITY; +import static jdk.jpackage.internal.cli.StandardOption.MAC_SIGN; +import static jdk.jpackage.internal.cli.StandardOption.MAC_SIGNING_KEYCHAIN; +import static jdk.jpackage.internal.cli.StandardOption.MAC_SIGNING_KEY_NAME; +import static jdk.jpackage.internal.cli.StandardOption.PREDEFINED_APP_IMAGE; +import static jdk.jpackage.internal.cli.StandardOption.PREDEFINED_RUNTIME_IMAGE; +import static jdk.jpackage.internal.model.MacPackage.RUNTIME_BUNDLE_LAYOUT; +import static jdk.jpackage.internal.model.StandardPackageType.MAC_DMG; +import static jdk.jpackage.internal.model.StandardPackageType.MAC_PKG; +import static jdk.jpackage.internal.util.function.ExceptionBox.rethrowUnchecked; + +import java.nio.file.Path; +import java.util.Objects; +import java.util.Optional; +import jdk.jpackage.internal.ApplicationBuilder.MainLauncherStartupInfo; +import jdk.jpackage.internal.SigningIdentityBuilder.ExpiredCertificateException; +import jdk.jpackage.internal.SigningIdentityBuilder.StandardCertificateSelector; +import jdk.jpackage.internal.cli.Options; +import jdk.jpackage.internal.cli.StandardFaOption; +import jdk.jpackage.internal.model.ApplicationLaunchers; +import jdk.jpackage.internal.model.ExternalApplication; +import jdk.jpackage.internal.model.FileAssociation; +import jdk.jpackage.internal.model.Launcher; +import jdk.jpackage.internal.model.MacApplication; +import jdk.jpackage.internal.model.MacDmgPackage; +import jdk.jpackage.internal.model.MacFileAssociation; +import jdk.jpackage.internal.model.MacLauncher; +import jdk.jpackage.internal.model.MacPackage; +import jdk.jpackage.internal.model.MacPkgPackage; +import jdk.jpackage.internal.model.PackageType; +import jdk.jpackage.internal.model.RuntimeLayout; +import jdk.jpackage.internal.util.Result; +import jdk.jpackage.internal.util.function.ExceptionBox; + + +final class MacFromOptions { + + static MacApplication createMacApplication(Options options) { + return createMacApplicationInternal(options).app(); + } + + static MacDmgPackage createMacDmgPackage(Options options) { + + final var app = createMacApplicationInternal(options); + + final var superPkgBuilder = createMacPackageBuilder(options, app, MAC_DMG); + + final var pkgBuilder = new MacDmgPackageBuilder(superPkgBuilder); + + MAC_DMG_CONTENT.ifPresentIn(options, pkgBuilder::dmgContent); + + return pkgBuilder.create(); + } + + static MacPkgPackage createMacPkgPackage(Options options) { + + // + // One of "MacSignTest.testExpiredCertificate" test cases expects + // two error messages about expired certificates in the output: one for + // certificate for signing an app image, another certificate for signing a PKG. + // So creation of a PKG package is a bit messy. + // + + final boolean sign = MAC_SIGN.findIn(options).orElse(false); + final boolean appStore = MAC_APP_STORE.findIn(options).orElse(false); + + final var appResult = Result.create(() -> createMacApplicationInternal(options)); + + final Optional pkgBuilder; + if (appResult.hasValue()) { + final var superPkgBuilder = createMacPackageBuilder(options, appResult.orElseThrow(), MAC_PKG); + pkgBuilder = Optional.of(new MacPkgPackageBuilder(superPkgBuilder)); + } else { + // Failed to create an app. Is it because of the expired certificate? + rethrowIfNotExpiredCertificateException(appResult); + // Yes, the certificate for signing the app image has expired. + // Keep going, try to create a signing config for the package. + pkgBuilder = Optional.empty(); + } + + if (sign) { + final var signingIdentityBuilder = createSigningIdentityBuilder(options); + MAC_INSTALLER_SIGN_IDENTITY.ifPresentIn(options, signingIdentityBuilder::signingIdentity); + MAC_SIGNING_KEY_NAME.findIn(options).ifPresent(userName -> { + final StandardCertificateSelector domain; + if (appStore) { + domain = StandardCertificateSelector.APP_STORE_PKG_INSTALLER; + } else { + domain = StandardCertificateSelector.PKG_INSTALLER; + } + + signingIdentityBuilder.certificateSelector(StandardCertificateSelector.create(userName, domain)); + }); + + if (pkgBuilder.isPresent()) { + pkgBuilder.orElseThrow().signingBuilder(signingIdentityBuilder); + } else { + // + // The certificate for signing the app image has expired. Can not create a + // package because there is no app. + // Try to create a signing config for the package and see if the certificate for + // signing the package is also expired. + // + + final var expiredAppCertException = appResult.firstError().orElseThrow(); + + final var pkgSignConfigResult = Result.create(signingIdentityBuilder::create); + try { + rethrowIfNotExpiredCertificateException(pkgSignConfigResult); + // The certificate for the package signing config is also expired! + } catch (RuntimeException ex) { + // Some error occurred trying to configure the signing config for the package. + // Ignore it, bail out with the first error. + rethrowUnchecked(expiredAppCertException); + } + + Log.error(pkgSignConfigResult.firstError().orElseThrow().getMessage()); + rethrowUnchecked(expiredAppCertException); + } + } + + return pkgBuilder.orElseThrow().create(); + } + + private record ApplicationWithDetails(MacApplication app, Optional externalApp) { + ApplicationWithDetails { + Objects.requireNonNull(app); + Objects.requireNonNull(externalApp); + } + } + + private static ApplicationWithDetails createMacApplicationInternal(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); + } + }); + + final var launcherFromOptions = new LauncherFromOptions().faMapper(MacFromOptions::createMacFa); + + final var superAppBuilder = buildApplicationBuilder() + .runtimeLayout(RUNTIME_BUNDLE_LAYOUT) + .predefinedRuntimeLayout(predefinedRuntimeLayout.map(RuntimeLayout::unresolve).orElse(null)) + .create(options, launcherOptions -> { + var launcher = launcherFromOptions.create(launcherOptions); + return MacLauncher.create(launcher); + }, (MacLauncher _, Launcher launcher) -> { + return MacLauncher.create(launcher); + }, APPLICATION_LAYOUT); + + if (PREDEFINED_APP_IMAGE.containsIn(options)) { + // Set the main launcher start up info. + // AppImageFile assumes the main launcher start up info is available when + // it is constructed from Application instance. + // This happens when jpackage signs predefined app image. + final var appImageFileOptions = superAppBuilder.externalApplication().orElseThrow().extra(); + final var mainLauncherStartupInfo = new MainLauncherStartupInfo(APPCLASS.getFrom(appImageFileOptions)); + final var launchers = superAppBuilder.launchers().orElseThrow(); + final var mainLauncher = ApplicationBuilder.overrideLauncherStartupInfo(launchers.mainLauncher(), mainLauncherStartupInfo); + superAppBuilder.launchers(new ApplicationLaunchers(MacLauncher.create(mainLauncher), launchers.additionalLaunchers())); + } + + final var app = superAppBuilder.create(); + + final var appBuilder = new MacApplicationBuilder(app); + + 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); + MAC_BUNDLE_IDENTIFIER.ifPresentIn(options, appBuilder::bundleIdentifier); + MAC_APP_CATEGORY.ifPresentIn(options, appBuilder::category); + + final boolean sign; + final boolean appStore; + + if (PREDEFINED_APP_IMAGE.containsIn(options) && OptionUtils.bundlingOperation(options) != SIGN_MAC_APP_IMAGE) { + final var appImageFileOptions = superAppBuilder.externalApplication().orElseThrow().extra(); + sign = MAC_SIGN.getFrom(appImageFileOptions); + appStore = MAC_APP_STORE.getFrom(appImageFileOptions); + } else { + sign = MAC_SIGN.getFrom(options); + appStore = MAC_APP_STORE.getFrom(options); + } + + appBuilder.appStore(appStore); + + if (sign) { + final var signingIdentityBuilder = createSigningIdentityBuilder(options); + MAC_APP_IMAGE_SIGN_IDENTITY.ifPresentIn(options, signingIdentityBuilder::signingIdentity); + MAC_SIGNING_KEY_NAME.findIn(options).ifPresent(userName -> { + final StandardCertificateSelector domain; + if (appStore) { + domain = StandardCertificateSelector.APP_STORE_APP_IMAGE; + } else { + domain = StandardCertificateSelector.APP_IMAGE; + } + + signingIdentityBuilder.certificateSelector(StandardCertificateSelector.create(userName, domain)); + }); + + final var signingBuilder = new AppImageSigningConfigBuilder(signingIdentityBuilder); + if (appStore) { + signingBuilder.entitlementsResourceName("sandbox.plist"); + } + + app.mainLauncher().flatMap(Launcher::startupInfo).ifPresentOrElse( + signingBuilder::signingIdentifierPrefix, + () -> { + // Runtime installer does not have the main launcher, use + // 'bundleIdentifier' as the prefix by default. + var bundleIdentifier = appBuilder.create().bundleIdentifier(); + signingBuilder.signingIdentifierPrefix(bundleIdentifier + "."); + }); + MAC_BUNDLE_SIGNING_PREFIX.ifPresentIn(options, signingBuilder::signingIdentifierPrefix); + + MAC_ENTITLEMENTS.ifPresentIn(options, signingBuilder::entitlements); + + appBuilder.signingBuilder(signingBuilder); + } + + return new ApplicationWithDetails(appBuilder.create(), superAppBuilder.externalApplication()); + } + + private static MacPackageBuilder createMacPackageBuilder(Options options, ApplicationWithDetails app, PackageType type) { + + final var builder = new MacPackageBuilder(createPackageBuilder(options, app.app(), type)); + + app.externalApp() + .map(ExternalApplication::extra) + .flatMap(MAC_SIGN::findIn) + .ifPresent(builder::predefinedAppImageSigned); + + PREDEFINED_RUNTIME_IMAGE.findIn(options) + .map(MacBundle::new) + .filter(MacBundle::isValid) + .map(MacBundle::isSigned) + .ifPresent(builder::predefinedAppImageSigned); + + return builder; + } + + private static void rethrowIfNotExpiredCertificateException(Result result) { + final var ex = result.firstError().orElseThrow(); + + if (ex instanceof ExpiredCertificateException) { + return; + } + + if (ex instanceof ExceptionBox box) { + if (box.getCause() instanceof Exception cause) { + rethrowIfNotExpiredCertificateException(Result.ofError(cause)); + } + } + + rethrowUnchecked(ex); + } + + private static SigningIdentityBuilder createSigningIdentityBuilder(Options options) { + final var builder = new SigningIdentityBuilder(); + MAC_SIGNING_KEYCHAIN.findIn(options).map(Path::toString).ifPresent(builder::keychain); + return builder; + } + + private static MacFileAssociation createMacFa(Options options, FileAssociation fa) { + + final var builder = new MacFileAssociationBuilder(); + + StandardFaOption.MAC_CFBUNDLETYPEROLE.ifPresentIn(options, builder::cfBundleTypeRole); + StandardFaOption.MAC_LSHANDLERRANK.ifPresentIn(options, builder::lsHandlerRank); + StandardFaOption.MAC_NSSTORETYPEKEY.ifPresentIn(options, builder::nsPersistentStoreTypeKey); + StandardFaOption.MAC_NSDOCUMENTCLASS.ifPresentIn(options, builder::nsDocumentClass); + StandardFaOption.MAC_LSTYPEISPACKAGE.ifPresentIn(options, builder::lsTypeIsPackage); + StandardFaOption.MAC_LSDOCINPLACE.ifPresentIn(options, builder::lsSupportsOpeningDocumentsInPlace); + StandardFaOption.MAC_UIDOCBROWSER.ifPresentIn(options, builder::uiSupportsDocumentBrowser); + StandardFaOption.MAC_NSEXPORTABLETYPES.ifPresentIn(options, builder::nsExportableTypes); + StandardFaOption.MAC_UTTYPECONFORMSTO.ifPresentIn(options, builder::utTypeConformsTo); + + return builder.create(fa); + } +} diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromParams.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromParams.java deleted file mode 100644 index 72c33ef6475..00000000000 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromParams.java +++ /dev/null @@ -1,384 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.jpackage.internal; - -import static jdk.jpackage.internal.BundlerParamInfo.createBooleanBundlerParam; -import static jdk.jpackage.internal.BundlerParamInfo.createPathBundlerParam; -import static jdk.jpackage.internal.BundlerParamInfo.createStringBundlerParam; -import static jdk.jpackage.internal.FromParams.createApplicationBuilder; -import static jdk.jpackage.internal.FromParams.createApplicationBundlerParam; -import static jdk.jpackage.internal.FromParams.createPackageBuilder; -import static jdk.jpackage.internal.FromParams.createPackageBundlerParam; -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.StandardBundlerParam.DMG_CONTENT; -import static jdk.jpackage.internal.StandardBundlerParam.ICON; -import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_APP_IMAGE; -import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_APP_IMAGE_FILE; -import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_RUNTIME_IMAGE; -import static jdk.jpackage.internal.StandardBundlerParam.SIGN_BUNDLE; -import static jdk.jpackage.internal.StandardBundlerParam.hasPredefinedAppImage; -import static jdk.jpackage.internal.model.MacPackage.RUNTIME_BUNDLE_LAYOUT; -import static jdk.jpackage.internal.model.StandardPackageType.MAC_DMG; -import static jdk.jpackage.internal.model.StandardPackageType.MAC_PKG; -import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.Callable; -import java.util.function.Predicate; -import jdk.jpackage.internal.ApplicationBuilder.MainLauncherStartupInfo; -import jdk.jpackage.internal.SigningIdentityBuilder.ExpiredCertificateException; -import jdk.jpackage.internal.SigningIdentityBuilder.StandardCertificateSelector; -import jdk.jpackage.internal.model.ApplicationLaunchers; -import jdk.jpackage.internal.model.ConfigException; -import jdk.jpackage.internal.model.FileAssociation; -import jdk.jpackage.internal.model.Launcher; -import jdk.jpackage.internal.model.MacApplication; -import jdk.jpackage.internal.model.MacDmgPackage; -import jdk.jpackage.internal.model.MacFileAssociation; -import jdk.jpackage.internal.model.MacLauncher; -import jdk.jpackage.internal.model.MacPackage; -import jdk.jpackage.internal.model.MacPkgPackage; -import jdk.jpackage.internal.model.PackageType; -import jdk.jpackage.internal.model.RuntimeLayout; -import jdk.jpackage.internal.util.function.ExceptionBox; - - -final class MacFromParams { - - private static MacApplication createMacApplication( - Map params) throws ConfigException, IOException { - - final var predefinedRuntimeLayout = PREDEFINED_RUNTIME_IMAGE.findIn(params) - .map(MacPackage::guessRuntimeLayout); - - if (predefinedRuntimeLayout.isPresent()) { - validateRuntimeHasJliLib(predefinedRuntimeLayout.orElseThrow()); - if (APP_STORE.findIn(params).orElse(false)) { - validateRuntimeHasNoBinDir(predefinedRuntimeLayout.orElseThrow()); - } - } - - final var launcherFromParams = new LauncherFromParams(Optional.of(MacFromParams::createMacFa)); - - final var superAppBuilder = createApplicationBuilder(params, toFunction(launcherParams -> { - var launcher = launcherFromParams.create(launcherParams); - return MacLauncher.create(launcher); - }), (MacLauncher _, Launcher launcher) -> { - return MacLauncher.create(launcher); - }, APPLICATION_LAYOUT, RUNTIME_BUNDLE_LAYOUT, predefinedRuntimeLayout.map(RuntimeLayout::unresolve)); - - if (hasPredefinedAppImage(params)) { - // Set the main launcher start up info. - // AppImageFile assumes the main launcher start up info is available when - // it is constructed from Application instance. - // This happens when jpackage signs predefined app image. - final var mainLauncherStartupInfo = new MainLauncherStartupInfo(superAppBuilder.mainLauncherClassName().orElseThrow()); - final var launchers = superAppBuilder.launchers().orElseThrow(); - final var mainLauncher = ApplicationBuilder.overrideLauncherStartupInfo(launchers.mainLauncher(), mainLauncherStartupInfo); - superAppBuilder.launchers(new ApplicationLaunchers(MacLauncher.create(mainLauncher), launchers.additionalLaunchers())); - } - - final var app = superAppBuilder.create(); - - final var appBuilder = new MacApplicationBuilder(app); - - if (hasPredefinedAppImage(params)) { - appBuilder.externalInfoPlistFile(PREDEFINED_APP_IMAGE.findIn(params).map(MacBundle::new).orElseThrow().infoPlistFile()); - } - - ICON.copyInto(params, appBuilder::icon); - MAC_CF_BUNDLE_NAME.copyInto(params, appBuilder::bundleName); - MAC_CF_BUNDLE_IDENTIFIER.copyInto(params, appBuilder::bundleIdentifier); - APP_CATEGORY.copyInto(params, appBuilder::category); - - final boolean sign; - final boolean appStore; - - if (hasPredefinedAppImage(params) && PACKAGE_TYPE.findIn(params).filter(Predicate.isEqual("app-image")).isEmpty()) { - final var appImageFileExtras = new MacAppImageFileExtras(superAppBuilder.externalApplication().orElseThrow()); - sign = appImageFileExtras.signed(); - appStore = appImageFileExtras.appStore(); - } else { - sign = SIGN_BUNDLE.findIn(params).orElse(false); - appStore = APP_STORE.findIn(params).orElse(false); - } - - appBuilder.appStore(appStore); - - if (sign) { - final var signingIdentityBuilder = createSigningIdentityBuilder(params); - APP_IMAGE_SIGN_IDENTITY.copyInto(params, signingIdentityBuilder::signingIdentity); - SIGNING_KEY_USER.findIn(params).ifPresent(userName -> { - final StandardCertificateSelector domain; - if (appStore) { - domain = StandardCertificateSelector.APP_STORE_APP_IMAGE; - } else { - domain = StandardCertificateSelector.APP_IMAGE; - } - - signingIdentityBuilder.certificateSelector(StandardCertificateSelector.create(userName, domain)); - }); - - final var signingBuilder = new AppImageSigningConfigBuilder(signingIdentityBuilder); - if (appStore) { - signingBuilder.entitlementsResourceName("sandbox.plist"); - } - - final var bundleIdentifier = appBuilder.create().bundleIdentifier(); - app.mainLauncher().flatMap(Launcher::startupInfo).ifPresentOrElse( - signingBuilder::signingIdentifierPrefix, - () -> { - // Runtime installer does not have main launcher, so use - // 'bundleIdentifier' as prefix by default. - signingBuilder.signingIdentifierPrefix( - bundleIdentifier + "."); - }); - SIGN_IDENTIFIER_PREFIX.copyInto(params, signingBuilder::signingIdentifierPrefix); - - ENTITLEMENTS.copyInto(params, signingBuilder::entitlements); - - appBuilder.signingBuilder(signingBuilder); - } - - return appBuilder.create(); - } - - private static MacPackageBuilder createMacPackageBuilder( - Map params, MacApplication app, - PackageType type) throws ConfigException { - final var builder = new MacPackageBuilder(createPackageBuilder(params, app, type)); - - PREDEFINED_APP_IMAGE_FILE.findIn(params) - .map(MacAppImageFileExtras::new) - .map(MacAppImageFileExtras::signed) - .ifPresent(builder::predefinedAppImageSigned); - - PREDEFINED_RUNTIME_IMAGE.findIn(params) - .map(MacBundle::new) - .filter(MacBundle::isValid) - .map(MacBundle::isSigned) - .ifPresent(builder::predefinedAppImageSigned); - - return builder; - } - - private static MacDmgPackage createMacDmgPackage( - Map params) throws ConfigException, IOException { - - final var app = APPLICATION.fetchFrom(params); - - final var superPkgBuilder = createMacPackageBuilder(params, app, MAC_DMG); - - final var pkgBuilder = new MacDmgPackageBuilder(superPkgBuilder); - - DMG_CONTENT.copyInto(params, pkgBuilder::dmgContent); - - return pkgBuilder.create(); - } - - private record WithExpiredCertificateException(Optional obj, Optional certEx) { - WithExpiredCertificateException { - if (obj.isEmpty() == certEx.isEmpty()) { - throw new IllegalArgumentException(); - } - } - - static WithExpiredCertificateException of(Callable callable) { - try { - return new WithExpiredCertificateException<>(Optional.of(callable.call()), Optional.empty()); - } catch (ExpiredCertificateException ex) { - return new WithExpiredCertificateException<>(Optional.empty(), Optional.of(ex)); - } catch (ExceptionBox ex) { - if (ex.getCause() instanceof ExpiredCertificateException certEx) { - return new WithExpiredCertificateException<>(Optional.empty(), Optional.of(certEx)); - } - throw ex; - } catch (RuntimeException ex) { - throw ex; - } catch (Throwable t) { - throw ExceptionBox.rethrowUnchecked(t); - } - } - } - - private static MacPkgPackage createMacPkgPackage( - Map params) throws ConfigException, IOException { - - // This is over complicated to make "MacSignTest.testExpiredCertificate" test pass. - - final boolean sign = SIGN_BUNDLE.findIn(params).orElse(false); - final boolean appStore = APP_STORE.findIn(params).orElse(false); - - final var appOrExpiredCertEx = WithExpiredCertificateException.of(() -> { - return APPLICATION.fetchFrom(params); - }); - - final Optional pkgBuilder; - if (appOrExpiredCertEx.obj().isPresent()) { - final var superPkgBuilder = createMacPackageBuilder(params, appOrExpiredCertEx.obj().orElseThrow(), MAC_PKG); - pkgBuilder = Optional.of(new MacPkgPackageBuilder(superPkgBuilder)); - } else { - pkgBuilder = Optional.empty(); - } - - if (sign) { - final var signingIdentityBuilder = createSigningIdentityBuilder(params); - INSTALLER_SIGN_IDENTITY.copyInto(params, signingIdentityBuilder::signingIdentity); - SIGNING_KEY_USER.findIn(params).ifPresent(userName -> { - final StandardCertificateSelector domain; - if (appStore) { - domain = StandardCertificateSelector.APP_STORE_PKG_INSTALLER; - } else { - domain = StandardCertificateSelector.PKG_INSTALLER; - } - - signingIdentityBuilder.certificateSelector(StandardCertificateSelector.create(userName, domain)); - }); - - if (pkgBuilder.isPresent()) { - pkgBuilder.orElseThrow().signingBuilder(signingIdentityBuilder); - } else { - final var expiredPkgCert = WithExpiredCertificateException.of(() -> { - return signingIdentityBuilder.create(); - }).certEx(); - expiredPkgCert.map(ConfigException::getMessage).ifPresent(Log::error); - throw appOrExpiredCertEx.certEx().orElseThrow(); - } - } - - return pkgBuilder.orElseThrow().create(); - } - - private static SigningIdentityBuilder createSigningIdentityBuilder(Map params) { - final var builder = new SigningIdentityBuilder(); - SIGNING_KEYCHAIN.copyInto(params, builder::keychain); - return builder; - } - - private static MacFileAssociation createMacFa(FileAssociation fa, Map params) { - - final var builder = new MacFileAssociationBuilder(); - - FA_MAC_CFBUNDLETYPEROLE.copyInto(params, builder::cfBundleTypeRole); - FA_MAC_LSHANDLERRANK.copyInto(params, builder::lsHandlerRank); - FA_MAC_NSSTORETYPEKEY.copyInto(params, builder::nsPersistentStoreTypeKey); - FA_MAC_NSDOCUMENTCLASS.copyInto(params, builder::nsDocumentClass); - FA_MAC_LSTYPEISPACKAGE.copyInto(params, builder::lsTypeIsPackage); - FA_MAC_LSDOCINPLACE.copyInto(params, builder::lsSupportsOpeningDocumentsInPlace); - FA_MAC_UIDOCBROWSER.copyInto(params, builder::uiSupportsDocumentBrowser); - FA_MAC_NSEXPORTABLETYPES.copyInto(params, builder::nsExportableTypes); - FA_MAC_UTTYPECONFORMSTO.copyInto(params, builder::utTypeConformsTo); - - return toFunction(builder::create).apply(fa); - } - - static final BundlerParamInfo APPLICATION = createApplicationBundlerParam( - MacFromParams::createMacApplication); - - static final BundlerParamInfo DMG_PACKAGE = createPackageBundlerParam( - MacFromParams::createMacDmgPackage); - - static final BundlerParamInfo PKG_PACKAGE = createPackageBundlerParam( - MacFromParams::createMacPkgPackage); - - private static final BundlerParamInfo MAC_CF_BUNDLE_NAME = createStringBundlerParam( - Arguments.CLIOptions.MAC_BUNDLE_NAME.getId()); - - private static final BundlerParamInfo APP_CATEGORY = createStringBundlerParam( - Arguments.CLIOptions.MAC_CATEGORY.getId()); - - private static final BundlerParamInfo ENTITLEMENTS = createPathBundlerParam( - Arguments.CLIOptions.MAC_ENTITLEMENTS.getId()); - - private static final BundlerParamInfo MAC_CF_BUNDLE_IDENTIFIER = createStringBundlerParam( - Arguments.CLIOptions.MAC_BUNDLE_IDENTIFIER.getId()); - - private static final BundlerParamInfo SIGN_IDENTIFIER_PREFIX = createStringBundlerParam( - Arguments.CLIOptions.MAC_BUNDLE_SIGNING_PREFIX.getId()); - - private static final BundlerParamInfo APP_IMAGE_SIGN_IDENTITY = createStringBundlerParam( - Arguments.CLIOptions.MAC_APP_IMAGE_SIGN_IDENTITY.getId()); - - private static final BundlerParamInfo INSTALLER_SIGN_IDENTITY = createStringBundlerParam( - Arguments.CLIOptions.MAC_INSTALLER_SIGN_IDENTITY.getId()); - - private static final BundlerParamInfo SIGNING_KEY_USER = createStringBundlerParam( - Arguments.CLIOptions.MAC_SIGNING_KEY_NAME.getId()); - - private static final BundlerParamInfo SIGNING_KEYCHAIN = createStringBundlerParam( - Arguments.CLIOptions.MAC_SIGNING_KEYCHAIN.getId()); - - private static final BundlerParamInfo PACKAGE_TYPE = createStringBundlerParam( - Arguments.CLIOptions.PACKAGE_TYPE.getId()); - - private static final BundlerParamInfo APP_STORE = createBooleanBundlerParam( - Arguments.CLIOptions.MAC_APP_STORE.getId()); - - private static final BundlerParamInfo FA_MAC_CFBUNDLETYPEROLE = createStringBundlerParam( - Arguments.MAC_CFBUNDLETYPEROLE); - - private static final BundlerParamInfo FA_MAC_LSHANDLERRANK = createStringBundlerParam( - Arguments.MAC_LSHANDLERRANK); - - private static final BundlerParamInfo FA_MAC_NSSTORETYPEKEY = createStringBundlerParam( - Arguments.MAC_NSSTORETYPEKEY); - - private static final BundlerParamInfo FA_MAC_NSDOCUMENTCLASS = createStringBundlerParam( - Arguments.MAC_NSDOCUMENTCLASS); - - private static final BundlerParamInfo FA_MAC_LSTYPEISPACKAGE = createBooleanBundlerParam( - Arguments.MAC_LSTYPEISPACKAGE); - - private static final BundlerParamInfo FA_MAC_LSDOCINPLACE = createBooleanBundlerParam( - Arguments.MAC_LSDOCINPLACE); - - private static final BundlerParamInfo FA_MAC_UIDOCBROWSER = createBooleanBundlerParam( - Arguments.MAC_UIDOCBROWSER); - - @SuppressWarnings("unchecked") - private static final BundlerParamInfo> FA_MAC_NSEXPORTABLETYPES = - new BundlerParamInfo<>( - Arguments.MAC_NSEXPORTABLETYPES, - (Class>) (Object) List.class, - null, - (s, p) -> Arrays.asList(s.split("(,|\\s)+")) - ); - - @SuppressWarnings("unchecked") - private static final BundlerParamInfo> FA_MAC_UTTYPECONFORMSTO = - new BundlerParamInfo<>( - Arguments.MAC_UTTYPECONFORMSTO, - (Class>) (Object) List.class, - null, - (s, p) -> Arrays.asList(s.split("(,|\\s)+")) - ); -} 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 9576f6a6a99..e19f234d0d6 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackageBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackageBuilder.java @@ -29,7 +29,6 @@ import java.nio.file.Files; import java.util.Objects; -import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.MacApplication; import jdk.jpackage.internal.model.MacPackage; import jdk.jpackage.internal.model.MacPackageMixin; @@ -49,7 +48,7 @@ PackageBuilder pkgBuilder() { return pkgBuilder; } - MacPackage create() throws ConfigException { + MacPackage create() { final var app = (MacApplication)pkgBuilder.app(); 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 b82b20c0c36..53f297282ba 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java @@ -75,7 +75,6 @@ import jdk.jpackage.internal.model.MacPackage; import jdk.jpackage.internal.model.Package; import jdk.jpackage.internal.model.PackageType; -import jdk.jpackage.internal.model.PackagerException; import jdk.jpackage.internal.util.FileUtils; import jdk.jpackage.internal.util.PathUtils; import jdk.jpackage.internal.util.function.ThrowingConsumer; @@ -225,10 +224,12 @@ static Package createSignAppImagePackage(MacApplication app, BuildEnv env) { if (!app.sign()) { throw new IllegalArgumentException(); } - return toSupplier(() -> { - return new PackageBuilder(app, SignAppImagePackageType.VALUE).predefinedAppImage( - Objects.requireNonNull(env.appImageDir())).installDir(Path.of("/foo")).create(); - }).get(); + return new PackageBuilder( + app, + SignAppImagePackageType.VALUE + ).predefinedAppImage( + Objects.requireNonNull(env.appImageDir()) + ).installDir(Path.of("/foo")).create(); } static final class LayoutUtils { @@ -268,7 +269,7 @@ static AppImageBuildEnv fromPackag static AppImageTaskAction withBundleLayout(AppImageTaskAction action) { return new AppImageTaskAction<>() { @Override - public void execute(AppImageBuildEnv env) throws IOException, PackagerException { + public void execute(AppImageBuildEnv env) throws IOException { if (!env.envLayout().runtimeDirectory().getName(0).equals(Path.of("Contents"))) { env = LayoutUtils.fromPackagerLayout(env); } @@ -610,11 +611,20 @@ public boolean test(TaskID taskID) { } @Override - public void execute(TaskAction taskAction) throws IOException, PackagerException { + public void execute(TaskAction taskAction) throws IOException { delegate.execute(taskAction); } } + private static final ApplicationLayout MAC_APPLICATION_LAYOUT = ApplicationLayout.build() + .launchersDirectory("Contents/MacOS") + .appDirectory("Contents/app") + .runtimeDirectory("Contents/runtime/Contents/Home") + .desktopIntegrationDirectory("Contents/Resources") + .appModsDirectory("Contents/app/mods") + .contentDirectory("Contents") + .create(); + static final MacApplicationLayout APPLICATION_LAYOUT = MacApplicationLayout.create( - ApplicationLayoutUtils.PLATFORM_APPLICATION_LAYOUT, Path.of("Contents/runtime")); + MAC_APPLICATION_LAYOUT, Path.of("Contents/runtime")); } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java deleted file mode 100644 index e827f238db3..00000000000 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java +++ /dev/null @@ -1,99 +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. 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.nio.file.Path; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import jdk.jpackage.internal.model.ConfigException; -import jdk.jpackage.internal.model.MacPkgPackage; -import jdk.jpackage.internal.model.PackagerException; - -public class MacPkgBundler extends MacBaseInstallerBundler { - - @Override - public String getName() { - return I18N.getString("pkg.bundler.name"); - } - - @Override - public String getID() { - return "pkg"; - } - - @Override - public boolean validate(Map params) - throws ConfigException { - try { - Objects.requireNonNull(params); - - final var pkg = MacFromParams.PKG_PACKAGE.fetchFrom(params); - - // run basic validation to ensure requirements are met - // we are not interested in return code, only possible exception - validateAppImageAndBundeler(params); - - // hdiutil is always available so there's no need - // to test for availability. - - return true; - } catch (RuntimeException re) { - if (re.getCause() instanceof ConfigException) { - throw (ConfigException) re.getCause(); - } else { - throw new ConfigException(re); - } - } - } - - @Override - public Path execute(Map params, - Path outputParentDir) throws PackagerException { - - var pkg = MacFromParams.PKG_PACKAGE.fetchFrom(params); - - Log.verbose(I18N.format("message.building-pkg", pkg.app().name())); - - return Packager.build().outputDir(outputParentDir) - .pkg(pkg) - .env(MacBuildEnvFromParams.BUILD_ENV.fetchFrom(params)) - .pipelineBuilderMutatorFactory((env, _, outputDir) -> { - return new MacPkgPackager(env, pkg, outputDir); - }).execute(MacPackagingPipeline.build(Optional.of(pkg))); - } - - @Override - public boolean supported(boolean runtimeInstaller) { - return true; - } - - @Override - public boolean isDefault() { - return false; - } - -} diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackageBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackageBuilder.java index 663b8b16265..b81340354e1 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackageBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackageBuilder.java @@ -26,7 +26,6 @@ import java.util.Objects; import java.util.Optional; -import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.MacPkgPackage; import jdk.jpackage.internal.model.MacPkgPackageMixin; import jdk.jpackage.internal.model.PkgSigningConfig; @@ -42,20 +41,16 @@ MacPkgPackageBuilder signingBuilder(SigningIdentityBuilder v) { return this; } - MacPkgPackage create() throws ConfigException { + MacPkgPackage create() { var pkg = MacPkgPackage.create(pkgBuilder.create(), new MacPkgPackageMixin.Stub(createSigningConfig())); validatePredefinedAppImage(pkg); return pkg; } - private Optional createSigningConfig() throws ConfigException { - if (signingBuilder != null) { - return signingBuilder.create().map(cfg -> { - return new PkgSigningConfig.Stub(cfg.identity(), cfg.keychain().map(Keychain::name)); - }); - } else { - return Optional.empty(); - } + private Optional createSigningConfig() { + return Optional.ofNullable(signingBuilder).flatMap(SigningIdentityBuilder::create).map(cfg -> { + return new PkgSigningConfig.Stub(cfg.identity(), cfg.keychain().map(Keychain::name)); + }); } private static void validatePredefinedAppImage(MacPkgPackage pkg) { 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 e7ef6a23fa7..cfab12cbcba 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacRuntimeValidator.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacRuntimeValidator.java @@ -30,12 +30,11 @@ import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.function.Predicate; -import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.RuntimeLayout; final class MacRuntimeValidator { - static void validateRuntimeHasJliLib(RuntimeLayout runtimeLayout) throws ConfigException { + static void validateRuntimeHasJliLib(RuntimeLayout runtimeLayout) { final var jliName = Path.of("libjli.dylib"); try (var walk = Files.walk(runtimeLayout.runtimeDirectory().resolve("lib"))) { if (walk.map(Path::getFileName).anyMatch(Predicate.isEqual(jliName))) { @@ -51,7 +50,7 @@ static void validateRuntimeHasJliLib(RuntimeLayout runtimeLayout) throws ConfigE runtimeLayout.unresolve().runtimeDirectory().resolve("lib/**").resolve(jliName)).create(); } - static void validateRuntimeHasNoBinDir(RuntimeLayout runtimeLayout) throws ConfigException { + static void validateRuntimeHasNoBinDir(RuntimeLayout runtimeLayout) { if (Files.isDirectory(runtimeLayout.runtimeDirectory().resolve("bin"))) { throw I18N.buildConfigException() .message("error.invalid-runtime-image-bin-dir", runtimeLayout.rootDirectory()) diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/SigningIdentityBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/SigningIdentityBuilder.java index f90e76bb23d..9a57ce1e697 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/SigningIdentityBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/SigningIdentityBuilder.java @@ -82,7 +82,7 @@ SigningIdentityBuilder keychain(String v) { return this; } - Optional create() throws ConfigException { + Optional create() { if (signingIdentity == null && certificateSelector == null) { return Optional.empty(); } else { @@ -90,11 +90,11 @@ Optional create() throws ConfigException { } } - private Optional validatedKeychain() throws ConfigException { + private Optional validatedKeychain() { return Optional.ofNullable(keychain).map(Keychain::new); } - private SigningIdentity validatedSigningIdentity() throws ConfigException { + private SigningIdentity validatedSigningIdentity() { if (signingIdentity != null) { return new SigningIdentityImpl(signingIdentity); } @@ -142,7 +142,7 @@ private SigningIdentity validatedSigningIdentity() throws ConfigException { } private static X509Certificate selectSigningIdentity(List certs, - CertificateSelector certificateSelector, Optional keychain) throws ConfigException { + CertificateSelector certificateSelector, Optional keychain) { Objects.requireNonNull(certificateSelector); Objects.requireNonNull(keychain); switch (certs.size()) { diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacApplication.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacApplication.java index 04ab7042ac5..cfe10e8a012 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacApplication.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacApplication.java @@ -25,13 +25,18 @@ package jdk.jpackage.internal.model; import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toMap; +import static java.util.stream.Collectors.toUnmodifiableMap; +import static jdk.jpackage.internal.cli.StandardAppImageFileOption.MAC_APP_STORE; +import static jdk.jpackage.internal.cli.StandardAppImageFileOption.MAC_MAIN_CLASS; +import static jdk.jpackage.internal.cli.StandardAppImageFileOption.MAC_SIGNED; import java.nio.file.Path; import java.util.Map; +import java.util.Optional; import java.util.function.Function; import java.util.stream.IntStream; import java.util.stream.Stream; +import jdk.jpackage.internal.cli.OptionValue; import jdk.jpackage.internal.util.CompositeProxy; public interface MacApplication extends Application, MacApplicationMixin { @@ -76,7 +81,14 @@ default boolean sign() { @Override default Map extraAppImageFileData() { - return Stream.of(ExtraAppImageFileField.values()).collect(toMap(ExtraAppImageFileField::fieldName, x -> x.asString(this))); + return Stream.of(ExtraAppImageFileField.values()).map(field -> { + return field.findStringValue(this).map(value -> { + return Map.entry(field.fieldName(), value); + }); + }) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue)); } public static MacApplication create(Application app, MacApplicationMixin mixin) { @@ -84,23 +96,31 @@ public static MacApplication create(Application app, MacApplicationMixin mixin) } public enum ExtraAppImageFileField { - SIGNED("signed", app -> Boolean.toString(app.sign())), - APP_STORE("app-store", app -> Boolean.toString(app.appStore())); + SIGNED(MAC_SIGNED, app -> { + return Optional.of(Boolean.toString(app.sign())); + }), + APP_STORE(MAC_APP_STORE, app -> { + return Optional.of(Boolean.toString(app.appStore())); + }), + APP_CLASS(MAC_MAIN_CLASS, app -> { + return app.mainLauncher().flatMap(Launcher::startupInfo).map(LauncherStartupInfo::qualifiedClassName); + }), + ; - ExtraAppImageFileField(String fieldName, Function getter) { - this.fieldName = fieldName; + ExtraAppImageFileField(OptionValue option, Function> getter) { + this.fieldName = option.getName(); this.getter = getter; } - public String fieldName() { + String fieldName() { return fieldName; } - String asString(MacApplication app) { + Optional findStringValue(MacApplication app) { return getter.apply(app); } private final String fieldName; - private final Function getter; + private final Function> getter; } } 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 afa71d84d5c..d01297aa814 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 @@ -23,26 +23,17 @@ # questions. # # - -app.bundler.name=Mac Application Image -store.bundler.name=Mac App Store Ready Bundler -dmg.bundler.name=Mac DMG Package -pkg.bundler.name=Mac PKG Package - error.invalid-cfbundle-version.advice=Set a compatible 'app-version' value. Valid versions are one to three integers separated by dots. error.explicit-sign-no-cert=Signature explicitly requested but no signing certificate found error.explicit-sign-no-cert.advice=Specify a valid mac-signing-key-user-name and mac-signing-keychain -error.must-sign-app-store=Mac App Store apps must be signed, and signing has been disabled by bundler configuration -error.must-sign-app-store.advice=Use --mac-sign option with appropriate user-name and keychain -error.certificate.expired=Error: Certificate expired {0} +error.certificate.expired=Certificate expired {0} 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=Error: --mac-sign option is required with predefined application image and with type [app-image] -error.tool.failed.with.output=Error: "{0}" failed with following output: +error.app-image.mac-sign.required=--mac-sign option is required with predefined application image and with type [app-image] +error.tool.failed.with.output="{0}" failed with following output: error.invalid-runtime-image-missing-file=Runtime image "{0}" is missing "{1}" file 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 -resource.bundle-config-file=Bundle config file resource.app-info-plist=Application Info.plist resource.app-runtime-info-plist=Embedded Java Runtime Info.plist resource.runtime-info-plist=Java Runtime Info.plist @@ -60,21 +51,15 @@ resource.pkg-background-image=pkg background image resource.pkg-pdf=project definition file resource.launchd-plist-file=launchd plist file - message.bundle-name-too-long-warning={0} is set to ''{1}'', which is longer than 16 characters. For a better Mac experience consider shortening it. message.preparing-info-plist=Preparing Info.plist: {0}. message.icon-not-icns= The specified icon "{0}" is not an ICNS file and will not be used. The default icon will be used in it's place. message.version-string-too-many-components='app-version' may have between 1 and 3 numbers: 1, 1.2, 1.2.3. message.version-string-first-number-not-zero=The first number in an app-version cannot be zero or negative. -message.creating-association-with-null-extension=Creating association with null extension. -message.ignoring.symlink=Warning: codesign is skipping the symlink {0}. -message.already.signed=File already signed: {0}. -message.keychain.error=Error: unable to get keychain list. -message.building-bundle=Building Mac App Store Package for {0}. -message.invalid-identifier=invalid mac bundle identifier [{0}]. +message.keychain.error=Unable to get keychain list. +message.invalid-identifier=Invalid mac bundle identifier [{0}]. message.invalid-identifier.advice=specify identifier with "--mac-package-identifier". message.building-dmg=Building DMG package for {0}. -message.running-script=Running shell script on application image [{0}]. message.preparing-dmg-setup=Preparing dmg setup: {0}. message.creating-dmg-file=Creating DMG file: {0}. message.dmg-cannot-be-overwritten=Dmg file exists [{0}] and can not be removed. diff --git a/src/jdk.jpackage/macosx/classes/module-info.java.extra b/src/jdk.jpackage/macosx/classes/module-info.java.extra index 1496167cd4a..e6202dd1156 100644 --- a/src/jdk.jpackage/macosx/classes/module-info.java.extra +++ b/src/jdk.jpackage/macosx/classes/module-info.java.extra @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -23,8 +23,5 @@ * questions. */ -provides jdk.jpackage.internal.Bundler with - jdk.jpackage.internal.MacAppBundler, - jdk.jpackage.internal.MacDmgBundler, - jdk.jpackage.internal.MacPkgBundler; - +provides jdk.jpackage.internal.cli.CliBundlingEnvironment with + jdk.jpackage.internal.MacBundlingEnvironment; diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AddLauncherArguments.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AddLauncherArguments.java deleted file mode 100644 index 93d037c6a45..00000000000 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AddLauncherArguments.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (c) 2018, 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. 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.nio.file.Path; -import java.util.HashMap; -import java.util.Map; -import java.util.List; -import java.util.Optional; - -import jdk.internal.util.OperatingSystem; - -import jdk.jpackage.internal.Arguments.CLIOptions; -import static jdk.jpackage.internal.StandardBundlerParam.LAUNCHER_DATA; -import static jdk.jpackage.internal.StandardBundlerParam.APP_NAME; - -/* - * AddLauncherArguments - * - * Processes a add-launcher properties file to create the Map of - * bundle params applicable to the add-launcher: - * - * BundlerParams p = (new AddLauncherArguments(file)).getLauncherMap(); - * - * A add-launcher is another executable program generated by either the - * create-app-image mode or the create-installer mode. - * The add-launcher may be the same program with different configuration, - * or a completely different program created from the same files. - * - * There may be multiple add-launchers, each created by using the - * command line arg "--add-launcher - * - * The add-launcher properties file may have any of: - * - * appVersion - * description - * module - * main-jar - * main-class - * icon - * arguments - * java-options - * launcher-as-service - * win-console - * win-shortcut - * win-menu - * linux-app-category - * linux-shortcut - * - */ -class AddLauncherArguments { - - private final String name; - private final String filename; - private Map allArgs; - private Map bundleParams; - - AddLauncherArguments(String name, String filename) { - this.name = name; - this.filename = filename; - } - - private void initLauncherMap() { - if (bundleParams != null) { - return; - } - - allArgs = Arguments.getPropertiesFromFile(filename); - allArgs.put(CLIOptions.NAME.getId(), name); - - bundleParams = new HashMap<>(); - String mainJar = getOptionValue(CLIOptions.MAIN_JAR); - String mainClass = getOptionValue(CLIOptions.APPCLASS); - String module = getOptionValue(CLIOptions.MODULE); - - if (module != null && mainClass != null) { - Arguments.putUnlessNull(bundleParams, CLIOptions.MODULE.getId(), - module + "/" + mainClass); - } else if (module != null) { - Arguments.putUnlessNull(bundleParams, CLIOptions.MODULE.getId(), - module); - } else { - Arguments.putUnlessNull(bundleParams, CLIOptions.MAIN_JAR.getId(), - mainJar); - Arguments.putUnlessNull(bundleParams, CLIOptions.APPCLASS.getId(), - mainClass); - } - - Arguments.putUnlessNull(bundleParams, CLIOptions.NAME.getId(), - getOptionValue(CLIOptions.NAME)); - - Arguments.putUnlessNull(bundleParams, CLIOptions.VERSION.getId(), - getOptionValue(CLIOptions.VERSION)); - - Arguments.putUnlessNull(bundleParams, CLIOptions.DESCRIPTION.getId(), - getOptionValue(CLIOptions.DESCRIPTION)); - - Arguments.putUnlessNull(bundleParams, CLIOptions.RELEASE.getId(), - getOptionValue(CLIOptions.RELEASE)); - - Arguments.putUnlessNull(bundleParams, CLIOptions.ICON.getId(), - Optional.ofNullable(getOptionValue(CLIOptions.ICON)).map( - Path::of).orElse(null)); - - Arguments.putUnlessNull(bundleParams, - CLIOptions.LAUNCHER_AS_SERVICE.getId(), getOptionValue( - CLIOptions.LAUNCHER_AS_SERVICE)); - - if (OperatingSystem.isWindows()) { - Arguments.putUnlessNull(bundleParams, - CLIOptions.WIN_CONSOLE_HINT.getId(), - getOptionValue(CLIOptions.WIN_CONSOLE_HINT)); - Arguments.putUnlessNull(bundleParams, CLIOptions.WIN_SHORTCUT_HINT.getId(), - getOptionValue(CLIOptions.WIN_SHORTCUT_HINT)); - Arguments.putUnlessNull(bundleParams, CLIOptions.WIN_MENU_HINT.getId(), - getOptionValue(CLIOptions.WIN_MENU_HINT)); - } - - if (OperatingSystem.isLinux()) { - Arguments.putUnlessNull(bundleParams, CLIOptions.LINUX_CATEGORY.getId(), - getOptionValue(CLIOptions.LINUX_CATEGORY)); - Arguments.putUnlessNull(bundleParams, CLIOptions.LINUX_SHORTCUT_HINT.getId(), - getOptionValue(CLIOptions.LINUX_SHORTCUT_HINT)); - } - - // "arguments" and "java-options" even if value is null: - if (allArgs.containsKey(CLIOptions.ARGUMENTS.getId())) { - String argumentStr = getOptionValue(CLIOptions.ARGUMENTS); - bundleParams.put(CLIOptions.ARGUMENTS.getId(), - Arguments.getArgumentList(argumentStr)); - } - - if (allArgs.containsKey(CLIOptions.JAVA_OPTIONS.getId())) { - String jvmargsStr = getOptionValue(CLIOptions.JAVA_OPTIONS); - bundleParams.put(CLIOptions.JAVA_OPTIONS.getId(), - Arguments.getArgumentList(jvmargsStr)); - } - } - - private String getOptionValue(CLIOptions option) { - if (option == null || allArgs == null) { - return null; - } - - String id = option.getId(); - - if (allArgs.containsKey(id)) { - return allArgs.get(id); - } - - return null; - } - - Map getLauncherMap() { - initLauncherMap(); - return bundleParams; - } - - static Map merge( - Map original, - Map additional, String... exclude) { - Map tmp = new HashMap<>(original); - List.of(exclude).forEach(tmp::remove); - - // remove LauncherData from map so it will be re-computed - tmp.remove(LAUNCHER_DATA.getID()); - // remove "application-name" so it will be re-computed - tmp.remove(APP_NAME.getID()); - - if (additional.containsKey(CLIOptions.MODULE.getId())) { - tmp.remove(CLIOptions.MAIN_JAR.getId()); - tmp.remove(CLIOptions.APPCLASS.getId()); - } else if (additional.containsKey(CLIOptions.MAIN_JAR.getId())) { - tmp.remove(CLIOptions.MODULE.getId()); - } - if (additional.containsKey(CLIOptions.ARGUMENTS.getId())) { - // if add launcher properties file contains "arguments", even with - // null value, disregard the "arguments" from command line - tmp.remove(CLIOptions.ARGUMENTS.getId()); - } - if (additional.containsKey(CLIOptions.JAVA_OPTIONS.getId())) { - // same thing for java-options - tmp.remove(CLIOptions.JAVA_OPTIONS.getId()); - } - tmp.putAll(additional); - return tmp; - } - -} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageBundler.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageBundler.java deleted file mode 100644 index 192630a5656..00000000000 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageBundler.java +++ /dev/null @@ -1,168 +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. 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 static jdk.jpackage.internal.StandardBundlerParam.APP_NAME; -import static jdk.jpackage.internal.StandardBundlerParam.LAUNCHER_DATA; -import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_APP_IMAGE; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.text.MessageFormat; -import java.util.Map; -import java.util.Objects; -import jdk.internal.util.OperatingSystem; -import jdk.jpackage.internal.model.ConfigException; -import jdk.jpackage.internal.model.PackagerException; - - -class AppImageBundler extends AbstractBundler { - - @Override - public final String getName() { - return I18N.getString("app.bundler.name"); - } - - @Override - public final String getID() { - return "app"; - } - - @Override - public final String getBundleType() { - return "IMAGE"; - } - - @Override - public final boolean validate(Map params) - throws ConfigException { - try { - Objects.requireNonNull(params); - - if (!params.containsKey(PREDEFINED_APP_IMAGE.getID()) - && !StandardBundlerParam.isRuntimeInstaller(params)) { - LAUNCHER_DATA.fetchFrom(params); - } - - if (paramsValidator != null) { - paramsValidator.validate(params); - } - } catch (RuntimeException re) { - if (re.getCause() instanceof ConfigException) { - throw (ConfigException) re.getCause(); - } else { - throw new ConfigException(re); - } - } - - return true; - } - - @Override - public final Path execute(Map params, - Path outputParentDir) throws PackagerException { - - final var predefinedAppImage = PREDEFINED_APP_IMAGE.fetchFrom(params); - - try { - if (predefinedAppImage == null) { - Path rootDirectory = createRoot(params, outputParentDir); - appImageSupplier.prepareApplicationFiles(params, rootDirectory); - return rootDirectory; - } else { - appImageSupplier.prepareApplicationFiles(params, predefinedAppImage); - return predefinedAppImage; - } - } catch (PackagerException pe) { - throw pe; - } catch (RuntimeException|IOException ex) { - Log.verbose(ex); - throw new PackagerException(ex); - } - } - - @Override - public final boolean supported(boolean runtimeInstaller) { - return true; - } - - @Override - public final boolean isDefault() { - return false; - } - - @FunctionalInterface - static interface AppImageSupplier { - - void prepareApplicationFiles(Map params, - Path root) throws PackagerException, IOException; - } - - final AppImageBundler setAppImageSupplier(AppImageSupplier v) { - appImageSupplier = v; - return this; - } - - final AppImageBundler setParamsValidator(ParamsValidator v) { - paramsValidator = v; - return this; - } - - @FunctionalInterface - interface ParamsValidator { - void validate(Map params) throws ConfigException; - } - - private Path createRoot(Map params, - Path outputDirectory) throws PackagerException, IOException { - - IOUtils.writableOutputDir(outputDirectory); - - String imageName = APP_NAME.fetchFrom(params); - if (OperatingSystem.isMacOS()) { - imageName = imageName + ".app"; - } - - Log.verbose(MessageFormat.format( - I18N.getString("message.creating-app-bundle"), - imageName, outputDirectory.toAbsolutePath())); - - // Create directory structure - Path rootDirectory = outputDirectory.resolve(imageName); - if (Files.exists(rootDirectory)) { - throw new PackagerException("error.root-exists", - rootDirectory.toAbsolutePath().toString()); - } - - Files.createDirectories(rootDirectory); - - return rootDirectory; - } - - private ParamsValidator paramsValidator; - private AppImageSupplier appImageSupplier; -} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java index 8b8a22edc56..b6b4322302e 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java @@ -25,148 +25,149 @@ package jdk.jpackage.internal; import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toMap; -import static java.util.stream.Collectors.toSet; +import static java.util.stream.Collectors.toUnmodifiableMap; +import static java.util.stream.Collectors.toUnmodifiableSet; +import static jdk.jpackage.internal.cli.StandardAppImageFileOption.APP_VERSION; +import static jdk.jpackage.internal.cli.StandardAppImageFileOption.LAUNCHER_AS_SERVICE; +import static jdk.jpackage.internal.cli.StandardAppImageFileOption.DESCRIPTION; +import static jdk.jpackage.internal.cli.StandardAppImageFileOption.LAUNCHER_NAME; import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import java.io.IOException; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Objects; -import java.util.Optional; import java.util.Set; import java.util.stream.Stream; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.cli.OptionValue; +import jdk.jpackage.internal.cli.Options; +import jdk.jpackage.internal.cli.StandardAppImageFileOption.AppImageFileOptionScope; +import jdk.jpackage.internal.cli.StandardAppImageFileOption.InvalidOptionValueException; +import jdk.jpackage.internal.cli.StandardAppImageFileOption.MissingMandatoryOptionException; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ApplicationLayout; -import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.ExternalApplication; +import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.util.XmlUtils; +import jdk.jpackage.internal.util.function.ExceptionBox; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.SAXException; -final class AppImageFile implements ExternalApplication { +final class AppImageFile { AppImageFile(Application app) { - this(new ApplicationData(app)); + appVersion = Objects.requireNonNull(app.version()); + extra = Objects.requireNonNull(app.extraAppImageFileData()); + launcherInfos = app.launchers().stream().map(LauncherInfo::new).toList(); } - private AppImageFile(ApplicationData app) { - - appVersion = app.version(); - launcherName = app.mainLauncherName(); - mainClass = app.mainLauncherMainClassName(); - extra = app.extra; - creatorVersion = getVersion(); - creatorPlatform = getPlatform(); - addLauncherInfos = app.additionalLaunchers; - } - - @Override - public List getAddLaunchers() { - return addLauncherInfos; - } - - @Override - public String getAppVersion() { - return appVersion; - } - - @Override - public String getAppName() { - return launcherName; - } - - @Override - public String getLauncherName() { - return launcherName; - } - - @Override - public String getMainClass() { - return mainClass; - } - - @Override - public Map getExtra() { - return extra; + /** + * Writes the values captured in this instance into the application image info + * file in the given application layout. + *

+ * It is an equivalent to calling + * {@link #save(ApplicationLayout, OperatingSystem)} method with + * {@code OperatingSystem.current()} for the second parameter. + * + * @param appLayout the application layout + * @throws IOException if an I/O error occurs when writing + */ + void save(ApplicationLayout appLayout) throws IOException { + save(appLayout, OperatingSystem.current()); } /** - * Saves file with application image info in application image using values - * from this instance. + * Writes the values captured in this instance into the application image info + * file in the given application layout. + * + * @param appLayout the application layout + * @param os the target OS + * @throws IOException if an I/O error occurs when writing */ - void save(ApplicationLayout appLayout) throws IOException { + void save(ApplicationLayout appLayout, OperatingSystem os) throws IOException { XmlUtils.createXml(getPathInAppImage(appLayout), xml -> { xml.writeStartElement("jpackage-state"); - xml.writeAttribute("version", creatorVersion); - xml.writeAttribute("platform", creatorPlatform); + xml.writeAttribute("version", getVersion()); + xml.writeAttribute("platform", getPlatform(os)); xml.writeStartElement("app-version"); xml.writeCharacters(appVersion); xml.writeEndElement(); - xml.writeStartElement("main-launcher"); - xml.writeCharacters(launcherName); - xml.writeEndElement(); - - xml.writeStartElement("main-class"); - xml.writeCharacters(mainClass); - xml.writeEndElement(); - for (var extraKey : extra.keySet().stream().sorted().toList()) { xml.writeStartElement(extraKey); xml.writeCharacters(extra.get(extraKey)); xml.writeEndElement(); } - for (var li : addLauncherInfos) { - xml.writeStartElement("add-launcher"); - xml.writeAttribute("name", li.name()); - xml.writeAttribute("service", Boolean.toString(li.service())); - for (var extraKey : li.extra().keySet().stream().sorted().toList()) { - xml.writeStartElement(extraKey); - xml.writeCharacters(li.extra().get(extraKey)); - xml.writeEndElement(); - } - xml.writeEndElement(); + launcherInfos.getFirst().save(xml, "main-launcher"); + + for (var li : launcherInfos.subList(1, launcherInfos.size())) { + li.save(xml, "add-launcher"); } }); } /** - * Returns path to application image info file. - * @param appLayout - application layout + * Returns the path to the application image info file in the given application layout. + * + * @param appLayout the application layout */ static Path getPathInAppImage(ApplicationLayout appLayout) { return appLayout.appDirectory().resolve(FILENAME); } /** - * Loads application image info from application image. - * @param appImageDir - path at which to resolve the given application layout - * @param appLayout - application layout + * Loads application image info from the specified application layout. + *

+ * It is an equivalent to calling + * {@link #load(ApplicationLayout, OperatingSystem)} method with + * {@code OperatingSystem.current()} for the second parameter. + * + * @param appLayout the application layout + */ + static ExternalApplication load(ApplicationLayout appLayout) { + return load(appLayout, OperatingSystem.current()); + } + + /** + * Loads application image info from the specified application layout and OS. + * + * @param appLayout the application layout + * @param os the OS defining extra properties of the application and + * additional launchers */ - static AppImageFile load(Path appImageDir, ApplicationLayout appLayout) throws ConfigException, IOException { - var srcFilePath = getPathInAppImage(appLayout.resolveAt(appImageDir)); + static ExternalApplication load(ApplicationLayout appLayout, OperatingSystem os) { + Objects.requireNonNull(appLayout); + Objects.requireNonNull(os); + + final var appImageDir = appLayout.rootDirectory(); + final var appImageFilePath = getPathInAppImage(appLayout); + final var relativeAppImageFilePath = appImageDir.relativize(appImageFilePath); + try { - final Document doc = XmlUtils.initDocumentBuilder().parse(Files.newInputStream(srcFilePath)); + final Document doc = XmlUtils.initDocumentBuilder().parse(Files.newInputStream(appImageFilePath)); final XPath xPath = XPathFactory.newInstance().newXPath(); final var isPlatformValid = XmlUtils.queryNodes(doc, xPath, "/jpackage-state/@platform").findFirst().map( - Node::getNodeValue).map(getPlatform()::equals).orElse(false); + Node::getNodeValue).map(getPlatform(os)::equals).orElse(false); if (!isPlatformValid) { throw new InvalidAppImageFileException(); } @@ -177,102 +178,73 @@ static AppImageFile load(Path appImageDir, ApplicationLayout appLayout) throws C throw new InvalidAppImageFileException(); } - final AppImageProperties props; - try { - props = AppImageProperties.main(doc, xPath); - } catch (IllegalArgumentException ex) { - throw new InvalidAppImageFileException(ex); - } + final var appOptions = AppImageFileOptionScope.APP.parse(appImageFilePath, AppImageProperties.main(doc, xPath), os); + + final var mainLauncherOptions = LauncherElement.MAIN.readAll(doc, xPath).stream().reduce((_, second) -> { + return second; + }).map(launcherProps -> { + return AppImageFileOptionScope.LAUNCHER.parse(appImageFilePath, launcherProps, os); + }).orElseThrow(InvalidAppImageFileException::new); - final var additionalLaunchers = AppImageProperties.launchers(doc, xPath).stream().map(launcherProps -> { - try { - return new LauncherInfo(launcherProps.get("name"), - launcherProps.find("service").map(Boolean::parseBoolean).orElse(false), launcherProps.getExtra()); - } catch (IllegalArgumentException ex) { - throw new InvalidAppImageFileException(ex); - } + final var addLauncherOptions = LauncherElement.ADDITIONAL.readAll(doc, xPath).stream().map(launcherProps -> { + return AppImageFileOptionScope.LAUNCHER.parse(appImageFilePath, launcherProps, os); }).toList(); - return new AppImageFile(new ApplicationData(props.get("app-version"), props.get("main-launcher"), - props.get("main-class"), props.getExtra(), additionalLaunchers)); + try { + return ExternalApplication.create(Options.concat(appOptions, mainLauncherOptions), addLauncherOptions, os); + } catch (NoSuchElementException ex) { + throw new InvalidAppImageFileException(ex); + } } catch (XPathExpressionException ex) { // This should never happen as XPath expressions should be correct - throw new RuntimeException(ex); + throw ExceptionBox.rethrowUnchecked(ex); } catch (SAXException ex) { - // Exception reading input XML (probably malformed XML) - throw new IOException(ex); + // Malformed input XML + throw new JPackageException(I18N.format("error.malformed-app-image-file", relativeAppImageFilePath, appImageDir), ex); } catch (NoSuchFileException ex) { - throw I18N.buildConfigException("error.foreign-app-image", appImageDir).create(); - } catch (InvalidAppImageFileException ex) { + // Don't save the original exception as its error message is redundant. + throw new JPackageException(I18N.format("error.missing-app-image-file", relativeAppImageFilePath, appImageDir)); + } catch (InvalidAppImageFileException|InvalidOptionValueException|MissingMandatoryOptionException ex) { // Invalid input XML - throw I18N.buildConfigException("error.invalid-app-image", appImageDir, srcFilePath).create(); + throw new JPackageException(I18N.format("error.invalid-app-image-file", relativeAppImageFilePath, appImageDir), ex); + } catch (IOException ex) { + throw new JPackageException(I18N.format("error.reading-app-image-file", relativeAppImageFilePath, appImageDir), ex); } } - static boolean getBooleanExtraFieldValue(String fieldId, ExternalApplication appImageFile) { - Objects.requireNonNull(fieldId); - Objects.requireNonNull(appImageFile); - return Optional.ofNullable(appImageFile.getExtra().get(fieldId)).map(Boolean::parseBoolean).orElse(false); - } - static String getVersion() { return System.getProperty("java.version"); } - static String getPlatform() { - return PLATFORM_LABELS.get(OperatingSystem.current()); + static String getPlatform(OperatingSystem os) { + return Objects.requireNonNull(PLATFORM_LABELS.get(Objects.requireNonNull(os))); } + private static final class AppImageProperties { - private AppImageProperties(Map data, Set stdKeys) { - this.data = data; - this.stdKeys = stdKeys; - } - static AppImageProperties main(Document xml, XPath xPath) throws XPathExpressionException { - final var data = queryProperties(xml.getDocumentElement(), xPath, MAIN_PROPERTIES_XPATH_QUERY); - return new AppImageProperties(data, MAIN_ELEMENT_NAMES); + static Map main(Document xml, XPath xPath) throws XPathExpressionException { + return queryProperties(xml.getDocumentElement(), xPath, MAIN_PROPERTIES_XPATH_QUERY); } - static AppImageProperties launcher(Element addLauncherNode, XPath xPath) throws XPathExpressionException { - final var attrData = XmlUtils.toStream(addLauncherNode.getAttributes()) - .collect(toMap(Node::getNodeName, Node::getNodeValue)); + static Map launcher(Element launcherNode, XPath xPath) throws XPathExpressionException { + final var attrData = XmlUtils.toStream(launcherNode.getAttributes()) + .collect(toUnmodifiableMap(Node::getNodeName, Node::getNodeValue)); - final var extraData = queryProperties(addLauncherNode, xPath, LAUNCHER_PROPERTIES_XPATH_QUERY); + final var extraData = queryProperties(launcherNode, xPath, LAUNCHER_PROPERTIES_XPATH_QUERY); final Map data = new HashMap<>(attrData); data.putAll(extraData); - return new AppImageProperties(data, LAUNCHER_ATTR_NAMES); - } - - static List launchers(Document xml, XPath xPath) throws XPathExpressionException { - return XmlUtils.queryNodes(xml, xPath, "/jpackage-state/add-launcher") - .map(Element.class::cast).map(toFunction(e -> { - return launcher(e, xPath); - })).toList(); - } - - String get(String name) { - return find(name).orElseThrow(InvalidAppImageFileException::new); - } - - Optional find(String name) { - return Optional.ofNullable(data.get(name)); - } - - Map getExtra() { - Map extra = new HashMap<>(data); - stdKeys.forEach(extra::remove); - return extra; + return data; } private static Map queryProperties(Element e, XPath xPath, String xpathExpr) throws XPathExpressionException { return XmlUtils.queryNodes(e, xPath, xpathExpr) .map(Element.class::cast) - .collect(toMap(Node::getNodeName, selectedElement -> { + .collect(toUnmodifiableMap(Node::getNodeName, selectedElement -> { return selectedElement.getTextContent(); }, (a, b) -> b)); } @@ -285,13 +257,14 @@ private static String xpathQueryForExtraProperties(Set excludeNames) { return String.format("*[(%s) and not(*)]", otherElementNames); } - private final Map data; - private final Set stdKeys; - - private static final Set LAUNCHER_ATTR_NAMES = Set.of("name", "service"); + private static final Set LAUNCHER_ATTR_NAMES = Stream.of( + LAUNCHER_NAME + ).map(OptionValue::getName).collect(toUnmodifiableSet()); private static final String LAUNCHER_PROPERTIES_XPATH_QUERY = xpathQueryForExtraProperties(LAUNCHER_ATTR_NAMES); - private static final Set MAIN_ELEMENT_NAMES = Set.of("app-version", "main-launcher", "main-class"); + private static final Set MAIN_ELEMENT_NAMES = Stream.of( + APP_VERSION + ).map(OptionValue::getName).collect(toUnmodifiableSet()); private static final String MAIN_PROPERTIES_XPATH_QUERY; static { @@ -301,40 +274,65 @@ private static String xpathQueryForExtraProperties(Set excludeNames) { MAIN_PROPERTIES_XPATH_QUERY = String.format("%s|/jpackage-state/%s", nonEmptyMainElements, xpathQueryForExtraProperties(Stream.concat(MAIN_ELEMENT_NAMES.stream(), - Stream.of("add-launcher")).collect(toSet()))); + Stream.of("main-launcher", "add-launcher")).collect(toUnmodifiableSet()))); } } - private record ApplicationData(String version, String mainLauncherName, String mainLauncherMainClassName, - Map extra, List additionalLaunchers) { - ApplicationData { - Objects.requireNonNull(version); - Objects.requireNonNull(mainLauncherName); - Objects.requireNonNull(mainLauncherMainClassName); - Objects.requireNonNull(extra); - Objects.requireNonNull(additionalLaunchers); + private enum LauncherElement { + MAIN("main-launcher"), + ADDITIONAL("add-launcher"); - for (final var property : List.of(version, mainLauncherName, mainLauncherMainClassName)) { - if (property.isBlank()) { - throw new IllegalArgumentException(); - } - } + LauncherElement(String elementName) { + this.elementName = Objects.requireNonNull(elementName); } - ApplicationData(Application app) { - this(app, app.mainLauncher().orElseThrow()); + List> readAll(Document xml, XPath xPath) throws XPathExpressionException { + return XmlUtils.queryNodes(xml, xPath, "/jpackage-state/" + elementName + "[@name]") + .map(Element.class::cast).map(toFunction(e -> { + return AppImageProperties.launcher(e, xPath); + })).toList(); } - private ApplicationData(Application app, Launcher mainLauncher) { - this(app.version(), mainLauncher.name(), mainLauncher.startupInfo().orElseThrow().qualifiedClassName(), - app.extraAppImageFileData(), app.additionalLaunchers().stream().map(launcher -> { - return new LauncherInfo(launcher.name(), launcher.isService(), - launcher.extraAppImageFileData()); - }).toList()); + private final String elementName; + } + + private record LauncherInfo(String name, Map properties) { + LauncherInfo { + Objects.requireNonNull(name); + Objects.requireNonNull(properties); + } + + LauncherInfo(Launcher launcher) { + this(launcher.name(), properties(launcher)); + } + + void save(XMLStreamWriter xml, String elementName) throws IOException, XMLStreamException { + xml.writeStartElement(elementName); + xml.writeAttribute("name", name()); + for (var key : properties().keySet().stream().sorted().toList()) { + xml.writeStartElement(key); + xml.writeCharacters(properties().get(key)); + xml.writeEndElement(); + } + xml.writeEndElement(); + } + + private static Map properties(Launcher launcher) { + List> standardProps = new ArrayList<>(); + if (launcher.isService()) { + standardProps.add(Map.entry(LAUNCHER_AS_SERVICE.getName(), Boolean.TRUE.toString())); + } + standardProps.add(Map.entry(DESCRIPTION.getName(), launcher.description())); + + return Stream.concat( + standardProps.stream(), + launcher.extraAppImageFileData().entrySet().stream() + ).collect(toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue)); } } + private static class InvalidAppImageFileException extends RuntimeException { InvalidAppImageFileException() { @@ -347,13 +345,10 @@ private static class InvalidAppImageFileException extends RuntimeException { private static final long serialVersionUID = 1L; } + private final String appVersion; - private final String launcherName; - private final String mainClass; private final Map extra; - private final List addLauncherInfos; - private final String creatorVersion; - private final String creatorPlatform; + private final List launcherInfos; private static final String FILENAME = ".jpackage.xml"; 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 76a5fc1a50c..6896668ffdc 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java @@ -38,9 +38,7 @@ import jdk.jpackage.internal.model.AppImageLayout; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ApplicationLaunchers; -import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.ExternalApplication; -import jdk.jpackage.internal.model.ExternalApplication.LauncherInfo; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.LauncherIcon; import jdk.jpackage.internal.model.LauncherStartupInfo; @@ -49,28 +47,17 @@ final class ApplicationBuilder { - Application create() throws ConfigException { + Application create() { Objects.requireNonNull(appImageLayout); final var launchersAsList = Optional.ofNullable(launchers).map( ApplicationLaunchers::asList).orElseGet(List::of); - final var launcherCount = launchersAsList.size(); - - if (launcherCount != launchersAsList.stream().map(Launcher::name).distinct().count()) { - throw buildConfigException("ERR_NoUniqueName").create(); - } - - final String effectiveName; - if (name != null) { - effectiveName = name; - } else if (!launchersAsList.isEmpty()) { - effectiveName = launchers.mainLauncher().name(); - } else { - throw buildConfigException("error.no.name").advice("error.no.name.advice").create(); - } - - Objects.requireNonNull(launchersAsList); + final String effectiveName = Optional.ofNullable(name).or(() -> { + return Optional.ofNullable(launchers).map(ApplicationLaunchers::mainLauncher).map(Launcher::name); + }).orElseThrow(() -> { + return buildConfigException("error.no.name").advice("error.no.name.advice").create(); + }); return new Application.Stub( effectiveName, @@ -80,7 +67,10 @@ Application create() throws ConfigException { Optional.ofNullable(copyright).orElseGet(DEFAULTS::copyright), Optional.ofNullable(srcDir), Optional.ofNullable(contentDirs).orElseGet(List::of), - appImageLayout, Optional.ofNullable(runtimeBuilder), launchersAsList, Map.of()); + appImageLayout, + Optional.ofNullable(runtimeBuilder), + launchersAsList, + Map.of()); } ApplicationBuilder runtimeBuilder(RuntimeBuilder v) { @@ -88,25 +78,8 @@ ApplicationBuilder runtimeBuilder(RuntimeBuilder v) { return this; } - ApplicationBuilder initFromExternalApplication(ExternalApplication app, - Function mapper) { - - externalApp = Objects.requireNonNull(app); - - if (version == null) { - version = app.getAppVersion(); - } - if (name == null) { - name = app.getAppName(); - } - runtimeBuilder = null; - - var mainLauncherInfo = new LauncherInfo(app.getLauncherName(), false, Map.of()); - - launchers = new ApplicationLaunchers( - mapper.apply(mainLauncherInfo), - app.getAddLaunchers().stream().map(mapper).toList()); - + ApplicationBuilder externalApplication(ExternalApplication v) { + externalApp = v; return this; } @@ -123,15 +96,6 @@ Optional externalApplication() { return Optional.ofNullable(externalApp); } - Optional mainLauncherClassName() { - return launchers() - .map(ApplicationLaunchers::mainLauncher) - .flatMap(Launcher::startupInfo) - .map(LauncherStartupInfo::qualifiedClassName).or(() -> { - return externalApplication().map(ExternalApplication::getMainClass); - }); - } - ApplicationBuilder appImageLayout(AppImageLayout v) { appImageLayout = v; return this; diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationLayoutUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationLayoutUtils.java deleted file mode 100644 index 0ea72ae160c..00000000000 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationLayoutUtils.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.jpackage.internal; - -import java.nio.file.Path; -import jdk.internal.util.OperatingSystem; -import jdk.jpackage.internal.model.ApplicationLayout; - - -final class ApplicationLayoutUtils { - - public static final ApplicationLayout PLATFORM_APPLICATION_LAYOUT; - - private static final ApplicationLayout WIN_APPLICATION_LAYOUT = ApplicationLayout.build() - .setAll("") - .appDirectory("app") - .runtimeDirectory("runtime") - .appModsDirectory(Path.of("app", "mods")) - .create(); - - private static final ApplicationLayout MAC_APPLICATION_LAYOUT = ApplicationLayout.build() - .launchersDirectory("Contents/MacOS") - .appDirectory("Contents/app") - .runtimeDirectory("Contents/runtime/Contents/Home") - .desktopIntegrationDirectory("Contents/Resources") - .appModsDirectory("Contents/app/mods") - .contentDirectory("Contents") - .create(); - - private static final ApplicationLayout LINUX_APPLICATION_LAYOUT = ApplicationLayout.build() - .launchersDirectory("bin") - .appDirectory("lib/app") - .runtimeDirectory("lib/runtime") - .desktopIntegrationDirectory("lib") - .appModsDirectory("lib/app/mods") - .contentDirectory("lib") - .create(); - - static { - switch (OperatingSystem.current()) { - case WINDOWS -> PLATFORM_APPLICATION_LAYOUT = WIN_APPLICATION_LAYOUT; - case MACOS -> PLATFORM_APPLICATION_LAYOUT = MAC_APPLICATION_LAYOUT; - case LINUX -> PLATFORM_APPLICATION_LAYOUT = LINUX_APPLICATION_LAYOUT; - default -> { - throw new UnsupportedOperationException(); - } - } - } -} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java deleted file mode 100644 index f0323bbd841..00000000000 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java +++ /dev/null @@ -1,867 +0,0 @@ -/* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.jpackage.internal; - -import jdk.internal.util.OperatingSystem; -import jdk.jpackage.internal.model.ConfigException; -import jdk.jpackage.internal.model.PackagerException; -import java.io.IOException; -import java.io.Reader; -import java.nio.file.Files; -import java.nio.file.Path; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Properties; -import java.util.ResourceBundle; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Arguments - * - * This class encapsulates and processes the command line arguments, - * in effect, implementing all the work of jpackage tool. - * - * The primary entry point, processArguments(): - * Processes and validates command line arguments, constructing DeployParams. - * Validates the DeployParams, and generate the BundleParams. - * Generates List of Bundlers from BundleParams valid for this platform. - * Executes each Bundler in the list. - */ -public class Arguments { - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.MainResources"); - - private static final String FA_EXTENSIONS = "extension"; - private static final String FA_CONTENT_TYPE = "mime-type"; - private static final String FA_DESCRIPTION = "description"; - private static final String FA_ICON = "icon"; - - // Mac specific file association keys - // String - public static final String MAC_CFBUNDLETYPEROLE = "mac.CFBundleTypeRole"; - public static final String MAC_LSHANDLERRANK = "mac.LSHandlerRank"; - public static final String MAC_NSSTORETYPEKEY = "mac.NSPersistentStoreTypeKey"; - public static final String MAC_NSDOCUMENTCLASS = "mac.NSDocumentClass"; - // Boolean - public static final String MAC_LSTYPEISPACKAGE = "mac.LSTypeIsPackage"; - public static final String MAC_LSDOCINPLACE = "mac.LSSupportsOpeningDocumentsInPlace"; - public static final String MAC_UIDOCBROWSER = "mac.UISupportsDocumentBrowser"; - // Array of strings - public static final String MAC_NSEXPORTABLETYPES = "mac.NSExportableTypes"; - public static final String MAC_UTTYPECONFORMSTO = "mac.UTTypeConformsTo"; - - // regexp for parsing args (for example, for additional launchers) - private static Pattern pattern = Pattern.compile( - "(?:(?:([\"'])(?:\\\\\\1|.)*?(?:\\1|$))|(?:\\\\[\"'\\s]|[^\\s]))++"); - - private DeployParams deployParams = null; - - private int pos = 0; - private List argList = null; - - private List allOptions = null; - - private boolean hasMainJar = false; - private boolean hasMainClass = false; - private boolean hasMainModule = false; - public boolean userProvidedBuildRoot = false; - - private String buildRoot = null; - private String mainJarPath = null; - - private boolean runtimeInstaller = false; - - private List addLaunchers = null; - - private static final Map argIds = new HashMap<>(); - private static final Map argShortIds = new HashMap<>(); - - static { - // init maps for parsing arguments - (EnumSet.allOf(CLIOptions.class)).forEach(option -> { - argIds.put(option.getIdWithPrefix(), option); - if (option.getShortIdWithPrefix() != null) { - argShortIds.put(option.getShortIdWithPrefix(), option); - } - }); - } - - private static final InheritableThreadLocal instance = - new InheritableThreadLocal(); - - public Arguments(String[] args) { - instance.set(this); - - argList = new ArrayList(args.length); - for (String arg : args) { - argList.add(arg); - } - - pos = 0; - - deployParams = new DeployParams(); - - allOptions = new ArrayList<>(); - - addLaunchers = new ArrayList<>(); - } - - // CLIOptions is public for DeployParamsTest - public enum CLIOptions { - PACKAGE_TYPE("type", "t", OptionCategories.PROPERTY, () -> { - var type = popArg(); - context().deployParams.setTargetFormat(type); - setOptionValue("type", type); - }), - - INPUT ("input", "i", OptionCategories.PROPERTY, () -> { - setOptionValue("input", popArg()); - }), - - OUTPUT ("dest", "d", OptionCategories.PROPERTY, () -> { - var path = Path.of(popArg()); - setOptionValue("dest", path); - }), - - DESCRIPTION ("description", OptionCategories.PROPERTY), - - VENDOR ("vendor", OptionCategories.PROPERTY), - - APPCLASS ("main-class", OptionCategories.PROPERTY, () -> { - context().hasMainClass = true; - setOptionValue("main-class", popArg()); - }), - - NAME ("name", "n", OptionCategories.PROPERTY), - - VERBOSE ("verbose", OptionCategories.PROPERTY, () -> { - setOptionValue("verbose", true); - Log.setVerbose(); - }), - - RESOURCE_DIR("resource-dir", - OptionCategories.PROPERTY, () -> { - String resourceDir = popArg(); - setOptionValue("resource-dir", resourceDir); - }), - - DMG_CONTENT ("mac-dmg-content", OptionCategories.PROPERTY, () -> { - List content = getArgumentList(popArg()); - content.forEach(a -> setOptionValue("mac-dmg-content", a)); - }), - - ARGUMENTS ("arguments", OptionCategories.PROPERTY, () -> { - List arguments = getArgumentList(popArg()); - setOptionValue("arguments", arguments); - }), - - JLINK_OPTIONS ("jlink-options", OptionCategories.PROPERTY, () -> { - List options = getArgumentList(popArg()); - setOptionValue("jlink-options", options); - }), - - ICON ("icon", OptionCategories.PROPERTY), - - COPYRIGHT ("copyright", OptionCategories.PROPERTY), - - LICENSE_FILE ("license-file", OptionCategories.PROPERTY), - - VERSION ("app-version", OptionCategories.PROPERTY), - - RELEASE ("linux-app-release", OptionCategories.PROPERTY), - - ABOUT_URL ("about-url", OptionCategories.PROPERTY), - - JAVA_OPTIONS ("java-options", OptionCategories.PROPERTY, () -> { - List args = getArgumentList(popArg()); - args.forEach(a -> setOptionValue("java-options", a)); - }), - - APP_CONTENT ("app-content", OptionCategories.PROPERTY, () -> { - getArgumentList(popArg()).forEach( - a -> setOptionValue("app-content", a)); - }), - - FILE_ASSOCIATIONS ("file-associations", - OptionCategories.PROPERTY, () -> { - Map args = new HashMap<>(); - - // load .properties file - Map initialMap = getPropertiesFromFile(popArg()); - - putUnlessNull(args, StandardBundlerParam.FA_EXTENSIONS.getID(), - initialMap.get(FA_EXTENSIONS)); - - putUnlessNull(args, StandardBundlerParam.FA_CONTENT_TYPE.getID(), - initialMap.get(FA_CONTENT_TYPE)); - - putUnlessNull(args, StandardBundlerParam.FA_DESCRIPTION.getID(), - initialMap.get(FA_DESCRIPTION)); - - putUnlessNull(args, StandardBundlerParam.FA_ICON.getID(), - initialMap.get(FA_ICON)); - - // Mac extended file association arguments - putUnlessNull(args, MAC_CFBUNDLETYPEROLE, - initialMap.get(MAC_CFBUNDLETYPEROLE)); - - putUnlessNull(args, MAC_LSHANDLERRANK, - initialMap.get(MAC_LSHANDLERRANK)); - - putUnlessNull(args, MAC_NSSTORETYPEKEY, - initialMap.get(MAC_NSSTORETYPEKEY)); - - putUnlessNull(args, MAC_NSDOCUMENTCLASS, - initialMap.get(MAC_NSDOCUMENTCLASS)); - - putUnlessNull(args, MAC_LSTYPEISPACKAGE, - initialMap.get(MAC_LSTYPEISPACKAGE)); - - putUnlessNull(args, MAC_LSDOCINPLACE, - initialMap.get(MAC_LSDOCINPLACE)); - - putUnlessNull(args, MAC_UIDOCBROWSER, - initialMap.get(MAC_UIDOCBROWSER)); - - putUnlessNull(args, MAC_NSEXPORTABLETYPES, - initialMap.get(MAC_NSEXPORTABLETYPES)); - - putUnlessNull(args, MAC_UTTYPECONFORMSTO, - initialMap.get(MAC_UTTYPECONFORMSTO)); - - ArrayList> associationList = - new ArrayList>(); - - associationList.add(args); - - // check that we really add _another_ value to the list - setOptionValue("file-associations", associationList); - - }), - - ADD_LAUNCHER ("add-launcher", - OptionCategories.PROPERTY, () -> { - String spec = popArg(); - String name = null; - String filename = spec; - if (spec.contains("=")) { - String[] values = spec.split("=", 2); - name = values[0]; - filename = values[1]; - } - context().addLaunchers.add( - new AddLauncherArguments(name, filename)); - }), - - TEMP_ROOT ("temp", OptionCategories.PROPERTY, () -> { - context().buildRoot = popArg(); - context().userProvidedBuildRoot = true; - setOptionValue("temp", context().buildRoot); - }), - - INSTALL_DIR ("install-dir", OptionCategories.PROPERTY), - - PREDEFINED_APP_IMAGE ("app-image", OptionCategories.PROPERTY), - - PREDEFINED_RUNTIME_IMAGE ("runtime-image", OptionCategories.PROPERTY), - - MAIN_JAR ("main-jar", OptionCategories.PROPERTY, () -> { - context().mainJarPath = popArg(); - context().hasMainJar = true; - setOptionValue("main-jar", context().mainJarPath); - }), - - MODULE ("module", "m", OptionCategories.MODULAR, () -> { - context().hasMainModule = true; - setOptionValue("module", popArg()); - }), - - ADD_MODULES ("add-modules", OptionCategories.MODULAR), - - MODULE_PATH ("module-path", "p", OptionCategories.MODULAR), - - LAUNCHER_AS_SERVICE ("launcher-as-service", OptionCategories.PROPERTY, () -> { - setOptionValue("launcher-as-service", true); - }), - - MAC_SIGN ("mac-sign", "s", OptionCategories.PLATFORM_MAC, () -> { - setOptionValue("mac-sign", true); - }), - - MAC_APP_STORE ("mac-app-store", OptionCategories.PLATFORM_MAC, () -> { - setOptionValue("mac-app-store", true); - }), - - MAC_CATEGORY ("mac-app-category", OptionCategories.PLATFORM_MAC), - - MAC_BUNDLE_NAME ("mac-package-name", OptionCategories.PLATFORM_MAC), - - MAC_BUNDLE_IDENTIFIER("mac-package-identifier", - OptionCategories.PLATFORM_MAC), - - MAC_BUNDLE_SIGNING_PREFIX ("mac-package-signing-prefix", - OptionCategories.PLATFORM_MAC), - - MAC_SIGNING_KEY_NAME ("mac-signing-key-user-name", - OptionCategories.PLATFORM_MAC), - - MAC_APP_IMAGE_SIGN_IDENTITY ("mac-app-image-sign-identity", - OptionCategories.PLATFORM_MAC), - - MAC_INSTALLER_SIGN_IDENTITY ("mac-installer-sign-identity", - OptionCategories.PLATFORM_MAC), - - MAC_SIGNING_KEYCHAIN ("mac-signing-keychain", - OptionCategories.PLATFORM_MAC), - - MAC_ENTITLEMENTS ("mac-entitlements", OptionCategories.PLATFORM_MAC), - - WIN_HELP_URL ("win-help-url", OptionCategories.PLATFORM_WIN), - - WIN_UPDATE_URL ("win-update-url", OptionCategories.PLATFORM_WIN), - - WIN_MENU_HINT ("win-menu", OptionCategories.PLATFORM_WIN, - createArgumentWithOptionalValueAction("win-menu")), - - WIN_MENU_GROUP ("win-menu-group", OptionCategories.PLATFORM_WIN), - - WIN_SHORTCUT_HINT ("win-shortcut", OptionCategories.PLATFORM_WIN, - createArgumentWithOptionalValueAction("win-shortcut")), - - WIN_SHORTCUT_PROMPT ("win-shortcut-prompt", - OptionCategories.PLATFORM_WIN, () -> { - setOptionValue("win-shortcut-prompt", true); - }), - - WIN_PER_USER_INSTALLATION ("win-per-user-install", - OptionCategories.PLATFORM_WIN, () -> { - setOptionValue("win-per-user-install", false); - }), - - WIN_DIR_CHOOSER ("win-dir-chooser", - OptionCategories.PLATFORM_WIN, () -> { - setOptionValue("win-dir-chooser", true); - }), - - WIN_UPGRADE_UUID ("win-upgrade-uuid", - OptionCategories.PLATFORM_WIN), - - WIN_CONSOLE_HINT ("win-console", OptionCategories.PLATFORM_WIN, () -> { - setOptionValue("win-console", true); - }), - - LINUX_BUNDLE_NAME ("linux-package-name", - OptionCategories.PLATFORM_LINUX), - - LINUX_DEB_MAINTAINER ("linux-deb-maintainer", - OptionCategories.PLATFORM_LINUX), - - LINUX_CATEGORY ("linux-app-category", - OptionCategories.PLATFORM_LINUX), - - LINUX_RPM_LICENSE_TYPE ("linux-rpm-license-type", - OptionCategories.PLATFORM_LINUX), - - LINUX_PACKAGE_DEPENDENCIES ("linux-package-deps", - OptionCategories.PLATFORM_LINUX), - - LINUX_SHORTCUT_HINT ("linux-shortcut", OptionCategories.PLATFORM_LINUX, - createArgumentWithOptionalValueAction("linux-shortcut")), - - LINUX_MENU_GROUP ("linux-menu-group", OptionCategories.PLATFORM_LINUX); - - private final String id; - private final String shortId; - private final OptionCategories category; - private final Runnable action; - private static Arguments argContext; - - private CLIOptions(String id, OptionCategories category) { - this(id, null, category, null); - } - - private CLIOptions(String id, String shortId, - OptionCategories category) { - this(id, shortId, category, null); - } - - private CLIOptions(String id, - OptionCategories category, Runnable action) { - this(id, null, category, action); - } - - private CLIOptions(String id, String shortId, - OptionCategories category, Runnable action) { - this.id = id; - this.shortId = shortId; - this.action = action; - this.category = category; - } - - public static Arguments context() { - return instance.get(); - } - - public String getId() { - return this.id; - } - - String getIdWithPrefix() { - return "--" + this.id; - } - - String getShortIdWithPrefix() { - return this.shortId == null ? null : "-" + this.shortId; - } - - void execute() { - if (action != null) { - action.run(); - } else { - defaultAction(); - } - } - - private void defaultAction() { - context().deployParams.addBundleArgument(id, popArg()); - } - - private static void setOptionValue(String option, Object value) { - context().deployParams.addBundleArgument(option, value); - } - - private static String popArg() { - nextArg(); - return (context().pos >= context().argList.size()) ? - "" : context().argList.get(context().pos); - } - - private static String getArg() { - return (context().pos >= context().argList.size()) ? - "" : context().argList.get(context().pos); - } - - private static void nextArg() { - context().pos++; - } - - private static void prevArg() { - Objects.checkIndex(context().pos, context().argList.size()); - context().pos--; - } - - private static boolean hasNextArg() { - return context().pos < context().argList.size(); - } - - private static Runnable createArgumentWithOptionalValueAction(String option) { - Objects.requireNonNull(option); - return () -> { - nextArg(); - if (hasNextArg()) { - var value = getArg(); - if (value.startsWith("-")) { - prevArg(); - setOptionValue(option, true); - } else { - setOptionValue(option, value); - } - } else { - setOptionValue(option, true); - } - }; - } - } - - enum OptionCategories { - MODULAR, - PROPERTY, - PLATFORM_MAC, - PLATFORM_WIN, - PLATFORM_LINUX; - } - - public boolean processArguments() { - try { - // parse cmd line - String arg; - CLIOptions option; - for (; CLIOptions.hasNextArg(); CLIOptions.nextArg()) { - arg = CLIOptions.getArg(); - if ((option = toCLIOption(arg)) != null) { - // found a CLI option - allOptions.add(option); - option.execute(); - } else { - throw new PackagerException("ERR_InvalidOption", arg); - } - } - - // display error for arguments that are not supported - // for current configuration. - - validateArguments(); - - List> launchersAsMap = - new ArrayList<>(); - - for (AddLauncherArguments sl : addLaunchers) { - launchersAsMap.add(sl.getLauncherMap()); - } - - deployParams.addBundleArgument( - StandardBundlerParam.ADD_LAUNCHERS.getID(), - launchersAsMap); - - // at this point deployParams should be already configured - - deployParams.validate(); - - BundleParams bp = deployParams.getBundleParams(); - - // validate name(s) - ArrayList usedNames = new ArrayList(); - usedNames.add(bp.getName()); // add main app name - - for (AddLauncherArguments sl : addLaunchers) { - Map slMap = sl.getLauncherMap(); - String slName = - (String) slMap.get(Arguments.CLIOptions.NAME.getId()); - if (slName == null) { - throw new PackagerException("ERR_NoAddLauncherName"); - } - // same rules apply to additional launcher names as app name - DeployParams.validateName(slName, false); - for (String usedName : usedNames) { - if (slName.equals(usedName)) { - throw new PackagerException("ERR_NoUniqueName"); - } - } - usedNames.add(slName); - } - - generateBundle(bp.getBundleParamsAsMap()); - return true; - } catch (Exception e) { - Log.verbose(e); - String msg1 = e.getMessage(); - Log.fatalError(msg1); - if (e.getCause() != null && e.getCause() != e) { - String msg2 = e.getCause().getMessage(); - if (msg2 != null && !msg1.contains(msg2)) { - Log.fatalError(msg2); - } - } - return false; - } - } - - private void validateArguments() throws PackagerException { - String type = deployParams.getTargetFormat(); - String ptype = (type != null) ? type : "default"; - boolean imageOnly = deployParams.isTargetAppImage(); - boolean hasAppImage = allOptions.contains( - CLIOptions.PREDEFINED_APP_IMAGE); - boolean hasRuntime = allOptions.contains( - CLIOptions.PREDEFINED_RUNTIME_IMAGE); - boolean installerOnly = !imageOnly && hasAppImage; - boolean isMac = OperatingSystem.isMacOS(); - runtimeInstaller = !imageOnly && hasRuntime && !hasAppImage && - !hasMainModule && !hasMainJar; - - for (CLIOptions option : allOptions) { - if (!ValidOptions.checkIfSupported(option)) { - // includes option valid only on different platform - throw new PackagerException("ERR_UnsupportedOption", - option.getIdWithPrefix()); - } - if ((imageOnly && !isMac) || (imageOnly && !hasAppImage && isMac)) { - if (!ValidOptions.checkIfImageSupported(option)) { - throw new PackagerException("ERR_InvalidTypeOption", - option.getIdWithPrefix(), type); - } - } else if (imageOnly && hasAppImage && isMac) { // Signing app image - if (!ValidOptions.checkIfSigningSupported(option)) { - throw new PackagerException( - "ERR_InvalidOptionWithAppImageSigning", - option.getIdWithPrefix()); - } - } else if (installerOnly || runtimeInstaller) { - if (!ValidOptions.checkIfInstallerSupported(option)) { - if (runtimeInstaller) { - throw new PackagerException("ERR_NoInstallerEntryPoint", - option.getIdWithPrefix()); - } else { - throw new PackagerException("ERR_InvalidTypeOption", - option.getIdWithPrefix(), ptype); - } - } - } - } - if (hasRuntime) { - if (hasAppImage) { - // note --runtime-image is only for image or runtime installer. - throw new PackagerException("ERR_MutuallyExclusiveOptions", - CLIOptions.PREDEFINED_RUNTIME_IMAGE.getIdWithPrefix(), - CLIOptions.PREDEFINED_APP_IMAGE.getIdWithPrefix()); - } - if (allOptions.contains(CLIOptions.ADD_MODULES)) { - throw new PackagerException("ERR_MutuallyExclusiveOptions", - CLIOptions.PREDEFINED_RUNTIME_IMAGE.getIdWithPrefix(), - CLIOptions.ADD_MODULES.getIdWithPrefix()); - } - if (allOptions.contains(CLIOptions.JLINK_OPTIONS)) { - throw new PackagerException("ERR_MutuallyExclusiveOptions", - CLIOptions.PREDEFINED_RUNTIME_IMAGE.getIdWithPrefix(), - CLIOptions.JLINK_OPTIONS.getIdWithPrefix()); - } - } - if (allOptions.contains(CLIOptions.MAC_SIGNING_KEY_NAME) && - allOptions.contains(CLIOptions.MAC_APP_IMAGE_SIGN_IDENTITY)) { - throw new PackagerException("ERR_MutuallyExclusiveOptions", - CLIOptions.MAC_SIGNING_KEY_NAME.getIdWithPrefix(), - CLIOptions.MAC_APP_IMAGE_SIGN_IDENTITY.getIdWithPrefix()); - } - if (allOptions.contains(CLIOptions.MAC_SIGNING_KEY_NAME) && - allOptions.contains(CLIOptions.MAC_INSTALLER_SIGN_IDENTITY)) { - throw new PackagerException("ERR_MutuallyExclusiveOptions", - CLIOptions.MAC_SIGNING_KEY_NAME.getIdWithPrefix(), - CLIOptions.MAC_INSTALLER_SIGN_IDENTITY.getIdWithPrefix()); - } - if (isMac && (imageOnly || "dmg".equals(type)) && - allOptions.contains(CLIOptions.MAC_INSTALLER_SIGN_IDENTITY)) { - throw new PackagerException("ERR_InvalidTypeOption", - CLIOptions.MAC_INSTALLER_SIGN_IDENTITY.getIdWithPrefix(), - type); - } - if (allOptions.contains(CLIOptions.DMG_CONTENT) - && !("dmg".equals(type))) { - throw new PackagerException("ERR_InvalidTypeOption", - CLIOptions.DMG_CONTENT.getIdWithPrefix(), ptype); - } - if (hasMainJar && hasMainModule) { - throw new PackagerException("ERR_BothMainJarAndModule"); - } - if (imageOnly && !hasAppImage && !hasMainJar && !hasMainModule) { - throw new PackagerException("ERR_NoEntryPoint"); - } - } - - private jdk.jpackage.internal.Bundler getPlatformBundler() { - boolean appImage = deployParams.isTargetAppImage(); - String type = deployParams.getTargetFormat(); - String bundleType = (appImage ? "IMAGE" : "INSTALLER"); - - for (jdk.jpackage.internal.Bundler bundler : - Bundlers.createBundlersInstance().getBundlers(bundleType)) { - if (type == null) { - if (bundler.isDefault()) { - return bundler; - } - } else { - if (appImage || type.equalsIgnoreCase(bundler.getID())) { - return bundler; - } - } - } - return null; - } - - private void generateBundle(Map params) - throws PackagerException { - - // the temp dir needs to be fetched from the params early, - // to prevent each copy of the params (such as may be used for - // additional launchers) from generating a separate temp dir when - // the default is used (the default is a new temp directory) - // The bundler.cleanup() below would not otherwise be able to - // clean these extra (and unneeded) temp directories. - StandardBundlerParam.TEMP_ROOT.fetchFrom(params); - - // determine what bundler to run - jdk.jpackage.internal.Bundler bundler = getPlatformBundler(); - - if (bundler == null || !bundler.supported(runtimeInstaller)) { - String type = Optional.ofNullable(bundler).map(Bundler::getID).orElseGet( - () -> deployParams.getTargetFormat()); - throw new PackagerException("ERR_InvalidInstallerType", type); - } - - Map localParams = new HashMap<>(params); - try { - Path result = executeBundler(bundler, params, localParams); - if (result == null) { - throw new PackagerException("MSG_BundlerFailed", - bundler.getID(), bundler.getName()); - } - Log.verbose(MessageFormat.format( - I18N.getString("message.bundle-created"), - bundler.getName())); - } catch (ConfigException e) { - Log.verbose(e); - if (e.getAdvice() != null) { - throw new PackagerException(e, "MSG_BundlerConfigException", - bundler.getName(), e.getMessage(), e.getAdvice()); - } else { - throw new PackagerException(e, - "MSG_BundlerConfigExceptionNoAdvice", - bundler.getName(), e.getMessage()); - } - } catch (RuntimeException re) { - Log.verbose(re); - throw new PackagerException(re, "MSG_BundlerRuntimeException", - bundler.getName(), re.toString()); - } finally { - if (userProvidedBuildRoot) { - Log.verbose(MessageFormat.format( - I18N.getString("message.debug-working-directory"), - (Path.of(buildRoot)).toAbsolutePath().toString())); - } else { - // always clean up the temporary directory created - // when --temp option not used. - bundler.cleanup(localParams); - } - } - } - - private static Path executeBundler(Bundler bundler, Map params, - Map localParams) throws ConfigException, PackagerException { - try { - bundler.validate(localParams); - return bundler.execute(localParams, StandardBundlerParam.OUTPUT_DIR.fetchFrom(params)); - } catch (ConfigException|PackagerException ex) { - throw ex; - } catch (RuntimeException ex) { - if (ex.getCause() instanceof ConfigException cfgEx) { - throw cfgEx; - } else if (ex.getCause() instanceof PackagerException pkgEx) { - throw pkgEx; - } else { - throw ex; - } - } - } - - static CLIOptions toCLIOption(String arg) { - CLIOptions option; - if ((option = argIds.get(arg)) == null) { - option = argShortIds.get(arg); - } - return option; - } - - static Map getPropertiesFromFile(String filename) { - Map map = new HashMap<>(); - // load properties file - Properties properties = new Properties(); - try (Reader reader = Files.newBufferedReader(Path.of(filename))) { - properties.load(reader); - } catch (IOException e) { - Log.error("Exception: " + e.getMessage()); - } - - for (final String name: properties.stringPropertyNames()) { - map.put(name, properties.getProperty(name)); - } - - return map; - } - - static List getArgumentList(String inputString) { - List list = new ArrayList<>(); - if (inputString == null || inputString.isEmpty()) { - return list; - } - - // The "pattern" regexp attempts to abide to the rule that - // strings are delimited by whitespace unless surrounded by - // quotes, then it is anything (including spaces) in the quotes. - Matcher m = pattern.matcher(inputString); - while (m.find()) { - String s = inputString.substring(m.start(), m.end()).trim(); - // Ensure we do not have an empty string. trim() will take care of - // whitespace only strings. The regex preserves quotes and escaped - // chars so we need to clean them before adding to the List - if (!s.isEmpty()) { - list.add(unquoteIfNeeded(s)); - } - } - return list; - } - - static void putUnlessNull(Map params, - String param, Object value) { - if (value != null) { - params.put(param, value); - } - } - - private static String unquoteIfNeeded(String in) { - if (in == null) { - return null; - } - - if (in.isEmpty()) { - return ""; - } - - // Use code points to preserve non-ASCII chars - StringBuilder sb = new StringBuilder(); - int codeLen = in.codePointCount(0, in.length()); - int quoteChar = -1; - for (int i = 0; i < codeLen; i++) { - int code = in.codePointAt(i); - if (code == '"' || code == '\'') { - // If quote is escaped make sure to copy it - if (i > 0 && in.codePointAt(i - 1) == '\\') { - sb.deleteCharAt(sb.length() - 1); - sb.appendCodePoint(code); - continue; - } - if (quoteChar != -1) { - if (code == quoteChar) { - // close quote, skip char - quoteChar = -1; - } else { - sb.appendCodePoint(code); - } - } else { - // opening quote, skip char - quoteChar = code; - } - } else { - sb.appendCodePoint(code); - } - } - return sb.toString(); - } -} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BasicBundlers.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BasicBundlers.java deleted file mode 100644 index 7f444fe7337..00000000000 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BasicBundlers.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2014, 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. 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.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.ServiceLoader; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * BasicBundlers - * - * A basic bundlers collection that loads the default bundlers. - * Loads the common bundlers. - *

    - *
  • Windows file image
  • - *
  • Mac .app
  • - *
  • Linux file image
  • - *
  • Windows MSI
  • - *
  • Windows EXE
  • - *
  • Mac DMG
  • - *
  • Mac PKG
  • - *
  • Linux DEB
  • - *
  • Linux RPM
  • - * - *
- */ -public class BasicBundlers implements Bundlers { - - boolean defaultsLoaded = false; - - private final Collection bundlers = new CopyOnWriteArrayList<>(); - - @Override - public Collection getBundlers() { - return Collections.unmodifiableCollection(bundlers); - } - - @Override - public Collection getBundlers(String type) { - if (type == null) return Collections.emptySet(); - switch (type) { - case "NONE": - return Collections.emptySet(); - case "ALL": - return getBundlers(); - default: - return Arrays.asList(getBundlers().stream() - .filter(b -> type.equalsIgnoreCase(b.getBundleType())) - .toArray(Bundler[]::new)); - } - } - - // Loads bundlers from the META-INF/services direct - @Override - public void loadBundlersFromServices(ClassLoader cl) { - ServiceLoader loader = ServiceLoader.load(Bundler.class, cl); - for (Bundler aLoader : loader) { - bundlers.add(aLoader); - } - } -} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvBuilder.java index ba70913eca9..17b805b8259 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvBuilder.java @@ -24,14 +24,13 @@ */ package jdk.jpackage.internal; -import java.io.IOException; -import java.nio.file.Files; +import static jdk.jpackage.internal.cli.StandardValidator.IS_DIRECTORY_EMPTY_OR_NON_EXISTENT_PREDICATE; + import java.nio.file.Path; import java.util.Objects; import java.util.Optional; import jdk.jpackage.internal.model.AppImageLayout; import jdk.jpackage.internal.model.Application; -import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.Package; import jdk.jpackage.internal.resources.ResourceLocator; @@ -41,20 +40,12 @@ final class BuildEnvBuilder { this.root = Objects.requireNonNull(root); } - BuildEnv create() throws ConfigException { - var exceptionBuilder = I18N.buildConfigException("ERR_BuildRootInvalid", root); - if (Files.isDirectory(root)) { - try (var rootDirContents = Files.list(root)) { - if (rootDirContents.findAny().isPresent()) { - // The root directory is not empty. - throw exceptionBuilder.create(); - } - } catch (IOException ioe) { - throw exceptionBuilder.cause(ioe).create(); - } - } else if (Files.exists(root)) { - // The root is not a directory. - throw exceptionBuilder.create(); + BuildEnv create() { + // The directory should be validated earlier with a proper error message. + // Here is only a sanity check. + if (!IS_DIRECTORY_EMPTY_OR_NON_EXISTENT_PREDICATE.test(root)) { + throw new UnsupportedOperationException( + String.format("Root work directory [%s] should be empty or non existent", root)); } return BuildEnv.create(root, Optional.ofNullable(resourceDir), verbose, diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvFromOptions.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvFromOptions.java new file mode 100644 index 00000000000..8faabe97a3d --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvFromOptions.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal; + +import static jdk.jpackage.internal.cli.StandardOption.PREDEFINED_APP_IMAGE; +import static jdk.jpackage.internal.cli.StandardOption.PREDEFINED_RUNTIME_IMAGE; +import static jdk.jpackage.internal.cli.StandardOption.RESOURCE_DIR; +import static jdk.jpackage.internal.cli.StandardOption.TEMP_ROOT; +import static jdk.jpackage.internal.cli.StandardOption.VERBOSE; + +import java.nio.file.Path; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; +import jdk.jpackage.internal.cli.Options; +import jdk.jpackage.internal.model.Application; +import jdk.jpackage.internal.model.ApplicationLayout; +import jdk.jpackage.internal.model.Package; +import jdk.jpackage.internal.model.RuntimeLayout; + +final class BuildEnvFromOptions { + + BuildEnvFromOptions() { + predefinedRuntimeImageLayout(RuntimeLayout.DEFAULT); + } + + BuildEnvFromOptions predefinedAppImageLayout(Function v) { + predefinedAppImageLayout = v; + return this; + } + + BuildEnvFromOptions predefinedAppImageLayout(ApplicationLayout v) { + return predefinedAppImageLayout(path -> v.resolveAt(path)); + } + + BuildEnvFromOptions predefinedRuntimeImageLayout(Function v) { + predefinedRuntimeImageLayout = v; + return this; + } + + BuildEnvFromOptions predefinedRuntimeImageLayout(RuntimeLayout v) { + return predefinedRuntimeImageLayout(path -> v.resolveAt(path)); + } + + BuildEnv create(Options options, Application app) { + return create(options, app, Optional.empty()); + } + + BuildEnv create(Options options, Package pkg) { + return create(options, pkg.app(), Optional.of(pkg)); + } + + private BuildEnv create(Options options, Application app, Optional pkg) { + Objects.requireNonNull(options); + Objects.requireNonNull(app); + Objects.requireNonNull(pkg); + Objects.requireNonNull(predefinedAppImageLayout); + Objects.requireNonNull(predefinedRuntimeImageLayout); + + final var builder = new BuildEnvBuilder(TEMP_ROOT.getFrom(options)); + + RESOURCE_DIR.ifPresentIn(options, builder::resourceDir); + VERBOSE.ifPresentIn(options, builder::verbose); + + if (app.isRuntime()) { + var path = PREDEFINED_RUNTIME_IMAGE.getFrom(options); + builder.appImageLayout(predefinedRuntimeImageLayout.apply(path)); + } else if (PREDEFINED_APP_IMAGE.containsIn(options)) { + var path = PREDEFINED_APP_IMAGE.getFrom(options); + builder.appImageLayout(predefinedAppImageLayout.apply(path)); + } else { + pkg.ifPresentOrElse(builder::appImageDirFor, () -> { + builder.appImageDirFor(app); + }); + } + + return builder.create(); + } + + private Function predefinedAppImageLayout; + private Function predefinedRuntimeImageLayout; +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvFromParams.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvFromParams.java deleted file mode 100644 index 6fb1a342fbf..00000000000 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvFromParams.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.jpackage.internal; - -import static jdk.jpackage.internal.ApplicationLayoutUtils.PLATFORM_APPLICATION_LAYOUT; -import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_APP_IMAGE; -import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_RUNTIME_IMAGE; -import static jdk.jpackage.internal.StandardBundlerParam.RESOURCE_DIR; -import static jdk.jpackage.internal.StandardBundlerParam.TEMP_ROOT; -import static jdk.jpackage.internal.StandardBundlerParam.VERBOSE; - -import java.nio.file.Path; -import java.util.Map; -import java.util.function.Function; -import jdk.jpackage.internal.model.ApplicationLayout; -import jdk.jpackage.internal.model.ConfigException; -import jdk.jpackage.internal.model.RuntimeLayout; - -final class BuildEnvFromParams { - - static BuildEnv create(Map params, - Function predefinedAppImageLayoutProvider, - Function predefinedRuntimeImageLayoutProvider) throws ConfigException { - - final var builder = new BuildEnvBuilder(TEMP_ROOT.fetchFrom(params)); - - RESOURCE_DIR.copyInto(params, builder::resourceDir); - VERBOSE.copyInto(params, builder::verbose); - - final var app = FromParams.APPLICATION.findIn(params).orElseThrow(); - - final var pkg = FromParams.getCurrentPackage(params); - - if (app.isRuntime()) { - var layout = predefinedRuntimeImageLayoutProvider.apply(PREDEFINED_RUNTIME_IMAGE.findIn(params).orElseThrow()); - builder.appImageLayout(layout); - } else if (StandardBundlerParam.hasPredefinedAppImage(params)) { - var layout = predefinedAppImageLayoutProvider.apply(PREDEFINED_APP_IMAGE.findIn(params).orElseThrow()); - builder.appImageLayout(layout); - } else if (pkg.isPresent()) { - builder.appImageDirFor(pkg.orElseThrow()); - } else { - builder.appImageDirFor(app); - } - - return builder.create(); - } - - static final BundlerParamInfo BUILD_ENV = BundlerParamInfo.createBundlerParam(BuildEnv.class, params -> { - return create(params, PLATFORM_APPLICATION_LAYOUT::resolveAt, RuntimeLayout.DEFAULT::resolveAt); - }); -} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundleParams.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundleParams.java deleted file mode 100644 index 937a3655e8b..00000000000 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundleParams.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2012, 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. 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.util.HashMap; -import java.util.Map; -import static jdk.jpackage.internal.StandardBundlerParam.APP_NAME; - -public class BundleParams { - - protected final Map params; - - /** - * create a new bundle with all default values - */ - public BundleParams() { - params = new HashMap<>(); - } - - /** - * Create a bundle params with a copy of the params - * @param params map of initial parameters to be copied in. - */ - public BundleParams(Map params) { - this.params = new HashMap<>(params); - } - - public void addAllBundleParams(Map params) { - this.params.putAll(params); - } - - // NOTE: we do not care about application parameters here - // as they will be embedded into jar file manifest and - // java launcher will take care of them! - - public Map getBundleParamsAsMap() { - return new HashMap<>(params); - } - - public String getName() { - return APP_NAME.fetchFrom(params); - } - - private void putUnlessNull(String param, Object value) { - if (value != null) { - params.put(param, value); - } - } -} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Bundler.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Bundler.java deleted file mode 100644 index 0d29677e826..00000000000 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Bundler.java +++ /dev/null @@ -1,126 +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. 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.nio.file.Path; -import java.util.Map; -import jdk.jpackage.internal.model.ConfigException; -import jdk.jpackage.internal.model.PackagerException; - -/** - * Bundler - * - * The basic interface implemented by all Bundlers. - */ -public interface Bundler { - /** - * @return User Friendly name of this bundler. - */ - String getName(); - - /** - * @return Command line identifier of the bundler. Should be unique. - */ - String getID(); - - /** - * @return The bundle type of the bundle that is created by this bundler. - */ - String getBundleType(); - - /** - * Determines if this bundler will execute with the given parameters. - * - * @param params The parameters to be validate. Validation may modify - * the map, so if you are going to be using the same map - * across multiple bundlers you should pass in a deep copy. - * @return true if valid - * @throws ConfigException If the configuration params are incorrect. The - * exception may contain advice on how to modify the params map - * to make it valid. - */ - public boolean validate(Map params) - throws ConfigException; - - /** - * Creates a bundle from existing content. - * - * If a call to {@link #validate(java.util.Map)} date} returns true with - * the parameters map, then you can expect a valid output. - * However if an exception was thrown out of validate or it returned - * false then you should not expect sensible results from this call. - * It may or may not return a value, and it may or may not throw an - * exception. But any output should not be considered valid or sane. - * - * @param params The Bundle parameters, - * Keyed by the id from the ParamInfo. Execution may - * modify the map, so if you are going to be using the - * same map across multiple bundlers you should pass - * in a deep copy. - * @param outputParentDir - * The parent dir that the returned bundle will be placed in. - * @return The resulting bundled file - * - * For a bundler that produces a single artifact file this will be the - * location of that artifact (.exe file, .deb file, etc) - * - * For a bundler that produces a specific directory format output this will - * be the location of that specific directory (.app file, etc). - * - * For a bundler that produce multiple files, this will be a parent - * directory of those files (linux and windows images), whose name is not - * relevant to the result. - * - * @throws java.lang.IllegalArgumentException for any of the following - * reasons: - *
    - *
  • A required parameter is not found in the params list, for - * example missing the main class.
  • - *
  • A parameter has the wrong type of an object, for example a - * String where a File is required
  • - *
  • Bundler specific incompatibilities with the parameters, for - * example a bad version number format or an application id with - * forward slashes.
  • - *
- */ - public Path execute(Map params, - Path outputParentDir) throws PackagerException; - - /** - * Removes temporary files that are used for bundling. - */ - public void cleanup(Map params); - - /** - * Returns "true" if this bundler is supported on current platform. - */ - public boolean supported(boolean runtimeInstaller); - - /** - * Returns "true" if this bundler is he default for the current platform. - */ - public boolean isDefault(); -} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundlerParamInfo.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundlerParamInfo.java deleted file mode 100644 index 81030b18f6d..00000000000 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundlerParamInfo.java +++ /dev/null @@ -1,179 +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. 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.nio.file.Path; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; -import jdk.jpackage.internal.util.function.ThrowingFunction; - -/** - * BundlerParamInfo - * - * A BundlerParamInfo encapsulates an individual bundler parameter of type . - * - * @param id The command line and hashmap name of the parameter - * - * @param valueType Type of the parameter - * - * @param defaultValueFunction If the value is not set, and no fallback value is found, the - * parameter uses the value returned by the producer. - * - * @param stringConverter An optional string converter for command line arguments. - */ -record BundlerParamInfo(String id, Class valueType, - Function, T> defaultValueFunction, - BiFunction, T> stringConverter) { - - BundlerParamInfo { - Objects.requireNonNull(id); - Objects.requireNonNull(valueType); - } - - static BundlerParamInfo createStringBundlerParam(String id) { - return new BundlerParamInfo<>(id, String.class, null, null); - } - - static BundlerParamInfo createBooleanBundlerParam(String id) { - return new BundlerParamInfo<>(id, Boolean.class, null, BundlerParamInfo::toBoolean); - } - - static BundlerParamInfo createPathBundlerParam(String id) { - return new BundlerParamInfo<>(id, Path.class, null, BundlerParamInfo::toPath); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - static BundlerParamInfo createBundlerParam(String id, Class valueType, - ThrowingFunction, U> valueCtor) { - return new BundlerParamInfo(id, valueType, ThrowingFunction.toFunction(valueCtor), null); - } - - static BundlerParamInfo createBundlerParam(Class valueType, - ThrowingFunction, U> valueCtor) { - return createBundlerParam(valueType.getName(), valueType, valueCtor); - } - - static boolean toBoolean(String value, Map params) { - if (value == null || "null".equalsIgnoreCase(value)) { - return false; - } else { - return Boolean.valueOf(value); - } - } - - static Path toPath(String value, Map params) { - return Path.of(value); - } - - String getID() { - return id; - } - - Class getValueType() { - return valueType; - } - - /** - * Returns true if value was not provided on command line for this parameter. - * - * @param params - params from which value will be fetch - * @return true if value was not provided on command line, false otherwise - */ - boolean getIsDefaultValue(Map params) { - Object o = params.get(getID()); - if (o != null) { - return false; // We have user provided value - } - - if (params.containsKey(getID())) { - return false; // explicit nulls are allowed for provided value - } - - return true; - } - - Function, T> getDefaultValueFunction() { - return defaultValueFunction; - } - - BiFunction, T> getStringConverter() { - return stringConverter; - } - - final T fetchFrom(Map params) { - return fetchFrom(params, true); - } - - @SuppressWarnings("unchecked") - final T fetchFrom(Map params, - boolean invokeDefault) { - Object o = params.get(getID()); - if (o instanceof String && getStringConverter() != null) { - return getStringConverter().apply((String) o, params); - } - - Class klass = getValueType(); - if (klass.isInstance(o)) { - return (T) o; - } - if (o != null) { - throw new IllegalArgumentException("Param " + getID() - + " should be of type " + getValueType() - + " but is a " + o.getClass()); - } - if (params.containsKey(getID())) { - // explicit nulls are allowed - return null; - } - - if (invokeDefault && (getDefaultValueFunction() != null)) { - T result = getDefaultValueFunction().apply(params); - if (result != null) { - params.put(getID(), result); - } - return result; - } - - // ultimate fallback - return null; - } - - Optional findIn(Map params) { - if (params.containsKey(getID())) { - return Optional.of(fetchFrom(params, true)); - } else { - return Optional.empty(); - } - } - - void copyInto(Map params, Consumer consumer) { - findIn(params).ifPresent(consumer); - } -} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Bundlers.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Bundlers.java deleted file mode 100644 index 955952d563d..00000000000 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Bundlers.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2014, 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. 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.util.Collection; -import java.util.Iterator; -import java.util.ServiceLoader; - -/** - * Bundlers - * - * The interface implemented by BasicBundlers - */ -public interface Bundlers { - - /** - * This convenience method will call - * {@link #createBundlersInstance(ClassLoader)} - * with the classloader that this Bundlers is loaded from. - * - * @return an instance of Bundlers loaded and configured from - * the current ClassLoader. - */ - public static Bundlers createBundlersInstance() { - return createBundlersInstance(Bundlers.class.getClassLoader()); - } - - /** - * This convenience method will automatically load a Bundlers instance - * from either META-INF/services or the default - * {@link BasicBundlers} if none are found in - * the services meta-inf. - * - * After instantiating the bundlers instance it will load the default - * bundlers via {@link #loadDefaultBundlers()} as well as requesting - * the services loader to load any other bundelrs via - * {@link #loadBundlersFromServices(ClassLoader)}. - - * - * @param servicesClassLoader the classloader to search for - * META-INF/service registered bundlers - * @return an instance of Bundlers loaded and configured from - * the specified ClassLoader - */ - public static Bundlers createBundlersInstance( - ClassLoader servicesClassLoader) { - ServiceLoader bundlersLoader = - ServiceLoader.load(Bundlers.class, servicesClassLoader); - Bundlers bundlers = null; - Iterator iter = bundlersLoader.iterator(); - if (iter.hasNext()) { - bundlers = iter.next(); - } - if (bundlers == null) { - bundlers = new BasicBundlers(); - } - - bundlers.loadBundlersFromServices(servicesClassLoader); - return bundlers; - } - - /** - * Returns all of the preconfigured, requested, and manually - * configured bundlers loaded with this instance. - * - * @return a read-only collection of the requested bundlers - */ - Collection getBundlers(); - - /** - * Returns all of the preconfigured, requested, and manually - * configured bundlers loaded with this instance that are of - * a specific BundleType, such as disk images, installers, or - * remote installers. - * - * @return a read-only collection of the requested bundlers - */ - Collection getBundlers(String type); - - /** - * Loads bundlers from the META-INF/services directly. - * - * This method is called from the - * {@link #createBundlersInstance(ClassLoader)} - * and {@link #createBundlersInstance()} methods. - */ - void loadBundlersFromServices(ClassLoader cl); - -} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/CLIHelp.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/CLIHelp.java deleted file mode 100644 index 7790ebb3ebb..00000000000 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/CLIHelp.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import jdk.internal.util.OperatingSystem; - -import java.util.ResourceBundle; -import java.io.File; -import java.text.MessageFormat; - - -/** - * CLIHelp - * - * Generate and show the command line interface help message(s). - */ -public class CLIHelp { - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.HelpResources"); - - // generates --help for jpackage's CLI - public static void showHelp(boolean noArgs) { - - if (noArgs) { - Log.info(I18N.getString("MSG_Help_no_args")); - } else { - OperatingSystem platform = OperatingSystem.current(); - String types; - String pLaunchOptions; - String pInstallOptions; - String pInstallDir; - String pAppImageDescription; - String pSignSampleUsage; - String pAppContentNote; - switch (platform) { - case MACOS: - types = "{\"app-image\", \"dmg\", \"pkg\"}"; - pLaunchOptions = I18N.getString("MSG_Help_mac_launcher"); - pInstallOptions = I18N.getString("MSG_Help_mac_install"); - pInstallDir - = I18N.getString("MSG_Help_mac_linux_install_dir"); - pAppImageDescription - = I18N.getString("MSG_Help_mac_app_image"); - pSignSampleUsage - = I18N.getString("MSG_Help_mac_sign_sample_usage"); - pAppContentNote - = I18N.getString("MSG_Help_mac_app_content_note"); - break; - case LINUX: - types = "{\"app-image\", \"rpm\", \"deb\"}"; - pLaunchOptions = ""; - pInstallOptions = I18N.getString("MSG_Help_linux_install"); - pInstallDir - = I18N.getString("MSG_Help_mac_linux_install_dir"); - pAppImageDescription - = I18N.getString("MSG_Help_default_app_image"); - pSignSampleUsage = ""; - pAppContentNote = ""; - break; - case WINDOWS: - types = "{\"app-image\", \"exe\", \"msi\"}"; - pLaunchOptions = I18N.getString("MSG_Help_win_launcher"); - pInstallOptions = I18N.getString("MSG_Help_win_install"); - pInstallDir - = I18N.getString("MSG_Help_win_install_dir"); - pAppImageDescription - = I18N.getString("MSG_Help_default_app_image"); - pSignSampleUsage = ""; - pAppContentNote = ""; - break; - default: - types = "{\"app-image\", \"exe\", \"msi\", \"rpm\", \"deb\", \"pkg\", \"dmg\"}"; - pLaunchOptions = I18N.getString("MSG_Help_win_launcher") - + I18N.getString("MSG_Help_mac_launcher"); - pInstallOptions = I18N.getString("MSG_Help_win_install") - + I18N.getString("MSG_Help_linux_install") - + I18N.getString("MSG_Help_mac_install"); - pInstallDir - = I18N.getString("MSG_Help_default_install_dir"); - pAppImageDescription - = I18N.getString("MSG_Help_default_app_image"); - pSignSampleUsage = ""; - pAppContentNote = ""; - break; - } - Log.info(MessageFormat.format(I18N.getString("MSG_Help"), - File.pathSeparator, types, pLaunchOptions, - pInstallOptions, pInstallDir, pAppImageDescription, - pSignSampleUsage, pAppContentNote)); - } - } -} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/CfgFile.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/CfgFile.java index 6b4ba3c4410..9cb9fb5cba0 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/CfgFile.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/CfgFile.java @@ -30,7 +30,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Optional; +import java.util.Objects; import java.util.stream.Stream; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ApplicationLayout; @@ -47,10 +47,14 @@ final class CfgFile { CfgFile(Application app, Launcher launcher) { startupInfo = launcher.startupInfo().orElseThrow(); outputFileName = launcher.executableName() + ".cfg"; - version = app.version(); + version = Objects.requireNonNull(app.version()); } void create(ApplicationLayout appLayout) throws IOException { + Objects.requireNonNull(appLayout); + + Objects.requireNonNull(startupInfo.qualifiedClassName()); + List> content = new ArrayList<>(); final var refs = new Referencies(appLayout); @@ -58,7 +62,7 @@ void create(ApplicationLayout appLayout) throws IOException { content.add(Map.entry("[Application]", SECTION_TAG)); if (startupInfo instanceof LauncherModularStartupInfo modularStartupInfo) { - content.add(Map.entry("app.mainmodule", modularStartupInfo.moduleName() + content.add(Map.entry("app.mainmodule", Objects.requireNonNull(modularStartupInfo.moduleName()) + "/" + startupInfo.qualifiedClassName())); } else if (startupInfo instanceof LauncherJarStartupInfo jarStartupInfo) { Path mainJarPath = refs.appDirectory().resolve(jarStartupInfo.jarPath()); @@ -67,16 +71,13 @@ void create(ApplicationLayout appLayout) throws IOException { content.add(Map.entry("app.mainjar", mainJarPath)); } else { content.add(Map.entry("app.classpath", mainJarPath)); - } - - if (!jarStartupInfo.isJarWithMainClass()) { content.add(Map.entry("app.mainclass", startupInfo.qualifiedClassName())); } } else { throw new UnsupportedOperationException(); } - for (var value : Optional.ofNullable(startupInfo.classPath()).orElseGet(List::of)) { + for (var value : startupInfo.classPath()) { content.add(Map.entry("app.classpath", refs.appDirectory().resolve(value).toString())); } @@ -88,7 +89,7 @@ void create(ApplicationLayout appLayout) throws IOException { "java-options", "-Djpackage.app-version=" + version)); // add user supplied java options if there are any - for (var value : Optional.ofNullable(startupInfo.javaOptions()).orElseGet(List::of)) { + for (var value : startupInfo.javaOptions()) { content.add(Map.entry("java-options", value)); } @@ -98,7 +99,7 @@ void create(ApplicationLayout appLayout) throws IOException { content.add(Map.entry("java-options", refs.appModsDirectory())); } - var arguments = Optional.ofNullable(startupInfo.defaultParameters()).orElseGet(List::of); + var arguments = startupInfo.defaultParameters(); if (!arguments.isEmpty()) { content.add(Map.entry("[ArgOptions]", SECTION_TAG)); for (var value : arguments) { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java new file mode 100644 index 00000000000..0bf7d6f41b2 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal; + +import static java.util.stream.Collectors.toMap; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.cli.CliBundlingEnvironment; +import jdk.jpackage.internal.cli.Options; +import jdk.jpackage.internal.cli.StandardBundlingOperation; +import jdk.jpackage.internal.model.AppImagePackageType; +import jdk.jpackage.internal.model.Application; +import jdk.jpackage.internal.model.BundlingOperationDescriptor; +import jdk.jpackage.internal.model.JPackageException; +import jdk.jpackage.internal.model.Package; +import jdk.jpackage.internal.model.PackageType; +import jdk.jpackage.internal.model.StandardPackageType; +import jdk.jpackage.internal.util.Result; + +class DefaultBundlingEnvironment implements CliBundlingEnvironment { + + DefaultBundlingEnvironment(Builder builder) { + this(Optional.ofNullable(builder.defaultOperationSupplier), builder.bundlers); + } + + DefaultBundlingEnvironment(Optional>> defaultOperationSupplier, + Map>>> bundlers) { + + this.bundlers = bundlers.entrySet().stream().collect(toMap(Map.Entry::getKey, e -> { + return new CachingSupplier<>(e.getValue()); + })); + + this.defaultOperationSupplier = Objects.requireNonNull(defaultOperationSupplier).map(CachingSupplier::new); + } + + + static final class Builder { + + Builder defaultOperation(Supplier> v) { + defaultOperationSupplier = v; + return this; + } + + Builder defaultOperation(StandardBundlingOperation v) { + return defaultOperation(() -> Optional.of(v.descriptor())); + } + + Builder bundler(StandardBundlingOperation op, Supplier>> bundlerSupplier) { + bundlers.put(Objects.requireNonNull(op.descriptor()), Objects.requireNonNull(bundlerSupplier)); + return this; + } + + Builder bundler(StandardBundlingOperation op, + Supplier> sysEnvResultSupplier, BiConsumer bundler) { + return bundler(op, createBundlerSupplier(sysEnvResultSupplier, bundler)); + } + + Builder bundler(StandardBundlingOperation op, Consumer bundler) { + Objects.requireNonNull(bundler); + return bundler(op, () -> Result.ofValue(bundler)); + } + + private Supplier> defaultOperationSupplier; + private final Map>>> bundlers = new HashMap<>(); + } + + + static Builder build() { + return new Builder(); + } + + static Supplier>> createBundlerSupplier( + Supplier> sysEnvResultSupplier, BiConsumer bundler) { + Objects.requireNonNull(sysEnvResultSupplier); + Objects.requireNonNull(bundler); + return () -> { + return sysEnvResultSupplier.get().map(sysEnv -> { + return options -> { + bundler.accept(options, sysEnv); + }; + }); + }; + } + + static void createApplicationImage(Options options, Application app, PackagingPipeline.Builder pipelineBuilder) { + Objects.requireNonNull(options); + Objects.requireNonNull(app); + Objects.requireNonNull(pipelineBuilder); + + final var outputDir = OptionUtils.outputDir(options).resolve(app.appImageDirName()); + + IOUtils.writableOutputDir(outputDir.getParent()); + + final var env = new BuildEnvFromOptions() + .predefinedAppImageLayout(app.asApplicationLayout().orElseThrow()) + .create(options, app); + + Log.verbose(I18N.format("message.creating-app-bundle", outputDir.getFileName(), outputDir.toAbsolutePath().getParent())); + + if (Files.exists(outputDir)) { + throw new JPackageException(I18N.format("error.root-exists", outputDir.toAbsolutePath())); + } + + pipelineBuilder.excludeDirFromCopying(outputDir.getParent()) + .create().execute(BuildEnv.withAppImageDir(env, outputDir), app); + } + + static void createNativePackage(Options options, + Function createPackage, + BiFunction createBuildEnv, + PackagingPipeline.Builder pipelineBuilder, + Packager.PipelineBuilderMutatorFactory pipelineBuilderMutatorFactory) { + + Objects.requireNonNull(pipelineBuilder); + createNativePackage(options, createPackage, createBuildEnv, _ -> pipelineBuilder, pipelineBuilderMutatorFactory); + } + + static void createNativePackage(Options options, + Function createPackage, + BiFunction createBuildEnv, + Function createPipelineBuilder, + Packager.PipelineBuilderMutatorFactory pipelineBuilderMutatorFactory) { + + Objects.requireNonNull(options); + Objects.requireNonNull(createPackage); + Objects.requireNonNull(createBuildEnv); + Objects.requireNonNull(createPipelineBuilder); + Objects.requireNonNull(pipelineBuilderMutatorFactory); + + var pkg = Objects.requireNonNull(createPackage.apply(options)); + + Packager.build().pkg(pkg) + .outputDir(OptionUtils.outputDir(options)) + .env(Objects.requireNonNull(createBuildEnv.apply(options, pkg))) + .pipelineBuilderMutatorFactory(pipelineBuilderMutatorFactory) + .execute(Objects.requireNonNull(createPipelineBuilder.apply(pkg))); + } + + @Override + public Optional defaultOperation() { + return defaultOperationSupplier.flatMap(Supplier::get); + } + + @Override + public void createBundle(BundlingOperationDescriptor op, Options cmdline) { + final var bundler = getBundlerSupplier(op).get().orElseThrow(); + Optional permanentWorkDirectory = Optional.empty(); + try (var tempDir = new TempDirectory(cmdline)) { + if (!tempDir.deleteOnClose()) { + permanentWorkDirectory = Optional.of(tempDir.path()); + } + bundler.accept(tempDir.options()); + + var packageType = OptionUtils.bundlingOperation(cmdline).packageType(); + + Log.verbose(I18N.format("message.bundle-created", I18N.getString(bundleTypeDescription(packageType, op.os())))); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } finally { + permanentWorkDirectory.ifPresent(workDir -> { + Log.verbose(I18N.format("message.debug-working-directory", workDir.toAbsolutePath())); + }); + } + } + + @Override + public Collection configurationErrors(BundlingOperationDescriptor op) { + return getBundlerSupplier(op).get().errors(); + } + + private Supplier>> getBundlerSupplier(BundlingOperationDescriptor op) { + return Optional.ofNullable(bundlers.get(op)).orElseThrow(NoSuchElementException::new); + } + + private String bundleTypeDescription(PackageType type, OperatingSystem os) { + switch (type) { + case StandardPackageType stdType -> { + switch (stdType) { + case WIN_MSI -> { + return "bundle-type.win-msi"; + } + case WIN_EXE -> { + return "bundle-type.win-exe"; + } + case LINUX_DEB -> { + return "bundle-type.linux-deb"; + } + case LINUX_RPM -> { + return "bundle-type.linux-rpm"; + } + case MAC_DMG -> { + return "bundle-type.mac-dmg"; + } + case MAC_PKG -> { + return "bundle-type.mac-pkg"; + } + default -> { + throw new AssertionError(); + } + } + } + case AppImagePackageType appImageType -> { + switch (os) { + case WINDOWS -> { + return "bundle-type.win-app"; + } + case LINUX -> { + return "bundle-type.linux-app"; + } + case MACOS -> { + return "bundle-type.mac-app"; + } + default -> { + throw new AssertionError(); + } + } + } + default -> { + throw new AssertionError(); + } + } + } + + + private static final class CachingSupplier implements Supplier { + + CachingSupplier(Supplier getter) { + this.getter = Objects.requireNonNull(getter); + } + + @Override + public T get() { + return cachedValue.updateAndGet(v -> { + return Optional.ofNullable(v).orElseGet(getter); + }); + } + + private final Supplier getter; + private final AtomicReference cachedValue = new AtomicReference<>(); + } + + + private final Map>>> bundlers; + private final Optional>> defaultOperationSupplier; +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DeployParams.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DeployParams.java deleted file mode 100644 index d7b4052d34a..00000000000 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DeployParams.java +++ /dev/null @@ -1,362 +0,0 @@ -/* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.jpackage.internal; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.InvalidPathException; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; -import java.util.stream.Stream; -import jdk.jpackage.internal.model.PackagerException; - -/** - * DeployParams - * - * This class is generated and used in Arguments.processArguments() as - * intermediate step in generating the BundleParams and ultimately the Bundles - */ -public class DeployParams { - - String targetFormat = null; // means default type for this platform - - // raw arguments to the bundler - Map bundlerArguments = new LinkedHashMap<>(); - - static class Template { - Path in; - Path out; - - Template(Path in, Path out) { - this.in = in; - this.out = out; - } - } - - // we need to expand as in some cases - // (most notably jpackage) - // we may get "." as filename and assumption is we include - // everything in the given folder - // (IOUtils.copyfiles() have recursive behavior) - List expandFileset(Path root) throws IOException { - List files = new LinkedList<>(); - if (!Files.isSymbolicLink(root)) { - if (Files.isDirectory(root)) { - try (Stream stream = Files.list(root)) { - List children = stream.toList(); - if (children != null && children.size() > 0) { - children.forEach(f -> { - try { - files.addAll(expandFileset(f)); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - }); - } else { - // Include empty folders - files.add(root); - } - } - } else { - files.add(root); - } - } - return files; - } - - static void validateName(String s, boolean forApp) - throws PackagerException { - - String exceptionKey = forApp ? - "ERR_InvalidAppName" : "ERR_InvalidSLName"; - - if (s == null) { - if (forApp) { - return; - } else { - throw new PackagerException(exceptionKey); - } - } - if (s.length() == 0 || s.charAt(s.length() - 1) == '\\') { - throw new PackagerException(exceptionKey, s); - } - try { - // name must be valid path element for this file system - Path p = Path.of(s); - // and it must be a single name element in a path - if (p.getNameCount() != 1) { - throw new PackagerException(exceptionKey, s); - } - } catch (InvalidPathException ipe) { - throw new PackagerException(ipe, exceptionKey, s); - } - - for (int i = 0; i < s.length(); i++) { - char a = s.charAt(i); - // We check for ASCII codes first which we accept. If check fails, - // check if it is acceptable extended ASCII or unicode character. - if (a < ' ' || a > '~') { - // Accept anything else including special chars like copyright - // symbols. Note: space will be included by ASCII check above, - // but other whitespace like tabs or new line will be rejected. - if (Character.isISOControl(a) || - Character.isWhitespace(a)) { - throw new PackagerException(exceptionKey, s); - } - } else if (a == '"' || a == '%') { - throw new PackagerException(exceptionKey, s); - } - } - } - - @SuppressWarnings("unchecked") - public void validate() throws PackagerException { - boolean hasModule = (bundlerArguments.get( - Arguments.CLIOptions.MODULE.getId()) != null); - boolean hasAppImage = (bundlerArguments.get( - Arguments.CLIOptions.PREDEFINED_APP_IMAGE.getId()) != null); - boolean hasMain = (bundlerArguments.get( - Arguments.CLIOptions.MAIN_JAR.getId()) != null); - boolean hasRuntimeImage = (bundlerArguments.get( - Arguments.CLIOptions.PREDEFINED_RUNTIME_IMAGE.getId()) != null); - boolean hasInput = (bundlerArguments.get( - Arguments.CLIOptions.INPUT.getId()) != null); - boolean hasModulePath = (bundlerArguments.get( - Arguments.CLIOptions.MODULE_PATH.getId()) != null); - boolean hasMacAppStore = (bundlerArguments.get( - Arguments.CLIOptions.MAC_APP_STORE.getId()) != null); - boolean runtimeInstaller = !isTargetAppImage() && - !hasAppImage && !hasModule && !hasMain && hasRuntimeImage; - - if (isTargetAppImage()) { - // Module application requires --runtime-image or --module-path - if (hasModule) { - if (!hasModulePath && !hasRuntimeImage && !hasAppImage) { - throw new PackagerException("ERR_MissingArgument", - "--runtime-image or --module-path"); - } - } else { - if (!hasInput && !hasAppImage) { - throw new PackagerException("error.no-input-parameter"); - } - } - } else { - if (!runtimeInstaller) { - if (hasModule) { - if (!hasModulePath && !hasRuntimeImage && !hasAppImage) { - throw new PackagerException("ERR_MissingArgument", - "--runtime-image, --module-path or --app-image"); - } - } else { - if (!hasInput && !hasAppImage) { - throw new PackagerException("ERR_MissingArgument", - "--input or --app-image"); - } - } - } - } - - // if bundling non-modular image, or installer without app-image - // then we need some resources and a main class - if (!hasModule && !hasAppImage && !runtimeInstaller && !hasMain) { - throw new PackagerException("ERR_MissingArgument", "--main-jar"); - } - - String name = (String)bundlerArguments.get( - Arguments.CLIOptions.NAME.getId()); - validateName(name, true); - - // Validate app image if set - String appImage = (String)bundlerArguments.get( - Arguments.CLIOptions.PREDEFINED_APP_IMAGE.getId()); - if (appImage != null) { - Path appImageDir = Path.of(appImage); - if (!Files.exists(appImageDir) - || appImageDir.toFile().list() == null - || appImageDir.toFile().list().length == 0) { - throw new PackagerException("ERR_AppImageNotExist", appImage); - } - } - - // Validate temp dir - String root = (String)bundlerArguments.get( - Arguments.CLIOptions.TEMP_ROOT.getId()); - if (root != null && Files.exists(Path.of(root))) { - try (Stream stream = Files.walk(Path.of(root), 1)) { - Path [] contents = stream.toArray(Path[]::new); - // contents.length > 1 because Files.walk(path) includes path - if (contents != null && contents.length > 1) { - throw new PackagerException( - "ERR_BuildRootInvalid", root); - } - } catch (IOException ioe) { - throw new PackagerException(ioe); - } - } - - // Validate resource dir - String resources = (String)bundlerArguments.get( - Arguments.CLIOptions.RESOURCE_DIR.getId()); - if (resources != null) { - if (!(Files.exists(Path.of(resources)))) { - throw new PackagerException( - "message.resource-dir-does-not-exist", - Arguments.CLIOptions.RESOURCE_DIR.getId(), resources); - } - } - - // Validate predefined runtime dir - String runtime = (String)bundlerArguments.get( - Arguments.CLIOptions.PREDEFINED_RUNTIME_IMAGE.getId()); - if (runtime != null) { - if (!(Files.exists(Path.of(runtime)))) { - throw new PackagerException( - "message.runtime-image-dir-does-not-exist", - Arguments.CLIOptions.PREDEFINED_RUNTIME_IMAGE.getId(), - runtime); - } - } - - - // Validate license file if set - String license = (String)bundlerArguments.get( - Arguments.CLIOptions.LICENSE_FILE.getId()); - if (license != null) { - if (!(Files.exists(Path.of(license)))) { - throw new PackagerException("ERR_LicenseFileNotExit"); - } - } - - // Validate icon file if set - String icon = (String)bundlerArguments.get( - Arguments.CLIOptions.ICON.getId()); - if (icon != null) { - if (!(Files.exists(Path.of(icon)))) { - throw new PackagerException("ERR_IconFileNotExit", - Path.of(icon).toAbsolutePath().toString()); - } - } - - - if (hasMacAppStore) { - // Validate jlink-options if mac-app-store is set - Object jlinkOptions = bundlerArguments.get( - Arguments.CLIOptions.JLINK_OPTIONS.getId()); - if (jlinkOptions instanceof List) { - List options = (List) jlinkOptions; - if (!options.contains("--strip-native-commands")) { - throw new PackagerException( - "ERR_MissingJLinkOptMacAppStore", - "--strip-native-commands"); - } - } - } - } - - void setTargetFormat(String t) { - targetFormat = t; - } - - String getTargetFormat() { - return targetFormat; - } - - boolean isTargetAppImage() { - return ("app-image".equals(targetFormat)); - } - - private static final Set multi_args = new TreeSet<>(Arrays.asList( - StandardBundlerParam.JAVA_OPTIONS.getID(), - StandardBundlerParam.ARGUMENTS.getID(), - StandardBundlerParam.MODULE_PATH.getID(), - StandardBundlerParam.ADD_MODULES.getID(), - StandardBundlerParam.LIMIT_MODULES.getID(), - StandardBundlerParam.FILE_ASSOCIATIONS.getID(), - StandardBundlerParam.DMG_CONTENT.getID(), - StandardBundlerParam.APP_CONTENT.getID(), - StandardBundlerParam.JLINK_OPTIONS.getID() - )); - - @SuppressWarnings("unchecked") - public void addBundleArgument(String key, Object value) { - // special hack for multi-line arguments - if (multi_args.contains(key)) { - Object existingValue = bundlerArguments.get(key); - if (existingValue instanceof String && value instanceof String) { - String delim = "\n\n"; - if (key.equals(StandardBundlerParam.MODULE_PATH.getID())) { - delim = File.pathSeparator; - } else if ( - key.equals(StandardBundlerParam.DMG_CONTENT.getID()) || - key.equals(StandardBundlerParam.APP_CONTENT.getID()) || - key.equals(StandardBundlerParam.ADD_MODULES.getID())) { - delim = ","; - } - bundlerArguments.put(key, existingValue + delim + value); - } else if (existingValue instanceof List && value instanceof List) { - ((List)existingValue).addAll((List)value); - } else if (existingValue instanceof Map && - value instanceof String && ((String)value).contains("=")) { - String[] mapValues = ((String)value).split("=", 2); - ((Map)existingValue).put(mapValues[0], mapValues[1]); - } else { - bundlerArguments.put(key, value); - } - } else { - bundlerArguments.put(key, value); - } - } - - BundleParams getBundleParams() { - BundleParams bundleParams = new BundleParams(); - - // check for collisions - TreeSet keys = new TreeSet<>(bundlerArguments.keySet()); - keys.retainAll(bundleParams.getBundleParamsAsMap().keySet()); - - if (!keys.isEmpty()) { - throw new RuntimeException("Deploy Params and Bundler Arguments " - + "overlap in the following values:" + keys.toString()); - } - - bundleParams.addAllBundleParams(bundlerArguments); - - return bundleParams; - } - - @Override - public String toString() { - return "DeployParams {" + "output: " + "}"; - } - -} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FileAssociationGroup.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FileAssociationGroup.java index 349a09f237c..ed89ffe1ef6 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FileAssociationGroup.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FileAssociationGroup.java @@ -111,6 +111,10 @@ Builder description(String v) { return this; } + Optional description() { + return Optional.ofNullable(description); + } + Builder mimeTypes(Collection v) { mimeTypes = Set.copyOf(v); return this; diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromOptions.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromOptions.java new file mode 100644 index 00000000000..6b74cab4e65 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromOptions.java @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal; + +import static jdk.jpackage.internal.ApplicationBuilder.normalizeIcons; +import static jdk.jpackage.internal.JLinkRuntimeBuilder.ensureBaseModuleInModulePath; +import static jdk.jpackage.internal.OptionUtils.isRuntimeInstaller; +import static jdk.jpackage.internal.cli.StandardOption.ABOUT_URL; +import static jdk.jpackage.internal.cli.StandardOption.ADDITIONAL_LAUNCHERS; +import static jdk.jpackage.internal.cli.StandardOption.ADD_MODULES; +import static jdk.jpackage.internal.cli.StandardOption.APP_CONTENT; +import static jdk.jpackage.internal.cli.StandardOption.APP_VERSION; +import static jdk.jpackage.internal.cli.StandardOption.COPYRIGHT; +import static jdk.jpackage.internal.cli.StandardOption.DESCRIPTION; +import static jdk.jpackage.internal.cli.StandardOption.INPUT; +import static jdk.jpackage.internal.cli.StandardOption.INSTALL_DIR; +import static jdk.jpackage.internal.cli.StandardOption.JLINK_OPTIONS; +import static jdk.jpackage.internal.cli.StandardOption.LICENSE_FILE; +import static jdk.jpackage.internal.cli.StandardOption.MODULE_PATH; +import static jdk.jpackage.internal.cli.StandardOption.NAME; +import static jdk.jpackage.internal.cli.StandardOption.PREDEFINED_APP_IMAGE; +import static jdk.jpackage.internal.cli.StandardOption.PREDEFINED_RUNTIME_IMAGE; +import static jdk.jpackage.internal.cli.StandardOption.RESOURCE_DIR; +import static jdk.jpackage.internal.cli.StandardOption.VENDOR; + +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.Function; +import jdk.jpackage.internal.cli.Options; +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; + +final class FromOptions { + + static ApplicationBuilderBuilder buildApplicationBuilder() { + return new ApplicationBuilderBuilder(); + } + + static PackageBuilder createPackageBuilder(Options options, Application app, PackageType type) { + + final var builder = new PackageBuilder(app, type); + + NAME.ifPresentIn(options, builder::name); + DESCRIPTION.ifPresentIn(options, builder::description); + APP_VERSION.ifPresentIn(options, builder::version); + ABOUT_URL.ifPresentIn(options, builder::aboutURL); + LICENSE_FILE.ifPresentIn(options, builder::licenseFile); + PREDEFINED_APP_IMAGE.ifPresentIn(options, builder::predefinedAppImage); + PREDEFINED_RUNTIME_IMAGE.ifPresentIn(options, builder::predefinedAppImage); + INSTALL_DIR.ifPresentIn(options, builder::installDir); + + return builder; + } + + + static final class ApplicationBuilderBuilder { + + private ApplicationBuilderBuilder() { + } + + ApplicationBuilder create(Options options, + Function launcherCtor, + BiFunction launcherOverrideCtor, + ApplicationLayout appLayout) { + + final Optional thePredefinedRuntimeLayout; + if (PREDEFINED_RUNTIME_IMAGE.containsIn(options)) { + thePredefinedRuntimeLayout = Optional.ofNullable( + predefinedRuntimeLayout).or(() -> Optional.of(RuntimeLayout.DEFAULT)); + } else { + thePredefinedRuntimeLayout = Optional.empty(); + } + + final var transfomer = new OptionsTransformer(options, appLayout); + final var appBuilder = createApplicationBuilder( + transfomer.appOptions(), + launcherCtor, + launcherOverrideCtor, + appLayout, + Optional.ofNullable(runtimeLayout).orElse(RuntimeLayout.DEFAULT), + thePredefinedRuntimeLayout); + + transfomer.externalApp().ifPresent(appBuilder::externalApplication); + + return appBuilder; + } + + /** + * Sets the layout of the predefined runtime image. + * @param v the layout of the predefined runtime image. Null is permitted. + * @return this + */ + ApplicationBuilderBuilder predefinedRuntimeLayout(RuntimeLayout v) { + predefinedRuntimeLayout = v; + return this; + } + + /** + * Sets the layout of a runtime bundle. + * @param v the layout of a runtime bundle. Null is permitted. + * @return this + */ + ApplicationBuilderBuilder runtimeLayout(RuntimeLayout v) { + runtimeLayout = v; + return this; + } + + private RuntimeLayout runtimeLayout; + private RuntimeLayout predefinedRuntimeLayout; + } + + + private static ApplicationBuilder createApplicationBuilder(Options options, + Function launcherCtor, + BiFunction launcherOverrideCtor, + ApplicationLayout appLayout, RuntimeLayout runtimeLayout, + Optional predefinedRuntimeLayout) { + + final var appBuilder = new ApplicationBuilder(); + + final var isRuntimeInstaller = isRuntimeInstaller(options); + + final var predefinedRuntimeImage = PREDEFINED_RUNTIME_IMAGE.findIn(options); + + final var predefinedRuntimeDirectory = predefinedRuntimeLayout.flatMap(layout -> { + return predefinedRuntimeImage.map(layout::resolveAt); + }).map(RuntimeLayout::runtimeDirectory); + + NAME.findIn(options).or(() -> { + if (isRuntimeInstaller) { + return predefinedRuntimeImage.map(Path::getFileName).map(Path::toString); + } else { + return Optional.empty(); + } + }).ifPresent(appBuilder::name); + DESCRIPTION.ifPresentIn(options, appBuilder::description); + APP_VERSION.ifPresentIn(options, appBuilder::version); + VENDOR.ifPresentIn(options, appBuilder::vendor); + COPYRIGHT.ifPresentIn(options, appBuilder::copyright); + INPUT.ifPresentIn(options, appBuilder::srcDir); + APP_CONTENT.ifPresentIn(options, appBuilder::contentDirs); + + if (isRuntimeInstaller) { + appBuilder.appImageLayout(runtimeLayout); + } else { + appBuilder.appImageLayout(appLayout); + + final var launchers = createLaunchers(options, launcherCtor); + + if (PREDEFINED_APP_IMAGE.containsIn(options)) { + appBuilder.launchers(launchers); + } else { + appBuilder.launchers(normalizeIcons(launchers, RESOURCE_DIR.findIn(options), launcherOverrideCtor)); + + final var runtimeBuilderBuilder = new RuntimeBuilderBuilder(); + + 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) + .map(Optional::orElseThrow).toList(); + final var jlinkOptionsBuilder = runtimeBuilderBuilder.forNewRuntime(startupInfos); + ADD_MODULES.findIn(options).map(Set::copyOf).ifPresent(jlinkOptionsBuilder::addModules); + JLINK_OPTIONS.ifPresentIn(options, jlinkOptionsBuilder::options); + jlinkOptionsBuilder.apply(); + }); + + appBuilder.runtimeBuilder(runtimeBuilderBuilder.create()); + } + } + + return appBuilder; + } + + private static ApplicationLaunchers createLaunchers(Options options, Function launcherCtor) { + var launchers = ADDITIONAL_LAUNCHERS.getFrom(options); + + var mainLauncher = launcherCtor.apply(options); + + // + // Additional launcher should: + // - Use description from the main launcher by default. + // + var mainLauncherDefaults = Options.of(Map.of(DESCRIPTION, mainLauncher.description())); + + var additionalLaunchers = launchers.stream().map(launcherOptions -> { + return launcherOptions.copyWithParent(mainLauncherDefaults); + }).map(launcherCtor).toList(); + + return new ApplicationLaunchers(mainLauncher, additionalLaunchers); + } +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java deleted file mode 100644 index cee7492dbc7..00000000000 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.jpackage.internal; - -import static jdk.jpackage.internal.Arguments.CLIOptions.LINUX_SHORTCUT_HINT; -import static jdk.jpackage.internal.Arguments.CLIOptions.WIN_MENU_HINT; -import static jdk.jpackage.internal.Arguments.CLIOptions.WIN_SHORTCUT_HINT; -import static jdk.jpackage.internal.StandardBundlerParam.ABOUT_URL; -import static jdk.jpackage.internal.StandardBundlerParam.ADD_LAUNCHERS; -import static jdk.jpackage.internal.StandardBundlerParam.ADD_MODULES; -import static jdk.jpackage.internal.StandardBundlerParam.APP_CONTENT; -import static jdk.jpackage.internal.StandardBundlerParam.APP_NAME; -import static jdk.jpackage.internal.StandardBundlerParam.COPYRIGHT; -import static jdk.jpackage.internal.StandardBundlerParam.DESCRIPTION; -import static jdk.jpackage.internal.StandardBundlerParam.FILE_ASSOCIATIONS; -import static jdk.jpackage.internal.StandardBundlerParam.ICON; -import static jdk.jpackage.internal.StandardBundlerParam.INSTALLER_NAME; -import static jdk.jpackage.internal.StandardBundlerParam.INSTALL_DIR; -import static jdk.jpackage.internal.StandardBundlerParam.JLINK_OPTIONS; -import static jdk.jpackage.internal.StandardBundlerParam.LAUNCHER_AS_SERVICE; -import static jdk.jpackage.internal.StandardBundlerParam.LICENSE_FILE; -import static jdk.jpackage.internal.StandardBundlerParam.LIMIT_MODULES; -import static jdk.jpackage.internal.StandardBundlerParam.MODULE_PATH; -import static jdk.jpackage.internal.StandardBundlerParam.NAME; -import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_APP_IMAGE; -import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_APP_IMAGE_FILE; -import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_RUNTIME_IMAGE; -import static jdk.jpackage.internal.StandardBundlerParam.RESOURCE_DIR; -import static jdk.jpackage.internal.StandardBundlerParam.SOURCE_DIR; -import static jdk.jpackage.internal.StandardBundlerParam.VENDOR; -import static jdk.jpackage.internal.StandardBundlerParam.VERSION; -import static jdk.jpackage.internal.StandardBundlerParam.hasPredefinedAppImage; -import static jdk.jpackage.internal.StandardBundlerParam.isRuntimeInstaller; -import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.BiFunction; -import java.util.function.Function; -import jdk.jpackage.internal.model.Application; -import jdk.jpackage.internal.model.ApplicationLaunchers; -import jdk.jpackage.internal.model.ApplicationLayout; -import jdk.jpackage.internal.model.ConfigException; -import jdk.jpackage.internal.model.ExternalApplication; -import jdk.jpackage.internal.model.ExternalApplication.LauncherInfo; -import jdk.jpackage.internal.model.Launcher; -import jdk.jpackage.internal.model.LauncherShortcut; -import jdk.jpackage.internal.model.LauncherShortcutStartupDirectory; -import jdk.jpackage.internal.model.PackageType; -import jdk.jpackage.internal.model.ParseUtils; -import jdk.jpackage.internal.model.RuntimeLayout; -import jdk.jpackage.internal.util.function.ThrowingFunction; - -final class FromParams { - - static ApplicationBuilder createApplicationBuilder(Map params, - Function, Launcher> launcherMapper, - BiFunction launcherOverrideCtor, - ApplicationLayout appLayout) throws ConfigException, IOException { - return createApplicationBuilder(params, launcherMapper, launcherOverrideCtor, appLayout, RuntimeLayout.DEFAULT, Optional.of(RuntimeLayout.DEFAULT)); - } - - static ApplicationBuilder createApplicationBuilder(Map params, - Function, Launcher> launcherMapper, - BiFunction launcherOverrideCtor, - ApplicationLayout appLayout, RuntimeLayout runtimeLayout, - Optional predefinedRuntimeLayout) throws ConfigException, IOException { - - final var appBuilder = new ApplicationBuilder(); - - APP_NAME.copyInto(params, appBuilder::name); - DESCRIPTION.copyInto(params, appBuilder::description); - appBuilder.version(VERSION.fetchFrom(params)); - VENDOR.copyInto(params, appBuilder::vendor); - COPYRIGHT.copyInto(params, appBuilder::copyright); - SOURCE_DIR.copyInto(params, appBuilder::srcDir); - APP_CONTENT.copyInto(params, appBuilder::contentDirs); - - final var isRuntimeInstaller = isRuntimeInstaller(params); - - final var predefinedRuntimeImage = PREDEFINED_RUNTIME_IMAGE.findIn(params); - - final var predefinedRuntimeDirectory = predefinedRuntimeLayout.flatMap( - layout -> predefinedRuntimeImage.map(layout::resolveAt)).map(RuntimeLayout::runtimeDirectory); - - if (isRuntimeInstaller) { - appBuilder.appImageLayout(runtimeLayout); - } else { - appBuilder.appImageLayout(appLayout); - - if (hasPredefinedAppImage(params)) { - final var appImageFile = PREDEFINED_APP_IMAGE_FILE.fetchFrom(params); - appBuilder.initFromExternalApplication(appImageFile, launcherInfo -> { - var launcherParams = mapLauncherInfo(appImageFile, launcherInfo); - return launcherMapper.apply(mergeParams(params, launcherParams)); - }); - } else { - final var launchers = createLaunchers(params, launcherMapper); - - final var runtimeBuilderBuilder = new RuntimeBuilderBuilder(); - - runtimeBuilderBuilder.modulePath(MODULE_PATH.fetchFrom(params)); - - predefinedRuntimeDirectory.ifPresentOrElse(runtimeBuilderBuilder::forRuntime, () -> { - final var startupInfos = launchers.asList().stream() - .map(Launcher::startupInfo) - .map(Optional::orElseThrow).toList(); - final var jlinkOptionsBuilder = runtimeBuilderBuilder.forNewRuntime(startupInfos); - ADD_MODULES.copyInto(params, jlinkOptionsBuilder::addModules); - LIMIT_MODULES.copyInto(params, jlinkOptionsBuilder::limitModules); - JLINK_OPTIONS.copyInto(params, jlinkOptionsBuilder::options); - jlinkOptionsBuilder.apply(); - }); - - final var normalizedLaunchers = ApplicationBuilder.normalizeIcons(launchers, RESOURCE_DIR.findIn(params), launcherOverrideCtor); - - appBuilder.launchers(normalizedLaunchers).runtimeBuilder(runtimeBuilderBuilder.create()); - } - } - - return appBuilder; - } - - static PackageBuilder createPackageBuilder( - Map params, Application app, - PackageType type) throws ConfigException { - - final var builder = new PackageBuilder(app, type); - - builder.name(INSTALLER_NAME.fetchFrom(params)); - DESCRIPTION.copyInto(params, builder::description); - VERSION.copyInto(params, builder::version); - ABOUT_URL.copyInto(params, builder::aboutURL); - LICENSE_FILE.findIn(params).map(Path::of).ifPresent(builder::licenseFile); - PREDEFINED_APP_IMAGE.findIn(params).ifPresent(builder::predefinedAppImage); - PREDEFINED_RUNTIME_IMAGE.findIn(params).ifPresent(builder::predefinedAppImage); - INSTALL_DIR.findIn(params).map(Path::of).ifPresent(builder::installDir); - - return builder; - } - - static BundlerParamInfo createApplicationBundlerParam( - ThrowingFunction, T> ctor) { - return BundlerParamInfo.createBundlerParam(Application.class, ctor); - } - - static BundlerParamInfo createPackageBundlerParam( - ThrowingFunction, T> ctor) { - return BundlerParamInfo.createBundlerParam(jdk.jpackage.internal.model.Package.class, ctor); - } - - static Optional getCurrentPackage(Map params) { - return Optional.ofNullable((jdk.jpackage.internal.model.Package)params.get( - jdk.jpackage.internal.model.Package.class.getName())); - } - - static Optional findLauncherShortcut( - BundlerParamInfo shortcutParam, - Map mainParams, - Map launcherParams) { - - Optional launcherValue; - if (launcherParams == mainParams) { - // The main launcher - launcherValue = Optional.empty(); - } else { - launcherValue = shortcutParam.findIn(launcherParams); - } - - return launcherValue.map(ParseUtils::parseLauncherShortcutForAddLauncher).or(() -> { - return Optional.ofNullable(mainParams.get(shortcutParam.getID())).map(toFunction(value -> { - if (value instanceof Boolean) { - return new LauncherShortcut(LauncherShortcutStartupDirectory.DEFAULT); - } else { - try { - return ParseUtils.parseLauncherShortcutForMainLauncher((String)value); - } catch (IllegalArgumentException ex) { - throw I18N.buildConfigException("error.invalid-option-value", value, "--" + shortcutParam.getID()).create(); - } - } - })); - }); - } - - private static ApplicationLaunchers createLaunchers( - Map params, - Function, Launcher> launcherMapper) { - var launchers = ADD_LAUNCHERS.findIn(params).orElseGet(List::of); - - var mainLauncher = launcherMapper.apply(params); - var additionalLaunchers = launchers.stream().map(launcherParams -> { - return launcherMapper.apply(mergeParams(params, launcherParams)); - }).toList(); - - return new ApplicationLaunchers(mainLauncher, additionalLaunchers); - } - - private static Map mapLauncherInfo(ExternalApplication appImageFile, LauncherInfo launcherInfo) { - Map launcherParams = new HashMap<>(); - launcherParams.put(NAME.getID(), launcherInfo.name()); - if (!appImageFile.getLauncherName().equals(launcherInfo.name())) { - // This is not the main launcher, accept the value - // of "launcher-as-service" from the app image file (.jpackage.xml). - launcherParams.put(LAUNCHER_AS_SERVICE.getID(), Boolean.toString(launcherInfo.service())); - } - launcherParams.putAll(launcherInfo.extra()); - return launcherParams; - } - - private static Map mergeParams(Map mainParams, - Map launcherParams) { - if (!launcherParams.containsKey(DESCRIPTION.getID())) { - launcherParams = new HashMap<>(launcherParams); -// FIXME: this is a good improvement but it fails existing tests -// launcherParams.put(DESCRIPTION.getID(), String.format("%s (%s)", DESCRIPTION.fetchFrom( -// mainParams), APP_NAME.fetchFrom(launcherParams))); - launcherParams.put(DESCRIPTION.getID(), DESCRIPTION.fetchFrom(mainParams)); - } - return AddLauncherArguments.merge(mainParams, launcherParams, ICON.getID(), - ADD_LAUNCHERS.getID(), FILE_ASSOCIATIONS.getID(), WIN_MENU_HINT.getId(), - WIN_SHORTCUT_HINT.getId(), LINUX_SHORTCUT_HINT.getId()); - } - - static final BundlerParamInfo APPLICATION = createApplicationBundlerParam(null); -} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java index 427051719bb..aac113d7777 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java @@ -32,7 +32,7 @@ import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.List; -import jdk.jpackage.internal.model.PackagerException; +import jdk.jpackage.internal.model.JPackageException; /** * IOUtils @@ -90,19 +90,17 @@ static void exec(ProcessBuilder pb, boolean testForPresenceOnly, } } - static void writableOutputDir(Path outdir) throws PackagerException { + static void writableOutputDir(Path outdir) { if (!Files.isDirectory(outdir)) { try { Files.createDirectories(outdir); } catch (IOException ex) { - throw new PackagerException("error.cannot-create-output-dir", - outdir.toAbsolutePath().toString()); + throw new JPackageException(I18N.format("error.cannot-create-output-dir", outdir.toAbsolutePath())); } } if (!Files.isWritable(outdir)) { - throw new PackagerException("error.cannot-write-to-output-dir", - outdir.toAbsolutePath().toString()); + throw new JPackageException(I18N.format("error.cannot-write-to-output-dir", outdir.toAbsolutePath())); } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java index 6ac9758e179..37f166a4e7c 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java @@ -36,7 +36,6 @@ import java.lang.module.ResolvedModule; import java.nio.file.Files; import java.nio.file.Path; -import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -51,10 +50,9 @@ import java.util.stream.Stream; import jdk.internal.module.ModulePath; import jdk.jpackage.internal.model.AppImageLayout; -import jdk.jpackage.internal.model.ConfigException; +import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.LauncherModularStartupInfo; import jdk.jpackage.internal.model.LauncherStartupInfo; -import jdk.jpackage.internal.model.PackagerException; import jdk.jpackage.internal.model.RuntimeBuilder; final class JLinkRuntimeBuilder implements RuntimeBuilder { @@ -64,7 +62,7 @@ private JLinkRuntimeBuilder(List jlinkCmdLine) { } @Override - public void create(AppImageLayout appImageLayout) throws PackagerException { + public void create(AppImageLayout appImageLayout) { var args = new ArrayList(); args.add("--output"); args.add(appImageLayout.runtimeDirectory().toString()); @@ -79,7 +77,7 @@ public void create(AppImageLayout appImageLayout) throws PackagerException { args.add(0, "jlink"); Log.verbose(args, List.of(jlinkOut), retVal, -1); if (retVal != 0) { - throw new PackagerException("error.jlink.failed", jlinkOut); + throw new JPackageException(I18N.format("error.jlink.failed", jlinkOut)); } } @@ -96,7 +94,7 @@ static ModuleFinder createModuleFinder(Collection modulePath) { } static RuntimeBuilder createJLinkRuntimeBuilder(List modulePath, Set addModules, - Set limitModules, List options, List startupInfos) throws ConfigException { + Set limitModules, List options, List startupInfos) { return new JLinkRuntimeBuilder(createJLinkCmdline(modulePath, addModules, limitModules, options, startupInfos)); } @@ -148,7 +146,7 @@ static List ensureBaseModuleInModulePath(List modulePath) { } private static List createJLinkCmdline(List modulePath, Set addModules, - Set limitModules, List options, List startupInfos) throws ConfigException { + Set limitModules, List options, List startupInfos) { List launcherModules = startupInfos.stream().map(si -> { if (si instanceof LauncherModularStartupInfo siModular) { return siModular.moduleName(); @@ -182,8 +180,7 @@ private static List createJLinkCmdline(List modulePath, Set { - throw new ConfigException(MessageFormat.format(I18N.getString( - "error.blocked.option"), option), null); + throw I18N.buildConfigException("error.blocked.option", option).create(); } default -> { args.add(option); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JPackageToolProvider.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JPackageToolProvider.java deleted file mode 100644 index 079d03a076c..00000000000 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JPackageToolProvider.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2017, 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. 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.PrintWriter; -import java.util.Optional; -import java.util.spi.ToolProvider; - -/** - * JPackageToolProvider - * - * This is the ToolProvider implementation exported - * to java.util.spi.ToolProvider and ultimately javax.tools.ToolProvider - */ -public class JPackageToolProvider implements ToolProvider { - - public String name() { - return "jpackage"; - } - - public Optional description() { - return Optional.of(jdk.jpackage.main.Main.I18N.getString("jpackage.description")); - } - - public synchronized int run( - PrintWriter out, PrintWriter err, String... args) { - try { - return new jdk.jpackage.main.Main().execute(out, err, args); - } catch (RuntimeException re) { - Log.fatalError(re.getMessage()); - Log.verbose(re); - return 1; - } - } -} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherBuilder.java index 0f6b5d6ac8d..e3720e0a776 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherBuilder.java @@ -25,7 +25,6 @@ package jdk.jpackage.internal; import static jdk.jpackage.internal.I18N.buildConfigException; -import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; import java.nio.file.Path; import java.util.List; @@ -34,23 +33,20 @@ import java.util.Optional; import java.util.function.Function; import jdk.internal.util.OperatingSystem; -import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.CustomLauncherIcon; -import jdk.jpackage.internal.model.DefaultLauncherIcon; import jdk.jpackage.internal.model.FileAssociation; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.Launcher.Stub; -import jdk.jpackage.internal.util.PathUtils; import jdk.jpackage.internal.model.LauncherIcon; import jdk.jpackage.internal.model.LauncherStartupInfo; -import jdk.jpackage.internal.model.ResourceDirLauncherIcon; +import jdk.jpackage.internal.util.PathUtils; final class LauncherBuilder { - Launcher create() throws ConfigException { + Launcher create() { CustomLauncherIcon.fromLauncherIcon(icon) .map(CustomLauncherIcon::path) - .ifPresent(toConsumer(LauncherBuilder::validateIcon)); + .ifPresent(LauncherBuilder::validateIcon); final var fa = createFileAssociations(faSources, Optional.ofNullable(faTraits).orElse(DEFAULT_FA_TRAITS)); @@ -123,7 +119,7 @@ static OverridableResource createLauncherIconResource(Launcher launcher, .setCategory("icon"); } - static void validateIcon(Path icon) throws ConfigException { + static Path validateIcon(Path icon) { switch (OperatingSystem.current()) { case WINDOWS -> { if (!icon.getFileName().toString().toLowerCase().endsWith(".ico")) { @@ -144,13 +140,15 @@ static void validateIcon(Path icon) throws ConfigException { throw new UnsupportedOperationException(); } } + + return icon; } record FileAssociationTraits() { } private static List createFileAssociations( - List groups, FileAssociationTraits faTraits) throws ConfigException { + List groups, FileAssociationTraits faTraits) { Objects.requireNonNull(groups); Objects.requireNonNull(faTraits); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherData.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherData.java deleted file mode 100644 index 488e9106479..00000000000 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherData.java +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.jpackage.internal; - -import jdk.jpackage.internal.model.ConfigException; -import java.io.File; -import java.io.IOException; -import java.lang.module.ModuleReference; -import java.nio.file.Files; -import java.nio.file.InvalidPathException; -import java.nio.file.Path; -import java.text.MessageFormat; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.function.Supplier; -import java.util.jar.Attributes; -import java.util.jar.JarFile; -import java.util.jar.Manifest; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_RUNTIME_IMAGE; - -/** - * Extracts data needed to run application from parameters. - */ -final class LauncherData { - boolean isModular() { - return moduleInfo != null; - } - - String qualifiedClassName() { - return qualifiedClassName; - } - - boolean isClassNameFromMainJar() { - return jarMainClass != null; - } - - String packageName() { - int sepIdx = qualifiedClassName.lastIndexOf('.'); - if (sepIdx < 0) { - return ""; - } - return qualifiedClassName.substring(sepIdx + 1); - } - - String moduleName() { - verifyIsModular(true); - return moduleInfo.name(); - } - - List modulePath() { - verifyIsModular(true); - return modulePath; - } - - Path mainJarName() { - verifyIsModular(false); - return mainJarName; - } - - List classPath() { - return classPath; - } - - String getAppVersion() { - if (isModular()) { - return moduleInfo.version().orElse(null); - } - - return null; - } - - private LauncherData() { - } - - private void verifyIsModular(boolean isModular) { - if ((moduleInfo == null) == isModular) { - throw new IllegalStateException(); - } - } - - static LauncherData create(Map params) throws - ConfigException, IOException { - - final String mainModule = getMainModule(params); - final LauncherData result; - if (mainModule == null) { - result = createNonModular(params); - } else { - result = createModular(mainModule, params); - } - result.initClasspath(params); - return result; - } - - private static LauncherData createModular(String mainModule, - Map params) throws ConfigException, - IOException { - - LauncherData launcherData = new LauncherData(); - - final int sepIdx = mainModule.indexOf("/"); - final String moduleName; - if (sepIdx > 0) { - launcherData.qualifiedClassName = mainModule.substring(sepIdx + 1); - moduleName = mainModule.substring(0, sepIdx); - } else { - moduleName = mainModule; - } - launcherData.modulePath = getModulePath(params); - - // Try to find module in the specified module path list. - ModuleReference moduleRef = JLinkRuntimeBuilder.createModuleFinder( - launcherData.modulePath).find(moduleName).orElse(null); - - if (moduleRef != null) { - launcherData.moduleInfo = ModuleInfo.fromModuleReference(moduleRef); - } else if (params.containsKey(PREDEFINED_RUNTIME_IMAGE.getID())) { - // Failed to find module in the specified module path list and - // there is external runtime given to jpackage. - // Lookup module in this runtime. - Path cookedRuntime = PREDEFINED_RUNTIME_IMAGE.fetchFrom(params); - launcherData.moduleInfo = ModuleInfo.fromCookedRuntime(moduleName, - cookedRuntime).orElse(null); - } - - if (launcherData.moduleInfo == null) { - throw new ConfigException(MessageFormat.format(I18N.getString( - "error.no-module-in-path"), moduleName), null); - } - - if (launcherData.qualifiedClassName == null) { - launcherData.qualifiedClassName = launcherData.moduleInfo.mainClass().orElse(null); - if (launcherData.qualifiedClassName == null) { - throw new ConfigException(I18N.getString("ERR_NoMainClass"), null); - } - } - - return launcherData; - } - - private static LauncherData createNonModular( - Map params) throws ConfigException, IOException { - LauncherData launcherData = new LauncherData(); - - launcherData.qualifiedClassName = getMainClass(params); - - launcherData.mainJarName = getMainJarName(params); - - Path mainJarDir = StandardBundlerParam.SOURCE_DIR.fetchFrom(params); - - final Path mainJarPath; - if (launcherData.mainJarName != null && mainJarDir != null) { - mainJarPath = mainJarDir.resolve(launcherData.mainJarName); - if (!Files.exists(mainJarPath)) { - throw new ConfigException(MessageFormat.format(I18N.getString( - "error.main-jar-does-not-exist"), - launcherData.mainJarName), I18N.getString( - "error.main-jar-does-not-exist.advice")); - } - } else { - mainJarPath = null; - } - - if (launcherData.qualifiedClassName == null) { - if (mainJarPath == null) { - throw new ConfigException(I18N.getString("error.no-main-class"), - I18N.getString("error.no-main-class.advice")); - } - - try (JarFile jf = new JarFile(mainJarPath.toFile())) { - Manifest m = jf.getManifest(); - Attributes attrs = (m != null) ? m.getMainAttributes() : null; - if (attrs != null) { - launcherData.qualifiedClassName = attrs.getValue( - Attributes.Name.MAIN_CLASS); - launcherData.jarMainClass = launcherData.qualifiedClassName; - } - } - } - - if (launcherData.qualifiedClassName == null) { - throw new ConfigException(MessageFormat.format(I18N.getString( - "error.no-main-class-with-main-jar"), - launcherData.mainJarName), MessageFormat.format( - I18N.getString( - "error.no-main-class-with-main-jar.advice"), - launcherData.mainJarName)); - } - - return launcherData; - } - - private void initClasspath(Map params) - throws IOException { - Path inputDir = StandardBundlerParam.SOURCE_DIR.fetchFrom(params); - if (inputDir == null) { - classPath = Collections.emptyList(); - } else { - try (Stream walk = Files.walk(inputDir, Integer.MAX_VALUE)) { - Set jars = walk.filter(Files::isRegularFile) - .filter(file -> file.toString().endsWith(".jar")) - .map(p -> inputDir.toAbsolutePath() - .relativize(p.toAbsolutePath())) - .collect(Collectors.toSet()); - jars.remove(mainJarName); - classPath = jars.stream().sorted().toList(); - } - } - } - - private static String getMainClass(Map params) { - return getStringParam(params, Arguments.CLIOptions.APPCLASS.getId()); - } - - private static Path getMainJarName(Map params) - throws ConfigException { - return getPathParam(params, Arguments.CLIOptions.MAIN_JAR.getId()); - } - - private static String getMainModule(Map params) { - return getStringParam(params, Arguments.CLIOptions.MODULE.getId()); - } - - private static String getStringParam(Map params, - String paramName) { - Optional value = Optional.ofNullable(params.get(paramName)); - return value.map(Object::toString).orElse(null); - } - - private static T getPathParam(String paramName, Supplier func) throws ConfigException { - try { - return func.get(); - } catch (InvalidPathException ex) { - throw new ConfigException(MessageFormat.format(I18N.getString( - "error.not-path-parameter"), paramName, - ex.getLocalizedMessage()), null, ex); - } - } - - private static Path getPathParam(Map params, - String paramName) throws ConfigException { - return getPathParam(paramName, () -> { - String value = getStringParam(params, paramName); - Path result = null; - if (value != null) { - result = Path.of(value); - } - return result; - }); - } - - private static List getModulePath(Map params) - throws ConfigException { - List modulePath = getPathListParameter(Arguments.CLIOptions.MODULE_PATH.getId(), params); - - if (params.containsKey(PREDEFINED_RUNTIME_IMAGE.getID())) { - Path runtimePath = PREDEFINED_RUNTIME_IMAGE.fetchFrom(params); - runtimePath = runtimePath.resolve("lib"); - modulePath = Stream.of(modulePath, List.of(runtimePath)) - .flatMap(List::stream) - .toList(); - } - - return modulePath; - } - - private static List getPathListParameter(String paramName, - Map params) throws ConfigException { - return getPathParam(paramName, () -> - params.get(paramName) instanceof String value ? - Stream.of(value.split(File.pathSeparator)).map(Path::of).toList() : List.of()); - } - - private String qualifiedClassName; - private String jarMainClass; - private Path mainJarName; - private List classPath; - private List modulePath; - private ModuleInfo moduleInfo; -} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherFromOptions.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherFromOptions.java new file mode 100644 index 00000000000..0749cc48b9b --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherFromOptions.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal; + +import static jdk.jpackage.internal.cli.StandardOption.APPCLASS; +import static jdk.jpackage.internal.cli.StandardOption.ARGUMENTS; +import static jdk.jpackage.internal.cli.StandardOption.DESCRIPTION; +import static jdk.jpackage.internal.cli.StandardOption.FILE_ASSOCIATIONS; +import static jdk.jpackage.internal.cli.StandardOption.ICON; +import static jdk.jpackage.internal.cli.StandardOption.INPUT; +import static jdk.jpackage.internal.cli.StandardOption.JAVA_OPTIONS; +import static jdk.jpackage.internal.cli.StandardOption.LAUNCHER_AS_SERVICE; +import static jdk.jpackage.internal.cli.StandardOption.MAIN_JAR; +import static jdk.jpackage.internal.cli.StandardOption.MODULE; +import static jdk.jpackage.internal.cli.StandardOption.MODULE_PATH; +import static jdk.jpackage.internal.cli.StandardOption.NAME; +import static jdk.jpackage.internal.cli.StandardOption.PREDEFINED_APP_IMAGE; +import static jdk.jpackage.internal.cli.StandardOption.PREDEFINED_RUNTIME_IMAGE; + +import java.nio.file.Path; +import java.util.List; +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.stream.IntStream; +import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.FileAssociationGroup.FileAssociationException; +import jdk.jpackage.internal.FileAssociationGroup.FileAssociationNoExtensionsException; +import jdk.jpackage.internal.FileAssociationGroup.FileAssociationNoMimesException; +import jdk.jpackage.internal.cli.Options; +import jdk.jpackage.internal.cli.StandardFaOption; +import jdk.jpackage.internal.model.CustomLauncherIcon; +import jdk.jpackage.internal.model.DefaultLauncherIcon; +import jdk.jpackage.internal.model.FileAssociation; +import jdk.jpackage.internal.model.Launcher; +import jdk.jpackage.internal.model.LauncherIcon; + +final class LauncherFromOptions { + + LauncherFromOptions() { + } + + LauncherFromOptions faGroupBuilderMutator(BiConsumer v) { + faGroupBuilderMutator = v; + return this; + } + + LauncherFromOptions faMapper(BiFunction v) { + faMapper = v; + return this; + } + + LauncherFromOptions faWithDefaultDescription() { + return faGroupBuilderMutator((faGroupBuilder, launcherBuilder) -> { + if (faGroupBuilder.description().isEmpty()) { + var description = String.format("%s association", launcherBuilder.create().name()); + faGroupBuilder.description(description); + } + }); + } + + Launcher create(Options options) { + final var builder = new LauncherBuilder().defaultIconResourceName(defaultIconResourceName()); + + DESCRIPTION.ifPresentIn(options, builder::description); + builder.icon(toLauncherIcon(ICON.findIn(options).orElse(null))); + LAUNCHER_AS_SERVICE.ifPresentIn(options, builder::isService); + NAME.ifPresentIn(options, builder::name); + + if (PREDEFINED_APP_IMAGE.findIn(options).isEmpty()) { + final var startupInfoBuilder = new LauncherStartupInfoBuilder(); + + INPUT.ifPresentIn(options, startupInfoBuilder::inputDir); + ARGUMENTS.ifPresentIn(options, startupInfoBuilder::defaultParameters); + JAVA_OPTIONS.ifPresentIn(options, startupInfoBuilder::javaOptions); + MAIN_JAR.ifPresentIn(options, startupInfoBuilder::mainJar); + APPCLASS.ifPresentIn(options, startupInfoBuilder::mainClassName); + MODULE.ifPresentIn(options, startupInfoBuilder::moduleName); + MODULE_PATH.ifPresentIn(options, startupInfoBuilder::modulePath); + PREDEFINED_RUNTIME_IMAGE.ifPresentIn(options, startupInfoBuilder::predefinedRuntimeImage); + + builder.startupInfo(startupInfoBuilder.create()); + } + + final var faOptionsList = FILE_ASSOCIATIONS.findIn(options).orElseGet(List::of); + + final var faGroups = IntStream.range(0, faOptionsList.size()).mapToObj(idx -> { + final var faOptions = faOptionsList.get(idx); + + final var faGroupBuilder = FileAssociationGroup.build(); + + StandardFaOption.DESCRIPTION.ifPresentIn(faOptions, faGroupBuilder::description); + StandardFaOption.ICON.ifPresentIn(faOptions, faGroupBuilder::icon); + StandardFaOption.EXTENSIONS.ifPresentIn(faOptions, faGroupBuilder::extensions); + StandardFaOption.CONTENT_TYPE.ifPresentIn(faOptions, faGroupBuilder::mimeTypes); + + faGroupBuilderMutator().ifPresent(mutator -> { + mutator.accept(faGroupBuilder, builder); + }); + + final var faID = idx + 1; + + final FileAssociationGroup faGroup; + try { + faGroup = faGroupBuilder.create(); + } catch (FileAssociationNoMimesException ex) { + throw I18N.buildConfigException() + .message("error.no-content-types-for-file-association", faID) + .advice("error.no-content-types-for-file-association.advice", faID) + .create(); + } catch (FileAssociationNoExtensionsException ex) { + // TODO: Must do something about this condition! + throw new AssertionError(); + } catch (FileAssociationException ex) { + // Should never happen + throw new UnsupportedOperationException(ex); + } + + return faMapper().map(mapper -> { + return new FileAssociationGroup(faGroup.items().stream().map(fa -> { + return mapper.apply(faOptions, fa); + }).toList()); + }).orElse(faGroup); + + }).toList(); + + return builder.faGroups(faGroups).create(); + } + + private Optional> faGroupBuilderMutator() { + return Optional.ofNullable(faGroupBuilderMutator); + } + + private Optional> faMapper() { + return Optional.ofNullable(faMapper); + } + + private static LauncherIcon toLauncherIcon(Path launcherIconPath) { + if (launcherIconPath == null) { + return DefaultLauncherIcon.INSTANCE; + } else if (launcherIconPath.toString().isEmpty()) { + return null; + } else { + return CustomLauncherIcon.create(launcherIconPath); + } + } + + private static String defaultIconResourceName() { + switch (OperatingSystem.current()) { + case WINDOWS -> { + return "JavaApp.ico"; + } + case LINUX -> { + return "JavaApp.png"; + } + case MACOS -> { + return "JavaApp.icns"; + } + default -> { + throw new UnsupportedOperationException(); + } + } + } + + private BiConsumer faGroupBuilderMutator; + private BiFunction faMapper; +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherFromParams.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherFromParams.java deleted file mode 100644 index b4ce1cdb200..00000000000 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherFromParams.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.jpackage.internal; - -import static jdk.jpackage.internal.I18N.buildConfigException; -import static jdk.jpackage.internal.StandardBundlerParam.ARGUMENTS; -import static jdk.jpackage.internal.StandardBundlerParam.DESCRIPTION; -import static jdk.jpackage.internal.StandardBundlerParam.FA_CONTENT_TYPE; -import static jdk.jpackage.internal.StandardBundlerParam.FA_DESCRIPTION; -import static jdk.jpackage.internal.StandardBundlerParam.FA_EXTENSIONS; -import static jdk.jpackage.internal.StandardBundlerParam.FA_ICON; -import static jdk.jpackage.internal.StandardBundlerParam.FILE_ASSOCIATIONS; -import static jdk.jpackage.internal.StandardBundlerParam.ICON; -import static jdk.jpackage.internal.StandardBundlerParam.JAVA_OPTIONS; -import static jdk.jpackage.internal.StandardBundlerParam.LAUNCHER_AS_SERVICE; -import static jdk.jpackage.internal.StandardBundlerParam.LAUNCHER_DATA; -import static jdk.jpackage.internal.StandardBundlerParam.NAME; -import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_APP_IMAGE; -import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; - -import java.nio.file.Path; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.function.BiFunction; -import java.util.stream.IntStream; -import jdk.internal.util.OperatingSystem; -import jdk.jpackage.internal.model.ConfigException; -import jdk.jpackage.internal.model.CustomLauncherIcon; -import jdk.jpackage.internal.model.DefaultLauncherIcon; -import jdk.jpackage.internal.model.FileAssociation; -import jdk.jpackage.internal.model.Launcher; -import jdk.jpackage.internal.model.LauncherIcon; - -record LauncherFromParams(Optional, FileAssociation>> faExtension) { - - LauncherFromParams { - Objects.requireNonNull(faExtension); - } - - LauncherFromParams() { - this(Optional.empty()); - } - - Launcher create(Map params) throws ConfigException { - final var builder = new LauncherBuilder().defaultIconResourceName(defaultIconResourceName()); - - DESCRIPTION.copyInto(params, builder::description); - builder.icon(toLauncherIcon(ICON.findIn(params).orElse(null))); - LAUNCHER_AS_SERVICE.copyInto(params, builder::isService); - NAME.copyInto(params, builder::name); - - if (PREDEFINED_APP_IMAGE.findIn(params).isEmpty()) { - final var startupInfoBuilder = new LauncherStartupInfoBuilder(); - - startupInfoBuilder.launcherData(LAUNCHER_DATA.fetchFrom(params)); - ARGUMENTS.copyInto(params, startupInfoBuilder::defaultParameters); - JAVA_OPTIONS.copyInto(params, startupInfoBuilder::javaOptions); - - builder.startupInfo(startupInfoBuilder.create()); - } - - final var faParamsList = FILE_ASSOCIATIONS.findIn(params).orElseGet(List::of); - - final var faGroups = IntStream.range(0, faParamsList.size()).mapToObj(idx -> { - final var faParams = faParamsList.get(idx); - return toSupplier(() -> { - final var faGroupBuilder = FileAssociationGroup.build(); - - if (OperatingSystem.current() == OperatingSystem.MACOS) { - FA_DESCRIPTION.copyInto(faParams, faGroupBuilder::description); - } else { - faGroupBuilder.description(FA_DESCRIPTION.findIn(faParams).orElseGet(() -> { - return String.format("%s association", toSupplier(builder::create).get().name()); - })); - } - - FA_ICON.copyInto(faParams, faGroupBuilder::icon); - FA_EXTENSIONS.copyInto(faParams, faGroupBuilder::extensions); - FA_CONTENT_TYPE.copyInto(faParams, faGroupBuilder::mimeTypes); - - final var faID = idx + 1; - - final FileAssociationGroup faGroup; - try { - faGroup = faGroupBuilder.create(); - } catch (FileAssociationGroup.FileAssociationNoMimesException ex) { - throw buildConfigException() - .message("error.no-content-types-for-file-association", faID) - .advice("error.no-content-types-for-file-association.advice", faID) - .create(); - } - - if (faExtension.isPresent()) { - return new FileAssociationGroup(faGroup.items().stream().map(fa -> { - return faExtension.get().apply(fa, faParams); - }).toList()); - } else { - return faGroup; - } - }).get(); - }).toList(); - - return builder.faGroups(faGroups).create(); - } - - private static LauncherIcon toLauncherIcon(Path launcherIconPath) { - if (launcherIconPath == null) { - return DefaultLauncherIcon.INSTANCE; - } else if (launcherIconPath.toString().isEmpty()) { - return null; - } else { - return CustomLauncherIcon.create(launcherIconPath); - } - } - - private static String defaultIconResourceName() { - switch (OperatingSystem.current()) { - case WINDOWS -> { - return "JavaApp.ico"; - } - case LINUX -> { - return "JavaApp.png"; - } - case MACOS -> { - return "JavaApp.icns"; - } - default -> { - throw new UnsupportedOperationException(); - } - } - } -} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherStartupInfoBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherStartupInfoBuilder.java index 5273f2d251c..b2fc48af9e4 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherStartupInfoBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherStartupInfoBuilder.java @@ -24,69 +24,216 @@ */ package jdk.jpackage.internal; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; import java.nio.file.Path; import java.util.List; -import java.util.function.UnaryOperator; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Predicate; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.stream.Stream; +import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.LauncherJarStartupInfo; import jdk.jpackage.internal.model.LauncherJarStartupInfoMixin; import jdk.jpackage.internal.model.LauncherModularStartupInfo; import jdk.jpackage.internal.model.LauncherModularStartupInfoMixin; -import jdk.jpackage.internal.model.LauncherStartupInfo.Stub; import jdk.jpackage.internal.model.LauncherStartupInfo; final class LauncherStartupInfoBuilder { LauncherStartupInfo create() { - return decorator.apply(new Stub(qualifiedClassName, javaOptions, - defaultParameters, classPath)); - } - - LauncherStartupInfoBuilder launcherData(LauncherData launcherData) { - if (launcherData.isModular()) { - decorator = new ModuleStartupInfo(launcherData.moduleName()); + if (moduleName != null) { + return createModular(); + } else if (mainJar != null) { + return createNonModular(); } else { - decorator = new JarStartupInfo(launcherData.mainJarName(), - launcherData.isClassNameFromMainJar()); + throw new JPackageException(I18N.format("ERR_NoEntryPoint")); } - classPath = launcherData.classPath(); - qualifiedClassName = launcherData.qualifiedClassName(); + } + + LauncherStartupInfoBuilder inputDir(Path v) { + inputDir = v; return this; } LauncherStartupInfoBuilder javaOptions(List v) { + if (v != null) { + v.forEach(Objects::requireNonNull); + } javaOptions = v; return this; } LauncherStartupInfoBuilder defaultParameters(List v) { + if (v != null) { + v.forEach(Objects::requireNonNull); + } defaultParameters = v; return this; } - private static record ModuleStartupInfo(String moduleName) implements UnaryOperator { + LauncherStartupInfoBuilder mainJar(Path v) { + mainJar = v; + return this; + } + + LauncherStartupInfoBuilder mainClassName(String v) { + mainClassName = v; + return this; + } - @Override - public LauncherStartupInfo apply(LauncherStartupInfo base) { - return LauncherModularStartupInfo.create(base, - new LauncherModularStartupInfoMixin.Stub(moduleName)); + LauncherStartupInfoBuilder predefinedRuntimeImage(Path v) { + cookedRuntimePath = v; + return this; + } + + LauncherStartupInfoBuilder moduleName(String v) { + if (v == null) { + moduleName = null; + } else { + var slashIdx = v.indexOf('/'); + if (slashIdx < 0) { + moduleName = v; + } else { + moduleName = v.substring(0, slashIdx); + if (slashIdx < v.length() - 1) { + mainClassName(v.substring(slashIdx + 1)); + } + } } + return this; } - private static record JarStartupInfo(Path jarPath, - boolean isClassNameFromMainJar) implements - UnaryOperator { + LauncherStartupInfoBuilder modulePath(List v) { + modulePath = v; + return this; + } - @Override - public LauncherStartupInfo apply(LauncherStartupInfo base) { - return LauncherJarStartupInfo.create(base, - new LauncherJarStartupInfoMixin.Stub(jarPath, - isClassNameFromMainJar)); + private Optional inputDir() { + return Optional.ofNullable(inputDir); + } + + private Optional mainClassName() { + return Optional.ofNullable(mainClassName); + } + + private Optional cookedRuntimePath() { + return Optional.ofNullable(cookedRuntimePath); + } + + private LauncherStartupInfo createLauncherStartupInfo(String mainClassName, List classpath) { + Objects.requireNonNull(mainClassName); + classpath.forEach(Objects::requireNonNull); + return new LauncherStartupInfo.Stub(mainClassName, + Optional.ofNullable(javaOptions).orElseGet(List::of), + Optional.ofNullable(defaultParameters).orElseGet(List::of), + classpath); + } + + private static List createClasspath(Path inputDir, Set excludes) { + excludes.forEach(Objects::requireNonNull); + try (final var walk = Files.walk(inputDir)) { + return walk.filter(Files::isRegularFile) + .filter(file -> file.getFileName().toString().endsWith(".jar")) + .map(inputDir::relativize) + .filter(Predicate.not(excludes::contains)) + .distinct() + .toList(); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + private LauncherModularStartupInfo createModular() { + final var fullModulePath = getFullModulePath(); + + // Try to find the module in the specified module path list. + final var moduleInfo = JLinkRuntimeBuilder.createModuleFinder(fullModulePath).find(moduleName) + .map(ModuleInfo::fromModuleReference).or(() -> { + // Failed to find the module in the specified module path list. + return cookedRuntimePath().flatMap(cookedRuntime -> { + // Lookup the module in the external runtime. + return ModuleInfo.fromCookedRuntime(moduleName, cookedRuntime); + }); + }).orElseThrow(() -> { + return I18N.buildConfigException("error.no-module-in-path", moduleName).create(); + }); + + final var effectiveMainClassName = mainClassName().or(moduleInfo::mainClass).orElseThrow(() -> { + return I18N.buildConfigException("ERR_NoMainClass").create(); + }); + + // If module is located in the file system, exclude it from the classpath. + final var classpath = inputDir().map(theInputDir -> { + var classpathExcludes = moduleInfo.fileLocation().filter(moduleFile -> { + return moduleFile.startsWith(theInputDir); + }).map(theInputDir::relativize).map(Set::of).orElseGet(Set::of); + return createClasspath(theInputDir, classpathExcludes); + }).orElseGet(List::of); + + return LauncherModularStartupInfo.create( + createLauncherStartupInfo(effectiveMainClassName, classpath), + new LauncherModularStartupInfoMixin.Stub(moduleInfo.name(), moduleInfo.version())); + } + + private List getFullModulePath() { + return cookedRuntimePath().map(runtimeImage -> { + return Stream.of(modulePath(), List.of(runtimeImage.resolve("lib"))).flatMap(List::stream).toList(); + }).orElse(modulePath()); + } + + private List modulePath() { + return Optional.ofNullable(modulePath).orElseGet(List::of); + } + + private LauncherJarStartupInfo createNonModular() { + final var theInputDir = inputDir().orElseThrow(); + + final var mainJarPath = theInputDir.resolve(mainJar); + + if (!Files.exists(mainJarPath)) { + throw I18N.buildConfigException() + .message("error.main-jar-does-not-exist", mainJar) + .advice("error.main-jar-does-not-exist.advice") + .create(); } + + final var effectiveMainClassName = mainClassName().or(() -> { + try (final var jf = new JarFile(mainJarPath.toFile())) { + return Optional.ofNullable(jf.getManifest()).map(Manifest::getMainAttributes).map(attrs -> { + return attrs.getValue(Attributes.Name.MAIN_CLASS); + }); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }).orElseThrow(() -> { + return I18N.buildConfigException() + .message("error.no-main-class-with-main-jar", mainJar) + .advice("error.no-main-class-with-main-jar.advice", mainJar) + .create(); + }); + + return LauncherJarStartupInfo.create( + createLauncherStartupInfo(effectiveMainClassName, createClasspath(theInputDir, Set.of(mainJar))), + new LauncherJarStartupInfoMixin.Stub(mainJar, mainClassName().isEmpty())); } - private String qualifiedClassName; + // Modular options + private String moduleName; + private List modulePath; + + // Non-modular options + private Path mainJar; + + // Common options + private Path inputDir; + private String mainClassName; private List javaOptions; private List defaultParameters; - private List classPath; - private UnaryOperator decorator; + private Path cookedRuntimePath; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OptionUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OptionUtils.java new file mode 100644 index 00000000000..97e1274f078 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OptionUtils.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal; + +import static jdk.jpackage.internal.cli.StandardOption.BUNDLING_OPERATION_DESCRIPTOR; +import static jdk.jpackage.internal.cli.StandardOption.DEST; +import static jdk.jpackage.internal.cli.StandardOption.MAIN_JAR; +import static jdk.jpackage.internal.cli.StandardOption.MODULE; +import static jdk.jpackage.internal.cli.StandardOption.PREDEFINED_APP_IMAGE; +import static jdk.jpackage.internal.cli.StandardOption.PREDEFINED_RUNTIME_IMAGE; + +import java.nio.file.Path; +import jdk.jpackage.internal.cli.Options; +import jdk.jpackage.internal.cli.StandardBundlingOperation; + +final class OptionUtils { + + static boolean isRuntimeInstaller(Options options) { + return PREDEFINED_RUNTIME_IMAGE.containsIn(options) + && !PREDEFINED_APP_IMAGE.containsIn(options) + && !MAIN_JAR.containsIn(options) + && !MODULE.containsIn(options); + } + + static Path outputDir(Options options) { + return DEST.getFrom(options); + } + + static StandardBundlingOperation bundlingOperation(Options options) { + return StandardBundlingOperation.valueOf(BUNDLING_OPERATION_DESCRIPTOR.getFrom(options)).orElseThrow(); + } +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OptionsTransformer.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OptionsTransformer.java new file mode 100644 index 00000000000..a01be089a49 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OptionsTransformer.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal; + +import static jdk.jpackage.internal.cli.StandardOption.ADDITIONAL_LAUNCHERS; +import static jdk.jpackage.internal.cli.StandardOption.APP_VERSION; +import static jdk.jpackage.internal.cli.StandardOption.ICON; +import static jdk.jpackage.internal.cli.StandardOption.NAME; +import static jdk.jpackage.internal.cli.StandardOption.PREDEFINED_APP_IMAGE; + +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import jdk.jpackage.internal.cli.Options; +import jdk.jpackage.internal.cli.WithOptionIdentifier; +import jdk.jpackage.internal.model.ApplicationLayout; +import jdk.jpackage.internal.model.ExternalApplication; + +record OptionsTransformer(Options mainOptions, Optional externalApp) { + + OptionsTransformer { + Objects.requireNonNull(mainOptions); + Objects.requireNonNull(externalApp); + } + + OptionsTransformer(Options mainOptions, ApplicationLayout appLayout) { + this(mainOptions, PREDEFINED_APP_IMAGE.findIn(mainOptions).map(appLayout::resolveAt).map(AppImageFile::load)); + } + + Options appOptions() { + return externalApp.map(ea -> { + var overrideOptions = Map.of( + NAME, ea.appName(), + APP_VERSION, ea.appVersion(), + ADDITIONAL_LAUNCHERS, ea.addLaunchers().stream().map(li -> { + return Options.concat(li.extra(), Options.of(Map.of( + NAME, li.name(), + // This should prevent the code building the Launcher instance + // from the Options object from trying to create a startup info object. + PREDEFINED_APP_IMAGE, PREDEFINED_APP_IMAGE.getFrom(mainOptions) + ))); + }).toList() + ); + return Options.concat( + Options.of(overrideOptions), + ea.extra(), + // Remove icon if any from the application/launcher options. + // If the icon is specified in the main options, it for the installer. + mainOptions.copyWithout(ICON.id()) + ); + }).orElse(mainOptions); + } +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackageBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackageBuilder.java index f93cd0eab14..f537a1ea0a2 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackageBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackageBuilder.java @@ -31,7 +31,6 @@ import java.util.Optional; import jdk.jpackage.internal.model.AppImageLayout; import jdk.jpackage.internal.model.Application; -import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.Package; import jdk.jpackage.internal.model.Package.Stub; import jdk.jpackage.internal.model.PackageType; @@ -44,7 +43,7 @@ final class PackageBuilder { this.type = Objects.requireNonNull(type); } - Package create() throws ConfigException { + Package create() { final var validatedName = validatedName(); Path relativeInstallDir; @@ -208,8 +207,7 @@ private AppImageLayout validatedInstalledPackageLayout(Path relativeInstallDir) }); } - private static Path mapInstallDir(Path installDir, PackageType pkgType) - throws ConfigException { + private static Path mapInstallDir(Path installDir, PackageType pkgType) { var ex = buildConfigException("error.invalid-install-dir", installDir).create(); if (installDir.getNameCount() == 0) { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Packager.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Packager.java index 501fd64bdca..8e47b046eb1 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Packager.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Packager.java @@ -29,7 +29,6 @@ import java.util.Optional; import java.util.function.Consumer; import jdk.jpackage.internal.model.Package; -import jdk.jpackage.internal.model.PackagerException; final class Packager { @@ -69,7 +68,7 @@ BuildEnv env() { return Objects.requireNonNull(env); } - Path execute(PackagingPipeline.Builder pipelineBuilder) throws PackagerException { + Path execute(PackagingPipeline.Builder pipelineBuilder) { Objects.requireNonNull(pkg); Objects.requireNonNull(env); Objects.requireNonNull(outputDir); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java index 6f4e0d0d2d8..a2750fee260 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java @@ -46,7 +46,6 @@ import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ApplicationLayout; import jdk.jpackage.internal.model.Package; -import jdk.jpackage.internal.model.PackagerException; import jdk.jpackage.internal.pipeline.DirectedEdge; import jdk.jpackage.internal.pipeline.FixedDAG; import jdk.jpackage.internal.pipeline.TaskPipelineBuilder; @@ -62,7 +61,7 @@ final class PackagingPipeline { * @param env the build environment * @param app the application */ - void execute(BuildEnv env, Application app) throws PackagerException { + void execute(BuildEnv env, Application app) { execute(contextMapper.apply(createTaskContext(env, app))); } @@ -81,7 +80,7 @@ void execute(BuildEnv env, Application app) throws PackagerException { * @param pkg the package * @param outputDir the output directory for the package file */ - void execute(BuildEnv env, Package pkg, Path outputDir) throws PackagerException { + void execute(BuildEnv env, Package pkg, Path outputDir) { execute((StartupParameters)createPackagingTaskContext(env, pkg, outputDir, taskConfig)); } @@ -91,7 +90,7 @@ void execute(BuildEnv env, Package pkg, Path outputDir) throws PackagerException * * @param startupParameters the pipeline startup parameters */ - void execute(StartupParameters startupParameters) throws PackagerException { + void execute(StartupParameters startupParameters) { execute(contextMapper.apply(createTaskContext((PackagingTaskContext)startupParameters))); } @@ -132,7 +131,7 @@ enum PackageTaskID implements TaskID { } interface TaskContext extends Predicate { - void execute(TaskAction taskAction) throws IOException, PackagerException; + void execute(TaskAction taskAction) throws IOException; } record AppImageBuildEnv(BuildEnv env, T app) { @@ -161,27 +160,27 @@ U resolvedLayout() { @FunctionalInterface interface ApplicationImageTaskAction extends TaskAction { - void execute(AppImageBuildEnv env) throws IOException, PackagerException; + void execute(AppImageBuildEnv env) throws IOException; } @FunctionalInterface interface AppImageTaskAction extends TaskAction { - void execute(AppImageBuildEnv env) throws IOException, PackagerException; + void execute(AppImageBuildEnv env) throws IOException; } @FunctionalInterface interface CopyAppImageTaskAction extends TaskAction { - void execute(T pkg, AppImageLayout srcAppImage, AppImageLayout dstAppImage) throws IOException, PackagerException; + void execute(T pkg, AppImageLayout srcAppImage, AppImageLayout dstAppImage) throws IOException; } @FunctionalInterface interface PackageTaskAction extends TaskAction { - void execute(PackageBuildEnv env) throws IOException, PackagerException; + void execute(PackageBuildEnv env) throws IOException; } @FunctionalInterface interface NoArgTaskAction extends TaskAction { - void execute() throws IOException, PackagerException; + void execute() throws IOException; } record TaskConfig(Optional action) { @@ -493,7 +492,7 @@ private static PackagingTaskContext createPackagingTaskContext(BuildEnv env, Pac return new PackagingTaskContext(BuildEnv.withAppImageLayout(env, dstLayout), pkg, outputDir, srcLayout); } - private void execute(TaskContext context) throws PackagerException { + private void execute(TaskContext context) { final Map> tasks = taskConfig.entrySet().stream().collect(toMap(Map.Entry::getKey, task -> { return createTask(context, task.getKey(), task.getValue()); })); @@ -508,14 +507,8 @@ private void execute(TaskContext context) throws PackagerException { try { builder.create().call(); - } catch (ExceptionBox ex) { - throw new PackagerException(ex.getCause()); - } catch (RuntimeException ex) { - throw ex; - } catch (PackagerException ex) { - throw ex; } catch (Exception ex) { - throw new PackagerException(ex); + throw ExceptionBox.rethrowUnchecked(ex); } } @@ -546,7 +539,7 @@ public boolean test(TaskID taskID) { @SuppressWarnings("unchecked") @Override - public void execute(TaskAction taskAction) throws IOException, PackagerException { + public void execute(TaskAction taskAction) throws IOException { if (taskAction instanceof PackageTaskAction) { ((PackageTaskAction)taskAction).execute(pkgBuildEnv()); } else if (taskAction instanceof CopyAppImageTaskAction) { @@ -600,7 +593,7 @@ public boolean test(TaskID taskID) { @SuppressWarnings("unchecked") @Override - public void execute(TaskAction taskAction) throws IOException, PackagerException { + public void execute(TaskAction taskAction) throws IOException { if (taskAction instanceof AppImageTaskAction) { final var taskEnv = pkg.map(PackagingTaskContext::appImageBuildEnv).orElseGet(this::appBuildEnv); ((AppImageTaskAction)taskAction).execute(taskEnv); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RuntimeBuilderBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RuntimeBuilderBuilder.java index dcaf097b1a3..a7d9bad39b1 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RuntimeBuilderBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RuntimeBuilderBuilder.java @@ -25,7 +25,6 @@ package jdk.jpackage.internal; import static jdk.jpackage.internal.model.RuntimeBuilder.getDefaultModulePath; -import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; import java.io.IOException; import java.io.UncheckedIOException; @@ -38,7 +37,6 @@ import java.util.Set; import java.util.function.Supplier; import jdk.jpackage.internal.model.ApplicationLayout; -import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.LauncherStartupInfo; import jdk.jpackage.internal.model.RuntimeBuilder; import jdk.jpackage.internal.util.FileUtils; @@ -112,8 +110,7 @@ private List validatedOptions() { ); } - private static RuntimeBuilder createCopyingRuntimeBuilder(Path runtimeDir, - Path... modulePath) throws ConfigException { + private static RuntimeBuilder createCopyingRuntimeBuilder(Path runtimeDir, Path... modulePath) { return appImageLayout -> { try { // copy whole runtime, skipping jmods and src.zip @@ -147,10 +144,9 @@ private record CopyingRuntime(RuntimeBuilderBuilder thiz, Path predefinedRuntime @Override public RuntimeBuilder get() { - return toSupplier(() -> createCopyingRuntimeBuilder( + return createCopyingRuntimeBuilder( predefinedRuntimeImage, - Optional.ofNullable(thiz.modulePath).orElseGet(List::of).toArray(Path[]::new)) - ).get(); + Optional.ofNullable(thiz.modulePath).orElseGet(List::of).toArray(Path[]::new)); } } @@ -160,13 +156,12 @@ private record BuildingRuntime(RuntimeBuilderBuilder thiz, Set addModule @Override public RuntimeBuilder get() { - return toSupplier(() -> JLinkRuntimeBuilder.createJLinkRuntimeBuilder( + return JLinkRuntimeBuilder.createJLinkRuntimeBuilder( Optional.ofNullable(thiz.modulePath).orElseGet(List::of), Optional.ofNullable(addModules).orElseGet(Set::of), Optional.ofNullable(limitModules).orElseGet(Set::of), Optional.ofNullable(options).orElseGet(List::of), - startupInfos) - ).get(); + startupInfos); } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/StandardBundlerParam.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/StandardBundlerParam.java deleted file mode 100644 index 2b35a6830f8..00000000000 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/StandardBundlerParam.java +++ /dev/null @@ -1,513 +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. 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.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Stream; -import jdk.jpackage.internal.model.ConfigException; -import jdk.jpackage.internal.model.ExternalApplication; -import static jdk.jpackage.internal.ApplicationLayoutUtils.PLATFORM_APPLICATION_LAYOUT; - -/** - * Standard bundler parameters. - * - * Contains static definitions of all of the common bundler parameters. - * (additional platform specific and mode specific bundler parameters - * are defined in each of the specific bundlers) - * - * Also contains static methods that operate on maps of parameters. - */ -final class StandardBundlerParam { - - private static final String DEFAULT_VERSION = "1.0"; - private static final String DEFAULT_RELEASE = "1"; - private static final String[] DEFAULT_JLINK_OPTIONS = { - "--strip-native-commands", - "--strip-debug", - "--no-man-pages", - "--no-header-files"}; - - static final BundlerParamInfo LAUNCHER_DATA = BundlerParamInfo.createBundlerParam( - LauncherData.class, LauncherData::create); - - static final BundlerParamInfo SOURCE_DIR = - new BundlerParamInfo<>( - Arguments.CLIOptions.INPUT.getId(), - Path.class, - p -> null, - (s, p) -> Path.of(s) - ); - - static final BundlerParamInfo OUTPUT_DIR = - new BundlerParamInfo<>( - Arguments.CLIOptions.OUTPUT.getId(), - Path.class, - p -> Path.of("").toAbsolutePath(), - (s, p) -> Path.of(s) - ); - - // note that each bundler is likely to replace this one with - // their own converter - static final BundlerParamInfo MAIN_JAR = - new BundlerParamInfo<>( - Arguments.CLIOptions.MAIN_JAR.getId(), - Path.class, - params -> LAUNCHER_DATA.fetchFrom(params).mainJarName(), - null - ); - - static final BundlerParamInfo PREDEFINED_APP_IMAGE = - new BundlerParamInfo<>( - Arguments.CLIOptions.PREDEFINED_APP_IMAGE.getId(), - Path.class, - params -> null, - (s, p) -> Path.of(s)); - - static final BundlerParamInfo PREDEFINED_APP_IMAGE_FILE = BundlerParamInfo.createBundlerParam( - ExternalApplication.class, params -> { - if (hasPredefinedAppImage(params)) { - var appImage = PREDEFINED_APP_IMAGE.fetchFrom(params); - return AppImageFile.load(appImage, PLATFORM_APPLICATION_LAYOUT); - } else { - return null; - } - }); - - static final BundlerParamInfo MAIN_CLASS = - new BundlerParamInfo<>( - Arguments.CLIOptions.APPCLASS.getId(), - String.class, - params -> { - if (isRuntimeInstaller(params)) { - return null; - } else if (hasPredefinedAppImage(params)) { - PREDEFINED_APP_IMAGE_FILE.fetchFrom(params).getMainClass(); - } - return LAUNCHER_DATA.fetchFrom(params).qualifiedClassName(); - }, - (s, p) -> s - ); - - static final BundlerParamInfo PREDEFINED_RUNTIME_IMAGE = - new BundlerParamInfo<>( - Arguments.CLIOptions.PREDEFINED_RUNTIME_IMAGE.getId(), - Path.class, - params -> null, - (s, p) -> Path.of(s) - ); - - // this is the raw --app-name arg - used in APP_NAME and INSTALLER_NAME - static final BundlerParamInfo NAME = - new BundlerParamInfo<>( - Arguments.CLIOptions.NAME.getId(), - String.class, - params -> null, - (s, p) -> s - ); - - // this is the application name, either from the app-image (if given), - // the name (if given) derived from the main-class, or the runtime image - static final BundlerParamInfo APP_NAME = - new BundlerParamInfo<>( - "application-name", - String.class, - params -> { - String appName = NAME.fetchFrom(params); - if (hasPredefinedAppImage(params)) { - appName = PREDEFINED_APP_IMAGE_FILE.fetchFrom(params).getLauncherName(); - } else if (appName == null) { - String s = MAIN_CLASS.fetchFrom(params); - if (s != null) { - int idx = s.lastIndexOf("."); - appName = (idx < 0) ? s : s.substring(idx+1); - } else if (isRuntimeInstaller(params)) { - Path f = PREDEFINED_RUNTIME_IMAGE.fetchFrom(params); - if (f != null) { - appName = f.getFileName().toString(); - } - } - } - return appName; - }, - (s, p) -> s - ); - - static final BundlerParamInfo INSTALLER_NAME = - new BundlerParamInfo<>( - "installer-name", - String.class, - params -> { - String installerName = NAME.fetchFrom(params); - return (installerName != null) ? installerName : - APP_NAME.fetchFrom(params); - }, - (s, p) -> s - ); - - static final BundlerParamInfo ICON = - new BundlerParamInfo<>( - Arguments.CLIOptions.ICON.getId(), - Path.class, - params -> null, - (s, p) -> Path.of(s) - ); - - static final BundlerParamInfo ABOUT_URL = - new BundlerParamInfo<>( - Arguments.CLIOptions.ABOUT_URL.getId(), - String.class, - params -> null, - (s, p) -> s - ); - - static final BundlerParamInfo VENDOR = - new BundlerParamInfo<>( - Arguments.CLIOptions.VENDOR.getId(), - String.class, - params -> I18N.getString("param.vendor.default"), - (s, p) -> s - ); - - static final BundlerParamInfo DESCRIPTION = - new BundlerParamInfo<>( - Arguments.CLIOptions.DESCRIPTION.getId(), - String.class, - params -> params.containsKey(APP_NAME.getID()) - ? APP_NAME.fetchFrom(params) - : I18N.getString("param.description.default"), - (s, p) -> s - ); - - static final BundlerParamInfo COPYRIGHT = - new BundlerParamInfo<>( - Arguments.CLIOptions.COPYRIGHT.getId(), - String.class, - params -> MessageFormat.format(I18N.getString( - "param.copyright.default"), new Date()), - (s, p) -> s - ); - - @SuppressWarnings("unchecked") - static final BundlerParamInfo> ARGUMENTS = - new BundlerParamInfo<>( - Arguments.CLIOptions.ARGUMENTS.getId(), - (Class>) (Object) List.class, - params -> Collections.emptyList(), - (s, p) -> null - ); - - @SuppressWarnings("unchecked") - static final BundlerParamInfo> JAVA_OPTIONS = - new BundlerParamInfo<>( - Arguments.CLIOptions.JAVA_OPTIONS.getId(), - (Class>) (Object) List.class, - params -> Collections.emptyList(), - (s, p) -> Arrays.asList(s.split("\n\n")) - ); - - static final BundlerParamInfo VERSION = - new BundlerParamInfo<>( - Arguments.CLIOptions.VERSION.getId(), - String.class, - StandardBundlerParam::getDefaultAppVersion, - (s, p) -> s - ); - - static final BundlerParamInfo RELEASE = - new BundlerParamInfo<>( - Arguments.CLIOptions.RELEASE.getId(), - String.class, - params -> DEFAULT_RELEASE, - (s, p) -> s - ); - - public static final BundlerParamInfo LICENSE_FILE = - new BundlerParamInfo<>( - Arguments.CLIOptions.LICENSE_FILE.getId(), - String.class, - params -> null, - (s, p) -> s - ); - - static final BundlerParamInfo TEMP_ROOT = - new BundlerParamInfo<>( - Arguments.CLIOptions.TEMP_ROOT.getId(), - Path.class, - params -> { - try { - return Files.createTempDirectory("jdk.jpackage"); - } catch (IOException ioe) { - return null; - } - }, - (s, p) -> Path.of(s) - ); - - public static final BundlerParamInfo CONFIG_ROOT = - new BundlerParamInfo<>( - "configRoot", - Path.class, - params -> { - Path root = TEMP_ROOT.fetchFrom(params).resolve("config"); - try { - Files.createDirectories(root); - } catch (IOException ioe) { - return null; - } - return root; - }, - (s, p) -> null - ); - - static final BundlerParamInfo VERBOSE = - new BundlerParamInfo<>( - Arguments.CLIOptions.VERBOSE.getId(), - Boolean.class, - params -> false, - // valueOf(null) is false, and we actually do want null - (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? - true : Boolean.valueOf(s) - ); - - static final BundlerParamInfo RESOURCE_DIR = - new BundlerParamInfo<>( - Arguments.CLIOptions.RESOURCE_DIR.getId(), - Path.class, - params -> null, - (s, p) -> Path.of(s) - ); - - static final BundlerParamInfo INSTALL_DIR = - new BundlerParamInfo<>( - Arguments.CLIOptions.INSTALL_DIR.getId(), - String.class, - params -> null, - (s, p) -> s - ); - - static final BundlerParamInfo LAUNCHER_AS_SERVICE = - new BundlerParamInfo<>( - Arguments.CLIOptions.LAUNCHER_AS_SERVICE.getId(), - Boolean.class, - params -> false, - // valueOf(null) is false, and we actually do want null - (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? - true : Boolean.valueOf(s) - ); - - - @SuppressWarnings("unchecked") - static final BundlerParamInfo>> ADD_LAUNCHERS = - new BundlerParamInfo<>( - Arguments.CLIOptions.ADD_LAUNCHER.getId(), - (Class>>) (Object) - List.class, - params -> new ArrayList<>(1), - // valueOf(null) is false, and we actually do want null - (s, p) -> null - ); - - @SuppressWarnings("unchecked") - static final BundlerParamInfo - >> FILE_ASSOCIATIONS = - new BundlerParamInfo<>( - Arguments.CLIOptions.FILE_ASSOCIATIONS.getId(), - (Class>>) (Object) - List.class, - params -> new ArrayList<>(1), - // valueOf(null) is false, and we actually do want null - (s, p) -> null - ); - - @SuppressWarnings("unchecked") - static final BundlerParamInfo> FA_EXTENSIONS = - new BundlerParamInfo<>( - "fileAssociation.extension", - (Class>) (Object) List.class, - params -> null, // null means not matched to an extension - (s, p) -> Arrays.asList(s.split("(,|\\s)+")) - ); - - @SuppressWarnings("unchecked") - static final BundlerParamInfo> FA_CONTENT_TYPE = - new BundlerParamInfo<>( - "fileAssociation.contentType", - (Class>) (Object) List.class, - params -> null, - // null means not matched to a content/mime type - (s, p) -> Arrays.asList(s.split("(,|\\s)+")) - ); - - static final BundlerParamInfo FA_DESCRIPTION = - new BundlerParamInfo<>( - "fileAssociation.description", - String.class, - p -> null, - (s, p) -> s - ); - - static final BundlerParamInfo FA_ICON = - new BundlerParamInfo<>( - "fileAssociation.icon", - Path.class, - ICON::fetchFrom, - (s, p) -> Path.of(s) - ); - - @SuppressWarnings("unchecked") - static final BundlerParamInfo> DMG_CONTENT = - new BundlerParamInfo<>( - Arguments.CLIOptions.DMG_CONTENT.getId(), - (Class>) (Object)List.class, - p -> Collections.emptyList(), - (s, p) -> Stream.of(s.split(",")).map(Path::of).toList() - ); - - @SuppressWarnings("unchecked") - static final BundlerParamInfo> APP_CONTENT = - new BundlerParamInfo<>( - Arguments.CLIOptions.APP_CONTENT.getId(), - (Class>) (Object)List.class, - p->Collections.emptyList(), - (s, p) -> Stream.of(s.split(",")).map(Path::of).toList() - ); - - @SuppressWarnings("unchecked") - static final BundlerParamInfo> MODULE_PATH = - new BundlerParamInfo<>( - Arguments.CLIOptions.MODULE_PATH.getId(), - (Class>) (Object)List.class, - p -> JLinkRuntimeBuilder.ensureBaseModuleInModulePath(List.of()), - (s, p) -> { - List modulePath = Stream.of(s.split(File.pathSeparator)) - .map(Path::of) - .toList(); - return JLinkRuntimeBuilder.ensureBaseModuleInModulePath(modulePath); - }); - - static final BundlerParamInfo MODULE = - new BundlerParamInfo<>( - Arguments.CLIOptions.MODULE.getId(), - String.class, - p -> null, - (s, p) -> { - return String.valueOf(s); - }); - - @SuppressWarnings("unchecked") - static final BundlerParamInfo> ADD_MODULES = - new BundlerParamInfo<>( - Arguments.CLIOptions.ADD_MODULES.getId(), - (Class>) (Object) Set.class, - p -> new LinkedHashSet(), - (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split(","))) - ); - - @SuppressWarnings("unchecked") - static final BundlerParamInfo> JLINK_OPTIONS = - new BundlerParamInfo<>( - Arguments.CLIOptions.JLINK_OPTIONS.getId(), - (Class>) (Object) List.class, - p -> Arrays.asList(DEFAULT_JLINK_OPTIONS), - (s, p) -> null); - - @SuppressWarnings("unchecked") - static final BundlerParamInfo> LIMIT_MODULES = - new BundlerParamInfo<>( - "limit-modules", - (Class>) (Object) Set.class, - p -> new LinkedHashSet(), - (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split(","))) - ); - - static final BundlerParamInfo SIGN_BUNDLE = - new BundlerParamInfo<>( - Arguments.CLIOptions.MAC_SIGN.getId(), - Boolean.class, - params -> false, - (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? - null : Boolean.valueOf(s) - ); - - static boolean isRuntimeInstaller(Map params) { - if (params.containsKey(MODULE.getID()) || - params.containsKey(MAIN_JAR.getID()) || - params.containsKey(PREDEFINED_APP_IMAGE.getID())) { - return false; // we are building or are given an application - } - // runtime installer requires --runtime-image, if this is false - // here then we should have thrown error validating args. - return params.containsKey(PREDEFINED_RUNTIME_IMAGE.getID()); - } - - static boolean hasPredefinedAppImage(Map params) { - return params.containsKey(PREDEFINED_APP_IMAGE.getID()); - } - - private static String getDefaultAppVersion(Map params) { - String appVersion = DEFAULT_VERSION; - - if (isRuntimeInstaller(params)) { - return appVersion; - } - - LauncherData launcherData = null; - try { - launcherData = LAUNCHER_DATA.fetchFrom(params); - } catch (RuntimeException ex) { - if (ex.getCause() instanceof ConfigException) { - return appVersion; - } - throw ex; - } - - if (launcherData.isModular()) { - String moduleVersion = launcherData.getAppVersion(); - if (moduleVersion != null) { - Log.verbose(MessageFormat.format(I18N.getString( - "message.module-version"), - moduleVersion, - launcherData.moduleName())); - appVersion = moduleVersion; - } - } - - return appVersion; - } -} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractBundler.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/TempDirectory.java similarity index 53% rename from src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractBundler.java rename to src/jdk.jpackage/share/classes/jdk/jpackage/internal/TempDirectory.java index 762b65b530e..50d1701bf0d 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractBundler.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/TempDirectory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,36 +22,51 @@ * 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.nio.file.Files; import java.nio.file.Path; -import java.util.Map; +import jdk.jpackage.internal.cli.Options; +import jdk.jpackage.internal.cli.StandardOption; import jdk.jpackage.internal.util.FileUtils; +final class TempDirectory implements Closeable { -/** - * AbstractBundler - * - * This is the base class all bundlers extend from. - * It contains methods and parameters common to all bundlers. - * The concrete implementations are in the platform specific bundlers. - */ -abstract class AbstractBundler implements Bundler { + TempDirectory(Options options) throws IOException { + final var tempDir = StandardOption.TEMP_ROOT.findIn(options); + if (tempDir.isPresent()) { + this.path = tempDir.orElseThrow(); + this.options = options; + } else { + this.path = Files.createTempDirectory("jdk.jpackage"); + this.options = options.copyWithDefaultValue(StandardOption.TEMP_ROOT, path); + } - @Override - public String toString() { - return getName(); + deleteOnClose = tempDir.isEmpty(); + } + + Options options() { + return options; + } + + Path path() { + return path; + } + + boolean deleteOnClose() { + return deleteOnClose; } @Override - public void cleanup(Map params) { - try { - FileUtils.deleteRecursive( - StandardBundlerParam.TEMP_ROOT.fetchFrom(params)); - } catch (IOException e) { - Log.verbose(e.getMessage()); + public void close() throws IOException { + if (deleteOnClose) { + FileUtils.deleteRecursive(path); } } + + private final Path path; + private final Options options; + private final boolean deleteOnClose; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ValidOptions.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ValidOptions.java deleted file mode 100644 index 89a2e4b56fe..00000000000 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ValidOptions.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (c) 2018, 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. 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.util.EnumSet; -import java.util.HashMap; - -import jdk.internal.util.OperatingSystem; -import jdk.jpackage.internal.Arguments.CLIOptions; - -/** - * ValidOptions - * - * Two basic methods for validating command line options. - * - * initArgs() - * Computes the Map of valid options for each mode on this Platform. - * - * checkIfSupported(CLIOptions arg) - * Determine if the given arg is valid on this platform. - * - * checkIfImageSupported(CLIOptions arg) - * Determine if the given arg is valid for creating app image. - * - * checkIfInstallerSupported(CLIOptions arg) - * Determine if the given arg is valid for creating installer. - * - * checkIfSigningSupported(CLIOptions arg) - * Determine if the given arg is valid for signing app image. - * - */ -class ValidOptions { - - enum USE { - ALL, // valid in all cases - LAUNCHER, // valid when creating a launcher - INSTALL, // valid when creating an installer - SIGN, // valid when signing is requested - } - - private static final HashMap> options = new HashMap<>(); - - // initializing list of mandatory arguments - static { - put(CLIOptions.NAME.getId(), USE.ALL); - put(CLIOptions.VERSION.getId(), USE.ALL); - put(CLIOptions.OUTPUT.getId(), USE.ALL); - put(CLIOptions.TEMP_ROOT.getId(), USE.ALL); - put(CLIOptions.VERBOSE.getId(), - EnumSet.of(USE.ALL, USE.SIGN)); - put(CLIOptions.PREDEFINED_RUNTIME_IMAGE.getId(), USE.ALL); - put(CLIOptions.RESOURCE_DIR.getId(), USE.ALL); - put(CLIOptions.DESCRIPTION.getId(), USE.ALL); - put(CLIOptions.VENDOR.getId(), USE.ALL); - put(CLIOptions.COPYRIGHT.getId(), USE.ALL); - put(CLIOptions.PACKAGE_TYPE.getId(), - EnumSet.of(USE.ALL, USE.SIGN)); - put(CLIOptions.ICON.getId(), USE.ALL); - - put(CLIOptions.INPUT.getId(), USE.LAUNCHER); - put(CLIOptions.MODULE.getId(), USE.LAUNCHER); - put(CLIOptions.MODULE_PATH.getId(), USE.LAUNCHER); - put(CLIOptions.ADD_MODULES.getId(), USE.LAUNCHER); - put(CLIOptions.MAIN_JAR.getId(), USE.LAUNCHER); - put(CLIOptions.APPCLASS.getId(), USE.LAUNCHER); - put(CLIOptions.ARGUMENTS.getId(), USE.LAUNCHER); - put(CLIOptions.JAVA_OPTIONS.getId(), USE.LAUNCHER); - put(CLIOptions.ADD_LAUNCHER.getId(), USE.LAUNCHER); - put(CLIOptions.JLINK_OPTIONS.getId(), USE.LAUNCHER); - put(CLIOptions.APP_CONTENT.getId(), USE.LAUNCHER); - - put(CLIOptions.LICENSE_FILE.getId(), USE.INSTALL); - put(CLIOptions.INSTALL_DIR.getId(), USE.INSTALL); - put(CLIOptions.PREDEFINED_APP_IMAGE.getId(), - (OperatingSystem.isMacOS()) ? - EnumSet.of(USE.INSTALL, USE.SIGN) : - EnumSet.of(USE.INSTALL)); - put(CLIOptions.LAUNCHER_AS_SERVICE.getId(), USE.INSTALL); - - put(CLIOptions.ABOUT_URL.getId(), USE.INSTALL); - - put(CLIOptions.FILE_ASSOCIATIONS.getId(), - (OperatingSystem.isMacOS()) ? USE.ALL : USE.INSTALL); - - if (OperatingSystem.isWindows()) { - put(CLIOptions.WIN_CONSOLE_HINT.getId(), USE.LAUNCHER); - - put(CLIOptions.WIN_HELP_URL.getId(), USE.INSTALL); - put(CLIOptions.WIN_UPDATE_URL.getId(), USE.INSTALL); - - put(CLIOptions.WIN_MENU_HINT.getId(), USE.INSTALL); - put(CLIOptions.WIN_MENU_GROUP.getId(), USE.INSTALL); - put(CLIOptions.WIN_SHORTCUT_HINT.getId(), USE.INSTALL); - put(CLIOptions.WIN_SHORTCUT_PROMPT.getId(), USE.INSTALL); - put(CLIOptions.WIN_DIR_CHOOSER.getId(), USE.INSTALL); - put(CLIOptions.WIN_UPGRADE_UUID.getId(), USE.INSTALL); - put(CLIOptions.WIN_PER_USER_INSTALLATION.getId(), - USE.INSTALL); - } - - if (OperatingSystem.isMacOS()) { - put(CLIOptions.MAC_SIGN.getId(), - EnumSet.of(USE.ALL, USE.SIGN)); - put(CLIOptions.MAC_BUNDLE_NAME.getId(), USE.ALL); - put(CLIOptions.MAC_BUNDLE_IDENTIFIER.getId(), USE.ALL); - put(CLIOptions.MAC_BUNDLE_SIGNING_PREFIX.getId(), - EnumSet.of(USE.ALL, USE.SIGN)); - put(CLIOptions.MAC_SIGNING_KEY_NAME.getId(), - EnumSet.of(USE.ALL, USE.SIGN)); - put(CLIOptions.MAC_APP_IMAGE_SIGN_IDENTITY.getId(), - EnumSet.of(USE.ALL, USE.SIGN)); - put(CLIOptions.MAC_INSTALLER_SIGN_IDENTITY.getId(), - EnumSet.of(USE.INSTALL, USE.SIGN)); - put(CLIOptions.MAC_SIGNING_KEYCHAIN.getId(), - EnumSet.of(USE.ALL, USE.SIGN)); - put(CLIOptions.MAC_APP_STORE.getId(), USE.ALL); - put(CLIOptions.MAC_CATEGORY.getId(), USE.ALL); - put(CLIOptions.MAC_ENTITLEMENTS.getId(), - EnumSet.of(USE.ALL, USE.SIGN)); - put(CLIOptions.DMG_CONTENT.getId(), USE.INSTALL); - } - - if (OperatingSystem.isLinux()) { - put(CLIOptions.LINUX_BUNDLE_NAME.getId(), USE.INSTALL); - put(CLIOptions.LINUX_DEB_MAINTAINER.getId(), USE.INSTALL); - put(CLIOptions.LINUX_CATEGORY.getId(), USE.INSTALL); - put(CLIOptions.LINUX_RPM_LICENSE_TYPE.getId(), USE.INSTALL); - put(CLIOptions.LINUX_PACKAGE_DEPENDENCIES.getId(), - USE.INSTALL); - put(CLIOptions.LINUX_MENU_GROUP.getId(), USE.INSTALL); - put(CLIOptions.RELEASE.getId(), USE.INSTALL); - put(CLIOptions.LINUX_SHORTCUT_HINT.getId(), USE.INSTALL); - } - } - - static boolean checkIfSupported(CLIOptions arg) { - return options.containsKey(arg.getId()); - } - - static boolean checkIfImageSupported(CLIOptions arg) { - EnumSet value = options.get(arg.getId()); - return value.contains(USE.ALL) || - value.contains(USE.LAUNCHER) || - value.contains(USE.SIGN); - } - - static boolean checkIfInstallerSupported(CLIOptions arg) { - EnumSet value = options.get(arg.getId()); - return value.contains(USE.ALL) || value.contains(USE.INSTALL); - } - - static boolean checkIfSigningSupported(CLIOptions arg) { - EnumSet value = options.get(arg.getId()); - return value.contains(USE.SIGN); - } - - private static EnumSet put(String key, USE value) { - return options.put(key, EnumSet.of(value)); - } - - private static EnumSet put(String key, EnumSet value) { - return options.put(key, value); - } -} diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBuildEnvFromParams.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/AdditionalLauncher.java similarity index 76% rename from src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBuildEnvFromParams.java rename to src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/AdditionalLauncher.java index 31759c8c529..34019ff9e81 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBuildEnvFromParams.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/AdditionalLauncher.java @@ -22,13 +22,11 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.jpackage.internal; -import jdk.jpackage.internal.model.MacPackage; +package jdk.jpackage.internal.cli; -final class MacBuildEnvFromParams { +import java.nio.file.Path; - static final BundlerParamInfo BUILD_ENV = BundlerParamInfo.createBundlerParam(BuildEnv.class, params -> { - return BuildEnvFromParams.create(params, MacPackagingPipeline.APPLICATION_LAYOUT::resolveAt, MacPackage::guessRuntimeLayout); - }); + +record AdditionalLauncher(String name, Path propertyFile) { } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/BundlingOperationModifier.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/BundlingOperationModifier.java new file mode 100644 index 00000000000..33525f3e54d --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/BundlingOperationModifier.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.cli; + +/** + * Modifiers for jpackage operations. + */ +enum BundlingOperationModifier implements OptionScope { + /** + * Create runtime native bundle. + */ + BUNDLE_RUNTIME, + + /** + * Create native bundle from the predefined app image. + */ + BUNDLE_PREDEFINED_APP_IMAGE, + + ; +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/BundlingOperationOptionScope.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/BundlingOperationOptionScope.java new file mode 100644 index 00000000000..04af5865baa --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/BundlingOperationOptionScope.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.internal.cli; + + +import jdk.jpackage.internal.model.BundlingOperationDescriptor; + +/** + * Bundling operation scope. + *

+ * The scope of bundling operations. E.g., app image or native package bundling. + */ +interface BundlingOperationOptionScope extends OptionScope { + BundlingOperationDescriptor descriptor(); +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/CliBundlingEnvironment.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/CliBundlingEnvironment.java new file mode 100644 index 00000000000..09ff0997c14 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/CliBundlingEnvironment.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.cli; + +import java.util.NoSuchElementException; +import jdk.jpackage.internal.model.BundlingEnvironment; +import jdk.jpackage.internal.model.BundlingOperationDescriptor; + +/** + * CLI bundling environment. + */ +public interface CliBundlingEnvironment extends BundlingEnvironment { + + /** + * Requests to run a bundling operation denoted with the given descriptor with + * the given values of command line options. + * + * @param op the descriptor of the requested bundling operation + * @param cmdline the validated values of the command line options + * @throws NoSuchElementException if the specified descriptor is not one of the + * items in the list returned by + * {@link #supportedOperations()} method + */ + void createBundle(BundlingOperationDescriptor op, Options cmdline); +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/DefaultOptions.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/DefaultOptions.java new file mode 100644 index 00000000000..91342500277 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/DefaultOptions.java @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.internal.cli; + +import static java.util.stream.Collectors.toUnmodifiableMap; +import static java.util.stream.Collectors.toUnmodifiableSet; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + + +final class DefaultOptions implements Options { + + DefaultOptions(Map values) { + this(values, Optional.empty()); + } + + DefaultOptions( + Map values, + Predicate optionNamesFilter) { + + this(values, Optional.of(optionNamesFilter)); + } + + DefaultOptions( + Map values, + Optional> optionNamesFilter) { + + map = values.entrySet().stream().collect(toUnmodifiableMap(e -> { + return e.getKey().id(); + }, e -> { + return new OptionIdentifierWithValue(e.getKey(), e.getValue()); + })); + + var optionNamesStream = optionNames(values.keySet().stream()); + optionNames = optionNamesFilter.map(optionNamesStream::filter).orElse(optionNamesStream) + .collect(toUnmodifiableSet()); + } + + private DefaultOptions(Snapshot snapshot) { + map = snapshot.map(); + optionNames = snapshot.optionNames(); + } + + static DefaultOptions create(Snapshot snapshot) { + var options = new DefaultOptions(snapshot); + + var mapOptionNames = optionNames( + options.map.values().stream().map(OptionIdentifierWithValue::withId) + ).collect(toUnmodifiableSet()); + + for (var e : options.map.entrySet()) { + if (e.getKey() != e.getValue().withId().id()) { + throw new IllegalArgumentException("Corrupted options map"); + } + } + + if (!mapOptionNames.containsAll(snapshot.optionNames())) { + throw new IllegalArgumentException("Unexpected option names"); + } + return options; + } + + @Override + public Optional find(OptionIdentifier id) { + return Optional.ofNullable(map.get(Objects.requireNonNull(id))).map(OptionIdentifierWithValue::value); + } + + @Override + public boolean contains(OptionName optionName) { + return optionNames.contains(Objects.requireNonNull(optionName)); + } + + @Override + public Set ids() { + return Collections.unmodifiableSet(map.keySet()); + } + + @Override + public DefaultOptions copyWithout(Iterable ids) { + return copy(StreamSupport.stream(ids.spliterator(), false), false); + } + + @Override + public DefaultOptions copyWith(Iterable ids) { + return copy(StreamSupport.stream(ids.spliterator(), false), true); + } + + DefaultOptions add(DefaultOptions other) { + return new DefaultOptions(new Snapshot(Stream.of(this, other).flatMap(v -> { + return v.map.values().stream(); + }).collect(toUnmodifiableMap(OptionIdentifierWithValue::id, x -> x, (first, _) -> { + return first; + })), Stream.of(this, other) + .map(DefaultOptions::optionNames) + .flatMap(Collection::stream) + .collect(toUnmodifiableSet()))); + } + + Set optionNames() { + return optionNames; + } + + Set withOptionIdentifierSet() { + return map.values().stream() + .map(OptionIdentifierWithValue::withId) + .collect(toUnmodifiableSet()); + } + + record Snapshot(Map map, Set optionNames) { + Snapshot { + Objects.requireNonNull(map); + Objects.requireNonNull(optionNames); + } + } + + record OptionIdentifierWithValue(WithOptionIdentifier withId, Object value) { + OptionIdentifierWithValue { + Objects.requireNonNull(withId); + Objects.requireNonNull(value); + } + + OptionIdentifier id() { + return withId.id(); + } + + OptionIdentifierWithValue copyWithValue(Object value) { + return new OptionIdentifierWithValue(withId, value); + } + } + + private DefaultOptions copy(Stream ids, boolean includes) { + var includeIds = ids.collect(toUnmodifiableSet()); + return new DefaultOptions(map.values().stream().filter(v -> { + return includeIds.contains(v.id()) == includes; + }).collect(toUnmodifiableMap(OptionIdentifierWithValue::withId, OptionIdentifierWithValue::value))); + } + + private static Stream optionNames(Stream options) { + return options.map(v -> { + Optional> spec; + switch (v) { + case Option option -> { + spec = Optional.of(option.spec()); + } + case OptionValue optionValue -> { + spec = optionValue.asOption().map(Option::spec); + } + default -> { + spec = Optional.empty(); + } + } + return spec; + }).filter(Optional::isPresent).map(Optional::get).map(OptionSpec::names).flatMap(Collection::stream); + } + + static final DefaultOptions EMPTY = new DefaultOptions(Map.of()); + + private final Map map; + private final Set optionNames; +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/HelpFormatter.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/HelpFormatter.java new file mode 100644 index 00000000000..1d8a5eefc79 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/HelpFormatter.java @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.internal.cli; + +import java.util.ArrayList; +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; + + +/** + * Generic help formatter. + */ +final class HelpFormatter { + + private HelpFormatter(List optionGroups, OptionGroupFormatter formatter) { + this.optionGroups = Objects.requireNonNull(optionGroups); + this.formatter = Objects.requireNonNull(formatter); + } + + void format(Consumer sink) { + for (var group : optionGroups) { + formatter.format(group, sink); + } + } + + static Builder build() { + return new Builder(); + } + + + static final class Builder { + + private Builder() { + } + + HelpFormatter create() { + return new HelpFormatter(groups, validatedGroupFormatter()); + } + + Builder groups(Collection v) { + groups.addAll(v); + return this; + } + + Builder groups(OptionGroup... v) { + return groups(List.of(v)); + } + + Builder groupFormatter(OptionGroupFormatter v) { + groupFormatter = v; + return this; + } + + private OptionGroupFormatter validatedGroupFormatter() { + return Optional.ofNullable(groupFormatter).orElseGet(Builder::createConsoleFormatter); + } + + private static OptionGroupFormatter createConsoleFormatter() { + return new ConsoleOptionGroupFormatter(new ConsoleOptionFormatter(2, 10)); + } + + private final List groups = new ArrayList<>(); + private OptionGroupFormatter groupFormatter; + } + + + interface OptionFormatter { + + public default void format(OptionSpec optionSpec, Consumer sink) { + format(optionSpec.names().stream().map(OptionName::formatForCommandLine).collect(Collectors.joining(" ")), + optionSpec.valuePattern(), + optionSpec.description(), sink); + } + + void format(String optionNames, Optional valuePattern, String description, Consumer sink); + } + + interface OptionGroupFormatter { + + default void format(OptionGroup group, Consumer sink) { + formatHeader(group.name(), sink); + formatBody(group.options(), sink); + } + + void formatHeader(String gropName, Consumer sink); + + void formatBody(Iterable> optionSpecs, Consumer sink); + } + + + record ConsoleOptionFormatter(int nameOffset, int descriptionOffset) implements OptionFormatter { + + @Override + public void format(String optionNames, Optional valuePattern, String description, Consumer sink) { + sink.accept(" ".repeat(nameOffset)); + sink.accept(optionNames); + valuePattern.map(v -> " " + v).ifPresent(sink); + eol(sink); + final var descriptionOffsetStr = " ".repeat(descriptionOffset); + Stream.of(description.split("\\R")).map(line -> { + return descriptionOffsetStr + line; + }).forEach(line -> { + sink.accept(line); + eol(sink); + }); + } + } + + + record ConsoleOptionGroupFormatter(OptionFormatter optionFormatter) implements OptionGroupFormatter { + + ConsoleOptionGroupFormatter { + Objects.requireNonNull(optionFormatter); + } + + @Override + public void formatHeader(String groupName, Consumer sink) { + Objects.requireNonNull(groupName); + eol(sink); + sink.accept(groupName + ":"); + eol(sink); + } + + @Override + public void formatBody(Iterable> optionSpecs, Consumer sink) { + optionSpecs.forEach(optionSpec -> { + optionFormatter.format(optionSpec, sink); + }); + } + } + + + record OptionGroup(String name, List> options) { + + OptionGroup { + Objects.requireNonNull(name); + Objects.requireNonNull(options); + } + } + + + static Consumer eol(Consumer sink) { + sink.accept(System.lineSeparator()); + return sink; + } + + + private final List optionGroups; + private final OptionGroupFormatter formatter; +} diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBaseInstallerBundler.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/I18N.java similarity index 56% rename from src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBaseInstallerBundler.java rename to src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/I18N.java index 5c912728c32..63c2b88039f 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBaseInstallerBundler.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/I18N.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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 @@ -22,31 +22,34 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ +package jdk.jpackage.internal.cli; -package jdk.jpackage.internal; - -import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_APP_IMAGE; - +import java.util.List; import java.util.Map; -import jdk.jpackage.internal.model.ConfigException; +import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.util.MultiResourceBundle; +import jdk.jpackage.internal.util.StringBundle; -public abstract class MacBaseInstallerBundler extends AbstractBundler { +final class I18N { - public MacBaseInstallerBundler() { - appImageBundler = new MacAppBundler(); + private I18N() { } - protected void validateAppImageAndBundeler( - Map params) throws ConfigException { - if (PREDEFINED_APP_IMAGE.fetchFrom(params) == null) { - appImageBundler.validate(params); - } + static String format(String key, Object ... args) { + return BUNDLE.format(key, args); } - @Override - public String getBundleType() { - return "INSTALLER"; + private static final StringBundle BUNDLE; + + static { + var prefix = "jdk.jpackage.internal.resources."; + BUNDLE = StringBundle.fromResourceBundle(MultiResourceBundle.create( + prefix + "MainResources", + Map.of( + OperatingSystem.LINUX, List.of(prefix + "LinuxResources"), + OperatingSystem.MACOS, List.of(prefix + "MacResources"), + OperatingSystem.WINDOWS, List.of(prefix + "WinResources") + ) + )); } - - private final Bundler appImageBundler; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/JOptSimpleOptionsBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/JOptSimpleOptionsBuilder.java new file mode 100644 index 00000000000..57b92471e4a --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/JOptSimpleOptionsBuilder.java @@ -0,0 +1,817 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.internal.cli; + +import static java.util.stream.Collectors.toUnmodifiableMap; +import static java.util.stream.Collectors.toUnmodifiableSet; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +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.UnaryOperator; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import jdk.internal.joptsimple.ArgumentAcceptingOptionSpec; +import jdk.internal.joptsimple.OptionParser; +import jdk.internal.joptsimple.OptionSet; +import jdk.jpackage.internal.cli.DefaultOptions.OptionIdentifierWithValue; +import jdk.jpackage.internal.cli.DefaultOptions.Snapshot; +import jdk.jpackage.internal.cli.OptionSpec.MergePolicy; +import jdk.jpackage.internal.util.Result; + + +/** + * Builds an instance of {@link Options} interface backed with joptsimple command + * line parser. + * + * Two types of command line argument processing are supported: + *
    + *
  1. Parse command line. Parsed data is stored as a map of strings. + *
  2. Convert strings to objects. Parsed data is stored as a map of objects. + *
+ */ +final class JOptSimpleOptionsBuilder { + + Function> create() { + return createJOptSimpleParser()::parse; + } + + JOptSimpleOptionsBuilder options(Collection v) { + v.stream().map(u -> { + switch (u) { + case Option o -> { + return o; + } + case OptionValue ov -> { + return ov.getOption(); + } + default -> { + throw new IllegalArgumentException(); + } + } + }).forEach(options::add); + return this; + } + + JOptSimpleOptionsBuilder options(WithOptionIdentifier... v) { + return options(List.of(v)); + } + + JOptSimpleOptionsBuilder optionSpecMapper(UnaryOperator> v) { + optionSpecMapper = v; + return this; + } + + JOptSimpleOptionsBuilder jOptSimpleParserErrorHandler(Function v) { + jOptSimpleParserErrorHandler = v; + return this; + } + + private JOptSimpleParser createJOptSimpleParser() { + return JOptSimpleParser.create(options, Optional.ofNullable(optionSpecMapper), + Optional.ofNullable(jOptSimpleParserErrorHandler)); + } + + + static final class ConvertedOptionsBuilder { + + private ConvertedOptionsBuilder(TypedOptions options) { + impl = Objects.requireNonNull(options); + } + + Options create() { + return impl; + } + + ConvertedOptionsBuilder copyWithExcludes(Collection v) { + return new ConvertedOptionsBuilder(impl.copyWithout(v)); + } + + List nonOptionArguments() { + return impl.nonOptionArguments(); + } + + List detectedOptions() { + return impl.detectedOptions(); + } + + private final TypedOptions impl; + } + + + static final class OptionsBuilder { + + private OptionsBuilder(UntypedOptions options) { + impl = Objects.requireNonNull(options); + } + + Result convertedOptions() { + return impl.toTypedOptions().map(ConvertedOptionsBuilder::new); + } + + Options create() { + return impl; + } + + OptionsBuilder copyWithExcludes(Collection v) { + return new OptionsBuilder(impl.copyWithout(v)); + } + + List nonOptionArguments() { + return impl.nonOptionArguments(); + } + + List detectedOptions() { + return impl.detectedOptions(); + } + + private final UntypedOptions impl; + } + + + enum JOptSimpleErrorType { + + // jdk.internal.joptsimple.UnrecognizedOptionException + UNRECOGNIZED_OPTION(() -> { + new OptionParser(false).parse("--foo"); + }), + + // jdk.internal.joptsimple.OptionMissingRequiredArgumentException + OPTION_MISSING_REQUIRED_ARGUMENT(() -> { + var parser = new OptionParser(false); + parser.accepts("foo").withRequiredArg(); + parser.parse("--foo"); + }), + ; + + JOptSimpleErrorType(Runnable initializer) { + try { + initializer.run(); + // Should never get to this point as the above line is expected to throw + // an exception of type `jdk.internal.joptsimple.OptionException`. + throw new AssertionError(); + } catch (jdk.internal.joptsimple.OptionException ex) { + type = ex.getClass(); + } + } + + private final Class type; + } + + + record JOptSimpleError(JOptSimpleErrorType type, OptionName optionName) { + + JOptSimpleError { + Objects.requireNonNull(type); + Objects.requireNonNull(optionName); + } + + static JOptSimpleError create(jdk.internal.joptsimple.OptionException ex) { + var optionName = OptionName.of(ex.options().getFirst()); + return Stream.of(JOptSimpleErrorType.values()).filter(v -> { + return v.type.isInstance(ex); + }).findFirst().map(v -> { + return new JOptSimpleError(v, optionName); + }).orElseThrow(); + } + } + + + private record JOptSimpleParser( + OptionParser parser, + Map> optionMap, + Optional> jOptSimpleParserErrorHandler) { + + private JOptSimpleParser { + Objects.requireNonNull(parser); + Objects.requireNonNull(optionMap); + Objects.requireNonNull(jOptSimpleParserErrorHandler); + } + + Result parse(String... args) { + return applyParser(parser, args).map(optionSet -> { + final OptionSet mergerOptionSet; + if (optionMap.values().stream().allMatch(spec -> spec.names().size() == 1)) { + // No specs with multiple names, merger not needed. + mergerOptionSet = optionSet; + } else { + final var parser2 = createOptionParser(); + final var optionSpecApplier = new OptionSpecApplier(); + for (final var spec : optionMap.values()) { + optionSpecApplier.applyToParser(parser2, spec); + } + + mergerOptionSet = parser2.parse(args); + } + return new OptionsBuilder(new UntypedOptions(optionSet, mergerOptionSet, optionMap)); + }); + } + + static JOptSimpleParser create(Iterable