diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 7dfd29b8457..3022e07b6ee 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -55,7 +55,7 @@ on: jobs: build-macos: name: build - runs-on: macos-11 + runs-on: macos-13 strategy: fail-fast: false diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 738f66ac0fd..88d38bba9f2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 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 @@ -148,8 +148,8 @@ jobs: apt-architecture: 'i386' # Some multilib libraries do not have proper inter-dependencies, so we have to # install their dependencies manually. - apt-extra-packages: 'libfreetype-dev:i386 libtiff-dev:i386 libcupsimage2-dev:i386 libc6-i386 libgcc-s1:i386 libstdc++6:i386' - extra-conf-options: '--with-target-bits=32' + apt-extra-packages: 'libfreetype-dev:i386 libtiff-dev:i386 libcupsimage2-dev:i386 libc6-i386 libgcc-s1:i386 libstdc++6:i386 libffi-dev:i386' + extra-conf-options: '--with-target-bits=32 --enable-fallback-linker --enable-libffi-bundling' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} if: needs.select.outputs.linux-x86 == 'true' @@ -229,7 +229,7 @@ jobs: uses: ./.github/workflows/build-macos.yml with: platform: macos-x64 - xcode-toolset-version: '12.5.1' + xcode-toolset-version: '14.3.1' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} if: needs.select.outputs.macos-x64 == 'true' @@ -240,7 +240,7 @@ jobs: uses: ./.github/workflows/build-macos.yml with: platform: macos-aarch64 - xcode-toolset-version: '12.5.1' + xcode-toolset-version: '14.3.1' extra-conf-options: '--openjdk-target=aarch64-apple-darwin' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} @@ -320,7 +320,7 @@ jobs: with: platform: macos-x64 bootjdk-platform: macos-x64 - runs-on: macos-11 + runs-on: macos-13 test-windows-x64: name: windows-x64 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3febb11b86b..6f2757fe97f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 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 @@ -132,7 +132,7 @@ jobs: run: | # On macOS we need to install some dependencies for testing brew install make - sudo xcode-select --switch /Applications/Xcode_11.7.app/Contents/Developer + sudo xcode-select --switch /Applications/Xcode_14.3.1.app/Contents/Developer # This will make GNU make available as 'make' and not only as 'gmake' echo '/usr/local/opt/make/libexec/gnubin' >> $GITHUB_PATH if: runner.os == 'macOS' diff --git a/make/autoconf/flags.m4 b/make/autoconf/flags.m4 index 04bee0c9969..8c029f7d2f5 100644 --- a/make/autoconf/flags.m4 +++ b/make/autoconf/flags.m4 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 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 @@ -128,16 +128,12 @@ AC_DEFUN([FLAGS_SETUP_MACOSX_VERSION], # The expected format for is either nn.n.n or nn.nn.nn. See # /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/AvailabilityVersions.h - # MACOSX_VERSION_MIN specifies the lowest version of Macosx that the built + # MACOSX_VERSION_MIN specifies the lowest version of macOS that the built # binaries should be compatible with, even if compiled on a newer version # of the OS. It currently has a hard coded value. Setting this also limits # exposure to API changes in header files. Bumping this is likely to # require code changes to build. - if test "x$OPENJDK_TARGET_CPU_ARCH" = xaarch64; then - MACOSX_VERSION_MIN=11.00.00 - else - MACOSX_VERSION_MIN=10.12.0 - fi + MACOSX_VERSION_MIN=11.00.00 MACOSX_VERSION_MIN_NODOTS=${MACOSX_VERSION_MIN//\./} AC_SUBST(MACOSX_VERSION_MIN) diff --git a/make/autoconf/lib-hsdis.m4 b/make/autoconf/lib-hsdis.m4 index 470a0ae8358..974d0e8b793 100644 --- a/make/autoconf/lib-hsdis.m4 +++ b/make/autoconf/lib-hsdis.m4 @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2021, 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 @@ -236,7 +236,8 @@ AC_DEFUN([LIB_SETUP_HSDIS_BINUTILS], if test "x$BINUTILS_DIR" = xsystem; then AC_CHECK_LIB(bfd, bfd_openr, [ HSDIS_LIBS="-lbfd" ], [ binutils_system_error="libbfd not found" ]) AC_CHECK_LIB(opcodes, disassembler, [ HSDIS_LIBS="$HSDIS_LIBS -lopcodes" ], [ binutils_system_error="libopcodes not found" ]) - AC_CHECK_LIB(iberty, xmalloc, [ HSDIS_LIBS="$HSDIS_LIBS -liberty" ], [ binutils_system_error="libiberty not found" ]) + # libiberty is not required on Ubuntu + AC_CHECK_LIB(iberty, xmalloc, [ HSDIS_LIBS="$HSDIS_LIBS -liberty" ]) AC_CHECK_LIB(z, deflate, [ HSDIS_LIBS="$HSDIS_LIBS -lz" ], [ binutils_system_error="libz not found" ]) HSDIS_CFLAGS="-DLIBARCH_$OPENJDK_TARGET_CPU_LEGACY_LIB" elif test "x$BINUTILS_DIR" != x; then diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index 8d9ec7e3bd6..e81b6715a48 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -426,9 +426,14 @@ var getJibProfilesProfiles = function (input, common, data) { target_os: "linux", target_cpu: "x86", build_cpu: "x64", - dependencies: ["devkit", "gtest"], - configure_args: concat(common.configure_args_32bit, - "--with-jvm-variants=minimal,server", "--with-zlib=system"), + dependencies: ["devkit", "gtest", "libffi"], + configure_args: concat(common.configure_args_32bit, [ + "--with-jvm-variants=minimal,server", + "--with-zlib=system", + "--with-libffi=" + input.get("libffi", "home_path"), + "--enable-libffi-bundling", + "--enable-fallback-linker" + ]) }, "macosx-x64": { @@ -436,7 +441,7 @@ var getJibProfilesProfiles = function (input, common, data) { target_cpu: "x64", dependencies: ["devkit", "gtest", "pandoc"], configure_args: concat(common.configure_args_64bit, "--with-zlib=system", - "--with-macosx-version-max=10.12.00", + "--with-macosx-version-max=11.00.00", "--enable-compatible-cds-alignment", // Use system SetFile instead of the one in the devkit as the // devkit one may not work on Catalina. diff --git a/make/hotspot/gensrc/GensrcAdlc.gmk b/make/hotspot/gensrc/GensrcAdlc.gmk index 1b752bacc00..0898d91e1c2 100644 --- a/make/hotspot/gensrc/GensrcAdlc.gmk +++ b/make/hotspot/gensrc/GensrcAdlc.gmk @@ -133,6 +133,21 @@ ifeq ($(call check-jvm-feature, compiler2), true) ADLCFLAGS += -DARM=1 endif + # Set ASSERT, NDEBUG and PRODUCT flags just like in JvmFlags.gmk + ifeq ($(DEBUG_LEVEL), release) + # release builds disable uses of assert macro from . + ADLCFLAGS += -DNDEBUG + # For hotspot, release builds differ internally between "optimized" and "product" + # in that "optimize" does not define PRODUCT. + ifneq ($(HOTSPOT_DEBUG_LEVEL), optimized) + ADLCFLAGS += -DPRODUCT + endif + else ifeq ($(DEBUG_LEVEL), fastdebug) + ADLCFLAGS += -DASSERT + else ifeq ($(DEBUG_LEVEL), slowdebug) + ADLCFLAGS += -DASSERT + endif + ############################################################################## # Concatenate all ad source files into a single file, which will be fed to # adlc. Also include a #line directive at the start of every included file diff --git a/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java b/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java index 49ed1c3aaa8..b6298c00c9a 100644 --- a/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java +++ b/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java @@ -30,7 +30,6 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.*; -import java.text.MessageFormat; import java.time.*; import java.util.*; import java.util.ResourceBundle.Control; @@ -92,6 +91,12 @@ public class CLDRConverter { static final String[] EMPTY_ZONE = {"", "", "", "", "", ""}; static final String META_ETCUTC_ZONE_NAME = "ETC_UTC"; + // constants used for TZDB short names + private static final String NBSP = "\u00A0"; + private static final String STD = "std"; + private static final String DST = "dst"; + private static final String NO_SUBST = "-"; + private static SupplementDataParseHandler handlerSuppl; private static LikelySubtagsParseHandler handlerLikelySubtags; private static WinZonesParseHandler handlerWinZones; @@ -123,6 +128,10 @@ public class CLDRConverter { static Map pluralRules; static Map dayPeriodRules; + // TZDB Short Names Map + private static final Map tzdbShortNamesMap = HashMap.newHashMap(512); + private static final Map tzdbSubstLetters = HashMap.newHashMap(512); + static enum DraftType { UNCONFIRMED, PROVISIONAL, @@ -284,6 +293,9 @@ public static void main(String[] args) throws Exception { pluralRules = generateRules(handlerPlurals); dayPeriodRules = generateRules(handlerDayPeriodRule); + // TZDB short names map + generateTZDBShortNamesMap(); + List bundles = readBundleList(); convertBundles(bundles); @@ -757,21 +769,25 @@ private static Map extractZoneNames(Map map, Str .orElse(tzid); Object data = map.get(TIMEZONE_ID_PREFIX + tzKey); - if (data instanceof String[]) { + if (data instanceof String[] tznames) { // Hack for UTC. UTC is an alias to Etc/UTC in CLDR if (tzid.equals("Etc/UTC") && !map.containsKey(TIMEZONE_ID_PREFIX + "UTC")) { - names.put(METAZONE_ID_PREFIX + META_ETCUTC_ZONE_NAME, data); + names.put(METAZONE_ID_PREFIX + META_ETCUTC_ZONE_NAME, tznames); names.put(tzid, META_ETCUTC_ZONE_NAME); names.put("UTC", META_ETCUTC_ZONE_NAME); } else { - names.put(tzid, data); + // TZDB short names + fillTZDBShortNames(tzid, tznames); + names.put(tzid, tznames); } } else { String meta = handlerMetaZones.get(tzKey); if (meta != null) { String metaKey = METAZONE_ID_PREFIX + meta; data = map.get(metaKey); - if (data instanceof String[]) { + if (data instanceof String[] tznames) { + // TZDB short names + fillTZDBShortNames(tzid, tznames); // Keep the metazone prefix here. names.put(metaKey, data); names.put(tzid, meta); @@ -1246,6 +1262,125 @@ private static Map coverageLevelsMap() throws Exception { return covMap; } + /* + * Generates two maps from TZ database files, where they have usual abbreviation + * of the time zone names as "FORMAT". + * + * `tzdbShortNamesMap` maps the time zone id, such as "America/Los_Angeles" to + * its FORMAT and Rule which determines the substitution. In "America/Los_Angeles" + * case, its FORMAT is "P%sT" and the Rule is "US". They are concatenated with + * an NBSP, so the eventual mapping will be: + * + * "America/Los_Angeles" -> "P%sTUS" + * + * The other map, `tzdbSubstLetters` maps the Rule to its substitution letters. + * The key of the map is the Rule name, appended with "std" or "dst" + * depending on the savings, e.g., + * + * "USstd" -> "S" + * "USdst" -> "D" + * + * These two mappings resolve the short names for time zones in each type, + * such as: + * + * Standard short name for "America/Los_Angeles" -> "PST" + * DST short name for "America/Los_Angeles" -> "PDT" + * Generic short name for "America/Los_Angeles" -> "PT" + */ + private static void generateTZDBShortNamesMap() throws IOException { + Files.walk(Path.of(tzDataDir), 1, FileVisitOption.FOLLOW_LINKS) + .filter(p -> p.toFile().isFile()) + .forEach(p -> { + try { + String zone = null; + String rule = null; + String format = null; + for (var line : Files.readAllLines(p)) { + if (line.contains("#STDOFF")) continue; + line = line.replaceAll("[ \t]*#.*", ""); + + // Zone line + if (line.startsWith("Zone")) { + var zl = line.split("[ \t]+", -1); + zone = zl[1]; + rule = zl[3]; + format = zl[4]; + } else { + if (zone != null) { + if (line.isBlank()) { + tzdbShortNamesMap.put(zone, format + NBSP + rule); + zone = null; + rule = null; + format = null; + } else { + var s = line.split("[ \t]+", -1); + rule = s[2]; + format = s[3]; + } + } + } + + // Rule line + if (line.startsWith("Rule")) { + var rl = line.split("[ \t]+", -1); + tzdbSubstLetters.put(rl[1] + NBSP + (rl[8].equals("0") ? STD : DST), + rl[9].replace(NO_SUBST, "")); + } + } + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + }); + } + + /* + * Fill the TZDB short names if there is no name provided by the CLDR + */ + private static void fillTZDBShortNames(String tzid, String[] names) { + var val = tzdbShortNamesMap.get(tzid); + if (val != null) { + var format = val.split(NBSP)[0]; + var rule = val.split(NBSP)[1]; + IntStream.of(1, 3, 5).forEach(i -> { + if (names[i] == null) { + if (format.contains("%s")) { + names[i] = switch (i) { + case 1 -> format.formatted(tzdbSubstLetters.get(rule + NBSP + STD)); + case 3 -> format.formatted(tzdbSubstLetters.get(rule + NBSP + DST)); + case 5 -> format.formatted(""); + default -> throw new InternalError(); + }; + } else if (format.contains("/")) { // such as "+08/+09" or "GMT/BST" + names[i] = switch (i) { + case 1, 5 -> convertGMTName(format.substring(0, format.indexOf("/"))); + case 3 -> convertGMTName(format.substring(format.indexOf("/") + 1)); + default -> throw new InternalError(); + }; + } else { + names[i] = convertGMTName(format); + } + } + }); + } + } + + /* + * Convert TZDB offsets to JDK's offsets, eg, "-08" to "GMT-08:00". + * If it cannot recognize the pattern, return the argument as is. + */ + private static String convertGMTName(String f) { + try { + // Should pre-fill GMT format once COMPAT is gone. + // Till then, fall back to GMT format at runtime, after COMPAT short + // names are populated + ZoneOffset.of(f); + return null; + } catch (DateTimeException dte) { + // textual representation. return as is + } + return f; + } + // for debug static void dumpMap(Map map) { map.entrySet().stream() diff --git a/make/modules/java.desktop/lib/Awt2dLibraries.gmk b/make/modules/java.desktop/lib/Awt2dLibraries.gmk index 3208e1db31a..48b99c32d58 100644 --- a/make/modules/java.desktop/lib/Awt2dLibraries.gmk +++ b/make/modules/java.desktop/lib/Awt2dLibraries.gmk @@ -919,19 +919,6 @@ endif ################################################################################ -# MACOSX_METAL_VERSION_MIN specifies the lowest version of Macosx -# that should be used to compile Metal shaders. We support Metal -# pipeline only on Macosx >=10.14. For Macosx versions <10.14 even if -# we enable Metal pipeline using -Dsun.java2d.metal=true, at -# runtime we force it to use OpenGL pipeline. And MACOSX_VERSION_MIN -# for aarch64 has always been >10.14 so we use continue to use -# MACOSX_VERSION_MIN for aarch64. -ifeq ($(OPENJDK_TARGET_CPU_ARCH), xaarch64) - MACOSX_METAL_VERSION_MIN=$(MACOSX_VERSION_MIN) -else - MACOSX_METAL_VERSION_MIN=10.14.0 -endif - ifeq ($(call isTargetOs, macosx), true) SHADERS_SRC := $(TOPDIR)/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/shaders.metal SHADERS_SUPPORT_DIR := $(SUPPORT_OUTPUTDIR)/native/java.desktop/libosxui @@ -944,7 +931,7 @@ ifeq ($(call isTargetOs, macosx), true) OUTPUT_FILE := $(SHADERS_AIR), \ SUPPORT_DIR := $(SHADERS_SUPPORT_DIR), \ COMMAND := $(METAL) -c -std=osx-metal2.0 \ - -mmacosx-version-min=$(MACOSX_METAL_VERSION_MIN) \ + -mmacosx-version-min=$(MACOSX_VERSION_MIN) \ -o $(SHADERS_AIR) $(SHADERS_SRC), \ )) diff --git a/make/modules/jdk.localedata/Gensrc.gmk b/make/modules/jdk.localedata/Gensrc.gmk index fc0e09dd8bb..df6400b16b1 100644 --- a/make/modules/jdk.localedata/Gensrc.gmk +++ b/make/modules/jdk.localedata/Gensrc.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2014, 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 @@ -36,6 +36,7 @@ TARGETS += $(GENSRC_LOCALEDATA) CLDR_DATA_DIR := $(TOPDIR)/make/data/cldr/common GENSRC_DIR := $(SUPPORT_OUTPUTDIR)/gensrc/jdk.localedata CLDR_GEN_DONE := $(GENSRC_DIR)/_cldr-gensrc.marker +TZ_DATA_DIR := $(TOPDIR)/src/java.base/share/data/tzdata $(CLDR_GEN_DONE): $(wildcard $(CLDR_DATA_DIR)/dtd/*.dtd) \ $(wildcard $(CLDR_DATA_DIR)/main/*.xml) \ @@ -47,7 +48,8 @@ $(CLDR_GEN_DONE): $(wildcard $(CLDR_DATA_DIR)/dtd/*.dtd) \ $(TOOL_CLDRCONVERTER) -base $(CLDR_DATA_DIR) \ -baselocales "en-US" \ -year $(COPYRIGHT_YEAR) \ - -o $(GENSRC_DIR)) + -o $(GENSRC_DIR) \ + -tzdatadir $(TZ_DATA_DIR)) $(TOUCH) $@ TARGETS += $(CLDR_GEN_DONE) diff --git a/make/test/BuildMicrobenchmark.gmk b/make/test/BuildMicrobenchmark.gmk index 0a6a56f2a2e..d6caf33e875 100644 --- a/make/test/BuildMicrobenchmark.gmk +++ b/make/test/BuildMicrobenchmark.gmk @@ -108,6 +108,7 @@ $(eval $(call SetupJavaCompilation, BUILD_JDK_MICROBENCHMARK, \ --add-exports java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED \ --add-exports java.base/jdk.internal.vm=ALL-UNNAMED \ --add-exports java.base/jdk.internal.event=ALL-UNNAMED \ + --add-exports java.base/jdk.internal.foreign=ALL-UNNAMED \ --enable-preview \ -processor org.openjdk.jmh.generators.BenchmarkProcessor, \ JAVA_FLAGS := --add-modules jdk.unsupported --limit-modules java.management \ diff --git a/src/hotspot/cpu/aarch64/frame_aarch64.hpp b/src/hotspot/cpu/aarch64/frame_aarch64.hpp index 3d1f588359f..e58d66d5cf5 100644 --- a/src/hotspot/cpu/aarch64/frame_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/frame_aarch64.hpp @@ -165,7 +165,7 @@ frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc); - frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc, CodeBlob* cb); + frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc, CodeBlob* cb, bool allow_cb_null = false); // used for fast frame construction by continuations frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc, CodeBlob* cb, const ImmutableOopMap* oop_map, bool on_heap); diff --git a/src/hotspot/cpu/aarch64/frame_aarch64.inline.hpp b/src/hotspot/cpu/aarch64/frame_aarch64.inline.hpp index 12a4b5b4e19..f5b46a53519 100644 --- a/src/hotspot/cpu/aarch64/frame_aarch64.inline.hpp +++ b/src/hotspot/cpu/aarch64/frame_aarch64.inline.hpp @@ -91,7 +91,7 @@ inline frame::frame(intptr_t* sp, intptr_t* fp, address pc) { init(sp, fp, pc); } -inline frame::frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc, CodeBlob* cb) { +inline frame::frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc, CodeBlob* cb, bool allow_cb_null) { assert(pauth_ptr_is_raw(pc), "cannot be signed"); intptr_t a = intptr_t(sp); intptr_t b = intptr_t(fp); @@ -102,7 +102,7 @@ inline frame::frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address assert(pc != nullptr, "no pc?"); _cb = cb; _oop_map = nullptr; - assert(_cb != nullptr, "pc: " INTPTR_FORMAT, p2i(pc)); + assert(_cb != nullptr || allow_cb_null, "pc: " INTPTR_FORMAT, p2i(pc)); _on_heap = false; DEBUG_ONLY(_frame_index = -1;) diff --git a/src/hotspot/cpu/arm/interp_masm_arm.cpp b/src/hotspot/cpu/arm/interp_masm_arm.cpp index 66281326b61..643aea681dd 100644 --- a/src/hotspot/cpu/arm/interp_masm_arm.cpp +++ b/src/hotspot/cpu/arm/interp_masm_arm.cpp @@ -37,6 +37,7 @@ #include "oops/markWord.hpp" #include "oops/method.hpp" #include "oops/methodData.hpp" +#include "oops/resolvedFieldEntry.hpp" #include "oops/resolvedIndyEntry.hpp" #include "prims/jvmtiExport.hpp" #include "prims/jvmtiThreadState.hpp" @@ -312,6 +313,36 @@ void InterpreterMacroAssembler::load_resolved_indy_entry(Register cache, Registe add(cache, cache, index); } +void InterpreterMacroAssembler::load_field_entry(Register cache, Register index, int bcp_offset) { + // Get index out of bytecode pointer + assert_different_registers(cache, index); + + get_index_at_bcp(index, bcp_offset, cache /*as tmp*/, sizeof(u2)); + + // Scale the index to be the entry index * sizeof(ResolvedFieldEntry) + // sizeof(ResolvedFieldEntry) is 16 on Arm, so using shift + if (is_power_of_2(sizeof(ResolvedFieldEntry))) { + // load constant pool cache pointer + ldr(cache, Address(FP, frame::interpreter_frame_cache_offset * wordSize)); + // Get address of field entries array + ldr(cache, Address(cache, in_bytes(ConstantPoolCache::field_entries_offset()))); + + add(cache, cache, Array::base_offset_in_bytes()); + add(cache, cache, AsmOperand(index, lsl, log2i_exact(sizeof(ResolvedFieldEntry)))); + } + else { + mov(cache, sizeof(ResolvedFieldEntry)); + mul(index, index, cache); + // load constant pool cache pointer + ldr(cache, Address(FP, frame::interpreter_frame_cache_offset * wordSize)); + + // Get address of field entries array + ldr(cache, Address(cache, in_bytes(ConstantPoolCache::field_entries_offset()))); + add(cache, cache, Array::base_offset_in_bytes()); + add(cache, cache, index); + } +} + // Generate a subtype check: branch to not_subtype if sub_klass is // not a subtype of super_klass. // Profiling code for the subtype check failure (profile_typecheck_failed) diff --git a/src/hotspot/cpu/arm/interp_masm_arm.hpp b/src/hotspot/cpu/arm/interp_masm_arm.hpp index fd0729a0a3b..f02c474f0c0 100644 --- a/src/hotspot/cpu/arm/interp_masm_arm.hpp +++ b/src/hotspot/cpu/arm/interp_masm_arm.hpp @@ -102,6 +102,7 @@ class InterpreterMacroAssembler: public MacroAssembler { void load_resolved_klass_at_offset(Register Rcpool, Register Rindex, Register Rklass); void load_resolved_indy_entry(Register cache, Register index); + void load_field_entry(Register cache, Register index, int bcp_offset = 1); void pop_ptr(Register r); void pop_i(Register r = R0_tos); diff --git a/src/hotspot/cpu/arm/templateTable_arm.cpp b/src/hotspot/cpu/arm/templateTable_arm.cpp index a36188a4337..334e6fb9441 100644 --- a/src/hotspot/cpu/arm/templateTable_arm.cpp +++ b/src/hotspot/cpu/arm/templateTable_arm.cpp @@ -39,6 +39,7 @@ #include "oops/objArrayKlass.hpp" #include "oops/oop.inline.hpp" #include "oops/resolvedIndyEntry.hpp" +#include "oops/resolvedFieldEntry.hpp" #include "prims/jvmtiExport.hpp" #include "prims/methodHandles.hpp" #include "runtime/frame.inline.hpp" @@ -227,7 +228,16 @@ void TemplateTable::patch_bytecode(Bytecodes::Code bc, Register bc_reg, // additional, required work. assert(byte_no == f1_byte || byte_no == f2_byte, "byte_no out of range"); assert(load_bc_into_bc_reg, "we use bc_reg as temp"); - __ get_cache_and_index_and_bytecode_at_bcp(bc_reg, temp_reg, temp_reg, byte_no, 1, sizeof(u2)); + __ load_field_entry(temp_reg, bc_reg); + if (byte_no == f1_byte) { + __ add(temp_reg, temp_reg, in_bytes(ResolvedFieldEntry::get_code_offset())); + } else { + __ add(temp_reg, temp_reg, in_bytes(ResolvedFieldEntry::put_code_offset())); + } + // Load-acquire the bytecode to match store-release in ResolvedFieldEntry::fill_in() + __ ldrb(temp_reg, temp_reg); + __ membar(MacroAssembler::Membar_mask_bits(MacroAssembler::LoadLoad | MacroAssembler::LoadStore), noreg, true); + __ mov(bc_reg, bc); __ cbz(temp_reg, L_patch_done); // test if bytecode is zero } @@ -2566,6 +2576,28 @@ void TemplateTable::resolve_cache_and_index(int byte_no, assert_different_registers(Rcache, Rindex, Rtemp); Label resolved; + Bytecodes::Code code = bytecode(); + assert(byte_no == f1_byte || byte_no == f2_byte, "byte_no out of range"); + __ get_cache_and_index_and_bytecode_at_bcp(Rcache, Rindex, Rtemp, byte_no, 1, index_size); + __ cmp(Rtemp, code); // have we resolved this bytecode? + __ b(resolved, eq); + + // resolve first time through + address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_from_cache); + __ mov(R1, code); + __ call_VM(noreg, entry, R1); + // Update registers with resolved info + __ get_cache_and_index_at_bcp(Rcache, Rindex, 1, index_size); + __ bind(resolved); +} + +void TemplateTable::resolve_cache_and_index_for_field(int byte_no, + Register Rcache, + Register Rindex) { + assert_different_registers(Rcache, Rindex, Rtemp); + + Label resolved; + Bytecodes::Code code = bytecode(); switch (code) { case Bytecodes::_nofast_getfield: code = Bytecodes::_getfield; break; @@ -2574,7 +2606,17 @@ void TemplateTable::resolve_cache_and_index(int byte_no, } assert(byte_no == f1_byte || byte_no == f2_byte, "byte_no out of range"); - __ get_cache_and_index_and_bytecode_at_bcp(Rcache, Rindex, Rtemp, byte_no, 1, index_size); + __ load_field_entry(Rcache, Rindex); + if (byte_no == f1_byte) { + __ add(Rtemp, Rcache, in_bytes(ResolvedFieldEntry::get_code_offset())); + } else { + __ add(Rtemp, Rcache, in_bytes(ResolvedFieldEntry::put_code_offset())); + } + + // Load-acquire the bytecode to match store-release in ResolvedFieldEntry::fill_in() + __ ldrb(Rtemp, Rtemp); + __ membar(MacroAssembler::Membar_mask_bits(MacroAssembler::LoadLoad | MacroAssembler::LoadStore), noreg, true); + __ cmp(Rtemp, code); // have we resolved this bytecode? __ b(resolved, eq); @@ -2583,10 +2625,35 @@ void TemplateTable::resolve_cache_and_index(int byte_no, __ mov(R1, code); __ call_VM(noreg, entry, R1); // Update registers with resolved info - __ get_cache_and_index_at_bcp(Rcache, Rindex, 1, index_size); + __ load_field_entry(Rcache, Rindex); __ bind(resolved); } +void TemplateTable::load_resolved_field_entry(Register obj, + Register cache, + Register tos_state, + Register offset, + Register flags, + bool is_static = false) { + assert_different_registers(cache, tos_state, flags, offset); + + // Field offset + __ ldr(offset, Address(cache, in_bytes(ResolvedFieldEntry::field_offset_offset()))); + + // Flags + __ ldrb(flags, Address(cache, in_bytes(ResolvedFieldEntry::flags_offset()))); + + // TOS state + __ ldrb(tos_state, Address(cache, in_bytes(ResolvedFieldEntry::type_offset()))); + + // Klass overwrite register + if (is_static) { + __ ldr(obj, Address(cache, ResolvedFieldEntry::field_holder_offset())); + const int mirror_offset = in_bytes(Klass::java_mirror_offset()); + __ ldr(obj, Address(obj, mirror_offset)); + __ resolve_oop_handle(obj); + } +} // The Rcache and Rindex registers must be set before call void TemplateTable::load_field_cp_cache_entry(Register Rcache, @@ -2742,8 +2809,8 @@ void TemplateTable::jvmti_post_field_access(Register Rcache, __ cbz(Rtemp, Lcontinue); // cache entry pointer - __ add(R2, Rcache, AsmOperand(Rindex, lsl, LogBytesPerWord)); - __ add(R2, R2, in_bytes(ConstantPoolCache::base_offset())); + __ load_field_entry(R2, Rindex); + if (is_static) { __ mov(R1, 0); // null object reference } else { @@ -2756,7 +2823,7 @@ void TemplateTable::jvmti_post_field_access(Register Rcache, // R2: cache entry pointer __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_access), R1, R2); - __ get_cache_and_index_at_bcp(Rcache, Rindex, 1); + __ load_field_entry(Rcache, Rindex); __ bind(Lcontinue); } @@ -2773,29 +2840,25 @@ void TemplateTable::pop_and_check_object(Register r) { void TemplateTable::getfield_or_static(int byte_no, bool is_static, RewriteControl rc) { transition(vtos, vtos); - const Register Roffset = R2_tmp; - const Register Robj = R3_tmp; - const Register Rcache = R4_tmp; - const Register Rflagsav = Rtmp_save0; // R4/R19 - const Register Rindex = R5_tmp; - const Register Rflags = R5_tmp; + const Register Rcache = R4_tmp; + const Register Rindex = R3_tmp; + + const Register Roffset = R2_tmp; + const Register Rtos_state = R3_tmp; + const Register Robj = R4_tmp; // Rcache is free at the time of loading Robj + const Register Rflags = R5_tmp; - resolve_cache_and_index(byte_no, Rcache, Rindex, sizeof(u2)); + resolve_cache_and_index_for_field(byte_no, Rcache, Rindex); jvmti_post_field_access(Rcache, Rindex, is_static, false); - load_field_cp_cache_entry(Rcache, Rindex, Roffset, Rflags, Robj, is_static); - - __ mov(Rflagsav, Rflags); + load_resolved_field_entry(Robj, Rcache, Rtos_state, Roffset, Rflags, is_static); - if (!is_static) pop_and_check_object(Robj); + if (!is_static) { + pop_and_check_object(Robj); + } Label Done, Lint, Ltable, shouldNotReachHere; Label Lbtos, Lztos, Lctos, Lstos, Litos, Lltos, Lftos, Ldtos, Latos; - // compute type - __ logical_shift_right(Rflags, Rflags, ConstantPoolCacheEntry::tos_state_shift); - // Make sure we don't need to mask flags after the above shift - ConstantPoolCacheEntry::verify_tos_state_shift(); - // There are actually two versions of implementation of getfield/getstatic: // // 1) Table switch using add(PC,...) instruction (fast_version) @@ -2818,16 +2881,16 @@ void TemplateTable::getfield_or_static(int byte_no, bool is_static, RewriteContr assert(number_of_states == 10, "number of tos states should be equal to 9"); - __ cmp(Rflags, itos); + __ cmp(Rtos_state, itos); if(atos_merged_with_itos) { - __ cmp(Rflags, atos, ne); + __ cmp(Rtos_state, atos, ne); } // table switch by type if(fast_version) { - __ add(PC, PC, AsmOperand(Rflags, lsl, log_max_block_size + Assembler::LogInstructionSize), ne); + __ add(PC, PC, AsmOperand(Rtos_state, lsl, log_max_block_size + Assembler::LogInstructionSize), ne); } else { - __ ldr(PC, Address(PC, Rflags, lsl, LogBytesPerWord), ne); + __ ldr(PC, Address(PC, Rtos_state, lsl, LogBytesPerWord), ne); } // jump to itos/atos case @@ -2868,7 +2931,7 @@ void TemplateTable::getfield_or_static(int byte_no, bool is_static, RewriteContr // ztos (same as btos for getfield) { - assert(ztos == seq++, "btos has unexpected value"); + assert(ztos == seq++, "ztos has unexpected value"); FixedSizeCodeBlock ztos_block(_masm, max_block_size, fast_version); __ bind(Lztos); __ access_load_at(T_BOOLEAN, IN_HEAP, Address(Robj, Roffset), R0_tos, noreg, noreg, noreg); @@ -2992,13 +3055,13 @@ void TemplateTable::getfield_or_static(int byte_no, bool is_static, RewriteContr __ bind(Done); - // Check for volatile field - Label notVolatile; - __ tbz(Rflagsav, ConstantPoolCacheEntry::is_volatile_shift, notVolatile); - - volatile_barrier(MacroAssembler::Membar_mask_bits(MacroAssembler::LoadLoad | MacroAssembler::LoadStore), Rtemp); - - __ bind(notVolatile); + { + // Check for volatile field + Label notVolatile; + __ tbz(Rflags, ResolvedFieldEntry::is_volatile_shift, notVolatile); + __ membar(MacroAssembler::Membar_mask_bits(MacroAssembler::LoadLoad | MacroAssembler::LoadStore), Rtemp); + __ bind(notVolatile); + } } void TemplateTable::getfield(int byte_no) { @@ -3029,6 +3092,8 @@ void TemplateTable::jvmti_post_field_mod(Register Rcache, Register Rindex, bool __ ldr_global_s32(Rtemp, (address)JvmtiExport::get_field_modification_count_addr()); __ cbz(Rtemp, Lcontinue); + __ mov(R2, Rcache); + if (is_static) { // Life is simple. Null out the object pointer. __ mov(R1, 0); @@ -3037,16 +3102,10 @@ void TemplateTable::jvmti_post_field_mod(Register Rcache, Register Rindex, bool // We don't know the size of the value, though; it could be one or two words // depending on its type. As a result, we must find the type to determine where // the object is. + __ ldrb(R3, Address(Rcache, in_bytes(ResolvedFieldEntry::type_offset()))); - __ add(Rtemp, Rcache, AsmOperand(Rindex, lsl, LogBytesPerWord)); - __ ldr_u32(Rtemp, Address(Rtemp, cp_base_offset + ConstantPoolCacheEntry::flags_offset())); - - __ logical_shift_right(Rtemp, Rtemp, ConstantPoolCacheEntry::tos_state_shift); - // Make sure we don't need to mask Rtemp after the above shift - ConstantPoolCacheEntry::verify_tos_state_shift(); - - __ cmp(Rtemp, ltos); - __ cond_cmp(Rtemp, dtos, ne); + __ cmp(R3, ltos); + __ cond_cmp(R3, dtos, ne); // two word value (ltos/dtos) __ ldr(R1, Address(SP, Interpreter::expr_offset_in_bytes(2)), eq); @@ -3054,10 +3113,6 @@ void TemplateTable::jvmti_post_field_mod(Register Rcache, Register Rindex, bool __ ldr(R1, Address(SP, Interpreter::expr_offset_in_bytes(1)), ne); } - // cache entry pointer - __ add(R2, Rcache, AsmOperand(Rindex, lsl, LogBytesPerWord)); - __ add(R2, R2, in_bytes(cp_base_offset)); - // object (tos) __ mov(R3, Rstack_top); @@ -3066,7 +3121,7 @@ void TemplateTable::jvmti_post_field_mod(Register Rcache, Register Rindex, bool // R3: value object on the stack __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_modification), R1, R2, R3); - __ get_cache_and_index_at_bcp(Rcache, Rindex, 1); + __ load_field_entry(Rcache, Rindex); __ bind(Lcontinue); } @@ -3076,34 +3131,29 @@ void TemplateTable::jvmti_post_field_mod(Register Rcache, Register Rindex, bool void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteControl rc) { transition(vtos, vtos); - const Register Roffset = R2_tmp; - const Register Robj = R3_tmp; - const Register Rcache = R4_tmp; - const Register Rflagsav = Rtmp_save0; // R4/R19 - const Register Rindex = R5_tmp; - const Register Rflags = R5_tmp; + const Register Rcache = R4_tmp; + const Register Rindex = R3_tmp; + + const Register Roffset = R2_tmp; + const Register Rtos_state = R3_tmp; + const Register Robj = R4_tmp; // Rcache is free at the time of loading Robj + const Register Rflags = R5_tmp; - resolve_cache_and_index(byte_no, Rcache, Rindex, sizeof(u2)); + resolve_cache_and_index_for_field(byte_no, Rcache, Rindex); jvmti_post_field_mod(Rcache, Rindex, is_static); - load_field_cp_cache_entry(Rcache, Rindex, Roffset, Rflags, Robj, is_static); + load_resolved_field_entry(Robj, Rcache, Rtos_state, Roffset, Rflags, is_static); // Check for volatile field - Label notVolatile; - __ mov(Rflagsav, Rflags); - __ tbz(Rflagsav, ConstantPoolCacheEntry::is_volatile_shift, notVolatile); - - volatile_barrier(MacroAssembler::Membar_mask_bits(MacroAssembler::StoreStore | MacroAssembler::LoadStore), Rtemp); - - __ bind(notVolatile); + { + Label notVolatile; + __ tbz(Rflags, ResolvedFieldEntry::is_volatile_shift, notVolatile); + __ membar(MacroAssembler::Membar_mask_bits(MacroAssembler::StoreStore | MacroAssembler::LoadStore), Rtemp); + __ bind(notVolatile); + } Label Done, Lint, shouldNotReachHere; Label Ltable, Lbtos, Lztos, Lctos, Lstos, Litos, Lltos, Lftos, Ldtos, Latos; - // compute type - __ logical_shift_right(Rflags, Rflags, ConstantPoolCacheEntry::tos_state_shift); - // Make sure we don't need to mask flags after the above shift - ConstantPoolCacheEntry::verify_tos_state_shift(); - // There are actually two versions of implementation of putfield/putstatic: // // 32-bit ARM: @@ -3124,13 +3174,13 @@ void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteContr assert(number_of_states == 10, "number of tos states should be equal to 9"); // itos case is frequent and is moved outside table switch - __ cmp(Rflags, itos); + __ cmp(Rtos_state, itos); // table switch by type if (fast_version) { - __ add(PC, PC, AsmOperand(Rflags, lsl, log_max_block_size + Assembler::LogInstructionSize), ne); + __ add(PC, PC, AsmOperand(Rtos_state, lsl, log_max_block_size + Assembler::LogInstructionSize), ne); } else { - __ ldr(PC, Address(PC, Rflags, lsl, LogBytesPerWord), ne); + __ ldr(PC, Address(PC, Rtos_state, lsl, LogBytesPerWord), ne); } // jump to itos case @@ -3267,7 +3317,7 @@ void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteContr // atos { - assert(atos == seq++, "dtos has unexpected value"); + assert(atos == seq++, "atos has unexpected value"); __ bind(Latos); __ pop(atos); if (!is_static) pop_and_check_object(Robj); @@ -3293,32 +3343,11 @@ void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteContr __ bind(Done); - Label notVolatile2; - if (is_static) { - // Just check for volatile. Memory barrier for static final field - // is handled by class initialization. - __ tbz(Rflagsav, ConstantPoolCacheEntry::is_volatile_shift, notVolatile2); - volatile_barrier(MacroAssembler::StoreLoad, Rtemp); - __ bind(notVolatile2); - } else { - // Check for volatile field and final field - Label skipMembar; - - __ tst(Rflagsav, 1 << ConstantPoolCacheEntry::is_volatile_shift | - 1 << ConstantPoolCacheEntry::is_final_shift); - __ b(skipMembar, eq); - - __ tbz(Rflagsav, ConstantPoolCacheEntry::is_volatile_shift, notVolatile2); - - // StoreLoad barrier after volatile field write - volatile_barrier(MacroAssembler::StoreLoad, Rtemp); - __ b(skipMembar); - - // StoreStore barrier after final field write - __ bind(notVolatile2); - volatile_barrier(MacroAssembler::StoreStore, Rtemp); - - __ bind(skipMembar); + { + Label notVolatile; + __ tbz(Rflags, ResolvedFieldEntry::is_volatile_shift, notVolatile); + __ membar(MacroAssembler::Membar_mask_bits(MacroAssembler::StoreStore | MacroAssembler::LoadStore), Rtemp); + __ bind(notVolatile); } } @@ -3358,7 +3387,7 @@ void TemplateTable::jvmti_post_fast_field_mod(TosState state) { __ push(state); // save value on the stack // access constant pool cache entry - __ get_cache_entry_pointer_at_bcp(R2, R1, 1); + __ load_field_entry(R2, R1); __ mov(R1, R3); assert(Interpreter::expr_offset_in_bytes(0) == 0, "adjust this code"); @@ -3383,30 +3412,26 @@ void TemplateTable::fast_storefield(TosState state) { jvmti_post_fast_field_mod(state); - const Register Rcache = R2_tmp; - const Register Rindex = R3_tmp; - const Register Roffset = R3_tmp; - const Register Rflags = Rtmp_save0; // R4/R19 - const Register Robj = R5_tmp; + const Register Rcache = R4_tmp; + const Register Rindex = R3_tmp; - // access constant pool cache - __ get_cache_and_index_at_bcp(Rcache, Rindex, 1); + const Register Roffset = R2_tmp; + const Register Rtos_state = R3_tmp; + const Register Robj = R4_tmp; // Rcache is free at the time of loading Robj + const Register Rflags = R5_tmp; - __ add(Rcache, Rcache, AsmOperand(Rindex, lsl, LogBytesPerWord)); + // access constant pool cache + __ load_field_entry(Rcache, Rindex); + load_resolved_field_entry(Robj, Rcache, Rtos_state, Roffset, Rflags); // load flags to test volatile - __ ldr_u32(Rflags, Address(Rcache, base + ConstantPoolCacheEntry::flags_offset())); - - // replace index with field offset from cache entry - __ ldr(Roffset, Address(Rcache, base + ConstantPoolCacheEntry::f2_offset())); - - // Check for volatile store - Label notVolatile; - __ tbz(Rflags, ConstantPoolCacheEntry::is_volatile_shift, notVolatile); - - volatile_barrier(MacroAssembler::Membar_mask_bits(MacroAssembler::StoreStore | MacroAssembler::LoadStore), Rtemp); - - __ bind(notVolatile); + { + // Check for volatile store + Label notVolatile; + __ tbz(Rflags, ResolvedFieldEntry::is_volatile_shift, notVolatile); + __ membar(MacroAssembler::Membar_mask_bits(MacroAssembler::StoreStore | MacroAssembler::LoadStore), Rtemp); + __ bind(notVolatile); + } // Get object from stack pop_and_check_object(Robj); @@ -3446,23 +3471,14 @@ void TemplateTable::fast_storefield(TosState state) { ShouldNotReachHere(); } - Label notVolatile2; - Label skipMembar; - __ tst(Rflags, 1 << ConstantPoolCacheEntry::is_volatile_shift | - 1 << ConstantPoolCacheEntry::is_final_shift); - __ b(skipMembar, eq); - - __ tbz(Rflags, ConstantPoolCacheEntry::is_volatile_shift, notVolatile2); - - // StoreLoad barrier after volatile field write - volatile_barrier(MacroAssembler::StoreLoad, Rtemp); - __ b(skipMembar); - - // StoreStore barrier after final field write - __ bind(notVolatile2); - volatile_barrier(MacroAssembler::StoreStore, Rtemp); + { + // Check for volatile store + Label notVolatile; + __ tbz(Rflags, ResolvedFieldEntry::is_volatile_shift, notVolatile); + __ membar(MacroAssembler::Membar_mask_bits(MacroAssembler::StoreStore | MacroAssembler::LoadStore), Rtemp); + __ bind(notVolatile); + } - __ bind(skipMembar); } void TemplateTable::fast_accessfield(TosState state) { @@ -3476,7 +3492,7 @@ void TemplateTable::fast_accessfield(TosState state) { __ ldr_global_s32(R2, (address) JvmtiExport::get_field_access_count_addr()); __ cbz(R2, done); // access constant pool cache entry - __ get_cache_entry_pointer_at_bcp(R2, R1, 1); + __ load_field_entry(R2, R1); __ push_ptr(R0_tos); // save object pointer before call_VM() clobbers it __ verify_oop(R0_tos); __ mov(R1, R0_tos); @@ -3495,16 +3511,15 @@ void TemplateTable::fast_accessfield(TosState state) { const Register Roffset = R3_tmp; // access constant pool cache - __ get_cache_and_index_at_bcp(Rcache, Rindex, 1); + __ load_field_entry(Rcache, Rindex); // replace index with field offset from cache entry - __ add(Rtemp, Rcache, AsmOperand(Rindex, lsl, LogBytesPerWord)); - __ ldr(Roffset, Address(Rtemp, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::f2_offset())); + __ ldr(Roffset, Address(Rcache, ResolvedFieldEntry::field_offset_offset())); // load flags to test volatile - __ ldr_u32(Rflags, Address(Rtemp, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset())); + __ ldrb(Rflags, Address(Rcache, ResolvedFieldEntry::flags_offset())); __ verify_oop(Robj); - __ null_check(Robj, Rtemp); + __ null_check(Robj); Address addr = Address(Robj, Roffset); // access field @@ -3538,13 +3553,13 @@ void TemplateTable::fast_accessfield(TosState state) { ShouldNotReachHere(); } - // Check for volatile load - Label notVolatile; - __ tbz(Rflags, ConstantPoolCacheEntry::is_volatile_shift, notVolatile); - - volatile_barrier(MacroAssembler::Membar_mask_bits(MacroAssembler::LoadLoad | MacroAssembler::LoadStore), Rtemp); - - __ bind(notVolatile); + { + // Check for volatile load + Label notVolatile; + __ tbz(Rflags, ResolvedFieldEntry::is_volatile_shift, notVolatile); + __ membar(MacroAssembler::Membar_mask_bits(MacroAssembler::LoadLoad | MacroAssembler::LoadStore), Rtemp); + __ bind(notVolatile); + } } @@ -3562,12 +3577,11 @@ void TemplateTable::fast_xaccess(TosState state) { __ ldr(Robj, aaddress(0)); // access constant pool cache - __ get_cache_and_index_at_bcp(Rcache, Rindex, 2); - __ add(Rtemp, Rcache, AsmOperand(Rindex, lsl, LogBytesPerWord)); - __ ldr(Roffset, Address(Rtemp, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::f2_offset())); + __ load_field_entry(Rcache, Rindex, 2); + __ ldr(Roffset, Address(Rcache, ResolvedFieldEntry::field_offset_offset())); // load flags to test volatile - __ ldr_u32(Rflags, Address(Rtemp, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset())); + __ ldrb(Rflags, Address(Rcache, ResolvedFieldEntry::flags_offset())); // make sure exception is reported in correct bcp range (getfield is next instruction) __ add(Rbcp, Rbcp, 1); @@ -3590,13 +3604,13 @@ void TemplateTable::fast_xaccess(TosState state) { ShouldNotReachHere(); } - // Check for volatile load - Label notVolatile; - __ tbz(Rflags, ConstantPoolCacheEntry::is_volatile_shift, notVolatile); - - volatile_barrier(MacroAssembler::Membar_mask_bits(MacroAssembler::LoadLoad | MacroAssembler::LoadStore), Rtemp); - - __ bind(notVolatile); + { + // Check for volatile load + Label notVolatile; + __ tbz(Rflags, ResolvedFieldEntry::is_volatile_shift, notVolatile); + __ membar(MacroAssembler::Membar_mask_bits(MacroAssembler::LoadLoad | MacroAssembler::LoadStore), Rtemp); + __ bind(notVolatile); + } __ bind(done); } diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index 5a1d05c139c..0a092883fd4 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -769,7 +769,8 @@ bool os::create_thread(Thread* thread, ThreadType thr_type, // Init thread attributes. pthread_attr_t attr; - pthread_attr_init(&attr); + int rslt = pthread_attr_init(&attr); + guarantee(rslt == 0, "pthread_attr_init has to return 0"); guarantee(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0, "???"); // Make sure we run in 1:1 kernel-user-thread mode. @@ -803,6 +804,7 @@ bool os::create_thread(Thread* thread, ThreadType thr_type, stack_size / K); thread->set_osthread(nullptr); delete osthread; + pthread_attr_destroy(&attr); return false; } diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index afaa5d9778b..40a576c09ae 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -911,7 +911,12 @@ bool os::create_thread(Thread* thread, ThreadType thr_type, // init thread attributes pthread_attr_t attr; - pthread_attr_init(&attr); + int rslt = pthread_attr_init(&attr); + if (rslt != 0) { + thread->set_osthread(nullptr); + delete osthread; + return false; + } pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // Calculate stack size if it's not specified by caller. @@ -960,6 +965,7 @@ bool os::create_thread(Thread* thread, ThreadType thr_type, stack_size / K); thread->set_osthread(nullptr); delete osthread; + pthread_attr_destroy(&attr); return false; } diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index c302a5d031c..11dbead5d39 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -156,13 +156,10 @@ int os::get_native_stack(address* stack, int frames, int toSkip) { stack[frame_idx ++] = fr.pc(); } if (fr.fp() == nullptr || fr.cb() != nullptr || - fr.sender_pc() == nullptr || os::is_first_C_frame(&fr)) break; - - if (fr.sender_pc() && !os::is_first_C_frame(&fr)) { - fr = os::get_sender_for_C_frame(&fr); - } else { + fr.sender_pc() == nullptr || os::is_first_C_frame(&fr)) { break; } + fr = os::get_sender_for_C_frame(&fr); } num_of_frames = frame_idx; for (; frame_idx < frames; frame_idx ++) { diff --git a/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp b/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp index ff84024088e..928833d2889 100644 --- a/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp +++ b/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp @@ -177,7 +177,7 @@ frame os::fetch_compiled_frame_from_context(const void* ucVoid) { // JVM compiled with -fno-omit-frame-pointer, so RFP is saved on the stack. frame os::get_sender_for_C_frame(frame* fr) { - return frame(fr->link(), fr->link(), fr->sender_pc()); + return frame(fr->sender_sp(), fr->link(), fr->sender_pc()); } NOINLINE frame os::current_frame() { diff --git a/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp index 70581166cf1..86721c94797 100644 --- a/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp +++ b/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp @@ -152,8 +152,23 @@ frame os::fetch_compiled_frame_from_context(const void* ucVoid) { // By default, gcc always saves frame pointer rfp on this stack. This // may get turned off by -fomit-frame-pointer. +// The "Procedure Call Standard for the Arm 64-bit Architecture" doesn't +// specify a location for the frame record within a stack frame (6.4.6). +// GCC currently chooses to save it at the top of the frame (lowest address). +// This means that using fr->sender_sp() to set the caller's frame _unextended_sp, +// as we do in x86, is wrong. Using fr->link() instead only makes sense for +// native frames. Setting a correct value for _unextended_sp is important +// if this value is later used to get that frame's caller. This will happen +// if we end up calling frame::sender_for_compiled_frame(), which will be the +// case if the _pc is associated with a CodeBlob that has a _frame_size > 0 +// (nmethod, runtime stub, safepoint stub, etc). frame os::get_sender_for_C_frame(frame* fr) { - return frame(fr->link(), fr->link(), fr->sender_pc()); + address pc = fr->sender_pc(); + CodeBlob* cb = CodeCache::find_blob(pc); + bool use_codeblob = cb != nullptr && cb->frame_size() > 0; + assert(!use_codeblob || !Interpreter::contains(pc), "should not be an interpreter frame"); + intptr_t* sender_sp = use_codeblob ? (fr->link() + frame::metadata_words - cb->frame_size()) : fr->link(); + return frame(sender_sp, sender_sp, fr->link(), pc, cb, true /* allow_cb_null */); } NOINLINE frame os::current_frame() { diff --git a/src/hotspot/share/c1/c1_Compilation.cpp b/src/hotspot/share/c1/c1_Compilation.cpp index ed7519febe8..864510da371 100644 --- a/src/hotspot/share/c1/c1_Compilation.cpp +++ b/src/hotspot/share/c1/c1_Compilation.cpp @@ -33,7 +33,10 @@ #include "c1/c1_ValueMap.hpp" #include "c1/c1_ValueStack.hpp" #include "code/debugInfoRec.hpp" +#include "compiler/compilationMemoryStatistic.hpp" +#include "compiler/compilerDirectives.hpp" #include "compiler/compileLog.hpp" +#include "compiler/compileTask.hpp" #include "compiler/compilerDirectives.hpp" #include "memory/resourceArea.hpp" #include "runtime/sharedRuntime.hpp" @@ -442,6 +445,9 @@ void Compilation::install_code(int frame_size) { void Compilation::compile_method() { + + CompilationMemoryStatisticMark cmsm(env()->task()->directive()); + { PhaseTraceTime timeit(_t_setup); diff --git a/src/hotspot/share/cds/unregisteredClasses.cpp b/src/hotspot/share/cds/unregisteredClasses.cpp index 53b9debcd62..06d006ea1bb 100644 --- a/src/hotspot/share/cds/unregisteredClasses.cpp +++ b/src/hotspot/share/cds/unregisteredClasses.cpp @@ -46,11 +46,9 @@ InstanceKlass* UnregisteredClasses::load_class(Symbol* name, const char* path, T assert(name != nullptr, "invariant"); assert(DumpSharedSpaces, "this function is only used with -Xshare:dump"); - { - PerfClassTraceTime vmtimer(ClassLoader::perf_sys_class_lookup_time(), - THREAD->get_thread_stat()->perf_timers_addr(), - PerfClassTraceTime::CLASS_LOAD); - } + PerfClassTraceTime vmtimer(ClassLoader::perf_app_classload_time(), + THREAD->get_thread_stat()->perf_timers_addr(), + PerfClassTraceTime::CLASS_LOAD); Symbol* path_symbol = SymbolTable::new_symbol(path); Handle url_classloader = get_url_classloader(path_symbol, CHECK_NULL); diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp index 18f038e0197..56fe33141a1 100644 --- a/src/hotspot/share/classfile/classLoader.cpp +++ b/src/hotspot/share/classfile/classLoader.cpp @@ -123,7 +123,6 @@ PerfCounter* ClassLoader::_perf_class_verify_selftime = nullptr; PerfCounter* ClassLoader::_perf_classes_linked = nullptr; PerfCounter* ClassLoader::_perf_class_link_time = nullptr; PerfCounter* ClassLoader::_perf_class_link_selftime = nullptr; -PerfCounter* ClassLoader::_perf_sys_class_lookup_time = nullptr; PerfCounter* ClassLoader::_perf_shared_classload_time = nullptr; PerfCounter* ClassLoader::_perf_sys_classload_time = nullptr; PerfCounter* ClassLoader::_perf_app_classload_time = nullptr; @@ -1377,7 +1376,6 @@ void ClassLoader::initialize(TRAPS) { NEWPERFEVENTCOUNTER(_perf_classes_linked, SUN_CLS, "linkedClasses"); NEWPERFEVENTCOUNTER(_perf_classes_verified, SUN_CLS, "verifiedClasses"); - NEWPERFTICKCOUNTER(_perf_sys_class_lookup_time, SUN_CLS, "lookupSysClassTime"); NEWPERFTICKCOUNTER(_perf_shared_classload_time, SUN_CLS, "sharedClassLoadTime"); NEWPERFTICKCOUNTER(_perf_sys_classload_time, SUN_CLS, "sysClassLoadTime"); NEWPERFTICKCOUNTER(_perf_app_classload_time, SUN_CLS, "appClassLoadTime"); diff --git a/src/hotspot/share/classfile/classLoader.hpp b/src/hotspot/share/classfile/classLoader.hpp index 5237e05b4b8..4cb1967194a 100644 --- a/src/hotspot/share/classfile/classLoader.hpp +++ b/src/hotspot/share/classfile/classLoader.hpp @@ -168,7 +168,6 @@ class ClassLoader: AllStatic { static PerfCounter* _perf_classes_linked; static PerfCounter* _perf_class_link_time; static PerfCounter* _perf_class_link_selftime; - static PerfCounter* _perf_sys_class_lookup_time; static PerfCounter* _perf_shared_classload_time; static PerfCounter* _perf_sys_classload_time; static PerfCounter* _perf_app_classload_time; @@ -289,7 +288,6 @@ class ClassLoader: AllStatic { static PerfCounter* perf_classes_linked() { return _perf_classes_linked; } static PerfCounter* perf_class_link_time() { return _perf_class_link_time; } static PerfCounter* perf_class_link_selftime() { return _perf_class_link_selftime; } - static PerfCounter* perf_sys_class_lookup_time() { return _perf_sys_class_lookup_time; } static PerfCounter* perf_shared_classload_time() { return _perf_shared_classload_time; } static PerfCounter* perf_sys_classload_time() { return _perf_sys_classload_time; } static PerfCounter* perf_app_classload_time() { return _perf_app_classload_time; } diff --git a/src/hotspot/share/classfile/symbolTable.cpp b/src/hotspot/share/classfile/symbolTable.cpp index 6162be7c0fe..4410d1c61d6 100644 --- a/src/hotspot/share/classfile/symbolTable.cpp +++ b/src/hotspot/share/classfile/symbolTable.cpp @@ -219,7 +219,7 @@ void SymbolTable::create_table () { if (symbol_alloc_arena_size == 0) { _arena = new (mtSymbol) Arena(mtSymbol); } else { - _arena = new (mtSymbol) Arena(mtSymbol, symbol_alloc_arena_size); + _arena = new (mtSymbol) Arena(mtSymbol, Arena::Tag::tag_other, symbol_alloc_arena_size); } } diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index 3bc1db70251..8c906fdb416 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -1251,6 +1251,54 @@ void CodeCache::cleanup_inline_caches_whitebox() { // Keeps track of time spent for checking dependencies NOT_PRODUCT(static elapsedTimer dependentCheckTime;) +#ifndef PRODUCT +// Check if any of live methods dependencies have been invalidated. +// (this is expensive!) +static void check_live_nmethods_dependencies(DepChange& changes) { + // Checked dependencies are allocated into this ResourceMark + ResourceMark rm; + + // Turn off dependency tracing while actually testing dependencies. + FlagSetting fs(Dependencies::_verify_in_progress, true); + + typedef ResourceHashtable DepTable; + + DepTable* table = new DepTable(); + + // Iterate over live nmethods and check dependencies of all nmethods that are not + // marked for deoptimization. A particular dependency is only checked once. + NMethodIterator iter(NMethodIterator::only_not_unloading); + while(iter.next()) { + nmethod* nm = iter.method(); + // Only notify for live nmethods + if (!nm->is_marked_for_deoptimization()) { + for (Dependencies::DepStream deps(nm); deps.next(); ) { + // Construct abstraction of a dependency. + DependencySignature* current_sig = new DependencySignature(deps); + + // Determine if dependency is already checked. table->put(...) returns + // 'true' if the dependency is added (i.e., was not in the hashtable). + if (table->put(*current_sig, 1)) { + if (deps.check_dependency() != nullptr) { + // Dependency checking failed. Print out information about the failed + // dependency and finally fail with an assert. We can fail here, since + // dependency checking is never done in a product build. + tty->print_cr("Failed dependency:"); + changes.print(); + nm->print(); + nm->print_dependencies_on(tty); + assert(false, "Should have been marked for deoptimization"); + } + } + } + } + } +} +#endif + void CodeCache::mark_for_deoptimization(DeoptimizationScope* deopt_scope, KlassDepChange& changes) { MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); @@ -1272,7 +1320,7 @@ void CodeCache::mark_for_deoptimization(DeoptimizationScope* deopt_scope, KlassD // Object pointers are used as unique identifiers for dependency arguments. This // is only possible if no safepoint, i.e., GC occurs during the verification code. dependentCheckTime.start(); - nmethod::check_all_dependencies(changes); + check_live_nmethods_dependencies(changes); dependentCheckTime.stop(); } #endif diff --git a/src/hotspot/share/code/compiledIC.cpp b/src/hotspot/share/code/compiledIC.cpp index c19ee182d51..2127d5e2d26 100644 --- a/src/hotspot/share/code/compiledIC.cpp +++ b/src/hotspot/share/code/compiledIC.cpp @@ -559,15 +559,14 @@ void CompiledIC::compute_monomorphic_entry(const methodHandle& method, bool CompiledIC::is_icholder_entry(address entry) { CodeBlob* cb = CodeCache::find_blob(entry); - if (cb != nullptr && cb->is_adapter_blob()) { - return true; + if (cb == nullptr) { + return false; } - // itable stubs also use CompiledICHolder - if (cb != nullptr && cb->is_vtable_blob()) { - VtableStub* s = VtableStubs::entry_point(entry); - return (s != nullptr) && s->is_itable_stub(); + if (cb->is_adapter_blob()) { + return true; + } else if (cb->is_vtable_blob()) { + return VtableStubs::is_icholder_entry(entry); } - return false; } diff --git a/src/hotspot/share/code/icBuffer.cpp b/src/hotspot/share/code/icBuffer.cpp index a43d3678ad3..ba44df15c2e 100644 --- a/src/hotspot/share/code/icBuffer.cpp +++ b/src/hotspot/share/code/icBuffer.cpp @@ -34,6 +34,7 @@ #include "memory/resourceArea.hpp" #include "oops/method.hpp" #include "oops/oop.inline.hpp" +#include "runtime/atomic.hpp" #include "runtime/handles.inline.hpp" #include "runtime/javaThread.hpp" #include "runtime/mutexLocker.hpp" @@ -44,8 +45,8 @@ DEF_STUB_INTERFACE(ICStub); StubQueue* InlineCacheBuffer::_buffer = nullptr; -CompiledICHolder* InlineCacheBuffer::_pending_released = nullptr; -int InlineCacheBuffer::_pending_count = 0; +CompiledICHolder* volatile InlineCacheBuffer::_pending_released = nullptr; +volatile int InlineCacheBuffer::_pending_count = 0; #ifdef ASSERT ICRefillVerifier::ICRefillVerifier() @@ -247,26 +248,42 @@ void* InlineCacheBuffer::cached_value_for(CompiledIC *ic) { // Free CompiledICHolder*s that are no longer in use void InlineCacheBuffer::release_pending_icholders() { assert(SafepointSynchronize::is_at_safepoint(), "should only be called during a safepoint"); - CompiledICHolder* holder = _pending_released; + CompiledICHolder* holder = Atomic::load(&_pending_released); _pending_released = nullptr; + int count = 0; while (holder != nullptr) { CompiledICHolder* next = holder->next(); delete holder; holder = next; - _pending_count--; + count++; } - assert(_pending_count == 0, "wrong count"); + assert(pending_icholder_count() == count, "wrong count"); + Atomic::store(&_pending_count, 0); } // Enqueue this icholder for release during the next safepoint. It's -// not safe to free them until them since they might be visible to +// not safe to free them until then since they might be visible to // another thread. void InlineCacheBuffer::queue_for_release(CompiledICHolder* icholder) { - MutexLocker mex(InlineCacheBuffer_lock, Mutex::_no_safepoint_check_flag); - icholder->set_next(_pending_released); - _pending_released = icholder; - _pending_count++; + assert(icholder->next() == nullptr, "multiple enqueue?"); + + CompiledICHolder* old = Atomic::load(&_pending_released); + for (;;) { + icholder->set_next(old); + // The only reader runs at a safepoint serially so there is no need for a more strict atomic. + CompiledICHolder* cur = Atomic::cmpxchg(&_pending_released, old, icholder, memory_order_relaxed); + if (cur == old) { + break; + } + old = cur; + } + Atomic::inc(&_pending_count, memory_order_relaxed); + if (TraceICBuffer) { tty->print_cr("enqueueing icholder " INTPTR_FORMAT " to be freed", p2i(icholder)); } } + +int InlineCacheBuffer::pending_icholder_count() { + return Atomic::load(&_pending_count); +} diff --git a/src/hotspot/share/code/icBuffer.hpp b/src/hotspot/share/code/icBuffer.hpp index d385b99d59d..c2da3abdca3 100644 --- a/src/hotspot/share/code/icBuffer.hpp +++ b/src/hotspot/share/code/icBuffer.hpp @@ -146,8 +146,8 @@ class InlineCacheBuffer: public AllStatic { static StubQueue* _buffer; - static CompiledICHolder* _pending_released; - static int _pending_count; + static CompiledICHolder* volatile _pending_released; + static volatile int _pending_count; static StubQueue* buffer() { return _buffer; } @@ -176,7 +176,7 @@ class InlineCacheBuffer: public AllStatic { static void release_pending_icholders(); static void queue_for_release(CompiledICHolder* icholder); - static int pending_icholder_count() { return _pending_count; } + static int pending_icholder_count(); // New interface static bool create_transition_stub(CompiledIC *ic, void* cached_value, address entry); diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 51889df95f1..d0a02cb43eb 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -2148,51 +2148,6 @@ PcDesc* PcDescContainer::find_pc_desc_internal(address pc, bool approximate, con } } - -void nmethod::check_all_dependencies(DepChange& changes) { - // Checked dependencies are allocated into this ResourceMark - ResourceMark rm; - - // Turn off dependency tracing while actually testing dependencies. - NOT_PRODUCT( FlagSetting fs(Dependencies::_verify_in_progress, true)); - - typedef ResourceHashtable DepTable; - - DepTable* table = new DepTable(); - - // Iterate over live nmethods and check dependencies of all nmethods that are not - // marked for deoptimization. A particular dependency is only checked once. - NMethodIterator iter(NMethodIterator::only_not_unloading); - while(iter.next()) { - nmethod* nm = iter.method(); - // Only notify for live nmethods - if (!nm->is_marked_for_deoptimization()) { - for (Dependencies::DepStream deps(nm); deps.next(); ) { - // Construct abstraction of a dependency. - DependencySignature* current_sig = new DependencySignature(deps); - - // Determine if dependency is already checked. table->put(...) returns - // 'true' if the dependency is added (i.e., was not in the hashtable). - if (table->put(*current_sig, 1)) { - if (deps.check_dependency() != nullptr) { - // Dependency checking failed. Print out information about the failed - // dependency and finally fail with an assert. We can fail here, since - // dependency checking is never done in a product build. - tty->print_cr("Failed dependency:"); - changes.print(); - nm->print(); - nm->print_dependencies_on(tty); - assert(false, "Should have been marked for deoptimization"); - } - } - } - } - } -} - bool nmethod::check_dependency_on(DepChange& changes) { // What has happened: // 1) a new class dependee has been added diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp index 05a00b04aa2..dea88ee6d1c 100644 --- a/src/hotspot/share/code/nmethod.hpp +++ b/src/hotspot/share/code/nmethod.hpp @@ -677,10 +677,6 @@ class nmethod : public CompiledMethod { virtual int compile_id() const { return _compile_id; } const char* compile_kind() const; - // tells if any of this method's dependencies have been invalidated - // (this is expensive!) - static void check_all_dependencies(DepChange& changes); - // tells if this compiled method is dependent on the given changes, // and the changes have invalidated it bool check_dependency_on(DepChange& changes); diff --git a/src/hotspot/share/code/vtableStubs.cpp b/src/hotspot/share/code/vtableStubs.cpp index 934f805eefc..eed3dc8e787 100644 --- a/src/hotspot/share/code/vtableStubs.cpp +++ b/src/hotspot/share/code/vtableStubs.cpp @@ -95,8 +95,7 @@ void VtableStub::print() const { print_on(tty); } // hash value). Each list is anchored in a little hash _table, indexed // by that hash value. -VtableStub* VtableStubs::_table[VtableStubs::N]; -int VtableStubs::_number_of_vtable_stubs = 0; +VtableStub* volatile VtableStubs::_table[VtableStubs::N]; int VtableStubs::_vtab_stub_size = 0; int VtableStubs::_itab_stub_size = 0; @@ -126,13 +125,13 @@ int VtableStubs::_itab_stub_size = 0; void VtableStubs::initialize() { + assert(VtableStub::_receiver_location == VMRegImpl::Bad(), "initialized multiple times?"); + VtableStub::_receiver_location = SharedRuntime::name_for_receiver(); { MutexLocker ml(VtableStubs_lock, Mutex::_no_safepoint_check_flag); - assert(_number_of_vtable_stubs == 0, "potential performance bug: VtableStubs initialized more than once"); - assert(is_power_of_2(int(N)), "N must be a power of 2"); for (int i = 0; i < N; i++) { - _table[i] = nullptr; + Atomic::store(&_table[i], (VtableStub*)nullptr); } } } @@ -259,7 +258,7 @@ inline uint VtableStubs::hash(bool is_vtable_stub, int vtable_index){ VtableStub* VtableStubs::lookup(bool is_vtable_stub, int vtable_index) { assert_lock_strong(VtableStubs_lock); unsigned hash = VtableStubs::hash(is_vtable_stub, vtable_index); - VtableStub* s = _table[hash]; + VtableStub* s = Atomic::load(&_table[hash]); while( s && !s->matches(is_vtable_stub, vtable_index)) s = s->next(); return s; } @@ -269,10 +268,10 @@ void VtableStubs::enter(bool is_vtable_stub, int vtable_index, VtableStub* s) { assert_lock_strong(VtableStubs_lock); assert(s->matches(is_vtable_stub, vtable_index), "bad vtable stub"); unsigned int h = VtableStubs::hash(is_vtable_stub, vtable_index); - // enter s at the beginning of the corresponding list - s->set_next(_table[h]); - _table[h] = s; - _number_of_vtable_stubs++; + // Insert s at the beginning of the corresponding list. + s->set_next(Atomic::load(&_table[h])); + // Make sure that concurrent readers not taking the mutex observe the writing of "next". + Atomic::release_store(&_table[h], s); } VtableStub* VtableStubs::entry_point(address pc) { @@ -280,10 +279,17 @@ VtableStub* VtableStubs::entry_point(address pc) { VtableStub* stub = (VtableStub*)(pc - VtableStub::entry_offset()); uint hash = VtableStubs::hash(stub->is_vtable_stub(), stub->index()); VtableStub* s; - for (s = _table[hash]; s != nullptr && s != stub; s = s->next()) {} + for (s = Atomic::load(&_table[hash]); s != nullptr && s != stub; s = s->next()) {} return (s == stub) ? s : nullptr; } +bool VtableStubs::is_icholder_entry(address pc) { + assert(contains(pc), "must contain all vtable blobs"); + VtableStub* stub = (VtableStub*)(pc - VtableStub::entry_offset()); + // itable stubs use CompiledICHolder. + return stub->is_itable_stub(); +} + bool VtableStubs::contains(address pc) { // simple solution for now - we may want to use // a faster way if this function is called often @@ -292,11 +298,8 @@ bool VtableStubs::contains(address pc) { VtableStub* VtableStubs::stub_containing(address pc) { - // Note: No locking needed since any change to the data structure - // happens with an atomic store into it (we don't care about - // consistency with the _number_of_vtable_stubs counter). for (int i = 0; i < N; i++) { - for (VtableStub* s = _table[i]; s != nullptr; s = s->next()) { + for (VtableStub* s = Atomic::load_acquire(&_table[i]); s != nullptr; s = s->next()) { if (s->contains(pc)) return s; } } @@ -308,11 +311,11 @@ void vtableStubs_init() { } void VtableStubs::vtable_stub_do(void f(VtableStub*)) { - for (int i = 0; i < N; i++) { - for (VtableStub* s = _table[i]; s != nullptr; s = s->next()) { - f(s); - } + for (int i = 0; i < N; i++) { + for (VtableStub* s = Atomic::load_acquire(&_table[i]); s != nullptr; s = s->next()) { + f(s); } + } } diff --git a/src/hotspot/share/code/vtableStubs.hpp b/src/hotspot/share/code/vtableStubs.hpp index 60225d77768..7076e50f3e3 100644 --- a/src/hotspot/share/code/vtableStubs.hpp +++ b/src/hotspot/share/code/vtableStubs.hpp @@ -80,10 +80,11 @@ class VtableStubs : AllStatic { mask = N - 1 }; + static_assert(is_power_of_2((int)N), "N must be a power of 2"); + private: friend class VtableStub; - static VtableStub* _table[N]; // table of existing stubs - static int _number_of_vtable_stubs; // number of stubs created so far (for statistics) + static VtableStub* volatile _table[N]; // table of existing stubs static int _vtab_stub_size; // current size estimate for vtable stub (quasi-constant) static int _itab_stub_size; // current size estimate for itable stub (quasi-constant) @@ -106,9 +107,9 @@ class VtableStubs : AllStatic { static address find_itable_stub(int itable_index) { return find_stub(false, itable_index); } static VtableStub* entry_point(address pc); // vtable stub entry point for a pc + static bool is_icholder_entry(address pc); // is the blob containing pc (which must be a vtable blob) an icholder? static bool contains(address pc); // is pc within any stub? static VtableStub* stub_containing(address pc); // stub containing pc or nullptr - static int number_of_vtable_stubs() { return _number_of_vtable_stubs; } static void initialize(); static void vtable_stub_do(void f(VtableStub*)); // iterates over all vtable stubs }; diff --git a/src/hotspot/share/compiler/compilationMemoryStatistic.cpp b/src/hotspot/share/compiler/compilationMemoryStatistic.cpp new file mode 100644 index 00000000000..182ab9fd2ed --- /dev/null +++ b/src/hotspot/share/compiler/compilationMemoryStatistic.cpp @@ -0,0 +1,451 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Red Hat, Inc. 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "logging/log.hpp" +#include "logging/logStream.hpp" +#include "compiler/abstractCompiler.hpp" +#include "compiler/compilationMemoryStatistic.hpp" +#include "compiler/compilerDirectives.hpp" +#include "compiler/compileTask.hpp" +#include "compiler/compilerDefinitions.hpp" +#include "compiler/compilerThread.hpp" +#include "memory/arena.hpp" +#include "memory/resourceArea.hpp" +#include "oops/symbol.hpp" +#ifdef COMPILER2 +#include "opto/node.hpp" // compile.hpp is not self-contained +#include "opto/compile.hpp" +#endif +#include "services/nmtCommon.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/os.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/ostream.hpp" +#include "utilities/quickSort.hpp" +#include "utilities/resourceHash.hpp" + + +ArenaStatCounter::ArenaStatCounter() : + _current(0), _start(0), _peak(0), + _na(0), _ra(0), + _na_at_peak(0), _ra_at_peak(0), _live_nodes_at_peak(0) +{} + +size_t ArenaStatCounter::peak_since_start() const { + return _peak > _start ? _peak - _start : 0; +} + +void ArenaStatCounter::start() { + _peak = _start = _current; +} + +void ArenaStatCounter::update_c2_node_count() { +#ifdef COMPILER2 + CompilerThread* const th = Thread::current()->as_Compiler_thread(); + const CompileTask* const task = th->task(); + if (task != nullptr && + th->task()->compiler() != nullptr && + th->task()->compiler()->type() == compiler_c2) { + const Compile* const comp = Compile::current(); + if (comp != nullptr) { + _live_nodes_at_peak = comp->live_nodes(); + } + } +#endif +} + +// Account an arena allocation or de-allocation. +bool ArenaStatCounter::account(ssize_t delta, int tag) { + bool rc = false; +#ifdef ASSERT + // Note: if this fires, we free more arena memory under the scope of the + // CompilationMemoryHistoryMark than we allocate. This cannot be since we + // assume arena allocations in CompilerThread to be stack bound and symmetric. + assert(delta >= 0 || ((ssize_t)_current + delta) >= 0, + "Negative overflow (d=%zd %zu %zu %zu)", delta, _current, _start, _peak); +#endif + // Update totals + _current += delta; + // Update detail counter + switch ((Arena::Tag)tag) { + case Arena::Tag::tag_ra: _ra += delta; break; + case Arena::Tag::tag_node: _na += delta; break; + default: // ignore + break; + }; + // Did we reach a peak? + if (_current > _peak) { + _peak = _current; + assert(delta > 0, "Sanity (%zu %zu %zu)", _current, _start, _peak); + _na_at_peak = _na; + _ra_at_peak = _ra; + update_c2_node_count(); + rc = true; + } + return rc; +} + +void ArenaStatCounter::print_on(outputStream* st) const { + st->print("%zu [na %zu ra %zu]", peak_since_start(), _na_at_peak, _ra_at_peak); +#ifdef ASSERT + st->print(" (%zu->%zu->%zu)", _start, _peak, _current); +#endif +} + +////////////////////////// +// Backend + +class FullMethodName { + Symbol* const _k; + Symbol* const _m; + Symbol* const _s; + +public: + + FullMethodName(Symbol* k, Symbol* m, Symbol* s) : _k(k), _m(m), _s(s) {} + FullMethodName(const FullMethodName& o) : _k(o._k), _m(o._m), _s(o._s) {} + + void make_permanent() { + _k->make_permanent(); + _m->make_permanent(); + _s->make_permanent(); + } + + static unsigned compute_hash(const FullMethodName& n) { + return Symbol::compute_hash(n._k) ^ + Symbol::compute_hash(n._m) ^ + Symbol::compute_hash(n._s); + } + + char* as_C_string(char* buf, size_t len) const { + stringStream ss(buf, len); + ResourceMark rm; + ss.print_raw(_k->as_C_string()); + ss.print_raw("::"); + ss.print_raw(_m->as_C_string()); + ss.put('('); + ss.print_raw(_s->as_C_string()); + ss.put(')'); + return buf; + } + + bool equals(const FullMethodName& b) const { + return _k == b._k && _m == b._m && _s == b._s; + } + + bool operator== (const FullMethodName& other) const { return equals(other); } +}; + +// Note: not mtCompiler since we don't want to change what we measure +class MemStatEntry : public CHeapObj { + const FullMethodName _method; + CompilerType _comptype; + double _time; + // How often this has been recompiled. + int _num_recomp; + // Compiling thread. Only for diagnostic purposes. Thread may not be alive anymore. + const Thread* _thread; + + size_t _total; + size_t _na_at_peak; + size_t _ra_at_peak; + unsigned _live_nodes_at_peak; + +public: + + MemStatEntry(FullMethodName method) + : _method(method), _comptype(compiler_c1), + _time(0), _num_recomp(0), _thread(nullptr), + _total(0), _na_at_peak(0), _ra_at_peak(0), _live_nodes_at_peak(0) { + } + + void set_comptype(CompilerType comptype) { _comptype = comptype; } + void set_current_time() { _time = os::elapsedTime(); } + void set_current_thread() { _thread = Thread::current(); } + void inc_recompilation() { _num_recomp++; } + + void set_total(size_t n) { _total = n; } + void set_na_at_peak(size_t n) { _na_at_peak = n; } + void set_ra_at_peak(size_t n) { _ra_at_peak = n; } + void set_live_nodes_at_peak(unsigned n) { _live_nodes_at_peak = n; } + + size_t total() const { return _total; } + + static void print_legend(outputStream* st) { + st->print_cr("Legend:"); + st->print_cr(" total : memory allocated via arenas while compiling"); + st->print_cr(" NA : ...how much in node arenas (if c2)"); + st->print_cr(" RA : ...how much in resource areas"); + st->print_cr(" #nodes : ...how many nodes (if c2)"); + st->print_cr(" time : time of last compilation (sec)"); + st->print_cr(" type : compiler type"); + st->print_cr(" #rc : how often recompiled"); + st->print_cr(" thread : compiler thread"); + } + + static void print_header(outputStream* st) { + st->print_cr("total NA RA #nodes time type #rc thread method"); + } + + void print_on(outputStream* st, bool human_readable) const { + int col = 0; + + // Total + if (human_readable) { + st->print(PROPERFMT " ", PROPERFMTARGS(_total)); + } else { + st->print("%zu ", _total); + } + col += 10; st->fill_to(col); + + // NA + if (human_readable) { + st->print(PROPERFMT " ", PROPERFMTARGS(_na_at_peak)); + } else { + st->print("%zu ", _na_at_peak); + } + col += 10; st->fill_to(col); + + // RA + if (human_readable) { + st->print(PROPERFMT " ", PROPERFMTARGS(_ra_at_peak)); + } else { + st->print("%zu ", _ra_at_peak); + } + col += 10; st->fill_to(col); + + // Number of Nodes when memory peaked + st->print("%u ", _live_nodes_at_peak); + col += 8; st->fill_to(col); + + // TimeStamp + st->print("%.3f ", _time); + col += 8; st->fill_to(col); + + // Type + st->print("%s ", compilertype2name(_comptype)); + col += 6; st->fill_to(col); + + // Recomp + st->print("%u ", _num_recomp); + col += 4; st->fill_to(col); + + // Thread + st->print(PTR_FORMAT " ", p2i(_thread)); + + // MethodName + char buf[1024]; + st->print("%s ", _method.as_C_string(buf, sizeof(buf))); + st->cr(); + } + + int compare_by_size(const MemStatEntry* b) const { + const size_t x1 = b->_total; + const size_t x2 = _total; + return x1 < x2 ? -1 : x1 == x2 ? 0 : 1; + } + + bool equals(const FullMethodName& b) const { + return _method.equals(b); + } +}; + +class MemStatTable : + public ResourceHashtable +{ +public: + + void add(const FullMethodName& fmn, CompilerType comptype, + size_t total, size_t na_at_peak, size_t ra_at_peak, + unsigned live_nodes_at_peak) { + assert_lock_strong(NMTCompilationCostHistory_lock); + + MemStatEntry** pe = get(fmn); + MemStatEntry* e = nullptr; + if (pe == nullptr) { + e = new MemStatEntry(fmn); + put(fmn, e); + } else { + // Update existing entry + e = *pe; + assert(e != nullptr, "Sanity"); + } + e->set_current_time(); + e->set_current_thread(); + e->set_comptype(comptype); + e->inc_recompilation(); + e->set_total(total); + e->set_na_at_peak(na_at_peak); + e->set_ra_at_peak(ra_at_peak); + e->set_live_nodes_at_peak(live_nodes_at_peak); + } + + // Returns a C-heap-allocated SortMe array containing all entries from the table, + // optionally filtered by entry size + MemStatEntry** calc_flat_array(int& num, size_t min_size) { + assert_lock_strong(NMTCompilationCostHistory_lock); + + const int num_all = number_of_entries(); + MemStatEntry** flat = NEW_C_HEAP_ARRAY(MemStatEntry*, num_all, mtInternal); + int i = 0; + auto do_f = [&] (const FullMethodName& ignored, MemStatEntry* e) { + if (e->total() >= min_size) { + flat[i] = e; + assert(i < num_all, "Sanity"); + i ++; + } + }; + iterate_all(do_f); + if (min_size == 0) { + assert(i == num_all, "Sanity"); + } else { + assert(i <= num_all, "Sanity"); + } + num = i; + return flat; + } +}; + +bool CompilationMemoryStatistic::_enabled = false; + +static MemStatTable* _the_table = nullptr; + +void CompilationMemoryStatistic::initialize() { + assert(_enabled == false && _the_table == nullptr, "Only once"); + _the_table = new (mtCompiler) MemStatTable; + _enabled = true; + log_info(compilation, alloc)("Compilation memory statistic enabled"); +} + +void CompilationMemoryStatistic::on_start_compilation() { + assert(enabled(), "Not enabled?"); + Thread::current()->as_Compiler_thread()->arena_stat()->start(); +} + +void CompilationMemoryStatistic::on_end_compilation() { + assert(enabled(), "Not enabled?"); + ResourceMark rm; + CompilerThread* const th = Thread::current()->as_Compiler_thread(); + const ArenaStatCounter* const arena_stat = th->arena_stat(); + const CompilerType ct = th->task()->compiler()->type(); + + const Method* const m = th->task()->method(); + FullMethodName fmn(m->klass_name(), m->name(), m->signature()); + fmn.make_permanent(); + + const DirectiveSet* directive = th->task()->directive(); + assert(directive->should_collect_memstat(), "Only call if memstat is enabled"); + const bool print = directive->should_print_memstat(); + + if (print) { + char buf[1024]; + fmn.as_C_string(buf, sizeof(buf)); + tty->print("%s Arena usage %s: ", compilertype2name(ct), buf); + arena_stat->print_on(tty); + tty->cr(); + } + { + MutexLocker ml(NMTCompilationCostHistory_lock, Mutex::_no_safepoint_check_flag); + assert(_the_table != nullptr, "not initialized"); + + _the_table->add(fmn, ct, + arena_stat->peak_since_start(), // total + arena_stat->na_at_peak(), + arena_stat->ra_at_peak(), + arena_stat->live_nodes_at_peak()); + } +} + +void CompilationMemoryStatistic::on_arena_change(ssize_t diff, const Arena* arena) { + assert(enabled(), "Not enabled?"); + CompilerThread* const th = Thread::current()->as_Compiler_thread(); + th->arena_stat()->account(diff, (int)arena->get_tag()); +} + +static inline ssize_t diff_entries_by_size(const MemStatEntry* e1, const MemStatEntry* e2) { + return e1->compare_by_size(e2); +} + +void CompilationMemoryStatistic::print_all_by_size(outputStream* st, bool human_readable, size_t min_size) { + st->print_cr("Compilation memory statistics"); + + if (!enabled()) { + st->print_cr("(unavailable)"); + return; + } + + st->cr(); + + MemStatEntry::print_legend(st); + st->cr(); + + if (min_size > 0) { + st->print_cr(" (cutoff: %zu bytes)", min_size); + } + st->cr(); + + MemStatEntry::print_header(st); + + MemStatEntry** filtered = nullptr; + { + MutexLocker ml(NMTCompilationCostHistory_lock, Mutex::_no_safepoint_check_flag); + + if (_the_table != nullptr) { + // We sort with quicksort + int num = 0; + filtered = _the_table->calc_flat_array(num, min_size); + if (min_size > 0) { + st->print_cr("(%d/%d)", num, _the_table->number_of_entries()); + } + if (num > 0) { + QuickSort::sort(filtered, num, diff_entries_by_size, false); + // Now print. Has to happen under lock protection too, since entries may be changed. + for (int i = 0; i < num; i ++) { + filtered[i]->print_on(st, human_readable); + } + } else { + st->print_cr("No entries."); + } + } else { + st->print_cr("Not initialized."); + } + } // locked + + FREE_C_HEAP_ARRAY(Entry, filtered); +} + +CompilationMemoryStatisticMark::CompilationMemoryStatisticMark(const DirectiveSet* directive) + : _active(directive->should_collect_memstat()) { + if (_active) { + CompilationMemoryStatistic::on_start_compilation(); + } +} +CompilationMemoryStatisticMark::~CompilationMemoryStatisticMark() { + if (_active) { + CompilationMemoryStatistic::on_end_compilation(); + } +} diff --git a/src/hotspot/share/compiler/compilationMemoryStatistic.hpp b/src/hotspot/share/compiler/compilationMemoryStatistic.hpp new file mode 100644 index 00000000000..06ac9382199 --- /dev/null +++ b/src/hotspot/share/compiler/compilationMemoryStatistic.hpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Red Hat, Inc. 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 + * 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_COMPILER_COMPILATIONMEMORYSTATISTIC_HPP +#define SHARE_COMPILER_COMPILATIONMEMORYSTATISTIC_HPP + +#include "compiler/compilerDefinitions.hpp" +#include "memory/allocation.hpp" +#include "memory/allStatic.hpp" +#include "utilities/globalDefinitions.hpp" + +class outputStream; +class Symbol; +class DirectiveSet; + +// Counters for allocations from one arena +class ArenaStatCounter : public CHeapObj { + // Current bytes, total + size_t _current; + // bytes when compilation started + size_t _start; + // bytes at last peak, total + size_t _peak; + // Current bytes used for node arenas, total + size_t _na; + // Current bytes used for resource areas + size_t _ra; + + // Peak composition: + // Size of node arena when total peaked (c2 only) + size_t _na_at_peak; + // Size of resource area when total peaked + size_t _ra_at_peak; + // Number of live nodes when total peaked (c2 only) + unsigned _live_nodes_at_peak; + + void update_c2_node_count(); + +public: + ArenaStatCounter(); + + // Size of peak since last compilation + size_t peak_since_start() const; + + // Peak details + size_t na_at_peak() const { return _na_at_peak; } + size_t ra_at_peak() const { return _ra_at_peak; } + unsigned live_nodes_at_peak() const { return _live_nodes_at_peak; } + + // Mark the start of a compilation. + void start(); + + // Account an arena allocation or de-allocation. + // Returns true if new peak reached + bool account(ssize_t delta, int tag); + + void set_live_nodes_at_peak(unsigned i) { _live_nodes_at_peak = i; } + void print_on(outputStream* st) const; +}; + +class CompilationMemoryStatistic : public AllStatic { + static bool _enabled; +public: + static void initialize(); + // true if CollectMemStat or PrintMemStat has been enabled for any method + static bool enabled() { return _enabled; } + static void on_start_compilation(); + static void on_end_compilation(); + static void on_arena_change(ssize_t diff, const Arena* arena); + static void print_all_by_size(outputStream* st, bool human_readable, size_t minsize); +}; + +// RAII object to wrap one compilation +class CompilationMemoryStatisticMark { + const bool _active; +public: + CompilationMemoryStatisticMark(const DirectiveSet* directive); + ~CompilationMemoryStatisticMark(); +}; + +#endif // SHARE_COMPILER_COMPILATIONMEMORYSTATISTIC_HPP diff --git a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp index 723e7a08cc0..f265271857d 100644 --- a/src/hotspot/share/compiler/compileBroker.cpp +++ b/src/hotspot/share/compiler/compileBroker.cpp @@ -31,6 +31,7 @@ #include "code/codeHeapState.hpp" #include "code/dependencyContext.hpp" #include "compiler/compilationLog.hpp" +#include "compiler/compilationMemoryStatistic.hpp" #include "compiler/compilationPolicy.hpp" #include "compiler/compileBroker.hpp" #include "compiler/compileLog.hpp" @@ -650,6 +651,10 @@ void CompileBroker::compilation_init(JavaThread* THREAD) { } #endif // INCLUDE_JVMCI + if (CompilerOracle::should_collect_memstat()) { + CompilationMemoryStatistic::initialize(); + } + // Start the compiler thread(s) init_compiler_threads(); // totalTime performance counter is always created as it is required @@ -1695,8 +1700,11 @@ void CompileBroker::wait_for_completion(CompileTask* task) { bool free_task; #if INCLUDE_JVMCI AbstractCompiler* comp = compiler(task->comp_level()); - if (comp->is_jvmci() && !task->should_wait_for_compilation()) { + if (!UseJVMCINativeLibrary && comp->is_jvmci() && !task->should_wait_for_compilation()) { // It may return before compilation is completed. + // Note that libjvmci should not pre-emptively unblock + // a thread waiting for a compilation as it does not call + // Java code and so is not deadlock prone like jarjvmci. free_task = wait_for_jvmci_completion((JVMCICompiler*) comp, task, thread); } else #endif diff --git a/src/hotspot/share/compiler/compilerDefinitions.hpp b/src/hotspot/share/compiler/compilerDefinitions.hpp index 4e8b55f1a36..6321e3e0738 100644 --- a/src/hotspot/share/compiler/compilerDefinitions.hpp +++ b/src/hotspot/share/compiler/compilerDefinitions.hpp @@ -38,7 +38,7 @@ enum CompilerType : u1 { }; extern const char* compilertype2name_tab[compiler_number_of_types]; // Map CompilerType to its name -inline const char* compilertype2name(CompilerType t) { return (uint)t < compiler_number_of_types ? compilertype2name_tab[t] : nullptr; } +inline const char* compilertype2name(CompilerType t) { return (uint)t < compiler_number_of_types ? compilertype2name_tab[t] : "invalid"; } // Handy constants for deciding which compiler mode to use. enum MethodCompilation { diff --git a/src/hotspot/share/compiler/compilerDirectives.cpp b/src/hotspot/share/compiler/compilerDirectives.cpp index a15f9974f5e..0368a6ad744 100644 --- a/src/hotspot/share/compiler/compilerDirectives.cpp +++ b/src/hotspot/share/compiler/compilerDirectives.cpp @@ -202,6 +202,14 @@ bool DirectiveSet::is_c2(CompilerDirectives* directive) const { return this == directive->_c2_store; } +bool DirectiveSet::should_collect_memstat() const { + return MemStatOption > 0; +} + +bool DirectiveSet::should_print_memstat() const { + return MemStatOption == (uintx)MemStatAction::print; +} + // In the list of Control/disabled intrinsics, the ID of the control intrinsics can separated: // - by ',' (if -XX:Control/DisableIntrinsic is used once when invoking the VM) or // - by '\n' (if -XX:Control/DisableIntrinsic is used multiple times when invoking the VM) or diff --git a/src/hotspot/share/compiler/compilerDirectives.hpp b/src/hotspot/share/compiler/compilerDirectives.hpp index f05bec6a104..8c8dcb99f0d 100644 --- a/src/hotspot/share/compiler/compilerDirectives.hpp +++ b/src/hotspot/share/compiler/compilerDirectives.hpp @@ -41,6 +41,7 @@ cflags(BreakAtExecute, bool, false, BreakAtExecute) \ cflags(BreakAtCompile, bool, false, BreakAtCompile) \ cflags(Log, bool, LogCompilation, Unknown) \ + cflags(MemStat, uintx, 0, MemStat) \ cflags(PrintAssembly, bool, PrintAssembly, PrintAssembly) \ cflags(PrintCompilation, bool, PrintCompilation, PrintCompilation) \ cflags(PrintInlining, bool, PrintInlining, PrintInlining) \ @@ -131,6 +132,8 @@ class DirectiveSet : public CHeapObj { void finalize(outputStream* st); bool is_c1(CompilerDirectives* directive) const; bool is_c2(CompilerDirectives* directive) const; + bool should_collect_memstat() const; + bool should_print_memstat() const; typedef enum { #define enum_of_flags(name, type, dvalue, cc_flag) name##Index, diff --git a/src/hotspot/share/compiler/compilerOracle.cpp b/src/hotspot/share/compiler/compilerOracle.cpp index 031e9b25b7a..e1fb019bdbf 100644 --- a/src/hotspot/share/compiler/compilerOracle.cpp +++ b/src/hotspot/share/compiler/compilerOracle.cpp @@ -101,6 +101,7 @@ class TypedMethodOptionMatcher; static TypedMethodOptionMatcher* option_list = nullptr; static bool any_set = false; +static bool print_final_memstat_report = false; // A filter for quick lookup if an option is set static bool option_filter[static_cast(CompileCommand::Unknown) + 1] = { 0 }; @@ -325,6 +326,7 @@ static void register_command(TypedMethodOptionMatcher* matcher, tty->print("CompileCommand: %s ", option2name(option)); matcher->print(); } + return; } @@ -455,6 +457,15 @@ bool CompilerOracle::should_print_methods() { return has_command(CompileCommand::Print); } +// Tells whether there are any methods to collect memory statistics for +bool CompilerOracle::should_collect_memstat() { + return has_command(CompileCommand::MemStat); +} + +bool CompilerOracle::should_print_final_memstat_report() { + return print_final_memstat_report; +} + bool CompilerOracle::should_log(const methodHandle& method) { if (!LogCompilation) return false; if (!has_command(CompileCommand::Log)) { @@ -623,6 +634,22 @@ void skip_comma(char* &line) { } } +static bool parseEnumValueAsUintx(enum CompileCommand option, const char* line, uintx& value, int& bytes_read, char* errorbuf, const int buf_size) { + if (option == CompileCommand::MemStat) { + if (strncasecmp(line, "collect", 7) == 0) { + value = (uintx)MemStatAction::collect; + } else if (strncasecmp(line, "print", 5) == 0) { + value = (uintx)MemStatAction::print; + print_final_memstat_report = true; + } else { + jio_snprintf(errorbuf, buf_size, "MemStat: invalid value expected 'collect' or 'print' (omitting value means 'collect')"); + } + return true; // handled + } + return false; +#undef HANDLE_VALUE +} + static void scan_value(enum OptionType type, char* line, int& total_bytes_read, TypedMethodOptionMatcher* matcher, enum CompileCommand option, char* errorbuf, const int buf_size) { int bytes_read = 0; @@ -642,7 +669,13 @@ static void scan_value(enum OptionType type, char* line, int& total_bytes_read, } } else if (type == OptionType::Uintx) { uintx value; - if (sscanf(line, "" UINTX_FORMAT "%n", &value, &bytes_read) == 1) { + // Is it a named enum? + bool success = parseEnumValueAsUintx(option, line, value, bytes_read, errorbuf, buf_size); + if (!success) { + // Is it a raw number? + success = (sscanf(line, "" UINTX_FORMAT "%n", &value, &bytes_read) == 1); + } + if (success) { total_bytes_read += bytes_read; line += bytes_read; register_command(matcher, option, value); @@ -914,10 +947,14 @@ bool CompilerOracle::parse_from_line(char* line) { } skip_whitespace(line); if (*line == '\0') { - // if this is a bool option this implies true if (option2type(option) == OptionType::Bool) { + // if this is a bool option this implies true register_command(matcher, option, true); return true; + } else if (option == CompileCommand::MemStat) { + // MemStat default action is to collect data but to not print + register_command(matcher, option, (uintx)MemStatAction::collect); + return true; } else { jio_snprintf(error_buf, sizeof(error_buf), " Option '%s' is not followed by a value", option2name(option)); print_parse_error(error_buf, original.get()); diff --git a/src/hotspot/share/compiler/compilerOracle.hpp b/src/hotspot/share/compiler/compilerOracle.hpp index 1de323cae01..251f29fda38 100644 --- a/src/hotspot/share/compiler/compilerOracle.hpp +++ b/src/hotspot/share/compiler/compilerOracle.hpp @@ -57,6 +57,7 @@ class methodHandle; option(Break, "break", Bool) \ option(BreakAtExecute, "BreakAtExecute", Bool) \ option(BreakAtCompile, "BreakAtCompile", Bool) \ + option(MemStat, "MemStat", Uintx) \ option(PrintAssembly, "PrintAssembly", Bool) \ option(PrintCompilation, "PrintCompilation", Bool) \ option(PrintInlining, "PrintInlining", Bool) \ @@ -113,6 +114,10 @@ enum class OptionType { Unknown }; +enum class MemStatAction { + collect = 1, print = 2 +}; + class CompilerOracle : AllStatic { private: static bool _quiet; @@ -151,6 +156,10 @@ class CompilerOracle : AllStatic { // Tells whether there are any methods to print for print_method_statistics() static bool should_print_methods(); + // Tells whether there are any methods to (collect|collect+print) memory statistics for + static bool should_collect_memstat(); + static bool should_print_final_memstat_report(); + // Tags the method as blackhole candidate, if possible. static void tag_blackhole_if_possible(const methodHandle& method); diff --git a/src/hotspot/share/compiler/compilerThread.cpp b/src/hotspot/share/compiler/compilerThread.cpp index ddd49fec175..77a5f4b7027 100644 --- a/src/hotspot/share/compiler/compilerThread.cpp +++ b/src/hotspot/share/compiler/compilerThread.cpp @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "compiler/compilationMemoryStatistic.hpp" #include "compiler/compileBroker.hpp" #include "compiler/compileTask.hpp" #include "compiler/compilerThread.hpp" @@ -39,6 +40,7 @@ CompilerThread::CompilerThread(CompileQueue* queue, _counters = counters; _buffer_blob = nullptr; _compiler = nullptr; + _arena_stat = CompilationMemoryStatistic::enabled() ? new ArenaStatCounter : nullptr; // Compiler uses resource area for compilation, let's bias it to mtCompiler resource_area()->bias_to(mtCompiler); @@ -51,6 +53,7 @@ CompilerThread::CompilerThread(CompileQueue* queue, CompilerThread::~CompilerThread() { // Delete objects which were allocated on heap. delete _counters; + delete _arena_stat; } void CompilerThread::thread_entry(JavaThread* thread, TRAPS) { diff --git a/src/hotspot/share/compiler/compilerThread.hpp b/src/hotspot/share/compiler/compilerThread.hpp index 3ae4bef3dad..65bb4481c02 100644 --- a/src/hotspot/share/compiler/compilerThread.hpp +++ b/src/hotspot/share/compiler/compilerThread.hpp @@ -27,8 +27,9 @@ #include "runtime/javaThread.hpp" -class BufferBlob; class AbstractCompiler; +class ArenaStatCounter; +class BufferBlob; class ciEnv; class CompileThread; class CompileLog; @@ -54,6 +55,8 @@ class CompilerThread : public JavaThread { AbstractCompiler* _compiler; TimeStamp _idle_time; + ArenaStatCounter* _arena_stat; + public: static CompilerThread* current() { @@ -81,6 +84,7 @@ class CompilerThread : public JavaThread { CompileQueue* queue() const { return _queue; } CompilerCounters* counters() const { return _counters; } + ArenaStatCounter* arena_stat() const { return _arena_stat; } // Get/set the thread's compilation environment. ciEnv* env() { return _env; } diff --git a/src/hotspot/share/gc/g1/g1Allocator.cpp b/src/hotspot/share/gc/g1/g1Allocator.cpp index cf44b68fc86..ffd2b65f555 100644 --- a/src/hotspot/share/gc/g1/g1Allocator.cpp +++ b/src/hotspot/share/gc/g1/g1Allocator.cpp @@ -191,11 +191,14 @@ size_t G1Allocator::unsafe_max_tlab_alloc() { uint node_index = current_node_index(); HeapRegion* hr = mutator_alloc_region(node_index)->get(); size_t max_tlab = _g1h->max_tlab_size() * wordSize; - if (hr == nullptr) { + + if (hr == nullptr || hr->free() < MinTLABSize) { + // The next TLAB allocation will most probably happen in a new region, + // therefore we can attempt to allocate the maximum allowed TLAB size. return max_tlab; - } else { - return clamp(hr->free(), MinTLABSize, max_tlab); } + + return MIN2(hr->free(), max_tlab); } size_t G1Allocator::used_in_alloc_regions() { diff --git a/src/hotspot/share/gc/serial/generation.cpp b/src/hotspot/share/gc/serial/generation.cpp index 32f70f08c65..0c011d813ba 100644 --- a/src/hotspot/share/gc/serial/generation.cpp +++ b/src/hotspot/share/gc/serial/generation.cpp @@ -25,12 +25,12 @@ #include "precompiled.hpp" #include "gc/serial/cardTableRS.hpp" #include "gc/serial/generation.hpp" +#include "gc/serial/serialHeap.hpp" #include "gc/shared/collectedHeap.inline.hpp" #include "gc/shared/continuationGCSupport.inline.hpp" #include "gc/shared/gcLocker.hpp" #include "gc/shared/gcTimer.hpp" #include "gc/shared/gcTrace.hpp" -#include "gc/shared/genCollectedHeap.hpp" #include "gc/shared/generationSpec.hpp" #include "gc/shared/space.inline.hpp" #include "gc/shared/spaceDecorator.inline.hpp" @@ -58,11 +58,11 @@ Generation::Generation(ReservedSpace rs, size_t initial_size) : } size_t Generation::initial_size() { - GenCollectedHeap* gch = GenCollectedHeap::heap(); - if (gch->is_young_gen(this)) { - return gch->young_gen_spec()->init_size(); + SerialHeap* serial_heap = SerialHeap::heap(); + if (serial_heap->is_young_gen(this)) { + return serial_heap->young_gen_spec()->init_size(); } - return gch->old_gen_spec()->init_size(); + return serial_heap->old_gen_spec()->init_size(); } size_t Generation::max_capacity() const { @@ -86,7 +86,7 @@ void Generation::print_summary_info_on(outputStream* st) { double time = sr->accumulated_time.seconds(); st->print_cr("Accumulated %s generation GC time %3.7f secs, " "%u GC's, avg GC time %3.7f", - GenCollectedHeap::heap()->is_young_gen(this) ? "young" : "old" , + SerialHeap::heap()->is_young_gen(this) ? "young" : "old" , time, sr->invocations, sr->invocations > 0 ? time / sr->invocations : 0.0); @@ -94,18 +94,6 @@ void Generation::print_summary_info_on(outputStream* st) { // Utility iterator classes -class GenerationIsInReservedClosure : public SpaceClosure { - public: - const void* _p; - Space* sp; - virtual void do_space(Space* s) { - if (sp == nullptr) { - if (s->is_in_reserved(_p)) sp = s; - } - } - GenerationIsInReservedClosure(const void* p) : _p(p), sp(nullptr) {} -}; - class GenerationIsInClosure : public SpaceClosure { public: const void* _p; @@ -128,8 +116,8 @@ size_t Generation::max_contiguous_available() const { // The largest number of contiguous free words in this or any higher generation. size_t avail = contiguous_available(); size_t old_avail = 0; - if (GenCollectedHeap::heap()->is_young_gen(this)) { - old_avail = GenCollectedHeap::heap()->old_gen()->contiguous_available(); + if (SerialHeap::heap()->is_young_gen(this)) { + old_avail = SerialHeap::heap()->old_gen()->contiguous_available(); } return MAX2(avail, old_avail); } @@ -147,7 +135,7 @@ oop Generation::promote(oop obj, size_t obj_size) { assert(obj_size == obj->size(), "bad obj_size passed in"); #ifndef PRODUCT - if (GenCollectedHeap::heap()->promotion_should_fail()) { + if (SerialHeap::heap()->promotion_should_fail()) { return nullptr; } #endif // #ifndef PRODUCT @@ -248,8 +236,6 @@ void Generation::object_iterate(ObjectClosure* cl) { space_iterate(&blk); } -#if INCLUDE_SERIALGC - void Generation::prepare_for_compaction(CompactPoint* cp) { // Generic implementation, can be specialized ContiguousSpace* space = first_compaction_space(); @@ -280,5 +266,3 @@ void Generation::compact() { sp = sp->next_compaction_space(); } } - -#endif // INCLUDE_SERIALGC diff --git a/src/hotspot/share/gc/serial/generation.hpp b/src/hotspot/share/gc/serial/generation.hpp index b005abc7a61..2c0f72012ac 100644 --- a/src/hotspot/share/gc/serial/generation.hpp +++ b/src/hotspot/share/gc/serial/generation.hpp @@ -54,7 +54,6 @@ class GenerationSpec; class ContiguousSpace; class CompactPoint; class OopClosure; -class GenCollectedHeap; class GCStats; // A "ScratchBlock" represents a block of memory in one generation usable by @@ -290,14 +289,12 @@ class Generation: public CHeapObj { GCStats* gc_stats() const { return _gc_stats; } virtual void update_gc_stats(Generation* current_generation, bool full) {} -#if INCLUDE_SERIALGC // Mark sweep support phase2 virtual void prepare_for_compaction(CompactPoint* cp); // Mark sweep support phase3 virtual void adjust_pointers(); // Mark sweep support phase4 virtual void compact(); -#endif // Accessing "marks". diff --git a/src/hotspot/share/gc/shared/space.hpp b/src/hotspot/share/gc/shared/space.hpp index ca9c97a2fff..3729df804fb 100644 --- a/src/hotspot/share/gc/shared/space.hpp +++ b/src/hotspot/share/gc/shared/space.hpp @@ -86,13 +86,13 @@ class Space: public CHeapObj { virtual void set_bottom(HeapWord* value) { _bottom = value; } virtual void set_end(HeapWord* value) { _end = value; } - virtual HeapWord* saved_mark_word() const { return _saved_mark_word; } + HeapWord* saved_mark_word() const { return _saved_mark_word; } void set_saved_mark_word(HeapWord* p) { _saved_mark_word = p; } // Returns true if this object has been allocated since a // generation's "save_marks" call. - virtual bool obj_allocated_since_save_marks(const oop obj) const { + bool obj_allocated_since_save_marks(const oop obj) const { return cast_from_oop(obj) >= saved_mark_word(); } @@ -178,7 +178,7 @@ class Space: public CHeapObj { // structure supporting these calls, possibly speeding up future calls. // The default implementation, however, is simply to call the const // version. - virtual HeapWord* block_start(const void* p); + HeapWord* block_start(const void* p); // Requires "addr" to be the start of a chunk, and returns its size. // "addr + size" is required to be the start of a new chunk, or the end @@ -191,7 +191,7 @@ class Space: public CHeapObj { // Requires "addr" to be the start of a block, and returns "TRUE" iff // the block is an object and the object is alive. - virtual bool obj_is_alive(const HeapWord* addr) const; + bool obj_is_alive(const HeapWord* addr) const; // Allocation (return null if full). Assumes the caller has established // mutually exclusive access to the space. @@ -206,10 +206,10 @@ class Space: public CHeapObj { virtual void adjust_pointers() = 0; #endif - virtual void print() const; + void print() const; virtual void print_on(outputStream* st) const; - virtual void print_short() const; - virtual void print_short_on(outputStream* st) const; + void print_short() const; + void print_short_on(outputStream* st) const; // Debugging virtual void verify() const = 0; diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp index 36d2bf7b68f..049d8286ed4 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp @@ -530,7 +530,7 @@ Node* ShenandoahBarrierSetC2::store_at_resolved(C2Access& access, C2AccessValue& Node* ShenandoahBarrierSetC2::load_at_resolved(C2Access& access, const Type* val_type) const { // 1: non-reference load, no additional barrier is needed if (!access.is_oop()) { - return BarrierSetC2::load_at_resolved(access, val_type);; + return BarrierSetC2::load_at_resolved(access, val_type); } Node* load = BarrierSetC2::load_at_resolved(access, val_type); diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp index 8c7ee3a3aed..4e041eaffe2 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp @@ -63,6 +63,7 @@ bool ShenandoahBarrierC2Support::expand(Compile* C, PhaseIterGVN& igvn) { C->clear_major_progress(); C->process_for_post_loop_opts_igvn(igvn); + if (C->failing()) return false; } C->set_post_loop_opts_phase(); // now for real! } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp index 8dd4fd71ab8..6dc57139f45 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp @@ -353,10 +353,6 @@ bool ShenandoahAllocationRate::is_spiking(double rate, double threshold) const { return false; } -double ShenandoahAllocationRate::instantaneous_rate(size_t allocated) const { - return instantaneous_rate(os::elapsedTime(), allocated); -} - double ShenandoahAllocationRate::instantaneous_rate(double time, size_t allocated) const { size_t last_value = _last_sample_value; double last_time = _last_sample_time; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp index f7695b7e953..be86b7297b0 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp @@ -37,7 +37,6 @@ class ShenandoahAllocationRate : public CHeapObj { double sample(size_t allocated); - double instantaneous_rate(size_t allocated) const; double upper_bound(double sds) const; bool is_spiking(double rate, double threshold) const; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp index 1c8daba3d24..6674c40f768 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp @@ -220,4 +220,4 @@ void ShenandoahAssertNotForwardedClosure::do_oop(narrowOop* p) { do_oop_work(p); void ShenandoahAssertNotForwardedClosure::do_oop(oop* p) { do_oop_work(p); } #endif -#endif // SHARE_GC_SHENANDOAH_SHENANDOAHCLOSURES_HPP +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHCLOSURES_INLINE_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp index a297abdb297..92d447258f2 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp @@ -36,70 +36,6 @@ #include "runtime/atomic.hpp" #include "utilities/powerOfTwo.hpp" -ShenandoahParallelCodeCacheIterator::ShenandoahParallelCodeCacheIterator(const GrowableArray* heaps) { - _length = heaps->length(); - _iters = NEW_C_HEAP_ARRAY(ShenandoahParallelCodeHeapIterator, _length, mtGC); - for (int h = 0; h < _length; h++) { - _iters[h] = ShenandoahParallelCodeHeapIterator(heaps->at(h)); - } -} - -ShenandoahParallelCodeCacheIterator::~ShenandoahParallelCodeCacheIterator() { - FREE_C_HEAP_ARRAY(ParallelCodeHeapIterator, _iters); -} - -void ShenandoahParallelCodeCacheIterator::parallel_blobs_do(CodeBlobClosure* f) { - for (int c = 0; c < _length; c++) { - _iters[c].parallel_blobs_do(f); - } -} - -ShenandoahParallelCodeHeapIterator::ShenandoahParallelCodeHeapIterator(CodeHeap* heap) : - _heap(heap), _claimed_idx(0), _finished(false) { -} - -void ShenandoahParallelCodeHeapIterator::parallel_blobs_do(CodeBlobClosure* f) { - assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint"); - - /* - * Parallel code heap walk. - * - * This code makes all threads scan all code heaps, but only one thread would execute the - * closure on given blob. This is achieved by recording the "claimed" blocks: if a thread - * had claimed the block, it can process all blobs in it. Others have to fast-forward to - * next attempt without processing. - * - * Late threads would return immediately if iterator is finished. - */ - - if (_finished) { - return; - } - - int stride = 256; // educated guess - int stride_mask = stride - 1; - assert (is_power_of_2(stride), "sanity"); - - int count = 0; - bool process_block = true; - - for (CodeBlob *cb = CodeCache::first_blob(_heap); cb != nullptr; cb = CodeCache::next_blob(_heap, cb)) { - int current = count++; - if ((current & stride_mask) == 0) { - process_block = (current >= _claimed_idx) && - (Atomic::cmpxchg(&_claimed_idx, current, current + stride, memory_order_relaxed) == current); - } - if (process_block) { - f->do_code_blob(cb); -#ifdef ASSERT - if (cb->is_nmethod()) - Universe::heap()->verify_nmethod((nmethod*)cb); -#endif - } - } - - _finished = true; -} ShenandoahNMethodTable* ShenandoahCodeRoots::_nmethod_table; int ShenandoahCodeRoots::_disarmed_value = 1; @@ -303,7 +239,6 @@ void ShenandoahCodeRoots::purge() { } ShenandoahCodeRootsIterator::ShenandoahCodeRootsIterator() : - _par_iterator(CodeCache::heaps()), _table_snapshot(nullptr) { assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint"); MutexLocker locker(CodeCache_lock, Mutex::_no_safepoint_check_flag); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp index 84a7523b93d..b8870e71ed0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp @@ -39,38 +39,9 @@ class ShenandoahNMethodTable; class ShenandoahNMethodTableSnapshot; class WorkerThreads; -class ShenandoahParallelCodeHeapIterator { - friend class CodeCache; -private: - CodeHeap* _heap; - shenandoah_padding(0); - volatile int _claimed_idx; - volatile bool _finished; - shenandoah_padding(1); -public: - ShenandoahParallelCodeHeapIterator(CodeHeap* heap); - void parallel_blobs_do(CodeBlobClosure* f); -}; - -class ShenandoahParallelCodeCacheIterator { - friend class CodeCache; -private: - ShenandoahParallelCodeHeapIterator* _iters; - int _length; - - NONCOPYABLE(ShenandoahParallelCodeCacheIterator); - -public: - ShenandoahParallelCodeCacheIterator(const GrowableArray* heaps); - ~ShenandoahParallelCodeCacheIterator(); - void parallel_blobs_do(CodeBlobClosure* f); -}; - class ShenandoahCodeRootsIterator { friend class ShenandoahCodeRoots; protected: - ShenandoahParallelCodeCacheIterator _par_iterator; - ShenandoahSharedFlag _seq_claimed; ShenandoahNMethodTableSnapshot* _table_snapshot; public: @@ -88,7 +59,6 @@ class ShenandoahCodeRoots : public AllStatic { static void initialize(); static void register_nmethod(nmethod* nm); static void unregister_nmethod(nmethod* nm); - static void flush_nmethod(nmethod* nm); static ShenandoahNMethodTable* table() { return _nmethod_table; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp index 6e709963236..e04c55d362d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp @@ -58,7 +58,6 @@ class ShenandoahConcurrentMarkingTask : public WorkerTask { ShenandoahHeap* heap = ShenandoahHeap::heap(); ShenandoahConcurrentWorkerSession worker_session(worker_id); ShenandoahSuspendibleThreadSetJoiner stsj(ShenandoahSuspendibleWorkers); - ShenandoahObjToScanQueue* q = _cm->get_queue(worker_id); ShenandoahReferenceProcessor* rp = heap->ref_processor(); assert(rp != nullptr, "need reference processor"); StringDedup::Requests requests; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp index 45891838312..c0d6b007a27 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp @@ -279,7 +279,7 @@ HeapWord* ShenandoahFreeSet::allocate_contiguous(ShenandoahAllocRequest& req) { } end++; - }; + } size_t remainder = words_size & ShenandoahHeapRegion::region_size_words_mask(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGC.cpp index fa193880293..922f54edf3c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGC.cpp @@ -66,7 +66,6 @@ class ShenandoahUpdateRootsTask : public WorkerTask { assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Must be at a safepoint"); ShenandoahParallelWorkerSession worker_session(worker_id); - ShenandoahHeap* heap = ShenandoahHeap::heap(); ShenandoahUpdateRefsClosure cl; if (_check_alive) { ShenandoahForwardedIsAliveClosure is_alive; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 625f2ee847b..75c2528b46e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -1893,14 +1893,6 @@ address ShenandoahHeap::in_cset_fast_test_addr() { return (address) heap->collection_set()->biased_map_address(); } -address ShenandoahHeap::cancelled_gc_addr() { - return (address) ShenandoahHeap::heap()->_cancelled_gc.addr_of(); -} - -address ShenandoahHeap::gc_state_addr() { - return (address) ShenandoahHeap::heap()->_gc_state.addr_of(); -} - size_t ShenandoahHeap::bytes_allocated_since_gc_start() const { return Atomic::load(&_bytes_allocated_since_gc_start); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index b310a14a1be..9e837189de8 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -295,7 +295,6 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { public: char gc_state() const; - static address gc_state_addr(); void set_concurrent_mark_in_progress(bool in_progress); void set_evacuation_in_progress(bool in_progress); @@ -316,7 +315,7 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { inline bool is_full_gc_in_progress() const; inline bool is_full_gc_move_in_progress() const; inline bool has_forwarded_objects() const; - inline bool is_gc_in_progress_mask(uint mask) const; + inline bool is_stw_gc_in_progress() const; inline bool is_concurrent_strong_root_in_progress() const; inline bool is_concurrent_weak_root_in_progress() const; @@ -336,7 +335,6 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { bool try_cancel_gc(); public: - static address cancelled_gc_addr(); inline bool cancelled_gc() const; inline bool check_cancelled_gc_and_yield(bool sts_active = true); @@ -356,7 +354,6 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { void prepare_gc(); void prepare_regions_and_collection_set(bool concurrent); // Evacuation - void prepare_evacuation(bool concurrent); void evacuate_collection_set(bool concurrent); // Concurrent root processing void prepare_concurrent_roots(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp index efdc89ac648..4689e274773 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp @@ -383,10 +383,6 @@ inline bool ShenandoahHeap::is_evacuation_in_progress() const { return _gc_state.is_set(EVACUATION); } -inline bool ShenandoahHeap::is_gc_in_progress_mask(uint mask) const { - return _gc_state.is_set(mask); -} - inline bool ShenandoahHeap::is_degenerated_gc_in_progress() const { return _degenerated_gc_in_progress.is_set(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahJfrSupport.hpp b/src/hotspot/share/gc/shenandoah/shenandoahJfrSupport.hpp index 2ce1cd0b95f..1553787265c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahJfrSupport.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahJfrSupport.hpp @@ -38,4 +38,4 @@ class ShenandoahJFRSupport { static void register_jfr_type_serializers(); }; -#endif // SHARE_VM_GC_SHENANDOAH_SHENANDOAHJFRSUPPORT_HPP +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHJFRSUPPORT_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp index bca50b58197..25eed7c66e6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp @@ -30,14 +30,10 @@ #include "gc/shenandoah/shenandoahOopClosures.hpp" #include "gc/shenandoah/shenandoahTaskqueue.hpp" -class ShenandoahCMDrainMarkingStackClosure; - // Base class for mark // Mark class does not maintain states. Instead, mark states are // maintained by task queues, mark bitmap and SATB buffers (concurrent mark) class ShenandoahMark: public StackObj { - friend class ShenandoahCMDrainMarkingStackClosure; - protected: ShenandoahObjToScanQueueSet* const _task_queues; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp index 74aafeb3831..980050b8b00 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp @@ -52,39 +52,6 @@ ShenandoahNMethod::~ShenandoahNMethod() { } } -class ShenandoahHasCSetOopClosure : public OopClosure { -private: - ShenandoahHeap* const _heap; - bool _has_cset_oops; - -public: - ShenandoahHasCSetOopClosure(ShenandoahHeap *heap) : - _heap(heap), - _has_cset_oops(false) { - } - - bool has_cset_oops() const { - return _has_cset_oops; - } - - void do_oop(oop* p) { - oop value = RawAccess<>::oop_load(p); - if (!_has_cset_oops && _heap->in_collection_set(value)) { - _has_cset_oops = true; - } - } - - void do_oop(narrowOop* p) { - ShouldNotReachHere(); - } -}; - -bool ShenandoahNMethod::has_cset_oops(ShenandoahHeap *heap) { - ShenandoahHasCSetOopClosure cl(heap); - oops_do(&cl); - return cl.has_cset_oops(); -} - void ShenandoahNMethod::update() { ResourceMark rm; bool non_immediate_oops = false; @@ -209,10 +176,6 @@ class ShenandoahNMethodOopDetector : public OopClosure { GrowableArray* oops() { return &_oops; } - - bool has_oops() { - return !_oops.is_empty(); - } }; void ShenandoahNMethod::assert_same_oops(bool allow_dead) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp index 4fc90b03bed..6a856e68403 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp @@ -55,12 +55,6 @@ class ShenandoahNMethod : public CHeapObj { // Update oops when the nmethod is re-registered void update(); - bool has_cset_oops(ShenandoahHeap* heap); - - inline int oop_count() const; - inline bool has_oops() const; - - inline void mark_unregistered(); inline bool is_unregistered() const; static ShenandoahNMethod* for_nmethod(nmethod* nm); @@ -77,7 +71,6 @@ class ShenandoahNMethod : public CHeapObj { void assert_same_oops(bool allow_dead = false) NOT_DEBUG_RETURN; private: - bool has_non_immed_oops() const { return _has_non_immed_oops; } static void detect_reloc_oops(nmethod* nm, GrowableArray& oops, bool& _has_non_immed_oops); }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.inline.hpp index 2e642246272..df721c0a695 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.inline.hpp @@ -39,18 +39,6 @@ ShenandoahReentrantLock* ShenandoahNMethod::lock() { return &_lock; } -int ShenandoahNMethod::oop_count() const { - return _oops_count + static_cast(nm()->oops_end() - nm()->oops_begin()); -} - -bool ShenandoahNMethod::has_oops() const { - return oop_count() > 0; -} - -void ShenandoahNMethod::mark_unregistered() { - _unregistered = true; -} - bool ShenandoahNMethod::is_unregistered() const { return _unregistered; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp index 8c4f70dcd0f..f99de804bd5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp @@ -402,7 +402,7 @@ T* ShenandoahReferenceProcessor::keep(oop reference, ReferenceType type, uint wo } template -void ShenandoahReferenceProcessor::process_references(ShenandoahRefProcThreadLocal& refproc_data, uint worker_id) {; +void ShenandoahReferenceProcessor::process_references(ShenandoahRefProcThreadLocal& refproc_data, uint worker_id) { log_trace(gc, ref)("Processing discovered list #%u : " PTR_FORMAT, worker_id, p2i(refproc_data.discovered_list_head())); T* list = refproc_data.discovered_list_addr(); // The list head is basically a GC root, we need to resolve and update it, diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp index 8b7c8079aa5..639b35deca3 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp @@ -89,7 +89,6 @@ void ShenandoahCodeCacheRoots::code_blobs_do(CodeBlobClosure* blob_cl, uint work ShenandoahRootProcessor::ShenandoahRootProcessor(ShenandoahPhaseTimings::Phase phase) : _heap(ShenandoahHeap::heap()), - _phase(phase), _worker_phase(phase) { } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp index 75a23aea045..fcb28dfbce0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp @@ -134,7 +134,6 @@ class ShenandoahClassLoaderDataRoots { class ShenandoahRootProcessor : public StackObj { private: ShenandoahHeap* const _heap; - const ShenandoahPhaseTimings::Phase _phase; const ShenandoahGCWorkerPhase _worker_phase; public: ShenandoahRootProcessor(ShenandoahPhaseTimings::Phase phase); diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp index a46be0af239..6942fe0563f 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp @@ -2436,6 +2436,16 @@ C2V_VMENTRY_0(jint, arrayIndexScale, (JNIEnv* env, jobject, jchar type_char)) return type2aelembytes(type); C2V_END +C2V_VMENTRY(void, clearOopHandle, (JNIEnv* env, jobject, jlong oop_handle)) + if (oop_handle == 0L) { + JVMCI_THROW(NullPointerException); + } + // Assert before nulling out, for better debugging. + assert(JVMCIRuntime::is_oop_handle(oop_handle), "precondition"); + oop* oop_ptr = (oop*) oop_handle; + NativeAccess<>::oop_store(oop_ptr, (oop) nullptr); +C2V_END + C2V_VMENTRY(void, releaseClearedOopHandles, (JNIEnv* env, jobject)) JVMCIENV->runtime()->release_cleared_oop_handles(); C2V_END @@ -3260,6 +3270,7 @@ JNINativeMethod CompilerToVM::methods[] = { {CC "readArrayElement", CC "(" OBJECTCONSTANT "I)Ljava/lang/Object;", FN_PTR(readArrayElement)}, {CC "arrayBaseOffset", CC "(C)I", FN_PTR(arrayBaseOffset)}, {CC "arrayIndexScale", CC "(C)I", FN_PTR(arrayIndexScale)}, + {CC "clearOopHandle", CC "(J)V", FN_PTR(clearOopHandle)}, {CC "releaseClearedOopHandles", CC "()V", FN_PTR(releaseClearedOopHandles)}, {CC "registerNativeMethods", CC "(" CLASS ")[J", FN_PTR(registerNativeMethods)}, {CC "isCurrentThreadAttached", CC "()Z", FN_PTR(isCurrentThreadAttached)}, diff --git a/src/hotspot/share/jvmci/jvmciRuntime.cpp b/src/hotspot/share/jvmci/jvmciRuntime.cpp index afa19ce5850..68177fa8acd 100644 --- a/src/hotspot/share/jvmci/jvmciRuntime.cpp +++ b/src/hotspot/share/jvmci/jvmciRuntime.cpp @@ -887,6 +887,13 @@ jlong JVMCIRuntime::make_oop_handle(const Handle& obj) { return reinterpret_cast(ptr); } +#ifdef ASSERT +bool JVMCIRuntime::is_oop_handle(jlong handle) { + const oop* ptr = (oop*) handle; + return object_handles()->allocation_status(ptr) == OopStorage::ALLOCATED_ENTRY; +} +#endif + int JVMCIRuntime::release_and_clear_oop_handles() { guarantee(_num_attached_threads == cannot_be_attached, "only call during JVMCI runtime shutdown"); int released = release_cleared_oop_handles(); diff --git a/src/hotspot/share/jvmci/jvmciRuntime.hpp b/src/hotspot/share/jvmci/jvmciRuntime.hpp index 1d51f0ee31d..a3f464cd724 100644 --- a/src/hotspot/share/jvmci/jvmciRuntime.hpp +++ b/src/hotspot/share/jvmci/jvmciRuntime.hpp @@ -317,11 +317,12 @@ class JVMCIRuntime: public CHeapObj { // used when creating an IndirectHotSpotObjectConstantImpl in the // shared library JavaVM. jlong make_oop_handle(const Handle& obj); +#ifdef ASSERT + static bool is_oop_handle(jlong handle); +#endif // Releases all the non-null entries in _oop_handles whose referent is null. // Returns the number of handles released by this call. - // The method also resets _last_found_oop_handle_index to -1 - // and _null_oop_handles to 0. int release_cleared_oop_handles(); // Allocation and management of metadata handles. diff --git a/src/hotspot/share/logging/logAsyncWriter.cpp b/src/hotspot/share/logging/logAsyncWriter.cpp index 25292503f3d..3d987d04b8d 100644 --- a/src/hotspot/share/logging/logAsyncWriter.cpp +++ b/src/hotspot/share/logging/logAsyncWriter.cpp @@ -1,5 +1,6 @@ /* * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,13 +49,14 @@ const LogDecorations& AsyncLogWriter::None = LogDecorations(LogLevel::Warning, L LogDecorators::None); bool AsyncLogWriter::Buffer::push_back(LogFileStreamOutput* output, const LogDecorations& decorations, const char* msg) { - const size_t sz = Message::calc_size(strlen(msg)); + const size_t len = strlen(msg); + const size_t sz = Message::calc_size(len); const bool is_token = output == nullptr; // Always leave headroom for the flush token. Pushing a token must succeed. const size_t headroom = (!is_token) ? Message::calc_size(0) : 0; if (_pos + sz <= (_capacity - headroom)) { - new(_buf + _pos) Message(output, decorations, msg); + new(_buf + _pos) Message(output, decorations, msg, len); _pos += sz; return true; } @@ -114,29 +116,7 @@ AsyncLogWriter::AsyncLogWriter() } } -void AsyncLogWriter::write() { - ResourceMark rm; - AsyncLogMap snapshot; - - // lock protection. This guarantees I/O jobs don't block logsites. - { - AsyncLogLocker locker; - - _buffer_staging->reset(); - swap(_buffer, _buffer_staging); - - // move counters to snapshot and reset them. - _stats.iterate([&] (LogFileStreamOutput* output, uint32_t& counter) { - if (counter > 0) { - bool created = snapshot.put(output, counter); - assert(created == true, "sanity check"); - counter = 0; - } - return true; - }); - _data_available = false; - } - +void AsyncLogWriter::write(AsyncLogMap& snapshot) { int req = 0; auto it = _buffer_staging->iterator(); while (it.hasNext()) { @@ -157,7 +137,7 @@ void AsyncLogWriter::write() { if (counter > 0) { stringStream ss; ss.print(UINT32_FORMAT_W(6) " messages dropped due to async logging", counter); - output->write_blocking(decorations, ss.as_string(false)); + output->write_blocking(decorations, ss.freeze()); } return true; }); @@ -170,15 +150,31 @@ void AsyncLogWriter::write() { void AsyncLogWriter::run() { while (true) { + ResourceMark rm; + AsyncLogMap snapshot; { AsyncLogLocker locker; while (!_data_available) { _lock.wait(0/* no timeout */); } + // Only doing a swap and statistics under the lock to + // guarantee that I/O jobs don't block logsites. + _buffer_staging->reset(); + swap(_buffer, _buffer_staging); + + // move counters to snapshot and reset them. + _stats.iterate([&] (LogFileStreamOutput* output, uint32_t& counter) { + if (counter > 0) { + bool created = snapshot.put(output, counter); + assert(created == true, "sanity check"); + counter = 0; + } + return true; + }); + _data_available = false; } - - write(); + write(snapshot); } } diff --git a/src/hotspot/share/logging/logAsyncWriter.hpp b/src/hotspot/share/logging/logAsyncWriter.hpp index 02397f633f7..5015ddfe1a9 100644 --- a/src/hotspot/share/logging/logAsyncWriter.hpp +++ b/src/hotspot/share/logging/logAsyncWriter.hpp @@ -1,5 +1,6 @@ /* * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -66,13 +67,13 @@ class AsyncLogWriter : public NonJavaThread { uint32_t, 17, /*table_size*/ ALLOC_TYPE, mtLogging>; - // Messsage is the envelop of a log line and its associative data. + // Messsage is the envelope of a log line and its associative data. // Its length is variable because of the zero-terminated c-str. It is only valid when we create it using placement new // within a buffer. // // Example layout: // --------------------------------------------- - // |_output|_decorations|"a log line", |pad| <- pointer aligned. + // |_output|_decorations|"a log line", |pad| <- Message aligned. // |_output|_decorations|"yet another",|pad| // ... // |nullptr|_decorations|"",|pad| <- flush token @@ -84,16 +85,16 @@ class AsyncLogWriter : public NonJavaThread { LogFileStreamOutput* const _output; const LogDecorations _decorations; public: - Message(LogFileStreamOutput* output, const LogDecorations& decorations, const char* msg) + // msglen excludes NUL-byte + Message(LogFileStreamOutput* output, const LogDecorations& decorations, const char* msg, const size_t msglen) : _output(output), _decorations(decorations) { assert(msg != nullptr, "c-str message can not be null!"); - PRAGMA_STRINGOP_OVERFLOW_IGNORED - strcpy(reinterpret_cast(this+1), msg); + memcpy(reinterpret_cast(this+1), msg, msglen + 1); } // Calculate the size for a prospective Message object depending on its message length including the trailing zero static constexpr size_t calc_size(size_t message_len) { - return align_up(sizeof(Message) + message_len + 1, sizeof(void*)); + return align_up(sizeof(Message) + message_len + 1, alignof(Message)); } size_t size() const { @@ -114,6 +115,8 @@ class AsyncLogWriter : public NonJavaThread { public: Buffer(size_t capacity) : _pos(0), _capacity(capacity) { _buf = NEW_C_HEAP_ARRAY(char, capacity, mtLogging); + // Ensure _pos is Message-aligned + _pos = align_up(_buf, alignof(Message)) - _buf; assert(capacity >= Message::calc_size(0), "capcity must be great a token size"); } @@ -124,7 +127,10 @@ class AsyncLogWriter : public NonJavaThread { void push_flush_token(); bool push_back(LogFileStreamOutput* output, const LogDecorations& decorations, const char* msg); - void reset() { _pos = 0; } + void reset() { + // Ensure _pos is Message-aligned + _pos = align_up(_buf, alignof(Message)) - _buf; + } class Iterator { const Buffer& _buf; @@ -166,7 +172,7 @@ class AsyncLogWriter : public NonJavaThread { AsyncLogWriter(); void enqueue_locked(LogFileStreamOutput* output, const LogDecorations& decorations, const char* msg); - void write(); + void write(AsyncLogMap& snapshot); void run() override; void pre_run() override { NonJavaThread::pre_run(); diff --git a/src/hotspot/share/memory/arena.cpp b/src/hotspot/share/memory/arena.cpp index ac16279ab71..f767255116c 100644 --- a/src/hotspot/share/memory/arena.cpp +++ b/src/hotspot/share/memory/arena.cpp @@ -24,6 +24,7 @@ */ #include "precompiled.hpp" +#include "compiler/compilationMemoryStatistic.hpp" #include "memory/allocation.hpp" #include "memory/allocation.inline.hpp" #include "memory/arena.hpp" @@ -209,7 +210,7 @@ void Chunk::next_chop(Chunk* k) { k->_next = nullptr; } -Arena::Arena(MEMFLAGS flag, size_t init_size) : _flags(flag), _size_in_bytes(0) { +Arena::Arena(MEMFLAGS flag, Tag tag, size_t init_size) : _flags(flag), _tag(tag), _size_in_bytes(0) { init_size = ARENA_ALIGN(init_size); _chunk = ChunkPool::allocate_chunk(init_size, AllocFailStrategy::EXIT_OOM); _first = _chunk; @@ -219,7 +220,7 @@ Arena::Arena(MEMFLAGS flag, size_t init_size) : _flags(flag), _size_in_bytes(0) set_size_in_bytes(init_size); } -Arena::Arena(MEMFLAGS flag) : _flags(flag), _size_in_bytes(0) { +Arena::Arena(MEMFLAGS flag, Tag tag) : _flags(flag), _tag(tag), _size_in_bytes(0) { _chunk = ChunkPool::allocate_chunk(Chunk::init_size, AllocFailStrategy::EXIT_OOM); _first = _chunk; _hwm = _chunk->bottom(); // Save the cached hwm, max @@ -251,6 +252,12 @@ void Arena::set_size_in_bytes(size_t size) { ssize_t delta = size - size_in_bytes(); _size_in_bytes = size; MemTracker::record_arena_size_change(delta, _flags); + if (CompilationMemoryStatistic::enabled() && _flags == mtCompiler) { + Thread* const t = Thread::current(); + if (t != nullptr && t->is_Compiler_thread()) { + CompilationMemoryStatistic::on_arena_change(delta, this); + } + } } } diff --git a/src/hotspot/share/memory/arena.hpp b/src/hotspot/share/memory/arena.hpp index ad142e189ac..d5af068ffe1 100644 --- a/src/hotspot/share/memory/arena.hpp +++ b/src/hotspot/share/memory/arena.hpp @@ -86,13 +86,22 @@ class Chunk { // Fast allocation of memory class Arena : public CHeapObjBase { +public: + + enum class Tag { + tag_other = 0, + tag_ra, // resource area + tag_ha, // handle area + tag_node // C2 Node arena + }; + protected: friend class HandleMark; friend class NoHandleMark; friend class VMStructs; MEMFLAGS _flags; // Memory tracking flags - + const Tag _tag; Chunk* _first; // First chunk Chunk* _chunk; // current chunk char* _hwm; // High water mark @@ -115,9 +124,8 @@ class Arena : public CHeapObjBase { public: // Start the chunk_pool cleaner task static void start_chunk_pool_cleaner_task(); - - Arena(MEMFLAGS memflag); - Arena(MEMFLAGS memflag, size_t init_size); + Arena(MEMFLAGS memflag, Tag tag = Tag::tag_other); + Arena(MEMFLAGS memflag, Tag tag, size_t init_size); ~Arena(); void destruct_contents(); char* hwm() const { return _hwm; } @@ -171,6 +179,8 @@ class Arena : public CHeapObjBase { size_t size_in_bytes() const { return _size_in_bytes; }; void set_size_in_bytes(size_t size); + Tag get_tag() const { return _tag; } + private: // Reset this Arena to empty, access will trigger grow if necessary void reset(void) { diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp index 7394fbaf459..5c3087f1fa0 100644 --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -569,12 +569,6 @@ void Metaspace::initialize_class_space(ReservedSpace rs) { "wrong alignment"); MetaspaceContext::initialize_class_space_context(rs); - - // This does currently not work because rs may be the result of a split - // operation and NMT seems not to be able to handle splits. - // Will be fixed with JDK-8243535. - // MemTracker::record_virtual_memory_type((address)rs.base(), mtClass); - } // Returns true if class space has been setup (initialize_class_space). @@ -804,6 +798,9 @@ void Metaspace::global_initialize() { CompressedClassSpaceSize)); } + // Mark class space as such + MemTracker::record_virtual_memory_type((address)rs.base(), mtClass); + // Initialize space Metaspace::initialize_class_space(rs); diff --git a/src/hotspot/share/memory/resourceArea.hpp b/src/hotspot/share/memory/resourceArea.hpp index b81209a62d1..7d7692c19ea 100644 --- a/src/hotspot/share/memory/resourceArea.hpp +++ b/src/hotspot/share/memory/resourceArea.hpp @@ -26,6 +26,7 @@ #define SHARE_MEMORY_RESOURCEAREA_HPP #include "memory/allocation.hpp" +#include "memory/arena.hpp" #include "runtime/javaThread.hpp" // The resource area holds temporary data structures in the VM. @@ -51,10 +52,11 @@ class ResourceArea: public Arena { public: ResourceArea(MEMFLAGS flags = mtThread) : - Arena(flags) DEBUG_ONLY(COMMA _nesting(0)) {} + Arena(flags, Arena::Tag::tag_ra) DEBUG_ONLY(COMMA _nesting(0)) {} ResourceArea(size_t init_size, MEMFLAGS flags = mtThread) : - Arena(flags, init_size) DEBUG_ONLY(COMMA _nesting(0)) {} + Arena(flags, Arena::Tag::tag_ra, init_size) DEBUG_ONLY(COMMA _nesting(0)) { + } char* allocate_bytes(size_t size, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM); diff --git a/src/hotspot/share/oops/compiledICHolder.cpp b/src/hotspot/share/oops/compiledICHolder.cpp index c6ca6be2030..8bfa55bcce7 100644 --- a/src/hotspot/share/oops/compiledICHolder.cpp +++ b/src/hotspot/share/oops/compiledICHolder.cpp @@ -32,7 +32,7 @@ volatile int CompiledICHolder::_live_not_claimed_count; #endif CompiledICHolder::CompiledICHolder(Metadata* metadata, Klass* klass, bool is_method) - : _holder_metadata(metadata), _holder_klass(klass), _is_metadata_method(is_method) { + : _holder_metadata(metadata), _holder_klass(klass), _next(nullptr), _is_metadata_method(is_method) { #ifdef ASSERT Atomic::inc(&_live_count); Atomic::inc(&_live_not_claimed_count); diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index e0ec2e34a0a..5e9dfcacafd 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "classfile/vmClasses.hpp" +#include "compiler/compilationMemoryStatistic.hpp" #include "compiler/compilerDefinitions.inline.hpp" #include "runtime/handles.inline.hpp" #include "jfr/support/jfrIntrinsics.hpp" @@ -109,6 +110,8 @@ void C2Compiler::initialize() { void C2Compiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci, bool install_code, DirectiveSet* directive) { assert(is_initialized(), "Compiler thread must be initialized"); + CompilationMemoryStatisticMark cmsm(directive); + bool subsume_loads = SubsumeLoads; bool do_escape_analysis = DoEscapeAnalysis; bool do_iterative_escape_analysis = DoEscapeAnalysis; @@ -118,6 +121,7 @@ void C2Compiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci, boo bool do_superword = UseSuperWord; while (!env->failing()) { + ResourceMark rm; // Attempt to compile while subsuming loads into machine instructions. Options options(subsume_loads, do_escape_analysis, diff --git a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp index c33804181bb..971c37e6b45 100644 --- a/src/hotspot/share/opto/callGenerator.cpp +++ b/src/hotspot/share/opto/callGenerator.cpp @@ -97,6 +97,8 @@ JVMState* ParseGenerator::generate(JVMState* jvms) { } Parse parser(jvms, method(), _expected_uses); + if (C->failing()) return nullptr; + // Grab signature for matching/allocation GraphKit& exits = parser.exits(); diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index 0221fe0c3d0..7bffd75d3f6 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -1883,6 +1883,17 @@ static Node* split_flow_path(PhaseGVN *phase, PhiNode *phi) { return phi; } +// Returns the BasicType of a given convert node and a type, with special handling to ensure that conversions to +// and from half float will return the SHORT basic type, as that wouldn't be returned typically from TypeInt. +static BasicType get_convert_type(Node* convert, const Type* type) { + int convert_op = convert->Opcode(); + if (type->isa_int() && (convert_op == Op_ConvHF2F || convert_op == Op_ConvF2HF)) { + return T_SHORT; + } + + return type->basic_type(); +} + //============================================================================= //------------------------------simple_data_loop_check------------------------- // Try to determining if the phi node in a simple safe/unsafe data loop. @@ -2557,6 +2568,41 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { } #endif + // Try to convert a Phi with two duplicated convert nodes into a phi of the pre-conversion type and the convert node + // proceeding the phi, to de-duplicate the convert node and compact the IR. + if (can_reshape && progress == nullptr) { + ConvertNode* convert = in(1)->isa_Convert(); + if (convert != nullptr) { + int conv_op = convert->Opcode(); + bool ok = true; + + // Check the rest of the inputs + for (uint i = 2; i < req(); i++) { + // Make sure that all inputs are of the same type of convert node + if (in(i)->Opcode() != conv_op) { + ok = false; + break; + } + } + + if (ok) { + // Find the local bottom type to set as the type of the phi + const Type* source_type = Type::get_const_basic_type(convert->in_type()->basic_type()); + const Type* dest_type = convert->bottom_type(); + + PhiNode* newphi = new PhiNode(in(0), source_type, nullptr); + // Set inputs to the new phi be the inputs of the convert + for (uint i = 1; i < req(); i++) { + newphi->init_req(i, in(i)->in(1)); + } + + phase->is_IterGVN()->register_new_node_with_optimizer(newphi, this); + + return ConvertNode::create_convert(get_convert_type(convert, source_type), get_convert_type(convert, dest_type), newphi); + } + } + } + // Phi (VB ... VB) => VB (Phi ...) (Phi ...) if (EnableVectorReboxing && can_reshape && progress == nullptr && type()->isa_oopptr()) { progress = merge_through_phi(this, phase->is_IterGVN()); diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 42729fcd214..1e353a90c97 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -646,8 +646,8 @@ Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci, _unique(0), _dead_node_count(0), _dead_node_list(comp_arena()), - _node_arena_one(mtCompiler), - _node_arena_two(mtCompiler), + _node_arena_one(mtCompiler, Arena::Tag::tag_node), + _node_arena_two(mtCompiler, Arena::Tag::tag_node), _node_arena(&_node_arena_one), _mach_constant_base_node(nullptr), _Compile_types(mtCompiler), @@ -2245,6 +2245,8 @@ void Compile::Optimize() { process_for_unstable_if_traps(igvn); + if (failing()) return; + inline_incrementally(igvn); print_method(PHASE_INCREMENTAL_INLINE, 2); @@ -2255,6 +2257,8 @@ void Compile::Optimize() { // Inline valueOf() methods now. inline_boxing_calls(igvn); + if (failing()) return; + if (AlwaysIncrementalInline) { inline_incrementally(igvn); } @@ -2270,16 +2274,20 @@ void Compile::Optimize() { // CastPP nodes. remove_speculative_types(igvn); + if (failing()) return; + // No more new expensive nodes will be added to the list from here // so keep only the actual candidates for optimizations. cleanup_expensive_nodes(igvn); + if (failing()) return; + assert(EnableVectorSupport || !has_vbox_nodes(), "sanity"); if (EnableVectorSupport && has_vbox_nodes()) { TracePhase tp("", &timers[_t_vector]); PhaseVector pv(igvn); pv.optimize_vector_boxes(); - + if (failing()) return; print_method(PHASE_ITER_GVN_AFTER_VECTOR, 2); } assert(!has_vbox_nodes(), "sanity"); @@ -2299,6 +2307,8 @@ void Compile::Optimize() { // safepoints remove_root_to_sfpts_edges(igvn); + if (failing()) return; + // Perform escape analysis if (do_escape_analysis() && ConnectionGraph::has_candidates(this)) { if (has_loops()) { @@ -2411,6 +2421,8 @@ void Compile::Optimize() { process_for_post_loop_opts_igvn(igvn); + if (failing()) return; + #ifdef ASSERT bs->verify_gc_barriers(this, BarrierSetC2::BeforeMacroExpand); #endif @@ -2449,6 +2461,7 @@ void Compile::Optimize() { // More opportunities to optimize virtual and MH calls. // Though it's maybe too late to perform inlining, strength-reducing them to direct calls is still an option. process_late_inline_calls_no_inline(igvn); + if (failing()) return; } } // (End scope of igvn; run destructor if necessary for asserts.) @@ -4009,8 +4022,6 @@ bool Compile::final_graph_reshaping() { // Recheck with a better notion of 'required_outcnt' if (n->outcnt() != required_outcnt) { - DEBUG_ONLY( n->dump_bfs(1, 0, "-"); ); - assert(false, "malformed control flow"); record_method_not_compilable("malformed control flow"); return true; // Not all targets reachable! } @@ -4927,6 +4938,7 @@ void Compile::remove_speculative_types(PhaseIterGVN &igvn) { igvn.remove_speculative_types(); if (modified > 0) { igvn.optimize(); + if (failing()) return; } #ifdef ASSERT // Verify that after the IGVN is over no speculative type has resurfaced diff --git a/src/hotspot/share/opto/convertnode.cpp b/src/hotspot/share/opto/convertnode.cpp index 122195f108c..0a2131782a2 100644 --- a/src/hotspot/share/opto/convertnode.cpp +++ b/src/hotspot/share/opto/convertnode.cpp @@ -88,6 +88,54 @@ Node* Conv2BNode::Ideal(PhaseGVN* phase, bool can_reshape) { return nullptr; } +uint ConvertNode::ideal_reg() const { + return _type->ideal_reg(); +} + +Node* ConvertNode::create_convert(BasicType source, BasicType target, Node* input) { + if (source == T_INT) { + if (target == T_LONG) { + return new ConvI2LNode(input); + } else if (target == T_FLOAT) { + return new ConvI2FNode(input); + } else if (target == T_DOUBLE) { + return new ConvI2DNode(input); + } + } else if (source == T_LONG) { + if (target == T_INT) { + return new ConvL2INode(input); + } else if (target == T_FLOAT) { + return new ConvL2FNode(input); + } else if (target == T_DOUBLE) { + return new ConvL2DNode(input); + } + } else if (source == T_FLOAT) { + if (target == T_INT) { + return new ConvF2INode(input); + } else if (target == T_LONG) { + return new ConvF2LNode(input); + } else if (target == T_DOUBLE) { + return new ConvF2DNode(input); + } else if (target == T_SHORT) { + return new ConvF2HFNode(input); + } + } else if (source == T_DOUBLE) { + if (target == T_INT) { + return new ConvD2INode(input); + } else if (target == T_LONG) { + return new ConvD2LNode(input); + } else if (target == T_FLOAT) { + return new ConvD2FNode(input); + } + } else if (source == T_SHORT) { + if (target == T_FLOAT) { + return new ConvHF2FNode(input); + } + } + + assert(false, "Couldn't create conversion for type %s to %s", type2name(source), type2name(target)); + return nullptr; +} // The conversions operations are all Alpha sorted. Please keep it that way! //============================================================================= @@ -193,8 +241,9 @@ const Type* ConvF2DNode::Value(PhaseGVN* phase) const { const Type* ConvF2HFNode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if (t == Type::TOP) return Type::TOP; - if (t == Type::FLOAT) return TypeInt::SHORT; - if (StubRoutines::f2hf_adr() == nullptr) return bottom_type(); + if (t == Type::FLOAT || StubRoutines::f2hf_adr() == nullptr) { + return TypeInt::SHORT; + } const TypeF *tf = t->is_float_constant(); return TypeInt::make( StubRoutines::f2hf(tf->getf()) ); @@ -263,14 +312,15 @@ Node *ConvF2LNode::Ideal(PhaseGVN *phase, bool can_reshape) { const Type* ConvHF2FNode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if (t == Type::TOP) return Type::TOP; - if (t == TypeInt::SHORT) return Type::FLOAT; - if (StubRoutines::hf2f_adr() == nullptr) return bottom_type(); + if (t == TypeInt::SHORT || StubRoutines::hf2f_adr() == nullptr) { + return Type::FLOAT; + } const TypeInt *ti = t->is_int(); if (ti->is_con()) { return TypeF::make( StubRoutines::hf2f(ti->get_con()) ); } - return bottom_type(); + return Type::FLOAT; } //============================================================================= @@ -280,7 +330,7 @@ const Type* ConvI2DNode::Value(PhaseGVN* phase) const { if( t == Type::TOP ) return Type::TOP; const TypeInt *ti = t->is_int(); if( ti->is_con() ) return TypeD::make( (double)ti->get_con() ); - return bottom_type(); + return Type::DOUBLE; } //============================================================================= @@ -290,7 +340,7 @@ const Type* ConvI2FNode::Value(PhaseGVN* phase) const { if( t == Type::TOP ) return Type::TOP; const TypeInt *ti = t->is_int(); if( ti->is_con() ) return TypeF::make( (float)ti->get_con() ); - return bottom_type(); + return Type::FLOAT; } //------------------------------Identity--------------------------------------- @@ -710,7 +760,7 @@ const Type* ConvL2DNode::Value(PhaseGVN* phase) const { if( t == Type::TOP ) return Type::TOP; const TypeLong *tl = t->is_long(); if( tl->is_con() ) return TypeD::make( (double)tl->get_con() ); - return bottom_type(); + return Type::DOUBLE; } //============================================================================= @@ -720,7 +770,7 @@ const Type* ConvL2FNode::Value(PhaseGVN* phase) const { if( t == Type::TOP ) return Type::TOP; const TypeLong *tl = t->is_long(); if( tl->is_con() ) return TypeF::make( (float)tl->get_con() ); - return bottom_type(); + return Type::FLOAT; } //============================================================================= diff --git a/src/hotspot/share/opto/convertnode.hpp b/src/hotspot/share/opto/convertnode.hpp index 45277aead8d..cf76f5ab6fd 100644 --- a/src/hotspot/share/opto/convertnode.hpp +++ b/src/hotspot/share/opto/convertnode.hpp @@ -42,193 +42,190 @@ class Conv2BNode : public Node { virtual uint ideal_reg() const { return Op_RegI; } }; +class ConvertNode : public TypeNode { +protected: + ConvertNode(const Type* t, Node* input) : TypeNode(t, 2) { + init_class_id(Class_Convert); + init_req(1, input); + } +public: + virtual const Type* in_type() const = 0; + virtual uint ideal_reg() const; + + // Create a convert node for a given input and output type. + // Conversions to and from half float are specified via T_SHORT. + static Node* create_convert(BasicType source, BasicType target, Node* input); +}; + // The conversions operations are all Alpha sorted. Please keep it that way! //------------------------------ConvD2FNode------------------------------------ // Convert double to float -class ConvD2FNode : public Node { +class ConvD2FNode : public ConvertNode { public: - ConvD2FNode( Node *in1 ) : Node(0,in1) {} + ConvD2FNode(Node* in1) : ConvertNode(Type::FLOAT,in1) {} virtual int Opcode() const; - virtual const Type *bottom_type() const { return Type::FLOAT; } + virtual const Type* in_type() const { return Type::DOUBLE; } virtual const Type* Value(PhaseGVN* phase) const; virtual Node* Identity(PhaseGVN* phase); - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual uint ideal_reg() const { return Op_RegF; } + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); }; //------------------------------ConvD2INode------------------------------------ // Convert Double to Integer -class ConvD2INode : public Node { +class ConvD2INode : public ConvertNode { public: - ConvD2INode( Node *in1 ) : Node(0,in1) {} + ConvD2INode(Node* in1) : ConvertNode(TypeInt::INT,in1) {} virtual int Opcode() const; - virtual const Type *bottom_type() const { return TypeInt::INT; } + virtual const Type* in_type() const { return Type::DOUBLE; } virtual const Type* Value(PhaseGVN* phase) const; virtual Node* Identity(PhaseGVN* phase); - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual uint ideal_reg() const { return Op_RegI; } + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); }; //------------------------------ConvD2LNode------------------------------------ // Convert Double to Long -class ConvD2LNode : public Node { +class ConvD2LNode : public ConvertNode { public: - ConvD2LNode( Node *dbl ) : Node(0,dbl) {} + ConvD2LNode(Node* in1) : ConvertNode(TypeLong::LONG, in1) {} virtual int Opcode() const; - virtual const Type *bottom_type() const { return TypeLong::LONG; } + virtual const Type* in_type() const { return Type::DOUBLE; } virtual const Type* Value(PhaseGVN* phase) const; virtual Node* Identity(PhaseGVN* phase); - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual uint ideal_reg() const { return Op_RegL; } -}; - -class RoundDNode : public Node { - public: - RoundDNode( Node *dbl ) : Node(0,dbl) {} - virtual int Opcode() const; - virtual const Type *bottom_type() const { return TypeLong::LONG; } - virtual uint ideal_reg() const { return Op_RegL; } + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); }; //------------------------------ConvF2DNode------------------------------------ // Convert Float to a Double. -class ConvF2DNode : public Node { +class ConvF2DNode : public ConvertNode { public: - ConvF2DNode( Node *in1 ) : Node(0,in1) {} + ConvF2DNode(Node* in1) : ConvertNode(Type::DOUBLE,in1) {} virtual int Opcode() const; - virtual const Type *bottom_type() const { return Type::DOUBLE; } + virtual const Type* in_type() const { return Type::FLOAT; } virtual const Type* Value(PhaseGVN* phase) const; - virtual uint ideal_reg() const { return Op_RegD; } }; //------------------------------ConvF2HFNode------------------------------------ // Convert Float to Halffloat -class ConvF2HFNode : public Node { +class ConvF2HFNode : public ConvertNode { public: - ConvF2HFNode( Node *in1 ) : Node(0,in1) {} + ConvF2HFNode(Node* in1) : ConvertNode(TypeInt::SHORT, in1) {} virtual int Opcode() const; - virtual const Type *bottom_type() const { return TypeInt::SHORT; } + virtual const Type* in_type() const { return TypeInt::FLOAT; } virtual const Type* Value(PhaseGVN* phase) const; - virtual uint ideal_reg() const { return Op_RegI; } }; //------------------------------ConvF2INode------------------------------------ // Convert float to integer -class ConvF2INode : public Node { - public: - ConvF2INode( Node *in1 ) : Node(0,in1) {} +class ConvF2INode : public ConvertNode { +public: + ConvF2INode(Node* in1) : ConvertNode(TypeInt::INT, in1) {} virtual int Opcode() const; - virtual const Type *bottom_type() const { return TypeInt::INT; } + virtual const Type* in_type() const { return TypeInt::FLOAT; } virtual const Type* Value(PhaseGVN* phase) const; virtual Node* Identity(PhaseGVN* phase); - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual uint ideal_reg() const { return Op_RegI; } + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); }; - //------------------------------ConvF2LNode------------------------------------ // Convert float to long -class ConvF2LNode : public Node { - public: - ConvF2LNode( Node *in1 ) : Node(0,in1) {} +class ConvF2LNode : public ConvertNode { +public: + ConvF2LNode(Node* in1) : ConvertNode(TypeLong::LONG, in1) {} virtual int Opcode() const; - virtual const Type *bottom_type() const { return TypeLong::LONG; } + virtual const Type* in_type() const { return TypeInt::FLOAT; } virtual const Type* Value(PhaseGVN* phase) const; virtual Node* Identity(PhaseGVN* phase); - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual uint ideal_reg() const { return Op_RegL; } + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); }; //------------------------------ConvHF2FNode------------------------------------ // Convert Halffloat to float -class ConvHF2FNode : public Node { - public: - ConvHF2FNode( Node *in1 ) : Node(0,in1) {} +class ConvHF2FNode : public ConvertNode { +public: + ConvHF2FNode(Node* in1) : ConvertNode(Type::FLOAT, in1) {} virtual int Opcode() const; - virtual const Type *bottom_type() const { return Type::FLOAT; } + virtual const Type* in_type() const { return TypeInt::SHORT; } virtual const Type* Value(PhaseGVN* phase) const; - virtual uint ideal_reg() const { return Op_RegF; } }; //------------------------------ConvI2DNode------------------------------------ // Convert Integer to Double -class ConvI2DNode : public Node { - public: - ConvI2DNode( Node *in1 ) : Node(0,in1) {} +class ConvI2DNode : public ConvertNode { +public: + ConvI2DNode(Node* in1) : ConvertNode(Type::DOUBLE, in1) {} virtual int Opcode() const; - virtual const Type *bottom_type() const { return Type::DOUBLE; } + virtual const Type* in_type() const { return TypeInt::INT; } virtual const Type* Value(PhaseGVN* phase) const; - virtual uint ideal_reg() const { return Op_RegD; } }; //------------------------------ConvI2FNode------------------------------------ // Convert Integer to Float -class ConvI2FNode : public Node { - public: - ConvI2FNode( Node *in1 ) : Node(0,in1) {} +class ConvI2FNode : public ConvertNode { +public: + ConvI2FNode(Node* in1) : ConvertNode(Type::FLOAT, in1) {} virtual int Opcode() const; - virtual const Type *bottom_type() const { return Type::FLOAT; } + virtual const Type* in_type() const { return TypeInt::INT; } virtual const Type* Value(PhaseGVN* phase) const; virtual Node* Identity(PhaseGVN* phase); - virtual uint ideal_reg() const { return Op_RegF; } -}; - -class RoundFNode : public Node { - public: - RoundFNode( Node *in1 ) : Node(0,in1) {} - virtual int Opcode() const; - virtual const Type *bottom_type() const { return TypeInt::INT; } - virtual uint ideal_reg() const { return Op_RegI; } }; //------------------------------ConvI2LNode------------------------------------ // Convert integer to long -class ConvI2LNode : public TypeNode { +class ConvI2LNode : public ConvertNode { public: - ConvI2LNode(Node *in1, const TypeLong* t = TypeLong::INT) - : TypeNode(t, 2) - { init_req(1, in1); } + ConvI2LNode(Node* in1, const TypeLong* t = TypeLong::INT) : ConvertNode(t, in1) {} virtual int Opcode() const; + virtual const Type* in_type() const { return TypeInt::INT; } virtual const Type* Value(PhaseGVN* phase) const; - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); virtual Node* Identity(PhaseGVN* phase); - virtual uint ideal_reg() const { return Op_RegL; } }; //------------------------------ConvL2DNode------------------------------------ // Convert Long to Double -class ConvL2DNode : public Node { - public: - ConvL2DNode( Node *in1 ) : Node(0,in1) {} +class ConvL2DNode : public ConvertNode { +public: + ConvL2DNode(Node* in1) : ConvertNode(Type::DOUBLE, in1) {} virtual int Opcode() const; - virtual const Type *bottom_type() const { return Type::DOUBLE; } + virtual const Type* in_type() const { return TypeLong::LONG; } virtual const Type* Value(PhaseGVN* phase) const; - virtual uint ideal_reg() const { return Op_RegD; } }; //------------------------------ConvL2FNode------------------------------------ // Convert Long to Float -class ConvL2FNode : public Node { - public: - ConvL2FNode( Node *in1 ) : Node(0,in1) {} +class ConvL2FNode : public ConvertNode { +public: + ConvL2FNode(Node* in1) : ConvertNode(Type::FLOAT, in1) {} virtual int Opcode() const; - virtual const Type *bottom_type() const { return Type::FLOAT; } + virtual const Type* in_type() const { return TypeLong::LONG; } virtual const Type* Value(PhaseGVN* phase) const; - virtual uint ideal_reg() const { return Op_RegF; } }; //------------------------------ConvL2INode------------------------------------ // Convert long to integer -class ConvL2INode : public TypeNode { +class ConvL2INode : public ConvertNode { public: - ConvL2INode(Node *in1, const TypeInt* t = TypeInt::INT) - : TypeNode(t, 2) { - init_req(1, in1); - } + ConvL2INode(Node* in1, const TypeInt* t = TypeInt::INT) : ConvertNode(t, in1) {} virtual int Opcode() const; + virtual const Type* in_type() const { return TypeLong::LONG; } virtual Node* Identity(PhaseGVN* phase); virtual const Type* Value(PhaseGVN* phase) const; - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); +}; + +class RoundDNode : public Node { +public: + RoundDNode(Node* in1) : Node(nullptr, in1) {} + virtual int Opcode() const; + virtual const Type* bottom_type() const { return TypeLong::LONG; } + virtual uint ideal_reg() const { return Op_RegL; } +}; + +class RoundFNode : public Node { +public: + RoundFNode(Node* in1) : Node(nullptr, in1) {} + virtual int Opcode() const; + virtual const Type* bottom_type() const { return TypeInt::INT; } virtual uint ideal_reg() const { return Op_RegI; } }; diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index b711a5780cf..92180371c06 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -82,7 +82,8 @@ class GraphKit : public Phase { #ifdef ASSERT ~GraphKit() { - assert(!has_exceptions(), "user must call transfer_exceptions_into_jvms"); + assert(failing() || !has_exceptions(), + "unless compilation failed, user must call transfer_exceptions_into_jvms"); } #endif diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 03765c8cca0..847d9dc37dd 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -4692,6 +4692,7 @@ void PhaseIdealLoop::verify() const { bool success = true; PhaseIdealLoop phase_verify(_igvn, this); + if (C->failing()) return; // Verify ctrl and idom of every node. success &= verify_idom_and_nodes(C->root(), &phase_verify); diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index d1b2951761d..1804f1c7702 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -1194,7 +1194,7 @@ class PhaseIdealLoop : public PhaseTransform { if (!C->failing()) { // Cleanup any modified bits igvn.optimize(); - + if (C->failing()) { return; } v.log_loop_tree(); } } diff --git a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp index 37c9ec6ed13..b527a7657b2 100644 --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -356,7 +356,9 @@ void Matcher::match( ) { // Recursively match trees from old space into new space. // Correct leaves of new-space Nodes; they point to old-space. _visited.clear(); - C->set_cached_top_node(xform( C->top(), live_nodes )); + Node* const n = xform(C->top(), live_nodes); + if (C->failing()) return; + C->set_cached_top_node(n); if (!C->failing()) { Node* xroot = xform( C->root(), 1 ); if (xroot == nullptr) { diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index c94c439430b..1866ef87def 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -70,6 +70,7 @@ class CodeBuffer; class ConstraintCastNode; class ConNode; class ConINode; +class ConvertNode; class CompareAndSwapNode; class CompareAndExchangeNode; class CountedLoopNode; @@ -731,6 +732,7 @@ class Node { DEFINE_CLASS_ID(Con, Type, 8) DEFINE_CLASS_ID(ConI, Con, 0) DEFINE_CLASS_ID(SafePointScalarMerge, Type, 9) + DEFINE_CLASS_ID(Convert, Type, 10) DEFINE_CLASS_ID(Proj, Node, 3) @@ -889,6 +891,7 @@ class Node { DEFINE_CLASS_QUERY(ClearArray) DEFINE_CLASS_QUERY(CMove) DEFINE_CLASS_QUERY(Cmp) + DEFINE_CLASS_QUERY(Convert) DEFINE_CLASS_QUERY(CountedLoop) DEFINE_CLASS_QUERY(CountedLoopEnd) DEFINE_CLASS_QUERY(DecodeNarrowPtr) diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp index 36a87cd8b0d..0b867c69ebc 100644 --- a/src/hotspot/share/opto/parse1.cpp +++ b/src/hotspot/share/opto/parse1.cpp @@ -1575,6 +1575,7 @@ void Parse::do_one_block() { #endif //ASSERT do_one_bytecode(); + if (failing()) return; assert(!have_se || stopped() || failing() || (sp() - pre_bc_sp) == depth, "incorrect depth prediction: sp=%d, pre_bc_sp=%d, depth=%d", sp(), pre_bc_sp, depth); diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index c9cedb99a45..e86217f8de6 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -728,7 +728,7 @@ WB_ENTRY(jint, WB_NMTGetHashSize(JNIEnv* env, jobject o)) WB_END WB_ENTRY(jlong, WB_NMTNewArena(JNIEnv* env, jobject o, jlong init_size)) - Arena* arena = new (mtTest) Arena(mtTest, size_t(init_size)); + Arena* arena = new (mtTest) Arena(mtTest, Arena::Tag::tag_other, size_t(init_size)); return (jlong)arena; WB_END diff --git a/src/hotspot/share/runtime/basicLock.cpp b/src/hotspot/share/runtime/basicLock.cpp index 40ba712bf6c..b6cb4b075be 100644 --- a/src/hotspot/share/runtime/basicLock.cpp +++ b/src/hotspot/share/runtime/basicLock.cpp @@ -66,19 +66,26 @@ void BasicLock::move_to(oop obj, BasicLock* dest) { // is small (given the support for inflated fast-path locking in the fast_lock, etc) // we'll leave that optimization for another time. - if (displaced_header().is_neutral()) { - // The object is locked and the resulting ObjectMonitor* will also be - // locked so it can't be async deflated until ownership is dropped. - ObjectSynchronizer::inflate_helper(obj); - // WARNING: We cannot put a check here, because the inflation - // will not update the displaced header. Once BasicLock is inflated, - // no one should ever look at its content. - } else { - // Typically the displaced header will be 0 (recursive stack lock) or - // unused_mark. Naively we'd like to assert that the displaced mark - // value is either 0, neutral, or 3. But with the advent of the - // store-before-CAS avoidance in fast_lock/compiler_lock_object - // we can find any flavor mark in the displaced mark. + if (LockingMode == LM_LEGACY) { + if (displaced_header().is_neutral()) { + // The object is locked and the resulting ObjectMonitor* will also be + // locked so it can't be async deflated until ownership is dropped. + ObjectSynchronizer::inflate_helper(obj); + // WARNING: We cannot put a check here, because the inflation + // will not update the displaced header. Once BasicLock is inflated, + // no one should ever look at its content. + } else { + // Typically the displaced header will be 0 (recursive stack lock) or + // unused_mark. Naively we'd like to assert that the displaced mark + // value is either 0, neutral, or 3. But with the advent of the + // store-before-CAS avoidance in fast_lock/compiler_lock_object + // we can find any flavor mark in the displaced mark. + } + dest->set_displaced_header(displaced_header()); } - dest->set_displaced_header(displaced_header()); +#ifdef ASSERT + else { + dest->set_displaced_header(markWord(badDispHeaderDeopt)); + } +#endif } diff --git a/src/hotspot/share/runtime/handles.hpp b/src/hotspot/share/runtime/handles.hpp index b57be73d6b3..7865d32bef2 100644 --- a/src/hotspot/share/runtime/handles.hpp +++ b/src/hotspot/share/runtime/handles.hpp @@ -187,7 +187,7 @@ class HandleArea: public Arena { HandleArea* _prev; // link to outer (older) area public: // Constructor - HandleArea(HandleArea* prev) : Arena(mtThread, Chunk::tiny_size) { + HandleArea(HandleArea* prev) : Arena(mtThread, Tag::tag_ha, Chunk::tiny_size) { debug_only(_handle_mark_nesting = 0); debug_only(_no_handle_mark_nesting = 0); _prev = prev; diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp index 6f227988748..d075b6cb9c8 100644 --- a/src/hotspot/share/runtime/java.cpp +++ b/src/hotspot/share/runtime/java.cpp @@ -31,6 +31,7 @@ #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" #include "code/codeCache.hpp" +#include "compiler/compilationMemoryStatistic.hpp" #include "compiler/compileBroker.hpp" #include "compiler/compilerOracle.hpp" #include "gc/shared/collectedHeap.hpp" @@ -341,6 +342,10 @@ void print_statistics() { MetaspaceUtils::print_basic_report(tty, 0); } + if (CompilerOracle::should_print_final_memstat_report()) { + CompilationMemoryStatistic::print_all_by_size(tty, false, 0); + } + ThreadsSMRSupport::log_statistics(); } diff --git a/src/hotspot/share/runtime/lockStack.inline.hpp b/src/hotspot/share/runtime/lockStack.inline.hpp index 186c7169fae..9dd04d8f7fe 100644 --- a/src/hotspot/share/runtime/lockStack.inline.hpp +++ b/src/hotspot/share/runtime/lockStack.inline.hpp @@ -47,10 +47,14 @@ inline bool LockStack::can_push() const { } inline bool LockStack::is_owning_thread() const { - JavaThread* thread = JavaThread::current(); - bool is_owning = &thread->lock_stack() == this; - assert(is_owning == (get_thread() == thread), "is_owning sanity"); - return is_owning; + Thread* current = Thread::current(); + if (current->is_Java_thread()) { + JavaThread* thread = JavaThread::cast(current); + bool is_owning = &thread->lock_stack() == this; + assert(is_owning == (get_thread() == thread), "is_owning sanity"); + return is_owning; + } + return false; } inline void LockStack::push(oop o) { diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index 5a89df9b8b9..cdbeea9a269 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -139,6 +139,7 @@ Mutex* ThreadIdTableCreate_lock = nullptr; Mutex* SharedDecoder_lock = nullptr; Mutex* DCmdFactory_lock = nullptr; Mutex* NMTQuery_lock = nullptr; +Mutex* NMTCompilationCostHistory_lock = nullptr; #if INCLUDE_CDS #if INCLUDE_JVMTI @@ -309,6 +310,7 @@ void mutex_init() { MUTEX_DEFN(SharedDecoder_lock , PaddedMutex , tty-1); MUTEX_DEFN(DCmdFactory_lock , PaddedMutex , nosafepoint); MUTEX_DEFN(NMTQuery_lock , PaddedMutex , safepoint); + MUTEX_DEFN(NMTCompilationCostHistory_lock , PaddedMutex , nosafepoint); #if INCLUDE_CDS #if INCLUDE_JVMTI MUTEX_DEFN(CDSClassFileStream_lock , PaddedMutex , safepoint); diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp index a3088267afa..3bce342c251 100644 --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -117,6 +117,7 @@ extern Mutex* ThreadIdTableCreate_lock; // Used by ThreadIdTable to laz extern Mutex* SharedDecoder_lock; // serializes access to the decoder during normal (not error reporting) use extern Mutex* DCmdFactory_lock; // serialize access to DCmdFactory information extern Mutex* NMTQuery_lock; // serialize NMT Dcmd queries +extern Mutex* NMTCompilationCostHistory_lock; // guards NMT compilation cost history #if INCLUDE_CDS #if INCLUDE_JVMTI extern Mutex* CDSClassFileStream_lock; // FileMapInfo::open_stream_for_jvmti diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 0920190d679..31a38443672 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -3257,16 +3257,24 @@ JRT_LEAF(intptr_t*, SharedRuntime::OSR_migration_begin( JavaThread *current) ) kptr2 = fr.next_monitor_in_interpreter_frame(kptr2) ) { if (kptr2->obj() != nullptr) { // Avoid 'holes' in the monitor array BasicLock *lock = kptr2->lock(); - // Inflate so the object's header no longer refers to the BasicLock. - if (lock->displaced_header().is_unlocked()) { - // The object is locked and the resulting ObjectMonitor* will also be - // locked so it can't be async deflated until ownership is dropped. - // See the big comment in basicLock.cpp: BasicLock::move_to(). - ObjectSynchronizer::inflate_helper(kptr2->obj()); + if (LockingMode == LM_LEGACY) { + // Inflate so the object's header no longer refers to the BasicLock. + if (lock->displaced_header().is_unlocked()) { + // The object is locked and the resulting ObjectMonitor* will also be + // locked so it can't be async deflated until ownership is dropped. + // See the big comment in basicLock.cpp: BasicLock::move_to(). + ObjectSynchronizer::inflate_helper(kptr2->obj()); + } + // Now the displaced header is free to move because the + // object's header no longer refers to it. + buf[i] = (intptr_t)lock->displaced_header().value(); } - // Now the displaced header is free to move because the - // object's header no longer refers to it. - buf[i++] = (intptr_t)lock->displaced_header().value(); +#ifdef ASSERT + else { + buf[i] = badDispHeaderOSR; + } +#endif + i++; buf[i++] = cast_from_oop(kptr2->obj()); } } diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index 42885e935f7..cc431e8c900 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -43,6 +43,7 @@ #include "jfr/support/jfrThreadExtension.hpp" #endif +class CompilerThread; class HandleArea; class HandleMark; class ICRefillVerifier; @@ -324,6 +325,12 @@ class Thread: public ThreadShadow { virtual bool is_AttachListener_thread() const { return false; } virtual bool is_monitor_deflation_thread() const { return false; } + // Convenience cast functions + CompilerThread* as_Compiler_thread() const { + assert(is_Compiler_thread(), "Must be compiler thread"); + return (CompilerThread*)this; + } + // Can this thread make Java upcalls virtual bool can_call_java() const { return false; } diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index a5177f9d309..2c61cd28ed8 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -1493,6 +1493,7 @@ declare_c2_type(CastPPNode, ConstraintCastNode) \ declare_c2_type(CheckCastPPNode, TypeNode) \ declare_c2_type(Conv2BNode, Node) \ + declare_c2_type(ConvertNode, TypeNode) \ declare_c2_type(ConvD2FNode, Node) \ declare_c2_type(ConvD2INode, Node) \ declare_c2_type(ConvD2LNode, Node) \ diff --git a/src/hotspot/share/services/diagnosticCommand.cpp b/src/hotspot/share/services/diagnosticCommand.cpp index 8d346a5ea48..a54f2ebaa70 100644 --- a/src/hotspot/share/services/diagnosticCommand.cpp +++ b/src/hotspot/share/services/diagnosticCommand.cpp @@ -31,6 +31,8 @@ #include "classfile/systemDictionary.hpp" #include "classfile/vmClasses.hpp" #include "code/codeCache.hpp" +#include "compiler/compilationMemoryStatistic.hpp" +#include "compiler/compiler_globals.hpp" #include "compiler/compileBroker.hpp" #include "compiler/directivesParser.hpp" #include "gc/shared/gcVMOperations.hpp" @@ -138,6 +140,7 @@ void DCmd::register_dcmds(){ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); // Enhanced JMX Agent Support // These commands won't be exported via the DiagnosticCommandMBean until an @@ -1134,3 +1137,17 @@ void ThreadDumpToFileDCmd::dumpToFile(Symbol* name, Symbol* signature, const cha jbyte* addr = typeArrayOop(res)->byte_at_addr(0); output()->print_raw((const char*)addr, ba->length()); } + +CompilationMemoryStatisticDCmd::CompilationMemoryStatisticDCmd(outputStream* output, bool heap) : + DCmdWithParser(output, heap), + _human_readable("-H", "Human readable format", "BOOLEAN", false, "false"), + _minsize("-s", "Minimum memory size", "MEMORY SIZE", false, "0") { + _dcmdparser.add_dcmd_option(&_human_readable); + _dcmdparser.add_dcmd_option(&_minsize); +} + +void CompilationMemoryStatisticDCmd::execute(DCmdSource source, TRAPS) { + const bool human_readable = _human_readable.value(); + const size_t minsize = _minsize.has_value() ? _minsize.value()._size : 0; + CompilationMemoryStatistic::print_all_by_size(output(), human_readable, minsize); +} diff --git a/src/hotspot/share/services/diagnosticCommand.hpp b/src/hotspot/share/services/diagnosticCommand.hpp index 685223ac038..06b7e8748dc 100644 --- a/src/hotspot/share/services/diagnosticCommand.hpp +++ b/src/hotspot/share/services/diagnosticCommand.hpp @@ -954,4 +954,28 @@ class ThreadDumpToFileDCmd : public DCmdWithParser { virtual void execute(DCmdSource source, TRAPS); }; +class CompilationMemoryStatisticDCmd: public DCmdWithParser { +protected: + DCmdArgument _human_readable; + DCmdArgument _minsize; +public: + static int num_arguments() { return 2; } + CompilationMemoryStatisticDCmd(outputStream* output, bool heap); + static const char* name() { + return "Compiler.memory"; + } + static const char* description() { + return "Print compilation footprint"; + } + static const char* impact() { + return "Medium: Pause time depends on number of compiled methods"; + } + static const JavaPermission permission() { + JavaPermission p = {"java.lang.management.ManagementPermission", + "monitor", nullptr}; + return p; + } + virtual void execute(DCmdSource source, TRAPS); +}; + #endif // SHARE_SERVICES_DIAGNOSTICCOMMAND_HPP diff --git a/src/hotspot/share/services/mallocTracker.cpp b/src/hotspot/share/services/mallocTracker.cpp index bddfcfffc08..0793ca6f4e9 100644 --- a/src/hotspot/share/services/mallocTracker.cpp +++ b/src/hotspot/share/services/mallocTracker.cpp @@ -45,7 +45,6 @@ size_t MallocMemorySummary::_snapshot[CALC_OBJ_SIZE_IN_TYPE(MallocMemorySnapshot, size_t)]; -#ifdef ASSERT void MemoryCounter::update_peak(size_t size, size_t cnt) { size_t peak_sz = peak_size(); while (peak_sz < size) { @@ -59,7 +58,6 @@ void MemoryCounter::update_peak(size_t size, size_t cnt) { } } } -#endif // ASSERT // Total malloc'd memory used by arenas size_t MallocMemorySnapshot::total_arena() const { diff --git a/src/hotspot/share/services/mallocTracker.hpp b/src/hotspot/share/services/mallocTracker.hpp index 4088abd7005..cf9a623c0bd 100644 --- a/src/hotspot/share/services/mallocTracker.hpp +++ b/src/hotspot/share/services/mallocTracker.hpp @@ -46,25 +46,20 @@ class MemoryCounter { volatile size_t _count; volatile size_t _size; -#ifdef ASSERT // Peak size and count. Note: Peak count is the count at the point // peak size was reached, not the absolute highest peak count. volatile size_t _peak_count; volatile size_t _peak_size; void update_peak(size_t size, size_t cnt); -#endif // ASSERT public: - MemoryCounter() : _count(0), _size(0) { - DEBUG_ONLY(_peak_count = 0;) - DEBUG_ONLY(_peak_size = 0;) - } + MemoryCounter() : _count(0), _size(0), _peak_count(0), _peak_size(0) {} inline void allocate(size_t sz) { size_t cnt = Atomic::add(&_count, size_t(1), memory_order_relaxed); if (sz > 0) { size_t sum = Atomic::add(&_size, sz, memory_order_relaxed); - DEBUG_ONLY(update_peak(sum, cnt);) + update_peak(sum, cnt); } } @@ -81,7 +76,7 @@ class MemoryCounter { if (sz != 0) { assert(sz >= 0 || size() >= size_t(-sz), "Must be"); size_t sum = Atomic::add(&_size, size_t(sz), memory_order_relaxed); - DEBUG_ONLY(update_peak(sum, _count);) + update_peak(sum, _count); } } @@ -89,11 +84,11 @@ class MemoryCounter { inline size_t size() const { return Atomic::load(&_size); } inline size_t peak_count() const { - return DEBUG_ONLY(Atomic::load(&_peak_count)) NOT_DEBUG(0); + return Atomic::load(&_peak_count); } inline size_t peak_size() const { - return DEBUG_ONLY(Atomic::load(&_peak_size)) NOT_DEBUG(0); + return Atomic::load(&_peak_size); } }; @@ -181,11 +176,6 @@ class MallocMemorySnapshot : public ResourceObj { // Total malloc'd memory used by arenas size_t total_arena() const; - inline size_t thread_count() const { - MallocMemorySnapshot* s = const_cast(this); - return s->by_type(mtThreadStack)->malloc_count(); - } - void copy_to(MallocMemorySnapshot* s) { // Need to make sure that mtChunks don't get deallocated while the // copy is going on, because their size is adjusted using this diff --git a/src/hotspot/share/services/memBaseline.cpp b/src/hotspot/share/services/memBaseline.cpp index 4167e43f6b1..df9c4d85aa8 100644 --- a/src/hotspot/share/services/memBaseline.cpp +++ b/src/hotspot/share/services/memBaseline.cpp @@ -190,6 +190,7 @@ void MemBaseline::baseline(bool summaryOnly) { _instance_class_count = ClassLoaderDataGraph::num_instance_classes(); _array_class_count = ClassLoaderDataGraph::num_array_classes(); + _thread_count = ThreadStackTracker::thread_count(); baseline_summary(); _baseline_type = Summary_baselined; diff --git a/src/hotspot/share/services/memBaseline.hpp b/src/hotspot/share/services/memBaseline.hpp index 5f2442d3710..fa44a454704 100644 --- a/src/hotspot/share/services/memBaseline.hpp +++ b/src/hotspot/share/services/memBaseline.hpp @@ -64,6 +64,7 @@ class MemBaseline { size_t _instance_class_count; size_t _array_class_count; + size_t _thread_count; // Allocation sites information // Malloc allocation sites @@ -84,7 +85,7 @@ class MemBaseline { public: // create a memory baseline MemBaseline(): - _instance_class_count(0), _array_class_count(0), + _instance_class_count(0), _array_class_count(0), _thread_count(0), _baseline_type(Not_baselined) { } @@ -171,7 +172,7 @@ class MemBaseline { size_t thread_count() const { assert(baseline_type() != Not_baselined, "Not yet baselined"); - return _malloc_memory_snapshot.thread_count(); + return _thread_count; } // reset the baseline for reuse @@ -180,6 +181,7 @@ class MemBaseline { // _malloc_memory_snapshot and _virtual_memory_snapshot are copied over. _instance_class_count = 0; _array_class_count = 0; + _thread_count = 0; _malloc_sites.clear(); _virtual_memory_sites.clear(); diff --git a/src/hotspot/share/services/memReporter.cpp b/src/hotspot/share/services/memReporter.cpp index 3e2927a8bad..a277fec5c80 100644 --- a/src/hotspot/share/services/memReporter.cpp +++ b/src/hotspot/share/services/memReporter.cpp @@ -246,7 +246,7 @@ void MemSummaryReporter::report_summary_of_type(MEMFLAGS flag, // report malloc'd memory if (amount_in_current_scale(malloc_memory->malloc_size()) > 0 - DEBUG_ONLY(|| amount_in_current_scale(malloc_memory->malloc_peak_size()) > 0)) { + || amount_in_current_scale(malloc_memory->malloc_peak_size()) > 0) { print_malloc_line(malloc_memory->malloc_counter()); } diff --git a/src/hotspot/share/services/threadStackTracker.cpp b/src/hotspot/share/services/threadStackTracker.cpp index 14616e10469..decacc64c5d 100644 --- a/src/hotspot/share/services/threadStackTracker.cpp +++ b/src/hotspot/share/services/threadStackTracker.cpp @@ -49,40 +49,38 @@ int ThreadStackTracker::compare_thread_stack_base(const SimpleThreadStackSite& s void ThreadStackTracker::new_thread_stack(void* base, size_t size, const NativeCallStack& stack) { assert(MemTracker::tracking_level() >= NMT_summary, "Must be"); assert(base != nullptr, "Should have been filtered"); + ThreadCritical tc; if (track_as_vm()) { - ThreadCritical tc; VirtualMemoryTracker::add_reserved_region((address)base, size, stack, mtThreadStack); - _thread_count ++; } else { // Use a slot in mallocMemorySummary for thread stack bookkeeping MallocMemorySummary::record_malloc(size, mtThreadStack); if (MemTracker::tracking_level() == NMT_detail) { - ThreadCritical tc; assert(_simple_thread_stacks != nullptr, "Must be initialized"); SimpleThreadStackSite site((address)base, size, stack); _simple_thread_stacks->add(site); } } + _thread_count++; } void ThreadStackTracker::delete_thread_stack(void* base, size_t size) { assert(MemTracker::tracking_level() >= NMT_summary, "Must be"); assert(base != nullptr, "Should have been filtered"); + ThreadCritical tc; if(track_as_vm()) { - ThreadCritical tc; VirtualMemoryTracker::remove_released_region((address)base, size); - _thread_count--; } else { // Use a slot in mallocMemorySummary for thread stack bookkeeping MallocMemorySummary::record_free(size, mtThreadStack); if (MemTracker::tracking_level() == NMT_detail) { - ThreadCritical tc; assert(_simple_thread_stacks != nullptr, "Must be initialized"); SimpleThreadStackSite site((address)base, size, NativeCallStack::empty_stack()); // Fake object just to serve as compare target for delete bool removed = _simple_thread_stacks->remove(site); assert(removed, "Must exist"); } } + _thread_count--; } bool ThreadStackTracker::walk_simple_thread_stack_site(MallocSiteWalker* walker) { diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 2bdde4ef375..64d12de094c 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -1036,7 +1036,8 @@ const juint badHeapWordVal = 0xBAADBABE; // value used to zap const juint badMetaWordVal = 0xBAADFADE; // value used to zap metadata heap after GC const int badCodeHeapNewVal= 0xCC; // value used to zap Code heap at allocation const int badCodeHeapFreeVal = 0xDD; // value used to zap Code heap at deallocation - +const intptr_t badDispHeaderDeopt = 0xDE0BD000; // value to fill unused displaced header during deoptimization +const intptr_t badDispHeaderOSR = 0xDEAD05A0; // value to fill unused displaced header during OSR // (These must be implemented as #defines because C++ compilers are // not obligated to inline non-integral constants!) diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index 355483b4efe..df360335cc9 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -431,7 +431,7 @@ static frame next_frame(frame fr, Thread* t) { if (!t->is_in_full_stack((address)(fr.real_fp() + 1))) { return invalid; } - if (fr.is_java_frame() || fr.is_native_frame() || fr.is_runtime_frame()) { + if (fr.is_interpreted_frame() || (fr.cb() != nullptr && fr.cb()->frame_size() > 0)) { RegisterMap map(JavaThread::cast(t), RegisterMap::UpdateMap::skip, RegisterMap::ProcessFrames::include, diff --git a/src/java.base/aix/native/libjava/ProcessHandleImpl_aix.c b/src/java.base/aix/native/libjava/ProcessHandleImpl_aix.c index 93347bd8c20..3568af24ce4 100644 --- a/src/java.base/aix/native/libjava/ProcessHandleImpl_aix.c +++ b/src/java.base/aix/native/libjava/ProcessHandleImpl_aix.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,10 +24,12 @@ */ #include "jni.h" +#include "jni_util.h" #include "ProcessHandleImpl_unix.h" #include +#include /* * Implementation of native ProcessHandleImpl functions for AIX. @@ -36,9 +38,127 @@ void os_initNative(JNIEnv *env, jclass clazz) {} +/* + * Return pids of active processes, and optionally parent pids and + * start times for each process. + * For a specific non-zero pid, only the direct children are returned. + * If the pid is zero, all active processes are returned. + * Use getprocs64 to accumulate any process following the rules above. + * The resulting pids are stored into an array of longs named jarray. + * The number of pids is returned if they all fit. + * If the parentArray is non-null, store also the parent pid. + * In this case the parentArray must have the same length as the result pid array. + * Of course in the case of a given non-zero pid all entries in the parentArray + * will contain this pid, so this array does only make sense in the case of a given + * zero pid. + * If the jstimesArray is non-null, store also the start time of the pid. + * In this case the jstimesArray must have the same length as the result pid array. + * If the array(s) (is|are) too short, excess pids are not stored and + * the desired length is returned. + */ jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray, jlongArray jparentArray, jlongArray jstimesArray) { - return unix_getChildren(env, jpid, jarray, jparentArray, jstimesArray); + pid_t pid = (pid_t) jpid; + jlong* pids = NULL; + jlong* ppids = NULL; + jlong* stimes = NULL; + jsize parentArraySize = 0; + jsize arraySize = 0; + jsize stimesSize = 0; + jsize count = 0; + + arraySize = (*env)->GetArrayLength(env, jarray); + JNU_CHECK_EXCEPTION_RETURN(env, -1); + if (jparentArray != NULL) { + parentArraySize = (*env)->GetArrayLength(env, jparentArray); + JNU_CHECK_EXCEPTION_RETURN(env, -1); + + if (arraySize != parentArraySize) { + JNU_ThrowIllegalArgumentException(env, "array sizes not equal"); + return 0; + } + } + if (jstimesArray != NULL) { + stimesSize = (*env)->GetArrayLength(env, jstimesArray); + JNU_CHECK_EXCEPTION_RETURN(env, -1); + + if (arraySize != stimesSize) { + JNU_ThrowIllegalArgumentException(env, "array sizes not equal"); + return 0; + } + } + + const int chunk = 100; + struct procentry64 ProcessBuffer[chunk]; + pid_t idxptr = 0; + int i, num = 0; + + do { // Block to break out of on Exception + pids = (*env)->GetLongArrayElements(env, jarray, NULL); + if (pids == NULL) { + break; + } + if (jparentArray != NULL) { + ppids = (*env)->GetLongArrayElements(env, jparentArray, NULL); + if (ppids == NULL) { + break; + } + } + if (jstimesArray != NULL) { + stimes = (*env)->GetLongArrayElements(env, jstimesArray, NULL); + if (stimes == NULL) { + break; + } + } + + while ((num = getprocs64(ProcessBuffer, sizeof(struct procentry64), NULL, + sizeof(struct fdsinfo64), &idxptr, chunk)) != -1) { + for (i = 0; i < num; i++) { + pid_t childpid = (pid_t) ProcessBuffer[i].pi_pid; + pid_t ppid = (pid_t) ProcessBuffer[i].pi_ppid; + + // Get the parent pid, and start time + if (pid == 0 || ppid == pid) { + if (count < arraySize) { + // Only store if it fits + pids[count] = (jlong) childpid; + + if (ppids != NULL) { + // Store the parentPid + ppids[count] = (jlong) ppid; + } + if (stimes != NULL) { + // Store the process start time + stimes[count] = ((jlong) ProcessBuffer[i].pi_start) * 1000;; + } + } + count++; // Count to tabulate size needed + } + } + if (num < chunk) { + break; + } + } + } while (0); + + if (pids != NULL) { + (*env)->ReleaseLongArrayElements(env, jarray, pids, 0); + } + if (ppids != NULL) { + (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0); + } + if (stimes != NULL) { + (*env)->ReleaseLongArrayElements(env, jstimesArray, stimes, 0); + } + + if (num == -1) { + JNU_ThrowByNameWithLastError(env, + "java/lang/RuntimeException", "Unable to retrieve Process info"); + return -1; + } + + // If more pids than array had size for; count will be greater than array size + return count; } pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t pid, jlong *total, jlong *start) { diff --git a/src/java.base/aix/native/libnio/MappedMemoryUtils.c b/src/java.base/aix/native/libnio/MappedMemoryUtils.c index 51763e89082..5d0216cc251 100644 --- a/src/java.base/aix/native/libnio/MappedMemoryUtils.c +++ b/src/java.base/aix/native/libnio/MappedMemoryUtils.c @@ -158,6 +158,11 @@ static void check_aix_einval(JNIEnv* env, void* end_address) FILE* proc_file; { char* fname = (char*) malloc(sizeof(char) * PFNAME_LEN); + if (fname == NULL) { + JNU_ThrowOutOfMemoryError(env, NULL); + return; + } + pid_t the_pid = getpid(); jio_snprintf(fname, PFNAME_LEN, "/proc/%d/map", the_pid); proc_file = fopen(fname, "r"); @@ -170,6 +175,11 @@ static void check_aix_einval(JNIEnv* env, void* end_address) } { prmap_t* map_entry = (prmap_t*) malloc(sizeof(prmap_t)); + if (map_entry == NULL) { + JNU_ThrowOutOfMemoryError(env, NULL); + fclose(proc_file); + return; + } check_proc_map_array(env, proc_file, map_entry, end_address); free(map_entry); } diff --git a/src/java.base/linux/classes/sun/nio/fs/LinuxFileStore.java b/src/java.base/linux/classes/sun/nio/fs/LinuxFileStore.java index 0a711993bbd..9943238fd77 100644 --- a/src/java.base/linux/classes/sun/nio/fs/LinuxFileStore.java +++ b/src/java.base/linux/classes/sun/nio/fs/LinuxFileStore.java @@ -142,7 +142,7 @@ public boolean supportsFileAttributeView(Class type } return xattrEnabled; } - // POSIX attributes not supported on FAT + // POSIX attributes not supported on FAT32 if (type == PosixFileAttributeView.class && entry().fstype().equals("vfat")) return false; return super.supportsFileAttributeView(type); diff --git a/src/java.base/macosx/classes/sun/nio/fs/BsdFileStore.java b/src/java.base/macosx/classes/sun/nio/fs/BsdFileStore.java index e7d488de005..6066e0e1d90 100644 --- a/src/java.base/macosx/classes/sun/nio/fs/BsdFileStore.java +++ b/src/java.base/macosx/classes/sun/nio/fs/BsdFileStore.java @@ -25,9 +25,10 @@ package sun.nio.fs; +import java.io.IOException; import java.nio.file.attribute.FileAttributeView; +import java.nio.file.attribute.PosixFileAttributeView; import java.nio.file.attribute.UserDefinedFileAttributeView; -import java.io.IOException; import java.util.Arrays; /** @@ -100,6 +101,10 @@ public boolean supportsFileAttributeView(Class type UnixPath dir = new UnixPath(file().getFileSystem(), entry().dir()); return isExtendedAttributesEnabled(dir); } + // POSIX attributes not supported on FAT32 + if (type == PosixFileAttributeView.class && + entry().fstype().equals("msdos")) + return false; return super.supportsFileAttributeView(type); } @@ -107,6 +112,9 @@ public boolean supportsFileAttributeView(Class type public boolean supportsFileAttributeView(String name) { if (name.equals("user")) return supportsFileAttributeView(UserDefinedFileAttributeView.class); + // UNIX attributes not supported on FAT32 + if (name.equals("unix") && entry().fstype().equals("msdos")) + return false; return super.supportsFileAttributeView(name); } } diff --git a/src/java.base/macosx/native/libjava/ProcessHandleImpl_macosx.c b/src/java.base/macosx/native/libjava/ProcessHandleImpl_macosx.c index 8987f0b97a4..e3d811cfca6 100644 --- a/src/java.base/macosx/native/libjava/ProcessHandleImpl_macosx.c +++ b/src/java.base/macosx/native/libjava/ProcessHandleImpl_macosx.c @@ -47,13 +47,21 @@ void os_initNative(JNIEnv *env, jclass clazz) {} /* - * Returns the children of the requested pid and optionally each parent. - * - * Use sysctl to accumulate any process whose parent pid is zero or matches. - * The resulting pids are stored into the array of longs. + * Return pids of active processes, and optionally parent pids and + * start times for each process. + * For a specific non-zero pid jpid, only the direct children are returned. + * If the pid jpid is zero, all active processes are returned. + * Uses sysctl to accumulates any process following the rules above. + * The resulting pids are stored into an array of longs named jarray. * The number of pids is returned if they all fit. - * If the parentArray is non-null, store the parent pid. - * If the array is too short, excess pids are not stored and + * If the parentArray is non-null, store also the parent pid. + * In this case the parentArray must have the same length as the result pid array. + * Of course in the case of a given non-zero pid all entries in the parentArray + * will contain this pid, so this array does only make sense in the case of a given + * zero pid. + * If the jstimesArray is non-null, store also the start time of the pid. + * In this case the jstimesArray must have the same length as the result pid array. + * If the array(s) (is|are) too short, excess pids are not stored and * the desired length is returned. */ jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray, diff --git a/src/java.base/share/classes/com/sun/crypto/provider/AESParameters.java b/src/java.base/share/classes/com/sun/crypto/provider/AESParameters.java index 4c1c93ceb3c..becda6383b0 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/AESParameters.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/AESParameters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -44,7 +44,7 @@ */ public final class AESParameters extends AlgorithmParametersSpi { - private BlockCipherParamsCore core; + private final BlockCipherParamsCore core; public AESParameters() { core = new BlockCipherParamsCore(AESConstants.AES_BLOCK_SIZE, 4, 8); diff --git a/src/java.base/share/classes/com/sun/crypto/provider/BlockCipherParamsCore.java b/src/java.base/share/classes/com/sun/crypto/provider/BlockCipherParamsCore.java index 738b34d3aeb..ece9f8161d1 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/BlockCipherParamsCore.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/BlockCipherParamsCore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -46,10 +46,10 @@ * */ final class BlockCipherParamsCore { - private int block_size = 0; + private final int block_size; private byte[] iv = null; - private int[] moreSizes = null; + private final int[] moreSizes; BlockCipherParamsCore(int blksize, int... moreSizes) { block_size = blksize; diff --git a/src/java.base/share/classes/com/sun/crypto/provider/BlowfishCipher.java b/src/java.base/share/classes/com/sun/crypto/provider/BlowfishCipher.java index a389340b45a..247ea63f2e6 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/BlowfishCipher.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/BlowfishCipher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -29,9 +29,7 @@ import java.security.spec.*; import java.util.Arrays; -import sun.security.util.*; import javax.crypto.*; -import javax.crypto.spec.*; import javax.crypto.BadPaddingException; /** @@ -57,7 +55,7 @@ public final class BlowfishCipher extends CipherSpi { /* * internal CipherCore object which does the real work. */ - private CipherCore core = null; + private final CipherCore core; /** * Creates an instance of Blowfish cipher with default ECB mode and diff --git a/src/java.base/share/classes/com/sun/crypto/provider/BlowfishParameters.java b/src/java.base/share/classes/com/sun/crypto/provider/BlowfishParameters.java index beb2804c3fd..85bf2e2ea27 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/BlowfishParameters.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/BlowfishParameters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -44,7 +44,7 @@ public final class BlowfishParameters extends AlgorithmParametersSpi { - private BlockCipherParamsCore core; + private final BlockCipherParamsCore core; public BlowfishParameters() { core = new BlockCipherParamsCore diff --git a/src/java.base/share/classes/com/sun/crypto/provider/CipherBlockChaining.java b/src/java.base/share/classes/com/sun/crypto/provider/CipherBlockChaining.java index 4fc5a34b46b..5d756e26847 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/CipherBlockChaining.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/CipherBlockChaining.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,6 @@ import java.security.InvalidKeyException; import java.security.ProviderException; -import java.util.Objects; import jdk.internal.vm.annotation.IntrinsicCandidate; import sun.security.util.ArrayUtil; @@ -55,7 +54,7 @@ class CipherBlockChaining extends FeedbackCipher { /* * output buffer */ - private byte[] k; + private final byte[] k; // variables for save/restore calls private byte[] rSave = null; diff --git a/src/java.base/share/classes/com/sun/crypto/provider/CipherCore.java b/src/java.base/share/classes/com/sun/crypto/provider/CipherCore.java index 64ce01c53ab..2555448e3c0 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/CipherCore.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/CipherCore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -57,12 +57,12 @@ final class CipherCore { /* * internal buffer */ - private byte[] buffer = null; + private final byte[] buffer; /* * block size of cipher in bytes */ - private int blockSize = 0; + private final int blockSize; /* * unit size (number of input bytes that can be processed at a time) diff --git a/src/java.base/share/classes/com/sun/crypto/provider/CipherFeedback.java b/src/java.base/share/classes/com/sun/crypto/provider/CipherFeedback.java index 71e94cc7ffa..00b50b9e8ca 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/CipherFeedback.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/CipherFeedback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -57,7 +57,7 @@ final class CipherFeedback extends FeedbackCipher { * number of bytes for each stream unit, defaults to the blocksize * of the embedded cipher */ - private int numBytes; + private final int numBytes; // variables for save/restore calls private byte[] registerSave = null; diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DESCipher.java b/src/java.base/share/classes/com/sun/crypto/provider/DESCipher.java index 5fdfd32b2b5..ee283441eeb 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DESCipher.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DESCipher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -29,7 +29,6 @@ import java.security.spec.*; import java.util.Arrays; import javax.crypto.*; -import javax.crypto.spec.*; import javax.crypto.BadPaddingException; /** @@ -52,7 +51,7 @@ public final class DESCipher extends CipherSpi { /* * internal CipherCore object which does the real work. */ - private CipherCore core = null; + private final CipherCore core; /** * Creates an instance of DES cipher with default ECB mode and diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DESKey.java b/src/java.base/share/classes/com/sun/crypto/provider/DESKey.java index 87aa1753eb5..17f82b2eacc 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DESKey.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DESKey.java @@ -25,6 +25,8 @@ package com.sun.crypto.provider; +import java.io.IOException; +import java.io.InvalidObjectException; import java.lang.ref.Reference; import java.security.MessageDigest; import java.security.KeyRep; @@ -45,7 +47,7 @@ final class DESKey implements SecretKey { @java.io.Serial - static final long serialVersionUID = 7724971015953279128L; + private static final long serialVersionUID = 7724971015953279128L; private byte[] key; @@ -143,17 +145,26 @@ public boolean equals(Object obj) { } /** - * readObject is called to restore the state of this key from - * a stream. + * Restores the state of this object from the stream. + * + * @param s the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded */ @java.io.Serial private void readObject(java.io.ObjectInputStream s) - throws java.io.IOException, ClassNotFoundException + throws IOException, ClassNotFoundException { s.defaultReadObject(); + if ((key == null) || (key.length != DESKeySpec.DES_KEY_LEN)) { + throw new InvalidObjectException("Wrong key size"); + } byte[] temp = key; key = temp.clone(); Arrays.fill(temp, (byte)0x00); + + DESKeyGenerator.setParityBit(key, 0); + // Use the cleaner to zero the key when no longer referenced final byte[] k = this.key; CleanerFactory.cleaner().register(this, diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DESParameters.java b/src/java.base/share/classes/com/sun/crypto/provider/DESParameters.java index a5ae615870b..c825e700a74 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DESParameters.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DESParameters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -44,7 +44,7 @@ public final class DESParameters extends AlgorithmParametersSpi { - private BlockCipherParamsCore core; + private final BlockCipherParamsCore core; public DESParameters() { core = new BlockCipherParamsCore(DESConstants.DES_BLOCK_SIZE); diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DESedeCipher.java b/src/java.base/share/classes/com/sun/crypto/provider/DESedeCipher.java index a3599b608cc..3c1f122ae3b 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DESedeCipher.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DESedeCipher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -29,7 +29,6 @@ import java.security.spec.*; import java.util.Arrays; import javax.crypto.*; -import javax.crypto.spec.*; /** * This class implements the DESede algorithm (DES-EDE, tripleDES) in @@ -50,7 +49,7 @@ public sealed class DESedeCipher extends CipherSpi /* * internal CipherCore object which does the real work. */ - private CipherCore core = null; + private final CipherCore core; /** * Creates an instance of DESede cipher with default ECB mode and diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DESedeCrypt.java b/src/java.base/share/classes/com/sun/crypto/provider/DESedeCrypt.java index b037c44d86d..496a59b002a 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DESedeCrypt.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DESedeCrypt.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -48,7 +48,8 @@ final class DESedeCrypt extends DESCrypt implements DESConstants { private byte[] key1 = null; private byte[] key2 = null; private byte[] key3 = null; - private byte[] buf1, buf2; + private final byte[] buf1; + private final byte[] buf2; /* * constructor diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DESedeKey.java b/src/java.base/share/classes/com/sun/crypto/provider/DESedeKey.java index 938fd217eee..f5c3d8384df 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DESedeKey.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DESedeKey.java @@ -25,6 +25,8 @@ package com.sun.crypto.provider; +import java.io.IOException; +import java.io.InvalidObjectException; import java.lang.ref.Reference; import java.security.MessageDigest; import java.security.KeyRep; @@ -45,7 +47,7 @@ final class DESedeKey implements SecretKey { @java.io.Serial - static final long serialVersionUID = 2463986565756745178L; + private static final long serialVersionUID = 2463986565756745178L; private byte[] key; @@ -144,17 +146,28 @@ public boolean equals(Object obj) { } /** - * readObject is called to restore the state of this key from - * a stream. + * Restores the state of this object from the stream. + * + * @param s the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded */ @java.io.Serial private void readObject(java.io.ObjectInputStream s) - throws java.io.IOException, ClassNotFoundException + throws IOException, ClassNotFoundException { s.defaultReadObject(); + if ((key == null) || (key.length != DESedeKeySpec.DES_EDE_KEY_LEN)) { + throw new InvalidObjectException("Wrong key size"); + } byte[] temp = key; this.key = temp.clone(); java.util.Arrays.fill(temp, (byte)0x00); + + DESKeyGenerator.setParityBit(key, 0); + DESKeyGenerator.setParityBit(key, 8); + DESKeyGenerator.setParityBit(key, 16); + // Use the cleaner to zero the key when no longer referenced final byte[] k = this.key; CleanerFactory.cleaner().register(this, diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DESedeParameters.java b/src/java.base/share/classes/com/sun/crypto/provider/DESedeParameters.java index d2bba89b799..879a4d30cd2 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DESedeParameters.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DESedeParameters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -43,7 +43,7 @@ */ public final class DESedeParameters extends AlgorithmParametersSpi { - private BlockCipherParamsCore core; + private final BlockCipherParamsCore core; public DESedeParameters() { core = new BlockCipherParamsCore(DESConstants.DES_BLOCK_SIZE); diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DESedeWrapCipher.java b/src/java.base/share/classes/com/sun/crypto/provider/DESedeWrapCipher.java index 8ba473e467e..92d25b57a7e 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DESedeWrapCipher.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DESedeWrapCipher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 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 @@ -57,7 +57,7 @@ public final class DESedeWrapCipher extends CipherSpi { /* * internal cipher object which does the real work. */ - private FeedbackCipher cipher; + private final FeedbackCipher cipher; /* * iv for (re-)initializing the internal cipher object. 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 01a6e38d2aa..bd1c3f7c380 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 @@ -353,9 +353,9 @@ private Params paramsFromKey(Key k) throws InvalidKeyException { } } else if (k instanceof XECKey xkey && xkey.getParams() instanceof NamedParameterSpec ns) { - if (ns.getName().equals("X25519")) { + if (ns.getName().equalsIgnoreCase("X25519")) { return Params.X25519; - } else if (ns.getName().equals("X448")) { + } else if (ns.getName().equalsIgnoreCase("X448")) { return Params.X448; } } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java b/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java index 6d908721b4a..0d5363e866a 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java @@ -40,8 +40,6 @@ * algorithm. * * @author Jan Luehe - * - * * @see DHPublicKey * @see javax.crypto.KeyAgreement */ @@ -49,13 +47,13 @@ final class DHPrivateKey implements PrivateKey, javax.crypto.interfaces.DHPrivateKey, Serializable { @java.io.Serial - static final long serialVersionUID = 7565477590005668886L; + private static final long serialVersionUID = 7565477590005668886L; // only supported version of PKCS#8 PrivateKeyInfo private static final BigInteger PKCS8_VERSION = BigInteger.ZERO; // the private key - private BigInteger x; + private final BigInteger x; // the key bytes, without the algorithm information private byte[] key; @@ -64,13 +62,13 @@ final class DHPrivateKey implements PrivateKey, private byte[] encodedKey; // the prime modulus - private BigInteger p; + private final BigInteger p; // the base generator - private BigInteger g; + private final BigInteger g; // the private-value length (optional) - private int l; + private final int l; /** * Make a DH private key out of a private value x, a prime @@ -163,6 +161,8 @@ final class DHPrivateKey implements PrivateKey, // Private-value length is OPTIONAL if (params.data.available() != 0) { this.l = params.data.getInteger(); + } else { + this.l = 0; } if (params.data.available() != 0) { throw new InvalidKeyException("Extra parameter data"); @@ -172,7 +172,9 @@ final class DHPrivateKey implements PrivateKey, // privateKey // this.key = val.data.getOctetString(); - parseKeyBits(); + + DerInputStream in = new DerInputStream(this.key); + this.x = in.getBigInteger(); this.encodedKey = encodedKey.clone(); } catch (IOException | NumberFormatException e) { @@ -273,16 +275,6 @@ public DHParameterSpec getParams() { } } - private void parseKeyBits() throws InvalidKeyException { - try { - DerInputStream in = new DerInputStream(this.key); - this.x = in.getBigInteger(); - } catch (IOException e) { - throw new InvalidKeyException( - "Error parsing key encoding: " + e.getMessage(), e); - } - } - /** * Calculates a hash code value for the object. * Objects that are equal will also have the same hashcode. @@ -321,4 +313,28 @@ private Object writeReplace() throws java.io.ObjectStreamException { getFormat(), encodedKey); } + + /** + * Restores the state of this object from the stream. + *

+ * JDK 1.5+ objects use KeyReps instead. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + if ((key == null) || (key.length == 0)) { + throw new InvalidObjectException("key not deserializable"); + } + this.key = key.clone(); + if ((encodedKey == null) || (encodedKey.length == 0)) { + throw new InvalidObjectException( + "encoded key not deserializable"); + } + this.encodedKey = encodedKey.clone(); + } } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java b/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java index 78d7d71b1c1..47727c432a6 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java @@ -39,8 +39,6 @@ * A public key in X.509 format for the Diffie-Hellman key agreement algorithm. * * @author Jan Luehe - * - * * @see DHPrivateKey * @see javax.crypto.KeyAgreement */ @@ -48,10 +46,10 @@ final class DHPublicKey implements PublicKey, javax.crypto.interfaces.DHPublicKey, Serializable { @java.io.Serial - static final long serialVersionUID = 7647557958927458271L; + private static final long serialVersionUID = 7647557958927458271L; // the public key - private BigInteger y; + private final BigInteger y; // the key bytes, without the algorithm information private byte[] key; @@ -60,16 +58,16 @@ final class DHPublicKey implements PublicKey, private byte[] encodedKey; // the prime modulus - private BigInteger p; + private final BigInteger p; // the base generator - private BigInteger g; + private final BigInteger g; // the private-value length (optional) - private int l; + private final int l; // Note: this OID is used by DHPrivateKey as well. - static ObjectIdentifier DH_OID = + static final ObjectIdentifier DH_OID = ObjectIdentifier.of(KnownOIDs.DiffieHellman); /** @@ -155,6 +153,8 @@ final class DHPublicKey implements PublicKey, // Private-value length is OPTIONAL if (params.data.available() != 0) { this.l = params.data.getInteger(); + } else { + this.l = 0; } if (params.data.available() != 0) { throw new InvalidKeyException("Extra parameter data"); @@ -164,7 +164,10 @@ final class DHPublicKey implements PublicKey, * Parse the key */ this.key = derKeyVal.data.getBitString(); - parseKeyBits(); + + DerInputStream in = new DerInputStream(this.key); + this.y = in.getBigInteger(); + if (derKeyVal.data.available() != 0) { throw new InvalidKeyException("Excess key data"); } @@ -265,16 +268,6 @@ public String toString() { return sb.toString(); } - private void parseKeyBits() throws InvalidKeyException { - try { - DerInputStream in = new DerInputStream(this.key); - this.y = in.getBigInteger(); - } catch (IOException e) { - throw new InvalidKeyException( - "Error parsing key encoding: " + e.toString()); - } - } - /** * Calculates a hash code value for the object. * Objects that are equal will also have the same hashcode. @@ -313,4 +306,28 @@ private Object writeReplace() throws java.io.ObjectStreamException { getFormat(), getEncoded()); } + + /** + * Restores the state of this object from the stream. + *

+ * JDK 1.5+ objects use KeyReps instead. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + if ((key == null) || (key.length == 0)) { + throw new InvalidObjectException("key not deserializable"); + } + this.key = key.clone(); + if ((encodedKey == null) || (encodedKey.length == 0)) { + throw new InvalidObjectException( + "encoded key not deserializable"); + } + this.encodedKey = encodedKey.clone(); + } } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/EncryptedPrivateKeyInfo.java b/src/java.base/share/classes/com/sun/crypto/provider/EncryptedPrivateKeyInfo.java index cfeb66c62c6..698a5711356 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/EncryptedPrivateKeyInfo.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/EncryptedPrivateKeyInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -44,10 +44,10 @@ final class EncryptedPrivateKeyInfo { // the "encryptionAlgorithm" field - private AlgorithmId algid; + private final AlgorithmId algid; // the "encryptedData" field - private byte[] encryptedData; + private final byte[] encryptedData; // the ASN.1 encoded contents of this class private byte[] encoded; diff --git a/src/java.base/share/classes/com/sun/crypto/provider/GHASH.java b/src/java.base/share/classes/com/sun/crypto/provider/GHASH.java index 8a1a086e433..b7aaf6059d5 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/GHASH.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/GHASH.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -124,7 +124,7 @@ private static void blockMult(long[] st, long[] subH) { // hashtable subkeyHtbl holds 2*9 powers of subkeyH computed using // carry-less multiplication - private long[] subkeyHtbl; + private final long[] subkeyHtbl; // buffer for storing hash private final long[] state; diff --git a/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java b/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java index 03b481af6c2..686fe384707 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -74,8 +74,8 @@ * @since 1.8 */ abstract class GaloisCounterMode extends CipherSpi { - static int DEFAULT_IV_LEN = 12; // in bytes - static int DEFAULT_TAG_LEN = 16; // in bytes + private static final int DEFAULT_IV_LEN = 12; // in bytes + private static final int DEFAULT_TAG_LEN = 16; // in bytes // In NIST SP 800-38D, GCM input size is limited to be no longer // than (2^36 - 32) bytes. Otherwise, the counter will wrap // around and lead to a leak of plaintext. @@ -98,7 +98,7 @@ abstract class GaloisCounterMode extends CipherSpi { private boolean initialized = false; - SymmetricCipher blockCipher; + final SymmetricCipher blockCipher; // Engine instance for encryption or decryption private GCMEngine engine; private boolean encryption = true; @@ -106,7 +106,7 @@ abstract class GaloisCounterMode extends CipherSpi { // Default value is 128bits, this is in bytes. int tagLenBytes = DEFAULT_TAG_LEN; // Key size if the value is passed, in bytes. - int keySize; + private final int keySize; // Prevent reuse of iv or key boolean reInit = false; byte[] lastKey = EMPTY_BUF; diff --git a/src/java.base/share/classes/com/sun/crypto/provider/ISO10126Padding.java b/src/java.base/share/classes/com/sun/crypto/provider/ISO10126Padding.java index 19fb961681a..902d302cac4 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/ISO10126Padding.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/ISO10126Padding.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,7 +38,7 @@ */ final class ISO10126Padding implements Padding { - private int blockSize; + private final int blockSize; ISO10126Padding(int blockSize) { this.blockSize = blockSize; 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 ca6c0a56ea8..fcc450387af 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -99,7 +99,7 @@ private static final class TrustedCertEntry { * Private keys and certificates are stored in a hashtable. * Hash entries are keyed by alias names. */ - private Hashtable entries = new Hashtable(); + private final Hashtable entries = new Hashtable<>(); /** * Returns the key associated with the given alias, using the given diff --git a/src/java.base/share/classes/com/sun/crypto/provider/KeyProtector.java b/src/java.base/share/classes/com/sun/crypto/provider/KeyProtector.java index 44c7a1f6432..eadc032b36a 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/KeyProtector.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/KeyProtector.java @@ -78,7 +78,7 @@ final class KeyProtector { // the password used for protecting/recovering keys passed through this // key protector - private char[] password; + private final char[] password; /** * {@systemProperty jdk.jceks.iterationCount} property indicating the diff --git a/src/java.base/share/classes/com/sun/crypto/provider/OAEPParameters.java b/src/java.base/share/classes/com/sun/crypto/provider/OAEPParameters.java index fd609c9f74e..9d724728893 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/OAEPParameters.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/OAEPParameters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,9 +56,9 @@ public final class OAEPParameters extends AlgorithmParametersSpi { private String mdName; private MGF1ParameterSpec mgfSpec; private byte[] p; - private static ObjectIdentifier OID_MGF1 = + private static final ObjectIdentifier OID_MGF1 = ObjectIdentifier.of(KnownOIDs.MGF1); - private static ObjectIdentifier OID_PSpecified = + private static final ObjectIdentifier OID_PSpecified = ObjectIdentifier.of(KnownOIDs.PSpecified); public OAEPParameters() { diff --git a/src/java.base/share/classes/com/sun/crypto/provider/OutputFeedback.java b/src/java.base/share/classes/com/sun/crypto/provider/OutputFeedback.java index 7b518836cd1..aa56144f090 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/OutputFeedback.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/OutputFeedback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -45,18 +45,18 @@ final class OutputFeedback extends FeedbackCipher { /* * output buffer */ - private byte[] k = null; + private final byte[] k; /* * register buffer */ - private byte[] register = null; + private final byte[] register; /* * number of bytes for each stream unit, defaults to the blocksize * of the embedded cipher */ - private int numBytes; + private final int numBytes; // variables for save/restore calls private byte[] registerSave = null; diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PBEKey.java b/src/java.base/share/classes/com/sun/crypto/provider/PBEKey.java index 71efeb535aa..480a0810425 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/PBEKey.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/PBEKey.java @@ -25,6 +25,8 @@ package com.sun.crypto.provider; +import java.io.IOException; +import java.io.InvalidObjectException; import java.lang.ref.Reference; import java.lang.ref.Cleaner.Cleanable; import java.security.MessageDigest; @@ -46,11 +48,11 @@ final class PBEKey implements SecretKey { @java.io.Serial - static final long serialVersionUID = -2234768909660948176L; + private static final long serialVersionUID = -2234768909660948176L; private byte[] key; - private String type; + private final String type; private transient Cleanable cleanable; @@ -162,17 +164,35 @@ public boolean isDestroyed() { } /** - * readObject is called to restore the state of this key from - * a stream. + * Restores the state of this object from the stream. + * + * @param s the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded */ @java.io.Serial private void readObject(java.io.ObjectInputStream s) - throws java.io.IOException, ClassNotFoundException + throws IOException, ClassNotFoundException { s.defaultReadObject(); + if (key == null) { + throw new InvalidObjectException( + "PBEKey couldn't be deserialized"); + } byte[] temp = key; key = temp.clone(); Arrays.fill(temp, (byte)0x00); + + // Accept "\0" to signify "zero-length password with no terminator". + if (!(key.length == 1 && key[0] == 0)) { + for (int i = 0; i < key.length; i++) { + if ((key[i] < '\u0020') || (key[i] > '\u007E')) { + throw new InvalidObjectException( + "PBEKey had non-ASCII chars"); + } + } + } + // Use cleaner to zero the key when no longer referenced final byte[] k = this.key; cleanable = CleanerFactory.cleaner().register(this, diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PBEKeyFactory.java b/src/java.base/share/classes/com/sun/crypto/provider/PBEKeyFactory.java index d37f0e763ad..02451e536c6 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/PBEKeyFactory.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/PBEKeyFactory.java @@ -46,7 +46,7 @@ */ abstract class PBEKeyFactory extends SecretKeyFactorySpi { - private String type; + private final String type; private static final HashSet validTypes; /** diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PBES1Core.java b/src/java.base/share/classes/com/sun/crypto/provider/PBES1Core.java index f8972b13343..68b1771d7d9 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/PBES1Core.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/PBES1Core.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -43,10 +43,9 @@ final class PBES1Core { // the encapsulated DES cipher - private CipherCore cipher; - private MessageDigest md; - private int blkSize; - private String algo = null; + private final CipherCore cipher; + private final MessageDigest md; + private final String algo; private byte[] salt = null; private int iCount = 10; diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PBES2Parameters.java b/src/java.base/share/classes/com/sun/crypto/provider/PBES2Parameters.java index 1661acc434d..fb3dfa00148 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/PBES2Parameters.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/PBES2Parameters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -92,15 +92,15 @@ */ abstract class PBES2Parameters extends AlgorithmParametersSpi { - private static ObjectIdentifier pkcs5PBKDF2_OID = + private static final ObjectIdentifier pkcs5PBKDF2_OID = ObjectIdentifier.of(KnownOIDs.PBKDF2WithHmacSHA1); - private static ObjectIdentifier pkcs5PBES2_OID = + private static final ObjectIdentifier pkcs5PBES2_OID = ObjectIdentifier.of(KnownOIDs.PBES2); - private static ObjectIdentifier aes128CBC_OID = + private static final ObjectIdentifier aes128CBC_OID = ObjectIdentifier.of(KnownOIDs.AES_128$CBC$NoPadding); - private static ObjectIdentifier aes192CBC_OID = + private static final ObjectIdentifier aes192CBC_OID = ObjectIdentifier.of(KnownOIDs.AES_192$CBC$NoPadding); - private static ObjectIdentifier aes256CBC_OID = + private static final ObjectIdentifier aes256CBC_OID = ObjectIdentifier.of(KnownOIDs.AES_256$CBC$NoPadding); // the PBES2 algorithm name diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PBEWithMD5AndDESCipher.java b/src/java.base/share/classes/com/sun/crypto/provider/PBEWithMD5AndDESCipher.java index 5034434024d..a60656b3cd2 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/PBEWithMD5AndDESCipher.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/PBEWithMD5AndDESCipher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -43,7 +43,7 @@ public final class PBEWithMD5AndDESCipher extends CipherSpi { // the encapsulated DES cipher - private PBES1Core core; + private final PBES1Core core; /** * Creates an instance of this cipher, and initializes its mode (CBC) and diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PBEWithMD5AndTripleDESCipher.java b/src/java.base/share/classes/com/sun/crypto/provider/PBEWithMD5AndTripleDESCipher.java index 16784afc897..41a1b3ca7fa 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/PBEWithMD5AndTripleDESCipher.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/PBEWithMD5AndTripleDESCipher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -53,7 +53,7 @@ */ public final class PBEWithMD5AndTripleDESCipher extends CipherSpi { - private PBES1Core core; + private final PBES1Core core; /** * Creates an instance of this cipher, and initializes its mode (CBC) and diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java b/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java index 445a8a1814c..a02ce2d1551 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java @@ -25,7 +25,7 @@ package com.sun.crypto.provider; -import java.io.ObjectStreamException; +import java.io.*; import java.lang.ref.Reference; import java.lang.ref.Cleaner; import java.nio.ByteBuffer; @@ -58,16 +58,16 @@ final class PBKDF2KeyImpl implements javax.crypto.interfaces.PBEKey { @java.io.Serial - static final long serialVersionUID = -2234868909660948157L; + private static final long serialVersionUID = -2234868909660948157L; - private char[] passwd; - private byte[] salt; - private int iterCount; - private byte[] key; + private final char[] passwd; + private final byte[] salt; + private final int iterCount; + private final byte[] key; // The following fields are not Serializable. See writeReplace method. - private transient Mac prf; - private transient Cleaner.Cleanable cleaner; + private final transient Mac prf; + private final transient Cleaner.Cleanable cleaner; private static byte[] getPasswordBytes(char[] passwd) { CharBuffer cb = CharBuffer.wrap(passwd); @@ -93,6 +93,7 @@ private static byte[] getPasswordBytes(char[] passwd) { // Convert the password from char[] to byte[] byte[] passwdBytes = getPasswordBytes(this.passwd); + byte[] key = null; try { this.salt = keySpec.getSalt(); if (salt == null) { @@ -111,7 +112,7 @@ private static byte[] getPasswordBytes(char[] passwd) { throw new InvalidKeySpecException("Key length is negative"); } this.prf = Mac.getInstance(prfAlgo, SunJCE.getInstance()); - this.key = deriveKey(prf, passwdBytes, salt, iterCount, keyLength); + key = deriveKey(prf, passwdBytes, salt, iterCount, keyLength); } catch (NoSuchAlgorithmException nsae) { // not gonna happen; re-throw just in case throw new InvalidKeySpecException(nsae); @@ -122,7 +123,7 @@ private static byte[] getPasswordBytes(char[] passwd) { } } // Use the cleaner to zero the key when no longer referenced - final byte[] k = this.key; + final byte[] k = this.key = key; final char[] p = this.passwd; cleaner = CleanerFactory.cleaner().register(this, () -> { @@ -141,13 +142,14 @@ private static byte[] deriveKey(final Mac prf, final byte[] password, int intR = keyLength - (intL - 1)*hlen; // residue byte[] ui = new byte[hlen]; byte[] ti = new byte[hlen]; + String algName = prf.getAlgorithm(); // SecretKeySpec cannot be used, since password can be empty here. SecretKey macKey = new SecretKey() { @java.io.Serial private static final long serialVersionUID = 7874493593505141603L; @Override public String getAlgorithm() { - return prf.getAlgorithm(); + return algName; } @Override public String getFormat() { @@ -160,18 +162,27 @@ public byte[] getEncoded() { @Override public int hashCode() { return Arrays.hashCode(password) * 41 + - prf.getAlgorithm().toLowerCase(Locale.ENGLISH).hashCode(); + algName.toLowerCase(Locale.ENGLISH).hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || this.getClass() != obj.getClass()) return false; SecretKey sk = (SecretKey)obj; - return prf.getAlgorithm().equalsIgnoreCase( + return algName.equalsIgnoreCase( sk.getAlgorithm()) && MessageDigest.isEqual(password, sk.getEncoded()); } + // This derived key can't be deserialized. + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException( + "PBKDF2KeyImpl SecretKeys are not " + + "directly deserializable"); + } }; + prf.init(macKey); byte[] ibytes = new byte[4]; @@ -303,4 +314,20 @@ private Object writeReplace() throws ObjectStreamException { Reference.reachabilityFence(this); } } + + /** + * Restores the state of this object from the stream. + *

+ * Deserialization of this class is not supported. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException( + "PBKDF2KeyImpl keys are not directly deserializable"); + } } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PKCS5Padding.java b/src/java.base/share/classes/com/sun/crypto/provider/PKCS5Padding.java index 191d7308eba..7b3850bd556 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/PKCS5Padding.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/PKCS5Padding.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -38,7 +38,7 @@ */ final class PKCS5Padding implements Padding { - private int blockSize; + private final int blockSize; PKCS5Padding(int blockSize) { this.blockSize = blockSize; diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PrivateKeyInfo.java b/src/java.base/share/classes/com/sun/crypto/provider/PrivateKeyInfo.java index 2ea5220ed2e..cf4d1a15dbd 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/PrivateKeyInfo.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/PrivateKeyInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -53,10 +53,10 @@ final class PrivateKeyInfo { private static final BigInteger VERSION = BigInteger.ZERO; // the private-key algorithm - private AlgorithmId algid; + private final AlgorithmId algid; // the private-key value - private byte[] privkey; + private final byte[] privkey; /** * Constructs a PKCS#8 PrivateKeyInfo from its ASN.1 encoding. diff --git a/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java b/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java index 203d54495f2..14ada1699c1 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -25,6 +25,9 @@ package com.sun.crypto.provider; +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.security.*; import java.security.spec.AlgorithmParameterSpec; import java.util.Arrays; @@ -62,11 +65,11 @@ protected void engineInit(SecureRandom random) { @SuppressWarnings("deprecation") protected void engineInit(AlgorithmParameterSpec params, SecureRandom random) throws InvalidAlgorithmParameterException { - if (params instanceof TlsMasterSecretParameterSpec == false) { + if (!(params instanceof TlsMasterSecretParameterSpec)) { throw new InvalidAlgorithmParameterException(MSG); } this.spec = (TlsMasterSecretParameterSpec)params; - if ("RAW".equals(spec.getPremasterSecret().getFormat()) == false) { + if (!"RAW".equals(spec.getPremasterSecret().getFormat())) { throw new InvalidAlgorithmParameterException( "Key format must be RAW"); } @@ -191,6 +194,22 @@ public byte[] getEncoded() { return key.clone(); } - } + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + if ((key == null) || (key.length == 0)) { + throw new InvalidObjectException("TlsMasterSecretKey is null"); + } + key = key.clone(); + } + } } diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index 991c8bcf646..c95aa764ee6 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -83,6 +83,7 @@ import jdk.internal.reflect.ReflectionFactory; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.IntrinsicCandidate; +import jdk.internal.vm.annotation.Stable; import sun.invoke.util.Wrapper; import sun.reflect.generics.factory.CoreReflectionFactory; @@ -1060,6 +1061,7 @@ public Module getModule() { } // set by VM + @Stable private transient Module module; // Initialized in JVM not by private constructor diff --git a/src/java.base/share/classes/java/lang/Module.java b/src/java.base/share/classes/java/lang/Module.java index 33e92527488..694281bcca8 100644 --- a/src/java.base/share/classes/java/lang/Module.java +++ b/src/java.base/share/classes/java/lang/Module.java @@ -272,9 +272,8 @@ Module implAddEnableNativeAccess() { * restricted methods. * * @return {@code true} if this module can access restricted methods. - * @since 20 + * @since 22 */ - @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN) public boolean isNativeAccessEnabled() { Module target = moduleForNativeAccess(); return EnableNativeAccess.isNativeAccessEnabled(target); @@ -309,7 +308,7 @@ private Module moduleForNativeAccess() { } // This is invoked from Reflection.ensureNativeAccess - void ensureNativeAccess(Class owner, String methodName) { + void ensureNativeAccess(Class owner, String methodName, Class currentClass) { // The target module whose enableNativeAccess flag is ensured Module target = moduleForNativeAccess(); if (!EnableNativeAccess.isNativeAccessEnabled(target)) { @@ -320,13 +319,15 @@ void ensureNativeAccess(Class owner, String methodName) { // warn and set flag, so that only one warning is reported per module String cls = owner.getName(); String mtd = cls + "::" + methodName; - String mod = isNamed() ? "module " + getName() : "the unnamed module"; + String mod = isNamed() ? "module " + getName() : "an unnamed module"; String modflag = isNamed() ? getName() : "ALL-UNNAMED"; + String caller = currentClass != null ? currentClass.getName() : "code"; System.err.printf(""" WARNING: A restricted method in %s has been called - WARNING: %s has been called by %s - WARNING: Use --enable-native-access=%s to avoid a warning for this module - %n""", cls, mtd, mod, modflag); + WARNING: %s has been called by %s in %s + WARNING: Use --enable-native-access=%s to avoid a warning for callers in this module + WARNING: Restricted methods will be blocked in a future release unless native access is enabled + %n""", cls, mtd, caller, mod, modflag); } } } diff --git a/src/java.base/share/classes/java/lang/ModuleLayer.java b/src/java.base/share/classes/java/lang/ModuleLayer.java index fc74092f223..b038c1efa27 100644 --- a/src/java.base/share/classes/java/lang/ModuleLayer.java +++ b/src/java.base/share/classes/java/lang/ModuleLayer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -307,9 +307,7 @@ public Controller addOpens(Module source, String pn, Module target) { * *

This method is restricted. * Restricted methods are unsafe, and, if used incorrectly, their use might crash - * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain - * from depending on restricted methods, and use safe and supported functionalities, - * where possible. + * the JVM or, worse, silently result in memory corruption. * * @param target * The module to update @@ -322,9 +320,8 @@ public Controller addOpens(Module source, String pn, Module target) { * @throws IllegalCallerException * If the caller is in a module that does not have native access enabled * - * @since 20 + * @since 22 */ - @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) @CallerSensitive @Restricted public Controller enableNativeAccess(Module target) { diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index 9b19d7e2ac1..befd04ad916 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -28,6 +28,8 @@ import java.io.ObjectStreamField; import java.io.UnsupportedEncodingException; import java.lang.annotation.Native; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandles; import java.lang.constant.Constable; import java.lang.constant.ConstantDesc; @@ -1836,6 +1838,21 @@ public byte[] getBytes() { return encode(Charset.defaultCharset(), coder(), value); } + boolean bytesCompatible(Charset charset) { + if (isLatin1()) { + if (charset == ISO_8859_1.INSTANCE) { + return true; // ok, same encoding + } else if (charset == UTF_8.INSTANCE || charset == US_ASCII.INSTANCE) { + return !StringCoding.hasNegatives(value, 0, value.length); // ok, if ASCII-compatible + } + } + return false; + } + + void copyToSegmentRaw(MemorySegment segment, long offset) { + MemorySegment.copy(value, 0, segment, ValueLayout.JAVA_BYTE, offset, value.length); + } + /** * Compares this string to the specified object. The result is {@code * true} if and only if the argument is not {@code null} and is a {@code diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index e50858b26f1..e7ed153c620 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -35,6 +35,7 @@ import java.io.OutputStream; import java.io.PrintStream; import java.lang.annotation.Annotation; +import java.lang.foreign.MemorySegment; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.lang.invoke.StringConcatFactory; @@ -88,7 +89,6 @@ import jdk.internal.vm.ContinuationScope; import jdk.internal.vm.StackableScope; import jdk.internal.vm.ThreadContainer; -import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.IntrinsicCandidate; import jdk.internal.vm.annotation.Stable; import sun.nio.fs.DefaultFileSystemProvider; @@ -2454,8 +2454,8 @@ public Module addEnableNativeAccess(Module m) { public void addEnableNativeAccessToAllUnnamed() { Module.implAddEnableNativeAccessToAllUnnamed(); } - public void ensureNativeAccess(Module m, Class owner, String methodName) { - m.ensureNativeAccess(owner, methodName); + public void ensureNativeAccess(Module m, Class owner, String methodName, Class currentClass) { + m.ensureNativeAccess(owner, methodName, currentClass); } public ServicesCatalog getServicesCatalog(ModuleLayer layer) { return layer.getServicesCatalog(); @@ -2669,6 +2669,16 @@ public StackWalker newStackWalkerInstance(Set options, public String getLoaderNameID(ClassLoader loader) { return loader.nameAndId(); } + + @Override + public void copyToSegmentRaw(String string, MemorySegment segment, long offset) { + string.copyToSegmentRaw(segment, offset); + } + + @Override + public boolean bytesCompatible(String string, Charset charset) { + return string.bytesCompatible(charset); + } }); } } diff --git a/src/java.base/share/classes/java/lang/foreign/AddressLayout.java b/src/java.base/share/classes/java/lang/foreign/AddressLayout.java index fd17f071fdf..6c93f4b64ed 100644 --- a/src/java.base/share/classes/java/lang/foreign/AddressLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/AddressLayout.java @@ -26,7 +26,6 @@ package java.lang.foreign; import jdk.internal.foreign.layout.ValueLayouts; -import jdk.internal.javac.PreviewFeature; import jdk.internal.javac.Restricted; import jdk.internal.reflect.CallerSensitive; @@ -51,11 +50,13 @@ *

  • When creating an upcall stub, using {@link Linker#upcallStub(MethodHandle, FunctionDescriptor, Arena, Option...)}. * * + * @implSpec + * Implementations of this interface are immutable, thread-safe and value-based. + * * @see #ADDRESS * @see #ADDRESS_UNALIGNED - * @since 19 + * @since 22 */ -@PreviewFeature(feature = PreviewFeature.Feature.FOREIGN) public sealed interface AddressLayout extends ValueLayout permits ValueLayouts.OfAddressImpl { /** @@ -95,13 +96,12 @@ public sealed interface AddressLayout extends ValueLayout permits ValueLayouts.O * {@snippet lang = java: * AddressLayout addressLayout = ... * AddressLayout unboundedLayout = addressLayout.withTargetLayout( - * MemoryLayout.sequenceLayout(ValueLayout.JAVA_BYTE)); + * MemoryLayout.sequenceLayout(Long.MAX_VALUE, ValueLayout.JAVA_BYTE)); *} *

    * This method is restricted. * Restricted methods are unsafe, and, if used incorrectly, their use might crash - * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on - * restricted methods, and use safe and supported functionalities, where possible. + * the JVM or, worse, silently result in memory corruption. * * @param layout the target layout. * @return an address layout with same characteristics as this layout, but with the provided target layout. diff --git a/src/java.base/share/classes/java/lang/foreign/Arena.java b/src/java.base/share/classes/java/lang/foreign/Arena.java index 1f3c1adc16d..457847d76c6 100644 --- a/src/java.base/share/classes/java/lang/foreign/Arena.java +++ b/src/java.base/share/classes/java/lang/foreign/Arena.java @@ -26,7 +26,6 @@ package java.lang.foreign; import jdk.internal.foreign.MemorySessionImpl; -import jdk.internal.javac.PreviewFeature; import jdk.internal.ref.CleanerFactory; import java.lang.foreign.MemorySegment.Scope; @@ -184,7 +183,7 @@ * {@snippet lang = java: * try (Arena slicingArena = new SlicingArena(1000)) { * for (int i = 0; i < 10; i++) { - * MemorySegment s = slicingArena.allocateArray(JAVA_INT, 1, 2, 3, 4, 5); + * MemorySegment s = slicingArena.allocateFrom(JAVA_INT, 1, 2, 3, 4, 5); * ... * } * } // all memory allocated is released here @@ -195,9 +194,8 @@ * * @see MemorySegment * - * @since 20 + * @since 22 */ -@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) public interface Arena extends SegmentAllocator, AutoCloseable { /** @@ -269,9 +267,7 @@ static Arena ofShared() { * other than the arena's owner thread. */ @Override - default MemorySegment allocate(long byteSize, long byteAlignment) { - return ((MemorySessionImpl)scope()).allocate(byteSize, byteAlignment); - } + MemorySegment allocate(long byteSize, long byteAlignment); /** * {@return the arena scope} diff --git a/src/java.base/share/classes/java/lang/foreign/FunctionDescriptor.java b/src/java.base/share/classes/java/lang/foreign/FunctionDescriptor.java index 003eb7c3aa6..f715e4b91bd 100644 --- a/src/java.base/share/classes/java/lang/foreign/FunctionDescriptor.java +++ b/src/java.base/share/classes/java/lang/foreign/FunctionDescriptor.java @@ -32,7 +32,6 @@ import java.util.List; import jdk.internal.foreign.FunctionDescriptorImpl; -import jdk.internal.javac.PreviewFeature; /** * A function descriptor models the signature of a foreign function. A function descriptor is made up of zero or more @@ -44,9 +43,8 @@ * Implementing classes are immutable, thread-safe and value-based. * * @see MemoryLayout - * @since 19 + * @since 22 */ -@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) public sealed interface FunctionDescriptor permits FunctionDescriptorImpl { /** diff --git a/src/java.base/share/classes/java/lang/foreign/GroupLayout.java b/src/java.base/share/classes/java/lang/foreign/GroupLayout.java index 177f1f1d5fa..e6654fcc8a5 100644 --- a/src/java.base/share/classes/java/lang/foreign/GroupLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/GroupLayout.java @@ -26,7 +26,6 @@ package java.lang.foreign; import java.util.List; -import jdk.internal.javac.PreviewFeature; /** * A compound layout that is an aggregation of multiple, heterogeneous member layouts. There are two ways in which member layouts @@ -38,9 +37,8 @@ * This class is immutable, thread-safe and value-based. * * @sealedGraph - * @since 19 + * @since 22 */ -@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) public sealed interface GroupLayout extends MemoryLayout permits StructLayout, UnionLayout { /** diff --git a/src/java.base/share/classes/java/lang/foreign/Linker.java b/src/java.base/share/classes/java/lang/foreign/Linker.java index f9aecd4873b..9adca13b774 100644 --- a/src/java.base/share/classes/java/lang/foreign/Linker.java +++ b/src/java.base/share/classes/java/lang/foreign/Linker.java @@ -29,15 +29,12 @@ import jdk.internal.foreign.abi.LinkerOptions; import jdk.internal.foreign.abi.CapturableState; import jdk.internal.foreign.abi.SharedUtils; -import jdk.internal.javac.PreviewFeature; import jdk.internal.javac.Restricted; import jdk.internal.reflect.CallerSensitive; -import jdk.internal.reflect.Reflection; import java.lang.invoke.MethodHandle; -import java.nio.ByteOrder; +import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.Set; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -60,6 +57,12 @@ *

  • A linker allows foreign functions to call Java method handles, * via the generation of {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, Arena, Option...) upcall stubs}.
  • * + * A linker provides a way to look up the canonical layouts associated with the data types used by the ABI. + * For example, a linker implementing the C ABI might choose to provide a canonical layout for the C {@code size_t} + * type. On 64-bit platforms, this canonical layout might be equal to {@link ValueLayout#JAVA_LONG}. The canonical + * layouts supported by a linker are exposed via the {@link #canonicalLayouts()} method, which returns a map from + * type names to canonical layouts. + *

    * In addition, a linker provides a way to look up foreign functions in libraries that conform to the ABI. Each linker * chooses a set of libraries that are commonly used on the OS and processor combination associated with the ABI. * For example, a linker for Linux/x64 might choose two libraries: {@code libc} and {@code libm}. The functions in these @@ -93,10 +96,10 @@ * * {@snippet lang = java: * try (Arena arena = Arena.ofConfined()) { - * MemorySegment str = arena.allocateUtf8String("Hello"); + * MemorySegment str = arena.allocateFrom("Hello"); * long len = (long) strlen.invokeExact(str); // 5 * } - * } + *} *

    Describing C signatures

    * * When interacting with the native linker, clients must provide a platform-dependent description of the signature @@ -104,11 +107,8 @@ * defines the layouts associated with the parameter types and return type (if any) of the C function. *

    * Scalar C types such as {@code bool}, {@code int} are modelled as {@linkplain ValueLayout value layouts} - * of a suitable carrier. The mapping between a scalar type and its corresponding layout is dependent on the ABI - * implemented by the native linker. For instance, the C type {@code long} maps to the layout constant - * {@link ValueLayout#JAVA_LONG} on Linux/x64, but maps to the layout constant {@link ValueLayout#JAVA_INT} on - * Windows/x64. Similarly, the C type {@code size_t} maps to the layout constant {@link ValueLayout#JAVA_LONG} - * on 64-bit platforms, but maps to the layout constant {@link ValueLayout#JAVA_INT} on 32-bit platforms. + * of a suitable carrier. The {@linkplain #canonicalLayouts() mapping} between a scalar type and its corresponding + * canonical layout is dependent on the ABI implemented by the native linker (see below). *

    * Composite types are modelled as {@linkplain GroupLayout group layouts}. More specifically, a C {@code struct} type * maps to a {@linkplain StructLayout struct layout}, whereas a C {@code union} type maps to a {@link UnionLayout union @@ -123,7 +123,33 @@ * a pointer that is known to point to a C {@code int[2]} array can be modelled as an address layout whose * target layout is a sequence layout whose element count is 2, and whose element type is {@link ValueLayout#JAVA_INT}. *

    - * The following table shows some examples of how C types are modelled in Linux/x64: + * All native linker implementations are guaranteed to provide canonical layouts for the following set of types: + *

      + *
    • {@code bool}
    • + *
    • {@code char}
    • + *
    • {@code short}
    • + *
    • {@code int}
    • + *
    • {@code long}
    • + *
    • {@code long long}
    • + *
    • {@code float}
    • + *
    • {@code double}
    • + *
    • {@code size_t}
    • + *
    • {@code wchar_t}
    • + *
    • {@code void*}
    • + *
    + * As noted above, the specific canonical layout associated with each type can vary, depending on the data model + * supported by a given ABI. For instance, the C type {@code long} maps to the layout constant {@link ValueLayout#JAVA_LONG} + * on Linux/x64, but maps to the layout constant {@link ValueLayout#JAVA_INT} on Windows/x64. Similarly, the C type + * {@code size_t} maps to the layout constant {@link ValueLayout#JAVA_LONG} on 64-bit platforms, but maps to the layout + * constant {@link ValueLayout#JAVA_INT} on 32-bit platforms. + *

    + * A native linker typically does not provide canonical layouts for C's unsigned integral types. Instead, they are + * modelled using the canonical layouts associated with their corresponding signed integral types. For instance, + * the C type {@code unsigned long} maps to the layout constant {@link ValueLayout#JAVA_LONG} on Linux/x64, but maps to + * the layout constant {@link ValueLayout#JAVA_INT} on Windows/x64. + *

    + * The following table shows some examples of how C types are modelled in Linux/x64 according to the + * "System V Application Binary Interface" (all the examples provided here will assume these platform-dependent mappings): * *

    * @@ -138,19 +164,19 @@ * * * - * + * * * - * + * * * - * + * * * - * + * * * - * + * * * * @@ -201,20 +227,7 @@ * All native linker implementations operate on a subset of memory layouts. More formally, a layout {@code L} * is supported by a native linker {@code NL} if: *
      - *
    • {@code L} is a value layout {@code V} and {@code V.withoutName()} is {@linkplain MemoryLayout#equals(Object) equal} - * to one of the following layout constants: - *
        - *
      • {@link ValueLayout#JAVA_BOOLEAN}
      • - *
      • {@link ValueLayout#JAVA_BYTE}
      • - *
      • {@link ValueLayout#JAVA_CHAR}
      • - *
      • {@link ValueLayout#JAVA_SHORT}
      • - *
      • {@link ValueLayout#JAVA_INT}
      • - *
      • {@link ValueLayout#JAVA_LONG}
      • - *
      • {@link ValueLayout#JAVA_FLOAT}
      • - *
      • {@link ValueLayout#JAVA_DOUBLE}
      • - *
    • - *
    • {@code L} is an address layout {@code A} and {@code A.withoutTargetLayout().withoutName()} is - * {@linkplain MemoryLayout#equals(Object) equal} to {@link ValueLayout#ADDRESS}
    • + *
    • {@code L} is a value layout {@code V} and {@code V.withoutName()} is a canonical layout
    • *
    • {@code L} is a sequence layout {@code S} and all the following conditions hold: *
        *
      1. the alignment constraint of {@code S} is set to its natural alignment, and
      2. @@ -294,7 +307,7 @@ * {@snippet lang = java: * try (Arena arena = Arena.ofConfined()) { * MemorySegment comparFunc = linker.upcallStub(comparHandle, comparDesc, arena); - * MemorySegment array = arena.allocateArray(JAVA_INT, 0, 9, 3, 4, 6, 5, 1, 8, 2, 7); + * MemorySegment array = arena.allocateFrom(JAVA_INT, 0, 9, 3, 4, 6, 5, 1, 8, 2, 7); * qsort.invokeExact(array, 10L, 4L, comparFunc); * int[] sorted = array.toArray(JAVA_INT); // [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] * } @@ -391,15 +404,11 @@ * *

        Variadic functions

        * - * Variadic functions are C functions which can accept a variable number and type of arguments. They are declared: - *
          - *
        1. With a trailing ellipsis ({@code ...}) at the end of the formal parameter list, such as: {@code void foo(int x, ...);}
        2. - *
        3. With an empty formal parameter list, called a prototype-less function, such as: {@code void foo();}
        4. - *
        - * The arguments passed in place of the ellipsis, or the arguments passed to a prototype-less function are called - * variadic arguments. Variadic functions are, essentially, templates that can be specialized into multiple - * non-variadic functions by replacing the {@code ...} or empty formal parameter list with a list of variadic parameters - * of a fixed number and type. + * Variadic functions are C functions which can accept a variable number and type of arguments. They are declared with a + * trailing ellipsis ({@code ...}) at the end of the formal parameter list, such as: {@code void foo(int x, ...);} + * The arguments passed in place of the ellipsis are called variadic arguments. Variadic functions are, + * essentially, templates that can be specialized into multiple non-variadic functions by replacing the + * {@code ...} with a list of variadic parameters of a fixed number and type. *

        * It should be noted that values passed as variadic arguments undergo default argument promotion in C. For instance, the * following argument promotions are applied: @@ -411,21 +420,22 @@ *

    * whereby the signed-ness of the source type corresponds to the signed-ness of the promoted type. The complete process * of default argument promotion is described in the C specification. In effect these promotions place limits on the - * specialized form of a variadic function, as the variadic parameters of the specialized form will always have a promoted - * type. + * types that can be used to replace the {@code ...}, as the variadic parameters of the specialized form of a variadic + * function will always have a promoted type. *

    * The native linker only supports linking the specialized form of a variadic function. A variadic function in its specialized * form can be linked using a function descriptor describing the specialized form. Additionally, the * {@link Linker.Option#firstVariadicArg(int)} linker option must be provided to indicate the first variadic parameter in * the parameter list. The corresponding argument layout (if any), and all following argument layouts in the specialized - * function descriptor, are called variadic argument layouts. For a prototype-less function, the index passed to - * {@link Linker.Option#firstVariadicArg(int)} should always be {@code 0}. + * function descriptor, are called variadic argument layouts. *

    - * The native linker will reject an attempt to link a specialized function descriptor with any variadic argument layouts - * corresponding to a C type that would be subject to default argument promotion (as described above). Exactly which layouts - * will be rejected is platform specific, but as an example: on Linux/x64 the layouts {@link ValueLayout#JAVA_BOOLEAN}, - * {@link ValueLayout#JAVA_BYTE}, {@link ValueLayout#JAVA_CHAR}, {@link ValueLayout#JAVA_SHORT}, and - * {@link ValueLayout#JAVA_FLOAT} will be rejected. + * The native linker does not automatically perform default argument promotions. However, since passing an argument of a + * non-promoted type as a variadic argument is not supported in C, the native linker will reject an attempt to link a + * specialized function descriptor with any variadic argument value layouts corresponding to a non-promoted C type. + * Since the size of the C {@code int} type is platform-specific, exactly which layouts will be rejected is + * platform-specific as well. As an example: on Linux/x64 the layouts corresponding to the C types {@code _Bool}, + * {@code (unsigned) char}, {@code (unsigned) short}, and {@code float} (among others), will be rejected by the linker. + * The {@link #canonicalLayouts()} method can be used to find which layout corresponds to a particular C type. *

    * A well-known variadic function is the {@code printf} function, defined in the C standard library: * @@ -461,9 +471,9 @@ * * {@snippet lang = java: * try (Arena arena = Arena.ofConfined()) { - * int res = (int)printf.invokeExact(arena.allocateUtf8String("%d plus %d equals %d"), 2, 2, 4); //prints "2 plus 2 equals 4" - * } + * int res = (int)printf.invokeExact(arena.allocateFrom("%d plus %d equals %d"), 2, 2, 4); //prints "2 plus 2 equals 4" * } + *} * *

    Safety considerations

    * @@ -483,9 +493,8 @@ * @implSpec * Implementations of this interface are immutable, thread-safe and value-based. * - * @since 19 + * @since 22 */ -@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) public sealed interface Linker permits AbstractLinker { /** @@ -493,11 +502,11 @@ public sealed interface Linker permits AbstractLinker { * is the combination of OS and processor where the Java runtime is currently executing. * * @apiNote It is not currently possible to obtain a linker for a different combination of OS and processor. + * @implSpec A native linker implementation is guaranteed to provide canonical layouts for + * basic C types. * @implNote The libraries exposed by the {@linkplain #defaultLookup() default lookup} associated with the returned * linker are the native libraries loaded in the process where the Java runtime is currently executing. For example, * on Linux, these libraries typically include {@code libc}, {@code libm} and {@code libdl}. - * - * @throws UnsupportedOperationException if the underlying native platform is not supported. */ static Linker nativeLinker() { return SharedUtils.getSystemLinker(); @@ -513,8 +522,7 @@ static Linker nativeLinker() { *

    * This method is restricted. * Restricted methods are unsafe, and, if used incorrectly, their use might crash - * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on - * restricted methods, and use safe and supported functionalities, where possible. + * the JVM or, worse, silently result in memory corruption. * * @param address the native memory segment whose {@linkplain MemorySegment#address() base address} is the * address of the target foreign function. @@ -560,13 +568,17 @@ static Linker nativeLinker() { * {@code T}, then the size of the returned segment is set to {@code T.byteSize()}. *

    * The returned method handle will throw an {@link IllegalArgumentException} if the {@link MemorySegment} - * representing the target address of the foreign function is the {@link MemorySegment#NULL} address. - * The returned method handle will additionally throw {@link NullPointerException} if any argument passed to it is {@code null}. + * representing the target address of the foreign function is the {@link MemorySegment#NULL} address. If an argument + * is a {@link MemorySegment}, whose corresponding layout is a {@linkplain GroupLayout group layout}, the linker + * might attempt to access the contents of the segment. As such, one of the exceptions specified by the + * {@link MemorySegment#get(ValueLayout.OfByte, long)} or the + * {@link MemorySegment#copy(MemorySegment, long, MemorySegment, long, long)} methods may be thrown. + * The returned method handle will additionally throw {@link NullPointerException} if any argument + * passed to it is {@code null}. *

    * This method is restricted. * Restricted methods are unsafe, and, if used incorrectly, their use might crash - * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on - * restricted methods, and use safe and supported functionalities, where possible. + * the JVM or, worse, silently result in memory corruption. * * @param function the function descriptor of the target foreign function. * @param options the linker options associated with this linkage request. @@ -603,8 +615,7 @@ static Linker nativeLinker() { *

    * This method is restricted. * Restricted methods are unsafe, and, if used incorrectly, their use might crash - * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on - * restricted methods, and use safe and supported functionalities, where possible. + * the JVM or, worse, silently result in memory corruption. * * @param target the target method handle. * @param function the upcall stub function descriptor. @@ -639,11 +650,26 @@ static Linker nativeLinker() { */ SymbolLookup defaultLookup(); + /** + * {@return an unmodifiable mapping between the names of data types used by the ABI implemented by this linker and their + * canonical layouts} + *

    + * Each {@link Linker} is responsible for choosing the data types that are widely recognized as useful on the OS + * and processor combination supported by the {@link Linker}. Accordingly, the precise set of data type names + * and canonical layouts exposed by the linker is unspecified; it varies from one {@link Linker} to another. + * @implNote It is strongly recommended that the result of {@link #canonicalLayouts()} exposes a set of symbols that is stable over time. + * Clients of {@link #canonicalLayouts()} are likely to fail if a data type that was previously exposed by the linker + * is no longer exposed, or if its canonical layout is updated. + *

    If an implementer provides {@link Linker} implementations for multiple OS and processor combinations, then it is strongly + * recommended that the result of {@link #canonicalLayouts()} exposes, as much as possible, a consistent set of symbols + * across all the OS and processor combinations. + */ + Map canonicalLayouts(); + /** * A linker option is used to provide additional parameters to a linkage request. - * @since 20 + * @since 22 */ - @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) sealed interface Option permits LinkerOptions.LinkerOptionImpl { @@ -707,6 +733,8 @@ static Option firstVariadicArg(int index) { * // use errno * } * } + *

    + * This linker option can not be combined with {@link #critical}. * * @param capturedState the names of the values to save. * @throws IllegalArgumentException if at least one of the provided {@code capturedState} names @@ -750,19 +778,19 @@ static StructLayout captureStateLayout() { } /** - * {@return a linker option used to mark a foreign function as trivial} + * {@return a linker option used to mark a foreign function as critical} *

    - * A trivial function is a function that has an extremely short running time - * in all cases (similar to calling an empty function), and does not call back into Java (e.g. using an upcall stub). + * A critical function is a function that has an extremely short running time in all cases + * (similar to calling an empty function), and does not call back into Java (e.g. using an upcall stub). *

    * Using this linker option is a hint which some implementations may use to apply - * optimizations that are only valid for trivial functions. + * optimizations that are only valid for critical functions. *

    - * Using this linker option when linking non trivial functions is likely to have adverse effects, + * Using this linker option when linking non-critical functions is likely to have adverse effects, * such as loss of performance, or JVM crashes. */ - static Option isTrivial() { - return LinkerOptions.IsTrivial.INSTANCE; + static Option critical() { + return LinkerOptions.Critical.INSTANCE; } } } diff --git a/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java b/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java index c36bb2c3454..db656ada136 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java @@ -26,13 +26,9 @@ package java.lang.foreign; import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; -import java.util.EnumSet; import java.util.Objects; import java.util.Optional; -import java.util.Set; -import java.util.function.Function; import java.util.stream.Stream; import jdk.internal.foreign.LayoutPath; @@ -43,12 +39,11 @@ import jdk.internal.foreign.layout.SequenceLayoutImpl; import jdk.internal.foreign.layout.StructLayoutImpl; import jdk.internal.foreign.layout.UnionLayoutImpl; -import jdk.internal.javac.PreviewFeature; /** * A memory layout describes the contents of a memory segment. *

    - * There are two leaves in the layout hierarchy, {@linkplain ValueLayout value layouts}, which are used to represent values of given size and kind (see + * There are two leaves in the layout hierarchy, {@linkplain ValueLayout value layouts}, which are used to represent values of given size and kind * and {@linkplain PaddingLayout padding layouts} which are used, as the name suggests, to represent a portion of a memory * segment whose contents should be ignored, and which are primarily present for alignment reasons. * Some common value layout constants, such as {@link ValueLayout#JAVA_INT} and {@link ValueLayout#JAVA_FLOAT_UNALIGNED} @@ -76,7 +71,7 @@ * The above declaration can be modelled using a layout object, as follows: * * {@snippet lang=java : - * SequenceLayout taggedValues = MemoryLayout.sequenceLayout(5, + * SequenceLayout TAGGED_VALUES = MemoryLayout.sequenceLayout(5, * MemoryLayout.structLayout( * ValueLayout.JAVA_BYTE.withName("kind"), * MemoryLayout.paddingLayout(3), @@ -131,13 +126,13 @@ * For instance, given the {@code taggedValues} sequence layout constructed above, we can obtain the offset, * in bytes, of the member layout named value in the first sequence element, as follows: * {@snippet lang=java : - * long valueOffset = taggedValues.byteOffset(PathElement.sequenceElement(0), + * long valueOffset = TAGGED_VALUES.byteOffset(PathElement.sequenceElement(0), * PathElement.groupElement("value")); // yields 4 * } * * Similarly, we can select the member layout named {@code value}, as follows: * {@snippet lang=java : - * MemoryLayout value = taggedValues.select(PathElement.sequenceElement(), + * MemoryLayout value = TAGGED_VALUES.select(PathElement.sequenceElement(), * PathElement.groupElement("value")); * } * @@ -150,10 +145,13 @@ * the open elements in the path: * * {@snippet lang=java : - * VarHandle valueHandle = taggedValues.varHandle(PathElement.sequenceElement(), + * VarHandle valueHandle = TAGGED_VALUES.varHandle(PathElement.sequenceElement(), * PathElement.groupElement("value")); - * MemorySegment valuesSegment = ... - * int val = (int) valueHandle.get(valuesSegment, 2); // reads the "value" field of the third struct in the array + * MemorySegment taggedValues = ... + * // reads the "value" field of the third struct in the array (taggedValues[2].value) + * int val = (int) valueHandle.get(taggedValues, + * 0L, // base offset + * 2L); // sequence index * } * *

    @@ -163,10 +161,10 @@ * of the sequence element whose offset is to be computed: * * {@snippet lang=java : - * MethodHandle offsetHandle = taggedValues.byteOffsetHandle(PathElement.sequenceElement(), + * MethodHandle offsetHandle = TAGGED_VALUES.byteOffsetHandle(PathElement.sequenceElement(), * PathElement.groupElement("kind")); - * long offset1 = (long) offsetHandle.invokeExact(1L); // 8 - * long offset2 = (long) offsetHandle.invokeExact(2L); // 16 + * long offset1 = (long) offsetHandle.invokeExact(0L, 1L); // 0 + (1 * 8) = 8 + * long offset2 = (long) offsetHandle.invokeExact(0L, 2L); // 0 + (2 * 8) = 16 * } * *

    Dereference path elements

    @@ -204,7 +202,10 @@ * ); * * MemorySegment rect = ... - * int rect_y_4 = (int) rectPointYs.get(rect, 2); // rect.points[2]->y + * // dereferences the third point struct in the "points" array, and reads its "y" coordinate (rect.points[2]->y) + * int rect_y_2 = (int) rectPointYs.get(rect, + * 0L, // base offset + * 2L); // sequence index * } * *

    Layout path well-formedness

    @@ -231,13 +232,110 @@ * Any attempt to provide a layout path {@code P} that is not well-formed for an initial layout {@code C_0} will result * in an {@link IllegalArgumentException}. * + *

    Access mode restrictions

    + * + * A var handle returned by {@link #varHandle(PathElement...)} or {@link ValueLayout#varHandle()} features certain + * access characteristics, which are derived from the selected layout {@code L}: + *
      + *
    • A carrier type {@code T}, derived from {@code L.carrier()}
    • + *
    • An alignment constraint {@code A}, derived from {@code L.byteAlignment()}
    • + *
    • An access size {@code S}, derived from {@code L.byteSize()}
    • + *
    + * Depending on the above characteristics, the returned var handle might feature certain access mode restrictions. + * We say that a var handle is aligned if its alignment constraint {@code A} is compatible with the access size + * {@code S}, that is if {@code A >= S}. An aligned var handle is guaranteed to support the following access modes: + *
      + *
    • read write access modes for all {@code T}. On 32-bit platforms, access modes + * {@code get} and {@code set} for {@code long}, {@code double} and {@code MemorySegment} + * are supported but might lead to word tearing, as described in Section {@jls 17.7}. + * of The Java Language Specification. + *
    • atomic update access modes for {@code int}, {@code long}, + * {@code float}, {@code double} and {@link MemorySegment}. + * (Future major platform releases of the JDK may support additional + * types for certain currently unsupported access modes.) + *
    • numeric atomic update access modes for {@code int}, {@code long} and {@link MemorySegment}. + * (Future major platform releases of the JDK may support additional + * numeric types for certain currently unsupported access modes.) + *
    • bitwise atomic update access modes for {@code int}, {@code long} and {@link MemorySegment}. + * (Future major platform releases of the JDK may support additional + * numeric types for certain currently unsupported access modes.) + *
    + * If {@code T} is {@code float}, {@code double} or {@link MemorySegment} then atomic update access modes compare + * values using their bitwise representation (see {@link Float#floatToRawIntBits}, {@link Double#doubleToRawLongBits} + * and {@link MemorySegment#address()}, respectively). + *

    + * Alternatively, a var handle is unaligned if its alignment constraint {@code A} is incompatible with the + * access size {@code S}, that is, if {@code A < S}. An unaligned var handle only supports the {@code get} and {@code set} + * access modes. All other access modes will result in {@link UnsupportedOperationException} being thrown. Moreover, + * while supported, access modes {@code get} and {@code set} might lead to word tearing. + * + *

    Working with variable-length structs

    + * + * Memory layouts allow clients to describe the contents of a region of memory whose size is known statically. + * There are, however, cases, where the size of a region of memory is only known dynamically, as it depends + * on the value of one or more struct fields. Consider the following struct declaration in C: + * + * {@snippet lang=c : + * typedef struct { + * int size; + * struct { + * int x; + * int y; + * } points[]; + * } Polygon; + * } + * + * In the above code, a polygon is modelled as a size (the number of edges in the polygon) and an array of points + * (one for each vertex in the polygon). The number of vertices depends on the number of edges in the polygon. As such, + * the size of the {@code points} array is left unspecified in the C declaration, using a Flexible Array Member + * (a feature standardized in C99). + *

    + * Memory layouts do not support sequence layouts whose size is unknown. As such, it is not possible to model + * the above struct directly. That said, clients can still enjoy structured access provided by memory layouts, as + * demonstrated below: + * + * {@snippet lang=java : + * StructLayout POINT = MemoryLayout.structLayout( + * ValueLayout.JAVA_INT.withName("x"), + * ValueLayout.JAVA_INT.withName("y") + * ); + * + * StructLayout POLYGON = MemoryLayout.structLayout( + * ValueLayout.JAVA_INT.withName("size"), + * MemoryLayout.sequenceLayout(0, POINT).withName("points") + * ); + * + * VarHandle POLYGON_SIZE = POLYGON.varHandle(0, PathElement.groupElement("size")); + * VarHandle POINT_X = POINT.varHandle(PathElement.groupElement("x")); + * long POINTS_OFFSET = POLYGON.byteOffset(PathElement.groupElement("points")); + * } + * + * Note how we have split the polygon struct in two. The {@code POLYGON} layout contains a sequence layout + * of size zero. The element layout of the sequence layout is the {@code POINT} layout, which defines + * the {@code x} and {@code y} coordinates, accordingly. The first layout is used to obtain a var handle + * that provides access to the polygon size; the second layout is used to obtain a var handle that provides + * access to the {@code x} coordinate of a point struct. Finally, an offset to the start of the variable-length + * {@code points} array is also obtained. + *

    + * The {@code x} coordinates of all the points in a polygon can then be accessed as follows: + * {@snippet lang=java : + * MemorySegment polygon = ... + * int size = POLYGON_SIZE.get(polygon, 0L); + * for (int i = 0 ; i < size ; i++) { + * int x = POINT_X.get(polygon, POINT.scaleOffset(POINTS_OFFSET, i)); + * } + * } + * Here, we first obtain the polygon size, using the {@code POLYGON_SIZE} var handle. Then, in a loop, we read + * the {@code x} coordinates of all the points in the polygon. This is done by providing a custom base offset to + * the {@code POINT_X} var handle. The custom offset is computed as {@code POINTS_OFFSET + (i * POINT.byteSize())}, where + * {@code i} is the loop induction variable. + * * @implSpec * Implementations of this interface are immutable, thread-safe and value-based. * * @sealedGraph - * @since 19 + * @since 22 */ -@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, PaddingLayout, ValueLayout { /** @@ -292,6 +390,21 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin */ MemoryLayout withByteAlignment(long byteAlignment); + /** + * {@return {@code offset + (byteSize() * index)}} + * + * @param offset the base offset + * @param index the index to be scaled by the byte size of this layout + * @throws IllegalArgumentException if {@code offset} or {@code index} is negative + * @throws ArithmeticException if either the addition or multiplication overflows + */ + long scale(long offset, long index); + + /** + *{@return a method handle that can be used to invoke {@link #scale(long, long)} on this layout} + */ + MethodHandle scaleHandle(); + /** * Computes the offset, in bytes, of the layout selected by the given layout path, where the initial layout in the * path is this layout. @@ -302,10 +415,7 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin * @throws IllegalArgumentException if the layout path contains one or more open path elements. * @throws IllegalArgumentException if the layout path contains one or more dereference path elements. */ - default long byteOffset(PathElement... elements) { - return computePathOp(LayoutPath.rootPath(this), LayoutPath::offset, - EnumSet.of(PathKind.SEQUENCE_ELEMENT, PathKind.SEQUENCE_RANGE, PathKind.DEREF_ELEMENT), elements); - } + long byteOffset(PathElement... elements); /** * Creates a method handle that computes the offset, in bytes, of the layout selected @@ -314,7 +424,8 @@ default long byteOffset(PathElement... elements) { * The returned method handle has the following characteristics: *

      *
    • its return type is {@code long};
    • - *
    • it has as zero or more parameters of type {@code long}, one for each open path element + *
    • it has one leading {@code long} parameter representing the base offset;
    • + *
    • it has as zero or more trailing parameters of type {@code long}, one for each open path element * in the provided layout path. The order of these parameters corresponds to the order in which the open path * elements occur in the provided layout path. *
    @@ -322,13 +433,14 @@ default long byteOffset(PathElement... elements) { * The final offset returned by the method handle is computed as follows: * *
    {@code
    -     * offset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
    +     * offset = b + c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
          * }
    * - * where {@code x_1}, {@code x_2}, ... {@code x_n} are dynamic values provided as {@code long} - * arguments, whereas {@code c_1}, {@code c_2}, ... {@code c_m} are static offset constants - * and {@code s_0}, {@code s_1}, ... {@code s_n} are static stride constants which are derived from - * the layout path. + * where {@code b} represents the base offset provided as a dynamic {@code long} argument, {@code x_1}, {@code x_2}, + * ... {@code x_n} represent indices into sequences provided as dynamic {@code long} arguments, whereas + * {@code s_1}, {@code s_2}, ... {@code s_n} are static stride constants derived from the size of the element + * layout of a sequence, and {@code c_1}, {@code c_2}, ... {@code c_m} are other static offset constants + * (such as field offsets) which are derived from the layout path. * * @apiNote The returned method handle can be used to compute a layout offset, similarly to {@link #byteOffset(PathElement...)}, * but more flexibly, as some indices can be specified when invoking the method handle. @@ -338,10 +450,7 @@ default long byteOffset(PathElement... elements) { * @throws IllegalArgumentException if the layout path is not well-formed for this layout. * @throws IllegalArgumentException if the layout path contains one or more dereference path elements. */ - default MethodHandle byteOffsetHandle(PathElement... elements) { - return computePathOp(LayoutPath.rootPath(this), LayoutPath::offsetHandle, - EnumSet.of(PathKind.DEREF_ELEMENT), elements); - } + MethodHandle byteOffsetHandle(PathElement... elements); /** * Creates a var handle that accesses a memory segment at the offset selected by the given layout path, @@ -351,74 +460,89 @@ default MethodHandle byteOffsetHandle(PathElement... elements) { *
      *
    • its type is derived from the {@linkplain ValueLayout#carrier() carrier} of the * selected value layout;
    • - *
    • it has as zero or more access coordinates of type {@code long}, one for each - * open path element in the provided layout path. The order of these access - * coordinates corresponds to the order in which the open path elements occur in the provided - * layout path. + *
    • it has a leading parameter of type {@code MemorySegment} representing the accessed segment
    • + *
    • a following {@code long} parameter, corresponding to the base offset, denoted as {@code B};
    • + *
    • it has zero or more trailing access coordinates of type {@code long}, one for each + * open path element in the provided layout path, denoted as + * {@code I1, I2, ... In}, respectively. The order of these access coordinates corresponds to the order + * in which the open path elements occur in the provided layout path. *
    *

    - * The final address accessed by the returned var handle can be computed as follows: - * - *

    {@code
    -     * address = base(segment) + offset
    -     * }
    + * If the provided layout path {@code P} contains no dereference elements, then the offset {@code O} of the access + * operation is computed as follows: * - * Where {@code base(segment)} denotes a function that returns the physical base address of the accessed - * memory segment. For native segments, this function just returns the native segment's - * {@linkplain MemorySegment#address() address}. For heap segments, this function is more complex, as the address - * of heap segments is virtualized. The {@code offset} value can be expressed in the following form: - * - *
    {@code
    -     * offset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
    -     * }
    - * - * where {@code x_1}, {@code x_2}, ... {@code x_n} are dynamic values provided as {@code long} - * arguments, whereas {@code c_1}, {@code c_2}, ... {@code c_m} are static offset constants - * and {@code s_1}, {@code s_2}, ... {@code s_n} are static stride constants which are derived from - * the layout path. + * {@snippet lang = "java": + * O = this.offsetHandle(P).invokeExact(B, I1, I2, ... In); + * } *

    - * Additionally, the provided dynamic values must conform to bounds which are derived from the layout path, that is, - * {@code 0 <= x_i < b_i}, where {@code 1 <= i <= n}, or {@link IndexOutOfBoundsException} is thrown. + * Accessing a memory segment using the var handle returned by this method is subject to the following checks: + *

      + *
    • The physical address of the accessed memory segment must be aligned + * according to the {@linkplain #byteAlignment() alignment constraint} of the root layout (this layout), or + * an {@link IllegalArgumentException} will be issued. Note that the alignment constraint of the root layout + * can be more strict (but not less) than the alignment constraint of the selected value layout.
    • + *
    • The offset of the access operation (computed as above) must fall inside the spatial bounds of the + * accessed memory segment, or an {@link IndexOutOfBoundsException} is thrown. This is the case when {@code O + A <= S}, + * where {@code O} is the accessed offset (computed as above), {@code A} is the size of the selected layout and {@code S} + * is the size of the accessed memory segment.
    • + *
    • The accessed memory segment must be {@link MemorySegment#isAccessibleBy(Thread) accessible} from the + * thread performing the access operation, or a {@link WrongThreadException} is thrown.
    • + *
    • The {@linkplain MemorySegment#scope() scope} associated with the accessed segment must be + * {@linkplain MemorySegment.Scope#isAlive() alive}, or an {@link IllegalStateException} is thrown.
    • + *
    *

    - * The base address must be aligned according to the {@linkplain - * #byteAlignment() alignment constraint} of the root layout (this layout). Note that this can be more strict - * (but not less) than the alignment constraint of the selected value layout. + * If the selected layout is an {@linkplain AddressLayout address layout}, calling {@link VarHandle#get(Object...)} + * on the returned var handle will return a new memory segment. The segment is associated with a fresh scope that is + * always alive. Moreover, the size of the segment depends on whether the address layout has a + * {@linkplain AddressLayout#targetLayout() target layout}. More specifically: + *

      + *
    • If the address layout has a target layout {@code T}, then the size of the returned segment + * is {@code T.byteSize()};
    • + *
    • Otherwise, the address layout has no target layout, and the size of the returned segment + * is zero.
    • + *
    + * Moreover, if the selected layout is an {@linkplain AddressLayout address layout}, calling {@link VarHandle#set(Object...)} + * can throw {@link IllegalArgumentException} if the memory segment representing the address to be written is not a + * {@linkplain MemorySegment#isNative() native} memory segment. *

    - * Multiple paths can be chained, with dereference path elements. - * A dereference path element constructs a fresh native memory segment whose base address is the address value - * read obtained by accessing a memory segment at the offset determined by the layout path elements immediately preceding - * the dereference path element. In other words, if a layout path contains one or more dereference path elements, - * the final address accessed by the returned var handle can be computed as follows: - * - *

    {@code
    -     * address_1 = base(segment) + offset_1
    -     * address_2 = base(segment_1) + offset_2
    -     * ...
    -     * address_k = base(segment_k-1) + offset_k
    -     * }
    + * If the provided layout path has size {@code m} and contains a dereference path element in position {@code k} + * (where {@code k <= m}) then two layout paths {@code P} and {@code P'} are derived, where P contains all the path + * elements from 0 to {@code k - 1} and {@code P'} contains all the path elements from {@code k + 1} to + * {@code m} (if any). Then, the returned var handle is computed as follows: + * + * {@snippet lang = "java": + * VarHandle baseHandle = this.varHandle(P); + * MemoryLayout target = ((AddressLayout)this.select(P)).targetLayout().get(); + * VarHandle targetHandle = target.varHandle(P'); + * targetHandle = MethodHandles.insertCoordinates(targetHandle, 1, 0L); // always access nested targets at offset 0 + * targetHandle = MethodHandles.collectCoordinates(targetHandle, 0, + * baseHandle.toMethodHandle(VarHandle.AccessMode.GET)); + * } * - * where {@code k} is the number of dereference path elements in a layout path, {@code segment} is the input segment, - * {@code segment_1}, ... {@code segment_k-1} are the segments obtained by dereferencing the address associated with - * a given dereference path element (e.g. {@code segment_1} is a native segment whose base address is {@code address_1}), - * and {@code offset_1}, {@code offset_2}, ... {@code offset_k} are the offsets computed by evaluating - * the path elements after a given dereference operation (these offsets are obtained using the computation described - * above). In these more complex access operations, all memory accesses immediately preceding a dereference operation - * (e.g. those at addresses {@code address_1}, {@code address_2}, ..., {@code address_k-1} are performed using the - * {@link VarHandle.AccessMode#GET} access mode. + * (The above can be trivially generalized to cases where the provided layout path contains more than one dereference + * path elements). + *

    + * As an example, consider the memory layout expressed by a {@link GroupLayout} instance constructed as follows: + * {@snippet lang = "java": + * GroupLayout grp = java.lang.foreign.MemoryLayout.structLayout( + * MemoryLayout.paddingLayout(4), + * ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN).withName("value") + * ); + * } + * To access the member layout named {@code value}, we can construct a var handle as follows: + * {@snippet lang = "java": + * VarHandle handle = grp.varHandle(PathElement.groupElement("value")); //(MemorySegment, long) -> int + * } * - * @apiNote The resulting var handle features certain access mode restrictions, which are common to all - * {@linkplain MethodHandles#memorySegmentViewVarHandle(ValueLayout) memory segment view handles}. + * @apiNote The resulting var handle features certain access mode restrictions, + * which are common to all var handles derived from memory layouts. * * @param elements the layout path elements. * @return a var handle that accesses a memory segment at the offset selected by the given layout path. * @throws IllegalArgumentException if the layout path is not well-formed for this layout. * @throws IllegalArgumentException if the layout selected by the provided path is not a {@linkplain ValueLayout value layout}. - * @see MethodHandles#memorySegmentViewVarHandle(ValueLayout) */ - default VarHandle varHandle(PathElement... elements) { - return computePathOp(LayoutPath.rootPath(this), LayoutPath::dereferenceHandle, - Set.of(), elements); - } + VarHandle varHandle(PathElement... elements); /** * Creates a method handle which, given a memory segment, returns a {@linkplain MemorySegment#asSlice(long,long) slice} @@ -427,23 +551,27 @@ default VarHandle varHandle(PathElement... elements) { * The returned method handle has the following characteristics: *

      *
    • its return type is {@code MemorySegment};
    • - *
    • it has a leading parameter of type {@code MemorySegment}, corresponding to the memory segment - * to be sliced;
    • - *
    • it has as zero or more parameters of type {@code long}, one for each open path element + *
    • it has a leading parameter of type {@code MemorySegment} corresponding to the memory segment to be sliced
    • + *
    • a following {@code long} parameter, corresponding to the base offset
    • + *
    • it has as zero or more trailing parameters of type {@code long}, one for each open path element * in the provided layout path. The order of these parameters corresponds to the order in which the open path * elements occur in the provided layout path. *
    *

    - * The offset of the returned segment is computed as follows: - * {@snippet lang=java : - * long offset = byteOffset(elements); - * long size = select(elements).byteSize(); - * MemorySegment slice = segment.asSlice(offset, size); - * } + * The offset {@code O} of the returned segment is computed as if by a call to a + * {@linkplain #byteOffsetHandle(PathElement...) byte offset handle} constructed using the given path elements. *

    - * The segment to be sliced must be aligned according to the - * {@linkplain #byteAlignment() alignment constraint} of the root layout (this layout). Note that this can be more - * strict (but not less) than the alignment constraint of the selected value layout. + * Computing a slice of a memory segment using the method handle returned by this method is subject to the following checks: + *

      + *
    • The physical address of the accessed memory segment must be aligned + * according to the {@linkplain #byteAlignment() alignment constraint} of the root layout (this layout), or + * an {@link IllegalArgumentException} will be issued. Note that the alignment constraint of the root layout + * can be more strict (but not less) than the alignment constraint of the selected layout.
    • + *
    • The start offset of the slicing operation (computed as above) must fall fall inside the spatial bounds of the + * accessed memory segment, or an {@link IndexOutOfBoundsException} is thrown. This is the case when {@code O + A <= S}, + * where {@code O} is the start offset of the slicing operation (computed as above), {@code A} is the size of the + * selected layout and {@code S} is the size of the accessed memory segment.
    • + *
    * * @apiNote The returned method handle can be used to obtain a memory segment slice, similarly to {@link MemorySegment#asSlice(long, long)}, * but more flexibly, as some indices can be specified when invoking the method handle. @@ -453,10 +581,7 @@ default VarHandle varHandle(PathElement... elements) { * @throws IllegalArgumentException if the layout path is not well-formed for this layout. * @throws IllegalArgumentException if the layout path contains one or more dereference path elements. */ - default MethodHandle sliceHandle(PathElement... elements) { - return computePathOp(LayoutPath.rootPath(this), LayoutPath::sliceHandle, - Set.of(PathKind.DEREF_ELEMENT), elements); - } + MethodHandle sliceHandle(PathElement... elements); /** * Returns the layout selected from the provided path, where the initial layout in the path is this layout. @@ -468,23 +593,7 @@ default MethodHandle sliceHandle(PathElement... elements) { * @throws IllegalArgumentException if the layout path contains one or more path elements that select one or more * sequence element indices, such as {@link PathElement#sequenceElement(long)} and {@link PathElement#sequenceElement(long, long)}). */ - default MemoryLayout select(PathElement... elements) { - return computePathOp(LayoutPath.rootPath(this), LayoutPath::layout, - EnumSet.of(PathKind.SEQUENCE_ELEMENT_INDEX, PathKind.SEQUENCE_RANGE, PathKind.DEREF_ELEMENT), elements); - } - - private static Z computePathOp(LayoutPath path, Function finalizer, - Set badKinds, PathElement... elements) { - Objects.requireNonNull(elements); - for (PathElement e : elements) { - LayoutPath.PathElementImpl pathElem = (LayoutPath.PathElementImpl)Objects.requireNonNull(e); - if (badKinds.contains(pathElem.kind())) { - throw new IllegalArgumentException(String.format("Invalid %s selection in layout path", pathElem.kind().description())); - } - path = pathElem.apply(path); - } - return finalizer.apply(path); - } + MemoryLayout select(PathElement... elements); /** * An element in a layout path. There @@ -501,9 +610,8 @@ private static Z computePathOp(LayoutPath path, Function fina * @implSpec * Implementations of this interface are immutable, thread-safe and value-based. * - * @since 19 + * @since 22 */ - @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) sealed interface PathElement permits LayoutPath.PathElementImpl { /** @@ -669,24 +777,6 @@ static SequenceLayout sequenceLayout(long elementCount, MemoryLayout elementLayo SequenceLayoutImpl.of(elementCount, elementLayout)); } - /** - * Creates a sequence layout with the given element layout and the maximum element - * count such that it does not overflow a {@code long}. - * - * This is equivalent to the following code: - * {@snippet lang = java: - * sequenceLayout(Long.MAX_VALUE / elementLayout.byteSize(), elementLayout); - * } - * - * @param elementLayout the sequence element layout. - * @return a new sequence layout with the given element layout and maximum element count. - * @throws IllegalArgumentException if {@code elementLayout.byteSize() % elementLayout.byteAlignment() != 0}. - */ - static SequenceLayout sequenceLayout(MemoryLayout elementLayout) { - Objects.requireNonNull(elementLayout); - return sequenceLayout(Long.MAX_VALUE / elementLayout.byteSize(), elementLayout); - } - /** * Creates a struct layout with the given member layouts. * diff --git a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java index f648354df60..5565eaf2461 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java +++ b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java @@ -26,15 +26,16 @@ package java.lang.foreign; import java.io.UncheckedIOException; -import java.lang.foreign.Linker.Option; +import java.lang.foreign.ValueLayout.OfInt; import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.CharBuffer; import java.nio.channels.FileChannel; -import java.nio.channels.FileChannel.*; +import java.nio.channels.FileChannel.MapMode; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Objects; @@ -43,13 +44,10 @@ import java.util.function.Consumer; import java.util.stream.Stream; import jdk.internal.foreign.AbstractMemorySegmentImpl; -import jdk.internal.foreign.HeapMemorySegmentImpl; import jdk.internal.foreign.MemorySessionImpl; -import jdk.internal.foreign.NativeMemorySegmentImpl; +import jdk.internal.foreign.SegmentFactories; +import jdk.internal.foreign.StringSupport; import jdk.internal.foreign.Utils; -import jdk.internal.foreign.abi.SharedUtils; -import jdk.internal.foreign.layout.ValueLayouts; -import jdk.internal.javac.PreviewFeature; import jdk.internal.javac.Restricted; import jdk.internal.reflect.CallerSensitive; import jdk.internal.vm.annotation.ForceInline; @@ -124,37 +122,43 @@ * If the value to be read is stored in memory using {@linkplain ByteOrder#BIG_ENDIAN big-endian} encoding, the access operation * can be expressed as follows: * {@snippet lang=java : - * MemorySegment segment = ... * int value = segment.get(ValueLayout.JAVA_INT.withOrder(BIG_ENDIAN), 0); * } * - * For more complex access operations (e.g. structured memory access), clients can obtain a - * {@linkplain MethodHandles#memorySegmentViewVarHandle(ValueLayout) var handle} - * that accepts a segment and a {@code long} offset. More complex var handles - * can be obtained by adapting a segment var handle view using the var handle combinator functions defined in the - * {@link java.lang.invoke.MethodHandles} class: + * Access operations on memory segments are implemented using var handles. The {@link ValueLayout#varHandle()} + * method can be used to obtain a var handle that can be used to get/set values represented by the given value layout + * on a memory segment at the given offset: * - * {@snippet lang=java : - * MemorySegment segment = ... - * VarHandle intHandle = MethodHandles.memorySegmentViewVarHandle(ValueLayout.JAVA_INT); - * MethodHandle multiplyExact = MethodHandles.lookup() - * .findStatic(Math.class, "multiplyExact", - * MethodType.methodType(long.class, long.class, long.class)); - * intHandle = MethodHandles.filterCoordinates(intHandle, 1, - * MethodHandles.insertArguments(multiplyExact, 0, ValueLayout.JAVA_INT.byteSize())); - * int value = (int) intHandle.get(segment, 3L); // get int element at offset 3 * 4 = 12 + * {@snippet lang=java: + * VarHandle intAtOffsetHandle = ValueLayout.JAVA_INT.varHandle(); // (MemorySegment, long) + * int value = (int) intAtOffsetHandle.get(segment, 10L); // segment.get(ValueLayout.JAVA_INT, 10L) * } * - * Alternatively, complex var handles can can be obtained - * from {@linkplain MemoryLayout#varHandle(MemoryLayout.PathElement...) memory layouts} - * by providing a so called layout path: + * The var handle returned by {@link ValueLayout#varHandle()} features a base offset parameter. This parameter + * allows clients to express complex access operations, by injecting additional offset computation into the var handle. + * For instance, a var handle that can be used to access an element of an {@code int} array at a given logical + * index can be created as follows: * - * {@snippet lang=java : - * MemorySegment segment = ... - * VarHandle intHandle = ValueLayout.JAVA_INT.arrayElementVarHandle(); - * int value = (int) intHandle.get(segment, 3L); // get int element at offset 3 * 4 = 12 + * {@snippet lang=java: + * MethodHandle scale = ValueLayout.JAVA_INT.scaleHandle(); // (long, long)long + * VarHandle intAtOffsetAndIndexHandle = + * MethodHandles.collectCoordinates(intAtOffsetHandle, 1, scale); // (MemorySegment, long, long) + * int value = (int) intAtOffsetAndIndexHandle.get(segment, 2L, 3L); // segment.get(ValueLayout.JAVA_INT, 2L + (3L * 4L)) * } * + *

    + * Clients can also drop the base offset parameter, in order to make the access expression simpler. This can be used to + * implement access operation such as {@link #getAtIndex(OfInt, long)}: + * + * {@snippet lang=java: + * VarHandle intAtIndexHandle = + * MethodHandles.insertCoordinates(intAtOffsetAndIndexHandle, 1, 0L); // (MemorySegment, long) + * int value = (int) intAtIndexHandle.get(segment, 3L); // segment.getAtIndex(ValueLayout.JAVA_INT, 3L); + * } + * + * Var handles for more complex access expressions (e.g. struct field access, pointer dereference) can be created directly + * from memory layouts, using layout paths. + * *

    Slicing memory segments

    * * Memory segments support {@linkplain MemorySegment#asSlice(long, long) slicing}. Slicing a memory segment @@ -434,9 +438,8 @@ * @implSpec * Implementations of this interface are immutable, thread-safe and value-based. * - * @since 19 + * @since 22 */ -@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** @@ -570,10 +573,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * the alignment constraint specified by {@code layout}. * @return a slice of this memory segment. */ - default MemorySegment asSlice(long offset, MemoryLayout layout) { - Objects.requireNonNull(layout); - return asSlice(offset, layout.byteSize(), layout.byteAlignment()); - } + MemorySegment asSlice(long offset, MemoryLayout layout); /** * Returns a slice of this memory segment, at the given offset. The returned segment's address is the address @@ -597,8 +597,7 @@ default MemorySegment asSlice(long offset, MemoryLayout layout) { *

    * This method is restricted. * Restricted methods are unsafe, and, if used incorrectly, their use might crash - * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on - * restricted methods, and use safe and supported functionalities, where possible. + * the JVM or, worse, silently result in memory corruption. * * @param newSize the size of the returned segment. * @return a new memory segment that has the same address and scope as this segment, but the new @@ -631,8 +630,7 @@ default MemorySegment asSlice(long offset, MemoryLayout layout) { *

    * This method is restricted. * Restricted methods are unsafe, and, if used incorrectly, their use might crash - * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on - * restricted methods, and use safe and supported functionalities, where possible. + * the JVM or, worse, silently result in memory corruption. * * @apiNote The cleanup action (if present) should take care not to leak the received segment to external * clients which might access the segment after its backing region of memory is no longer available. Furthermore, @@ -671,8 +669,7 @@ default MemorySegment asSlice(long offset, MemoryLayout layout) { *

    * This method is restricted. * Restricted methods are unsafe, and, if used incorrectly, their use might crash - * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on - * restricted methods, and use safe and supported functionalities, where possible. + * the JVM or, worse, silently result in memory corruption. * * @apiNote The cleanup action (if present) should take care not to leak the received segment to external * clients which might access the segment after its backing region of memory is no longer available. Furthermore, @@ -739,30 +736,6 @@ default MemorySegment asSlice(long offset, MemoryLayout layout) { */ Optional asOverlappingSlice(MemorySegment other); - /** - * Returns the offset, in bytes, of the provided segment, relative to this - * segment. - * - *

    The offset is relative to the address of this segment and can be - * a negative or positive value. For instance, if both segments are native - * segments, or heap segments backed by the same array, the resulting offset - * can be computed as follows: - * - * {@snippet lang=java : - * other.address() - address() - * } - * - * If the segments share the same address, {@code 0} is returned. If - * {@code other} is a slice of this segment, the offset is always - * {@code 0 <= x < this.byteSize()}. - * - * @param other the segment to retrieve an offset to. - * @throws UnsupportedOperationException if the two segments cannot be compared, e.g. because they are of - * different kinds, or because they are backed by different Java arrays. - * @return the relative offset, in bytes, of the provided segment. - */ - long segmentOffset(MemorySegment other); - /** * Fills the contents of this memory segment with the given value. *

    @@ -771,7 +744,7 @@ default MemorySegment asSlice(long offset, MemoryLayout layout) { * * {@snippet lang=java : * for (long offset = 0; offset < segment.byteSize(); offset++) { - * byteHandle.set(ValueLayout.JAVA_BYTE, offset, value); + * segment.set(ValueLayout.JAVA_BYTE, offset, value); * } * } * @@ -812,10 +785,7 @@ default MemorySegment asSlice(long offset, MemoryLayout layout) { * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. * @return this segment. */ - default MemorySegment copyFrom(MemorySegment src) { - MemorySegment.copy(src, 0, this, 0, src.byteSize()); - return this; - } + MemorySegment copyFrom(MemorySegment src); /** * Finds and returns the offset, in bytes, of the first mismatch between @@ -844,10 +814,7 @@ default MemorySegment copyFrom(MemorySegment src) { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code other.isAccessibleBy(T) == false}. */ - default long mismatch(MemorySegment other) { - Objects.requireNonNull(other); - return MemorySegment.mismatch(this, 0, byteSize(), other, 0, other.byteSize()); - } + long mismatch(MemorySegment other); /** * Determines whether the contents of this mapped segment is resident in physical @@ -1072,52 +1039,111 @@ default long mismatch(MemorySegment other) { double[] toArray(ValueLayout.OfDouble elementLayout); /** - * Reads a UTF-8 encoded, null-terminated string from this segment at the given offset. + * Reads a null-terminated string from this segment at the given offset, using the + * {@linkplain StandardCharsets#UTF_8 UTF-8} charset. *

    - * This method always replaces malformed-input and unmappable-character - * sequences with this charset's default replacement string. The {@link - * java.nio.charset.CharsetDecoder} class should be used when more control - * over the decoding process is required. + * Calling this method is equivalent to the following code: + * {@snippet lang = java: + * getString(offset, StandardCharsets.UTF_8); + *} + * * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @return a Java string constructed from the bytes read from the given starting address up to (but not including) * the first {@code '\0'} terminator character (assuming one is found). - * @throws IllegalArgumentException if the size of the UTF-8 string is greater than the largest string supported by the platform. - * @throws IndexOutOfBoundsException if {@code offset < 0} or {@code offset > byteSize() - S}, where {@code S} is the size of the UTF-8 - * string (including the terminator character). + * @throws IllegalArgumentException if the size of the string is greater than the largest string supported by the platform. + * @throws IndexOutOfBoundsException if {@code offset < 0}. + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - (B + 1)}, where {@code B} is the size, + * in bytes, of the string encoded using UTF-8 charset {@code str.getBytes(StandardCharsets.UTF_8).length}). * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. */ - default String getUtf8String(long offset) { - return SharedUtils.toJavaStringInternal(this, offset); - } + String getString(long offset); /** - * Writes the given string into this segment at the given offset, converting it to a null-terminated byte sequence using UTF-8 encoding. + * Reads a null-terminated string from this segment at the given offset, using the provided charset. *

    * This method always replaces malformed-input and unmappable-character * sequences with this charset's default replacement string. The {@link * java.nio.charset.CharsetDecoder} class should be used when more control * over the decoding process is required. + * + * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. + * @param charset the charset used to {@linkplain Charset#newDecoder() decode} the string bytes. + * @return a Java string constructed from the bytes read from the given starting address up to (but not including) + * the first {@code '\0'} terminator character (assuming one is found). + * @throws IllegalArgumentException if the size of the string is greater than the largest string supported by the platform. + * @throws IndexOutOfBoundsException if {@code offset < 0}. + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - (B + N)}, where: + *

      + *
    • {@code B} is the size, in bytes, of the string encoded using the provided charset + * (e.g. {@code str.getBytes(charset).length});
    • + *
    • {@code N} is the size (in bytes) of the terminator char according to the provided charset. For instance, + * this is 1 for {@link StandardCharsets#US_ASCII} and 2 for {@link StandardCharsets#UTF_16}.
    • + *
    + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain Scope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if {@code charset} is not a {@linkplain StandardCharsets standard charset}. + */ + String getString(long offset, Charset charset); + + /** + * Writes the given string into this segment at the given offset, converting it to a null-terminated byte sequence + * using the {@linkplain StandardCharsets#UTF_8 UTF-8} charset. *

    - * If the given string contains any {@code '\0'} characters, they will be - * copied as well. This means that, depending on the method used to read - * the string, such as {@link MemorySegment#getUtf8String(long)}, the string - * will appear truncated when read again. + * Calling this method is equivalent to the following code: + * {@snippet lang = java: + * setString(offset, str, StandardCharsets.UTF_8); + *} * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * the final address of this write operation can be expressed as {@code address() + offset}. * @param str the Java string to be written into this segment. - * @throws IndexOutOfBoundsException if {@code offset < 0} or {@code offset > byteSize() - str.getBytes().length() + 1}. + * @throws IndexOutOfBoundsException if {@code offset < 0}. + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - (B + 1)}, where {@code B} is the size, + * in bytes, of the string encoded using UTF-8 charset {@code str.getBytes(StandardCharsets.UTF_8).length}). * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. */ - default void setUtf8String(long offset, String str) { - Utils.toCString(str.getBytes(StandardCharsets.UTF_8), SegmentAllocator.prefixAllocator(asSlice(offset))); - } + void setString(long offset, String str); + /** + * Writes the given string into this segment at the given offset, converting it to a null-terminated byte sequence + * using the provided charset. + *

    + * This method always replaces malformed-input and unmappable-character + * sequences with this charset's default replacement string. The {@link + * java.nio.charset.CharsetDecoder} class should be used when more control + * over the decoding process is required. + *

    + * If the given string contains any {@code '\0'} characters, they will be + * copied as well. This means that, depending on the method used to read + * the string, such as {@link MemorySegment#getString(long)}, the string + * will appear truncated when read again. + * + * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. + * the final address of this write operation can be expressed as {@code address() + offset}. + * @param str the Java string to be written into this segment. + * @param charset the charset used to {@linkplain Charset#newEncoder() encode} the string bytes. + * @throws IndexOutOfBoundsException if {@code offset < 0}. + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - (B + N)}, where: + *

      + *
    • {@code B} is the size, in bytes, of the string encoded using the provided charset + * (e.g. {@code str.getBytes(charset).length});
    • + *
    • {@code N} is the size (in bytes) of the terminator char according to the provided charset. For instance, + * this is 1 for {@link StandardCharsets#US_ASCII} and 2 for {@link StandardCharsets#UTF_16}.
    • + *
    + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain Scope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if {@code charset} is not a {@linkplain StandardCharsets standard charset}. + */ + void setString(long offset, String str, Charset charset); /** * Creates a memory segment that is backed by the same region of memory that backs the given {@link Buffer} instance. @@ -1155,7 +1181,7 @@ static MemorySegment ofBuffer(Buffer buffer) { * @return a heap memory segment backed by a byte array. */ static MemorySegment ofArray(byte[] byteArray) { - return HeapMemorySegmentImpl.OfByte.fromArray(byteArray); + return SegmentFactories.fromArray(byteArray); } /** @@ -1167,7 +1193,7 @@ static MemorySegment ofArray(byte[] byteArray) { * @return a heap memory segment backed by a char array. */ static MemorySegment ofArray(char[] charArray) { - return HeapMemorySegmentImpl.OfChar.fromArray(charArray); + return SegmentFactories.fromArray(charArray); } /** @@ -1179,7 +1205,7 @@ static MemorySegment ofArray(char[] charArray) { * @return a heap memory segment backed by a short array. */ static MemorySegment ofArray(short[] shortArray) { - return HeapMemorySegmentImpl.OfShort.fromArray(shortArray); + return SegmentFactories.fromArray(shortArray); } /** @@ -1191,7 +1217,7 @@ static MemorySegment ofArray(short[] shortArray) { * @return a heap memory segment backed by an int array. */ static MemorySegment ofArray(int[] intArray) { - return HeapMemorySegmentImpl.OfInt.fromArray(intArray); + return SegmentFactories.fromArray(intArray); } /** @@ -1203,7 +1229,7 @@ static MemorySegment ofArray(int[] intArray) { * @return a heap memory segment backed by a float array. */ static MemorySegment ofArray(float[] floatArray) { - return HeapMemorySegmentImpl.OfFloat.fromArray(floatArray); + return SegmentFactories.fromArray(floatArray); } /** @@ -1215,7 +1241,7 @@ static MemorySegment ofArray(float[] floatArray) { * @return a heap memory segment backed by a long array. */ static MemorySegment ofArray(long[] longArray) { - return HeapMemorySegmentImpl.OfLong.fromArray(longArray); + return SegmentFactories.fromArray(longArray); } /** @@ -1227,13 +1253,13 @@ static MemorySegment ofArray(long[] longArray) { * @return a heap memory segment backed by a double array. */ static MemorySegment ofArray(double[] doubleArray) { - return HeapMemorySegmentImpl.OfDouble.fromArray(doubleArray); + return SegmentFactories.fromArray(doubleArray); } /** - * A zero-length native segment modelling the {@code NULL} address. + * A zero-length native segment modelling the {@code NULL} address. Equivalent to {@code MemorySegment.ofAddress(0L)}. */ - MemorySegment NULL = new NativeMemorySegmentImpl(); + MemorySegment NULL = MemorySegment.ofAddress(0L); /** * Creates a zero-length native segment from the given {@linkplain #address() address value}. @@ -1247,7 +1273,7 @@ static MemorySegment ofArray(double[] doubleArray) { * @return a zero-length native segment with the given address. */ static MemorySegment ofAddress(long address) { - return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(address, 0); + return SegmentFactories.makeNativeSegmentUnchecked(address, 0); } /** @@ -1281,8 +1307,9 @@ static MemorySegment ofAddress(long address) { * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code dstSegment.isAccessibleBy(T) == false}. - * @throws IndexOutOfBoundsException if {@code srcOffset > srcSegment.byteSize() - bytes} or if - * {@code dstOffset > dstSegment.byteSize() - bytes}, or if either {@code srcOffset}, {@code dstOffset} + * @throws IndexOutOfBoundsException if {@code srcOffset > srcSegment.byteSize() - bytes}. + * @throws IndexOutOfBoundsException if {@code dstOffset > dstSegment.byteSize() - bytes}. + * @throws IndexOutOfBoundsException if either {@code srcOffset}, {@code dstOffset} * or {@code bytes} are {@code < 0}. * @throws UnsupportedOperationException if {@code dstSegment} is {@linkplain #isReadOnly() read-only}. */ @@ -1320,17 +1347,21 @@ static void copy(MemorySegment srcSegment, long srcOffset, * @param elementCount the number of elements to be copied. * @throws IllegalArgumentException if the element layouts have different sizes, if the source (resp. destination) segment/offset are * incompatible with the alignment constraint in the source - * (resp. destination) element layout, or if the source (resp. destination) element layout alignment is greater than its size. + * (resp. destination) element layout. + * @throws IllegalArgumentException if {@code srcElementLayout.byteAlignment() > srcElementLayout.byteSize()}. + * @throws IllegalArgumentException if {@code dstElementLayout.byteAlignment() > dstElementLayout.byteSize()}. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code srcSegment} is not * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code srcSegment().isAccessibleBy(T) == false}. + * such that {@code srcSegment.isAccessibleBy(T) == false}. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code dstSegment} is not * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code dstSegment().isAccessibleBy(T) == false}. + * such that {@code dstSegment.isAccessibleBy(T) == false}. * @throws UnsupportedOperationException if {@code dstSegment} is {@linkplain #isReadOnly() read-only}. - * @throws IndexOutOfBoundsException if {@code elementCount * srcLayout.byteSize()} or {@code elementCount * dtsLayout.byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code elementCount * srcLayout.byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code elementCount * dtsLayout.byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code srcOffset > srcSegment.byteSize() - (elementCount * srcLayout.byteSize())}. * @throws IndexOutOfBoundsException if {@code dstOffset > dstSegment.byteSize() - (elementCount * dstLayout.byteSize())}. * @throws IndexOutOfBoundsException if either {@code srcOffset}, {@code dstOffset} or {@code elementCount} are {@code < 0}. */ @@ -1359,10 +1390,7 @@ static void copy(MemorySegment srcSegment, ValueLayout srcElementLayout, long sr * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. */ - @ForceInline - default byte get(ValueLayout.OfByte layout, long offset) { - return (byte) ((ValueLayouts.OfByteImpl) layout).accessHandle().get(this, offset); - } + byte get(ValueLayout.OfByte layout, long offset); /** * Writes a byte into this segment at the given offset, with the given layout. @@ -1379,10 +1407,7 @@ default byte get(ValueLayout.OfByte layout, long offset) { * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ - @ForceInline - default void set(ValueLayout.OfByte layout, long offset, byte value) { - ((ValueLayouts.OfByteImpl) layout).accessHandle().set(this, offset, value); - } + void set(ValueLayout.OfByte layout, long offset, byte value); /** * Reads a boolean from this segment at the given offset, with the given layout. @@ -1398,10 +1423,7 @@ default void set(ValueLayout.OfByte layout, long offset, byte value) { * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. */ - @ForceInline - default boolean get(ValueLayout.OfBoolean layout, long offset) { - return (boolean) ((ValueLayouts.OfBooleanImpl) layout).accessHandle().get(this, offset); - } + boolean get(ValueLayout.OfBoolean layout, long offset); /** * Writes a boolean into this segment at the given offset, with the given layout. @@ -1418,10 +1440,7 @@ default boolean get(ValueLayout.OfBoolean layout, long offset) { * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ - @ForceInline - default void set(ValueLayout.OfBoolean layout, long offset, boolean value) { - ((ValueLayouts.OfBooleanImpl) layout).accessHandle().set(this, offset, value); - } + void set(ValueLayout.OfBoolean layout, long offset, boolean value); /** * Reads a char from this segment at the given offset, with the given layout. @@ -1437,10 +1456,7 @@ default void set(ValueLayout.OfBoolean layout, long offset, boolean value) { * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. */ - @ForceInline - default char get(ValueLayout.OfChar layout, long offset) { - return (char) ((ValueLayouts.OfCharImpl) layout).accessHandle().get(this, offset); - } + char get(ValueLayout.OfChar layout, long offset); /** * Writes a char into this segment at the given offset, with the given layout. @@ -1457,10 +1473,7 @@ default char get(ValueLayout.OfChar layout, long offset) { * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ - @ForceInline - default void set(ValueLayout.OfChar layout, long offset, char value) { - ((ValueLayouts.OfCharImpl) layout).accessHandle().set(this, offset, value); - } + void set(ValueLayout.OfChar layout, long offset, char value); /** * Reads a short from this segment at the given offset, with the given layout. @@ -1476,10 +1489,7 @@ default void set(ValueLayout.OfChar layout, long offset, char value) { * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. */ - @ForceInline - default short get(ValueLayout.OfShort layout, long offset) { - return (short) ((ValueLayouts.OfShortImpl) layout).accessHandle().get(this, offset); - } + short get(ValueLayout.OfShort layout, long offset); /** * Writes a short into this segment at the given offset, with the given layout. @@ -1496,10 +1506,7 @@ default short get(ValueLayout.OfShort layout, long offset) { * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ - @ForceInline - default void set(ValueLayout.OfShort layout, long offset, short value) { - ((ValueLayouts.OfShortImpl) layout).accessHandle().set(this, offset, value); - } + void set(ValueLayout.OfShort layout, long offset, short value); /** * Reads an int from this segment at the given offset, with the given layout. @@ -1515,10 +1522,7 @@ default void set(ValueLayout.OfShort layout, long offset, short value) { * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. */ - @ForceInline - default int get(ValueLayout.OfInt layout, long offset) { - return (int) ((ValueLayouts.OfIntImpl) layout).accessHandle().get(this, offset); - } + int get(ValueLayout.OfInt layout, long offset); /** * Writes an int into this segment at the given offset, with the given layout. @@ -1535,10 +1539,7 @@ default int get(ValueLayout.OfInt layout, long offset) { * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ - @ForceInline - default void set(ValueLayout.OfInt layout, long offset, int value) { - ((ValueLayouts.OfIntImpl) layout).accessHandle().set(this, offset, value); - } + void set(ValueLayout.OfInt layout, long offset, int value); /** * Reads a float from this segment at the given offset, with the given layout. @@ -1554,10 +1555,7 @@ default void set(ValueLayout.OfInt layout, long offset, int value) { * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. */ - @ForceInline - default float get(ValueLayout.OfFloat layout, long offset) { - return (float)((ValueLayouts.OfFloatImpl) layout).accessHandle().get(this, offset); - } + float get(ValueLayout.OfFloat layout, long offset); /** * Writes a float into this segment at the given offset, with the given layout. @@ -1574,10 +1572,7 @@ default float get(ValueLayout.OfFloat layout, long offset) { * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ - @ForceInline - default void set(ValueLayout.OfFloat layout, long offset, float value) { - ((ValueLayouts.OfFloatImpl) layout).accessHandle().set(this, offset, value); - } + void set(ValueLayout.OfFloat layout, long offset, float value); /** * Reads a long from this segment at the given offset, with the given layout. @@ -1593,10 +1588,7 @@ default void set(ValueLayout.OfFloat layout, long offset, float value) { * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. */ - @ForceInline - default long get(ValueLayout.OfLong layout, long offset) { - return (long) ((ValueLayouts.OfLongImpl) layout).accessHandle().get(this, offset); - } + long get(ValueLayout.OfLong layout, long offset); /** * Writes a long into this segment at the given offset, with the given layout. @@ -1613,10 +1605,7 @@ default long get(ValueLayout.OfLong layout, long offset) { * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ - @ForceInline - default void set(ValueLayout.OfLong layout, long offset, long value) { - ((ValueLayouts.OfLongImpl) layout).accessHandle().set(this, offset, value); - } + void set(ValueLayout.OfLong layout, long offset, long value); /** * Reads a double from this segment at the given offset, with the given layout. @@ -1632,10 +1621,7 @@ default void set(ValueLayout.OfLong layout, long offset, long value) { * incompatible with the alignment constraint in the provided layout. * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. */ - @ForceInline - default double get(ValueLayout.OfDouble layout, long offset) { - return (double) ((ValueLayouts.OfDoubleImpl) layout).accessHandle().get(this, offset); - } + double get(ValueLayout.OfDouble layout, long offset); /** * Writes a double into this segment at the given offset, with the given layout. @@ -1652,10 +1638,7 @@ default double get(ValueLayout.OfDouble layout, long offset) { * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ - @ForceInline - default void set(ValueLayout.OfDouble layout, long offset, double value) { - ((ValueLayouts.OfDoubleImpl) layout).accessHandle().set(this, offset, value); - } + void set(ValueLayout.OfDouble layout, long offset, double value); /** * Reads an address from this segment at the given offset, with the given layout. The read address is wrapped in @@ -1677,10 +1660,7 @@ default void set(ValueLayout.OfDouble layout, long offset, double value) { * incompatible with the alignment constraint in {@code T}. * @throws IndexOutOfBoundsException if {@code offset > byteSize() - layout.byteSize()}. */ - @ForceInline - default MemorySegment get(AddressLayout layout, long offset) { - return (MemorySegment) ((ValueLayouts.OfAddressImpl) layout).accessHandle().get(this, offset); - } + MemorySegment get(AddressLayout layout, long offset); /** * Writes an address into this segment at the given offset, with the given layout. @@ -1698,10 +1678,7 @@ default MemorySegment get(AddressLayout layout, long offset) { * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. * @throws UnsupportedOperationException if {@code value} is not a {@linkplain #isNative() native} segment. */ - @ForceInline - default void set(AddressLayout layout, long offset, MemorySegment value) { - ((ValueLayouts.OfAddressImpl) layout).accessHandle().set(this, offset, value); - } + void set(AddressLayout layout, long offset, MemorySegment value); /** * Reads a byte from this segment at the given index, scaled by the given layout size. @@ -1715,17 +1692,12 @@ default void set(AddressLayout layout, long offset, MemorySegment value) { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is - * incompatible with the alignment constraint in the provided layout, - * or if the layout alignment is greater than its size. + * incompatible with the alignment constraint in the provided layout. + * @throws IllegalArgumentException if {@code layout.byteAlignment() > layout.byteSize()}. * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. */ - @ForceInline - default byte getAtIndex(ValueLayout.OfByte layout, long index) { - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - return (byte) ((ValueLayouts.OfByteImpl) layout).accessHandle().get(this, index * layout.byteSize()); - } + byte getAtIndex(ValueLayout.OfByte layout, long index); /** * Reads a boolean from this segment at the given index, scaled by the given layout size. @@ -1739,17 +1711,12 @@ default byte getAtIndex(ValueLayout.OfByte layout, long index) { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is - * incompatible with the alignment constraint in the provided layout, - * or if the layout alignment is greater than its size. + * incompatible with the alignment constraint in the provided layout. + * @throws IllegalArgumentException if {@code layout.byteAlignment() > layout.byteSize()}. * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. */ - @ForceInline - default boolean getAtIndex(ValueLayout.OfBoolean layout, long index) { - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - return (boolean) ((ValueLayouts.OfBooleanImpl) layout).accessHandle().get(this, index * layout.byteSize()); - } + boolean getAtIndex(ValueLayout.OfBoolean layout, long index); /** * Reads a char from this segment at the given index, scaled by the given layout size. @@ -1763,17 +1730,12 @@ default boolean getAtIndex(ValueLayout.OfBoolean layout, long index) { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is - * incompatible with the alignment constraint in the provided layout, - * or if the layout alignment is greater than its size. + * incompatible with the alignment constraint in the provided layout. + * @throws IllegalArgumentException if {@code layout.byteAlignment() > layout.byteSize()}. * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. */ - @ForceInline - default char getAtIndex(ValueLayout.OfChar layout, long index) { - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - return (char) ((ValueLayouts.OfCharImpl) layout).accessHandle().get(this, index * layout.byteSize()); - } + char getAtIndex(ValueLayout.OfChar layout, long index); /** * Writes a char into this segment at the given index, scaled by the given layout size. @@ -1787,18 +1749,13 @@ default char getAtIndex(ValueLayout.OfChar layout, long index) { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is - * incompatible with the alignment constraint in the provided layout, - * or if the layout alignment is greater than its size. + * incompatible with the alignment constraint in the provided layout. + * @throws IllegalArgumentException if {@code layout.byteAlignment() > layout.byteSize()}. * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ - @ForceInline - default void setAtIndex(ValueLayout.OfChar layout, long index, char value) { - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - ((ValueLayouts.OfCharImpl) layout).accessHandle().set(this, index * layout.byteSize(), value); - } + void setAtIndex(ValueLayout.OfChar layout, long index, char value); /** * Reads a short from this segment at the given index, scaled by the given layout size. @@ -1812,17 +1769,12 @@ default void setAtIndex(ValueLayout.OfChar layout, long index, char value) { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is - * incompatible with the alignment constraint in the provided layout, - * or if the layout alignment is greater than its size. + * incompatible with the alignment constraint in the provided layout. + * @throws IllegalArgumentException if {@code layout.byteAlignment() > layout.byteSize()}. * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. */ - @ForceInline - default short getAtIndex(ValueLayout.OfShort layout, long index) { - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - return (short) ((ValueLayouts.OfShortImpl) layout).accessHandle().get(this, index * layout.byteSize()); - } + short getAtIndex(ValueLayout.OfShort layout, long index); /** * Writes a byte into this segment at the given index, scaled by the given layout size. @@ -1836,19 +1788,13 @@ default short getAtIndex(ValueLayout.OfShort layout, long index) { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is - * incompatible with the alignment constraint in the provided layout, - * or if the layout alignment is greater than its size. + * incompatible with the alignment constraint in the provided layout. + * @throws IllegalArgumentException if {@code layout.byteAlignment() > layout.byteSize()}. * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ - @ForceInline - default void setAtIndex(ValueLayout.OfByte layout, long index, byte value) { - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - ((ValueLayouts.OfByteImpl) layout).accessHandle().set(this, index * layout.byteSize(), value); - - } + void setAtIndex(ValueLayout.OfByte layout, long index, byte value); /** * Writes a boolean into this segment at the given index, scaled by the given layout size. @@ -1862,18 +1808,13 @@ default void setAtIndex(ValueLayout.OfByte layout, long index, byte value) { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is - * incompatible with the alignment constraint in the provided layout, - * or if the layout alignment is greater than its size. + * incompatible with the alignment constraint in the provided layout. + * @throws IllegalArgumentException if {@code layout.byteAlignment() > layout.byteSize()}. * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ - @ForceInline - default void setAtIndex(ValueLayout.OfBoolean layout, long index, boolean value) { - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - ((ValueLayouts.OfBooleanImpl) layout).accessHandle().set(this, index * layout.byteSize(), value); - } + void setAtIndex(ValueLayout.OfBoolean layout, long index, boolean value); /** * Writes a short into this segment at the given index, scaled by the given layout size. @@ -1887,18 +1828,13 @@ default void setAtIndex(ValueLayout.OfBoolean layout, long index, boolean value) * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is - * incompatible with the alignment constraint in the provided layout, - * or if the layout alignment is greater than its size. + * incompatible with the alignment constraint in the provided layout. + * @throws IllegalArgumentException if {@code layout.byteAlignment() > layout.byteSize()}. * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ - @ForceInline - default void setAtIndex(ValueLayout.OfShort layout, long index, short value) { - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - ((ValueLayouts.OfShortImpl) layout).accessHandle().set(this, index * layout.byteSize(), value); - } + void setAtIndex(ValueLayout.OfShort layout, long index, short value); /** * Reads an int from this segment at the given index, scaled by the given layout size. @@ -1912,17 +1848,12 @@ default void setAtIndex(ValueLayout.OfShort layout, long index, short value) { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is - * incompatible with the alignment constraint in the provided layout, - * or if the layout alignment is greater than its size. + * incompatible with the alignment constraint in the provided layout. + * @throws IllegalArgumentException if {@code layout.byteAlignment() > layout.byteSize()}. * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. */ - @ForceInline - default int getAtIndex(ValueLayout.OfInt layout, long index) { - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - return (int) ((ValueLayouts.OfIntImpl) layout).accessHandle().get(this, index * layout.byteSize()); - } + int getAtIndex(ValueLayout.OfInt layout, long index); /** * Writes an int into this segment at the given index, scaled by the given layout size. @@ -1936,18 +1867,13 @@ default int getAtIndex(ValueLayout.OfInt layout, long index) { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is - * incompatible with the alignment constraint in the provided layout, - * or if the layout alignment is greater than its size. + * incompatible with the alignment constraint in the provided layout. + * @throws IllegalArgumentException if {@code layout.byteAlignment() > layout.byteSize()}. * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ - @ForceInline - default void setAtIndex(ValueLayout.OfInt layout, long index, int value) { - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - ((ValueLayouts.OfIntImpl) layout).accessHandle().set(this, index * layout.byteSize(), value); - } + void setAtIndex(ValueLayout.OfInt layout, long index, int value); /** * Reads a float from this segment at the given index, scaled by the given layout size. @@ -1961,17 +1887,12 @@ default void setAtIndex(ValueLayout.OfInt layout, long index, int value) { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is - * incompatible with the alignment constraint in the provided layout, - * or if the layout alignment is greater than its size. + * incompatible with the alignment constraint in the provided layout. + * @throws IllegalArgumentException if {@code layout.byteAlignment() > layout.byteSize()}. * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. */ - @ForceInline - default float getAtIndex(ValueLayout.OfFloat layout, long index) { - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - return (float) ((ValueLayouts.OfFloatImpl) layout).accessHandle().get(this, index * layout.byteSize()); - } + float getAtIndex(ValueLayout.OfFloat layout, long index); /** * Writes a float into this segment at the given index, scaled by the given layout size. @@ -1985,18 +1906,13 @@ default float getAtIndex(ValueLayout.OfFloat layout, long index) { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is - * incompatible with the alignment constraint in the provided layout, - * or if the layout alignment is greater than its size. + * incompatible with the alignment constraint in the provided layout. + * @throws IllegalArgumentException if {@code layout.byteAlignment() > layout.byteSize()}. * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ - @ForceInline - default void setAtIndex(ValueLayout.OfFloat layout, long index, float value) { - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - ((ValueLayouts.OfFloatImpl) layout).accessHandle().set(this, index * layout.byteSize(), value); - } + void setAtIndex(ValueLayout.OfFloat layout, long index, float value); /** * Reads a long from this segment at the given index, scaled by the given layout size. @@ -2010,17 +1926,12 @@ default void setAtIndex(ValueLayout.OfFloat layout, long index, float value) { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is - * incompatible with the alignment constraint in the provided layout, - * or if the layout alignment is greater than its size. + * incompatible with the alignment constraint in the provided layout. + * @throws IllegalArgumentException if {@code layout.byteAlignment() > layout.byteSize()}. * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. */ - @ForceInline - default long getAtIndex(ValueLayout.OfLong layout, long index) { - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - return (long) ((ValueLayouts.OfLongImpl) layout).accessHandle().get(this, index * layout.byteSize()); - } + long getAtIndex(ValueLayout.OfLong layout, long index); /** * Writes a long into this segment at the given index, scaled by the given layout size. @@ -2034,18 +1945,13 @@ default long getAtIndex(ValueLayout.OfLong layout, long index) { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is - * incompatible with the alignment constraint in the provided layout, - * or if the layout alignment is greater than its size. + * incompatible with the alignment constraint in the provided layout. + * @throws IllegalArgumentException if {@code layout.byteAlignment() > layout.byteSize()}. * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ - @ForceInline - default void setAtIndex(ValueLayout.OfLong layout, long index, long value) { - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - ((ValueLayouts.OfLongImpl) layout).accessHandle().set(this, index * layout.byteSize(), value); - } + void setAtIndex(ValueLayout.OfLong layout, long index, long value); /** * Reads a double from this segment at the given index, scaled by the given layout size. @@ -2059,17 +1965,12 @@ default void setAtIndex(ValueLayout.OfLong layout, long index, long value) { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is - * incompatible with the alignment constraint in the provided layout, - * or if the layout alignment is greater than its size. + * incompatible with the alignment constraint in the provided layout. + * @throws IllegalArgumentException if {@code layout.byteAlignment() > layout.byteSize()}. * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. */ - @ForceInline - default double getAtIndex(ValueLayout.OfDouble layout, long index) { - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - return (double) ((ValueLayouts.OfDoubleImpl) layout).accessHandle().get(this, index * layout.byteSize()); - } + double getAtIndex(ValueLayout.OfDouble layout, long index); /** * Writes a double into this segment at the given index, scaled by the given layout size. @@ -2083,18 +1984,13 @@ default double getAtIndex(ValueLayout.OfDouble layout, long index) { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is - * incompatible with the alignment constraint in the provided layout, - * or if the layout alignment is greater than its size. + * incompatible with the alignment constraint in the provided layout. + * @throws IllegalArgumentException if {@code layout.byteAlignment() > layout.byteSize()}. * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ - @ForceInline - default void setAtIndex(ValueLayout.OfDouble layout, long index, double value) { - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - ((ValueLayouts.OfDoubleImpl) layout).accessHandle().set(this, index * layout.byteSize(), value); - } + void setAtIndex(ValueLayout.OfDouble layout, long index, double value); /** * Reads an address from this segment at the given at the given index, scaled by the given layout size. The read address is wrapped in @@ -2111,20 +2007,15 @@ default void setAtIndex(ValueLayout.OfDouble layout, long index, double value) { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is - * incompatible with the alignment constraint in the provided layout, - * or if the layout alignment is greater than its size. + * incompatible with the alignment constraint in the provided layout. + * @throws IllegalArgumentException if {@code layout.byteAlignment() > layout.byteSize()}. * @throws IllegalArgumentException if provided address layout has a {@linkplain AddressLayout#targetLayout() target layout} * {@code T}, and the address of the returned segment * incompatible with the alignment constraint in {@code T}. * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. */ - @ForceInline - default MemorySegment getAtIndex(AddressLayout layout, long index) { - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - return (MemorySegment) ((ValueLayouts.OfAddressImpl) layout).accessHandle().get(this, index * layout.byteSize()); - } + MemorySegment getAtIndex(AddressLayout layout, long index); /** * Writes an address into this segment at the given index, scaled by the given layout size. @@ -2138,19 +2029,14 @@ default MemorySegment getAtIndex(AddressLayout layout, long index) { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false}. * @throws IllegalArgumentException if the access operation is - * incompatible with the alignment constraint in the provided layout, - * or if the layout alignment is greater than its size. + * incompatible with the alignment constraint in the provided layout. + * @throws IllegalArgumentException if {@code layout.byteAlignment() > layout.byteSize()}. * @throws IndexOutOfBoundsException if {@code index * byteSize()} overflows. * @throws IndexOutOfBoundsException if {@code index * byteSize() > byteSize() - layout.byteSize()}. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. * @throws UnsupportedOperationException if {@code value} is not a {@linkplain #isNative() native} segment. */ - @ForceInline - default void setAtIndex(AddressLayout layout, long index, MemorySegment value) { - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - ((ValueLayouts.OfAddressImpl) layout).accessHandle().set(this, index * layout.byteSize(), value); - } + void setAtIndex(AddressLayout layout, long index, MemorySegment value); /** * Compares the specified object with this memory segment for equality. Returns {@code true} if and only if the specified @@ -2198,7 +2084,7 @@ default void setAtIndex(AddressLayout layout, long index, MemorySegment value) { * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code srcSegment} is not * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code srcSegment().isAccessibleBy(T) == false}. + * such that {@code srcSegment.isAccessibleBy(T) == false}. * @throws IllegalArgumentException if {@code dstArray} is not an array, or if it is an array but whose type is not supported. * @throws IllegalArgumentException if the destination array component type does not match {@code srcLayout.carrier()}. * @throws IllegalArgumentException if {@code offset} is incompatible @@ -2237,7 +2123,7 @@ static void copy( * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code dstSegment} is not * {@linkplain Scope#isAlive() alive}. * @throws WrongThreadException if this method is called from a thread {@code T}, - * such that {@code dstSegment().isAccessibleBy(T) == false}. + * such that {@code dstSegment.isAccessibleBy(T) == false}. * @throws IllegalArgumentException if {@code srcArray} is not an array, or if it is an array but whose type is not supported. * @throws IllegalArgumentException if the source array component type does not match {@code srcLayout.carrier()}. * @throws IllegalArgumentException if {@code offset} is incompatible @@ -2312,7 +2198,6 @@ static long mismatch(MemorySegment srcSegment, long srcFromOffset, long srcToOff * Scope instances can be compared for equality. That is, two scopes * are considered {@linkplain #equals(Object)} if they denote the same lifetime. */ - @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) sealed interface Scope permits MemorySessionImpl { /** * {@return {@code true}, if the regions of memory backing the memory segments associated with this scope are diff --git a/src/java.base/share/classes/java/lang/foreign/PaddingLayout.java b/src/java.base/share/classes/java/lang/foreign/PaddingLayout.java index 0df705999e3..921ef9c8479 100644 --- a/src/java.base/share/classes/java/lang/foreign/PaddingLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/PaddingLayout.java @@ -26,7 +26,6 @@ package java.lang.foreign; import jdk.internal.foreign.layout.PaddingLayoutImpl; -import jdk.internal.javac.PreviewFeature; /** * A padding layout. A padding layout specifies the size of extra space which is typically not accessed by applications, @@ -35,9 +34,8 @@ * @implSpec * Implementing classes are immutable, thread-safe and value-based. * - * @since 20 + * @since 22 */ -@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) public sealed interface PaddingLayout extends MemoryLayout permits PaddingLayoutImpl { /** diff --git a/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java b/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java index b659e32fa8a..fc74c78436b 100644 --- a/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java +++ b/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java @@ -25,16 +25,15 @@ package java.lang.foreign; -import java.lang.invoke.VarHandle; -import java.lang.reflect.Array; -import java.nio.ByteOrder; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Objects; -import java.util.function.Function; + import jdk.internal.foreign.AbstractMemorySegmentImpl; +import jdk.internal.foreign.ArenaImpl; import jdk.internal.foreign.SlicingAllocator; -import jdk.internal.foreign.Utils; -import jdk.internal.javac.PreviewFeature; +import jdk.internal.foreign.StringSupport; +import jdk.internal.vm.annotation.ForceInline; /** * An object that may be used to allocate {@linkplain MemorySegment memory segments}. Clients implementing this interface @@ -70,14 +69,32 @@ * Clients should consider using an {@linkplain Arena arena} instead, which, provides strong thread-safety, * lifetime and non-overlapping guarantees. * - * @since 19 + * @since 22 */ @FunctionalInterface -@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) public interface SegmentAllocator { /** - * {@return a new memory segment with a Java string converted into a UTF-8 encoded, null-terminated C string} + * Converts a Java string into a null-terminated C string using the {@linkplain StandardCharsets#UTF_8 UTF-8} charset, + * storing the result into a memory segment. + *

    + * Calling this method is equivalent to the following code: + * {@snippet lang = java: + * allocateFrom(str, StandardCharsets.UTF_8); + *} + * + * @param str the Java string to be converted into a C string. + * @return a new native segment containing the converted C string. + */ + @ForceInline + default MemorySegment allocateFrom(String str) { + Objects.requireNonNull(str); + return allocateFrom(str, sun.nio.cs.UTF_8.INSTANCE); + } + + /** + * Converts a Java string into a null-terminated C string using the provided charset, + * and storing the result into a memory segment. *

    * This method always replaces malformed-input and unmappable-character * sequences with this charset's default replacement byte array. The @@ -86,21 +103,51 @@ public interface SegmentAllocator { *

    * If the given string contains any {@code '\0'} characters, they will be * copied as well. This means that, depending on the method used to read - * the string, such as {@link MemorySegment#getUtf8String(long)}, the string + * the string, such as {@link MemorySegment#getString(long)}, the string * will appear truncated when read again. * + * @param str the Java string to be converted into a C string. + * @param charset the charset used to {@linkplain Charset#newEncoder() encode} the string bytes. + * @return a new native segment containing the converted C string. + * @throws IllegalArgumentException if {@code charset} is not a {@linkplain StandardCharsets standard charset}. * @implSpec The default implementation for this method copies the contents of the provided Java string - * into a new memory segment obtained by calling {@code this.allocate(str.length() + 1)}. - * @param str the Java string to be converted into a C string. + * into a new memory segment obtained by calling {@code this.allocate(B + N)}, where: + *

      + *
    • {@code B} is the size, in bytes, of the string encoded using the provided charset + * (e.g. {@code str.getBytes(charset).length});
    • + *
    • {@code N} is the size (in bytes) of the terminator char according to the provided charset. For instance, + * this is 1 for {@link StandardCharsets#US_ASCII} and 2 for {@link StandardCharsets#UTF_16}.
    • + *
    */ - default MemorySegment allocateUtf8String(String str) { + @ForceInline + default MemorySegment allocateFrom(String str, Charset charset) { + Objects.requireNonNull(charset); Objects.requireNonNull(str); - return Utils.toCString(str.getBytes(StandardCharsets.UTF_8), this); + int termCharSize = StringSupport.CharsetKind.of(charset).terminatorCharSize(); + MemorySegment segment; + int length; + if (StringSupport.bytesCompatible(str, charset)) { + length = str.length(); + segment = allocateNoInit((long) length + termCharSize); + StringSupport.copyToSegmentRaw(str, segment, 0); + } else { + byte[] bytes = str.getBytes(charset); + length = bytes.length; + segment = allocateNoInit((long) bytes.length + termCharSize); + MemorySegment.copy(bytes, 0, segment, ValueLayout.JAVA_BYTE, 0, bytes.length); + } + for (int i = 0 ; i < termCharSize ; i++) { + segment.set(ValueLayout.JAVA_BYTE, length + i, (byte)0); + } + return segment; } /** - * {@return a new memory segment initialized with the provided {@code byte} {@code value} as - * specified by the provided {@code layout} (i.e. byte ordering, alignment and size)} + * {@return a new memory segment initialized with the provided byte value.} + *

    + * The size of the allocated memory segment is the {@linkplain MemoryLayout#byteSize() size} of the given layout. + * The given value is written into the segment according to the byte order and alignment constraint of the + * given layout. * * @implSpec The default implementation is equivalent to: * {@snippet lang=java : @@ -112,17 +159,19 @@ default MemorySegment allocateUtf8String(String str) { * @param layout the layout of the block of memory to be allocated. * @param value the value to be set in the newly allocated memory segment. */ - default MemorySegment allocate(ValueLayout.OfByte layout, byte value) { + default MemorySegment allocateFrom(ValueLayout.OfByte layout, byte value) { Objects.requireNonNull(layout); - VarHandle handle = layout.varHandle(); - MemorySegment seg = allocate(layout); - handle.set(seg, value); + MemorySegment seg = allocateNoInit(layout); + seg.set(layout, 0, value); return seg; } /** - * {@return a new memory segment initialized with the provided {@code char} {@code value} as - * specified by the provided {@code layout} (i.e. byte ordering, alignment and size)} + * {@return a new memory segment initialized with the provided char value.} + *

    + * The size of the allocated memory segment is the {@linkplain MemoryLayout#byteSize() size} of the given layout. + * The given value is written into the segment according to the byte order and alignment constraint of the + * given layout. * * @implSpec The default implementation is equivalent to: * {@snippet lang=java : @@ -134,17 +183,19 @@ default MemorySegment allocate(ValueLayout.OfByte layout, byte value) { * @param layout the layout of the block of memory to be allocated. * @param value the value to be set in the newly allocated memory segment. */ - default MemorySegment allocate(ValueLayout.OfChar layout, char value) { + default MemorySegment allocateFrom(ValueLayout.OfChar layout, char value) { Objects.requireNonNull(layout); - VarHandle handle = layout.varHandle(); - MemorySegment seg = allocate(layout); - handle.set(seg, value); + MemorySegment seg = allocateNoInit(layout); + seg.set(layout, 0, value); return seg; } /** - * {@return a new memory segment initialized with the provided {@code short} {@code value} as - * specified by the provided {@code layout} (i.e. byte ordering, alignment and size)} + * {@return a new memory segment initialized with the provided short value.} + *

    + * The size of the allocated memory segment is the {@linkplain MemoryLayout#byteSize() size} of the given layout. + * The given value is written into the segment according to the byte order and alignment constraint of the + * given layout. * * @implSpec The default implementation is equivalent to: * {@snippet lang=java : @@ -156,17 +207,19 @@ default MemorySegment allocate(ValueLayout.OfChar layout, char value) { * @param layout the layout of the block of memory to be allocated. * @param value the value to be set in the newly allocated memory segment. */ - default MemorySegment allocate(ValueLayout.OfShort layout, short value) { + default MemorySegment allocateFrom(ValueLayout.OfShort layout, short value) { Objects.requireNonNull(layout); - VarHandle handle = layout.varHandle(); - MemorySegment seg = allocate(layout); - handle.set(seg, value); + MemorySegment seg = allocateNoInit(layout); + seg.set(layout, 0, value); return seg; } /** - * {@return a new memory segment initialized with the provided {@code int} {@code value} as - * specified by the provided {@code layout} (i.e. byte ordering, alignment and size)} + * {@return a new memory segment initialized with the provided int value.} + *

    + * The size of the allocated memory segment is the {@linkplain MemoryLayout#byteSize() size} of the given layout. + * The given value is written into the segment according to the byte order and alignment constraint of the + * given layout. * * @implSpec The default implementation is equivalent to: * {@snippet lang=java : @@ -178,17 +231,19 @@ default MemorySegment allocate(ValueLayout.OfShort layout, short value) { * @param layout the layout of the block of memory to be allocated. * @param value the value to be set in the newly allocated memory segment. */ - default MemorySegment allocate(ValueLayout.OfInt layout, int value) { + default MemorySegment allocateFrom(ValueLayout.OfInt layout, int value) { Objects.requireNonNull(layout); - VarHandle handle = layout.varHandle(); - MemorySegment seg = allocate(layout); - handle.set(seg, value); + MemorySegment seg = allocateNoInit(layout); + seg.set(layout, 0, value); return seg; } /** - * {@return a new memory segment initialized with the provided {@code float} {@code value} as - * specified by the provided {@code layout} (i.e. byte ordering, alignment and size)} + * {@return a new memory segment initialized with the provided float value.} + *

    + * The size of the allocated memory segment is the {@linkplain MemoryLayout#byteSize() size} of the given layout. + * The given value is written into the segment according to the byte order and alignment constraint of the + * given layout. * * @implSpec The default implementation is equivalent to: * {@snippet lang=java : @@ -200,17 +255,19 @@ default MemorySegment allocate(ValueLayout.OfInt layout, int value) { * @param layout the layout of the block of memory to be allocated. * @param value the value to be set in the newly allocated memory segment. */ - default MemorySegment allocate(ValueLayout.OfFloat layout, float value) { + default MemorySegment allocateFrom(ValueLayout.OfFloat layout, float value) { Objects.requireNonNull(layout); - VarHandle handle = layout.varHandle(); - MemorySegment seg = allocate(layout); - handle.set(seg, value); + MemorySegment seg = allocateNoInit(layout); + seg.set(layout, 0, value); return seg; } /** - * {@return a new memory segment initialized with the provided {@code long} {@code value} as - * specified by the provided {@code layout} (i.e. byte ordering, alignment and size)} + * {@return a new memory segment initialized with the provided long value.} + *

    + * The size of the allocated memory segment is the {@linkplain MemoryLayout#byteSize() size} of the given layout. + * The given value is written into the segment according to the byte order and alignment constraint of the + * given layout. * * @implSpec The default implementation is equivalent to: * {@snippet lang=java : @@ -222,17 +279,19 @@ default MemorySegment allocate(ValueLayout.OfFloat layout, float value) { * @param layout the layout of the block of memory to be allocated. * @param value the value to be set in the newly allocated memory segment. */ - default MemorySegment allocate(ValueLayout.OfLong layout, long value) { + default MemorySegment allocateFrom(ValueLayout.OfLong layout, long value) { Objects.requireNonNull(layout); - VarHandle handle = layout.varHandle(); - MemorySegment seg = allocate(layout); - handle.set(seg, value); + MemorySegment seg = allocateNoInit(layout); + seg.set(layout, 0, value); return seg; } /** - * {@return a new memory segment initialized with the provided {@code double} {@code value} as - * specified by the provided {@code layout} (i.e. byte ordering, alignment and size)} + * {@return a new memory segment initialized with the provided double value.} + *

    + * The size of the allocated memory segment is the {@linkplain MemoryLayout#byteSize() size} of the given layout. + * The given value is written into the segment according to the byte order and alignment constraint of the + * given layout. * * @implSpec The default implementation is equivalent to: * {@snippet lang=java : @@ -244,19 +303,21 @@ default MemorySegment allocate(ValueLayout.OfLong layout, long value) { * @param layout the layout of the block of memory to be allocated. * @param value the value to be set in the newly allocated memory segment. */ - default MemorySegment allocate(ValueLayout.OfDouble layout, double value) { + default MemorySegment allocateFrom(ValueLayout.OfDouble layout, double value) { Objects.requireNonNull(layout); - VarHandle handle = layout.varHandle(); - MemorySegment seg = allocate(layout); - handle.set(seg, value); + MemorySegment seg = allocateNoInit(layout); + seg.set(layout, 0, value); return seg; } /** - * {@return a new memory segment initialized with the address of the provided {@code value} as - * specified by the provided {@code layout} (i.e. byte ordering, alignment and size)} + * {@return a new memory segment initialized with the {@linkplain MemorySegment#address() address} of the provided memory segment.} *

    * The address value might be narrowed according to the platform address size (see {@link ValueLayout#ADDRESS}). + *

    + * The size of the allocated memory segment is the {@linkplain MemoryLayout#byteSize() size} of the given layout. + * The given value is written into the segment according to the byte order and alignment constraint of the + * given layout. * * @implSpec The default implementation is equivalent to: * {@snippet lang=java : @@ -268,164 +329,210 @@ default MemorySegment allocate(ValueLayout.OfDouble layout, double value) { * * @param layout the layout of the block of memory to be allocated. * @param value the value to be set in the newly allocated memory segment. + * @throws UnsupportedOperationException if {@code value} is not a {@linkplain MemorySegment#isNative() native} segment. */ - default MemorySegment allocate(AddressLayout layout, MemorySegment value) { + default MemorySegment allocateFrom(AddressLayout layout, MemorySegment value) { Objects.requireNonNull(value); Objects.requireNonNull(layout); - MemorySegment seg = allocate(layout); - layout.varHandle().set(seg, value); - return seg; + MemorySegment segment = allocateNoInit(layout); + segment.set(layout, 0, value); + return segment; } /** - * {@return a new memory segment with a {@linkplain MemorySegment#byteSize() byteSize()} of - * {@code E*layout.byteSize()} initialized with the provided {@code E} {@code byte} {@code elements} as - * specified by the provided {@code layout} (i.e. byte ordering, alignment and size)} - * - * @implSpec The default implementation is equivalent to: - * {@snippet lang=java : - * int size = Objects.requireNonNull(elements).length; - * MemorySegment seg = allocateArray(Objects.requireNonNull(elementLayout), size); - * MemorySegment.copy(elements, 0, seg, elementLayout, 0, size); - * return seg; + * {@return a new memory segment initialized with the contents of the provided segment.} + *

    + * The size of the allocated memory segment is the {@code elementLayout.byteSize() * elementCount}. + * The contents of the source segment is copied into the result segment element by element, according to the byte + * order and alignment constraint of the given element layout. + * + * @implSpec the default implementation for this method is equivalent to the following code: + * {@snippet lang = java: + * MemorySegment dest = this.allocate(elementLayout, elementCount); + * MemorySegment.copy(source, sourceElementLayout, sourceOffset, dest, elementLayout, 0, elementCount); + * return dest; * } - * - * @param elementLayout the element layout of the array to be allocated. - * @param elements the short elements to be copied to the newly allocated memory block. + * @param elementLayout the element layout of the allocated array. + * @param source the source segment. + * @param sourceElementLayout the element layout of the source segment. + * @param sourceOffset the starting offset, in bytes, of the source segment. + * @param elementCount the number of elements in the source segment to be copied. + * @throws IllegalArgumentException if {@code elementLayout.byteSize() != sourceElementLayout.byteSize()}. + * @throws IllegalArgumentException if the source segment/offset are incompatible with the alignment constraint + * in the source element layout. + * @throws IllegalArgumentException if {@code elementLayout.byteAlignment() > elementLayout.byteSize()}. + * @throws IllegalArgumentException if {@code sourceElementLayout.byteAlignment() > sourceElementLayout.byteSize()}. + * @throws IllegalStateException if the {@linkplain MemorySegment#scope() scope} associated with {@code source} is not + * {@linkplain MemorySegment.Scope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code source.isAccessibleBy(T) == false}. + * @throws IndexOutOfBoundsException if {@code elementCount * sourceElementLayout.byteSize()} overflows. + * @throws IndexOutOfBoundsException if {@code sourceOffset > source.byteSize() - (elementCount * sourceElementLayout.byteSize())}. + * @throws IndexOutOfBoundsException if either {@code sourceOffset} or {@code elementCount} are {@code < 0}. */ - default MemorySegment allocateArray(ValueLayout.OfByte elementLayout, byte... elements) { - return copyArrayWithSwapIfNeeded(elements, elementLayout, MemorySegment::ofArray); + @ForceInline + default MemorySegment allocateFrom(ValueLayout elementLayout, MemorySegment source, + ValueLayout sourceElementLayout, long sourceOffset, long elementCount) { + Objects.requireNonNull(source); + Objects.requireNonNull(sourceElementLayout); + Objects.requireNonNull(elementLayout); + MemorySegment dest = allocateNoInit(elementLayout, elementCount); + MemorySegment.copy(source, sourceElementLayout, sourceOffset, dest, elementLayout, 0, elementCount); + return dest; } /** - * {@return a new memory segment with a {@linkplain MemorySegment#byteSize() byteSize()} of - * {@code E*layout.byteSize()} initialized with the provided {@code E} {@code short} {@code elements} as - * specified by the provided {@code layout} (i.e. byte ordering, alignment and size)} - * - * @implSpec The default implementation is equivalent to: - * {@snippet lang=java : - * int size = Objects.requireNonNull(elements).length; - * MemorySegment seg = allocateArray(Objects.requireNonNull(elementLayout), size); - * MemorySegment.copy(elements, 0, seg, elementLayout, 0, size); - * return seg; - * } - * + * {@return a new memory segment initialized with the elements in the provided byte array.} + *

    + * The size of the allocated memory segment is {@code elementLayout.byteSize() * elements.length}. + * The contents of the source array is copied into the result segment element by element, according to the byte + * order and alignment constraint of the given element layout. + * + * @implSpec the default implementation for this method is equivalent to the following code: + * {@snippet lang = java: + * this.allocateFrom(layout, MemorySegment.ofArray(array), + * ValueLayout.JAVA_BYTE, 0, array.length) + *} * @param elementLayout the element layout of the array to be allocated. - * @param elements the short elements to be copied to the newly allocated memory block. + * @param elements the byte elements to be copied to the newly allocated memory block. + * @throws IllegalArgumentException if {@code elementLayout.byteAlignment() > elementLayout.byteSize()}. */ - default MemorySegment allocateArray(ValueLayout.OfShort elementLayout, short... elements) { - return copyArrayWithSwapIfNeeded(elements, elementLayout, MemorySegment::ofArray); + @ForceInline + default MemorySegment allocateFrom(ValueLayout.OfByte elementLayout, byte... elements) { + return allocateFrom(elementLayout, MemorySegment.ofArray(elements), + ValueLayout.JAVA_BYTE, 0, elements.length); } /** - * {@return a new memory segment with a {@linkplain MemorySegment#byteSize() byteSize()} of - * {@code E*layout.byteSize()} initialized with the provided {@code E} {@code char} {@code elements} as - * specified by the provided {@code layout} (i.e. byte ordering, alignment and size)} - * - * @implSpec The default implementation is equivalent to: - * {@snippet lang=java : - * int size = Objects.requireNonNull(elements).length; - * MemorySegment seg = allocateArray(Objects.requireNonNull(elementLayout), size); - * MemorySegment.copy(elements, 0, seg, elementLayout, 0, size); - * return seg; - * } - * + * {@return a new memory segment initialized with the elements in the provided short array.} + *

    + * The size of the allocated memory segment is {@code elementLayout.byteSize() * elements.length}. + * The contents of the source array is copied into the result segment element by element, according to the byte + * order and alignment constraint of the given element layout. + * + * @implSpec the default implementation for this method is equivalent to the following code: + * {@snippet lang = java: + * this.allocateFrom(layout, MemorySegment.ofArray(array), + * ValueLayout.JAVA_SHORT, 0, array.length) + *} * @param elementLayout the element layout of the array to be allocated. * @param elements the short elements to be copied to the newly allocated memory block. + * @throws IllegalArgumentException if {@code elementLayout.byteAlignment() > elementLayout.byteSize()}. */ - default MemorySegment allocateArray(ValueLayout.OfChar elementLayout, char... elements) { - return copyArrayWithSwapIfNeeded(elements, elementLayout, MemorySegment::ofArray); + @ForceInline + default MemorySegment allocateFrom(ValueLayout.OfShort elementLayout, short... elements) { + return allocateFrom(elementLayout, MemorySegment.ofArray(elements), + ValueLayout.JAVA_SHORT, 0, elements.length); } /** - * {@return a new memory segment with a {@linkplain MemorySegment#byteSize() byteSize()} of - * {@code E*layout.byteSize()} initialized with the provided {@code E} {@code int} {@code elements} as - * specified by the provided {@code layout} (i.e. byte ordering, alignment and size)} - * - * @implSpec The default implementation is equivalent to: - * {@snippet lang=java : - * int size = Objects.requireNonNull(elements).length; - * MemorySegment seg = allocateArray(Objects.requireNonNull(elementLayout), size); - * MemorySegment.copy(elements, 0, seg, elementLayout, 0, size); - * return seg; - * } - * + * {@return a new memory segment initialized with the elements in the provided char array.} + *

    + * The size of the allocated memory segment is {@code elementLayout.byteSize() * elements.length}. + * The contents of the source array is copied into the result segment element by element, according to the byte + * order and alignment constraint of the given element layout. + * + * @implSpec the default implementation for this method is equivalent to the following code: + * {@snippet lang = java: + * this.allocateFrom(layout, MemorySegment.ofArray(array), + * ValueLayout.JAVA_CHAR, 0, array.length) + *} * @param elementLayout the element layout of the array to be allocated. - * @param elements the short elements to be copied to the newly allocated memory block. + * @param elements the char elements to be copied to the newly allocated memory block. + * @throws IllegalArgumentException if {@code elementLayout.byteAlignment() > elementLayout.byteSize()}. */ - default MemorySegment allocateArray(ValueLayout.OfInt elementLayout, int... elements) { - return copyArrayWithSwapIfNeeded(elements, elementLayout, MemorySegment::ofArray); + @ForceInline + default MemorySegment allocateFrom(ValueLayout.OfChar elementLayout, char... elements) { + return allocateFrom(elementLayout, MemorySegment.ofArray(elements), + ValueLayout.JAVA_CHAR, 0, elements.length); } /** - * {@return a new memory segment with a {@linkplain MemorySegment#byteSize() byteSize()} of - * {@code E*layout.byteSize()} initialized with the provided {@code E} {@code float} {@code elements} as - * specified by the provided {@code layout} (i.e. byte ordering, alignment and size)} - * - * @implSpec The default implementation is equivalent to: - * {@snippet lang=java : - * int size = Objects.requireNonNull(elements).length; - * MemorySegment seg = allocateArray(Objects.requireNonNull(elementLayout), size); - * MemorySegment.copy(elements, 0, seg, elementLayout, 0, size); - * return seg; - * } - * + * {@return a new memory segment initialized with the elements in the provided int array.} + *

    + * The size of the allocated memory segment is {@code elementLayout.byteSize() * elements.length}. + * The contents of the source array is copied into the result segment element by element, according to the byte + * order and alignment constraint of the given element layout. + * + * @implSpec the default implementation for this method is equivalent to the following code: + * {@snippet lang = java: + * this.allocateFrom(layout, MemorySegment.ofArray(array), + * ValueLayout.JAVA_INT, 0, array.length) + *} * @param elementLayout the element layout of the array to be allocated. - * @param elements the short elements to be copied to the newly allocated memory block. + * @param elements the int elements to be copied to the newly allocated memory block. + * @throws IllegalArgumentException if {@code elementLayout.byteAlignment() > elementLayout.byteSize()}. */ - default MemorySegment allocateArray(ValueLayout.OfFloat elementLayout, float... elements) { - return copyArrayWithSwapIfNeeded(elements, elementLayout, MemorySegment::ofArray); + @ForceInline + default MemorySegment allocateFrom(ValueLayout.OfInt elementLayout, int... elements) { + return allocateFrom(elementLayout, MemorySegment.ofArray(elements), + ValueLayout.JAVA_INT, 0, elements.length); } /** - * {@return a new memory segment with a {@linkplain MemorySegment#byteSize() byteSize()} of - * {@code E*layout.byteSize()} initialized with the provided {@code E} {@code long} {@code elements} as - * specified by the provided {@code layout} (i.e. byte ordering, alignment and size)} - * - * @implSpec The default implementation is equivalent to: - * {@snippet lang=java : - * int size = Objects.requireNonNull(elements).length; - * MemorySegment seg = allocateArray(Objects.requireNonNull(elementLayout), size); - * MemorySegment.copy(elements, 0, seg, elementLayout, 0, size); - * return seg; - * } - * + * {@return a new memory segment initialized with the elements in the provided float array.} + *

    + * The size of the allocated memory segment is {@code elementLayout.byteSize() * elements.length}. + * The contents of the source array is copied into the result segment element by element, according to the byte + * order and alignment constraint of the given element layout. + * + * @implSpec the default implementation for this method is equivalent to the following code: + * {@snippet lang = java: + * this.allocateFrom(layout, MemorySegment.ofArray(array), + * ValueLayout.JAVA_FLOAT, 0, array.length) + *} * @param elementLayout the element layout of the array to be allocated. - * @param elements the short elements to be copied to the newly allocated memory block. + * @param elements the float elements to be copied to the newly allocated memory block. + * @throws IllegalArgumentException if {@code elementLayout.byteAlignment() > elementLayout.byteSize()}. */ - default MemorySegment allocateArray(ValueLayout.OfLong elementLayout, long... elements) { - return copyArrayWithSwapIfNeeded(elements, elementLayout, MemorySegment::ofArray); + @ForceInline + default MemorySegment allocateFrom(ValueLayout.OfFloat elementLayout, float... elements) { + return allocateFrom(elementLayout, MemorySegment.ofArray(elements), + ValueLayout.JAVA_FLOAT, 0, elements.length); } /** - * {@return a new memory segment with a {@linkplain MemorySegment#byteSize() byteSize()} of - * {@code E*layout.byteSize()} initialized with the provided {@code E} {@code double} {@code elements} as - * specified by the provided {@code layout} (i.e. byte ordering, alignment and size)} - * - * @implSpec The default implementation is equivalent to: - * {@snippet lang=java : - * int size = Objects.requireNonNull(elements).length; - * MemorySegment seg = allocateArray(Objects.requireNonNull(elementLayout), size); - * MemorySegment.copy(elements, 0, seg, elementLayout, 0, size); - * return seg; - * } - * + * {@return a new memory segment initialized with the elements in the provided long array.} + *

    + * The size of the allocated memory segment is {@code elementLayout.byteSize() * elements.length}. + * The contents of the source array is copied into the result segment element by element, according to the byte + * order and alignment constraint of the given element layout. + * + * @implSpec the default implementation for this method is equivalent to the following code: + * {@snippet lang = java: + * this.allocateFrom(layout, MemorySegment.ofArray(array), + * ValueLayout.JAVA_LONG, 0, array.length) + *} * @param elementLayout the element layout of the array to be allocated. - * @param elements the short elements to be copied to the newly allocated memory block. + * @param elements the long elements to be copied to the newly allocated memory block. + * @throws IllegalArgumentException if {@code elementLayout.byteAlignment() > elementLayout.byteSize()}. */ - default MemorySegment allocateArray(ValueLayout.OfDouble elementLayout, double... elements) { - return copyArrayWithSwapIfNeeded(elements, elementLayout, MemorySegment::ofArray); + @ForceInline + default MemorySegment allocateFrom(ValueLayout.OfLong elementLayout, long... elements) { + return allocateFrom(elementLayout, MemorySegment.ofArray(elements), + ValueLayout.JAVA_LONG, 0, elements.length); } - private MemorySegment copyArrayWithSwapIfNeeded(Z array, ValueLayout elementLayout, - Function heapSegmentFactory) { - int size = Array.getLength(Objects.requireNonNull(array)); - MemorySegment seg = allocateArray(Objects.requireNonNull(elementLayout), size); - if (size > 0) { - MemorySegment.copy(heapSegmentFactory.apply(array), elementLayout, 0, - seg, elementLayout.withOrder(ByteOrder.nativeOrder()), 0, size); - } - return seg; + /** + * {@return a new memory segment initialized with the elements in the provided double array.} + *

    + * The size of the allocated memory segment is {@code elementLayout.byteSize() * elements.length}. + * The contents of the source array is copied into the result segment element by element, according to the byte + * order and alignment constraint of the given element layout. + * + * @implSpec the default implementation for this method is equivalent to the following code: + * {@snippet lang = java: + * this.allocateFrom(layout, MemorySegment.ofArray(array), + * ValueLayout.JAVA_DOUBLE, 0, array.length) + *} + * @param elementLayout the element layout of the array to be allocated. + * @param elements the double elements to be copied to the newly allocated memory block. + * @throws IllegalArgumentException if {@code elementLayout.byteAlignment() > elementLayout.byteSize()}. + */ + @ForceInline + default MemorySegment allocateFrom(ValueLayout.OfDouble elementLayout, double... elements) { + return allocateFrom(elementLayout, MemorySegment.ofArray(elements), + ValueLayout.JAVA_DOUBLE, 0, elements.length); } /** @@ -452,7 +559,7 @@ default MemorySegment allocate(MemoryLayout layout) { * @throws IllegalArgumentException if {@code elementLayout.byteSize() * count} overflows. * @throws IllegalArgumentException if {@code count < 0}. */ - default MemorySegment allocateArray(MemoryLayout elementLayout, long count) { + default MemorySegment allocate(MemoryLayout elementLayout, long count) { Objects.requireNonNull(elementLayout); if (count < 0) { throw new IllegalArgumentException("Negative array size"); @@ -525,4 +632,25 @@ static SegmentAllocator slicingAllocator(MemorySegment segment) { static SegmentAllocator prefixAllocator(MemorySegment segment) { return (AbstractMemorySegmentImpl)Objects.requireNonNull(segment); } + + @ForceInline + private MemorySegment allocateNoInit(long byteSize) { + return this instanceof ArenaImpl arenaImpl ? + arenaImpl.allocateNoInit(byteSize, 1) : + allocate(byteSize); + } + + @ForceInline + private MemorySegment allocateNoInit(MemoryLayout layout) { + return this instanceof ArenaImpl arenaImpl ? + arenaImpl.allocateNoInit(layout.byteSize(), layout.byteAlignment()) : + allocate(layout); + } + + @ForceInline + private MemorySegment allocateNoInit(MemoryLayout layout, long size) { + return this instanceof ArenaImpl arenaImpl ? + arenaImpl.allocateNoInit(layout.byteSize() * size, layout.byteAlignment()) : + allocate(layout, size); + } } diff --git a/src/java.base/share/classes/java/lang/foreign/SequenceLayout.java b/src/java.base/share/classes/java/lang/foreign/SequenceLayout.java index c08b5ef4a8d..16384f4ac6c 100644 --- a/src/java.base/share/classes/java/lang/foreign/SequenceLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/SequenceLayout.java @@ -26,7 +26,6 @@ package java.lang.foreign; import jdk.internal.foreign.layout.SequenceLayoutImpl; -import jdk.internal.javac.PreviewFeature; /** * A compound layout that denotes a homogeneous repetition of a given element layout. @@ -50,9 +49,8 @@ * @implSpec * This class is immutable, thread-safe and value-based. * - * @since 19 + * @since 22 */ -@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) public sealed interface SequenceLayout extends MemoryLayout permits SequenceLayoutImpl { diff --git a/src/java.base/share/classes/java/lang/foreign/StructLayout.java b/src/java.base/share/classes/java/lang/foreign/StructLayout.java index 81421ff5acf..c0800bb6b1c 100644 --- a/src/java.base/share/classes/java/lang/foreign/StructLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/StructLayout.java @@ -26,7 +26,6 @@ package java.lang.foreign; import jdk.internal.foreign.layout.StructLayoutImpl; -import jdk.internal.javac.PreviewFeature; /** * A group layout whose member layouts are laid out one after the other. @@ -34,9 +33,8 @@ * @implSpec * Implementing classes are immutable, thread-safe and value-based. * - * @since 20 + * @since 22 */ -@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) public sealed interface StructLayout extends GroupLayout permits StructLayoutImpl { /** diff --git a/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java b/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java index 616d0336a0f..aafe44b4d40 100644 --- a/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java +++ b/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java @@ -29,7 +29,6 @@ import jdk.internal.access.SharedSecrets; import jdk.internal.foreign.MemorySessionImpl; import jdk.internal.foreign.Utils; -import jdk.internal.javac.PreviewFeature; import jdk.internal.javac.Restricted; import jdk.internal.loader.BuiltinClassLoader; import jdk.internal.loader.NativeLibrary; @@ -120,9 +119,8 @@ * MemorySegment malloc = stdlib.find("malloc").orElseThrow(); *} * - * @since 19 + * @since 22 */ -@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) @FunctionalInterface public interface SymbolLookup { @@ -216,8 +214,7 @@ static SymbolLookup loaderLookup() { *

    * This method is restricted. * Restricted methods are unsafe, and, if used incorrectly, their use might crash - * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on - * restricted methods, and use safe and supported functionalities, where possible. + * the JVM or, worse, silently result in memory corruption. * * @implNote The process of resolving a library name is OS-specific. For instance, in a POSIX-compliant OS, * the library name is resolved according to the specification of the {@code dlopen} function for that OS. @@ -251,8 +248,7 @@ static SymbolLookup libraryLookup(String name, Arena arena) { *

    * This method is restricted. * Restricted methods are unsafe, and, if used incorrectly, their use might crash - * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on - * restricted methods, and use safe and supported functionalities, where possible. + * the JVM or, worse, silently result in memory corruption. * * @implNote On Linux, the functionalities provided by this factory method and the returned symbol lookup are * implemented using the {@code dlopen}, {@code dlsym} and {@code dlclose} functions. diff --git a/src/java.base/share/classes/java/lang/foreign/UnionLayout.java b/src/java.base/share/classes/java/lang/foreign/UnionLayout.java index 8d0048aee4c..ddd33aeeba5 100644 --- a/src/java.base/share/classes/java/lang/foreign/UnionLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/UnionLayout.java @@ -26,7 +26,6 @@ package java.lang.foreign; import jdk.internal.foreign.layout.UnionLayoutImpl; -import jdk.internal.javac.PreviewFeature; /** * A group layout whose member layouts are laid out at the same starting offset. @@ -34,9 +33,8 @@ * @implSpec * Implementing classes are immutable, thread-safe and value-based. * - * @since 20 + * @since 22 */ -@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) public sealed interface UnionLayout extends GroupLayout permits UnionLayoutImpl { /** diff --git a/src/java.base/share/classes/java/lang/foreign/ValueLayout.java b/src/java.base/share/classes/java/lang/foreign/ValueLayout.java index 585fa9d467d..00f6541f133 100644 --- a/src/java.base/share/classes/java/lang/foreign/ValueLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/ValueLayout.java @@ -25,12 +25,9 @@ package java.lang.foreign; -import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.nio.ByteOrder; - import jdk.internal.foreign.layout.ValueLayouts; -import jdk.internal.javac.PreviewFeature; /** * A layout that models values of basic data types. Examples of values modelled by a value layout are @@ -51,9 +48,8 @@ * @implSpec implementing classes and subclasses are immutable, thread-safe and value-based. * * @sealedGraph - * @since 19 + * @since 22 */ -@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) public sealed interface ValueLayout extends MemoryLayout permits ValueLayout.OfBoolean, ValueLayout.OfByte, ValueLayout.OfChar, ValueLayout.OfShort, ValueLayout.OfInt, ValueLayout.OfFloat, ValueLayout.OfLong, ValueLayout.OfDouble, AddressLayout { @@ -76,66 +72,6 @@ public sealed interface ValueLayout extends MemoryLayout permits @Override ValueLayout withoutName(); - /** - * Creates a strided var handle that can be used to access a memory segment as multi-dimensional - * array. This array has a notional sequence layout featuring {@code shape.length} nested sequence layouts. The element - * layout of the innermost sequence layout in the notional sequence layout is this value layout. The resulting var handle - * is obtained as if calling the {@link #varHandle(PathElement...)} method on the notional layout, with a layout - * path containing exactly {@code shape.length + 1} {@linkplain PathElement#sequenceElement() open sequence layout path elements}. - *

    - * For instance, the following method call: - * - * {@snippet lang=java : - * VarHandle arrayHandle = ValueLayout.JAVA_INT.arrayElementVarHandle(10, 20); - * } - * - * Is equivalent to the following code: - * - * {@snippet lang = java: - * SequenceLayout notionalLayout = MemoryLayout.sequenceLayout( - * MemoryLayout.sequenceLayout(10, MemoryLayout.sequenceLayout(20, ValueLayout.JAVA_INT))); - * VarHandle arrayHandle = notionalLayout.varHandle(PathElement.sequenceElement(), - * PathElement.sequenceElement(), - * PathElement.sequenceElement()); - *} - * - * The resulting var handle {@code arrayHandle} will feature 3 coordinates of type {@code long}; each coordinate - * is interpreted as an index into the corresponding sequence layout. If we refer to the var handle coordinates, from left - * to right, as {@code x}, {@code y} and {@code z} respectively, the final offset accessed by the var handle can be - * computed with the following formula: - * - *

    {@code
    -     * offset = (10 * 20 * 4 * x) + (20 * 4 * y) + (4 * z)
    -     * }
    - * - * Additionally, the values of {@code x}, {@code y} and {@code z} are constrained as follows: - *
      - *
    • {@code 0 <= x < notionalLayout.elementCount() }
    • - *
    • {@code 0 <= y < 10 }
    • - *
    • {@code 0 <= z < 20 }
    • - *
    - *

    - * Consider the following access expressions: - * {@snippet lang=java : - * int value1 = (int) arrayHandle.get(10, 2, 4); // ok, accessed offset = 8176 - * int value2 = (int) arrayHandle.get(0, 0, 30); // out of bounds value for z - * } - * In the first case, access is well-formed, as the values for {@code x}, {@code y} and {@code z} conform to - * the bounds specified above. In the second case, access fails with {@link IndexOutOfBoundsException}, - * as the value for {@code z} is outside its specified bounds. - * - * @param shape the size of each nested array dimension. - * @return a var handle which can be used to access a memory segment as a multi-dimensional array, - * featuring {@code shape.length + 1} - * {@code long} coordinates. - * @throws IllegalArgumentException if {@code shape[i] < 0}, for at least one index {@code i}. - * @throws UnsupportedOperationException if {@code byteAlignment() > byteSize()}. - * @see MethodHandles#memorySegmentViewVarHandle - * @see MemoryLayout#varHandle(PathElement...) - * @see SequenceLayout - */ - VarHandle arrayElementVarHandle(int... shape); - /** * {@return the carrier associated with this value layout} */ @@ -149,19 +85,40 @@ public sealed interface ValueLayout extends MemoryLayout permits /** * {@inheritDoc} + * * @throws IllegalArgumentException {@inheritDoc} */ @Override ValueLayout withByteAlignment(long byteAlignment); + /** + * {@return a var handle which can be used to access values described by this value layout, in a given memory segment.} + *

    + * The returned var handle's {@linkplain VarHandle#varType() var type} is the {@linkplain ValueLayout#carrier() carrier type} of + * this value layout, and the list of coordinate types is {@code (MemorySegment, long)}, where the memory segment coordinate + * corresponds to the memory segment to be accessed, and the {@code long} coordinate corresponds to the byte offset + * into the accessed memory segment at which the access occurs. + *

    + * The returned var handle checks that accesses are aligned according to this value layout's + * {@linkplain MemoryLayout#byteAlignment() alignment constraint}. + * + * @apiNote This method is similar, but more efficient, than calling {@code MemoryLayout#varHandle(PathElement...)} + * with an empty path element array, as it avoids the creation of the var args array. + * + * @apiNote The returned var handle features certain access mode + * restrictions common to all memory access var handles derived from memory layouts. + * + * @see MemoryLayout#varHandle(PathElement...) + */ + VarHandle varHandle(); + /** * A value layout whose carrier is {@code boolean.class}. * * @see #JAVA_BOOLEAN - * @since 19 + * @since 22 */ - @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN) - sealed interface OfBoolean extends ValueLayout permits ValueLayouts.OfBooleanImpl { + sealed interface OfBoolean extends ValueLayout permits ValueLayouts.OfBooleanImpl { /** * {@inheritDoc} @@ -194,10 +151,9 @@ sealed interface OfBoolean extends ValueLayout permits ValueLayouts.OfBooleanImp * A value layout whose carrier is {@code byte.class}. * * @see #JAVA_BYTE - * @since 19 + * @since 22 */ - @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN) - sealed interface OfByte extends ValueLayout permits ValueLayouts.OfByteImpl { + sealed interface OfByte extends ValueLayout permits ValueLayouts.OfByteImpl { /** * {@inheritDoc} @@ -231,10 +187,9 @@ sealed interface OfByte extends ValueLayout permits ValueLayouts.OfByteImpl { * * @see #JAVA_CHAR * @see #JAVA_CHAR_UNALIGNED - * @since 19 + * @since 22 */ - @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN) - sealed interface OfChar extends ValueLayout permits ValueLayouts.OfCharImpl { + sealed interface OfChar extends ValueLayout permits ValueLayouts.OfCharImpl { /** * {@inheritDoc} @@ -268,10 +223,9 @@ sealed interface OfChar extends ValueLayout permits ValueLayouts.OfCharImpl { * * @see #JAVA_SHORT * @see #JAVA_SHORT_UNALIGNED - * @since 19 + * @since 22 */ - @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN) - sealed interface OfShort extends ValueLayout permits ValueLayouts.OfShortImpl { + sealed interface OfShort extends ValueLayout permits ValueLayouts.OfShortImpl { /** * {@inheritDoc} @@ -305,10 +259,9 @@ sealed interface OfShort extends ValueLayout permits ValueLayouts.OfShortImpl { * * @see #JAVA_INT * @see #JAVA_INT_UNALIGNED - * @since 19 + * @since 22 */ - @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN) - sealed interface OfInt extends ValueLayout permits ValueLayouts.OfIntImpl { + sealed interface OfInt extends ValueLayout permits ValueLayouts.OfIntImpl { /** * {@inheritDoc} @@ -342,10 +295,9 @@ sealed interface OfInt extends ValueLayout permits ValueLayouts.OfIntImpl { * * @see #JAVA_FLOAT * @see #JAVA_FLOAT_UNALIGNED - * @since 19 + * @since 22 */ - @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN) - sealed interface OfFloat extends ValueLayout permits ValueLayouts.OfFloatImpl { + sealed interface OfFloat extends ValueLayout permits ValueLayouts.OfFloatImpl { /** * {@inheritDoc} @@ -378,10 +330,9 @@ sealed interface OfFloat extends ValueLayout permits ValueLayouts.OfFloatImpl { * * @see #JAVA_LONG * @see #JAVA_LONG_UNALIGNED - * @since 19 + * @since 22 */ - @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN) - sealed interface OfLong extends ValueLayout permits ValueLayouts.OfLongImpl { + sealed interface OfLong extends ValueLayout permits ValueLayouts.OfLongImpl { /** * {@inheritDoc} @@ -415,10 +366,9 @@ sealed interface OfLong extends ValueLayout permits ValueLayouts.OfLongImpl { * * @see #JAVA_DOUBLE * @see #JAVA_DOUBLE_UNALIGNED - * @since 19 + * @since 22 */ - @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN) - sealed interface OfDouble extends ValueLayout permits ValueLayouts.OfDoubleImpl { + sealed interface OfDouble extends ValueLayout permits ValueLayouts.OfDoubleImpl { /** * {@inheritDoc} diff --git a/src/java.base/share/classes/java/lang/foreign/package-info.java b/src/java.base/share/classes/java/lang/foreign/package-info.java index 8a3bc1c0c4e..a427ccf371f 100644 --- a/src/java.base/share/classes/java/lang/foreign/package-info.java +++ b/src/java.base/share/classes/java/lang/foreign/package-info.java @@ -93,7 +93,7 @@ * ); * * try (Arena arena = Arena.ofConfined()) { - * MemorySegment cString = arena.allocateUtf8String("Hello"); + * MemorySegment cString = arena.allocateFrom("Hello"); * long len = (long)strlen.invokeExact(cString); // 5 * } *} @@ -109,7 +109,7 @@ * into a foreign function call, according to the rules specified by the ABI of the underlying platform. * The {@link java.lang.foreign.Arena} class also provides many useful methods for * interacting with foreign code, such as - * {@linkplain java.lang.foreign.SegmentAllocator#allocateUtf8String(java.lang.String) converting} Java strings into + * {@linkplain java.lang.foreign.SegmentAllocator#allocateFrom(java.lang.String) converting} Java strings into * zero-terminated, UTF-8 strings, as demonstrated in the above example. * *

    Restricted methods

    @@ -135,8 +135,8 @@ * type must not be null, and any null argument will elicit a {@code NullPointerException}. This fact is not individually * documented for methods of this API. * - * @apiNote Usual memory model guarantees, for example stated in {@jls 6.6} and {@jls 10.4}, do not apply - * when accessing native memory segments as these segments are backed by off-heap regions of memory. + * @apiNote Usual memory model guarantees (see {@jls 17.4}) do not apply when accessing native memory segments as + * these segments are backed by off-heap regions of memory. * * @implNote * In the reference implementation, access to restricted methods can be granted to specific modules using the command line option @@ -147,9 +147,7 @@ * * @spec jni/index.html Java Native Interface Specification * - * @since 19 + * @since 22 */ -@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) package java.lang.foreign; -import jdk.internal.javac.PreviewFeature; diff --git a/src/java.base/share/classes/java/lang/foreign/snippet-files/Snippets.java b/src/java.base/share/classes/java/lang/foreign/snippet-files/Snippets.java index dd99c2b4f8d..95b9cce7541 100644 --- a/src/java.base/share/classes/java/lang/foreign/snippet-files/Snippets.java +++ b/src/java.base/share/classes/java/lang/foreign/snippet-files/Snippets.java @@ -122,7 +122,7 @@ public static void main(String[] args) { // @start region="slicing-arena-main": try (Arena slicingArena = new SlicingArena(1000)) { for (int i = 0; i < 10; i++) { - MemorySegment s = slicingArena.allocateArray(JAVA_INT, 1, 2, 3, 4, 5); + MemorySegment s = slicingArena.allocateFrom(JAVA_INT, 1, 2, 3, 4, 5); // ... } } // all memory allocated is released here @@ -148,7 +148,7 @@ static class AddressLayoutSnippets { void withTargetLayout() { AddressLayout addressLayout = ADDRESS; AddressLayout unboundedLayout = addressLayout.withTargetLayout( - sequenceLayout(ValueLayout.JAVA_BYTE)); + sequenceLayout(Long.MAX_VALUE, ValueLayout.JAVA_BYTE)); } } @@ -168,7 +168,7 @@ void downcall() throws Throwable { ); try (Arena arena = Arena.ofConfined()) { - MemorySegment str = arena.allocateUtf8String("Hello"); + MemorySegment str = arena.allocateFrom("Hello"); long len = (long) strlen.invokeExact(str); // 5 } @@ -197,7 +197,7 @@ static int qsortCompare(MemorySegment elem1, MemorySegment elem2) { try (Arena arena = Arena.ofConfined()) { MemorySegment compareFunc = linker.upcallStub(comparHandle, comparDesc, arena); - MemorySegment array = arena.allocateArray(JAVA_INT, 0, 9, 3, 4, 6, 5, 1, 8, 2, 7); + MemorySegment array = arena.allocateFrom(JAVA_INT, 0, 9, 3, 4, 6, 5, 1, 8, 2, 7); qsort.invokeExact(array, 10L, 4L, compareFunc); int[] sorted = array.toArray(JAVA_INT); // [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] @@ -288,7 +288,7 @@ void variadicFunc() throws Throwable { ); try (Arena arena = Arena.ofConfined()) { - int res = (int) printf.invokeExact(arena.allocateUtf8String("%d plus %d equals %d"), 2, 2, 4); //prints "2 plus 2 equals 4" + int res = (int) printf.invokeExact(arena.allocateFrom("%d plus %d equals %d"), 2, 2, 4); //prints "2 plus 2 equals 4" } } @@ -313,7 +313,7 @@ void captureCallState() throws Throwable { try (Arena arena = Arena.ofConfined()) { MemorySegment capturedState = arena.allocate(capturedStateLayout); handle.invoke(capturedState); - int errno = (int) errnoHandle.get(capturedState); + int errno = (int) errnoHandle.get(capturedState, 0L); // use errno } } @@ -351,8 +351,8 @@ void header() throws Throwable { MethodHandle offsetHandle = taggedValues.byteOffsetHandle(PathElement.sequenceElement(), PathElement.groupElement("kind")); - long offset1 = (long) offsetHandle.invokeExact(1L); // 8 - long offset2 = (long) offsetHandle.invokeExact(2L); // 16 + long offset1 = (long) offsetHandle.invokeExact(0L, 1L); // 8 + long offset2 = (long) offsetHandle.invokeExact(0L, 2L); // 16 } void sliceHandle() { @@ -396,7 +396,7 @@ void header() throws NoSuchMethodException, IllegalAccessException { { MemorySegment segment = null; // ... - VarHandle intHandle = MethodHandles.memorySegmentViewVarHandle(ValueLayout.JAVA_INT); + VarHandle intHandle = ValueLayout.JAVA_INT.varHandle(); MethodHandle multiplyExact = MethodHandles.lookup() .findStatic(Math.class, "multiplyExact", MethodType.methodType(long.class, long.class, long.class)); @@ -408,8 +408,13 @@ void header() throws NoSuchMethodException, IllegalAccessException { { MemorySegment segment = null; // ... - VarHandle intHandle = ValueLayout.JAVA_INT.arrayElementVarHandle(); - int value = (int) intHandle.get(segment, 3L); // get int element at offset 3 * 4 = 12 + MemoryLayout segmentLayout = MemoryLayout.structLayout( + ValueLayout.JAVA_INT.withName("size"), + MemoryLayout.sequenceLayout(4, ValueLayout.JAVA_INT).withName("data") // array of 4 elements + ); + VarHandle intHandle = segmentLayout.varHandle(MemoryLayout.PathElement.groupElement("data"), + MemoryLayout.PathElement.sequenceElement()); + int value = (int) intHandle.get(segment, 0L, 3L); // get int element at offset 0 + offsetof(data) + 3 * 4 = 12 } { @@ -524,10 +529,8 @@ void fill() { MemorySegment segment = null; byte value = 42; - var byteHandle = MemoryLayout.sequenceLayout(ValueLayout.JAVA_BYTE) - .varHandle(MemoryLayout.PathElement.sequenceElement()); for (long l = 0; l < segment.byteSize(); l++) { - byteHandle.set(segment.address(), l, value); + segment.set(JAVA_BYTE, l, value); } } @@ -570,7 +573,7 @@ void header() throws Throwable { ); try (Arena arena = Arena.ofConfined()) { - MemorySegment cString = arena.allocateUtf8String("Hello"); + MemorySegment cString = arena.allocateFrom("Hello"); long len = (long) strlen.invokeExact(cString); // 5 } @@ -654,17 +657,6 @@ static class UnionLayoutSnippets { static class ValueLayoutSnippets { - void arrayElementVarHandle() { - VarHandle arrayHandle = ValueLayout.JAVA_INT.arrayElementVarHandle(10, 20); - - SequenceLayout arrayLayout = MemoryLayout.sequenceLayout( - MemoryLayout.sequenceLayout(10, - MemoryLayout.sequenceLayout(20, ValueLayout.JAVA_INT))); - - int value1 = (int) arrayHandle.get(10, 2, 4); // ok, accessed offset = 8176 - int value2 = (int) arrayHandle.get(0, 0, 30); // out of bounds value for z - } - void statics() { ADDRESS.withByteAlignment(1); JAVA_CHAR.withByteAlignment(1); diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java index 49de59d7aa5..da18a994a01 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java @@ -26,8 +26,6 @@ package java.lang.invoke; import jdk.internal.access.SharedSecrets; -import jdk.internal.foreign.Utils; -import jdk.internal.javac.PreviewFeature; import jdk.internal.misc.Unsafe; import jdk.internal.misc.VM; import jdk.internal.org.objectweb.asm.ClassReader; @@ -45,10 +43,6 @@ import sun.security.util.SecurityConstants; import java.lang.constant.ConstantDescs; -import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.lang.invoke.LambdaForm.BasicType; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -7978,85 +7972,6 @@ private static MethodType tableSwitchChecks(MethodHandle defaultCase, MethodHand return expectedType; } - /** - * Creates a var handle object, which can be used to dereference a {@linkplain java.lang.foreign.MemorySegment memory segment} - * at a given byte offset, using the provided value layout. - * - *

    The provided layout specifies the {@linkplain ValueLayout#carrier() carrier type}, - * the {@linkplain ValueLayout#byteSize() byte size}, - * the {@linkplain ValueLayout#byteAlignment() byte alignment} and the {@linkplain ValueLayout#order() byte order} - * associated with the returned var handle. - * - *

    The list of coordinate types associated with the returned var handle is {@code (MemorySegment, long)}, - * where the {@code long} coordinate type corresponds to byte offset into the given memory segment coordinate. - * Thus, the returned var handle accesses bytes at an offset in a given memory segment, composing bytes to or from - * a value of the var handle type. Moreover, the access operation will honor the endianness and the - * alignment constraints expressed in the provided layout. - * - *

    As an example, consider the memory layout expressed by a {@link GroupLayout} instance constructed as follows: - * {@snippet lang="java" : - * GroupLayout seq = java.lang.foreign.MemoryLayout.structLayout( - * MemoryLayout.paddingLayout(4), - * ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN).withName("value") - * ); - * } - * To access the member layout named {@code value}, we can construct a memory segment view var handle as follows: - * {@snippet lang="java" : - * VarHandle handle = MethodHandles.memorySegmentViewVarHandle(ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN)); //(MemorySegment, long) -> int - * handle = MethodHandles.insertCoordinates(handle, 1, 4); //(MemorySegment) -> int - * } - * - * @apiNote The resulting var handle features certain access mode restrictions, - * which are common to all memory segment view var handles. A memory segment view var handle is associated - * with an access size {@code S} and an alignment constraint {@code B} - * (both expressed in bytes). We say that a memory access operation is fully aligned if it occurs - * at a memory address {@code A} which is compatible with both alignment constraints {@code S} and {@code B}. - * If access is fully aligned then following access modes are supported and are - * guaranteed to support atomic access: - *

      - *
    • read write access modes for all {@code T}, with the exception of - * access modes {@code get} and {@code set} for {@code long} and - * {@code double} on 32-bit platforms. - *
    • atomic update access modes for {@code int}, {@code long}, - * {@code float}, {@code double} or {@link MemorySegment}. - * (Future major platform releases of the JDK may support additional - * types for certain currently unsupported access modes.) - *
    • numeric atomic update access modes for {@code int}, {@code long} and {@link MemorySegment}. - * (Future major platform releases of the JDK may support additional - * numeric types for certain currently unsupported access modes.) - *
    • bitwise atomic update access modes for {@code int}, {@code long} and {@link MemorySegment}. - * (Future major platform releases of the JDK may support additional - * numeric types for certain currently unsupported access modes.) - *
    - * - * If {@code T} is {@code float}, {@code double} or {@link MemorySegment} then atomic - * update access modes compare values using their bitwise representation - * (see {@link Float#floatToRawIntBits}, - * {@link Double#doubleToRawLongBits} and {@link MemorySegment#address()}, respectively). - *

    - * Alternatively, a memory access operation is partially aligned if it occurs at a memory address {@code A} - * which is only compatible with the alignment constraint {@code B}; in such cases, access for anything other than the - * {@code get} and {@code set} access modes will result in an {@code IllegalStateException}. If access is partially aligned, - * atomic access is only guaranteed with respect to the largest power of two that divides the GCD of {@code A} and {@code S}. - *

    - * In all other cases, we say that a memory access operation is misaligned; in such cases an - * {@code IllegalStateException} is thrown, irrespective of the access mode being used. - *

    - * Finally, if {@code T} is {@code MemorySegment} all write access modes throw {@link IllegalArgumentException} - * unless the value to be written is a {@linkplain MemorySegment#isNative() native} memory segment. - * - * @param layout the value layout for which a memory access handle is to be obtained. - * @return the new memory segment view var handle. - * @throws NullPointerException if {@code layout} is {@code null}. - * @see MemoryLayout#varHandle(MemoryLayout.PathElement...) - * @since 19 - */ - @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) - public static VarHandle memorySegmentViewVarHandle(ValueLayout layout) { - Objects.requireNonNull(layout); - return Utils.makeSegmentViewVarHandle(layout); - } - /** * Adapts a target var handle by pre-processing incoming and outgoing values using a pair of filter functions. *

    @@ -8087,9 +8002,8 @@ public static VarHandle memorySegmentViewVarHandle(ValueLayout layout) { * other than {@code (A... , S) -> T} and {@code (A... , T) -> S}, respectively, where {@code T} is the type of the target var handle, * or if it's determined that either {@code filterFromTarget} or {@code filterToTarget} throws any checked exceptions. * @throws NullPointerException if any of the arguments is {@code null}. - * @since 19 + * @since 22 */ - @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) public static VarHandle filterValue(VarHandle target, MethodHandle filterToTarget, MethodHandle filterFromTarget) { return VarHandles.filterValue(target, filterToTarget, filterFromTarget); } @@ -8123,9 +8037,8 @@ public static VarHandle filterValue(VarHandle target, MethodHandle filterToTarge * or if more filters are provided than the actual number of coordinate types available starting at {@code pos}, * or if it's determined that any of the filters throws any checked exceptions. * @throws NullPointerException if any of the arguments is {@code null} or {@code filters} contains {@code null}. - * @since 19 + * @since 22 */ - @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) public static VarHandle filterCoordinates(VarHandle target, int pos, MethodHandle... filters) { return VarHandles.filterCoordinates(target, pos, filters); } @@ -8155,9 +8068,8 @@ public static VarHandle filterCoordinates(VarHandle target, int pos, MethodHandl * other than {@code T1, T2 ... Tn }, where {@code T1, T2 ... Tn} are the coordinate types starting at position {@code pos} * of the target var handle. * @throws NullPointerException if any of the arguments is {@code null} or {@code values} contains {@code null}. - * @since 19 + * @since 22 */ - @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) public static VarHandle insertCoordinates(VarHandle target, int pos, Object... values) { return VarHandles.insertCoordinates(target, pos, values); } @@ -8198,9 +8110,8 @@ public static VarHandle insertCoordinates(VarHandle target, int pos, Object... v * a coordinate of {@code newCoordinates}, or if two corresponding coordinate types in * the target var handle and in {@code newCoordinates} are not identical. * @throws NullPointerException if any of the arguments is {@code null} or {@code newCoordinates} contains {@code null}. - * @since 19 + * @since 22 */ - @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) public static VarHandle permuteCoordinates(VarHandle target, List> newCoordinates, int... reorder) { return VarHandles.permuteCoordinates(target, newCoordinates, reorder); } @@ -8242,9 +8153,8 @@ public static VarHandle permuteCoordinates(VarHandle target, List> newC * if the resulting var handle's type would have too many coordinates, * or if it's determined that {@code filter} throws any checked exceptions. * @throws NullPointerException if any of the arguments is {@code null}. - * @since 19 + * @since 22 */ - @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) public static VarHandle collectCoordinates(VarHandle target, int pos, MethodHandle filter) { return VarHandles.collectCoordinates(target, pos, filter); } @@ -8268,9 +8178,8 @@ public static VarHandle collectCoordinates(VarHandle target, int pos, MethodHand * before calling the target var handle * @throws IllegalArgumentException if {@code pos} is not between 0 and the target var handle coordinate arity, inclusive. * @throws NullPointerException if any of the arguments is {@code null} or {@code valueTypes} contains {@code null}. - * @since 19 + * @since 22 */ - @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) public static VarHandle dropCoordinates(VarHandle target, int pos, Class... valueTypes) { return VarHandles.dropCoordinates(target, pos, valueTypes); } diff --git a/src/java.base/share/classes/java/lang/invoke/VarHandleSegmentViewBase.java b/src/java.base/share/classes/java/lang/invoke/VarHandleSegmentViewBase.java index 199eb7d6c11..5cb71cf0424 100644 --- a/src/java.base/share/classes/java/lang/invoke/VarHandleSegmentViewBase.java +++ b/src/java.base/share/classes/java/lang/invoke/VarHandleSegmentViewBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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 @@ -25,6 +25,8 @@ package java.lang.invoke; +import jdk.internal.foreign.Utils; + /** * Base class for memory segment var handle view implementations. */ @@ -54,6 +56,10 @@ abstract sealed class VarHandleSegmentViewBase extends VarHandle permits } static IllegalArgumentException newIllegalArgumentExceptionForMisalignedAccess(long address) { - return new IllegalArgumentException("Misaligned access at address: " + address); + return new IllegalArgumentException("Misaligned access at address: " + Utils.toHexString(address)); + } + + static UnsupportedOperationException newUnsupportedAccessModeForAlignment(long alignment) { + return new UnsupportedOperationException("Unsupported access mode for alignment: " + alignment); } } diff --git a/src/java.base/share/classes/java/lang/invoke/X-VarHandleSegmentView.java.template b/src/java.base/share/classes/java/lang/invoke/X-VarHandleSegmentView.java.template index 303acbdaeae..aa1c232e5df 100644 --- a/src/java.base/share/classes/java/lang/invoke/X-VarHandleSegmentView.java.template +++ b/src/java.base/share/classes/java/lang/invoke/X-VarHandleSegmentView.java.template @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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 @@ -43,7 +43,7 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess(); - static final int VM_ALIGN = $BoxType$.BYTES - 1; + static final int NON_PLAIN_ACCESS_MIN_ALIGN_MASK = $BoxType$.BYTES - 1; static final VarForm FORM = new VarForm(VarHandleSegmentAs$Type$s.class, MemorySegment.class, $type$.class, long.class); @@ -104,16 +104,15 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { } @ForceInline - static long offset(AbstractMemorySegmentImpl bb, long offset, long alignmentMask) { - long address = offsetNoVMAlignCheck(bb, offset, alignmentMask); - if ((address & VM_ALIGN) != 0) { - throw VarHandleSegmentViewBase.newIllegalArgumentExceptionForMisalignedAccess(address); + static long offsetNonPlain(AbstractMemorySegmentImpl bb, long offset, long alignmentMask) { + if ((alignmentMask & NON_PLAIN_ACCESS_MIN_ALIGN_MASK) != NON_PLAIN_ACCESS_MIN_ALIGN_MASK) { + throw VarHandleSegmentViewBase.newUnsupportedAccessModeForAlignment(alignmentMask + 1); } - return address; + return offsetPlain(bb, offset, alignmentMask); } @ForceInline - static long offsetNoVMAlignCheck(AbstractMemorySegmentImpl bb, long offset, long alignmentMask) { + static long offsetPlain(AbstractMemorySegmentImpl bb, long offset, long alignmentMask) { long base = bb.unsafeGetOffset(); long address = base + offset; long maxAlignMask = bb.maxAlignMask(); @@ -130,18 +129,18 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { #if[floatingPoint] $rawType$ rawValue = SCOPED_MEMORY_ACCESS.get$RawType$Unaligned(bb.sessionImpl(), bb.unsafeGetBase(), - offsetNoVMAlignCheck(bb, base, handle.alignmentMask), + offsetPlain(bb, base, handle.alignmentMask), handle.be); return $Type$.$rawType$BitsTo$Type$(rawValue); #else[floatingPoint] #if[byte] return SCOPED_MEMORY_ACCESS.get$Type$(bb.sessionImpl(), bb.unsafeGetBase(), - offsetNoVMAlignCheck(bb, base, handle.alignmentMask)); + offsetPlain(bb, base, handle.alignmentMask)); #else[byte] return SCOPED_MEMORY_ACCESS.get$Type$Unaligned(bb.sessionImpl(), bb.unsafeGetBase(), - offsetNoVMAlignCheck(bb, base, handle.alignmentMask), + offsetPlain(bb, base, handle.alignmentMask), handle.be); #end[byte] #end[floatingPoint] @@ -154,19 +153,19 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { #if[floatingPoint] SCOPED_MEMORY_ACCESS.put$RawType$Unaligned(bb.sessionImpl(), bb.unsafeGetBase(), - offsetNoVMAlignCheck(bb, base, handle.alignmentMask), + offsetPlain(bb, base, handle.alignmentMask), $Type$.$type$ToRaw$RawType$Bits(value), handle.be); #else[floatingPoint] #if[byte] SCOPED_MEMORY_ACCESS.put$Type$(bb.sessionImpl(), bb.unsafeGetBase(), - offsetNoVMAlignCheck(bb, base, handle.alignmentMask), + offsetPlain(bb, base, handle.alignmentMask), value); #else[byte] SCOPED_MEMORY_ACCESS.put$Type$Unaligned(bb.sessionImpl(), bb.unsafeGetBase(), - offsetNoVMAlignCheck(bb, base, handle.alignmentMask), + offsetPlain(bb, base, handle.alignmentMask), value, handle.be); #end[byte] @@ -180,7 +179,7 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { return convEndian(handle.be, SCOPED_MEMORY_ACCESS.get$RawType$Volatile(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask))); + offsetNonPlain(bb, base, handle.alignmentMask))); } @ForceInline @@ -189,7 +188,7 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { AbstractMemorySegmentImpl bb = checkAddress(obb, base, handle.length, false); SCOPED_MEMORY_ACCESS.put$RawType$Volatile(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), convEndian(handle.be, value)); } @@ -200,7 +199,7 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { return convEndian(handle.be, SCOPED_MEMORY_ACCESS.get$RawType$Acquire(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask))); + offsetNonPlain(bb, base, handle.alignmentMask))); } @ForceInline @@ -209,7 +208,7 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { AbstractMemorySegmentImpl bb = checkAddress(obb, base, handle.length, false); SCOPED_MEMORY_ACCESS.put$RawType$Release(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), convEndian(handle.be, value)); } @@ -220,7 +219,7 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { return convEndian(handle.be, SCOPED_MEMORY_ACCESS.get$RawType$Opaque(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask))); + offsetNonPlain(bb, base, handle.alignmentMask))); } @ForceInline @@ -229,7 +228,7 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { AbstractMemorySegmentImpl bb = checkAddress(obb, base, handle.length, false); SCOPED_MEMORY_ACCESS.put$RawType$Opaque(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), convEndian(handle.be, value)); } #if[CAS] @@ -240,7 +239,7 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { AbstractMemorySegmentImpl bb = checkAddress(obb, base, handle.length, false); return SCOPED_MEMORY_ACCESS.compareAndSet$RawType$(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), convEndian(handle.be, expected), convEndian(handle.be, value)); } @@ -251,7 +250,7 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { return convEndian(handle.be, SCOPED_MEMORY_ACCESS.compareAndExchange$RawType$(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), convEndian(handle.be, expected), convEndian(handle.be, value))); } @@ -262,7 +261,7 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { return convEndian(handle.be, SCOPED_MEMORY_ACCESS.compareAndExchange$RawType$Acquire(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), convEndian(handle.be, expected), convEndian(handle.be, value))); } @@ -273,7 +272,7 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { return convEndian(handle.be, SCOPED_MEMORY_ACCESS.compareAndExchange$RawType$Release(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), convEndian(handle.be, expected), convEndian(handle.be, value))); } @@ -283,7 +282,7 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { AbstractMemorySegmentImpl bb = checkAddress(obb, base, handle.length, false); return SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$Plain(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), convEndian(handle.be, expected), convEndian(handle.be, value)); } @@ -293,7 +292,7 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { AbstractMemorySegmentImpl bb = checkAddress(obb, base, handle.length, false); return SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), convEndian(handle.be, expected), convEndian(handle.be, value)); } @@ -303,7 +302,7 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { AbstractMemorySegmentImpl bb = checkAddress(obb, base, handle.length, false); return SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$Acquire(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), convEndian(handle.be, expected), convEndian(handle.be, value)); } @@ -313,7 +312,7 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { AbstractMemorySegmentImpl bb = checkAddress(obb, base, handle.length, false); return SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$Release(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), convEndian(handle.be, expected), convEndian(handle.be, value)); } @@ -324,7 +323,7 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { return convEndian(handle.be, SCOPED_MEMORY_ACCESS.getAndSet$RawType$(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), convEndian(handle.be, value))); } @@ -335,7 +334,7 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { return convEndian(handle.be, SCOPED_MEMORY_ACCESS.getAndSet$RawType$Acquire(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), convEndian(handle.be, value))); } @@ -346,7 +345,7 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { return convEndian(handle.be, SCOPED_MEMORY_ACCESS.getAndSet$RawType$Release(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), convEndian(handle.be, value))); } #end[CAS] @@ -359,10 +358,10 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { if (handle.be == BE) { return SCOPED_MEMORY_ACCESS.getAndAdd$RawType$(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), delta); } else { - return getAndAddConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), delta); + return getAndAddConvEndianWithCAS(bb, offsetNonPlain(bb, base, handle.alignmentMask), delta); } } @@ -373,10 +372,10 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { if (handle.be == BE) { return SCOPED_MEMORY_ACCESS.getAndAdd$RawType$Acquire(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), delta); } else { - return getAndAddConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), delta); + return getAndAddConvEndianWithCAS(bb, offsetNonPlain(bb, base, handle.alignmentMask), delta); } } @@ -387,10 +386,10 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { if (handle.be == BE) { return SCOPED_MEMORY_ACCESS.getAndAdd$RawType$Release(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), delta); } else { - return getAndAddConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), delta); + return getAndAddConvEndianWithCAS(bb, offsetNonPlain(bb, base, handle.alignmentMask), delta); } } @@ -415,10 +414,10 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { if (handle.be == BE) { return SCOPED_MEMORY_ACCESS.getAndBitwiseOr$RawType$(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), value); } else { - return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value); + return getAndBitwiseOrConvEndianWithCAS(bb, offsetNonPlain(bb, base, handle.alignmentMask), value); } } @@ -429,10 +428,10 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { if (handle.be == BE) { return SCOPED_MEMORY_ACCESS.getAndBitwiseOr$RawType$Release(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), value); } else { - return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value); + return getAndBitwiseOrConvEndianWithCAS(bb, offsetNonPlain(bb, base, handle.alignmentMask), value); } } @@ -443,10 +442,10 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { if (handle.be == BE) { return SCOPED_MEMORY_ACCESS.getAndBitwiseOr$RawType$Acquire(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), value); } else { - return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value); + return getAndBitwiseOrConvEndianWithCAS(bb, offsetNonPlain(bb, base, handle.alignmentMask), value); } } @@ -469,10 +468,10 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { if (handle.be == BE) { return SCOPED_MEMORY_ACCESS.getAndBitwiseAnd$RawType$(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), value); } else { - return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value); + return getAndBitwiseAndConvEndianWithCAS(bb, offsetNonPlain(bb, base, handle.alignmentMask), value); } } @@ -483,10 +482,10 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { if (handle.be == BE) { return SCOPED_MEMORY_ACCESS.getAndBitwiseAnd$RawType$Release(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), value); } else { - return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value); + return getAndBitwiseAndConvEndianWithCAS(bb, offsetNonPlain(bb, base, handle.alignmentMask), value); } } @@ -497,10 +496,10 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { if (handle.be == BE) { return SCOPED_MEMORY_ACCESS.getAndBitwiseAnd$RawType$Acquire(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), value); } else { - return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value); + return getAndBitwiseAndConvEndianWithCAS(bb, offsetNonPlain(bb, base, handle.alignmentMask), value); } } @@ -524,10 +523,10 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { if (handle.be == BE) { return SCOPED_MEMORY_ACCESS.getAndBitwiseXor$RawType$(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), value); } else { - return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value); + return getAndBitwiseXorConvEndianWithCAS(bb, offsetNonPlain(bb, base, handle.alignmentMask), value); } } @@ -538,10 +537,10 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { if (handle.be == BE) { return SCOPED_MEMORY_ACCESS.getAndBitwiseXor$RawType$Release(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), value); } else { - return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value); + return getAndBitwiseXorConvEndianWithCAS(bb, offsetNonPlain(bb, base, handle.alignmentMask), value); } } @@ -552,10 +551,10 @@ final class VarHandleSegmentAs$Type$s extends VarHandleSegmentViewBase { if (handle.be == BE) { return SCOPED_MEMORY_ACCESS.getAndBitwiseXor$RawType$Acquire(bb.sessionImpl(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offsetNonPlain(bb, base, handle.alignmentMask), value); } else { - return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value); + return getAndBitwiseXorConvEndianWithCAS(bb, offsetNonPlain(bb, base, handle.alignmentMask), value); } } diff --git a/src/java.base/share/classes/java/net/Socket.java b/src/java.base/share/classes/java/net/Socket.java index ada2073c408..9477f5deb3c 100644 --- a/src/java.base/share/classes/java/net/Socket.java +++ b/src/java.base/share/classes/java/net/Socket.java @@ -1096,7 +1096,10 @@ public int read(byte[] b, int off, int len) throws IOException { } long start = SocketReadEvent.timestamp(); int nbytes = implRead(b, off, len); - SocketReadEvent.offer(start, nbytes, parent.getRemoteSocketAddress(), getSoTimeout()); + long duration = SocketReadEvent.timestamp() - start; + if (SocketReadEvent.shouldCommit(duration)) { + SocketReadEvent.emit(start, duration, nbytes, parent.getRemoteSocketAddress(), getSoTimeout()); + } return nbytes; } @@ -1209,7 +1212,10 @@ public void write(byte[] b, int off, int len) throws IOException { } long start = SocketWriteEvent.timestamp(); implWrite(b, off, len); - SocketWriteEvent.offer(start, len, parent.getRemoteSocketAddress()); + long duration = SocketWriteEvent.timestamp() - start; + if (SocketWriteEvent.shouldCommit(duration)) { + SocketWriteEvent.emit(start, duration, len, parent.getRemoteSocketAddress()); + } } private void implWrite(byte[] b, int off, int len) throws IOException { diff --git a/src/java.base/share/classes/java/nio/X-Buffer.java.template b/src/java.base/share/classes/java/nio/X-Buffer.java.template index 1339b6ef468..2146c86a577 100644 --- a/src/java.base/share/classes/java/nio/X-Buffer.java.template +++ b/src/java.base/share/classes/java/nio/X-Buffer.java.template @@ -279,6 +279,9 @@ import jdk.internal.util.ArraysSupport; * * @author Mark Reinhold * @author JSR-51 Expert Group +#if[byte] + * @sealedGraph +#end[byte] * @since 1.4 */ diff --git a/src/java.base/share/classes/java/nio/channels/FileChannel.java b/src/java.base/share/classes/java/nio/channels/FileChannel.java index bdc392c59a8..f785b2290cb 100644 --- a/src/java.base/share/classes/java/nio/channels/FileChannel.java +++ b/src/java.base/share/classes/java/nio/channels/FileChannel.java @@ -1088,9 +1088,8 @@ public abstract MappedByteBuffer map(MapMode mode, long position, long size) * @throws UnsupportedOperationException * If an unsupported map mode is specified. * - * @since 19 + * @since 22 */ - @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) public MemorySegment map(MapMode mode, long offset, long size, Arena arena) throws IOException { diff --git a/src/java.base/share/classes/java/security/CodeSigner.java b/src/java.base/share/classes/java/security/CodeSigner.java index 76c8be058ba..d14c215b6cc 100644 --- a/src/java.base/share/classes/java/security/CodeSigner.java +++ b/src/java.base/share/classes/java/security/CodeSigner.java @@ -142,9 +142,9 @@ public boolean equals(Object obj) { public String toString() { StringBuilder sb = new StringBuilder(); sb.append("("); - sb.append("Signer: " + signerCertPath.getCertificates().get(0)); + sb.append("Signer: ").append(signerCertPath.getCertificates().get(0)); if (timestamp != null) { - sb.append("timestamp: " + timestamp); + sb.append("timestamp: ").append(timestamp); } sb.append(")"); return sb.toString(); @@ -160,8 +160,11 @@ public String toString() { */ @java.io.Serial private void readObject(ObjectInputStream ois) - throws IOException, ClassNotFoundException { - ois.defaultReadObject(); - myhash = -1; + throws IOException, ClassNotFoundException { + ois.defaultReadObject(); + if (signerCertPath == null) { + throw new InvalidObjectException("signerCertPath is null"); + } + myhash = -1; } } diff --git a/src/java.base/share/classes/java/security/cert/CertPathHelperImpl.java b/src/java.base/share/classes/java/security/cert/CertPathHelperImpl.java index bd6545a7357..3da6cb3dd4f 100644 --- a/src/java.base/share/classes/java/security/cert/CertPathHelperImpl.java +++ b/src/java.base/share/classes/java/security/cert/CertPathHelperImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -25,12 +25,10 @@ package java.security.cert; -import java.util.*; +import java.util.Date; import sun.security.provider.certpath.CertPathHelper; -import sun.security.x509.GeneralNameInterface; - /** * Helper class that allows the Sun CertPath provider to access * implementation dependent APIs in CertPath framework. @@ -55,11 +53,6 @@ static synchronized void initialize() { } } - protected void implSetPathToNames(X509CertSelector sel, - Set names) { - sel.setPathToNamesInternal(names); - } - protected void implSetDateAndTime(X509CRLSelector sel, Date date, long skew) { sel.setDateAndTime(date, skew); } diff --git a/src/java.base/share/classes/java/security/cert/X509CertSelector.java b/src/java.base/share/classes/java/security/cert/X509CertSelector.java index c472d58b473..9b712411a61 100644 --- a/src/java.base/share/classes/java/security/cert/X509CertSelector.java +++ b/src/java.base/share/classes/java/security/cert/X509CertSelector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -87,10 +87,6 @@ public class X509CertSelector implements CertSelector { private static final ObjectIdentifier ANY_EXTENDED_KEY_USAGE = ObjectIdentifier.of(KnownOIDs.anyExtendedKeyUsage); - static { - CertPathHelperImpl.initialize(); - } - private BigInteger serialNumber; private X500Principal issuer; private X500Principal subject; @@ -1127,14 +1123,6 @@ public void setPathToNames(Collection> names) throws IOException { } } - // called from CertPathHelper - void setPathToNamesInternal(Set names) { - // set names to non-null dummy value - // this breaks getPathToNames() - pathToNames = Collections.>emptySet(); - pathToGeneralNames = names; - } - /** * Adds a name to the pathToNames criterion. The {@code X509Certificate} * must not include name constraints that would prohibit building a diff --git a/src/java.base/share/classes/java/util/HexFormat.java b/src/java.base/share/classes/java/util/HexFormat.java index d54d491f3ab..b8c3a06e7ee 100644 --- a/src/java.base/share/classes/java/util/HexFormat.java +++ b/src/java.base/share/classes/java/util/HexFormat.java @@ -27,6 +27,7 @@ import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; +import jdk.internal.util.HexDigits; import java.io.IOException; import java.io.UncheckedIOException; @@ -150,15 +151,7 @@ public final class HexFormat { -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; /** * Format each byte of an array as a pair of hexadecimal digits. @@ -403,21 +396,20 @@ public A formatHex(A out, byte[] bytes, int fromIndex, in int length = toIndex - fromIndex; if (length > 0) { try { - out.append(prefix); - toHexDigits(out, bytes[fromIndex]); - if (suffix.isEmpty() && delimiter.isEmpty() && prefix.isEmpty()) { - for (int i = 1; i < length; i++) { - toHexDigits(out, bytes[fromIndex + i]); - } + String s = formatOptDelimiter(bytes, fromIndex, toIndex); + if (s != null) { + out.append(s); } else { + out.append(prefix); + toHexDigits(out, bytes[fromIndex]); for (int i = 1; i < length; i++) { out.append(suffix); out.append(delimiter); out.append(prefix); toHexDigits(out, bytes[fromIndex + i]); } + out.append(suffix); } - out.append(suffix); } catch (IOException ioe) { throw new UncheckedIOException(ioe.getMessage(), ioe); } @@ -438,29 +430,36 @@ public A formatHex(A out, byte[] bytes, int fromIndex, in * or non-empty prefix or suffix */ private String formatOptDelimiter(byte[] bytes, int fromIndex, int toIndex) { + char sep; byte[] rep; if (!prefix.isEmpty() || !suffix.isEmpty()) { return null; } + + boolean ucase = digitCase == Case.UPPERCASE; int length = toIndex - fromIndex; if (delimiter.isEmpty()) { // Allocate the byte array and fill in the hex pairs for each byte rep = new byte[checkMaxArraySize(length * 2L)]; for (int i = 0; i < length; i++) { - rep[i * 2] = (byte)toHighHexDigit(bytes[fromIndex + i]); - rep[i * 2 + 1] = (byte)toLowHexDigit(bytes[fromIndex + i]); + short pair = HexDigits.digitPair(bytes[fromIndex + i], ucase); + int pos = i * 2; + rep[pos] = (byte)pair; + rep[pos + 1] = (byte)(pair >>> 8); } - } else if (delimiter.length() == 1 && delimiter.charAt(0) < 256) { + } else if (delimiter.length() == 1 && (sep = delimiter.charAt(0)) < 256) { // Allocate the byte array and fill in the characters for the first byte // Then insert the delimiter and hexadecimal characters for each of the remaining bytes - char sep = delimiter.charAt(0); rep = new byte[checkMaxArraySize(length * 3L - 1L)]; - rep[0] = (byte) toHighHexDigit(bytes[fromIndex]); - rep[1] = (byte) toLowHexDigit(bytes[fromIndex]); + short pair = HexDigits.digitPair(bytes[fromIndex], ucase); + rep[0] = (byte)pair; + rep[1] = (byte)(pair >>> 8); for (int i = 1; i < length; i++) { - rep[i * 3 - 1] = (byte) sep; - rep[i * 3 ] = (byte) toHighHexDigit(bytes[fromIndex + i]); - rep[i * 3 + 1] = (byte) toLowHexDigit(bytes[fromIndex + i]); + int pos = i * 3; + pair = HexDigits.digitPair(bytes[fromIndex + i], ucase); + rep[pos - 1] = (byte) sep; + rep[pos] = (byte)pair; + rep[pos + 1] = (byte)(pair >>> 8); } } else { // Delimiter formatting not to a single byte @@ -887,7 +886,7 @@ private static int checkDigitCount(int fromIndex, int toIndex, int limit) { * otherwise {@code false} */ public static boolean isHexDigit(int ch) { - return ((ch >>> 8) == 0 && DIGITS[ch] >= 0); + return ((ch >>> 7) == 0 && DIGITS[ch] >= 0); } /** @@ -905,7 +904,7 @@ public static boolean isHexDigit(int ch) { */ public static int fromHexDigit(int ch) { int value; - if ((ch >>> 8) == 0 && (value = DIGITS[ch]) >= 0) { + if ((ch >>> 7) == 0 && (value = DIGITS[ch]) >= 0) { return value; } throw new NumberFormatException("not a hexadecimal digit: \"" + (char) ch + "\" = " + ch); diff --git a/src/java.base/share/classes/java/util/jar/Attributes.java b/src/java.base/share/classes/java/util/jar/Attributes.java index 33363fd566c..149bf38f67c 100644 --- a/src/java.base/share/classes/java/util/jar/Attributes.java +++ b/src/java.base/share/classes/java/util/jar/Attributes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -715,9 +715,10 @@ private static void addName(Map names, Name name) { // small footprint cost, but is likely to be quickly paid for by // reducing allocation when reading and parsing typical manifests - // JDK internal attributes + // JDK specific attributes addName(names, new Name("Add-Exports")); addName(names, new Name("Add-Opens")); + addName(names, new Name("Enable-Native-Access")); // LauncherHelper attributes addName(names, new Name("Launcher-Agent-Class")); addName(names, new Name("JavaFX-Application-Class")); diff --git a/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java b/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java index d1326f89175..e76a51e5d68 100644 --- a/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java +++ b/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java @@ -28,6 +28,9 @@ import jdk.internal.access.SharedSecrets; import javax.crypto.SecretKey; +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.security.MessageDigest; import java.security.spec.KeySpec; import java.util.Arrays; @@ -60,7 +63,7 @@ public class SecretKeySpec implements KeySpec, SecretKey { * * @serial */ - private final byte[] key; + private byte[] key; /** * The name of the algorithm associated with this key. @@ -251,4 +254,26 @@ public boolean equals(Object obj) { void clear() { Arrays.fill(key, (byte)0); } + + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + + if (key == null || algorithm == null) { + throw new InvalidObjectException("Missing argument"); + } + + this.key = key.clone(); + if (key.length == 0) { + throw new InvalidObjectException("Invalid key length"); + } + } } diff --git a/src/java.base/share/classes/javax/security/auth/callback/ChoiceCallback.java b/src/java.base/share/classes/javax/security/auth/callback/ChoiceCallback.java index dd6072f5db8..c005b4ea02b 100644 --- a/src/java.base/share/classes/javax/security/auth/callback/ChoiceCallback.java +++ b/src/java.base/share/classes/javax/security/auth/callback/ChoiceCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -25,6 +25,10 @@ package javax.security.auth.callback; +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; + /** *

    Underlying security services instantiate and pass a * {@code ChoiceCallback} to the {@code handle} @@ -48,7 +52,7 @@ public class ChoiceCallback implements Callback, java.io.Serializable { * @serial the list of choices * @since 1.4 */ - private final String[] choices; + private String[] choices; /** * @serial the choice to be used as the default choice * @since 1.4 @@ -72,7 +76,6 @@ public class ChoiceCallback implements Callback, java.io.Serializable { * a list of choices, a default choice, and a boolean specifying * whether multiple selections from the list of choices are allowed. * - * * @param prompt the prompt used to describe the list of choices. * * @param choices the list of choices. The array is cloned to protect @@ -104,15 +107,15 @@ public ChoiceCallback(String prompt, String[] choices, defaultChoice < 0 || defaultChoice >= choices.length) throw new IllegalArgumentException(); + this.prompt = prompt; + this.defaultChoice = defaultChoice; + this.multipleSelectionsAllowed = multipleSelectionsAllowed; + + this.choices = choices.clone(); for (int i = 0; i < choices.length; i++) { if (choices[i] == null || choices[i].isEmpty()) throw new IllegalArgumentException(); } - - this.prompt = prompt; - this.choices = choices.clone(); - this.defaultChoice = defaultChoice; - this.multipleSelectionsAllowed = multipleSelectionsAllowed; } /** @@ -196,4 +199,38 @@ public void setSelectedIndexes(int[] selections) { public int[] getSelectedIndexes() { return selections == null ? null : selections.clone(); } + + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + + if ((prompt == null) || prompt.isEmpty() || + (choices == null) || (choices.length == 0) || + (defaultChoice < 0) || (defaultChoice >= choices.length)) { + throw new InvalidObjectException( + "Missing/invalid prompt/choices"); + } + + choices = choices.clone(); + for (int i = 0; i < choices.length; i++) { + if ((choices[i] == null) || choices[i].isEmpty()) + throw new InvalidObjectException("Null/empty choices"); + } + + if (selections != null) { + selections = selections.clone(); + if (!multipleSelectionsAllowed && (selections.length != 1)) { + throw new InvalidObjectException( + "Multiple selections not allowed"); + } + } + } } diff --git a/src/java.base/share/classes/javax/security/auth/callback/ConfirmationCallback.java b/src/java.base/share/classes/javax/security/auth/callback/ConfirmationCallback.java index 926a3d889e2..437ce7041a7 100644 --- a/src/java.base/share/classes/javax/security/auth/callback/ConfirmationCallback.java +++ b/src/java.base/share/classes/javax/security/auth/callback/ConfirmationCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -25,6 +25,9 @@ package javax.security.auth.callback; +import java.io.IOException; +import java.io.ObjectInputStream; + /** *

    Underlying security services instantiate and pass a * {@code ConfirmationCallback} to the {@code handle} @@ -147,7 +150,7 @@ public class ConfirmationCallback implements Callback, java.io.Serializable { * @serial * @since 1.4 */ - private final String[] options; + private String[] options; /** * @serial * @since 1.4 @@ -252,16 +255,16 @@ public ConfirmationCallback(int messageType, defaultOption < 0 || defaultOption >= options.length) throw new IllegalArgumentException(); - for (int i = 0; i < options.length; i++) { - if (options[i] == null || options[i].isEmpty()) - throw new IllegalArgumentException(); - } - this.prompt = null; this.messageType = messageType; this.optionType = UNSPECIFIED_OPTION; - this.options = options.clone(); this.defaultOption = defaultOption; + + this.options = options.clone(); + for (int i = 0; i < options.length; i++) { + if (options[i] == null || options[i].isEmpty()) + throw new IllegalArgumentException(); + } } /** @@ -372,16 +375,16 @@ public ConfirmationCallback(String prompt, int messageType, defaultOption < 0 || defaultOption >= options.length) throw new IllegalArgumentException(); - for (int i = 0; i < options.length; i++) { - if (options[i] == null || options[i].isEmpty()) - throw new IllegalArgumentException(); - } - this.prompt = prompt; this.messageType = messageType; this.optionType = UNSPECIFIED_OPTION; - this.options = options.clone(); this.defaultOption = defaultOption; + + this.options = options.clone(); + for (int i = 0; i < options.length; i++) { + if (options[i] == null || options[i].isEmpty()) + throw new IllegalArgumentException(); + } } /** @@ -487,4 +490,20 @@ public void setSelectedIndex(int selection) { public int getSelectedIndex() { return selection; } + + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + if (options != null) { + options = options.clone(); + } + } } diff --git a/src/java.base/share/classes/javax/security/auth/callback/PasswordCallback.java b/src/java.base/share/classes/javax/security/auth/callback/PasswordCallback.java index b0758c012b2..bbe7ab882a6 100644 --- a/src/java.base/share/classes/javax/security/auth/callback/PasswordCallback.java +++ b/src/java.base/share/classes/javax/security/auth/callback/PasswordCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -25,6 +25,9 @@ package javax.security.auth.callback; +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.lang.ref.Cleaner; import java.util.Arrays; @@ -157,4 +160,27 @@ public void clearPassword() { private static Runnable cleanerFor(char[] password) { return () -> Arrays.fill(password, ' '); } + + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + + if (prompt == null || prompt.isEmpty()) { + throw new InvalidObjectException("Missing prompt"); + } + + if (inputPassword != null) { + inputPassword = inputPassword.clone(); + cleanable = CleanerFactory.cleaner().register( + this, cleanerFor(inputPassword)); + } + } } diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index 6ea7abf6b95..f026209aba4 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -27,6 +27,7 @@ import java.io.InputStream; import java.lang.annotation.Annotation; +import java.lang.foreign.MemorySegment; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.lang.module.ModuleDescriptor; @@ -273,7 +274,7 @@ public interface JavaLangAccess { * Ensure that the given module has native access. If not, warn or * throw exception depending on the configuration. */ - void ensureNativeAccess(Module m, Class owner, String methodName); + void ensureNativeAccess(Module m, Class owner, String methodName, Class currentClass); /** * Returns the ServicesCatalog for the given Layer. @@ -574,4 +575,14 @@ StackWalker newStackWalkerInstance(Set options, * explicitly set otherwise @ */ String getLoaderNameID(ClassLoader loader); + + /** + * Copy the string bytes to an existing segment, avoiding intermediate copies. + */ + void copyToSegmentRaw(String string, MemorySegment segment, long offset); + + /** + * Are the string bytes compatible with the given charset? + */ + boolean bytesCompatible(String string, Charset charset); } diff --git a/src/java.base/share/classes/jdk/internal/event/SocketReadEvent.java b/src/java.base/share/classes/jdk/internal/event/SocketReadEvent.java index 8f9b6ac5710..d7e3bef0eff 100644 --- a/src/java.base/share/classes/jdk/internal/event/SocketReadEvent.java +++ b/src/java.base/share/classes/jdk/internal/event/SocketReadEvent.java @@ -113,21 +113,19 @@ public static long timestamp() { * {@link #commit(long, long, String, String, int, long, long, boolean)}. * * @param start the start time + * @param duration the duration * @param nbytes how many bytes were transferred * @param remote the address of the remote socket * @param timeout maximum time to wait */ - public static void offer(long start, long nbytes, SocketAddress remote, long timeout) { - long duration = timestamp() - start; - if (shouldCommit(duration)) { - boolean eof = nbytes < 0 ? true : false; - nbytes = nbytes < 0 ? 0 : nbytes; - if (remote instanceof InetSocketAddress isa) { - commit(start, duration, isa.getHostString(), isa.getAddress().getHostAddress(), isa.getPort(), timeout, nbytes, eof); - } else if (remote instanceof UnixDomainSocketAddress udsa) { - String path = "[" + udsa.getPath().toString() + "]"; - commit(start, duration, "Unix domain socket", path, 0, timeout, nbytes, eof); - } + public static void emit(long start, long duration, long nbytes, SocketAddress remote, long timeout) { + boolean eof = nbytes < 0 ? true : false; + nbytes = nbytes < 0 ? 0 : nbytes; + if (remote instanceof InetSocketAddress isa) { + commit(start, duration, isa.getHostString(), isa.getAddress().getHostAddress(), isa.getPort(), timeout, nbytes, eof); + } else if (remote instanceof UnixDomainSocketAddress udsa) { + String path = "[" + udsa.getPath().toString() + "]"; + commit(start, duration, "Unix domain socket", path, 0, timeout, nbytes, eof); } } diff --git a/src/java.base/share/classes/jdk/internal/event/SocketWriteEvent.java b/src/java.base/share/classes/jdk/internal/event/SocketWriteEvent.java index 4fba69d3efe..a60ef69f7d0 100644 --- a/src/java.base/share/classes/jdk/internal/event/SocketWriteEvent.java +++ b/src/java.base/share/classes/jdk/internal/event/SocketWriteEvent.java @@ -108,19 +108,17 @@ public static long timestamp() { * {@link #commit(long, long, String, String, int, long)}. * * @param start the start time + * @param duration the duration * @param bytesWritten how many bytes were sent * @param remote the address of the remote socket being written to */ - public static void offer(long start, long bytesWritten, SocketAddress remote) { - long duration = timestamp() - start; - if (shouldCommit(duration)) { - long bytes = bytesWritten < 0 ? 0 : bytesWritten; - if (remote instanceof InetSocketAddress isa) { - commit(start, duration, isa.getHostString(), isa.getAddress().getHostAddress(), isa.getPort(), bytes); - } else if (remote instanceof UnixDomainSocketAddress udsa) { - String path = "[" + udsa.getPath().toString() + "]"; - commit(start, duration, "Unix domain socket", path, 0, bytes); - } + public static void emit(long start, long duration, long bytesWritten, SocketAddress remote) { + long bytes = bytesWritten < 0 ? 0 : bytesWritten; + if (remote instanceof InetSocketAddress isa) { + commit(start, duration, isa.getHostString(), isa.getAddress().getHostAddress(), isa.getPort(), bytes); + } else if (remote instanceof UnixDomainSocketAddress udsa) { + String path = "[" + udsa.getPath().toString() + "]"; + commit(start, duration, "Unix domain socket", path, 0, bytes); } } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java index c2c3ef3da18..8b674a90c33 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java @@ -36,6 +36,7 @@ import java.nio.IntBuffer; import java.nio.LongBuffer; import java.nio.ShortBuffer; +import java.nio.charset.Charset; import java.util.*; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -122,6 +123,12 @@ public MemorySegment asSlice(long offset, long newSize, long byteAlignment) { return asSliceNoCheck(offset, newSize); } + @Override + public MemorySegment asSlice(long offset, MemoryLayout layout) { + Objects.requireNonNull(layout); + return asSlice(offset, layout.byteSize(), layout.byteAlignment()); + } + @Override @CallerSensitive public final MemorySegment reinterpret(long newSize, Arena arena, Consumer cleanup) { @@ -151,9 +158,9 @@ public MemorySegment reinterpretInternal(Class callerClass, long newSize, Sco } if (!isNative()) throw new UnsupportedOperationException("Not a native segment"); Runnable action = cleanup != null ? - () -> cleanup.accept(NativeMemorySegmentImpl.makeNativeSegmentUnchecked(address(), newSize)) : + () -> cleanup.accept(SegmentFactories.makeNativeSegmentUnchecked(address(), newSize)) : null; - return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(address(), newSize, + return SegmentFactories.makeNativeSegmentUnchecked(address(), newSize, (MemorySessionImpl)scope, action); } @@ -264,7 +271,7 @@ public final Optional asOverlappingSlice(MemorySegment other) { final long thatEnd = thatStart + that.byteSize(); if (thisStart < thatEnd && thisEnd > thatStart) { //overlap occurs - long offsetToThat = this.segmentOffset(that); + long offsetToThat = that.address() - this.address(); long newOffset = offsetToThat >= 0 ? offsetToThat : 0; return Optional.of(asSlice(newOffset, Math.min(this.byteSize() - newOffset, that.byteSize() + offsetToThat))); } @@ -273,12 +280,15 @@ public final Optional asOverlappingSlice(MemorySegment other) { } @Override - public final long segmentOffset(MemorySegment other) { - AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl) Objects.requireNonNull(other); - if (unsafeGetBase() == that.unsafeGetBase()) { - return that.unsafeGetOffset() - this.unsafeGetOffset(); - } - throw new UnsupportedOperationException("Cannot compute offset from native to heap (or vice versa)."); + public MemorySegment copyFrom(MemorySegment src) { + MemorySegment.copy(src, 0, this, 0, src.byteSize()); + return this; + } + + @Override + public long mismatch(MemorySegment other) { + Objects.requireNonNull(other); + return MemorySegment.mismatch(this, 0, byteSize(), other, 0, other.byteSize()); } @Override @@ -741,4 +751,264 @@ private static long getBaseAndScale(Class arrayType) { throw new IllegalArgumentException("Not a supported array class: " + arrayType.getSimpleName()); } } + + // accessors + + @ForceInline + @Override + public byte get(ValueLayout.OfByte layout, long offset) { + return (byte) layout.varHandle().get(this, offset); + } + + @ForceInline + @Override + public void set(ValueLayout.OfByte layout, long offset, byte value) { + layout.varHandle().set(this, offset, value); + } + + @ForceInline + @Override + public boolean get(ValueLayout.OfBoolean layout, long offset) { + return (boolean) layout.varHandle().get(this, offset); + } + + @ForceInline + @Override + public void set(ValueLayout.OfBoolean layout, long offset, boolean value) { + layout.varHandle().set(this, offset, value); + } + + @ForceInline + @Override + public char get(ValueLayout.OfChar layout, long offset) { + return (char) layout.varHandle().get(this, offset); + } + + @ForceInline + @Override + public void set(ValueLayout.OfChar layout, long offset, char value) { + layout.varHandle().set(this, offset, value); + } + + @ForceInline + @Override + public short get(ValueLayout.OfShort layout, long offset) { + return (short) layout.varHandle().get(this, offset); + } + + @ForceInline + @Override + public void set(ValueLayout.OfShort layout, long offset, short value) { + layout.varHandle().set(this, offset, value); + } + + @ForceInline + @Override + public int get(ValueLayout.OfInt layout, long offset) { + return (int) layout.varHandle().get(this, offset); + } + + @ForceInline + @Override + public void set(ValueLayout.OfInt layout, long offset, int value) { + layout.varHandle().set(this, offset, value); + } + + @ForceInline + @Override + public float get(ValueLayout.OfFloat layout, long offset) { + return (float) layout.varHandle().get(this, offset); + } + + @ForceInline + @Override + public void set(ValueLayout.OfFloat layout, long offset, float value) { + layout.varHandle().set(this, offset, value); + } + + @ForceInline + @Override + public long get(ValueLayout.OfLong layout, long offset) { + return (long) layout.varHandle().get(this, offset); + } + + @ForceInline + @Override + public void set(ValueLayout.OfLong layout, long offset, long value) { + layout.varHandle().set(this, offset, value); + } + + @ForceInline + @Override + public double get(ValueLayout.OfDouble layout, long offset) { + return (double) layout.varHandle().get(this, offset); + } + + @ForceInline + @Override + public void set(ValueLayout.OfDouble layout, long offset, double value) { + layout.varHandle().set(this, offset, value); + } + + @ForceInline + @Override + public MemorySegment get(AddressLayout layout, long offset) { + return (MemorySegment) layout.varHandle().get(this, offset); + } + + @ForceInline + @Override + public void set(AddressLayout layout, long offset, MemorySegment value) { + layout.varHandle().set(this, offset, value); + } + + @ForceInline + @Override + public byte getAtIndex(ValueLayout.OfByte layout, long index) { + Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); + return (byte) layout.varHandle().get(this, index * layout.byteSize()); + } + + @ForceInline + @Override + public boolean getAtIndex(ValueLayout.OfBoolean layout, long index) { + Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); + return (boolean) layout.varHandle().get(this, index * layout.byteSize()); + } + + @ForceInline + @Override + public char getAtIndex(ValueLayout.OfChar layout, long index) { + Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); + return (char) layout.varHandle().get(this, index * layout.byteSize()); + } + + @ForceInline + @Override + public void setAtIndex(ValueLayout.OfChar layout, long index, char value) { + Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); + layout.varHandle().set(this, index * layout.byteSize(), value); + } + + @ForceInline + @Override + public short getAtIndex(ValueLayout.OfShort layout, long index) { + Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); + return (short) layout.varHandle().get(this, index * layout.byteSize()); + } + + @ForceInline + @Override + public void setAtIndex(ValueLayout.OfByte layout, long index, byte value) { + Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); + layout.varHandle().set(this, index * layout.byteSize(), value); + } + + @ForceInline + @Override + public void setAtIndex(ValueLayout.OfBoolean layout, long index, boolean value) { + Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); + layout.varHandle().set(this, index * layout.byteSize(), value); + } + + @ForceInline + @Override + public void setAtIndex(ValueLayout.OfShort layout, long index, short value) { + Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); + layout.varHandle().set(this, index * layout.byteSize(), value); + } + + @ForceInline + @Override + public int getAtIndex(ValueLayout.OfInt layout, long index) { + Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); + return (int) layout.varHandle().get(this, index * layout.byteSize()); + } + + @ForceInline + @Override + public void setAtIndex(ValueLayout.OfInt layout, long index, int value) { + Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); + layout.varHandle().set(this, index * layout.byteSize(), value); + } + + @ForceInline + @Override + public float getAtIndex(ValueLayout.OfFloat layout, long index) { + Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); + return (float) layout.varHandle().get(this, index * layout.byteSize()); + } + + @ForceInline + @Override + public void setAtIndex(ValueLayout.OfFloat layout, long index, float value) { + Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); + layout.varHandle().set(this, index * layout.byteSize(), value); + } + + @ForceInline + @Override + public long getAtIndex(ValueLayout.OfLong layout, long index) { + Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); + return (long) layout.varHandle().get(this, index * layout.byteSize()); + } + + @ForceInline + @Override + public void setAtIndex(ValueLayout.OfLong layout, long index, long value) { + Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); + layout.varHandle().set(this, index * layout.byteSize(), value); + } + + @ForceInline + @Override + public double getAtIndex(ValueLayout.OfDouble layout, long index) { + Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); + return (double) layout.varHandle().get(this, index * layout.byteSize()); + } + + @ForceInline + @Override + public void setAtIndex(ValueLayout.OfDouble layout, long index, double value) { + Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); + layout.varHandle().set(this, index * layout.byteSize(), value); + } + + @ForceInline + @Override + public MemorySegment getAtIndex(AddressLayout layout, long index) { + Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); + return (MemorySegment) layout.varHandle().get(this, index * layout.byteSize()); + } + + @ForceInline + @Override + public void setAtIndex(AddressLayout layout, long index, MemorySegment value) { + Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); + layout.varHandle().set(this, index * layout.byteSize(), value); + } + + @Override + public String getString(long offset) { + return getString(offset, sun.nio.cs.UTF_8.INSTANCE); + } + + @Override + public String getString(long offset, Charset charset) { + Objects.requireNonNull(charset); + return StringSupport.read(this, offset, charset); + } + + @Override + public void setString(long offset, String str) { + Objects.requireNonNull(str); + setString(offset, str, sun.nio.cs.UTF_8.INSTANCE); + } + + @Override + public void setString(long offset, String str, Charset charset) { + Objects.requireNonNull(charset); + Objects.requireNonNull(str); + StringSupport.write(this, offset, charset, str); + } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/ArenaImpl.java b/src/java.base/share/classes/jdk/internal/foreign/ArenaImpl.java new file mode 100644 index 00000000000..027aed5f8cc --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/ArenaImpl.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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.foreign; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.MemorySegment.Scope; +import java.util.Objects; + +public final class ArenaImpl implements Arena { + + private final MemorySessionImpl session; + private final boolean shouldReserveMemory; + ArenaImpl(MemorySessionImpl session) { + this.session = session; + shouldReserveMemory = session instanceof ImplicitSession; + } + + @Override + public Scope scope() { + return session; + } + + @Override + public void close() { + session.close(); + } + + public MemorySegment allocateNoInit(long byteSize, long byteAlignment) { + Utils.checkAllocationSizeAndAlign(byteSize, byteAlignment); + return SegmentFactories.allocateSegment(byteSize, byteAlignment, session, shouldReserveMemory); + } + + @Override + public MemorySegment allocate(long byteSize, long byteAlignment) { + MemorySegment segment = allocateNoInit(byteSize, byteAlignment); + return segment.fill((byte)0); + } +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java index e6b0ab7ab8d..dd6a6e49edd 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java @@ -26,7 +26,6 @@ package jdk.internal.foreign; -import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.nio.ByteBuffer; import java.util.Objects; @@ -48,7 +47,7 @@ * using sharper types would require use of type-conversions, which in turn would inhibit some C2 optimizations, * such as the elimination of store barriers in methods like {@link HeapMemorySegmentImpl#dup(long, long, boolean, MemorySessionImpl)}. */ -public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl { +abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl { private static final Unsafe UNSAFE = Unsafe.getUnsafe(); private static final int BYTE_ARR_BASE = UNSAFE.arrayBaseOffset(byte[].class); @@ -110,13 +109,6 @@ public byte[] unsafeGetBase() { return (byte[])Objects.requireNonNull(base); } - public static MemorySegment fromArray(byte[] arr) { - Objects.requireNonNull(arr); - long byteSize = (long)arr.length * Unsafe.ARRAY_BYTE_INDEX_SCALE; - return new OfByte(Unsafe.ARRAY_BYTE_BASE_OFFSET, arr, byteSize, false, - MemorySessionImpl.heapSession(arr)); - } - @Override public long maxAlignMask() { return MAX_ALIGN_1; @@ -144,13 +136,6 @@ public char[] unsafeGetBase() { return (char[])Objects.requireNonNull(base); } - public static MemorySegment fromArray(char[] arr) { - Objects.requireNonNull(arr); - long byteSize = (long)arr.length * Unsafe.ARRAY_CHAR_INDEX_SCALE; - return new OfChar(Unsafe.ARRAY_CHAR_BASE_OFFSET, arr, byteSize, false, - MemorySessionImpl.heapSession(arr)); - } - @Override public long maxAlignMask() { return MAX_ALIGN_2; @@ -178,13 +163,6 @@ public short[] unsafeGetBase() { return (short[])Objects.requireNonNull(base); } - public static MemorySegment fromArray(short[] arr) { - Objects.requireNonNull(arr); - long byteSize = (long)arr.length * Unsafe.ARRAY_SHORT_INDEX_SCALE; - return new OfShort(Unsafe.ARRAY_SHORT_BASE_OFFSET, arr, byteSize, false, - MemorySessionImpl.heapSession(arr)); - } - @Override public long maxAlignMask() { return MAX_ALIGN_2; @@ -212,13 +190,6 @@ public int[] unsafeGetBase() { return (int[])Objects.requireNonNull(base); } - public static MemorySegment fromArray(int[] arr) { - Objects.requireNonNull(arr); - long byteSize = (long)arr.length * Unsafe.ARRAY_INT_INDEX_SCALE; - return new OfInt(Unsafe.ARRAY_INT_BASE_OFFSET, arr, byteSize, false, - MemorySessionImpl.heapSession(arr)); - } - @Override public long maxAlignMask() { return MAX_ALIGN_4; @@ -246,13 +217,6 @@ public long[] unsafeGetBase() { return (long[])Objects.requireNonNull(base); } - public static MemorySegment fromArray(long[] arr) { - Objects.requireNonNull(arr); - long byteSize = (long)arr.length * Unsafe.ARRAY_LONG_INDEX_SCALE; - return new OfLong(Unsafe.ARRAY_LONG_BASE_OFFSET, arr, byteSize, false, - MemorySessionImpl.heapSession(arr)); - } - @Override public long maxAlignMask() { return MAX_ALIGN_8; @@ -280,13 +244,6 @@ public float[] unsafeGetBase() { return (float[])Objects.requireNonNull(base); } - public static MemorySegment fromArray(float[] arr) { - Objects.requireNonNull(arr); - long byteSize = (long)arr.length * Unsafe.ARRAY_FLOAT_INDEX_SCALE; - return new OfFloat(Unsafe.ARRAY_FLOAT_BASE_OFFSET, arr, byteSize, false, - MemorySessionImpl.heapSession(arr)); - } - @Override public long maxAlignMask() { return MAX_ALIGN_4; @@ -314,13 +271,6 @@ public double[] unsafeGetBase() { return (double[])Objects.requireNonNull(base); } - public static MemorySegment fromArray(double[] arr) { - Objects.requireNonNull(arr); - long byteSize = (long)arr.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE; - return new OfDouble(Unsafe.ARRAY_DOUBLE_BASE_OFFSET, arr, byteSize, false, - MemorySessionImpl.heapSession(arr)); - } - @Override public long maxAlignMask() { return MAX_ALIGN_8; diff --git a/src/java.base/share/classes/jdk/internal/foreign/LayoutPath.java b/src/java.base/share/classes/jdk/internal/foreign/LayoutPath.java index f368a8b7f23..f87bbf306c3 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/LayoutPath.java +++ b/src/java.base/share/classes/jdk/internal/foreign/LayoutPath.java @@ -26,6 +26,7 @@ package jdk.internal.foreign; import jdk.internal.vm.annotation.ForceInline; +import jdk.internal.vm.annotation.Stable; import java.lang.foreign.AddressLayout; import java.lang.foreign.GroupLayout; @@ -39,8 +40,14 @@ import java.lang.invoke.MethodType; import java.lang.invoke.VarHandle; import java.util.Arrays; +import java.util.List; +import java.util.Locale; import java.util.Objects; import java.util.function.UnaryOperator; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static java.util.stream.Collectors.joining; /** * This class provide support for constructing layout paths; that is, starting from a root path (see {@link #rootPath(MemoryLayout)}, @@ -61,6 +68,7 @@ public class LayoutPath { private static final MethodHandle MH_SLICE_LAYOUT; private static final MethodHandle MH_CHECK_ALIGN; private static final MethodHandle MH_SEGMENT_RESIZE; + private static final MethodHandle MH_ADD; static { try { @@ -72,9 +80,11 @@ public class LayoutPath { MH_SLICE_LAYOUT = lookup.findVirtual(MemorySegment.class, "asSlice", MethodType.methodType(MemorySegment.class, long.class, MemoryLayout.class)); MH_CHECK_ALIGN = lookup.findStatic(LayoutPath.class, "checkAlign", - MethodType.methodType(MemorySegment.class, MemorySegment.class, MemoryLayout.class)); + MethodType.methodType(void.class, MemorySegment.class, long.class, MemoryLayout.class)); MH_SEGMENT_RESIZE = lookup.findStatic(LayoutPath.class, "resizeSegment", MethodType.methodType(MemorySegment.class, MemorySegment.class, MemoryLayout.class)); + MH_ADD = lookup.findStatic(Long.class, "sum", + MethodType.methodType(long.class, long.class, long.class)); } catch (Throwable ex) { throw new ExceptionInInitializerError(ex); } @@ -84,7 +94,6 @@ public class LayoutPath { private final long offset; private final LayoutPath enclosing; private final long[] strides; - private final long[] bounds; private final MethodHandle[] derefAdapters; @@ -100,15 +109,13 @@ private LayoutPath(MemoryLayout layout, long offset, long[] strides, long[] boun // Layout path selector methods public LayoutPath sequenceElement() { - check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout"); - SequenceLayout seq = (SequenceLayout)layout; + SequenceLayout seq = requireSequenceLayout(); MemoryLayout elem = seq.elementLayout(); return LayoutPath.nestedPath(elem, offset, addStride(elem.byteSize()), addBound(seq.elementCount()), derefAdapters, this); } public LayoutPath sequenceElement(long start, long step) { - check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout"); - SequenceLayout seq = (SequenceLayout)layout; + SequenceLayout seq = requireSequenceLayout(); checkSequenceBounds(seq, start); MemoryLayout elem = seq.elementLayout(); long elemSize = elem.byteSize(); @@ -117,21 +124,19 @@ public LayoutPath sequenceElement(long start, long step) { start + 1; long maxIndex = Math.ceilDiv(nelems, Math.abs(step)); return LayoutPath.nestedPath(elem, offset + (start * elemSize), - addStride(elemSize * step), addBound(maxIndex), derefAdapters, this); + addStride(elemSize * step), addBound(maxIndex), derefAdapters, this); } public LayoutPath sequenceElement(long index) { - check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout"); - SequenceLayout seq = (SequenceLayout)layout; + SequenceLayout seq = requireSequenceLayout(); checkSequenceBounds(seq, index); long elemSize = seq.elementLayout().byteSize(); long elemOffset = elemSize * index; - return LayoutPath.nestedPath(seq.elementLayout(), offset + elemOffset, strides, bounds, derefAdapters,this); + return LayoutPath.nestedPath(seq.elementLayout(), offset + elemOffset, strides, bounds, derefAdapters, this); } public LayoutPath groupElement(String name) { - check(GroupLayout.class, "attempting to select a group element from a non-group layout"); - GroupLayout g = (GroupLayout)layout; + GroupLayout g = requireGroupLayout(); long offset = 0; MemoryLayout elem = null; for (int i = 0; i < g.memberLayouts().size(); i++) { @@ -145,20 +150,21 @@ public LayoutPath groupElement(String name) { } } if (elem == null) { - throw badLayoutPath("cannot resolve '" + name + "' in layout " + layout); + throw badLayoutPath( + String.format("cannot resolve '%s' in layout %s", name, breadcrumbs())); } return LayoutPath.nestedPath(elem, this.offset + offset, strides, bounds, derefAdapters, this); } public LayoutPath groupElement(long index) { - check(GroupLayout.class, "attempting to select a group element from a non-group layout"); - GroupLayout g = (GroupLayout)layout; + GroupLayout g = requireGroupLayout(); long elemSize = g.memberLayouts().size(); long offset = 0; MemoryLayout elem = null; for (int i = 0; i <= index; i++) { if (i == elemSize) { - throw badLayoutPath("cannot resolve element " + index + " in layout " + layout); + throw badLayoutPath( + String.format("cannot resolve element %d in layout: %s", index, breadcrumbs())); } elem = g.memberLayouts().get(i); if (g instanceof StructLayout && i < index) { @@ -171,7 +177,8 @@ public LayoutPath groupElement(long index) { public LayoutPath derefElement() { if (!(layout instanceof AddressLayout addressLayout) || addressLayout.targetLayout().isEmpty()) { - throw badLayoutPath("Cannot dereference layout: " + layout); + throw badLayoutPath( + String.format("Cannot dereference layout: %s", breadcrumbs())); } MemoryLayout derefLayout = addressLayout.targetLayout().get(); MethodHandle handle = dereferenceHandle(false).toMethodHandle(VarHandle.AccessMode.GET); @@ -196,26 +203,41 @@ public VarHandle dereferenceHandle() { public VarHandle dereferenceHandle(boolean adapt) { if (!(layout instanceof ValueLayout valueLayout)) { - throw new IllegalArgumentException("Path does not select a value layout"); + throw new IllegalArgumentException( + String.format("Path does not select a value layout: %s", breadcrumbs())); } // If we have an enclosing layout, drop the alignment check for the accessed element, // we check the root layout instead ValueLayout accessedLayout = enclosing != null ? valueLayout.withByteAlignment(1) : valueLayout; - VarHandle handle = Utils.makeSegmentViewVarHandle(accessedLayout); + VarHandle handle = accessedLayout.varHandle(); handle = MethodHandles.collectCoordinates(handle, 1, offsetHandle()); // we only have to check the alignment of the root layout for the first dereference we do, // as each dereference checks the alignment of the target address when constructing its segment // (see Utils::longToAddress) if (derefAdapters.length == 0 && enclosing != null) { - MethodHandle checkHandle = MethodHandles.insertArguments(MH_CHECK_ALIGN, 1, rootLayout()); - handle = MethodHandles.filterCoordinates(handle, 0, checkHandle); + // insert align check for the root layout on the initial MS + offset + List> coordinateTypes = handle.coordinateTypes(); + MethodHandle alignCheck = MethodHandles.insertArguments(MH_CHECK_ALIGN, 2, rootLayout()); + handle = MethodHandles.collectCoordinates(handle, 0, alignCheck); + int[] reorder = IntStream.concat(IntStream.of(0, 1), IntStream.range(0, coordinateTypes.size())).toArray(); + handle = MethodHandles.permuteCoordinates(handle, coordinateTypes, reorder); } if (adapt) { + if (derefAdapters.length > 0) { + // plug up the base offset if we have at least 1 enclosing dereference + handle = MethodHandles.insertCoordinates(handle, 1, 0); + } for (int i = derefAdapters.length; i > 0; i--) { - handle = MethodHandles.collectCoordinates(handle, 0, derefAdapters[i - 1]); + MethodHandle adapter = derefAdapters[i - 1]; + // the first/outermost adapter will have a base offset coordinate, the rest are constant 0 + if (i > 1) { + // plug in a constant 0 base offset for all but the outermost access in a deref chain + adapter = MethodHandles.insertArguments(adapter, 1, 0); + } + handle = MethodHandles.collectCoordinates(handle, 0, adapter); } } return handle; @@ -228,14 +250,14 @@ private static long addScaledOffset(long base, long index, long stride, long bou } public MethodHandle offsetHandle() { - MethodHandle mh = MethodHandles.identity(long.class); - for (int i = strides.length - 1; i >=0; i--) { + MethodHandle mh = MethodHandles.insertArguments(MH_ADD, 0, offset); + for (int i = strides.length - 1; i >= 0; i--) { MethodHandle collector = MethodHandles.insertArguments(MH_ADD_SCALED_OFFSET, 2, strides[i], bounds[i]); // (J, ...) -> J to (J, J, ...) -> J // i.e. new coord is prefixed. Last coord will correspond to innermost layout mh = MethodHandles.collectArguments(mh, 0, collector); } - mh = MethodHandles.insertArguments(mh, 0, offset); + return mh; } @@ -253,21 +275,26 @@ public MethodHandle sliceHandle() { sliceHandle = MH_SLICE_LAYOUT; // (MS, long, MemoryLayout) -> MS sliceHandle = MethodHandles.insertArguments(sliceHandle, 2, layout); // (MS, long) -> MS } - sliceHandle = MethodHandles.collectArguments(sliceHandle, 1, offsetHandle()); // (MS, ...) -> MS + sliceHandle = MethodHandles.collectArguments(sliceHandle, 1, offsetHandle()); // (MS, long, ...) -> MS if (enclosing != null) { - MethodHandle checkHandle = MethodHandles.insertArguments(MH_CHECK_ALIGN, 1, rootLayout()); - sliceHandle = MethodHandles.filterArguments(sliceHandle, 0, checkHandle); + // insert align check for the root layout on the initial MS + offset + MethodType oldType = sliceHandle.type(); + MethodHandle alignCheck = MethodHandles.insertArguments(MH_CHECK_ALIGN, 2, rootLayout()); + sliceHandle = MethodHandles.collectArguments(sliceHandle, 0, alignCheck); // (MS, long, MS, long) -> MS + int[] reorder = IntStream.concat(IntStream.of(0, 1), IntStream.range(0, oldType.parameterCount())).toArray(); + sliceHandle = MethodHandles.permuteArguments(sliceHandle, oldType, reorder); // (MS, long, ...) -> MS } return sliceHandle; } - private static MemorySegment checkAlign(MemorySegment segment, MemoryLayout constraint) { - if (!((AbstractMemorySegmentImpl) segment).isAlignedForElement(0, constraint)) { - throw new IllegalArgumentException("Target offset incompatible with alignment constraints: " + constraint.byteAlignment()); + private static void checkAlign(MemorySegment segment, long offset, MemoryLayout constraint) { + if (!((AbstractMemorySegmentImpl) segment).isAlignedForElement(offset, constraint)) { + throw new IllegalArgumentException(String.format( + "Target offset %d is incompatible with alignment constraint %d (of %s) for segment %s" + , offset, constraint.byteAlignment(), constraint, segment)); } - return segment; } public MemoryLayout layout() { @@ -292,15 +319,27 @@ private static LayoutPath derefPath(MemoryLayout layout, MethodHandle handle, La // Helper methods - private void check(Class layoutClass, String msg) { + private SequenceLayout requireSequenceLayout() { + return requireLayoutType(SequenceLayout.class, "sequence"); + } + + private GroupLayout requireGroupLayout() { + return requireLayoutType(GroupLayout.class, "group"); + } + + private T requireLayoutType(Class layoutClass, String name) { if (!layoutClass.isAssignableFrom(layout.getClass())) { - throw badLayoutPath(msg); + throw badLayoutPath( + String.format("attempting to select a %s element from a non-%s layout: %s", + name, name, breadcrumbs())); } + return layoutClass.cast(layout); } private void checkSequenceBounds(SequenceLayout seq, long index) { if (index >= seq.elementCount()) { - throw badLayoutPath(String.format("Sequence index out of bound; found: %d, size: %d", index, seq.elementCount())); + throw badLayoutPath(String.format("sequence index out of bounds; index: %d, elementCount is %d for layout %s", + index, seq.elementCount(), breadcrumbs())); } } @@ -320,6 +359,13 @@ private long[] addBound(long maxIndex) { return newBounds; } + private String breadcrumbs() { + return Stream.iterate(this, Objects::nonNull, lp -> lp.enclosing) + .map(LayoutPath::layout) + .map(Object::toString) + .collect(joining(", selected from: ")); + } + /** * This class provides an immutable implementation for the {@code PathElement} interface. A path element implementation * is simply a pointer to one of the selector methods provided by the {@code LayoutPath} class. diff --git a/src/java.base/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java index 0c9482d8d36..dce223c4683 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java @@ -35,7 +35,7 @@ * memory mapped segment, such as the file descriptor associated with the mapping. This information is crucial * in order to correctly reconstruct a byte buffer object from the segment (see {@link #makeByteBuffer()}). */ -public final class MappedMemorySegmentImpl extends NativeMemorySegmentImpl { +final class MappedMemorySegmentImpl extends NativeMemorySegmentImpl { private final UnmapperProxy unmapper; diff --git a/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java b/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java index 1d145c09351..c8c7dd2e543 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java @@ -77,17 +77,7 @@ public abstract sealed class MemorySessionImpl } public Arena asArena() { - return new Arena() { - @Override - public Scope scope() { - return MemorySessionImpl.this; - } - - @Override - public void close() { - MemorySessionImpl.this.close(); - } - }; + return new ArenaImpl(this); } @ForceInline @@ -153,11 +143,6 @@ public static MemorySessionImpl createImplicit(Cleaner cleaner) { return new ImplicitSession(cleaner); } - public MemorySegment allocate(long byteSize, long byteAlignment) { - Utils.checkAllocationSizeAndAlign(byteSize, byteAlignment); - return NativeMemorySegmentImpl.makeNativeSegment(byteSize, byteAlignment, this); - } - public abstract void release0(); public abstract void acquire0(); diff --git a/src/java.base/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java index 28d038e39b3..5339d3fb8b7 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java @@ -31,22 +31,13 @@ import java.util.Optional; import jdk.internal.misc.Unsafe; -import jdk.internal.misc.VM; import jdk.internal.vm.annotation.ForceInline; -import sun.security.action.GetBooleanAction; /** * Implementation for native memory segments. A native memory segment is essentially a wrapper around * a native long address. */ -public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl permits MappedMemorySegmentImpl { - - private static final Unsafe UNSAFE = Unsafe.getUnsafe(); - - // The maximum alignment supported by malloc - typically 16 bytes on - // 64-bit platforms and 8 bytes on 32-bit platforms. - private static final long MAX_MALLOC_ALIGN = Unsafe.ADDRESS_SIZE == 4 ? 8 : 16; - private static final boolean SKIP_ZERO_MEMORY = GetBooleanAction.privilegedGetProperty("jdk.internal.foreign.skipZeroMemory"); +sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl permits MappedMemorySegmentImpl { final long min; @@ -60,17 +51,6 @@ public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl pe : min; } - /** - * This constructor should only be used when initializing {@link MemorySegment#NULL}. Note: because of the memory - * segment class hierarchy, it is possible to end up in a situation where this constructor is called - * when the static fields in this class are not yet initialized. - */ - @ForceInline - public NativeMemorySegmentImpl() { - super(0L, false, new GlobalSession(null)); - this.min = 0L; - } - @Override public long address() { return min; @@ -112,62 +92,4 @@ public Object unsafeGetBase() { public long maxAlignMask() { return 0; } - - // factories - - public static MemorySegment makeNativeSegment(long byteSize, long byteAlignment, MemorySessionImpl sessionImpl) { - sessionImpl.checkValidState(); - if (VM.isDirectMemoryPageAligned()) { - byteAlignment = Math.max(byteAlignment, NIO_ACCESS.pageSize()); - } - long alignedSize = Math.max(1L, byteAlignment > MAX_MALLOC_ALIGN ? - byteSize + (byteAlignment - 1) : - byteSize); - - NIO_ACCESS.reserveMemory(alignedSize, byteSize); - - long buf = UNSAFE.allocateMemory(alignedSize); - if (!SKIP_ZERO_MEMORY) { - UNSAFE.setMemory(buf, alignedSize, (byte)0); - } - long alignedBuf = Utils.alignUp(buf, byteAlignment); - AbstractMemorySegmentImpl segment = new NativeMemorySegmentImpl(buf, alignedSize, - false, sessionImpl); - sessionImpl.addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() { - @Override - public void cleanup() { - UNSAFE.freeMemory(buf); - NIO_ACCESS.unreserveMemory(alignedSize, byteSize); - } - }); - if (alignedSize != byteSize) { - long delta = alignedBuf - buf; - segment = segment.asSlice(delta, byteSize); - } - return segment; - } - - // Unsafe native segment factories. These are used by the implementation code, to skip the sanity checks - // associated with MemorySegment::ofAddress. - - @ForceInline - public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, MemorySessionImpl sessionImpl, Runnable action) { - if (action == null) { - sessionImpl.checkValidState(); - } else { - sessionImpl.addCloseAction(action); - } - return new NativeMemorySegmentImpl(min, byteSize, false, sessionImpl); - } - - @ForceInline - public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, MemorySessionImpl sessionImpl) { - sessionImpl.checkValidState(); - return new NativeMemorySegmentImpl(min, byteSize, false, sessionImpl); - } - - @ForceInline - public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize) { - return new NativeMemorySegmentImpl(min, byteSize, false, new GlobalSession(null)); - } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java b/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java new file mode 100644 index 00000000000..c4f1d5a8a0f --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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.foreign; + +import jdk.internal.access.foreign.UnmapperProxy; +import jdk.internal.foreign.HeapMemorySegmentImpl.OfByte; +import jdk.internal.foreign.HeapMemorySegmentImpl.OfChar; +import jdk.internal.foreign.HeapMemorySegmentImpl.OfDouble; +import jdk.internal.foreign.HeapMemorySegmentImpl.OfFloat; +import jdk.internal.foreign.HeapMemorySegmentImpl.OfInt; +import jdk.internal.foreign.HeapMemorySegmentImpl.OfLong; +import jdk.internal.foreign.HeapMemorySegmentImpl.OfShort; +import jdk.internal.misc.Unsafe; +import jdk.internal.misc.VM; +import jdk.internal.vm.annotation.ForceInline; + +import java.lang.foreign.MemorySegment; +import java.util.Objects; + +/** + * This class is used to retrieve concrete memory segment implementations, while making sure that classes + * are initialized in the right order (that is, that {@code MemorySegment} is always initialized first). + * See {@link SegmentFactories#ensureInitialized()}. + */ +public class SegmentFactories { + + // The maximum alignment supported by malloc - typically 16 bytes on + // 64-bit platforms and 8 bytes on 32-bit platforms. + private static final long MAX_MALLOC_ALIGN = Unsafe.ADDRESS_SIZE == 4 ? 8 : 16; + + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + // Unsafe native segment factories. These are used by the implementation code, to skip the sanity checks + // associated with MemorySegment::ofAddress. + + @ForceInline + public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, MemorySessionImpl sessionImpl, Runnable action) { + ensureInitialized(); + if (action == null) { + sessionImpl.checkValidState(); + } else { + sessionImpl.addCloseAction(action); + } + return new NativeMemorySegmentImpl(min, byteSize, false, sessionImpl); + } + + @ForceInline + public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, MemorySessionImpl sessionImpl) { + ensureInitialized(); + sessionImpl.checkValidState(); + return new NativeMemorySegmentImpl(min, byteSize, false, sessionImpl); + } + + @ForceInline + public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize) { + ensureInitialized(); + return new NativeMemorySegmentImpl(min, byteSize, false, new GlobalSession(null)); + } + + public static MemorySegment fromArray(byte[] arr) { + ensureInitialized(); + Objects.requireNonNull(arr); + long byteSize = (long)arr.length * Unsafe.ARRAY_BYTE_INDEX_SCALE; + return new OfByte(Unsafe.ARRAY_BYTE_BASE_OFFSET, arr, byteSize, false, + MemorySessionImpl.heapSession(arr)); + } + + public static MemorySegment fromArray(short[] arr) { + ensureInitialized(); + Objects.requireNonNull(arr); + long byteSize = (long)arr.length * Unsafe.ARRAY_SHORT_INDEX_SCALE; + return new OfShort(Unsafe.ARRAY_SHORT_BASE_OFFSET, arr, byteSize, false, + MemorySessionImpl.heapSession(arr)); + } + + public static MemorySegment fromArray(int[] arr) { + ensureInitialized(); + Objects.requireNonNull(arr); + long byteSize = (long)arr.length * Unsafe.ARRAY_INT_INDEX_SCALE; + return new OfInt(Unsafe.ARRAY_INT_BASE_OFFSET, arr, byteSize, false, + MemorySessionImpl.heapSession(arr)); + } + + public static MemorySegment fromArray(char[] arr) { + ensureInitialized(); + Objects.requireNonNull(arr); + long byteSize = (long)arr.length * Unsafe.ARRAY_CHAR_INDEX_SCALE; + return new OfChar(Unsafe.ARRAY_CHAR_BASE_OFFSET, arr, byteSize, false, + MemorySessionImpl.heapSession(arr)); + } + + public static MemorySegment fromArray(float[] arr) { + ensureInitialized(); + Objects.requireNonNull(arr); + long byteSize = (long)arr.length * Unsafe.ARRAY_FLOAT_INDEX_SCALE; + return new OfFloat(Unsafe.ARRAY_FLOAT_BASE_OFFSET, arr, byteSize, false, + MemorySessionImpl.heapSession(arr)); + } + + public static MemorySegment fromArray(double[] arr) { + ensureInitialized(); + Objects.requireNonNull(arr); + long byteSize = (long)arr.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE; + return new OfDouble(Unsafe.ARRAY_DOUBLE_BASE_OFFSET, arr, byteSize, false, + MemorySessionImpl.heapSession(arr)); + } + + public static MemorySegment fromArray(long[] arr) { + ensureInitialized(); + Objects.requireNonNull(arr); + long byteSize = (long)arr.length * Unsafe.ARRAY_LONG_INDEX_SCALE; + return new OfLong(Unsafe.ARRAY_LONG_BASE_OFFSET, arr, byteSize, false, + MemorySessionImpl.heapSession(arr)); + } + + public static MemorySegment allocateSegment(long byteSize, long byteAlignment, MemorySessionImpl sessionImpl, + boolean shouldReserve) { + ensureInitialized(); + sessionImpl.checkValidState(); + if (VM.isDirectMemoryPageAligned()) { + byteAlignment = Math.max(byteAlignment, AbstractMemorySegmentImpl.NIO_ACCESS.pageSize()); + } + long alignedSize = Math.max(1L, byteAlignment > MAX_MALLOC_ALIGN ? + byteSize + (byteAlignment - 1) : + byteSize); + + if (shouldReserve) { + AbstractMemorySegmentImpl.NIO_ACCESS.reserveMemory(alignedSize, byteSize); + } + + long buf = allocateMemoryWrapper(alignedSize); + long alignedBuf = Utils.alignUp(buf, byteAlignment); + AbstractMemorySegmentImpl segment = new NativeMemorySegmentImpl(buf, alignedSize, + false, sessionImpl); + sessionImpl.addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() { + @Override + public void cleanup() { + UNSAFE.freeMemory(buf); + if (shouldReserve) { + AbstractMemorySegmentImpl.NIO_ACCESS.unreserveMemory(alignedSize, byteSize); + } + } + }); + if (alignedSize != byteSize) { + long delta = alignedBuf - buf; + segment = segment.asSlice(delta, byteSize); + } + return segment; + } + + private static long allocateMemoryWrapper(long size) { + try { + return UNSAFE.allocateMemory(size); + } catch (IllegalArgumentException ex) { + throw new OutOfMemoryError(); + } + } + + public static MemorySegment mapSegment(long size, UnmapperProxy unmapper, boolean readOnly, MemorySessionImpl sessionImpl) { + ensureInitialized(); + if (unmapper != null) { + AbstractMemorySegmentImpl segment = + new MappedMemorySegmentImpl(unmapper.address(), unmapper, size, + readOnly, sessionImpl); + MemorySessionImpl.ResourceList.ResourceCleanup resource = + new MemorySessionImpl.ResourceList.ResourceCleanup() { + @Override + public void cleanup() { + unmapper.unmap(); + } + }; + sessionImpl.addOrCleanupIfFail(resource); + return segment; + } else { + return new MappedMemorySegmentImpl(0, null, 0, readOnly, sessionImpl); + } + } + + // The method below needs to be called before any concrete subclass of MemorySegment + // is instantiated. This is to make sure that we cannot have an initialization deadlock + // where one thread attempts to initialize e.g. MemorySegment (and then NativeMemorySegmentImpl, via + // the MemorySegment.NULL field) while another thread is attempting to initialize + // NativeMemorySegmentImpl (and then MemorySegment, the super-interface). + @ForceInline + private static void ensureInitialized() { + MemorySegment segment = MemorySegment.NULL; + } +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/SlicingAllocator.java b/src/java.base/share/classes/jdk/internal/foreign/SlicingAllocator.java index 36f8551f448..db7d476053e 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/SlicingAllocator.java +++ b/src/java.base/share/classes/jdk/internal/foreign/SlicingAllocator.java @@ -31,13 +31,11 @@ public final class SlicingAllocator implements SegmentAllocator { private final MemorySegment segment; - private final long maxAlign; private long sp = 0L; public SlicingAllocator(MemorySegment segment) { this.segment = segment; - this.maxAlign = ((AbstractMemorySegmentImpl)segment).maxAlignMask(); } MemorySegment trySlice(long byteSize, long byteAlignment) { diff --git a/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java b/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java new file mode 100644 index 00000000000..eaa028cae39 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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.foreign; + +import jdk.internal.access.JavaLangAccess; +import jdk.internal.access.SharedSecrets; +import jdk.internal.foreign.abi.SharedUtils; +import jdk.internal.util.ArraysSupport; +import sun.security.action.GetPropertyAction; + +import java.lang.foreign.MemorySegment; +import java.nio.charset.Charset; + +import static java.lang.foreign.ValueLayout.*; + +/** + * Miscellaneous functions to read and write strings, in various charsets. + */ +public class StringSupport { + + static final JavaLangAccess JAVA_LANG_ACCESS = SharedSecrets.getJavaLangAccess(); + + private StringSupport() {} + + public static String read(MemorySegment segment, long offset, Charset charset) { + return switch (CharsetKind.of(charset)) { + case SINGLE_BYTE -> readByte(segment, offset, charset); + case DOUBLE_BYTE -> readShort(segment, offset, charset); + case QUAD_BYTE -> readInt(segment, offset, charset); + }; + } + + public static void write(MemorySegment segment, long offset, Charset charset, String string) { + switch (CharsetKind.of(charset)) { + case SINGLE_BYTE -> writeByte(segment, offset, charset, string); + case DOUBLE_BYTE -> writeShort(segment, offset, charset, string); + case QUAD_BYTE -> writeInt(segment, offset, charset, string); + } + } + + private static String readByte(MemorySegment segment, long offset, Charset charset) { + long len = chunkedStrlenByte(segment, offset); + byte[] bytes = new byte[(int)len]; + MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, (int)len); + return new String(bytes, charset); + } + + private static void writeByte(MemorySegment segment, long offset, Charset charset, String string) { + int bytes = copyBytes(string, segment, charset, offset); + segment.set(JAVA_BYTE, offset + bytes, (byte)0); + } + + private static String readShort(MemorySegment segment, long offset, Charset charset) { + long len = chunkedStrlenShort(segment, offset); + byte[] bytes = new byte[(int)len]; + MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, (int)len); + return new String(bytes, charset); + } + + private static void writeShort(MemorySegment segment, long offset, Charset charset, String string) { + int bytes = copyBytes(string, segment, charset, offset); + segment.set(JAVA_SHORT, offset + bytes, (short)0); + } + + private static String readInt(MemorySegment segment, long offset, Charset charset) { + long len = strlenInt(segment, offset); + byte[] bytes = new byte[(int)len]; + MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, (int)len); + return new String(bytes, charset); + } + + private static void writeInt(MemorySegment segment, long offset, Charset charset, String string) { + int bytes = copyBytes(string, segment, charset, offset); + segment.set(JAVA_INT, offset + bytes, 0); + } + + /** + * {@return the shortest distance beginning at the provided {@code start} + * to the encountering of a zero byte in the provided {@code segment}} + *

    + * The method divides the region of interest into three distinct regions: + *

    + *

    + * The body is using a heuristic method to determine if a long word + * contains a zero byte. The method might have false positives but + * never false negatives. + *

    + * This method is inspired by the `glibc/string/strlen.c` implementation + * + * @param segment to examine + * @param start from where examination shall begin + * @throws IllegalArgumentException if the examined region contains no zero bytes + * within a length that can be accepted by a String + */ + public static int chunkedStrlenByte(MemorySegment segment, long start) { + + // Handle the first unaligned "head" bytes separately + int headCount = (int)SharedUtils.remainsToAlignment(segment.address() + start, Long.BYTES); + + int offset = 0; + for (; offset < headCount; offset++) { + byte curr = segment.get(JAVA_BYTE, start + offset); + if (curr == 0) { + return offset; + } + } + + // We are now on a long-aligned boundary so this is the "body" + int bodyCount = bodyCount(segment.byteSize() - start - headCount); + + for (; offset < bodyCount; offset += Long.BYTES) { + // We know we are `long` aligned so, we can save on alignment checking here + long curr = segment.get(JAVA_LONG_UNALIGNED, start + offset); + // Is this a candidate? + if (mightContainZeroByte(curr)) { + for (int j = 0; j < 8; j++) { + if (segment.get(JAVA_BYTE, start + offset + j) == 0) { + return offset + j; + } + } + } + } + + // Handle the "tail" + return requireWithinArraySize((long) offset + strlenByte(segment, start + offset)); + } + + /* Bits 63 and N * 8 (N = 1..7) of this number are zero. Call these bits + the "holes". Note that there is a hole just to the left of + each byte, with an extra at the end: + + bits: 01111110 11111110 11111110 11111110 11111110 11111110 11111110 11111111 + bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD EEEEEEEE FFFFFFFF GGGGGGGG HHHHHHHH + + The 1-bits make sure that carries propagate to the next 0-bit. + The 0-bits provide holes for carries to fall into. + */ + private static final long HIMAGIC_FOR_BYTES = 0x8080_8080_8080_8080L; + private static final long LOMAGIC_FOR_BYTES = 0x0101_0101_0101_0101L; + + static boolean mightContainZeroByte(long l) { + return ((l - LOMAGIC_FOR_BYTES) & (~l) & HIMAGIC_FOR_BYTES) != 0; + } + + private static final long HIMAGIC_FOR_SHORTS = 0x8000_8000_8000_8000L; + private static final long LOMAGIC_FOR_SHORTS = 0x0001_0001_0001_0001L; + + static boolean mightContainZeroShort(long l) { + return ((l - LOMAGIC_FOR_SHORTS) & (~l) & HIMAGIC_FOR_SHORTS) != 0; + } + + static int requireWithinArraySize(long size) { + if (size > ArraysSupport.SOFT_MAX_ARRAY_LENGTH) { + throw newIaeStringTooLarge(); + } + return (int) size; + } + + static int bodyCount(long remaining) { + return (int) Math.min( + // Make sure we do not wrap around + Integer.MAX_VALUE - Long.BYTES, + // Remaining bytes to consider + remaining) + & -Long.BYTES; // Mask 0xFFFFFFF8 + } + + private static int strlenByte(MemorySegment segment, long start) { + for (int offset = 0; offset < ArraysSupport.SOFT_MAX_ARRAY_LENGTH; offset += 1) { + byte curr = segment.get(JAVA_BYTE, start + offset); + if (curr == 0) { + return offset; + } + } + throw newIaeStringTooLarge(); + } + + /** + * {@return the shortest distance beginning at the provided {@code start} + * to the encountering of a zero short in the provided {@code segment}} + *

    + * Note: The inspected region must be short aligned. + * + * @see #chunkedStrlenByte(MemorySegment, long) for more information + * + * @param segment to examine + * @param start from where examination shall begin + * @throws IllegalArgumentException if the examined region contains no zero shorts + * within a length that can be accepted by a String + */ + public static int chunkedStrlenShort(MemorySegment segment, long start) { + + // Handle the first unaligned "head" bytes separately + int headCount = (int)SharedUtils.remainsToAlignment(segment.address() + start, Long.BYTES); + + int offset = 0; + for (; offset < headCount; offset += Short.BYTES) { + short curr = segment.get(JAVA_SHORT, start + offset); + if (curr == 0) { + return offset; + } + } + + // We are now on a long-aligned boundary so this is the "body" + int bodyCount = bodyCount(segment.byteSize() - start - headCount); + + for (; offset < bodyCount; offset += Long.BYTES) { + // We know we are `long` aligned so, we can save on alignment checking here + long curr = segment.get(JAVA_LONG_UNALIGNED, start + offset); + // Is this a candidate? + if (mightContainZeroShort(curr)) { + for (int j = 0; j < Long.BYTES; j += Short.BYTES) { + if (segment.get(JAVA_SHORT_UNALIGNED, start + offset + j) == 0) { + return offset + j; + } + } + } + } + + // Handle the "tail" + return requireWithinArraySize((long) offset + strlenShort(segment, start + offset)); + } + + private static int strlenShort(MemorySegment segment, long start) { + for (int offset = 0; offset < ArraysSupport.SOFT_MAX_ARRAY_LENGTH; offset += Short.BYTES) { + short curr = segment.get(JAVA_SHORT_UNALIGNED, start + offset); + if (curr == (short)0) { + return offset; + } + } + throw newIaeStringTooLarge(); + } + + // The gain of using `long` wide operations for `int` is lower than for the two other `byte` and `short` variants + // so, there is only one method for ints. + public static int strlenInt(MemorySegment segment, long start) { + for (int offset = 0; offset < ArraysSupport.SOFT_MAX_ARRAY_LENGTH; offset += Integer.BYTES) { + // We are guaranteed to be aligned here so, we can use unaligned access. + int curr = segment.get(JAVA_INT_UNALIGNED, start + offset); + if (curr == 0) { + return offset; + } + } + throw newIaeStringTooLarge(); + } + + public enum CharsetKind { + SINGLE_BYTE(1), + DOUBLE_BYTE(2), + QUAD_BYTE(4); + + final int terminatorCharSize; + + CharsetKind(int terminatorCharSize) { + this.terminatorCharSize = terminatorCharSize; + } + + public int terminatorCharSize() { + return terminatorCharSize; + } + + public static CharsetKind of(Charset charset) { + // Comparing the charset to specific internal implementations avoids loading the class `StandardCharsets` + if (charset == sun.nio.cs.UTF_8.INSTANCE || + charset == sun.nio.cs.ISO_8859_1.INSTANCE || + charset == sun.nio.cs.US_ASCII.INSTANCE) { + return SINGLE_BYTE; + } else if (charset instanceof sun.nio.cs.UTF_16LE || + charset instanceof sun.nio.cs.UTF_16BE || + charset instanceof sun.nio.cs.UTF_16) { + return DOUBLE_BYTE; + } else if (charset instanceof sun.nio.cs.UTF_32LE || + charset instanceof sun.nio.cs.UTF_32BE || + charset instanceof sun.nio.cs.UTF_32) { + return QUAD_BYTE; + } else { + throw new IllegalArgumentException("Unsupported charset: " + charset); + } + } + } + + public static boolean bytesCompatible(String string, Charset charset) { + return JAVA_LANG_ACCESS.bytesCompatible(string, charset); + } + + public static int copyBytes(String string, MemorySegment segment, Charset charset, long offset) { + if (bytesCompatible(string, charset)) { + copyToSegmentRaw(string, segment, offset); + return string.length(); + } else { + byte[] bytes = string.getBytes(charset); + MemorySegment.copy(bytes, 0, segment, JAVA_BYTE, offset, bytes.length); + return bytes.length; + } + } + + public static void copyToSegmentRaw(String string, MemorySegment segment, long offset) { + JAVA_LANG_ACCESS.copyToSegmentRaw(string, segment, offset); + } + + private static IllegalArgumentException newIaeStringTooLarge() { + return new IllegalArgumentException("String too large"); + } + +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/Utils.java b/src/java.base/share/classes/jdk/internal/foreign/Utils.java index 3e8f012891e..3809aedba80 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/Utils.java +++ b/src/java.base/share/classes/jdk/internal/foreign/Utils.java @@ -29,7 +29,6 @@ import java.lang.foreign.AddressLayout; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.SegmentAllocator; import java.lang.foreign.StructLayout; import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; @@ -47,7 +46,6 @@ import jdk.internal.vm.annotation.ForceInline; import sun.invoke.util.Wrapper; -import static java.lang.foreign.ValueLayout.JAVA_BYTE; import static sun.security.action.GetPropertyAction.privilegedGetProperty; /** @@ -98,7 +96,19 @@ static VarHandle put(ValueLayout layout, VarHandle handle) { VarHandle prev = HANDLE_MAP.putIfAbsent(layout, handle); return prev != null ? prev : handle; } + + static VarHandle get(ValueLayout layout) { + return HANDLE_MAP.get(layout); + } } + layout = layout.withoutName(); // name doesn't matter + // keep the addressee layout as it's used below + + VarHandle handle = VarHandleCache.get(layout); + if (handle != null) { + return handle; + } + Class baseCarrier = layout.carrier(); if (layout.carrier() == MemorySegment.class) { baseCarrier = switch ((int) ValueLayout.ADDRESS.byteSize()) { @@ -110,7 +120,7 @@ static VarHandle put(ValueLayout layout, VarHandle handle) { baseCarrier = byte.class; } - VarHandle handle = SharedSecrets.getJavaLangInvokeAccess().memorySegmentViewHandle(baseCarrier, + handle = SharedSecrets.getJavaLangInvokeAccess().memorySegmentViewHandle(baseCarrier, layout.byteAlignment() - 1, layout.order()); if (layout.carrier() == boolean.class) { @@ -136,29 +146,17 @@ private static byte booleanToByte(boolean b) { @ForceInline public static MemorySegment longToAddress(long addr, long size, long align) { if (!isAligned(addr, align)) { - throw new IllegalArgumentException("Invalid alignment constraint for address: " + addr); + throw new IllegalArgumentException("Invalid alignment constraint for address: " + toHexString(addr)); } - return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, size); + return SegmentFactories.makeNativeSegmentUnchecked(addr, size); } @ForceInline public static MemorySegment longToAddress(long addr, long size, long align, MemorySessionImpl scope) { if (!isAligned(addr, align)) { - throw new IllegalArgumentException("Invalid alignment constraint for address: " + addr); + throw new IllegalArgumentException("Invalid alignment constraint for address: " + toHexString(addr)); } - return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, size, scope); - } - - public static void copy(MemorySegment addr, byte[] bytes) { - var heapSegment = MemorySegment.ofArray(bytes); - addr.copyFrom(heapSegment); - addr.set(JAVA_BYTE, bytes.length, (byte)0); - } - - public static MemorySegment toCString(byte[] bytes, SegmentAllocator allocator) { - MemorySegment addr = allocator.allocate(bytes.length + 1); - copy(addr, bytes); - return addr; + return SegmentFactories.makeNativeSegmentUnchecked(addr, size, scope); } @ForceInline diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java index e0c5c5575fc..ae41d4f1756 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java @@ -54,6 +54,7 @@ import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; +import java.util.HashSet; import java.util.List; import java.nio.ByteOrder; import java.util.Objects; @@ -73,6 +74,7 @@ public interface UpcallStubFactory { private record LinkRequest(FunctionDescriptor descriptor, LinkerOptions options) {} private final SoftReferenceCache DOWNCALL_CACHE = new SoftReferenceCache<>(); private final SoftReferenceCache UPCALL_CACHE = new SoftReferenceCache<>(); + private final Set CANONICAL_LAYOUTS_CACHE = new HashSet<>(canonicalLayouts().values()); @Override @CallerSensitive @@ -213,7 +215,7 @@ private void checkLayoutRecursive(MemoryLayout layout) { } // check for trailing padding - private static void checkGroupSize(GroupLayout gl, long maxUnpaddedOffset) { + private void checkGroupSize(GroupLayout gl, long maxUnpaddedOffset) { long expectedSize = Utils.alignUp(maxUnpaddedOffset, gl.byteAlignment()); if (gl.byteSize() != expectedSize) { throw new IllegalArgumentException("Layout '" + gl + "' has unexpected size: " @@ -223,7 +225,7 @@ private static void checkGroupSize(GroupLayout gl, long maxUnpaddedOffset) { // checks both that there is no excess padding between 'memberLayout' and // the previous layout - private static void checkMemberOffset(StructLayout parent, MemoryLayout memberLayout, + private void checkMemberOffset(StructLayout parent, MemoryLayout memberLayout, long lastUnpaddedOffset, long offset) { long expectedOffset = Utils.alignUp(lastUnpaddedOffset, memberLayout.byteAlignment()); if (expectedOffset != offset) { @@ -232,17 +234,17 @@ private static void checkMemberOffset(StructLayout parent, MemoryLayout memberLa } } - private static void checkSupported(ValueLayout valueLayout) { + private void checkSupported(ValueLayout valueLayout) { valueLayout = valueLayout.withoutName(); if (valueLayout instanceof AddressLayout addressLayout) { valueLayout = addressLayout.withoutTargetLayout(); } - if (!SUPPORTED_LAYOUTS.contains(valueLayout.withoutName())) { + if (!CANONICAL_LAYOUTS_CACHE.contains(valueLayout.withoutName())) { throw new IllegalArgumentException("Unsupported layout: " + valueLayout); } } - private static void checkHasNaturalAlignment(MemoryLayout layout) { + private void checkHasNaturalAlignment(MemoryLayout layout) { if (!((AbstractLayout) layout).hasNaturalAlignment()) { throw new IllegalArgumentException("Layout alignment must be natural alignment: " + layout); } @@ -273,16 +275,4 @@ private static FunctionDescriptor stripNames(FunctionDescriptor function) { .map(rl -> FunctionDescriptor.of(stripNames(rl), stripNames(function.argumentLayouts()))) .orElseGet(() -> FunctionDescriptor.ofVoid(stripNames(function.argumentLayouts()))); } - - private static final Set SUPPORTED_LAYOUTS = Set.of( - ValueLayout.JAVA_BOOLEAN, - ValueLayout.JAVA_BYTE, - ValueLayout.JAVA_CHAR, - ValueLayout.JAVA_SHORT, - ValueLayout.JAVA_INT, - ValueLayout.JAVA_FLOAT, - ValueLayout.JAVA_LONG, - ValueLayout.JAVA_DOUBLE, - ValueLayout.ADDRESS - ); } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/Binding.java b/src/java.base/share/classes/jdk/internal/foreign/abi/Binding.java index da288d51406..5ff8508adf4 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/Binding.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/Binding.java @@ -464,7 +464,7 @@ public void verify(Deque> stack) { @Override public void interpret(Deque stack, StoreFunc storeFunc, LoadFunc loadFunc, SegmentAllocator allocator) { - storeFunc.store(storage(), type(), stack.pop()); + storeFunc.store(storage(), stack.pop()); } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/BindingInterpreter.java b/src/java.base/share/classes/jdk/internal/foreign/abi/BindingInterpreter.java index bc0e2b0c5d6..df6fdaba9a7 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/BindingInterpreter.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/BindingInterpreter.java @@ -49,12 +49,12 @@ static Object box(List bindings, LoadFunc loadFunc, SegmentAllocator al } @FunctionalInterface - interface StoreFunc { - void store(VMStorage storage, Class type, Object o); + public interface StoreFunc { + void store(VMStorage storage, Object o); } @FunctionalInterface - interface LoadFunc { + public interface LoadFunc { Object load(VMStorage storage, Class type); } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequence.java b/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequence.java index fe1ac10b518..e301f692167 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequence.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequence.java @@ -192,7 +192,7 @@ public int capturedStateMask() { } public boolean needsTransition() { - return !linkerOptions.isTrivial(); + return !linkerOptions.isCritical(); } public int numLeadingParams() { diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java index a6de1e9c09d..a092d242a53 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java @@ -28,16 +28,22 @@ import jdk.internal.access.SharedSecrets; import sun.security.action.GetPropertyAction; +import java.lang.foreign.AddressLayout; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.SegmentAllocator; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.stream.Stream; +import jdk.internal.foreign.AbstractMemorySegmentImpl; +import jdk.internal.foreign.MemorySessionImpl; + import static java.lang.invoke.MethodHandles.collectArguments; import static java.lang.invoke.MethodHandles.foldArguments; import static java.lang.invoke.MethodHandles.identity; @@ -93,9 +99,8 @@ public MethodHandle getBoundMethodHandle() { handle = BindingSpecializer.specializeDowncall(handle, callingSequence, abi); } else { Map argIndexMap = SharedUtils.indexMap(argMoves); - Map retIndexMap = SharedUtils.indexMap(retMoves); - InvocationData invData = new InvocationData(handle, argIndexMap, retIndexMap); + InvocationData invData = new InvocationData(handle, callingSequence, argIndexMap); handle = insertArguments(MH_INVOKE_INTERP_BINDINGS.bindTo(this), 2, invData); MethodType interpType = callingSequence.callerMethodType(); if (callingSequence.needsReturnBuffer()) { @@ -146,17 +151,17 @@ private VMStorage[] toStorageArray(Binding.Move[] moves) { return Arrays.stream(moves).map(Binding.Move::storage).toArray(VMStorage[]::new); } - private record InvocationData(MethodHandle leaf, Map argIndexMap, Map retIndexMap) {} + private record InvocationData(MethodHandle leaf, CallingSequence callingSequence, Map argIndexMap) {} Object invokeInterpBindings(SegmentAllocator allocator, Object[] args, InvocationData invData) throws Throwable { Arena unboxArena = callingSequence.allocationSize() != 0 ? SharedUtils.newBoundedArena(callingSequence.allocationSize()) : SharedUtils.DUMMY_ARENA; + List acquiredScopes = new ArrayList<>(); try (unboxArena) { MemorySegment returnBuffer = null; // do argument processing, get Object[] as result - Object[] leafArgs = new Object[invData.leaf.type().parameterCount()]; if (callingSequence.needsReturnBuffer()) { // we supply the return buffer (argument array does not contain it) Object[] prefixedArgs = new Object[args.length + 1]; @@ -165,10 +170,21 @@ Object invokeInterpBindings(SegmentAllocator allocator, Object[] args, Invocatio System.arraycopy(args, 0, prefixedArgs, 1, args.length); args = prefixedArgs; } + + Object[] leafArgs = new Object[invData.leaf.type().parameterCount()]; for (int i = 0; i < args.length; i++) { Object arg = args[i]; + if (callingSequence.functionDesc().argumentLayouts().get(i) instanceof AddressLayout) { + MemorySessionImpl sessionImpl = ((AbstractMemorySegmentImpl) arg).sessionImpl(); + if (!(callingSequence.needsReturnBuffer() && i == 0)) { // don't acquire unboxArena's scope + sessionImpl.acquire0(); + // add this scope _after_ we acquire, so we only release scopes we actually acquired + // in case an exception occurs + acquiredScopes.add(sessionImpl); + } + } BindingInterpreter.unbox(arg, callingSequence.argumentBindings(i), - (storage, type, value) -> leafArgs[invData.argIndexMap.get(storage)] = value, unboxArena); + (storage, value) -> leafArgs[invData.argIndexMap.get(storage)] = value, unboxArena); } // call leaf @@ -194,6 +210,10 @@ public Object load(VMStorage storage, Class type) { return BindingInterpreter.box(callingSequence.returnBindings(), (storage, type) -> o, allocator); } + } finally { + for (MemorySessionImpl sessionImpl : acquiredScopes) { + sessionImpl.release0(); + } } } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java b/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java index 2c2bcbcc163..3729093f09e 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java @@ -63,7 +63,11 @@ private static LinkerOptions forShared(BiConsumer= 0; offset++) { - byte curr = segment.get(JAVA_BYTE, start + offset); - if (curr == 0) { - return offset; - } - } - throw new IllegalArgumentException("String too large"); - } - static Map indexMap(Binding.Move[] moves) { return IntStream.range(0, moves.length) .boxed() @@ -434,20 +418,6 @@ public MemorySegment allocate(long byteSize, long byteAlignment) { }; } - public static final class SimpleVaArg { - public final MemoryLayout layout; - public final Object value; - - public SimpleVaArg(MemoryLayout layout, Object value) { - this.layout = layout; - this.value = value; - } - - public VarHandle varHandle() { - return layout.varHandle(); - } - } - static void writeOverSized(MemorySegment ptr, Class type, Object o) { // use VH_LONG for integers to zero out the whole register in the process if (type == long.class) { @@ -515,4 +485,35 @@ static Object read(MemorySegment ptr, long offset, Class type) { throw new IllegalArgumentException("Unsupported carrier: " + type); } } + + public static Map canonicalLayouts(ValueLayout longLayout, ValueLayout sizetLayout, ValueLayout wchartLayout) { + return Map.ofEntries( + // specified canonical layouts + Map.entry("bool", ValueLayout.JAVA_BOOLEAN), + Map.entry("char", ValueLayout.JAVA_BYTE), + Map.entry("short", ValueLayout.JAVA_SHORT), + Map.entry("int", ValueLayout.JAVA_INT), + Map.entry("float", ValueLayout.JAVA_FLOAT), + Map.entry("long", longLayout), + Map.entry("long long", ValueLayout.JAVA_LONG), + Map.entry("double", ValueLayout.JAVA_DOUBLE), + Map.entry("void*", ValueLayout.ADDRESS), + Map.entry("size_t", sizetLayout), + Map.entry("wchar_t", wchartLayout), + // unspecified size-dependent layouts + Map.entry("int8_t", ValueLayout.JAVA_BYTE), + Map.entry("int16_t", ValueLayout.JAVA_SHORT), + Map.entry("int32_t", ValueLayout.JAVA_INT), + Map.entry("int64_t", ValueLayout.JAVA_LONG), + // unspecified JNI layouts + Map.entry("jboolean", ValueLayout.JAVA_BOOLEAN), + Map.entry("jchar", ValueLayout.JAVA_CHAR), + Map.entry("jbyte", ValueLayout.JAVA_BYTE), + Map.entry("jshort", ValueLayout.JAVA_SHORT), + Map.entry("jint", ValueLayout.JAVA_INT), + Map.entry("jlong", ValueLayout.JAVA_LONG), + Map.entry("jfloat", ValueLayout.JAVA_FLOAT), + Map.entry("jdouble", ValueLayout.JAVA_DOUBLE) + ); + } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallLinker.java index 7a3229ad531..c5d53a4c5fe 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallLinker.java @@ -177,7 +177,7 @@ private static Object invokeInterpBindings(MethodHandle leaf, Object[] lowLevelA Object[] returnValues = new Object[invData.retIndexMap.size()]; if (leaf.type().returnType() != void.class) { BindingInterpreter.unbox(o, invData.callingSequence.returnBindings(), - (storage, type, value) -> returnValues[invData.retIndexMap.get(storage)] = value, null); + (storage, value) -> returnValues[invData.retIndexMap.get(storage)] = value, null); } if (returnValues.length == 0) { @@ -187,15 +187,10 @@ private static Object invokeInterpBindings(MethodHandle leaf, Object[] lowLevelA } else { assert invData.callingSequence.needsReturnBuffer(); - Binding.VMStore[] retMoves = invData.callingSequence.returnBindings().stream() - .filter(Binding.VMStore.class::isInstance) - .map(Binding.VMStore.class::cast) - .toArray(Binding.VMStore[]::new); - - assert returnValues.length == retMoves.length; + assert returnValues.length == invData.retMoves().length; int retBufWriteOffset = 0; - for (int i = 0; i < retMoves.length; i++) { - Binding.VMStore store = retMoves[i]; + for (int i = 0; i < invData.retMoves().length; i++) { + Binding.VMStore store = invData.retMoves()[i]; Object value = returnValues[i]; SharedUtils.writeOverSized(returnBuffer.asSlice(retBufWriteOffset), store.type(), value); retBufWriteOffset += invData.abi.arch.typeSize(store.storage().type()); diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallStubs.java b/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallStubs.java index 854e5643dc9..79adbe51542 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallStubs.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallStubs.java @@ -28,6 +28,7 @@ import java.lang.foreign.Arena; import jdk.internal.foreign.MemorySessionImpl; +import jdk.internal.foreign.Utils; public final class UpcallStubs { @@ -36,7 +37,7 @@ private UpcallStubs() { private static void freeUpcallStub(long stubAddress) { if (!freeUpcallStub0(stubAddress)) { - throw new IllegalStateException("Not a stub address: " + stubAddress); + throw new IllegalStateException("Not a stub address: " + Utils.toHexString(stubAddress)); } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64Linker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64Linker.java index 28ff423d388..54307e1ec21 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64Linker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64Linker.java @@ -27,12 +27,16 @@ import jdk.internal.foreign.abi.AbstractLinker; import jdk.internal.foreign.abi.LinkerOptions; +import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.aarch64.CallArranger; import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.nio.ByteOrder; +import java.util.Map; /** * ABI implementation based on ARM document "Procedure Call Standard for @@ -40,6 +44,9 @@ */ public final class LinuxAArch64Linker extends AbstractLinker { + static final Map CANONICAL_LAYOUTS = + SharedUtils.canonicalLayouts(ValueLayout.JAVA_LONG, ValueLayout.JAVA_LONG, ValueLayout.JAVA_INT); + public static LinuxAArch64Linker getInstance() { final class Holder { private static final LinuxAArch64Linker INSTANCE = new LinuxAArch64Linker(); @@ -66,4 +73,9 @@ protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescrip protected ByteOrder linkerByteOrder() { return ByteOrder.LITTLE_ENDIAN; } + + @Override + public Map canonicalLayouts() { + return CANONICAL_LAYOUTS; + } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64Linker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64Linker.java index 1a1bdc31be9..3f380a04db8 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64Linker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64Linker.java @@ -27,12 +27,16 @@ import jdk.internal.foreign.abi.AbstractLinker; import jdk.internal.foreign.abi.LinkerOptions; +import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.aarch64.CallArranger; import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.nio.ByteOrder; +import java.util.Map; /** * ABI implementation for macOS on Apple silicon. Based on AAPCS with @@ -40,6 +44,9 @@ */ public final class MacOsAArch64Linker extends AbstractLinker { + static final Map CANONICAL_LAYOUTS = + SharedUtils.canonicalLayouts(ValueLayout.JAVA_LONG, ValueLayout.JAVA_LONG, ValueLayout.JAVA_INT); + public static MacOsAArch64Linker getInstance() { final class Holder { private static final MacOsAArch64Linker INSTANCE = new MacOsAArch64Linker(); @@ -66,4 +73,9 @@ protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescrip protected ByteOrder linkerByteOrder() { return ByteOrder.LITTLE_ENDIAN; } + + @Override + public Map canonicalLayouts() { + return CANONICAL_LAYOUTS; + } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/windows/WindowsAArch64Linker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/windows/WindowsAArch64Linker.java index a654d23fc89..71d18d7b182 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/windows/WindowsAArch64Linker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/windows/WindowsAArch64Linker.java @@ -28,18 +28,25 @@ import jdk.internal.foreign.abi.AbstractLinker; import jdk.internal.foreign.abi.LinkerOptions; +import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.aarch64.CallArranger; import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.nio.ByteOrder; +import java.util.Map; /** * ABI implementation for Windows/AArch64. Based on AAPCS with * changes to va_list. */ public final class WindowsAArch64Linker extends AbstractLinker { + + static final Map CANONICAL_LAYOUTS = + SharedUtils.canonicalLayouts(ValueLayout.JAVA_INT, ValueLayout.JAVA_LONG, ValueLayout.JAVA_CHAR); private static WindowsAArch64Linker instance; public static WindowsAArch64Linker getInstance() { @@ -63,4 +70,9 @@ protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescrip protected ByteOrder linkerByteOrder() { return ByteOrder.LITTLE_ENDIAN; } + + @Override + public Map canonicalLayouts() { + return CANONICAL_LAYOUTS; + } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIType.java b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIType.java index 465a17d2298..f5e3c29fc81 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIType.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FFIType.java @@ -37,13 +37,13 @@ import java.lang.foreign.ValueLayout; import java.lang.invoke.VarHandle; import java.util.Collections; -import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.Predicate; import static java.lang.foreign.ValueLayout.ADDRESS; +import static java.lang.foreign.ValueLayout.JAVA_BYTE; import static java.lang.foreign.ValueLayout.JAVA_INT; import static java.lang.foreign.ValueLayout.JAVA_LONG; import static java.lang.foreign.ValueLayout.JAVA_SHORT; @@ -58,18 +58,14 @@ * } ffi_type; */ class FFIType { - private static final ValueLayout SIZE_T = switch ((int) ADDRESS.byteSize()) { - case 8 -> JAVA_LONG; - case 4 -> JAVA_INT; - default -> throw new IllegalStateException("Address size not supported: " + ADDRESS.byteSize()); - }; + static final ValueLayout SIZE_T = layoutFor((int)ADDRESS.byteSize()); private static final ValueLayout UNSIGNED_SHORT = JAVA_SHORT; private static final StructLayout LAYOUT = Utils.computePaddedStructLayout( SIZE_T, UNSIGNED_SHORT, UNSIGNED_SHORT.withName("type"), ADDRESS.withName("elements")); private static final VarHandle VH_TYPE = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("type")); private static final VarHandle VH_ELEMENTS = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("elements")); - private static final VarHandle VH_SIZE_T_ARRAY = SIZE_T.arrayElementVarHandle(); + private static final VarHandle VH_SIZE_T = SIZE_T.varHandle(); private static MemorySegment make(List elements, FFIABI abi, Arena scope) { MemorySegment elementsSeg = scope.allocate((elements.size() + 1) * ADDRESS.byteSize()); @@ -83,8 +79,8 @@ private static MemorySegment make(List elements, FFIABI abi, Arena elementsSeg.setAtIndex(ADDRESS, i, MemorySegment.NULL); MemorySegment ffiType = scope.allocate(LAYOUT); - VH_TYPE.set(ffiType, LibFallback.structTag()); - VH_ELEMENTS.set(ffiType, elementsSeg); + VH_TYPE.set(ffiType, 0L, LibFallback.structTag()); + VH_ELEMENTS.set(ffiType, 0L, elementsSeg); return ffiType; } @@ -132,7 +128,7 @@ private static void verifyStructType(StructLayout structLayout, List") @@ -143,4 +139,23 @@ private static void verifyStructType(StructLayout structLayout, List JAVA_BYTE; + case 2 -> JAVA_SHORT; + case 4 -> JAVA_INT; + case 8 -> JAVA_LONG; + default -> throw new IllegalStateException("Unsupported size: " + byteSize); + }; + } + + private static long sizeTAtIndex(MemorySegment segment, int index) { + long offset = SIZE_T.scale(0, index); + if (VH_SIZE_T.varType() == long.class) { + return (long) VH_SIZE_T.get(segment, offset); + } else { + return (int) VH_SIZE_T.get(segment, offset); // 'erase' to long + } + } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FallbackLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FallbackLinker.java index 1adb6a6da9d..8e688c06eac 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FallbackLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FallbackLinker.java @@ -46,9 +46,18 @@ import java.nio.ByteOrder; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.function.Consumer; import static java.lang.foreign.ValueLayout.ADDRESS; +import static java.lang.foreign.ValueLayout.JAVA_BOOLEAN; +import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static java.lang.foreign.ValueLayout.JAVA_CHAR; +import static java.lang.foreign.ValueLayout.JAVA_DOUBLE; +import static java.lang.foreign.ValueLayout.JAVA_FLOAT; +import static java.lang.foreign.ValueLayout.JAVA_INT; +import static java.lang.foreign.ValueLayout.JAVA_LONG; +import static java.lang.foreign.ValueLayout.JAVA_SHORT; import static java.lang.invoke.MethodHandles.foldArguments; public final class FallbackLinker extends AbstractLinker { @@ -283,4 +292,47 @@ private static Object readValue(MemorySegment seg, MemoryLayout layout) { assert layout == null; return null; } + + @Override + public Map canonicalLayouts() { + // Avoid eager dependency on LibFallback, so we can safely check LibFallback.SUPPORTED + class Holder { + static final Map CANONICAL_LAYOUTS; + + static { + int wchar_size = LibFallback.wcharSize(); + MemoryLayout wchartLayout = switch(wchar_size) { + case 2 -> JAVA_CHAR; // prefer JAVA_CHAR + default -> FFIType.layoutFor(wchar_size); + }; + + CANONICAL_LAYOUTS = Map.ofEntries( + // specified canonical layouts + Map.entry("bool", JAVA_BOOLEAN), + Map.entry("char", JAVA_BYTE), + Map.entry("float", JAVA_FLOAT), + Map.entry("long long", JAVA_LONG), + Map.entry("double", JAVA_DOUBLE), + Map.entry("void*", ADDRESS), + // platform-dependent sizes + Map.entry("size_t", FFIType.SIZE_T), + Map.entry("short", FFIType.layoutFor(LibFallback.shortSize())), + Map.entry("int", FFIType.layoutFor(LibFallback.intSize())), + Map.entry("long", FFIType.layoutFor(LibFallback.longSize())), + Map.entry("wchar_t", wchartLayout), + // JNI types + Map.entry("jboolean", JAVA_BOOLEAN), + Map.entry("jchar", JAVA_CHAR), + Map.entry("jbyte", JAVA_BYTE), + Map.entry("jshort", JAVA_SHORT), + Map.entry("jint", JAVA_INT), + Map.entry("jlong", JAVA_LONG), + Map.entry("jfloat", JAVA_FLOAT), + Map.entry("jdouble", JAVA_DOUBLE) + ); + } + } + + return Holder.CANONICAL_LAYOUTS; + } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/LibFallback.java b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/LibFallback.java index f4461fc2f4f..7318c71b82f 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/LibFallback.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/LibFallback.java @@ -65,6 +65,12 @@ public Boolean run() { static MemorySegment pointerType() { return NativeConstants.POINTER_TYPE; } static MemorySegment voidType() { return NativeConstants.VOID_TYPE; } + // platform-dependent types + static int shortSize() { return NativeConstants.SIZEOF_SHORT; } + static int intSize() { return NativeConstants.SIZEOF_INT; } + static int longSize() {return NativeConstants.SIZEOF_LONG; } + static int wcharSize() {return NativeConstants.SIZEOF_WCHAR; } + static short structTag() { return NativeConstants.STRUCT_TAG; } private static final MethodType UPCALL_TARGET_TYPE = MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class); @@ -222,6 +228,10 @@ private static void checkStatus(int code) { private static native long ffi_type_float(); private static native long ffi_type_double(); private static native long ffi_type_pointer(); + private static native int ffi_sizeof_short(); + private static native int ffi_sizeof_int(); + private static native int ffi_sizeof_long(); + private static native int ffi_sizeof_wchar(); // put these in a separate class to avoid an UnsatisfiedLinkError // when LibFallback is initialized but the library is not present @@ -239,6 +249,11 @@ private NativeConstants() {} static final MemorySegment FLOAT_TYPE = MemorySegment.ofAddress(ffi_type_float()); static final MemorySegment DOUBLE_TYPE = MemorySegment.ofAddress(ffi_type_double()); static final MemorySegment POINTER_TYPE = MemorySegment.ofAddress(ffi_type_pointer()); + static final int SIZEOF_SHORT = ffi_sizeof_short(); + static final int SIZEOF_INT = ffi_sizeof_int(); + static final int SIZEOF_LONG = ffi_sizeof_long(); + static final int SIZEOF_WCHAR = ffi_sizeof_wchar(); + static final MemorySegment VOID_TYPE = MemorySegment.ofAddress(ffi_type_void()); static final short STRUCT_TAG = ffi_type_struct(); diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/linux/LinuxPPC64Linker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/linux/LinuxPPC64Linker.java index 150687d4078..7cf2d524bff 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/linux/LinuxPPC64Linker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/linux/LinuxPPC64Linker.java @@ -27,15 +27,22 @@ import jdk.internal.foreign.abi.AbstractLinker; import jdk.internal.foreign.abi.LinkerOptions; +import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.ppc64.CallArranger; import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.nio.ByteOrder; +import java.util.Map; public final class LinuxPPC64Linker extends AbstractLinker { + static final Map CANONICAL_LAYOUTS = + SharedUtils.canonicalLayouts(ValueLayout.JAVA_LONG, ValueLayout.JAVA_LONG, ValueLayout.JAVA_INT); + public static LinuxPPC64Linker getInstance() { final class Holder { private static final LinuxPPC64Linker INSTANCE = new LinuxPPC64Linker(); @@ -62,4 +69,9 @@ protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescrip protected ByteOrder linkerByteOrder() { return ByteOrder.BIG_ENDIAN; } + + @Override + public Map canonicalLayouts() { + return CANONICAL_LAYOUTS; + } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/linux/LinuxPPC64leLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/linux/LinuxPPC64leLinker.java index e92b5abe26a..be4d217d547 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/linux/LinuxPPC64leLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/linux/LinuxPPC64leLinker.java @@ -27,15 +27,22 @@ import jdk.internal.foreign.abi.AbstractLinker; import jdk.internal.foreign.abi.LinkerOptions; +import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.ppc64.CallArranger; import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.nio.ByteOrder; +import java.util.Map; public final class LinuxPPC64leLinker extends AbstractLinker { + static final Map CANONICAL_LAYOUTS = + SharedUtils.canonicalLayouts(ValueLayout.JAVA_LONG, ValueLayout.JAVA_LONG, ValueLayout.JAVA_INT); + public static LinuxPPC64leLinker getInstance() { final class Holder { private static final LinuxPPC64leLinker INSTANCE = new LinuxPPC64leLinker(); @@ -62,4 +69,9 @@ protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescrip protected ByteOrder linkerByteOrder() { return ByteOrder.LITTLE_ENDIAN; } + + @Override + public Map canonicalLayouts() { + return CANONICAL_LAYOUTS; + } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/riscv64/linux/LinuxRISCV64Linker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/riscv64/linux/LinuxRISCV64Linker.java index a6593979e99..1c3558b4742 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/riscv64/linux/LinuxRISCV64Linker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/riscv64/linux/LinuxRISCV64Linker.java @@ -30,14 +30,21 @@ import jdk.internal.foreign.abi.AbstractLinker; import jdk.internal.foreign.abi.LinkerOptions; +import jdk.internal.foreign.abi.SharedUtils; import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.nio.ByteOrder; +import java.util.Map; public final class LinuxRISCV64Linker extends AbstractLinker { + static final Map CANONICAL_LAYOUTS = + SharedUtils.canonicalLayouts(ValueLayout.JAVA_LONG, ValueLayout.JAVA_LONG, ValueLayout.JAVA_INT); + public static LinuxRISCV64Linker getInstance() { final class Holder { private static final LinuxRISCV64Linker INSTANCE = new LinuxRISCV64Linker(); @@ -64,4 +71,9 @@ protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescrip protected ByteOrder linkerByteOrder() { return ByteOrder.LITTLE_ENDIAN; } + + @Override + public Map canonicalLayouts() { + return CANONICAL_LAYOUTS; + } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/s390/linux/LinuxS390Linker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/s390/linux/LinuxS390Linker.java index ac004b9e1e0..80aa950026e 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/s390/linux/LinuxS390Linker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/s390/linux/LinuxS390Linker.java @@ -27,14 +27,21 @@ import jdk.internal.foreign.abi.AbstractLinker; import jdk.internal.foreign.abi.LinkerOptions; +import jdk.internal.foreign.abi.SharedUtils; import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.nio.ByteOrder; +import java.util.Map; public final class LinuxS390Linker extends AbstractLinker { + private static final Map CANONICAL_LAYOUTS = + SharedUtils.canonicalLayouts(ValueLayout.JAVA_LONG, ValueLayout.JAVA_LONG, ValueLayout.JAVA_INT); + public static LinuxS390Linker getInstance() { final class Holder { private static final LinuxS390Linker INSTANCE = new LinuxS390Linker(); @@ -61,4 +68,9 @@ protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescrip protected ByteOrder linkerByteOrder() { return ByteOrder.BIG_ENDIAN; } + + @Override + public Map canonicalLayouts() { + return CANONICAL_LAYOUTS; + } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVx64Linker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVx64Linker.java index 91c3beca229..91a0dc76831 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVx64Linker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVx64Linker.java @@ -27,17 +27,24 @@ import jdk.internal.foreign.abi.AbstractLinker; import jdk.internal.foreign.abi.LinkerOptions; +import jdk.internal.foreign.abi.SharedUtils; import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.nio.ByteOrder; +import java.util.Map; /** * ABI implementation based on System V ABI AMD64 supplement v.0.99.6 */ public final class SysVx64Linker extends AbstractLinker { + static final Map CANONICAL_LAYOUTS = + SharedUtils.canonicalLayouts(ValueLayout.JAVA_LONG, ValueLayout.JAVA_LONG, ValueLayout.JAVA_INT); + public static SysVx64Linker getInstance() { final class Holder { private static final SysVx64Linker INSTANCE = new SysVx64Linker(); @@ -64,4 +71,9 @@ protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescrip protected ByteOrder linkerByteOrder() { return ByteOrder.LITTLE_ENDIAN; } + + @Override + public Map canonicalLayouts() { + return CANONICAL_LAYOUTS; + } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/Windowsx64Linker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/Windowsx64Linker.java index 574f2b63411..f262bd6872b 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/Windowsx64Linker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/Windowsx64Linker.java @@ -26,17 +26,24 @@ import jdk.internal.foreign.abi.AbstractLinker; import jdk.internal.foreign.abi.LinkerOptions; +import jdk.internal.foreign.abi.SharedUtils; import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.nio.ByteOrder; +import java.util.Map; /** * ABI implementation based on Windows ABI AMD64 supplement v.0.99.6 */ public final class Windowsx64Linker extends AbstractLinker { + static final Map CANONICAL_LAYOUTS = + SharedUtils.canonicalLayouts(ValueLayout.JAVA_INT, ValueLayout.JAVA_LONG, ValueLayout.JAVA_CHAR); + public static Windowsx64Linker getInstance() { final class Holder { private static final Windowsx64Linker INSTANCE = new Windowsx64Linker(); @@ -63,4 +70,9 @@ protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescrip protected ByteOrder linkerByteOrder() { return ByteOrder.LITTLE_ENDIAN; } + + @Override + public Map canonicalLayouts() { + return CANONICAL_LAYOUTS; + } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractGroupLayout.java b/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractGroupLayout.java index 84b879aef07..6c5fba7ea6b 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractGroupLayout.java +++ b/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractGroupLayout.java @@ -39,8 +39,6 @@ * * @implSpec * This class is immutable, thread-safe and value-based. - * - * @since 19 */ public sealed abstract class AbstractGroupLayout & MemoryLayout> extends AbstractLayout diff --git a/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractLayout.java b/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractLayout.java index 10a796a4b18..42baaa69270 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractLayout.java +++ b/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractLayout.java @@ -25,16 +25,26 @@ */ package jdk.internal.foreign.layout; +import jdk.internal.foreign.LayoutPath; +import jdk.internal.foreign.LayoutPath.PathElementImpl.PathKind; import jdk.internal.foreign.Utils; import java.lang.foreign.GroupLayout; import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemoryLayout.PathElement; import java.lang.foreign.SequenceLayout; import java.lang.foreign.StructLayout; import java.lang.foreign.UnionLayout; import java.lang.foreign.ValueLayout; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.invoke.VarHandle; +import java.util.EnumSet; import java.util.Objects; import java.util.Optional; +import java.util.Set; +import java.util.function.Function; public abstract sealed class AbstractLayout & MemoryLayout> permits AbstractGroupLayout, PaddingLayoutImpl, SequenceLayoutImpl, ValueLayouts.AbstractValueLayout { @@ -53,8 +63,9 @@ public final L withName(String name) { return dup(byteAlignment(), Optional.of(name)); } + @SuppressWarnings("unchecked") public final L withoutName() { - return dup(byteAlignment(), Optional.empty()); + return name.isPresent() ? dup(byteAlignment(), Optional.empty()) : (L) this; } public final Optional name() { @@ -139,4 +150,72 @@ private static long requirePowerOfTwoAndGreaterOrEqualToOne(long value) { return value; } + public long scale(long offset, long index) { + if (offset < 0) { + throw new IllegalArgumentException("Negative offset: " + offset); + } + if (index < 0) { + throw new IllegalArgumentException("Negative index: " + index); + } + + return Math.addExact(offset, Math.multiplyExact(byteSize(), index)); + } + + public MethodHandle scaleHandle() { + class Holder { + static final MethodHandle MH_SCALE; + static { + try { + MH_SCALE = MethodHandles.lookup().findVirtual(MemoryLayout.class, "scale", + MethodType.methodType(long.class, long.class, long.class)); + } catch (ReflectiveOperationException e) { + throw new ExceptionInInitializerError(e); + } + } + } + return Holder.MH_SCALE.bindTo(this); + } + + + public long byteOffset(PathElement... elements) { + return computePathOp(LayoutPath.rootPath((MemoryLayout) this), LayoutPath::offset, + EnumSet.of(PathKind.SEQUENCE_ELEMENT, PathKind.SEQUENCE_RANGE, PathKind.DEREF_ELEMENT), elements); + } + + public MethodHandle byteOffsetHandle(PathElement... elements) { + return computePathOp(LayoutPath.rootPath((MemoryLayout) this), LayoutPath::offsetHandle, + EnumSet.of(PathKind.DEREF_ELEMENT), elements); + } + + public VarHandle varHandle(PathElement... elements) { + Objects.requireNonNull(elements); + if (this instanceof ValueLayout vl && elements.length == 0) { + return vl.varHandle(); // fast path + } + return computePathOp(LayoutPath.rootPath((MemoryLayout) this), LayoutPath::dereferenceHandle, + Set.of(), elements); + } + + public MethodHandle sliceHandle(PathElement... elements) { + return computePathOp(LayoutPath.rootPath((MemoryLayout) this), LayoutPath::sliceHandle, + Set.of(PathKind.DEREF_ELEMENT), elements); + } + + public MemoryLayout select(PathElement... elements) { + return computePathOp(LayoutPath.rootPath((MemoryLayout) this), LayoutPath::layout, + EnumSet.of(PathKind.SEQUENCE_ELEMENT_INDEX, PathKind.SEQUENCE_RANGE, PathKind.DEREF_ELEMENT), elements); + } + + private static Z computePathOp(LayoutPath path, Function finalizer, + Set badKinds, PathElement... elements) { + Objects.requireNonNull(elements); + for (PathElement e : elements) { + LayoutPath.PathElementImpl pathElem = (LayoutPath.PathElementImpl)Objects.requireNonNull(e); + if (badKinds.contains(pathElem.kind())) { + throw new IllegalArgumentException(String.format("Invalid %s selection in layout path", pathElem.kind().description())); + } + path = pathElem.apply(path); + } + return finalizer.apply(path); + } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/layout/ValueLayouts.java b/src/java.base/share/classes/jdk/internal/foreign/layout/ValueLayouts.java index 807da9cb253..2567227abf6 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/layout/ValueLayouts.java +++ b/src/java.base/share/classes/jdk/internal/foreign/layout/ValueLayouts.java @@ -31,16 +31,13 @@ import jdk.internal.reflect.Reflection; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.Stable; -import sun.invoke.util.Wrapper; +import java.lang.foreign.AddressLayout; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.AddressLayout; import java.lang.foreign.ValueLayout; import java.lang.invoke.VarHandle; import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.List; import java.util.Objects; import java.util.Optional; @@ -115,24 +112,6 @@ public boolean equals(Object other) { order.equals(otherValue.order); } - public final VarHandle arrayElementVarHandle(int... shape) { - Objects.requireNonNull(shape); - if (!Utils.isElementAligned((ValueLayout) this)) { - throw new UnsupportedOperationException("Layout alignment greater than its size"); - } - MemoryLayout layout = self(); - List path = new ArrayList<>(); - for (int i = shape.length; i > 0; i--) { - int size = shape[i - 1]; - if (size < 0) throw new IllegalArgumentException("Invalid shape size: " + size); - layout = MemoryLayout.sequenceLayout(size, layout); - path.add(MemoryLayout.PathElement.sequenceElement()); - } - layout = MemoryLayout.sequenceLayout(layout); - path.add(MemoryLayout.PathElement.sequenceElement()); - return layout.varHandle(path.toArray(new MemoryLayout.PathElement[0])); - } - /** * {@return the carrier associated with this value layout} */ @@ -177,7 +156,7 @@ static boolean isValidCarrier(Class carrier) { } @ForceInline - public final VarHandle accessHandle() { + public final VarHandle varHandle() { if (handle == null) { // this store to stable field is safe, because return value of 'makeMemoryAccessVarHandle' has stable identity handle = Utils.makeSegmentViewVarHandle(self()); @@ -393,7 +372,7 @@ public String toString() { *
  • {@link ValueLayout.OfFloat}, for {@code float.class}
  • *
  • {@link ValueLayout.OfLong}, for {@code long.class}
  • *
  • {@link ValueLayout.OfDouble}, for {@code double.class}
  • - *
  • {@link ValueLayout.OfAddress}, for {@code MemorySegment.class}
  • + *
  • {@link AddressLayout}, for {@code MemorySegment.class}
  • * * @param carrier the value layout carrier. * @param order the value layout's byte order. diff --git a/src/java.base/share/classes/jdk/internal/icu/text/BidiBase.java b/src/java.base/share/classes/jdk/internal/icu/text/BidiBase.java index 1161e9c8464..cdfdef9fbf9 100644 --- a/src/java.base/share/classes/jdk/internal/icu/text/BidiBase.java +++ b/src/java.base/share/classes/jdk/internal/icu/text/BidiBase.java @@ -4595,7 +4595,7 @@ public static void reorderVisually(byte[] levels, objectStart + " is out of range 0 to " + (objects.length-1)); } - if (0 > count || objects.length < (objectStart+count)) { + if (0 > count || objects.length - count < objectStart) { throw new IllegalArgumentException("Value count " + count + " is less than zero, or objectStart + count" + " is beyond objects length " + objects.length); diff --git a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java index 2fee4f2fb64..71c0fbc16bf 100644 --- a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java +++ b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java @@ -64,10 +64,10 @@ * Values should be annotated with the feature's {@code JEP}. */ public enum Feature { - // not used + // not used, but required for interim javac to not warn. VIRTUAL_THREADS, - @JEP(number=442, title="Foreign Function & Memory API", status="Third Preview") FOREIGN, + @JEP(number=430, title="String Templates") STRING_TEMPLATES, @JEP(number=443, title="Unnamed Patterns and Variables") diff --git a/src/java.base/share/classes/jdk/internal/misc/VM.java b/src/java.base/share/classes/jdk/internal/misc/VM.java index 3e7351f6321..9b51cfad458 100644 --- a/src/java.base/share/classes/jdk/internal/misc/VM.java +++ b/src/java.base/share/classes/jdk/internal/misc/VM.java @@ -148,6 +148,7 @@ public static long maxDirectMemory() { // User-controllable flag that determines if direct buffers should be page // aligned. The "-XX:+PageAlignDirectMemory" option can be used to force // buffers, allocated by ByteBuffer.allocateDirect, to be page aligned. + @Stable private static boolean pageAlignDirectMemory; // Returns {@code true} if the direct buffers should be page aligned. This diff --git a/src/java.base/share/classes/jdk/internal/module/Modules.java b/src/java.base/share/classes/jdk/internal/module/Modules.java index 00a1c1ab290..be3f8b63a9b 100644 --- a/src/java.base/share/classes/jdk/internal/module/Modules.java +++ b/src/java.base/share/classes/jdk/internal/module/Modules.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -134,6 +134,13 @@ public static void addOpensToAllUnnamed(Module m, String pn) { JLA.addOpensToAllUnnamed(m, pn); } + /** + * Adds native access to all unnamed modules. + */ + public static void addEnableNativeAccessToAllUnnamed() { + JLA.addEnableNativeAccessToAllUnnamed(); + } + /** * Updates module m to use a service. * Same as m2.addUses(service) but without a caller check. diff --git a/src/java.base/share/classes/jdk/internal/reflect/Reflection.java b/src/java.base/share/classes/jdk/internal/reflect/Reflection.java index 81eaf1545ff..f6fa4995388 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/Reflection.java +++ b/src/java.base/share/classes/jdk/internal/reflect/Reflection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -31,6 +31,7 @@ import java.util.Objects; import java.util.Set; +import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.misc.VM; import jdk.internal.module.ModuleBootstrap; @@ -115,7 +116,10 @@ public static void ensureNativeAccess(Class currentClass, Class owner, Str Module module = currentClass != null ? currentClass.getModule() : ClassLoader.getSystemClassLoader().getUnnamedModule(); - SharedSecrets.getJavaLangAccess().ensureNativeAccess(module, owner, methodName); + class Holder { + static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + } + Holder.JLA.ensureNativeAccess(module, owner, methodName, currentClass); } /** diff --git a/src/java.base/share/classes/jdk/internal/util/Architecture.java b/src/java.base/share/classes/jdk/internal/util/Architecture.java index 5c861902743..4f193e75597 100644 --- a/src/java.base/share/classes/jdk/internal/util/Architecture.java +++ b/src/java.base/share/classes/jdk/internal/util/Architecture.java @@ -114,7 +114,7 @@ private static Architecture initArch(String archName) { } // Initialize the architecture by mapping aliases and names to the enum values. - private static Architecture CURRENT_ARCH = initArch(PlatformProps.CURRENT_ARCH_STRING); + private static final Architecture CURRENT_ARCH = initArch(PlatformProps.CURRENT_ARCH_STRING); /** * {@return {@code true} if the current architecture is X64, Aka amd64} diff --git a/src/java.base/share/classes/jdk/internal/util/HexDigits.java b/src/java.base/share/classes/jdk/internal/util/HexDigits.java index 5ffb454ae00..1adda90a65a 100644 --- a/src/java.base/share/classes/jdk/internal/util/HexDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/HexDigits.java @@ -91,6 +91,30 @@ public final class HexDigits implements Digits { private HexDigits() { } + /** + * For values from 0 to 255 return a short encoding a pair of hex ASCII-encoded digit characters in little-endian + * @param i value to convert + * @param ucase true uppper case, false lower case + * @return a short encoding a pair of hex ASCII-encoded digit characters + */ + public static short digitPair(int i, boolean ucase) { + /* + * 0b0100_0000_0100_0000 is a selector that selects letters (1 << 6), + * uppercase or not, and shifting it right by 1 bit incidentally + * becomes a bit offset between cases (1 << 5). + * + * ([0-9] & 0b100_0000) >> 1 => 0 + * ([a-f] & 0b100_0000) >> 1 => 32 + * + * [0-9] - 0 => [0-9] + * [a-f] - 32 => [A-F] + */ + short v = DIGITS[i & 0xff]; + return ucase + ? (short) (v - ((v & 0b0100_0000_0100_0000) >> 1)) + : v; + } + /** * Return a little-endian packed integer for the 4 ASCII bytes for an input unsigned 2-byte integer. * {@code b0} is the most significant byte and {@code b1} is the least significant byte. diff --git a/src/java.base/share/classes/jdk/internal/util/xml/impl/XMLStreamWriterImpl.java b/src/java.base/share/classes/jdk/internal/util/xml/impl/XMLStreamWriterImpl.java index d92c238d174..2aff75606a2 100644 --- a/src/java.base/share/classes/jdk/internal/util/xml/impl/XMLStreamWriterImpl.java +++ b/src/java.base/share/classes/jdk/internal/util/xml/impl/XMLStreamWriterImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -603,10 +603,6 @@ protected static class Element { * the parent element */ protected Element _parent; - /** - * The size of the stack. - */ - protected short _Depth; /** * indicate if an element is an empty one */ diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index 26be8e9b587..bcf7daf8189 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -149,7 +149,7 @@ exports jdk.internal.javac to java.compiler, jdk.compiler, - jdk.incubator.vector, // participates in preview features + jdk.incubator.vector, jdk.jshell; exports jdk.internal.access to java.desktop, diff --git a/src/java.base/share/classes/sun/launcher/LauncherHelper.java b/src/java.base/share/classes/sun/launcher/LauncherHelper.java index d30f61d9e2b..05f797da70f 100644 --- a/src/java.base/share/classes/sun/launcher/LauncherHelper.java +++ b/src/java.base/share/classes/sun/launcher/LauncherHelper.java @@ -102,6 +102,7 @@ private LauncherHelper() {} private static final String MAIN_CLASS = "Main-Class"; private static final String ADD_EXPORTS = "Add-Exports"; private static final String ADD_OPENS = "Add-Opens"; + private static final String ENABLE_NATIVE_ACCESS = "Enable-Native-Access"; private static StringBuilder outBuf = new StringBuilder(); @@ -632,6 +633,13 @@ static String getMainClassFromJar(String jarname) { if (opens != null) { addExportsOrOpens(opens, true); } + String enableNativeAccess = mainAttrs.getValue(ENABLE_NATIVE_ACCESS); + if (enableNativeAccess != null) { + if (!enableNativeAccess.equals("ALL-UNNAMED")) { + abort(null, "java.launcher.jar.error.illegal.ena.value", enableNativeAccess); + } + Modules.addEnableNativeAccessToAllUnnamed(); + } /* * Hand off to FXHelper if it detects a JavaFX application diff --git a/src/java.base/share/classes/sun/launcher/resources/launcher.properties b/src/java.base/share/classes/sun/launcher/resources/launcher.properties index 9d5373d0856..219f0d977b5 100644 --- a/src/java.base/share/classes/sun/launcher/resources/launcher.properties +++ b/src/java.base/share/classes/sun/launcher/resources/launcher.properties @@ -63,8 +63,8 @@ java.launcher.opt.footer = \ \ can also be ALL-DEFAULT, ALL-SYSTEM,\n\ \ ALL-MODULE-PATH.\n\ \ --enable-native-access [,...]\n\ -\ modules that are permitted to perform restricted native operations.\n\ -\ can also be ALL-UNNAMED.\n\ +\ allow code in modules to access code and data outside the Java runtime.\n\ +\ can also be ALL-UNNAMED to indicate code on the class path.\n\ \ --list-modules\n\ \ list observable modules and exit\n\ \ -d \n\ @@ -268,6 +268,8 @@ java.launcher.jar.error1=\ java.launcher.jar.error2=manifest not found in {0} java.launcher.jar.error3=no main manifest attribute, in {0} java.launcher.jar.error4=error loading java agent in {0} +java.launcher.jar.error.illegal.ena.value=\ + Error: illegal value \"{0}\" for Enable-Native-Access manifest attribute. Only 'ALL-UNNAMED' is allowed java.launcher.init.error=initialization error java.launcher.javafx.error1=\ Error: The JavaFX launchApplication method has the wrong signature, it\n\ diff --git a/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java b/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java index 0cbcdf7c94a..f53508859ae 100644 --- a/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java @@ -50,9 +50,8 @@ import jdk.internal.access.JavaIOFileDescriptorAccess; import jdk.internal.access.SharedSecrets; -import jdk.internal.foreign.AbstractMemorySegmentImpl; -import jdk.internal.foreign.MappedMemorySegmentImpl; import jdk.internal.foreign.MemorySessionImpl; +import jdk.internal.foreign.SegmentFactories; import jdk.internal.misc.Blocker; import jdk.internal.misc.ExtendedMapMode; import jdk.internal.misc.Unsafe; @@ -1335,22 +1334,7 @@ public MemorySegment map(MapMode mode, long offset, long size, Arena arena) if (mode == MapMode.READ_ONLY) { readOnly = true; } - if (unmapper != null) { - AbstractMemorySegmentImpl segment = - new MappedMemorySegmentImpl(unmapper.address(), unmapper, size, - readOnly, sessionImpl); - MemorySessionImpl.ResourceList.ResourceCleanup resource = - new MemorySessionImpl.ResourceList.ResourceCleanup() { - @Override - public void cleanup() { - unmapper.unmap(); - } - }; - sessionImpl.addOrCleanupIfFail(resource); - return segment; - } else { - return new MappedMemorySegmentImpl(0, null, 0, readOnly, sessionImpl); - } + return SegmentFactories.mapSegment(size, unmapper, readOnly, sessionImpl); } private Unmapper mapInternal(MapMode mode, long position, long size, int prot, boolean isSync) diff --git a/src/java.base/share/classes/sun/nio/ch/Invoker.java b/src/java.base/share/classes/sun/nio/ch/Invoker.java index 599191b8872..475669a2b82 100644 --- a/src/java.base/share/classes/sun/nio/ch/Invoker.java +++ b/src/java.base/share/classes/sun/nio/ch/Invoker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -70,12 +70,8 @@ void incrementInvokeCount() { handlerInvokeCount++; } } - private static final ThreadLocal myGroupAndInvokeCount = - new ThreadLocal() { - @Override protected GroupAndInvokeCount initialValue() { - return null; - } - }; + private static final ThreadLocal myGroupAndInvokeCount + = new ThreadLocal(); /** * Binds this thread to the given group diff --git a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java index 6c65a964a65..af40407b85d 100644 --- a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java @@ -494,7 +494,10 @@ public int read(ByteBuffer buf) throws IOException { } long start = SocketReadEvent.timestamp(); int nbytes = implRead(buf); - SocketReadEvent.offer(start, nbytes, remoteAddress(), 0); + long duration = SocketReadEvent.timestamp() - start; + if (SocketReadEvent.shouldCommit(duration)) { + SocketReadEvent.emit(start, duration, nbytes, remoteAddress(), 0); + } return nbytes; } @@ -508,7 +511,10 @@ public long read(ByteBuffer[] dsts, int offset, int length) } long start = SocketReadEvent.timestamp(); long nbytes = implRead(dsts, offset, length); - SocketReadEvent.offer(start, nbytes, remoteAddress(), 0); + long duration = SocketReadEvent.timestamp() - start; + if (SocketReadEvent.shouldCommit(duration)) { + SocketReadEvent.emit(start, duration, nbytes, remoteAddress(), 0); + } return nbytes; } @@ -619,7 +625,10 @@ public int write(ByteBuffer buf) throws IOException { } long start = SocketWriteEvent.timestamp(); int nbytes = implWrite(buf); - SocketWriteEvent.offer(start, nbytes, remoteAddress()); + long duration = SocketWriteEvent.timestamp() - start; + if (SocketWriteEvent.shouldCommit(duration)) { + SocketWriteEvent.emit(start, duration, nbytes, remoteAddress()); + } return nbytes; } @@ -632,7 +641,10 @@ public long write(ByteBuffer[] srcs, int offset, int length) } long start = SocketWriteEvent.timestamp(); long nbytes = implWrite(srcs, offset, length); - SocketWriteEvent.offer(start, nbytes, remoteAddress()); + long duration = SocketWriteEvent.timestamp() - start; + if (SocketWriteEvent.shouldCommit(duration)) { + SocketWriteEvent.emit(start, duration, nbytes, remoteAddress()); + } return nbytes; } diff --git a/src/java.base/share/classes/sun/security/ec/ECPrivateKeyImpl.java b/src/java.base/share/classes/sun/security/ec/ECPrivateKeyImpl.java index a2a54391c1f..b1b8b2d188f 100644 --- a/src/java.base/share/classes/sun/security/ec/ECPrivateKeyImpl.java +++ b/src/java.base/share/classes/sun/security/ec/ECPrivateKeyImpl.java @@ -26,6 +26,8 @@ package sun.security.ec; import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.math.BigInteger; import java.security.*; @@ -41,7 +43,7 @@ /** * Key implementation for EC private keys. - * + *

    * ASN.1 syntax for EC private keys from SEC 1 v1.5 (draft): * *

    @@ -64,6 +66,7 @@
      */
     public final class ECPrivateKeyImpl extends PKCS8Key implements ECPrivateKey {
     
    +    @java.io.Serial
         private static final long serialVersionUID = 88695385615075129L;
     
         private BigInteger s;       // private value
    @@ -218,4 +221,20 @@ public PublicKey calculatePublicKey() {
                         "Unexpected error calculating public key", e);
             }
         }
    +
    +    /**
    +     * Restores the state of this object from the stream.
    +     * 

    + * Deserialization of this object is not supported. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException( + "ECPrivateKeyImpl keys are not directly deserializable"); + } } diff --git a/src/java.base/share/classes/sun/security/ec/ECPublicKeyImpl.java b/src/java.base/share/classes/sun/security/ec/ECPublicKeyImpl.java index 8a363607d42..8f099f1e52f 100644 --- a/src/java.base/share/classes/sun/security/ec/ECPublicKeyImpl.java +++ b/src/java.base/share/classes/sun/security/ec/ECPublicKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,8 @@ import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.security.*; import java.security.interfaces.*; import java.security.spec.*; @@ -44,6 +46,7 @@ */ public final class ECPublicKeyImpl extends X509Key implements ECPublicKey { + @java.io.Serial private static final long serialVersionUID = -2462037275160462289L; @SuppressWarnings("serial") // Type of field is not @@ -123,10 +126,27 @@ public String toString() { + "\n parameters: " + params; } - protected Object writeReplace() throws java.io.ObjectStreamException { + @java.io.Serial + private Object writeReplace() throws java.io.ObjectStreamException { return new KeyRep(KeyRep.Type.PUBLIC, getAlgorithm(), getFormat(), getEncoded()); } -} + + /** + * Restores the state of this object from the stream. + *

    + * Deserialization of this object is not supported. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException( + "ECPublicKeyImpl keys are not directly deserializable"); + } +} \ No newline at end of file diff --git a/src/java.base/share/classes/sun/security/ec/XDHPrivateKeyImpl.java b/src/java.base/share/classes/sun/security/ec/XDHPrivateKeyImpl.java index 8dd718c1d3b..dfc0d0f6cd3 100644 --- a/src/java.base/share/classes/sun/security/ec/XDHPrivateKeyImpl.java +++ b/src/java.base/share/classes/sun/security/ec/XDHPrivateKeyImpl.java @@ -37,6 +37,7 @@ public final class XDHPrivateKeyImpl extends PKCS8Key implements XECPrivateKey { + @java.io.Serial private static final long serialVersionUID = 1L; @SuppressWarnings("serial") // Type of field is not Serializable @@ -103,7 +104,7 @@ public Optional getScalar() { @Override public PublicKey calculatePublicKey() { - XECParameters params = paramSpec.getName().equals("X25519") + XECParameters params = paramSpec.getName().equalsIgnoreCase("X25519") ? XECParameters.X25519 : XECParameters.X448; try { @@ -114,5 +115,20 @@ public PublicKey calculatePublicKey() { "Unexpected error calculating public key", e); } } -} + /** + * Restores the state of this object from the stream. + *

    + * Deserialization of this object is not supported. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException( + "XDHPrivateKeyImpl keys are not directly deserializable"); + } +} diff --git a/src/java.base/share/classes/sun/security/ec/XDHPublicKeyImpl.java b/src/java.base/share/classes/sun/security/ec/XDHPublicKeyImpl.java index f1f3c88c1be..e6f8961f412 100644 --- a/src/java.base/share/classes/sun/security/ec/XDHPublicKeyImpl.java +++ b/src/java.base/share/classes/sun/security/ec/XDHPublicKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -25,6 +25,9 @@ package sun.security.ec; +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.KeyRep; @@ -39,6 +42,7 @@ public final class XDHPublicKeyImpl extends X509Key implements XECPublicKey { + @java.io.Serial private static final long serialVersionUID = 1L; private final BigInteger u; @@ -107,7 +111,8 @@ public String getAlgorithm() { return "XDH"; } - protected Object writeReplace() throws java.io.ObjectStreamException { + @java.io.Serial + private Object writeReplace() throws java.io.ObjectStreamException { return new KeyRep(KeyRep.Type.PUBLIC, getAlgorithm(), getFormat(), @@ -130,5 +135,21 @@ private static void reverse(byte [] arr) { j--; } } + + /** + * Restores the state of this object from the stream. + *

    + * Deserialization of this object is not supported. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException( + "XDHPublicKeyImpl keys are not directly deserializable"); + } } diff --git a/src/java.base/share/classes/sun/security/ec/ed/EdDSAPrivateKeyImpl.java b/src/java.base/share/classes/sun/security/ec/ed/EdDSAPrivateKeyImpl.java index 4adea3f857d..661ec9ed1b7 100644 --- a/src/java.base/share/classes/sun/security/ec/ed/EdDSAPrivateKeyImpl.java +++ b/src/java.base/share/classes/sun/security/ec/ed/EdDSAPrivateKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -26,6 +26,8 @@ package sun.security.ec.ed; import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.security.InvalidKeyException; import java.security.interfaces.EdECPrivateKey; import java.util.Optional; @@ -38,6 +40,7 @@ public final class EdDSAPrivateKeyImpl extends PKCS8Key implements EdECPrivateKey { + @java.io.Serial private static final long serialVersionUID = 1L; @SuppressWarnings("serial") // Type of field is not Serializable @@ -102,4 +105,20 @@ public NamedParameterSpec getParams() { public Optional getBytes() { return Optional.of(getKey()); } + + /** + * Restores the state of this object from the stream. + *

    + * Deserialization of this object is not supported. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException( + "EdDSAPrivateKeyImpl keys are not directly deserializable"); + } } diff --git a/src/java.base/share/classes/sun/security/ec/ed/EdDSAPublicKeyImpl.java b/src/java.base/share/classes/sun/security/ec/ed/EdDSAPublicKeyImpl.java index c1cba3a1fb1..671b53e1c6e 100644 --- a/src/java.base/share/classes/sun/security/ec/ed/EdDSAPublicKeyImpl.java +++ b/src/java.base/share/classes/sun/security/ec/ed/EdDSAPublicKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -25,6 +25,9 @@ package sun.security.ec.ed; +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.KeyRep; @@ -39,6 +42,7 @@ public final class EdDSAPublicKeyImpl extends X509Key implements EdECPublicKey { + @java.io.Serial private static final long serialVersionUID = 1L; @SuppressWarnings("serial") // Type of field is not Serializable @@ -108,7 +112,8 @@ public String getAlgorithm() { return "EdDSA"; } - protected Object writeReplace() throws java.io.ObjectStreamException { + @java.io.Serial + private Object writeReplace() throws java.io.ObjectStreamException { return new KeyRep(KeyRep.Type.PUBLIC, getAlgorithm(), getFormat(), getEncoded()); } @@ -129,4 +134,20 @@ private static void reverse(byte [] arr) { j--; } } + + /** + * Restores the state of this object from the stream. + *

    + * Deserialization of this object is not supported. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException( + "EdDSAPublicKeyImpl keys are not directly deserializable"); + } } diff --git a/src/java.base/share/classes/sun/security/provider/DSAPublicKeyImpl.java b/src/java.base/share/classes/sun/security/provider/DSAPublicKeyImpl.java index 2c402ef2863..ca84d202c14 100644 --- a/src/java.base/share/classes/sun/security/provider/DSAPublicKeyImpl.java +++ b/src/java.base/share/classes/sun/security/provider/DSAPublicKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -25,17 +25,20 @@ package sun.security.provider; +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.math.BigInteger; import java.security.KeyRep; import java.security.InvalidKeyException; /** * An X.509 public key for the Digital Signature Algorithm. - * + *

    * The difference between DSAPublicKeyImpl and DSAPublicKey is that * DSAPublicKeyImpl calls writeReplace with KeyRep, and DSAPublicKey * calls writeObject. - * + *

    * See the comments in DSAKeyFactory, 4532506, and 6232513. * */ @@ -72,10 +75,26 @@ public DSAPublicKeyImpl(byte[] encoded) throws InvalidKeyException { } @java.io.Serial - protected Object writeReplace() throws java.io.ObjectStreamException { + private Object writeReplace() throws java.io.ObjectStreamException { return new KeyRep(KeyRep.Type.PUBLIC, getAlgorithm(), getFormat(), getEncoded()); } + + /** + * Restores the state of this object from the stream. + *

    + * Deserialization of this object is not supported. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException( + "DSAPublicKeyImpl keys are not directly deserializable"); + } } diff --git a/src/java.base/share/classes/sun/security/provider/PolicyFile.java b/src/java.base/share/classes/sun/security/provider/PolicyFile.java index 2a5c13b8bc2..f218591ce11 100644 --- a/src/java.base/share/classes/sun/security/provider/PolicyFile.java +++ b/src/java.base/share/classes/sun/security/provider/PolicyFile.java @@ -2098,8 +2098,17 @@ public SelfPermission(String type, String name, String actions, this.actions.equals(that.actions))) return false; - if (this.certs.length != that.certs.length) + if ((this.certs == null) && (that.certs == null)) { + return true; + } + + if ((this.certs == null) || (that.certs == null)) { + return false; + } + + if (this.certs.length != that.certs.length) { return false; + } int i,j; boolean match; @@ -2163,7 +2172,7 @@ public String getSelfActions() { } public Certificate[] getCerts() { - return certs; + return (certs == null ? null : certs.clone()); } /** @@ -2176,6 +2185,22 @@ public Certificate[] getCerts() { @Override public String toString() { return "(SelfPermission " + type + " " + name + " " + actions + ")"; } + + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + if (certs != null) { + this.certs = certs.clone(); + } + } } /** diff --git a/src/java.base/share/classes/sun/security/provider/SecureRandom.java b/src/java.base/share/classes/sun/security/provider/SecureRandom.java index 5df3adf6ea8..a8d82b649b0 100644 --- a/src/java.base/share/classes/sun/security/provider/SecureRandom.java +++ b/src/java.base/share/classes/sun/security/provider/SecureRandom.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -26,6 +26,7 @@ package sun.security.provider; import java.io.IOException; +import java.io.InvalidObjectException; import java.security.MessageDigest; import java.security.SecureRandomSpi; import java.security.NoSuchAlgorithmException; @@ -192,7 +193,7 @@ private static void updateState(byte[] state, byte[] output) { /** * This static object will be seeded by SeedGenerator, and used * to seed future instances of SHA1PRNG SecureRandoms. - * + *

    * Bloch, Effective Java Second Edition: Item 71 */ private static class SeederHolder { @@ -265,18 +266,24 @@ public synchronized void engineNextBytes(byte[] result) { } /* - * readObject is called to restore the state of the random object from - * a stream. We have to create a new instance of MessageDigest, because + * This method is called to restore the state of the random object from + * a stream. + *

    + * We have to create a new instance of {@code MessageDigest}, because * it is not included in the stream (it is marked "transient"). - * - * Note that the engineNextBytes() method invoked on the restored random - * object will yield the exact same (random) bytes as the original. + *

    + * Note that the {@code engineNextBytes()} method invoked on the restored + * random object will yield the exact same (random) bytes as the original. * If you do not want this behaviour, you should re-seed the restored - * random object, using engineSetSeed(). + * random object, using {@code engineSetSeed()}. + * + * @param s the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded */ @java.io.Serial private void readObject(java.io.ObjectInputStream s) - throws IOException, ClassNotFoundException { + throws IOException, ClassNotFoundException { s.defaultReadObject (); @@ -295,5 +302,34 @@ private void readObject(java.io.ObjectInputStream s) "internal error: SHA-1 not available.", exc); } } + + // Various consistency checks + if ((remainder == null) && (remCount > 0)) { + throw new InvalidObjectException( + "Remainder indicated, but no data available"); + } + + // Not yet allocated state + if (state == null) { + if (remainder == null) { + return; + } else { + throw new InvalidObjectException( + "Inconsistent buffer allocations"); + } + } + + // Sanity check on sizes/pointer + if ((state.length != DIGEST_SIZE) || + ((remainder != null) && (remainder.length != DIGEST_SIZE)) || + (remCount < 0 ) || (remCount >= DIGEST_SIZE)) { + throw new InvalidObjectException( + "Inconsistent buffer sizes/state"); + } + + state = state.clone(); + if (remainder != null) { + remainder = remainder.clone(); + } } } diff --git a/src/java.base/share/classes/sun/security/provider/certpath/CertPathHelper.java b/src/java.base/share/classes/sun/security/provider/certpath/CertPathHelper.java index 3675209e03d..203b92ce30a 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/CertPathHelper.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/CertPathHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -26,14 +26,10 @@ package sun.security.provider.certpath; import java.util.Date; -import java.util.Set; import java.security.cert.TrustAnchor; -import java.security.cert.X509CertSelector; import java.security.cert.X509CRLSelector; -import sun.security.x509.GeneralNameInterface; - /** * Helper class that allows access to JDK specific known-public methods in the * java.security.cert package. It relies on a subclass in the @@ -55,18 +51,10 @@ protected CertPathHelper() { // empty } - protected abstract void implSetPathToNames(X509CertSelector sel, - Set names); - protected abstract void implSetDateAndTime(X509CRLSelector sel, Date date, long skew); protected abstract boolean implIsJdkCA(TrustAnchor anchor); - static void setPathToNames(X509CertSelector sel, - Set names) { - instance.implSetPathToNames(sel, names); - } - public static void setDateAndTime(X509CRLSelector sel, Date date, long skew) { instance.implSetDateAndTime(sel, date, skew); } diff --git a/src/java.base/share/classes/sun/security/provider/certpath/ForwardBuilder.java b/src/java.base/share/classes/sun/security/provider/certpath/ForwardBuilder.java index 26f97efc3d9..125af069203 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/ForwardBuilder.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/ForwardBuilder.java @@ -47,7 +47,6 @@ import sun.security.x509.AuthorityInfoAccessExtension; import sun.security.x509.AuthorityKeyIdentifierExtension; import static sun.security.x509.PKIXExtensions.*; -import sun.security.x509.SubjectAlternativeNameExtension; import sun.security.x509.X500Name; import sun.security.x509.X509CertImpl; @@ -257,14 +256,6 @@ private void getMatchingCACerts(ForwardState currentState, */ caSelector.setSubject(currentState.issuerDN); - /* - * Match on subjectNamesTraversed (both DNs and AltNames) - * (checks that current cert's name constraints permit it - * to certify all the DNs and AltNames that have been traversed) - */ - CertPathHelper.setPathToNames - (caSelector, currentState.subjectNamesTraversed); - /* * check the validity period */ @@ -697,19 +688,6 @@ void verifyCert(X509Certificate cert, State currentState, // Don't bother to verify untrusted certificate more. currState.untrustedChecker.check(cert, Collections.emptySet()); - /* - * Abort if we encounter the same certificate or a certificate with - * the same public key, subject DN, and subjectAltNames as a cert - * that is already in path. - */ - for (X509Certificate cpListCert : certPathList) { - if (repeated(cpListCert, cert)) { - throw new CertPathValidatorException( - "cert with repeated subject, public key, and " + - "subjectAltNames detected"); - } - } - /* check if trusted cert */ boolean isTrustedCert = trustedCerts.contains(cert); @@ -787,49 +765,6 @@ void verifyCert(X509Certificate cert, State currentState, } } - /** - * Return true if two certificates are equal or have the same subject, - * public key, and subject alternative names. - */ - private static boolean repeated( - X509Certificate currCert, X509Certificate nextCert) { - if (currCert.equals(nextCert)) { - return true; - } - return (currCert.getSubjectX500Principal().equals( - nextCert.getSubjectX500Principal()) && - currCert.getPublicKey().equals(nextCert.getPublicKey()) && - altNamesEqual(currCert, nextCert)); - } - - /** - * Return true if two certificates have the same subject alternative names. - */ - private static boolean altNamesEqual( - X509Certificate currCert, X509Certificate nextCert) { - X509CertImpl curr, next; - try { - curr = X509CertImpl.toImpl(currCert); - next = X509CertImpl.toImpl(nextCert); - } catch (CertificateException ce) { - return false; - } - - SubjectAlternativeNameExtension currAltNameExt = - curr.getSubjectAlternativeNameExtension(); - SubjectAlternativeNameExtension nextAltNameExt = - next.getSubjectAlternativeNameExtension(); - if (currAltNameExt != null) { - if (nextAltNameExt == null) { - return false; - } - return Arrays.equals(currAltNameExt.getExtensionValue(), - nextAltNameExt.getExtensionValue()); - } else { - return (nextAltNameExt == null); - } - } - /** * Verifies whether the input certificate completes the path. * First checks the cert against each trust anchor that was specified, diff --git a/src/java.base/share/classes/sun/security/provider/certpath/ForwardState.java b/src/java.base/share/classes/sun/security/provider/certpath/ForwardState.java index c93f0bcb3a0..537b6b8266f 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/ForwardState.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/ForwardState.java @@ -31,17 +31,11 @@ import java.security.cert.PKIXCertPathChecker; import java.security.cert.X509Certificate; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import java.util.ListIterator; import javax.security.auth.x500.X500Principal; import sun.security.util.Debug; -import sun.security.x509.SubjectAlternativeNameExtension; -import sun.security.x509.GeneralNames; -import sun.security.x509.GeneralName; -import sun.security.x509.GeneralNameInterface; -import sun.security.x509.X500Name; import sun.security.x509.X509CertImpl; /** @@ -61,9 +55,6 @@ class ForwardState implements State { /* The last cert in the path */ X509CertImpl cert; - /* The set of subjectDNs and subjectAltNames of all certs in the path */ - HashSet subjectNamesTraversed; - /* * The number of intermediate CA certs which have been traversed so * far in the path @@ -73,7 +64,6 @@ class ForwardState implements State { /* Flag indicating if state is initial (path is just starting) */ private boolean init = true; - /* the untrusted certificates checker */ UntrustedChecker untrustedChecker; @@ -103,8 +93,6 @@ public String toString() { "\n issuerDN of last cert: " + issuerDN + "\n traversedCACerts: " + traversedCACerts + "\n init: " + init + - "\n subjectNamesTraversed: \n" + - subjectNamesTraversed + "\n selfIssued: " + selfIssued + "\n" + "]\n"; } @@ -117,7 +105,6 @@ public String toString() { public void initState(List certPathCheckers) throws CertPathValidatorException { - subjectNamesTraversed = new HashSet<>(); traversedCACerts = 0; /* @@ -167,22 +154,6 @@ public void updateState(X509Certificate cert) } } - /* update subjectNamesTraversed only if this is the EE cert or if - this cert is not self-issued */ - if (init || !selfIssued) { - X500Principal subjName = cert.getSubjectX500Principal(); - subjectNamesTraversed.add(X500Name.asX500Name(subjName)); - - SubjectAlternativeNameExtension subjAltNameExt - = icert.getSubjectAlternativeNameExtension(); - if (subjAltNameExt != null) { - GeneralNames gNames = subjAltNameExt.getNames(); - for (GeneralName gName : gNames.names()) { - subjectNamesTraversed.add(gName.getName()); - } - } - } - init = false; } @@ -190,10 +161,6 @@ public void updateState(X509Certificate cert) * Clone current state. The state is cloned as each cert is * added to the path. This is necessary if backtracking occurs, * and a prior state needs to be restored. - * - * Note that this is a SMART clone. Not all fields are fully copied, - * because some of them will - * not have their contents modified by subsequent calls to updateState. */ @Override @SuppressWarnings("unchecked") // Safe casts assuming clone() works correctly @@ -213,13 +180,6 @@ public Object clone() { } } - /* - * Shallow copy traversed names. There is no need to - * deep copy contents, since the elements of the Set - * are never modified by subsequent calls to updateState(). - */ - clonedState.subjectNamesTraversed - = (HashSet)subjectNamesTraversed.clone(); return clonedState; } catch (CloneNotSupportedException e) { throw new InternalError(e.toString(), e); diff --git a/src/java.base/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java b/src/java.base/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java index 8c9081b3d9f..c4e31cf7947 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java @@ -33,6 +33,7 @@ import java.security.cert.CertPathValidatorException.BasicReason; import java.security.cert.PKIXReason; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -42,6 +43,7 @@ import sun.security.provider.certpath.PKIX.BuilderParams; import static sun.security.x509.PKIXExtensions.*; +import sun.security.x509.SubjectAlternativeNameExtension; import sun.security.x509.X509CertImpl; import sun.security.util.Debug; @@ -265,7 +267,7 @@ private void depthFirstSearchForward(X500Principal dN, */ Collection certs = builder.getMatchingCerts(currentState, buildParams.certStores()); - List vertices = addVertices(certs, adjList); + List vertices = addVertices(certs, adjList, cpList); if (debug != null) { debug.println("SunCertPathBuilder.depthFirstSearchForward(): " + "certs.size=" + vertices.size()); @@ -325,17 +327,32 @@ private void depthFirstSearchForward(X500Principal dN, * cert (which is signed by the trusted public key), but * don't add it yet to the cpList */ + PublicKey rootKey = cert.getPublicKey(); if (builder.trustAnchor.getTrustedCert() == null) { appendedCerts.add(0, cert); + rootKey = builder.trustAnchor.getCAPublicKey(); + if (debug != null) + debug.println( + "SunCertPathBuilder.depthFirstSearchForward " + + "using buildParams public key: " + + rootKey.toString()); } + TrustAnchor anchor = new TrustAnchor + (cert.getSubjectX500Principal(), rootKey, null); + // add the basic checker + List checkers = new ArrayList<>(); + BasicChecker basicChecker = new BasicChecker(anchor, + buildParams.date(), + buildParams.sigProvider(), + true); + checkers.add(basicChecker); Set initExpPolSet = Collections.singleton(PolicyChecker.ANY_POLICY); PolicyNodeImpl rootNode = new PolicyNodeImpl(null, PolicyChecker.ANY_POLICY, null, false, initExpPolSet, false); - List checkers = new ArrayList<>(); PolicyChecker policyChecker = new PolicyChecker(buildParams.initialPolicies(), appendedCerts.size(), @@ -346,28 +363,13 @@ private void depthFirstSearchForward(X500Principal dN, rootNode); checkers.add(policyChecker); + // add the constraints checker + checkers.add(new ConstraintsChecker(appendedCerts.size())); + // add the algorithm checker checkers.add(new AlgorithmChecker(builder.trustAnchor, buildParams.timestamp(), buildParams.variant())); - PublicKey rootKey = cert.getPublicKey(); - if (builder.trustAnchor.getTrustedCert() == null) { - rootKey = builder.trustAnchor.getCAPublicKey(); - if (debug != null) - debug.println( - "SunCertPathBuilder.depthFirstSearchForward " + - "using buildParams public key: " + - rootKey.toString()); - } - TrustAnchor anchor = new TrustAnchor - (cert.getSubjectX500Principal(), rootKey, null); - - // add the basic checker - BasicChecker basicChecker = new BasicChecker(anchor, - buildParams.date(), - buildParams.sigProvider(), - true); - checkers.add(basicChecker); buildParams.setCertPath(cf.generateCertPath(appendedCerts)); @@ -563,18 +565,79 @@ private void depthFirstSearchForward(X500Principal dN, * adjacency list. */ private static List addVertices(Collection certs, - List> adjList) + List> adjList, + List cpList) { List l = adjList.get(adjList.size() - 1); for (X509Certificate cert : certs) { - Vertex v = new Vertex(cert); - l.add(v); + boolean repeated = false; + for (X509Certificate cpListCert : cpList) { + /* + * Ignore if we encounter the same certificate or a + * certificate with the same public key, subject DN, and + * subjectAltNames as a cert that is already in path. + */ + if (repeated(cpListCert, cert)) { + if (debug != null) { + debug.println("cert with repeated subject, " + + "public key, and subjectAltNames detected"); + } + repeated = true; + break; + } + } + if (!repeated) { + l.add(new Vertex(cert)); + } } return l; } + /** + * Return true if two certificates are equal or have the same subject, + * public key, and subject alternative names. + */ + private static boolean repeated( + X509Certificate currCert, X509Certificate nextCert) { + if (currCert.equals(nextCert)) { + return true; + } + return (currCert.getSubjectX500Principal().equals( + nextCert.getSubjectX500Principal()) && + currCert.getPublicKey().equals(nextCert.getPublicKey()) && + altNamesEqual(currCert, nextCert)); + } + + /** + * Return true if two certificates have the same subject alternative names. + */ + private static boolean altNamesEqual( + X509Certificate currCert, X509Certificate nextCert) { + X509CertImpl curr, next; + try { + curr = X509CertImpl.toImpl(currCert); + next = X509CertImpl.toImpl(nextCert); + } catch (CertificateException ce) { + return false; + } + + SubjectAlternativeNameExtension currAltNameExt = + curr.getSubjectAlternativeNameExtension(); + SubjectAlternativeNameExtension nextAltNameExt = + next.getSubjectAlternativeNameExtension(); + if (currAltNameExt != null) { + if (nextAltNameExt == null) { + return false; + } + return Arrays.equals(currAltNameExt.getExtensionValue(), + nextAltNameExt.getExtensionValue()); + } else { + return (nextAltNameExt == null); + } + } + /** * Returns true if trust anchor certificate matches specified * certificate constraints. diff --git a/src/java.base/share/classes/sun/security/provider/certpath/X509CertPath.java b/src/java.base/share/classes/sun/security/provider/certpath/X509CertPath.java index 41da617e4e4..1a9ba8914e2 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/X509CertPath.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/X509CertPath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -25,9 +25,7 @@ package sun.security.provider.certpath; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; import java.security.cert.CertificateEncodingException; import java.security.cert.Certificate; import java.security.cert.CertificateException; @@ -379,4 +377,20 @@ public Iterator getEncodings() { public List getCertificates() { return certs; } + + /** + * Restores the state of this object from the stream. + *

    + * Deserialization of this object is not supported. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException( + "X509CertPaths are not directly deserializable"); + } } diff --git a/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java b/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java index 86e063f95cd..99365640ec7 100644 --- a/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java +++ b/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,8 @@ package sun.security.rsa; import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.math.BigInteger; import java.security.*; @@ -43,7 +45,7 @@ * RSA private key implementation for "RSA", "RSASSA-PSS" algorithms in CRT form. * For non-CRT private keys, see RSAPrivateKeyImpl. We need separate classes * to ensure correct behavior in instanceof checks, etc. - * + *

    * Note: RSA keys must be at least 512 bits long * * @see RSAPrivateKeyImpl @@ -356,4 +358,20 @@ private void parseKeyBits() throws InvalidKeyException { throw new InvalidKeyException("Invalid RSA private key", e); } } + + /** + * Restores the state of this object from the stream. + *

    + * Deserialization of this object is not supported. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException( + "RSAPrivateCrtKeyImpl keys are not directly deserializable"); + } } diff --git a/src/java.base/share/classes/sun/security/rsa/RSAPrivateKeyImpl.java b/src/java.base/share/classes/sun/security/rsa/RSAPrivateKeyImpl.java index 7285fce33a2..da5474cb26a 100644 --- a/src/java.base/share/classes/sun/security/rsa/RSAPrivateKeyImpl.java +++ b/src/java.base/share/classes/sun/security/rsa/RSAPrivateKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,9 @@ package sun.security.rsa; +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.math.BigInteger; import java.security.*; @@ -39,10 +42,11 @@ /** * RSA private key implementation for "RSA", "RSASSA-PSS" algorithms in non-CRT - * form (modulus, private exponent only). For CRT private keys, see - * RSAPrivateCrtKeyImpl. We need separate classes to ensure correct behavior - * in instanceof checks, etc. - * + * form (modulus, private exponent only). + *

    + * For CRT private keys, see RSAPrivateCrtKeyImpl. We need separate classes + * to ensure correct behavior in instanceof checks, etc. + *

    * Note: RSA keys must be at least 512 bits long * * @see RSAPrivateCrtKeyImpl @@ -141,4 +145,20 @@ public String toString() { + " bits" + "\n params: " + keyParams + "\n modulus: " + n + "\n private exponent: " + d; } + + /** + * Restores the state of this object from the stream. + *

    + * Deserialization of this object is not supported. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException( + "RSAPrivateKeyImpl keys are not directly deserializable"); + } } diff --git a/src/java.base/share/classes/sun/security/rsa/RSAPublicKeyImpl.java b/src/java.base/share/classes/sun/security/rsa/RSAPublicKeyImpl.java index 0cff2f6768c..5a0745604d2 100644 --- a/src/java.base/share/classes/sun/security/rsa/RSAPublicKeyImpl.java +++ b/src/java.base/share/classes/sun/security/rsa/RSAPublicKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,8 @@ package sun.security.rsa; import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.math.BigInteger; import java.security.*; @@ -39,7 +41,7 @@ /** * RSA public key implementation for "RSA", "RSASSA-PSS" algorithms. - * + *

    * Note: RSA keys must be at least 512 bits long * * @see RSAPrivateCrtKeyImpl @@ -233,10 +235,26 @@ public String toString() { } @java.io.Serial - protected Object writeReplace() throws java.io.ObjectStreamException { + private Object writeReplace() throws java.io.ObjectStreamException { return new KeyRep(KeyRep.Type.PUBLIC, getAlgorithm(), getFormat(), getEncoded()); } + + /** + * Restores the state of this object from the stream. + *

    + * Deserialization of this object is not supported. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException( + "RSAPublicKeyImpl keys are not directly deserializable"); + } } diff --git a/src/java.base/share/classes/sun/security/x509/X509CertImpl.java b/src/java.base/share/classes/sun/security/x509/X509CertImpl.java index e2e3c2541e7..656e4c168fb 100644 --- a/src/java.base/share/classes/sun/security/x509/X509CertImpl.java +++ b/src/java.base/share/classes/sun/security/x509/X509CertImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -536,7 +536,7 @@ public void checkValidity(Date date) /** * Return the requested attribute from the certificate. - * + *

    * Note that the X509CertInfo is not cloned for performance reasons. * Callers must ensure that they do not modify it. All other * attributes are cloned. @@ -1218,7 +1218,7 @@ private static Collection> makeAltNames(GeneralNames names) { for (GeneralName gname : names.names()) { GeneralNameInterface name = gname.getName(); List nameEntry = new ArrayList<>(2); - nameEntry.add(Integer.valueOf(name.getType())); + nameEntry.add(name.getType()); switch (name.getType()) { case GeneralNameInterface.NAME_RFC822: nameEntry.add(((RFC822Name) name).getName()); @@ -1631,4 +1631,20 @@ public static String getFingerprint(String algorithm, } } } + + /** + * Restores the state of this object from the stream. + *

    + * Deserialization of this object is not supported. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException( + "X509CertImpls are not directly deserializable"); + } } diff --git a/src/java.base/share/man/java.1 b/src/java.base/share/man/java.1 index 7e00d3d57e7..a72da6d7c11 100644 --- a/src/java.base/share/man/java.1 +++ b/src/java.base/share/man/java.1 @@ -521,6 +521,20 @@ Allows classes to depend on \f[B]preview features\f[R] [https://docs.oracle.com/en/java/javase/12/language/index.html#JSLAN-GUID-5A82FE0E-0CA4-4F1F-B075-564874FE2823] of the release. .TP +\f[V]--enable-native-access\f[R] \f[I]module\f[R][\f[V],\f[R]\f[I]module\f[R]...] +Native access involves access to code or data outside the Java runtime. +This is generally unsafe and, if done incorrectly, might crash the JVM +or result in memory corruption. +Methods that provide native access are restricted, and by default their +use causes warnings. +This option allows code in the specified modules to use restricted +methods without warnings. +\f[I]module\f[R] can be \f[V]ALL-UNNAMED\f[R] to indicate code on the +class path. +When this option is present, any use of restricted methods by code +outside the specified modules causes an +\f[V]IllegalCallerException\f[R]. +.TP \f[V]--finalization=\f[R]\f[I]value\f[R] Controls whether the JVM performs finalization of objects. Valid values are \[dq]enabled\[dq] and \[dq]disabled\[dq]. @@ -530,31 +544,32 @@ The value \[dq]disabled\[dq] disables finalization, so that no finalizers are invoked. .TP \f[V]--module-path\f[R] \f[I]modulepath\f[R]... or \f[V]-p\f[R] \f[I]modulepath\f[R] -Specifies where to find application modules with a list of path elements. -The elements of a module path can be a file path to a module or a directory -containing modules. Each module is either a modular JAR or an -exploded-module directory. +Specifies where to find application modules with a list of path +elements. +The elements of a module path can be a file path to a module or a +directory containing modules. +Each module is either a modular JAR or an exploded-module directory. .RS .PP -On Windows, semicolons (\f[V];\f[R]) separate path elements in this list; -on other platforms it is a colon (\f[V]:\f[R]). +On Windows, semicolons (\f[V];\f[R]) separate path elements in this +list; on other platforms it is a colon (\f[V]:\f[R]). .RE .TP \f[V]--upgrade-module-path\f[R] \f[I]modulepath\f[R]... -Specifies where to find module replacements of upgradeable modules in the -runtime image with a list of path elements. -The elements of a module path can be a file path to a module or a directory -containing modules. Each module is either a modular JAR or an -exploded-module directory. +Specifies where to find module replacements of upgradeable modules in +the runtime image with a list of path elements. +The elements of a module path can be a file path to a module or a +directory containing modules. +Each module is either a modular JAR or an exploded-module directory. .RS .PP -On Windows, semicolons (\f[V];\f[R]) separate path elements in this list; -on other platforms it is a colon (\f[V]:\f[R]). +On Windows, semicolons (\f[V];\f[R]) separate path elements in this +list; on other platforms it is a colon (\f[V]:\f[R]). .RE .TP \f[V]--add-modules\f[R] \f[I]module\f[R][\f[V],\f[R]\f[I]module\f[R]...] Specifies the root modules to resolve in addition to the initial module. -\f[I]module\f[R] also can be \f[V]ALL-DEFAULT\f[R], +\f[I]module\f[R] can also be \f[V]ALL-DEFAULT\f[R], \f[V]ALL-SYSTEM\f[R], and \f[V]ALL-MODULE-PATH\f[R]. .TP \f[V]--list-modules\f[R] @@ -1116,13 +1131,14 @@ This option is similar to \f[V]-XX:ThreadStackSize\f[R]. \f[V]--add-reads\f[R] \f[I]module\f[R]\f[V]=\f[R]\f[I]target-module\f[R](\f[V],\f[R]\f[I]target-module\f[R])* Updates \f[I]module\f[R] to read the \f[I]target-module\f[R], regardless of the module declaration. -\f[I]target-module\f[R] can be all unnamed to read all unnamed modules. +\f[I]target-module\f[R] can be \f[V]ALL-UNNAMED\f[R] to read all unnamed +modules. .TP \f[V]--add-exports\f[R] \f[I]module\f[R]\f[V]/\f[R]\f[I]package\f[R]\f[V]=\f[R]\f[I]target-module\f[R](\f[V],\f[R]\f[I]target-module\f[R])* Updates \f[I]module\f[R] to export \f[I]package\f[R] to \f[I]target-module\f[R], regardless of module declaration. -The \f[I]target-module\f[R] can be all unnamed to export to all unnamed -modules. +\f[I]target-module\f[R] can be \f[V]ALL-UNNAMED\f[R] to export to all +unnamed modules. .TP \f[V]--add-opens\f[R] \f[I]module\f[R]\f[V]/\f[R]\f[I]package\f[R]\f[V]=\f[R]\f[I]target-module\f[R](\f[V],\f[R]\f[I]target-module\f[R])* Updates \f[I]module\f[R] to open \f[I]package\f[R] to @@ -1304,6 +1320,7 @@ By default this option is disabled. .TP \f[V]-XX:FlightRecorderOptions=\f[R]\f[I]parameter\f[R]\f[V]=\f[R]\f[I]value\f[R] (or) \f[V]-XX:FlightRecorderOptions:\f[R]\f[I]parameter\f[R]\f[V]=\f[R]\f[I]value\f[R] Sets the parameters that control the behavior of JFR. +Multiple parameters can be specified by separating them with a comma. .RS .PP The following list contains the available JFR @@ -1369,9 +1386,6 @@ By default, the local buffer size is set to 8 kilobytes, with a minimum value of 4 kilobytes. Overriding this parameter could reduce performance and is not recommended. -.PP -You can specify values for multiple parameters by separating them with a -comma. .RE .TP \f[V]-XX:LargePageSizeInBytes=\f[R]\f[I]size\f[R] @@ -1658,6 +1672,9 @@ written when the recording is stopped, for example: \f[V]/home/user/recordings/recording.jfr\f[R] .IP \[bu] 2 \f[V]c:\[rs]recordings\[rs]recording.jfr\f[R] +.PP +If %p and/or %t is specified in the filename, it expands to the +JVM\[aq]s PID and the current timestamp, respectively. .RE .TP \f[V]name=\f[R]\f[I]identifier\f[R] @@ -1812,19 +1829,6 @@ See \f[B]Enable Logging with the JVM Unified Logging Framework\f[R] for a description of using Unified Logging. .RE .TP -\f[V]-XX:+UseHugeTLBFS\f[R] -\f[B]Linux only:\f[R] This option is the equivalent of specifying -\f[V]-XX:+UseLargePages\f[R]. -This option is disabled by default. -This option pre-allocates all large pages up-front, when memory is -reserved; consequently the JVM can\[aq]t dynamically grow or shrink -large pages memory areas; see \f[V]-XX:UseTransparentHugePages\f[R] if -you want this behavior. -.RS -.PP -See \f[B]Large Pages\f[R]. -.RE -.TP \f[V]-XX:+UseLargePages\f[R] Enables the use of large page memory. By default, this option is disabled and large page memory isn\[aq]t @@ -2172,37 +2176,16 @@ unlocks diagnostic JVM options. Sets the list of methods (separated by commas) to which compilation should be restricted. Only the specified methods are compiled. -Specify each method with the full class name (including the packages and -subpackages). -For example, to compile only the \f[V]length()\f[R] method of the -\f[V]String\f[R] class and the \f[V]size()\f[R] method of the -\f[V]List\f[R] class, use the following: -.RS -.RS -.PP -\f[V]-XX:CompileOnly=java/lang/String.length,java/util/List.size\f[R] -.RE -.PP -Note that the full class name is specified, including all packages and -subpackages separated by a slash (\f[V]/\f[R]). -For easier cut and paste operations, it\[aq]s also possible to use the -method name format produced by the \f[V]-XX:+PrintCompilation\f[R] and -\f[V]-XX:+LogCompilation\f[R] options: .RS .PP -\f[V]-XX:CompileOnly=java.lang.String::length,java.util.List::size\f[R] -.RE -.PP -Although wildcards aren\[aq]t supported, you can specify only the class -or package name to compile all methods in that class or package, as well -as specify just the method to compile methods with this name in any -class: +\f[V]-XX:CompileOnly=method1,method2,...,methodN\f[R] is an alias for: .IP .nf \f[CB] --XX:CompileOnly=java/lang/String --XX:CompileOnly=java/lang --XX:CompileOnly=.length +-XX:CompileCommand=compileonly,method1 +-XX:CompileCommand=compileonly,method2 +\&... +-XX:CompileCommand=compileonly,methodN \f[R] .fi .RE @@ -3655,14 +3638,6 @@ This is generally the best choice for small and simple applications that don\[aq]t require any special functionality from garbage collection. By default, this option is disabled and the default collector is used. .TP -\f[V]-XX:+UseSHM\f[R] -\f[B]Linux only:\f[R] Enables the JVM to use shared memory to set up -large pages. -.RS -.PP -See \f[B]Large Pages\f[R] for setting up large pages. -.RE -.TP \f[V]-XX:+UseStringDeduplication\f[R] Enables string deduplication. By default, this option is disabled. @@ -3815,6 +3790,19 @@ Controlled \f[I]relaxed strong encapsulation\f[R], as defined in This option was deprecated in JDK 16 by \f[B]JEP 396\f[R] [https://openjdk.org/jeps/396] and made obsolete in JDK 17 by \f[B]JEP 403\f[R] [https://openjdk.org/jeps/403]. +.TP +\f[V]-XX:+UseHugeTLBFS\f[R] +\f[B]Linux only:\f[R] This option is the equivalent of specifying +\f[V]-XX:+UseLargePages\f[R]. +This option is disabled by default. +This option pre-allocates all large pages up-front, when memory is +reserved; consequently the JVM can\[aq]t dynamically grow or shrink +large pages memory areas; see \f[V]-XX:UseTransparentHugePages\f[R] if +you want this behavior. +.TP +\f[V]-XX:+UseSHM\f[R] +\f[B]Linux only:\f[R] Enables the JVM to use shared memory to set up +large pages. .SH REMOVED JAVA OPTIONS .PP No documented java options have been removed in JDK 22. @@ -4915,20 +4903,6 @@ login as \f[V]root\f[R] and run: It is always recommended to check the value of \f[V]nr_hugepages\f[R] after the request to make sure the kernel was able to allocate the requested number of large pages. -.PP -When using the option \f[V]-XX:+UseSHM\f[R] to enable large pages you -also need to make sure the \f[V]SHMMAX\f[R] parameter is configured to -allow large enough shared memory segments to be allocated. -To allow a maximum shared segment of 8 GB, login as \f[V]root\f[R] and -run: -.RS -.PP -\f[V]# echo 8589934592 > /proc/sys/kernel/shmmax\f[R] -.RE -.PP -In some environments this is not needed since the default value is large -enough, but it is important to make sure the value is large enough to -fit the amount of memory intended to be backed by large pages. .RS .PP \f[B]Note:\f[R] The values contained in \f[V]/proc\f[R] and diff --git a/src/java.base/share/native/libfallbackLinker/fallbackLinker.c b/src/java.base/share/native/libfallbackLinker/fallbackLinker.c index 9b3d88dcc0c..e99483adbd5 100644 --- a/src/java.base/share/native/libfallbackLinker/fallbackLinker.c +++ b/src/java.base/share/native/libfallbackLinker/fallbackLinker.c @@ -29,6 +29,7 @@ #include #include +#include #ifdef _WIN64 #include #include @@ -206,3 +207,23 @@ JNIEXPORT jlong JNICALL Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1pointer(JNIEnv* env, jclass cls) { return ptr_to_jlong(&ffi_type_pointer); } + +JNIEXPORT jint JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1sizeof_1short(JNIEnv* env, jclass cls) { + return sizeof(short); +} + +JNIEXPORT jint JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1sizeof_1int(JNIEnv* env, jclass cls) { + return sizeof(int); +} + +JNIEXPORT jint JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1sizeof_1long(JNIEnv* env, jclass cls) { + return sizeof(long); +} + +JNIEXPORT jint JNICALL +Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1sizeof_1wchar(JNIEnv* env, jclass cls) { + return sizeof(wchar_t); +} diff --git a/src/java.base/share/native/libnet/net_util.h b/src/java.base/share/native/libnet/net_util.h index 771ce3c2bbf..06cf448a01d 100644 --- a/src/java.base/share/native/libnet/net_util.h +++ b/src/java.base/share/native/libnet/net_util.h @@ -170,12 +170,6 @@ NET_SetSockOpt(int fd, int level, int opt, const void *arg, int len); JNIEXPORT int JNICALL NET_Bind(int fd, SOCKETADDRESS *sa, int len); -JNIEXPORT int JNICALL -NET_MapSocketOption(jint cmd, int *level, int *optname); - -JNIEXPORT int JNICALL -NET_MapSocketOptionV6(jint cmd, int *level, int *optname); - JNIEXPORT jint JNICALL NET_EnableFastTcpLoopback(int fd); diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixFileAttributes.java b/src/java.base/unix/classes/sun/nio/fs/UnixFileAttributes.java index 3257b5bf32e..3b09bd259ce 100644 --- a/src/java.base/unix/classes/sun/nio/fs/UnixFileAttributes.java +++ b/src/java.base/unix/classes/sun/nio/fs/UnixFileAttributes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -25,10 +25,15 @@ package sun.nio.fs; -import java.nio.file.attribute.*; -import java.util.concurrent.TimeUnit; -import java.util.Set; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; +import java.nio.file.attribute.GroupPrincipal; +import java.nio.file.attribute.PosixFileAttributes; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.UserPrincipal; import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.TimeUnit; /** * Unix implementation of PosixFileAttributes. @@ -52,6 +57,7 @@ class UnixFileAttributes private long st_ctime_sec; private long st_ctime_nsec; private long st_birthtime_sec; + private long st_birthtime_nsec; // created lazily private volatile UserPrincipal owner; @@ -158,7 +164,7 @@ public FileTime lastAccessTime() { @Override public FileTime creationTime() { if (UnixNativeDispatcher.birthtimeSupported()) { - return FileTime.from(st_birthtime_sec, TimeUnit.SECONDS); + return toFileTime(st_birthtime_sec, st_birthtime_nsec); } else { // return last modified when birth time not supported return lastModifiedTime(); diff --git a/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c b/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c index 56a72fab1db..a54d5782547 100644 --- a/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c +++ b/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c @@ -485,18 +485,28 @@ void unix_getUserInfo(JNIEnv* env, jobject jinfo, uid_t uid) { } /* - * The following functions are common on Solaris, Linux and AIX. + * The following functions are for Linux */ -#if defined (__linux__) || defined(_AIX) +#if defined (__linux__) /* - * Returns the children of the requested pid and optionally each parent and - * start time. - * Reads /proc and accumulates any process who parent pid matches. - * The resulting pids are stored into the array of longs. + * Return pids of active processes, and optionally parent pids and + * start times for each process. + * For a specific non-zero pid, only the direct children are returned. + * If the pid is zero, all active processes are returned. + * Reads /proc and accumulates any process following the rules above. + * The resulting pids are stored into an array of longs named jarray. * The number of pids is returned if they all fit. - * If the array is too short, the negative of the desired length is returned. + * If the parentArray is non-null, store also the parent pid. + * In this case the parentArray must have the same length as the result pid array. + * Of course in the case of a given non-zero pid all entries in the parentArray + * will contain this pid, so this array does only make sense in the case of a given + * zero pid. + * If the jstimesArray is non-null, store also the start time of the pid. + * In this case the jstimesArray must have the same length as the result pid array. + * If the array(s) (is|are) too short, excess pids are not stored and + * the desired length is returned. */ jint unix_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray, jlongArray jparentArray, jlongArray jstimesArray) { @@ -607,7 +617,7 @@ jint unix_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray, return count; } -#endif // defined (__linux__) || defined(_AIX) +#endif // defined (__linux__) /* * The following functions are for AIX. 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 230de04c079..4766a883472 100644 --- a/src/java.base/unix/native/libjava/java_props_md.c +++ b/src/java.base/unix/native/libjava/java_props_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -238,6 +238,11 @@ static int ParseLocale(JNIEnv* env, int cat, char ** std_language, char ** std_s *std_language = "en"; if (language != NULL && mapLookup(language_names, language, std_language) == 0) { *std_language = malloc(strlen(language)+1); + if (*std_language == NULL) { + free(encoding_variant); + JNU_ThrowOutOfMemoryError(env, NULL); + return 0; + } strcpy(*std_language, language); } } @@ -246,6 +251,11 @@ static int ParseLocale(JNIEnv* env, int cat, char ** std_language, char ** std_s if (std_country != NULL && country != NULL) { if (mapLookup(country_names, country, std_country) == 0) { *std_country = malloc(strlen(country)+1); + if (*std_country == NULL) { + free(encoding_variant); + JNU_ThrowOutOfMemoryError(env, NULL); + return 0; + } strcpy(*std_country, country); } } diff --git a/src/java.base/unix/native/libnet/DefaultProxySelector.c b/src/java.base/unix/native/libnet/DefaultProxySelector.c index dc5424e4b2d..fd54d817af9 100644 --- a/src/java.base/unix/native/libnet/DefaultProxySelector.c +++ b/src/java.base/unix/native/libnet/DefaultProxySelector.c @@ -91,13 +91,13 @@ g_type_init_func* my_g_type_init_func = NULL; typedef struct _GProxyResolver GProxyResolver; typedef struct _GSocketConnectable GSocketConnectable; typedef struct GError GError; -typedef GProxyResolver* g_proxy_resolver_get_default_func(); -typedef char** g_proxy_resolver_lookup_func(); -typedef GSocketConnectable* g_network_address_parse_uri_func(); -typedef const char* g_network_address_get_hostname_func(); -typedef unsigned short g_network_address_get_port_func(); -typedef void g_strfreev_func(); -typedef void g_clear_error_func(); +typedef GProxyResolver* g_proxy_resolver_get_default_func(void); +typedef char** g_proxy_resolver_lookup_func(GProxyResolver* resolver, char* uri, void *null, GError **error_p); +typedef GSocketConnectable* g_network_address_parse_uri_func(char* proxy, unsigned short default_port, GError **error_p); +typedef const char* g_network_address_get_hostname_func(GSocketConnectable* conn); +typedef unsigned short g_network_address_get_port_func(GSocketConnectable* conn); +typedef void g_strfreev_func(char** proxies); +typedef void g_clear_error_func(GError **error_p); static g_proxy_resolver_get_default_func* g_proxy_resolver_get_default = NULL; static g_proxy_resolver_lookup_func* g_proxy_resolver_lookup = NULL; diff --git a/src/java.base/unix/native/libnet/net_util_md.c b/src/java.base/unix/native/libnet/net_util_md.c index 67194075c75..8a6249f504f 100644 --- a/src/java.base/unix/native/libnet/net_util_md.c +++ b/src/java.base/unix/native/libnet/net_util_md.c @@ -314,72 +314,6 @@ int NET_IsZeroAddr(jbyte* caddr) { return 1; } -/* - * Map the Java level socket option to the platform specific - * level and option name. - */ -int -NET_MapSocketOption(jint cmd, int *level, int *optname) { - static struct { - jint cmd; - int level; - int optname; - } const opts[] = { - { java_net_SocketOptions_TCP_NODELAY, IPPROTO_TCP, TCP_NODELAY }, - { java_net_SocketOptions_SO_OOBINLINE, SOL_SOCKET, SO_OOBINLINE }, - { java_net_SocketOptions_SO_LINGER, SOL_SOCKET, SO_LINGER }, - { java_net_SocketOptions_SO_SNDBUF, SOL_SOCKET, SO_SNDBUF }, - { java_net_SocketOptions_SO_RCVBUF, SOL_SOCKET, SO_RCVBUF }, - { java_net_SocketOptions_SO_KEEPALIVE, SOL_SOCKET, SO_KEEPALIVE }, - { java_net_SocketOptions_SO_REUSEADDR, SOL_SOCKET, SO_REUSEADDR }, - { java_net_SocketOptions_SO_REUSEPORT, SOL_SOCKET, SO_REUSEPORT }, - { java_net_SocketOptions_SO_BROADCAST, SOL_SOCKET, SO_BROADCAST }, - { java_net_SocketOptions_IP_TOS, IPPROTO_IP, IP_TOS }, - { java_net_SocketOptions_IP_MULTICAST_IF, IPPROTO_IP, IP_MULTICAST_IF }, - { java_net_SocketOptions_IP_MULTICAST_IF2, IPPROTO_IP, IP_MULTICAST_IF }, - { java_net_SocketOptions_IP_MULTICAST_LOOP, IPPROTO_IP, IP_MULTICAST_LOOP }, - }; - - int i; - - if (ipv6_available()) { - switch (cmd) { - // Different multicast options if IPv6 is enabled - case java_net_SocketOptions_IP_MULTICAST_IF: - case java_net_SocketOptions_IP_MULTICAST_IF2: - *level = IPPROTO_IPV6; - *optname = IPV6_MULTICAST_IF; - return 0; - - case java_net_SocketOptions_IP_MULTICAST_LOOP: - *level = IPPROTO_IPV6; - *optname = IPV6_MULTICAST_LOOP; - return 0; -#if defined(MACOSX) - // Map IP_TOS request to IPV6_TCLASS - case java_net_SocketOptions_IP_TOS: - *level = IPPROTO_IPV6; - *optname = IPV6_TCLASS; - return 0; -#endif - } - } - - /* - * Map the Java level option to the native level - */ - for (i=0; i<(int)(sizeof(opts) / sizeof(opts[0])); i++) { - if (cmd == opts[i].cmd) { - *level = opts[i].level; - *optname = opts[i].optname; - return 0; - } - } - - /* not found */ - return -1; -} - /* * Wrapper for getsockopt system routine - does any necessary * pre/post processing to deal with OS specific oddities :- diff --git a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c index 90c0a52d7e8..6d933be4e6e 100644 --- a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c +++ b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c @@ -51,6 +51,7 @@ #ifdef __linux__ #include +#include // makedev macros #endif #if defined(__linux__) || defined(_AIX) @@ -71,6 +72,98 @@ #define readdir64 readdir #endif +#if defined(__linux__) +// Account for the case where we compile on a system without statx +// support. We still want to ensure we can call statx at runtime +// if the runtime glibc version supports it (>= 2.28). We do this +// by defining binary compatible statx structs in this file and +// not relying on included headers. + +#ifndef __GLIBC__ +// Alpine doesn't know these types, define them +typedef unsigned int __uint32_t; +typedef unsigned short __uint16_t; +typedef unsigned long int __uint64_t; +#endif + +/* + * Timestamp structure for the timestamps in struct statx. + */ +struct my_statx_timestamp { + int64_t tv_sec; + __uint32_t tv_nsec; + int32_t __reserved; +}; + +/* + * struct statx used by statx system call on >= glibc 2.28 + * systems + */ +struct my_statx +{ + __uint32_t stx_mask; + __uint32_t stx_blksize; + __uint64_t stx_attributes; + __uint32_t stx_nlink; + __uint32_t stx_uid; + __uint32_t stx_gid; + __uint16_t stx_mode; + __uint16_t __statx_pad1[1]; + __uint64_t stx_ino; + __uint64_t stx_size; + __uint64_t stx_blocks; + __uint64_t stx_attributes_mask; + struct my_statx_timestamp stx_atime; + struct my_statx_timestamp stx_btime; + struct my_statx_timestamp stx_ctime; + struct my_statx_timestamp stx_mtime; + __uint32_t stx_rdev_major; + __uint32_t stx_rdev_minor; + __uint32_t stx_dev_major; + __uint32_t stx_dev_minor; + __uint64_t __statx_pad2[14]; +}; + +// statx masks, flags, constants + +#ifndef AT_SYMLINK_NOFOLLOW +#define AT_SYMLINK_NOFOLLOW 0x100 +#endif + +#ifndef AT_STATX_SYNC_AS_STAT +#define AT_STATX_SYNC_AS_STAT 0x0000 +#endif + +#ifndef AT_EMPTY_PATH +#define AT_EMPTY_PATH 0x1000 +#endif + +#ifndef STATX_BASIC_STATS +#define STATX_BASIC_STATS 0x000007ffU +#endif + +#ifndef STATX_BTIME +#define STATX_BTIME 0x00000800U +#endif + +#ifndef STATX_ALL +#define STATX_ALL (STATX_BTIME | STATX_BASIC_STATS) +#endif + +#ifndef AT_FDCWD +#define AT_FDCWD -100 +#endif + +#ifndef RTLD_DEFAULT +#define RTLD_DEFAULT RTLD_LOCAL +#endif + +#define NO_FOLLOW_SYMLINK 1 +#define FOLLOW_SYMLINK 0 + +#endif // __linux__ + + #include "jni.h" #include "jni_util.h" #include "jlong.h" @@ -117,9 +210,12 @@ static jfieldID attrs_st_mtime_nsec; static jfieldID attrs_st_ctime_sec; static jfieldID attrs_st_ctime_nsec; -#ifdef _DARWIN_FEATURE_64_BIT_INODE +#if defined(_DARWIN_FEATURE_64_BIT_INODE) || defined(__linux__) static jfieldID attrs_st_birthtime_sec; #endif +#if defined(__linux__) // Linux has nsec granularity if supported +static jfieldID attrs_st_birthtime_nsec; +#endif static jfieldID attrs_f_frsize; static jfieldID attrs_f_blocks; @@ -143,6 +239,10 @@ typedef int futimesat_func(int, const char *, const struct timeval *); typedef int futimens_func(int, const struct timespec *); typedef int lutimes_func(const char *, const struct timeval *); typedef DIR* fdopendir_func(int); +#if defined(__linux__) +typedef int statx_func(int dirfd, const char *restrict pathname, int flags, + unsigned int mask, struct my_statx *restrict statxbuf); +#endif static openat64_func* my_openat64_func = NULL; static fstatat64_func* my_fstatat64_func = NULL; @@ -152,6 +252,9 @@ static futimesat_func* my_futimesat_func = NULL; static futimens_func* my_futimens_func = NULL; static lutimes_func* my_lutimes_func = NULL; static fdopendir_func* my_fdopendir_func = NULL; +#if defined(__linux__) +static statx_func* my_statx_func = NULL; +#endif /** * fstatat missing from glibc on Linux. @@ -177,6 +280,13 @@ static int fstatat64_wrapper(int dfd, const char *path, } #endif +#if defined(__linux__) +static int statx_wrapper(int dirfd, const char *restrict pathname, int flags, + unsigned int mask, struct my_statx *restrict statxbuf) { + return (*my_statx_func)(dirfd, pathname, flags, mask, statxbuf); +} +#endif + /** * Call this to throw an internal UnixException when a system/library * call fails @@ -229,10 +339,14 @@ Java_sun_nio_fs_UnixNativeDispatcher_init(JNIEnv* env, jclass this) attrs_st_ctime_nsec = (*env)->GetFieldID(env, clazz, "st_ctime_nsec", "J"); CHECK_NULL_RETURN(attrs_st_ctime_nsec, 0); -#ifdef _DARWIN_FEATURE_64_BIT_INODE +#if defined(_DARWIN_FEATURE_64_BIT_INODE) || defined(__linux__) attrs_st_birthtime_sec = (*env)->GetFieldID(env, clazz, "st_birthtime_sec", "J"); CHECK_NULL_RETURN(attrs_st_birthtime_sec, 0); #endif +#if defined (__linux__) // Linux has nsec granularity + attrs_st_birthtime_nsec = (*env)->GetFieldID(env, clazz, "st_birthtime_nsec", "J"); + CHECK_NULL_RETURN(attrs_st_birthtime_nsec, 0); +#endif clazz = (*env)->FindClass(env, "sun/nio/fs/UnixFileStoreAttributes"); CHECK_NULL_RETURN(clazz, 0); @@ -314,6 +428,12 @@ Java_sun_nio_fs_UnixNativeDispatcher_init(JNIEnv* env, jclass this) #ifdef _DARWIN_FEATURE_64_BIT_INODE capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_BIRTHTIME; #endif +#if defined(__linux__) + my_statx_func = (statx_func*) dlsym(RTLD_DEFAULT, "statx"); + if (my_statx_func != NULL) { + capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_BIRTHTIME; + } +#endif /* supports extended attributes */ @@ -490,10 +610,37 @@ Java_sun_nio_fs_UnixNativeDispatcher_write0(JNIEnv* env, jclass this, jint fd, return (jint)n; } +#if defined(__linux__) +/** + * Copy statx members into sun.nio.fs.UnixFileAttributes + */ +static void copy_statx_attributes(JNIEnv* env, struct my_statx* buf, jobject attrs) { + (*env)->SetIntField(env, attrs, attrs_st_mode, (jint)buf->stx_mode); + (*env)->SetLongField(env, attrs, attrs_st_ino, (jlong)buf->stx_ino); + (*env)->SetIntField(env, attrs, attrs_st_nlink, (jint)buf->stx_nlink); + (*env)->SetIntField(env, attrs, attrs_st_uid, (jint)buf->stx_uid); + (*env)->SetIntField(env, attrs, attrs_st_gid, (jint)buf->stx_gid); + (*env)->SetLongField(env, attrs, attrs_st_size, (jlong)buf->stx_size); + (*env)->SetLongField(env, attrs, attrs_st_atime_sec, (jlong)buf->stx_atime.tv_sec); + (*env)->SetLongField(env, attrs, attrs_st_mtime_sec, (jlong)buf->stx_mtime.tv_sec); + (*env)->SetLongField(env, attrs, attrs_st_ctime_sec, (jlong)buf->stx_ctime.tv_sec); + (*env)->SetLongField(env, attrs, attrs_st_birthtime_sec, (jlong)buf->stx_btime.tv_sec); + (*env)->SetLongField(env, attrs, attrs_st_birthtime_nsec, (jlong)buf->stx_btime.tv_nsec); + (*env)->SetLongField(env, attrs, attrs_st_atime_nsec, (jlong)buf->stx_atime.tv_nsec); + (*env)->SetLongField(env, attrs, attrs_st_mtime_nsec, (jlong)buf->stx_mtime.tv_nsec); + (*env)->SetLongField(env, attrs, attrs_st_ctime_nsec, (jlong)buf->stx_ctime.tv_nsec); + // convert statx major:minor to dev_t using makedev + dev_t dev = makedev(buf->stx_dev_major, buf->stx_dev_minor); + dev_t rdev = makedev(buf->stx_rdev_major, buf->stx_rdev_minor); + (*env)->SetLongField(env, attrs, attrs_st_dev, (jlong)dev); + (*env)->SetLongField(env, attrs, attrs_st_rdev, (jlong)rdev); +} +#endif + /** * Copy stat64 members into sun.nio.fs.UnixFileAttributes */ -static void prepAttributes(JNIEnv* env, struct stat64* buf, jobject attrs) { +static void copy_stat64_attributes(JNIEnv* env, struct stat64* buf, jobject attrs) { (*env)->SetIntField(env, attrs, attrs_st_mode, (jint)buf->st_mode); (*env)->SetLongField(env, attrs, attrs_st_ino, (jlong)buf->st_ino); (*env)->SetLongField(env, attrs, attrs_st_dev, (jlong)buf->st_dev); @@ -508,6 +655,7 @@ static void prepAttributes(JNIEnv* env, struct stat64* buf, jobject attrs) { #ifdef _DARWIN_FEATURE_64_BIT_INODE (*env)->SetLongField(env, attrs, attrs_st_birthtime_sec, (jlong)buf->st_birthtime); + // rely on default value of 0 for st_birthtime_nsec field on Darwin #endif #ifndef MACOSX @@ -528,10 +676,25 @@ Java_sun_nio_fs_UnixNativeDispatcher_stat0(JNIEnv* env, jclass this, int err; struct stat64 buf; const char* path = (const char*)jlong_to_ptr(pathAddress); - +#if defined(__linux__) + struct my_statx statx_buf; + int flags = AT_STATX_SYNC_AS_STAT; + unsigned int mask = STATX_ALL; + + if (my_statx_func != NULL) { + // Prefer statx over stat64 on Linux if it's available + RESTARTABLE(statx_wrapper(AT_FDCWD, path, flags, mask, &statx_buf), err); + if (err == 0) { + copy_statx_attributes(env, &statx_buf, attrs); + return 0; + } else { + return errno; + } + } +#endif RESTARTABLE(stat64(path, &buf), err); if (err == 0) { - prepAttributes(env, &buf, attrs); + copy_stat64_attributes(env, &buf, attrs); return 0; } else { return errno; @@ -545,12 +708,28 @@ Java_sun_nio_fs_UnixNativeDispatcher_lstat0(JNIEnv* env, jclass this, int err; struct stat64 buf; const char* path = (const char*)jlong_to_ptr(pathAddress); - +#if defined(__linux__) + struct my_statx statx_buf; + int flags = AT_STATX_SYNC_AS_STAT | AT_SYMLINK_NOFOLLOW; + unsigned int mask = STATX_ALL; + + if (my_statx_func != NULL) { + // Prefer statx over stat64 on Linux if it's available + RESTARTABLE(statx_wrapper(AT_FDCWD, path, flags, mask, &statx_buf), err); + if (err == 0) { + copy_statx_attributes(env, &statx_buf, attrs); + } else { + throwUnixException(env, errno); + } + // statx was available, so return now + return; + } +#endif RESTARTABLE(lstat64(path, &buf), err); if (err == -1) { throwUnixException(env, errno); } else { - prepAttributes(env, &buf, attrs); + copy_stat64_attributes(env, &buf, attrs); } } @@ -560,12 +739,29 @@ Java_sun_nio_fs_UnixNativeDispatcher_fstat0(JNIEnv* env, jclass this, jint fd, { int err; struct stat64 buf; - +#if defined(__linux__) + struct my_statx statx_buf; + int flags = AT_EMPTY_PATH | AT_STATX_SYNC_AS_STAT; + unsigned int mask = STATX_ALL; + + if (my_statx_func != NULL) { + // statx supports FD use via dirfd iff pathname is an empty string and the + // AT_EMPTY_PATH flag is specified in flags + RESTARTABLE(statx_wrapper((int)fd, "", flags, mask, &statx_buf), err); + if (err == 0) { + copy_statx_attributes(env, &statx_buf, attrs); + } else { + throwUnixException(env, errno); + } + // statx was available, so return now + return; + } +#endif RESTARTABLE(fstat64((int)fd, &buf), err); if (err == -1) { throwUnixException(env, errno); } else { - prepAttributes(env, &buf, attrs); + copy_stat64_attributes(env, &buf, attrs); } } @@ -576,6 +772,26 @@ Java_sun_nio_fs_UnixNativeDispatcher_fstatat0(JNIEnv* env, jclass this, jint dfd int err; struct stat64 buf; const char* path = (const char*)jlong_to_ptr(pathAddress); +#if defined(__linux__) + struct my_statx statx_buf; + int flags = AT_STATX_SYNC_AS_STAT; + unsigned int mask = STATX_ALL; + + if (my_statx_func != NULL) { + // Prefer statx over stat64 on Linux if it's available + if (((int)flag & AT_SYMLINK_NOFOLLOW) > 0) { // flag set in java code + flags |= AT_SYMLINK_NOFOLLOW; + } + RESTARTABLE(statx_wrapper((int)dfd, path, flags, mask, &statx_buf), err); + if (err == 0) { + copy_statx_attributes(env, &statx_buf, attrs); + } else { + throwUnixException(env, errno); + } + // statx was available, so return now + return; + } +#endif if (my_fstatat64_func == NULL) { JNU_ThrowInternalError(env, "should not reach here"); @@ -585,7 +801,7 @@ Java_sun_nio_fs_UnixNativeDispatcher_fstatat0(JNIEnv* env, jclass this, jint dfd if (err == -1) { throwUnixException(env, errno); } else { - prepAttributes(env, &buf, attrs); + copy_stat64_attributes(env, &buf, attrs); } } diff --git a/src/java.base/windows/native/libjli/cmdtoargs.c b/src/java.base/windows/native/libjli/cmdtoargs.c index 9bbbdaf9865..548e70b9bbf 100644 --- a/src/java.base/windows/native/libjli/cmdtoargs.c +++ b/src/java.base/windows/native/libjli/cmdtoargs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -324,6 +324,10 @@ class Vector { bool check() { // "pgmname" rest of cmdline ie. pgmname + 2 double quotes + space + cmdline from windows char* cptr = (char*) malloc(strlen(argv[0]) + sizeof(char) * 3 + strlen(cmdline) + 1); + if (cptr == NULL) { + printf("*** cannot allocate memory\n"); + doabort(); + } _snprintf(cptr, MAX_PATH, "\"%s\" %s", argv[0], cmdline); JLI_CmdToArgs(cptr); free(cptr); diff --git a/src/java.base/windows/native/libnet/DefaultProxySelector.c b/src/java.base/windows/native/libnet/DefaultProxySelector.c index 6b91874eda1..06bc70114f3 100644 --- a/src/java.base/windows/native/libnet/DefaultProxySelector.c +++ b/src/java.base/windows/native/libnet/DefaultProxySelector.c @@ -239,7 +239,7 @@ Java_sun_net_spi_DefaultProxySelector_getSystemProxies(JNIEnv *env, if (use_auto_proxy) { WCHAR url[MAX_STR_LEN]; /* Create url for WinHttpGetProxyForUrl */ - _snwprintf(url, sizeof(url) - 1, L"%s://%s", lpProto, lpHost); + swprintf(url, MAX_STR_LEN, L"%s://%s", lpProto, lpHost); /* Get proxy for URL from Windows */ use_auto_proxy = WinHttpGetProxyForUrl(session, &url[0], &auto_proxy_options, &proxy_info); if (use_auto_proxy) { diff --git a/src/java.base/windows/native/libnet/net_util_md.c b/src/java.base/windows/native/libnet/net_util_md.c index 4ccc4c6631a..5305a02f1c2 100644 --- a/src/java.base/windows/native/libnet/net_util_md.c +++ b/src/java.base/windows/native/libnet/net_util_md.c @@ -225,75 +225,6 @@ jint reuseport_supported(int ipv6_available) return JNI_FALSE; } -/* call NET_MapSocketOptionV6 for the IPv6 fd only - * and NET_MapSocketOption for the IPv4 fd - */ -JNIEXPORT int JNICALL -NET_MapSocketOptionV6(jint cmd, int *level, int *optname) { - - switch (cmd) { - case java_net_SocketOptions_IP_MULTICAST_IF: - case java_net_SocketOptions_IP_MULTICAST_IF2: - *level = IPPROTO_IPV6; - *optname = IPV6_MULTICAST_IF; - return 0; - - case java_net_SocketOptions_IP_MULTICAST_LOOP: - *level = IPPROTO_IPV6; - *optname = IPV6_MULTICAST_LOOP; - return 0; - } - return NET_MapSocketOption (cmd, level, optname); -} - -/* - * Map the Java level socket option to the platform specific - * level and option name. - */ - -JNIEXPORT int JNICALL -NET_MapSocketOption(jint cmd, int *level, int *optname) { - - typedef struct { - jint cmd; - int level; - int optname; - } sockopts; - - static sockopts opts[] = { - { java_net_SocketOptions_TCP_NODELAY, IPPROTO_TCP, TCP_NODELAY }, - { java_net_SocketOptions_SO_OOBINLINE, SOL_SOCKET, SO_OOBINLINE }, - { java_net_SocketOptions_SO_LINGER, SOL_SOCKET, SO_LINGER }, - { java_net_SocketOptions_SO_SNDBUF, SOL_SOCKET, SO_SNDBUF }, - { java_net_SocketOptions_SO_RCVBUF, SOL_SOCKET, SO_RCVBUF }, - { java_net_SocketOptions_SO_KEEPALIVE, SOL_SOCKET, SO_KEEPALIVE }, - { java_net_SocketOptions_SO_REUSEADDR, SOL_SOCKET, SO_REUSEADDR }, - { java_net_SocketOptions_SO_BROADCAST, SOL_SOCKET, SO_BROADCAST }, - { java_net_SocketOptions_IP_MULTICAST_IF, IPPROTO_IP, IP_MULTICAST_IF }, - { java_net_SocketOptions_IP_MULTICAST_LOOP, IPPROTO_IP, IP_MULTICAST_LOOP }, - { java_net_SocketOptions_IP_TOS, IPPROTO_IP, IP_TOS }, - - }; - - - int i; - - /* - * Map the Java level option to the native level - */ - for (i=0; i<(int)(sizeof(opts) / sizeof(opts[0])); i++) { - if (cmd == opts[i].cmd) { - *level = opts[i].level; - *optname = opts[i].optname; - return 0; - } - } - - /* not found */ - return -1; -} - - /* * Wrapper for setsockopt dealing with Windows specific issues :- * diff --git a/src/java.base/windows/native/libnio/ch/UnixDomainSockets.c b/src/java.base/windows/native/libnio/ch/UnixDomainSockets.c index 209c30f3be7..bc185b3aa18 100644 --- a/src/java.base/windows/native/libnio/ch/UnixDomainSockets.c +++ b/src/java.base/windows/native/libnio/ch/UnixDomainSockets.c @@ -118,6 +118,9 @@ Java_sun_nio_ch_UnixDomainSockets_init(JNIEnv *env, jclass cl) if (result == SOCKET_ERROR) { if (GetLastError() == WSAENOBUFS) { infoPtr = (LPWSAPROTOCOL_INFOW)malloc(len); + if (infoPtr == NULL) { + return JNI_FALSE; + } result = WSAEnumProtocolsW(0, infoPtr, &len); if (result == SOCKET_ERROR) { free(infoPtr); diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CGraphicsDevice.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CGraphicsDevice.m index a93bfcb58c4..468c5f6dae0 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CGraphicsDevice.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CGraphicsDevice.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -35,6 +35,7 @@ #define DEFAULT_DEVICE_HEIGHT 768 #define DEFAULT_DEVICE_DPI 72 +static NSInteger architecture = -1; /* * Convert the mode string to the more convenient bits per pixel value */ @@ -58,7 +59,17 @@ static int getBPPFromModeString(CFStringRef mode) return 0; } -static BOOL isValidDisplayMode(CGDisplayModeRef mode){ +static BOOL isValidDisplayMode(CGDisplayModeRef mode) { + // Workaround for apple bug FB13261205, since it only affects arm based macs + // and arm support started with macOS 11 ignore the workaround for previous versions + if (@available(macOS 11, *)) { + if (architecture == -1) { + architecture = [[NSRunningApplication currentApplication] executableArchitecture]; + } + if (architecture == NSBundleExecutableArchitectureARM64) { + return (CGDisplayModeGetPixelWidth(mode) >= 800); + } + } return (1 < CGDisplayModeGetWidth(mode) && 1 < CGDisplayModeGetHeight(mode)); } diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabButtonAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabButtonAccessibility.m index 2b22db80aa8..1507f4884c3 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabButtonAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/TabButtonAccessibility.m @@ -84,10 +84,7 @@ - (void)performPressAction { - (NSAccessibilitySubrole)accessibilitySubrole { - if (@available(macOS 10.13, *)) { - return NSAccessibilityTabButtonSubrole; - } - return NSAccessibilityUnknownSubrole; + return NSAccessibilityTabButtonSubrole; } - (id)accessibilityValue diff --git a/src/java.desktop/macosx/native/libsplashscreen/splashscreen_sys.m b/src/java.desktop/macosx/native/libsplashscreen/splashscreen_sys.m index 33e850dbb38..66e0c362318 100644 --- a/src/java.desktop/macosx/native/libsplashscreen/splashscreen_sys.m +++ b/src/java.desktop/macosx/native/libsplashscreen/splashscreen_sys.m @@ -273,8 +273,10 @@ static int isInAquaSession() { pthread_attr_t attr; int rc; - pthread_attr_init(&attr); + int rslt = pthread_attr_init(&attr); + if (rslt != 0) return; rc = pthread_create(&thr, &attr, SplashScreenThread, (void *) splash); + pthread_attr_destroy(&attr); } void diff --git a/src/java.desktop/share/classes/java/awt/doc-files/FocusCycle.svg b/src/java.desktop/share/classes/java/awt/doc-files/FocusCycle.svg index 316ea25e7e6..59bd0992c9c 100644 --- a/src/java.desktop/share/classes/java/awt/doc-files/FocusCycle.svg +++ b/src/java.desktop/share/classes/java/awt/doc-files/FocusCycle.svg @@ -1,7 +1,7 @@ """, """ @@ -99,7 +99,7 @@ void html5Output() { """

    """, """ - -
    The overview summary page header.
    @@ -1129,6 +1131,7 @@ void checkModuleDeprecation(boolean found) {

    Module moduletags


    +
    @Deprecated module moduletags
    """, """ @@ -1142,6 +1145,7 @@ void checkModuleAnnotation() {

    Module moduleB


    +
    @AnnotationType(optional="Module Anno\ @@ -1354,11 +1358,13 @@ void checkLinkSource(boolean includePrivate) { checkOutput("moduleA/testpkgmdlA/TestClassInModuleA.html", true, """
    +

    public class TestClassInModuleA extends java.lang.Object
    +
    """); checkOutput("src-html/moduleA/testpkgmdlA/TestClassInModuleA.html", true, """ diff --git a/test/langtools/jdk/javadoc/doclet/testNavigation/TestNavigation.java b/test/langtools/jdk/javadoc/doclet/testNavigation/TestNavigation.java index 7dd8eaca2a1..9342026b6ca 100644 --- a/test/langtools/jdk/javadoc/doclet/testNavigation/TestNavigation.java +++ b/test/langtools/jdk/javadoc/doclet/testNavigation/TestNavigation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -91,7 +91,6 @@ public void test(Path ignore) { -
    """); @@ -100,7 +99,6 @@ public void test(Path ignore) { -
    """); } @@ -120,7 +118,6 @@ public void test1(Path ignore) { -
    """); diff --git a/test/langtools/jdk/javadoc/doclet/testOptions/TestOptions.java b/test/langtools/jdk/javadoc/doclet/testOptions/TestOptions.java index 350ca881b5e..898a498ddee 100644 --- a/test/langtools/jdk/javadoc/doclet/testOptions/TestOptions.java +++ b/test/langtools/jdk/javadoc/doclet/testOptions/TestOptions.java @@ -232,6 +232,7 @@ public void testLinkSource() { """

    DEFAULT_NAME

    +
    static final <\ span class="return-type">java.lang.String DEFAULT_NAME

    name

    +
    java.lang.String&\ nbsp;name
    """); diff --git a/test/langtools/jdk/javadoc/doclet/testPackageAnnotation/TestPackageAnnotation.java b/test/langtools/jdk/javadoc/doclet/testPackageAnnotation/TestPackageAnnotation.java index bf56ec43c1d..b8c2d654aa4 100644 --- a/test/langtools/jdk/javadoc/doclet/testPackageAnnotation/TestPackageAnnotation.java +++ b/test/langtools/jdk/javadoc/doclet/testPackageAnnotation/TestPackageAnnotation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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 @@ -55,6 +55,7 @@ public void testPackageInfoAnnotationNoComment() {

    Package pkg1


    +
    @Deprecated(since="1<2>3") package pkg1
    """); @@ -91,6 +92,7 @@ public void testPackageInfoAndHtml() {

    Package pkg3


    +
    @Deprecated(since="1<2>3") package pkg3
    """); diff --git a/test/langtools/jdk/javadoc/doclet/testRestricted/TestRestricted.java b/test/langtools/jdk/javadoc/doclet/testRestricted/TestRestricted.java new file mode 100644 index 00000000000..b9dd80d61fb --- /dev/null +++ b/test/langtools/jdk/javadoc/doclet/testRestricted/TestRestricted.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8316972 + * @summary Add javadoc support for restricted methods + * @library /tools/lib ../../lib + * @modules jdk.javadoc/jdk.javadoc.internal.tool + * @build toolbox.ToolBox javadoc.tester.* + * @run main TestRestricted + */ + +import java.io.IOException; +import java.nio.file.Path; + +import javadoc.tester.JavadocTester; +import toolbox.ToolBox; + +public class TestRestricted extends JavadocTester { + + public final ToolBox tb; + public static void main(String... args) throws Exception { + var tester = new TestRestricted(); + tester.runTests(); + } + + public TestRestricted() { + tb = new ToolBox(); + } + + @Test + public void testRestricted(Path base) throws IOException { + var src = base.resolve("src"); + tb.writeJavaFiles(src, """ + package pkg; + + import jdk.internal.javac.PreviewFeature; + import jdk.internal.javac.PreviewFeature.Feature; + import jdk.internal.javac.Restricted; + + /** + * Interface containing restricted methods. + * @see #restrictedMethod() + * @see #restrictedPreviewMethod() + */ + public interface I { + + /** + * Restricted method. + */ + @Restricted + public void restrictedMethod(); + + /** + * Restricted preview method. + */ + @PreviewFeature(feature=Feature.TEST) + @Restricted + public int restrictedPreviewMethod(); + } + """); + + javadoc("--enable-preview", "-source", System.getProperty("java.specification.version"), + "--add-exports", "java.base/jdk.internal.javac=ALL-UNNAMED", + "-d", base.resolve("api").toString(), + "-sourcepath", src.toString(), + "pkg"); + checkExit(Exit.OK); + + // Test restricted method note in class documentation + checkOutput("pkg/I.html", true, + """ +
      +
    • restrictedMethod()RESTRICTED
    • +
    • restrictedPreviewMethod()\ + PREVIEW RESTRICTED
    • """, + """ +
      Restricted.
      +
      Restricted method.
      """, + """ +
      Preview.
      +
      Restricted.
      +
      Restricted preview method.
      """, + """ +

      restrictedMethod

      +
      +
      sealed void restrictedMethod\ + ()
      +
      restrictedMethod is a restricted method of the Java platf\ + orm. +
      Programs can only use restrictedMethod\ + when access to restricted methods is enabled.
      +
      Restricted methods are unsafe, and, if used incorre\ + ctly, might crash the JVM or result in memory corruption.
      +
      """, + """ +

      restrictedPreviewMethod

      +
      +
      sealed int restrictedPreviewMethod<\ + /span>()
      +
      restrictedPreviewMethod is a preview API of the Java platfo\ + rm. +
      Programs can only use restrictedPreviewMethod when preview features are enabled.
      +
      Preview features may be removed in a future release, o\ + r upgraded to permanent features of the Java platform.
      +
      +
      restrictedPreviewMethod is a restricted method of \ + the Java platform. +
      Programs can only use restrictedPreviewMethod\ + when access to restricted methods is enabled.
      +
      Restricted methods are unsafe, and, if used incorre\ + ctly, might crash the JVM or result in memory corruption.
      +
      """); + + // Test link on index page + checkOutput("index-all.html", true, + """ + Restricted Methods"""); + + // Test restricted methods list + checkOutput("restricted-list.html", true, + """ +

      Restricted Methods

      +
      +
        +
      • +
        +
        Methods
        +
        +
        Method
        +
        Description
        + +
        +
        Restricted method.
        +
        + +
        +
        Restricted preview method.
        +
        """); + } +} diff --git a/test/langtools/jdk/javadoc/doclet/testReturnTag/TestReturnTag.java b/test/langtools/jdk/javadoc/doclet/testReturnTag/TestReturnTag.java index ceffa141609..5a081250137 100644 --- a/test/langtools/jdk/javadoc/doclet/testReturnTag/TestReturnTag.java +++ b/test/langtools/jdk/javadoc/doclet/testReturnTag/TestReturnTag.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -292,6 +292,7 @@ public class X { checkOutput("C.html", true, """
        Some text. Returns the result. More text.
        +
    """); } diff --git a/test/langtools/jdk/javadoc/doclet/testSearch/TestSearch.java b/test/langtools/jdk/javadoc/doclet/testSearch/TestSearch.java index b6f802d8625..08edce2031e 100644 --- a/test/langtools/jdk/javadoc/doclet/testSearch/TestSearch.java +++ b/test/langtools/jdk/javadoc/doclet/testSearch/TestSearch.java @@ -434,8 +434,6 @@ void checkSearchOutput(String fileName, boolean expectedOutput) { """); - checkOutput(fileName, true, - "
    "); } void checkSingleIndex() { diff --git a/test/langtools/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java b/test/langtools/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java index b4ceac438b2..d1bf5a4f2b3 100644 --- a/test/langtools/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java +++ b/test/langtools/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java @@ -138,7 +138,7 @@ public void test(Path base) { .summary-table > div, .details-table > div { text-align:left; padding: 8px 3px 3px 7px; - overflow-x: auto; + overflow: auto hidden; scrollbar-width: thin; }""", "@import url('fonts/dejavu.css');", @@ -161,7 +161,7 @@ public void test(Path base) { font-weight:bold; }""", """ - .deprecation-block { + .deprecation-block, .preview-block, .restricted-block { font-size:1em; font-family:var(--block-font-family); border-style:solid; diff --git a/test/langtools/jdk/javadoc/doclet/testSummaryTag/TestSummaryTag.java b/test/langtools/jdk/javadoc/doclet/testSummaryTag/TestSummaryTag.java index 6b348af9c7a..5cf51b5d4f8 100644 --- a/test/langtools/jdk/javadoc/doclet/testSummaryTag/TestSummaryTag.java +++ b/test/langtools/jdk/javadoc/doclet/testSummaryTag/TestSummaryTag.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -92,9 +92,11 @@ public void test1() { """

    m3

    +
    public void m3()
    First sentence some text maybe second sentence.
    +
    """ ); diff --git a/test/langtools/jdk/javadoc/doclet/testSystemPropertyPage/TestSystemPropertyPage.java b/test/langtools/jdk/javadoc/doclet/testSystemPropertyPage/TestSystemPropertyPage.java index 75bef1d4c81..b31fb1df50e 100644 --- a/test/langtools/jdk/javadoc/doclet/testSystemPropertyPage/TestSystemPropertyPage.java +++ b/test/langtools/jdk/javadoc/doclet/testSystemPropertyPage/TestSystemPropertyPage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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 @@ -67,11 +67,9 @@ public void test(Path base) throws Exception { checkOutput("system-properties.html", true, """ -
    -

    Mapping C types
    {@code bool}{@link ValueLayout#JAVA_BOOLEAN}{@code boolean}
    {@code char}
    {@code char}
    {@code unsigned char}
    {@link ValueLayout#JAVA_BYTE}{@code byte}
    {@code short}
    {@code short}
    {@code unsigned short}
    {@link ValueLayout#JAVA_SHORT}{@code short}
    {@code int}
    {@code int}
    {@code unsigned int}
    {@link ValueLayout#JAVA_INT}{@code int}
    {@code long}
    {@code long}
    {@code unsigned long}
    {@link ValueLayout#JAVA_LONG}{@code long}
    {@code long long}
    {@code long long}
    {@code unsigned long long}
    {@link ValueLayout#JAVA_LONG}{@code long}
    {@code float}