diff --git a/make/jdk/src/classes/build/tools/taglet/ToolGuide.java b/make/jdk/src/classes/build/tools/taglet/ToolGuide.java index ae70d424222fd..f3ce80cf241da 100644 --- a/make/jdk/src/classes/build/tools/taglet/ToolGuide.java +++ b/make/jdk/src/classes/build/tools/taglet/ToolGuide.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,8 +31,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.lang.model.element.Element; -import javax.lang.model.element.ModuleElement; import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; import com.sun.source.doctree.DocTree; import com.sun.source.doctree.UnknownBlockTagTree; @@ -75,7 +75,7 @@ public class ToolGuide implements Taglet { */ @Override public Set getAllowedLocations() { - return EnumSet.of(MODULE, PACKAGE); + return EnumSet.of(MODULE, PACKAGE, TYPE); } @Override @@ -151,6 +151,12 @@ private String docRoot(Element elem) { return pe.getEnclosingElement() != null ? "../" + pkgPart : pkgPart; + case CLASS: + TypeElement te = (TypeElement)elem; + return te.getQualifiedName() + .toString() + .replace('.', '/') + .replaceAll("[^/]+", ".."); default: throw new IllegalArgumentException(elem.getKind().toString()); diff --git a/make/modules/jdk.jpackage/Lib.gmk b/make/modules/jdk.jpackage/Lib.gmk index aa955b97bee33..9d118d950f5ef 100644 --- a/make/modules/jdk.jpackage/Lib.gmk +++ b/make/modules/jdk.jpackage/Lib.gmk @@ -92,9 +92,9 @@ ifeq ($(call isTargetOs, linux), true) JPACKAGE_LIBAPPLAUNCHER_INCLUDES := $(addprefix -I, $(JPACKAGE_LIBAPPLAUNCHER_SRC)) $(eval $(call SetupJdkLibrary, BUILD_JPACKAGE_LIBAPPLAUNCHER, \ - NAME := jpackageapplauncher, \ + NAME := jpackageapplauncheraux, \ OUTPUT_DIR := $(JPACKAGE_OUTPUT_DIR), \ - SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjpackageapplauncher, \ + SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjpackageapplauncheraux, \ SRC := $(JPACKAGE_LIBAPPLAUNCHER_SRC), \ EXCLUDE_FILES := LinuxLauncher.c LinuxPackage.c, \ TOOLCHAIN := TOOLCHAIN_LINK_CXX, \ diff --git a/make/test/JtregNativeJdk.gmk b/make/test/JtregNativeJdk.gmk index 270ff93d1475d..1a07de31653a0 100644 --- a/make/test/JtregNativeJdk.gmk +++ b/make/test/JtregNativeJdk.gmk @@ -54,6 +54,7 @@ BUILD_JDK_JTREG_EXECUTABLES_CFLAGS_exeJliLaunchTest := \ -I$(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS)/native/libjli BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libAsyncStackWalk := $(LIBCXX) +BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libAsyncInvokers := $(LIBCXX) # Platform specific setup ifeq ($(call isTargetOs, windows), true) @@ -66,6 +67,7 @@ ifeq ($(call isTargetOs, windows), true) BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeCallerAccessTest := jvm.lib BUILD_JDK_JTREG_EXECUTABLES_LIBS_exerevokeall := advapi32.lib BUILD_JDK_JTREG_LIBRARIES_CFLAGS_libAsyncStackWalk := /EHsc + BUILD_JDK_JTREG_LIBRARIES_CFLAGS_libAsyncInvokers := /EHsc else BUILD_JDK_JTREG_LIBRARIES_LIBS_libstringPlatformChars := -ljava BUILD_JDK_JTREG_LIBRARIES_LIBS_libDirectIO := -ljava diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 11c6ec7df0a26..1f1a21729345b 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -1199,6 +1199,9 @@ reg_class gov_pr ( // P7, non-allocatable, preserved with all elements preset to TRUE. ); +reg_class p0_reg(P0); +reg_class p1_reg(P1); + // Singleton class for condition codes reg_class int_flags(RFLAGS); @@ -4004,19 +4007,15 @@ encode %{ __ bind(object_has_monitor); STATIC_ASSERT(markWord::monitor_value <= INT_MAX); __ add(tmp, tmp, -(int)markWord::monitor_value); // monitor - __ ldr(rscratch1, Address(tmp, ObjectMonitor::owner_offset_in_bytes())); __ ldr(disp_hdr, Address(tmp, ObjectMonitor::recursions_offset_in_bytes())); Label notRecursive; - __ cmp(rscratch1, rthread); - __ br(Assembler::NE, cont); - __ cbz(disp_hdr, notRecursive); // Recursive lock __ sub(disp_hdr, disp_hdr, 1u); __ str(disp_hdr, Address(tmp, ObjectMonitor::recursions_offset_in_bytes())); - // flag == EQ was set in the ownership check above + __ cmp(disp_hdr, disp_hdr); // Sets flags for result __ b(cont); __ bind(notRecursive); @@ -5682,6 +5681,24 @@ operand pRegGov() interface(REG_INTER); %} +operand pRegGov_P0() +%{ + constraint(ALLOC_IN_RC(p0_reg)); + match(RegVectMask); + op_cost(0); + format %{ %} + interface(REG_INTER); +%} + +operand pRegGov_P1() +%{ + constraint(ALLOC_IN_RC(p1_reg)); + match(RegVectMask); + op_cost(0); + format %{ %} + interface(REG_INTER); +%} + // Flags register, used as output of signed compare instructions // note that on AArch64 we also use this register as the output for @@ -16660,7 +16677,7 @@ instruct partialSubtypeCheckVsZero(iRegP_R4 sub, iRegP_R0 super, iRegP_R2 temp, instruct string_compareU(iRegP_R1 str1, iRegI_R2 cnt1, iRegP_R3 str2, iRegI_R4 cnt2, iRegI_R0 result, iRegP_R10 tmp1, iRegL_R11 tmp2, rFlagsReg cr) %{ - predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::UU); + predicate((UseSVE == 0) && (((StrCompNode*)n)->encoding() == StrIntrinsicNode::UU)); match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); effect(KILL tmp1, KILL tmp2, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); @@ -16670,7 +16687,7 @@ instruct string_compareU(iRegP_R1 str1, iRegI_R2 cnt1, iRegP_R3 str2, iRegI_R4 c __ string_compare($str1$$Register, $str2$$Register, $cnt1$$Register, $cnt2$$Register, $result$$Register, $tmp1$$Register, $tmp2$$Register, - fnoreg, fnoreg, fnoreg, StrIntrinsicNode::UU); + fnoreg, fnoreg, fnoreg, pnoreg, pnoreg, StrIntrinsicNode::UU); %} ins_pipe(pipe_class_memory); %} @@ -16678,7 +16695,7 @@ instruct string_compareU(iRegP_R1 str1, iRegI_R2 cnt1, iRegP_R3 str2, iRegI_R4 c instruct string_compareL(iRegP_R1 str1, iRegI_R2 cnt1, iRegP_R3 str2, iRegI_R4 cnt2, iRegI_R0 result, iRegP_R10 tmp1, iRegL_R11 tmp2, rFlagsReg cr) %{ - predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::LL); + predicate((UseSVE == 0) && (((StrCompNode*)n)->encoding() == StrIntrinsicNode::LL)); match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); effect(KILL tmp1, KILL tmp2, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); @@ -16687,7 +16704,7 @@ instruct string_compareL(iRegP_R1 str1, iRegI_R2 cnt1, iRegP_R3 str2, iRegI_R4 c __ string_compare($str1$$Register, $str2$$Register, $cnt1$$Register, $cnt2$$Register, $result$$Register, $tmp1$$Register, $tmp2$$Register, - fnoreg, fnoreg, fnoreg, StrIntrinsicNode::LL); + fnoreg, fnoreg, fnoreg, pnoreg, pnoreg, StrIntrinsicNode::LL); %} ins_pipe(pipe_class_memory); %} @@ -16696,7 +16713,7 @@ instruct string_compareUL(iRegP_R1 str1, iRegI_R2 cnt1, iRegP_R3 str2, iRegI_R4 iRegI_R0 result, iRegP_R10 tmp1, iRegL_R11 tmp2, vRegD_V0 vtmp1, vRegD_V1 vtmp2, vRegD_V2 vtmp3, rFlagsReg cr) %{ - predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::UL); + predicate((UseSVE == 0) && (((StrCompNode*)n)->encoding() == StrIntrinsicNode::UL)); match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); effect(KILL tmp1, KILL tmp2, KILL vtmp1, KILL vtmp2, KILL vtmp3, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); @@ -16707,7 +16724,7 @@ instruct string_compareUL(iRegP_R1 str1, iRegI_R2 cnt1, iRegP_R3 str2, iRegI_R4 $cnt1$$Register, $cnt2$$Register, $result$$Register, $tmp1$$Register, $tmp2$$Register, $vtmp1$$FloatRegister, $vtmp2$$FloatRegister, - $vtmp3$$FloatRegister, StrIntrinsicNode::UL); + $vtmp3$$FloatRegister, pnoreg, pnoreg, StrIntrinsicNode::UL); %} ins_pipe(pipe_class_memory); %} @@ -16716,7 +16733,7 @@ instruct string_compareLU(iRegP_R1 str1, iRegI_R2 cnt1, iRegP_R3 str2, iRegI_R4 iRegI_R0 result, iRegP_R10 tmp1, iRegL_R11 tmp2, vRegD_V0 vtmp1, vRegD_V1 vtmp2, vRegD_V2 vtmp3, rFlagsReg cr) %{ - predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::LU); + predicate((UseSVE == 0) && (((StrCompNode*)n)->encoding() == StrIntrinsicNode::LU)); match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); effect(KILL tmp1, KILL tmp2, KILL vtmp1, KILL vtmp2, KILL vtmp3, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); @@ -16727,7 +16744,7 @@ instruct string_compareLU(iRegP_R1 str1, iRegI_R2 cnt1, iRegP_R3 str2, iRegI_R4 $cnt1$$Register, $cnt2$$Register, $result$$Register, $tmp1$$Register, $tmp2$$Register, $vtmp1$$FloatRegister, $vtmp2$$FloatRegister, - $vtmp3$$FloatRegister,StrIntrinsicNode::LU); + $vtmp3$$FloatRegister, pnoreg, pnoreg, StrIntrinsicNode::LU); %} ins_pipe(pipe_class_memory); %} diff --git a/src/hotspot/cpu/aarch64/aarch64_sve.ad b/src/hotspot/cpu/aarch64/aarch64_sve.ad index 8260459f2231f..6ad2d68526c8e 100644 --- a/src/hotspot/cpu/aarch64/aarch64_sve.ad +++ b/src/hotspot/cpu/aarch64/aarch64_sve.ad @@ -5551,6 +5551,105 @@ instruct stringU_indexof_char_sve(iRegP_R1 str1, iRegI_R2 cnt1, iRegI_R3 ch, ins_pipe(pipe_class_memory); %} +// Intrisics for String.compareTo() + +// Note that Z registers alias the corresponding NEON registers, we declare the vector operands of +// these string_compare variants as NEON register type for convenience so that the prototype of +// string_compare can be shared with all variants. + + +instruct string_compareLL_sve(iRegP_R1 str1, iRegI_R2 cnt1, iRegP_R3 str2, iRegI_R4 cnt2, + iRegI_R0 result, iRegP_R10 tmp1, iRegL_R11 tmp2, + vRegD_V0 vtmp1, vRegD_V1 vtmp2, pRegGov_P0 pgtmp1, + pRegGov_P1 pgtmp2, rFlagsReg cr) +%{ + predicate((UseSVE > 0) && (((StrCompNode*)n)->encoding() == StrIntrinsicNode::LL)); + match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); + effect(TEMP tmp1, TEMP tmp2, TEMP vtmp1, TEMP vtmp2, TEMP pgtmp1, TEMP pgtmp2, + USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); + + format %{ "String Compare $str1,$cnt1,$str2,$cnt2 -> $result # USE sve" %} + ins_encode %{ + // Count is in 8-bit bytes; non-Compact chars are 16 bits. + __ string_compare($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, $result$$Register, + $tmp1$$Register, $tmp2$$Register, + $vtmp1$$FloatRegister, $vtmp2$$FloatRegister, fnoreg, + as_PRegister($pgtmp1$$reg), as_PRegister($pgtmp2$$reg), + StrIntrinsicNode::LL); + %} + ins_pipe(pipe_class_memory); +%} + +instruct string_compareLU_sve(iRegP_R1 str1, iRegI_R2 cnt1, iRegP_R3 str2, iRegI_R4 cnt2, + iRegI_R0 result, iRegP_R10 tmp1, iRegL_R11 tmp2, + vRegD_V0 vtmp1, vRegD_V1 vtmp2, pRegGov_P0 pgtmp1, + pRegGov_P1 pgtmp2, rFlagsReg cr) +%{ + predicate((UseSVE > 0) && (((StrCompNode*)n)->encoding() == StrIntrinsicNode::LU)); + match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); + effect(TEMP tmp1, TEMP tmp2, TEMP vtmp1, TEMP vtmp2, TEMP pgtmp1, TEMP pgtmp2, + USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); + + format %{ "String Compare $str1,$cnt1,$str2,$cnt2 -> $result # USE sve" %} + ins_encode %{ + // Count is in 8-bit bytes; non-Compact chars are 16 bits. + __ string_compare($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, $result$$Register, + $tmp1$$Register, $tmp2$$Register, + $vtmp1$$FloatRegister, $vtmp2$$FloatRegister, fnoreg, + as_PRegister($pgtmp1$$reg), as_PRegister($pgtmp2$$reg), + StrIntrinsicNode::LU); + %} + ins_pipe(pipe_class_memory); +%} + +instruct string_compareUL_sve(iRegP_R1 str1, iRegI_R2 cnt1, iRegP_R3 str2, iRegI_R4 cnt2, + iRegI_R0 result, iRegP_R10 tmp1, iRegL_R11 tmp2, + vRegD_V0 vtmp1, vRegD_V1 vtmp2, pRegGov_P0 pgtmp1, + pRegGov_P1 pgtmp2, rFlagsReg cr) +%{ + predicate((UseSVE > 0) && (((StrCompNode*)n)->encoding() == StrIntrinsicNode::UL)); + match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); + effect(TEMP tmp1, TEMP tmp2, TEMP vtmp1, TEMP vtmp2, TEMP pgtmp1, TEMP pgtmp2, + USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); + + format %{ "String Compare $str1,$cnt1,$str2,$cnt2 -> $result # USE sve" %} + ins_encode %{ + // Count is in 8-bit bytes; non-Compact chars are 16 bits. + __ string_compare($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, $result$$Register, + $tmp1$$Register, $tmp2$$Register, + $vtmp1$$FloatRegister, $vtmp2$$FloatRegister, fnoreg, + as_PRegister($pgtmp1$$reg), as_PRegister($pgtmp2$$reg), + StrIntrinsicNode::UL); + %} + ins_pipe(pipe_class_memory); +%} + +instruct string_compareUU_sve(iRegP_R1 str1, iRegI_R2 cnt1, iRegP_R3 str2, iRegI_R4 cnt2, + iRegI_R0 result, iRegP_R10 tmp1, iRegL_R11 tmp2, + vRegD_V0 vtmp1, vRegD_V1 vtmp2, pRegGov_P0 pgtmp1, + pRegGov_P1 pgtmp2, rFlagsReg cr) +%{ + predicate((UseSVE > 0) && (((StrCompNode*)n)->encoding() == StrIntrinsicNode::UU)); + match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); + effect(TEMP tmp1, TEMP tmp2, TEMP vtmp1, TEMP vtmp2, TEMP pgtmp1, TEMP pgtmp2, + USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); + + format %{ "String Compare $str1,$cnt1,$str2,$cnt2 -> $result # USE sve" %} + ins_encode %{ + // Count is in 8-bit bytes; non-Compact chars are 16 bits. + __ string_compare($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, $result$$Register, + $tmp1$$Register, $tmp2$$Register, + $vtmp1$$FloatRegister, $vtmp2$$FloatRegister, fnoreg, + as_PRegister($pgtmp1$$reg), as_PRegister($pgtmp2$$reg), + StrIntrinsicNode::UU); + %} + ins_pipe(pipe_class_memory); +%} + // ---------------------------- Vector mask reductions --------------------------- instruct vmask_truecount(iRegINoSp dst, pReg src) %{ predicate(UseSVE > 0 && diff --git a/src/hotspot/cpu/aarch64/aarch64_sve_ad.m4 b/src/hotspot/cpu/aarch64/aarch64_sve_ad.m4 index 7589735365eea..65de321a6e121 100644 --- a/src/hotspot/cpu/aarch64/aarch64_sve_ad.m4 +++ b/src/hotspot/cpu/aarch64/aarch64_sve_ad.m4 @@ -3044,6 +3044,42 @@ dnl $1 $2 $3 STRING_INDEXOF_CHAR(L, Latin1, true) STRING_INDEXOF_CHAR(U, UTF16, false) +// Intrisics for String.compareTo() + +// Note that Z registers alias the corresponding NEON registers, we declare the vector operands of +// these string_compare variants as NEON register type for convenience so that the prototype of +// string_compare can be shared with all variants. + +dnl +define(`STRING_COMPARETO', ` +instruct string_compare$1_sve(iRegP_R1 str1, iRegI_R2 cnt1, iRegP_R3 str2, iRegI_R4 cnt2, + iRegI_R0 result, iRegP_R10 tmp1, iRegL_R11 tmp2, + vRegD_V0 vtmp1, vRegD_V1 vtmp2, pRegGov_P0 pgtmp1, + pRegGov_P1 pgtmp2, rFlagsReg cr) +%{ + predicate((UseSVE > 0) && (((StrCompNode*)n)->encoding() == StrIntrinsicNode::$1)); + match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); + effect(TEMP tmp1, TEMP tmp2, TEMP vtmp1, TEMP vtmp2, TEMP pgtmp1, TEMP pgtmp2, + USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); + + format %{ "String Compare $str1,$cnt1,$str2,$cnt2 -> $result # USE sve" %} + ins_encode %{ + // Count is in 8-bit bytes; non-Compact chars are 16 bits. + __ string_compare($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, $result$$Register, + $tmp1$$Register, $tmp2$$Register, + $vtmp1$$FloatRegister, $vtmp2$$FloatRegister, fnoreg, + as_PRegister($pgtmp1$$reg), as_PRegister($pgtmp2$$reg), + StrIntrinsicNode::$1); + %} + ins_pipe(pipe_class_memory); +%}')dnl +dnl $1 +STRING_COMPARETO(LL) +STRING_COMPARETO(LU) +STRING_COMPARETO(UL) +STRING_COMPARETO(UU) + // ---------------------------- Vector mask reductions --------------------------- instruct vmask_truecount(iRegINoSp dst, pReg src) %{ predicate(UseSVE > 0 && diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index 9eee231ec0fea..1bf593b4524a5 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -478,16 +478,17 @@ class Address { assert(size == 0, "bad size"); size = 0b100; } + assert(offset_ok_for_immed(_offset, size), + "must be, was: " INT64_FORMAT ", %d", _offset, size); unsigned mask = (1 << size) - 1; - if (_offset < 0 || _offset & mask) - { - i->f(0b00, 25, 24); - i->f(0, 21), i->f(0b00, 11, 10); - i->sf(_offset, 20, 12); - } else { - i->f(0b01, 25, 24); - i->f(_offset >> size, 21, 10); - } + if (_offset < 0 || _offset & mask) { + i->f(0b00, 25, 24); + i->f(0, 21), i->f(0b00, 11, 10); + i->sf(_offset, 20, 12); + } else { + i->f(0b01, 25, 24); + i->f(_offset >> size, 21, 10); + } } break; diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.inline.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.inline.hpp index 9fabf1699c531..e7efe472b8290 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.inline.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.inline.hpp @@ -30,8 +30,14 @@ #include "asm/codeBuffer.hpp" #include "code/codeCache.hpp" - +// Check if an offset is within the encoding range for LDR/STR instructions +// with an immediate offset, either using unscaled signed 9-bits or, scaled +// unsigned 12-bits. We favour the scaled unsigned encoding for all aligned +// offsets (only using the signed 9-bit encoding for negative and unaligned +// offsets). As a precondition, 0 <= shift <= 4 is the log2(size), for the +// supported data widths, {1, 2, 4, 8, 16} bytes. inline bool Address::offset_ok_for_immed(int64_t offset, uint shift) { + precond(shift < 5); uint mask = (1 << shift) - 1; if (offset < 0 || (offset & mask) != 0) { // Unscaled signed offset, encoded in a signed imm9 field. diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index 1cae3a3f3b64d..fead8af7d1bbd 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -187,14 +187,13 @@ Address LIR_Assembler::as_Address(LIR_Address* addr, Register tmp) { default: ShouldNotReachHere(); } - } else { - intptr_t addr_offset = intptr_t(addr->disp()); - if (Address::offset_ok_for_immed(addr_offset, addr->scale())) - return Address(base, addr_offset, Address::lsl(addr->scale())); - else { - __ mov(tmp, addr_offset); - return Address(base, tmp, Address::lsl(addr->scale())); - } + } else { + assert(addr->scale() == 0, + "expected for immediate operand, was: %d", addr->scale()); + ptrdiff_t offset = ptrdiff_t(addr->disp()); + // NOTE: Does not handle any 16 byte vector access. + const uint type_size = type2aelembytes(addr->type(), true); + return __ legitimize_address(Address(base, offset), type_size, tmp); } return Address(); } @@ -986,14 +985,7 @@ void LIR_Assembler::mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_Patch __ ldr(dest->as_register(), as_Address(from_addr)); break; case T_ADDRESS: - // FIXME: OMG this is a horrible kludge. Any offset from an - // address that matches klass_offset_in_bytes() will be loaded - // as a word, not a long. - if (UseCompressedClassPointers && addr->disp() == oopDesc::klass_offset_in_bytes()) { - __ ldrw(dest->as_register(), as_Address(from_addr)); - } else { - __ ldr(dest->as_register(), as_Address(from_addr)); - } + __ ldr(dest->as_register(), as_Address(from_addr)); break; case T_INT: __ ldrw(dest->as_register(), as_Address(from_addr)); @@ -1032,10 +1024,6 @@ void LIR_Assembler::mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_Patch // Load barrier has not yet been applied, so ZGC can't verify the oop here __ verify_oop(dest->as_register()); } - } else if (type == T_ADDRESS && addr->disp() == oopDesc::klass_offset_in_bytes()) { - if (UseCompressedClassPointers) { - __ decode_klass_not_null(dest->as_register()); - } } } @@ -2593,6 +2581,22 @@ void LIR_Assembler::emit_lock(LIR_OpLock* op) { __ bind(*op->stub()->continuation()); } +void LIR_Assembler::emit_load_klass(LIR_OpLoadKlass* op) { + Register obj = op->obj()->as_pointer_register(); + Register result = op->result_opr()->as_pointer_register(); + + CodeEmitInfo* info = op->info(); + if (info != NULL) { + add_debug_info_for_null_check_here(info); + } + + if (UseCompressedClassPointers) { + __ ldrw(result, Address (obj, oopDesc::klass_offset_in_bytes())); + __ decode_klass_not_null(result); + } else { + __ ldr(result, Address (obj, oopDesc::klass_offset_in_bytes())); + } +} void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) { ciMethod* method = op->profiled_method(); diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index 5ba1026415f61..f8289a4bda546 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -676,7 +676,8 @@ void C2_MacroAssembler::stringL_indexof_char(Register str1, Register cnt1, // Compare strings. void C2_MacroAssembler::string_compare(Register str1, Register str2, Register cnt1, Register cnt2, Register result, Register tmp1, Register tmp2, - FloatRegister vtmp1, FloatRegister vtmp2, FloatRegister vtmp3, int ae) { + FloatRegister vtmp1, FloatRegister vtmp2, FloatRegister vtmp3, + PRegister pgtmp1, PRegister pgtmp2, int ae) { Label DONE, SHORT_LOOP, SHORT_STRING, SHORT_LAST, TAIL, STUB, DIFF, NEXT_WORD, SHORT_LOOP_TAIL, SHORT_LAST2, SHORT_LAST_INIT, SHORT_LOOP_START, TAIL_CHECK; diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp index 81d9799f5adbc..2c9d4b59ecee6 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp @@ -32,7 +32,8 @@ void string_compare(Register str1, Register str2, Register cnt1, Register cnt2, Register result, Register tmp1, Register tmp2, FloatRegister vtmp1, - FloatRegister vtmp2, FloatRegister vtmp3, int ae); + FloatRegister vtmp2, FloatRegister vtmp3, + PRegister pgtmp1, PRegister pgtmp2, int ae); void string_indexof(Register str1, Register str2, Register cnt1, Register cnt2, diff --git a/src/hotspot/cpu/aarch64/register_aarch64.hpp b/src/hotspot/cpu/aarch64/register_aarch64.hpp index 69a0a7eb535de..7b472856dc442 100644 --- a/src/hotspot/cpu/aarch64/register_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/register_aarch64.hpp @@ -268,6 +268,8 @@ class PRegisterImpl: public AbstractRegisterImpl { }; // The predicate registers of SVE. +CONSTANT_REGISTER_DECLARATION(PRegister, pnoreg, (-1)); + CONSTANT_REGISTER_DECLARATION(PRegister, p0, ( 0)); CONSTANT_REGISTER_DECLARATION(PRegister, p1, ( 1)); CONSTANT_REGISTER_DECLARATION(PRegister, p2, ( 2)); diff --git a/src/hotspot/cpu/aarch64/register_definitions_aarch64.cpp b/src/hotspot/cpu/aarch64/register_definitions_aarch64.cpp index f48c70d09e670..32358a0b154c3 100644 --- a/src/hotspot/cpu/aarch64/register_definitions_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/register_definitions_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -188,6 +188,8 @@ REGISTER_DEFINITION(FloatRegister, z29); REGISTER_DEFINITION(FloatRegister, z30); REGISTER_DEFINITION(FloatRegister, z31); +REGISTER_DEFINITION(PRegister, pnoreg); + REGISTER_DEFINITION(PRegister, p0); REGISTER_DEFINITION(PRegister, p1); REGISTER_DEFINITION(PRegister, p2); diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index fff6cd8cf9967..b039208654294 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -5031,6 +5031,97 @@ class StubGenerator: public StubCodeGenerator { return start; } + enum string_compare_mode { + LL, + LU, + UL, + UU, + }; + + // The following registers are declared in aarch64.ad + // r0 = result + // r1 = str1 + // r2 = cnt1 + // r3 = str2 + // r4 = cnt2 + // r10 = tmp1 + // r11 = tmp2 + // z0 = ztmp1 + // z1 = ztmp2 + // p0 = pgtmp1 + // p1 = pgtmp2 + address generate_compare_long_string_sve(string_compare_mode mode) { + __ align(CodeEntryAlignment); + address entry = __ pc(); + Register result = r0, str1 = r1, cnt1 = r2, str2 = r3, cnt2 = r4, + tmp1 = r10, tmp2 = r11; + + Label LOOP, MATCH, DONE, NOMATCH; + Register vec_len = tmp1; + Register idx = tmp2; + // The minimum of the string lengths has been stored in cnt2. + Register cnt = cnt2; + FloatRegister ztmp1 = z0, ztmp2 = z1; + PRegister pgtmp1 = p0, pgtmp2 = p1; + + if (mode == LL) { + __ sve_cntb(vec_len); + } else { + __ sve_cnth(vec_len); + } + + __ mov(idx, 0); + __ sve_whilelt(pgtmp1, mode == LL ? __ B : __ H, idx, cnt); + + __ bind(LOOP); + switch (mode) { + case LL: + __ sve_ld1b(ztmp1, __ B, pgtmp1, Address(str1, idx)); + __ sve_ld1b(ztmp2, __ B, pgtmp1, Address(str2, idx)); + break; + case LU: + __ sve_ld1b(ztmp1, __ H, pgtmp1, Address(str1, idx)); + __ sve_ld1h(ztmp2, __ H, pgtmp1, Address(str2, idx, Address::lsl(1))); + break; + case UL: + __ sve_ld1h(ztmp1, __ H, pgtmp1, Address(str1, idx, Address::lsl(1))); + __ sve_ld1b(ztmp2, __ H, pgtmp1, Address(str2, idx)); + break; + case UU: + __ sve_ld1h(ztmp1, __ H, pgtmp1, Address(str1, idx, Address::lsl(1))); + __ sve_ld1h(ztmp2, __ H, pgtmp1, Address(str2, idx, Address::lsl(1))); + break; + default: ShouldNotReachHere(); + } + __ add(idx, idx, vec_len); + + // Compare strings. + __ sve_cmp(Assembler::NE, pgtmp2, mode == LL ? __ B : __ H, pgtmp1, ztmp1, ztmp2); + __ br(__ NE, MATCH); + __ sve_whilelt(pgtmp1, mode == LL ? __ B : __ H, idx, cnt); + __ br(__ LT, LOOP); + + // The result has been computed in the caller prior to entering this stub. + __ b(DONE); + + __ bind(MATCH); + + // Crop the vector to find its location. + __ sve_brkb(pgtmp2, pgtmp1, pgtmp2, false /* isMerge */); + + // Extract the first different characters of each string. + __ sve_lasta(rscratch1, mode == LL ? __ B : __ H, pgtmp2, ztmp1); + __ sve_lasta(rscratch2, mode == LL ? __ B : __ H, pgtmp2, ztmp2); + + // Compute the difference of the first different characters. + __ sub(result, rscratch1, rscratch2); + + __ bind(DONE); + __ ret(lr); + + return entry; + } + // r0 = result // r1 = str1 // r2 = cnt1 @@ -5153,6 +5244,7 @@ class StubGenerator: public StubCodeGenerator { } void generate_compare_long_strings() { + if (UseSVE == 0) { StubRoutines::aarch64::_compare_long_string_LL = generate_compare_long_string_same_encoding(true); StubRoutines::aarch64::_compare_long_string_UU @@ -5161,6 +5253,16 @@ class StubGenerator: public StubCodeGenerator { = generate_compare_long_string_different_encoding(true); StubRoutines::aarch64::_compare_long_string_UL = generate_compare_long_string_different_encoding(false); + } else { + StubRoutines::aarch64::_compare_long_string_LL + = generate_compare_long_string_sve(LL); + StubRoutines::aarch64::_compare_long_string_UU + = generate_compare_long_string_sve(UU); + StubRoutines::aarch64::_compare_long_string_LU + = generate_compare_long_string_sve(LU); + StubRoutines::aarch64::_compare_long_string_UL + = generate_compare_long_string_sve(UL); + } } // R0 = result diff --git a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp index 4d6ce557d0ab0..3ec2db3b31381 100644 --- a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp +++ b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp @@ -720,11 +720,7 @@ void LIR_Assembler::mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, break; case T_ADDRESS: - if (UseCompressedClassPointers && addr->disp() == oopDesc::klass_offset_in_bytes()) { - __ ldr_u32(dest->as_pointer_register(), as_Address(addr)); - } else { - __ ldr(dest->as_pointer_register(), as_Address(addr)); - } + __ ldr(dest->as_pointer_register(), as_Address(addr)); break; case T_INT: @@ -2445,6 +2441,21 @@ void LIR_Assembler::emit_lock(LIR_OpLock* op) { __ bind(*op->stub()->continuation()); } +void LIR_Assembler::emit_load_klass(LIR_OpLoadKlass* op) { + Register obj = op->obj()->as_pointer_register(); + Register result = op->result_opr()->as_pointer_register(); + + CodeEmitInfo* info = op->info(); + if (info != NULL) { + add_debug_info_for_null_check_here(info); + } + + if (UseCompressedClassPointers) { // On 32 bit arm?? + __ ldr_u32(result, Address(obj, oopDesc::klass_offset_in_bytes())); + } else { + __ ldr(result, Address(obj, oopDesc::klass_offset_in_bytes())); + } +} void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) { ciMethod* method = op->profiled_method(); diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp index 831e2bdfe930d..23e03cb36e361 100644 --- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp @@ -812,12 +812,7 @@ int LIR_Assembler::load(Register base, int offset, LIR_Opr to_reg, BasicType typ case T_LONG : __ ld(to_reg->as_register_lo(), offset, base); break; case T_METADATA: __ ld(to_reg->as_register(), offset, base); break; case T_ADDRESS: - if (offset == oopDesc::klass_offset_in_bytes() && UseCompressedClassPointers) { - __ lwz(to_reg->as_register(), offset, base); - __ decode_klass_not_null(to_reg->as_register()); - } else { - __ ld(to_reg->as_register(), offset, base); - } + __ ld(to_reg->as_register(), offset, base); break; case T_ARRAY : // fall through case T_OBJECT: @@ -2732,6 +2727,26 @@ void LIR_Assembler::emit_lock(LIR_OpLock* op) { __ bind(*op->stub()->continuation()); } +void LIR_Assembler::emit_load_klass(LIR_OpLoadKlass* op) { + Register obj = op->obj()->as_pointer_register(); + Register result = op->result_opr()->as_pointer_register(); + + CodeEmitInfo* info = op->info(); + if (info != NULL) { + if (!os::zero_page_read_protected() || !ImplicitNullChecks) { + explicit_null_check(obj, info); + } else { + add_debug_info_for_null_check_here(info); + } + } + + if (UseCompressedClassPointers) { + __ lwz(result, oopDesc::klass_offset_in_bytes(), obj); + __ decode_klass_not_null(result); + } else { + __ ld(result, oopDesc::klass_offset_in_bytes(), obj); + } +} void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) { ciMethod* method = op->profiled_method(); diff --git a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp index 6971490e0681c..cb5903886caf5 100644 --- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp @@ -950,12 +950,7 @@ void LIR_Assembler::mem2reg(LIR_Opr src_opr, LIR_Opr dest, BasicType type, LIR_P } break; case T_ADDRESS: - if (UseCompressedClassPointers && addr->disp() == oopDesc::klass_offset_in_bytes()) { - __ z_llgf(dest->as_register(), disp_value, disp_reg, src); - __ decode_klass_not_null(dest->as_register()); - } else { - __ z_lg(dest->as_register(), disp_value, disp_reg, src); - } + __ z_lg(dest->as_register(), disp_value, disp_reg, src); break; case T_ARRAY : // fall through case T_OBJECT: @@ -2754,6 +2749,22 @@ void LIR_Assembler::emit_lock(LIR_OpLock* op) { __ bind(*op->stub()->continuation()); } +void LIR_Assembler::emit_load_klass(LIR_OpLoadKlass* op) { + Register obj = op->obj()->as_pointer_register(); + Register result = op->result_opr()->as_pointer_register(); + + CodeEmitInfo* info = op->info(); + if (info != NULL) { + add_debug_info_for_null_check_here(info); + } + + if (UseCompressedClassPointers) { + __ z_llgf(result, Address(obj, oopDesc::klass_offset_in_bytes())); + __ decode_klass_not_null(result); + } else { + __ z_lg(result, Address(obj, oopDesc::klass_offset_in_bytes())); + } +} void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) { ciMethod* method = op->profiled_method(); int bci = op->profiled_bci(); diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp index 971c2515017df..07f2762fa16f1 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp @@ -1184,7 +1184,6 @@ void LIR_Assembler::mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_Patch LIR_Address* addr = src->as_address_ptr(); Address from_addr = as_Address(addr); - Register tmp_load_klass = LP64_ONLY(rscratch1) NOT_LP64(noreg); if (addr->base()->type() == T_OBJECT) { __ verify_oop(addr->base()->as_pointer_register()); @@ -1257,11 +1256,7 @@ void LIR_Assembler::mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_Patch break; case T_ADDRESS: - if (UseCompressedClassPointers && addr->disp() == oopDesc::klass_offset_in_bytes()) { - __ movl(dest->as_register(), from_addr); - } else { - __ movptr(dest->as_register(), from_addr); - } + __ movptr(dest->as_register(), from_addr); break; case T_INT: __ movl(dest->as_register(), from_addr); @@ -1367,12 +1362,6 @@ void LIR_Assembler::mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_Patch if (!UseZGC) { __ verify_oop(dest->as_register()); } - } else if (type == T_ADDRESS && addr->disp() == oopDesc::klass_offset_in_bytes()) { -#ifdef _LP64 - if (UseCompressedClassPointers) { - __ decode_klass_not_null(dest->as_register(), tmp_load_klass); - } -#endif } } @@ -3528,6 +3517,23 @@ void LIR_Assembler::emit_lock(LIR_OpLock* op) { __ bind(*op->stub()->continuation()); } +void LIR_Assembler::emit_load_klass(LIR_OpLoadKlass* op) { + Register obj = op->obj()->as_pointer_register(); + Register result = op->result_opr()->as_pointer_register(); + + CodeEmitInfo* info = op->info(); + if (info != NULL) { + add_debug_info_for_null_check_here(info); + } + +#ifdef _LP64 + if (UseCompressedClassPointers) { + __ movl(result, Address(obj, oopDesc::klass_offset_in_bytes())); + __ decode_klass_not_null(result, rscratch1); + } else +#endif + __ movptr(result, Address(obj, oopDesc::klass_offset_in_bytes())); +} void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) { ciMethod* method = op->profiled_method(); diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index 5a8569dc6e0f2..3f6d5a44b0dba 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -6196,7 +6196,7 @@ instruct vshiftcnt(vec dst, rRegI cnt) %{ // Byte vector shift instruct vshiftB(vec dst, vec src, vec shift, vec tmp, rRegI scratch) %{ - predicate(Matcher::vector_length(n) <= 8 && VectorNode::is_vshift_cnt(n->in(2))); + predicate(Matcher::vector_length(n) <= 8 && !n->as_ShiftV()->is_var_shift()); match(Set dst ( LShiftVB src shift)); match(Set dst ( RShiftVB src shift)); match(Set dst (URShiftVB src shift)); @@ -6216,7 +6216,7 @@ instruct vshiftB(vec dst, vec src, vec shift, vec tmp, rRegI scratch) %{ %} instruct vshift16B(vec dst, vec src, vec shift, vec tmp1, vec tmp2, rRegI scratch) %{ - predicate(Matcher::vector_length(n) == 16 && VectorNode::is_vshift_cnt(n->in(2)) && + predicate(Matcher::vector_length(n) == 16 && !n->as_ShiftV()->is_var_shift() && UseAVX <= 1); match(Set dst ( LShiftVB src shift)); match(Set dst ( RShiftVB src shift)); @@ -6241,7 +6241,7 @@ instruct vshift16B(vec dst, vec src, vec shift, vec tmp1, vec tmp2, rRegI scratc %} instruct vshift16B_avx(vec dst, vec src, vec shift, vec tmp, rRegI scratch) %{ - predicate(Matcher::vector_length(n) == 16 && VectorNode::is_vshift_cnt(n->in(2)) && + predicate(Matcher::vector_length(n) == 16 && !n->as_ShiftV()->is_var_shift() && UseAVX > 1); match(Set dst ( LShiftVB src shift)); match(Set dst ( RShiftVB src shift)); @@ -6262,7 +6262,7 @@ instruct vshift16B_avx(vec dst, vec src, vec shift, vec tmp, rRegI scratch) %{ %} instruct vshift32B_avx(vec dst, vec src, vec shift, vec tmp, rRegI scratch) %{ - predicate(Matcher::vector_length(n) == 32 && VectorNode::is_vshift_cnt(n->in(2))); + predicate(Matcher::vector_length(n) == 32 && !n->as_ShiftV()->is_var_shift()); match(Set dst ( LShiftVB src shift)); match(Set dst ( RShiftVB src shift)); match(Set dst (URShiftVB src shift)); @@ -6287,7 +6287,7 @@ instruct vshift32B_avx(vec dst, vec src, vec shift, vec tmp, rRegI scratch) %{ %} instruct vshift64B_avx(vec dst, vec src, vec shift, vec tmp1, vec tmp2, rRegI scratch) %{ - predicate(Matcher::vector_length(n) == 64 && VectorNode::is_vshift_cnt(n->in(2))); + predicate(Matcher::vector_length(n) == 64 && !n->as_ShiftV()->is_var_shift()); match(Set dst ( LShiftVB src shift)); match(Set dst (RShiftVB src shift)); match(Set dst (URShiftVB src shift)); @@ -6320,7 +6320,7 @@ instruct vshift64B_avx(vec dst, vec src, vec shift, vec tmp1, vec tmp2, rRegI sc // unsigned values. // Shorts/Chars vector left shift instruct vshiftS(vec dst, vec src, vec shift) %{ - predicate(VectorNode::is_vshift_cnt(n->in(2))); + predicate(!n->as_ShiftV()->is_var_shift()); match(Set dst ( LShiftVS src shift)); match(Set dst ( RShiftVS src shift)); match(Set dst (URShiftVS src shift)); @@ -6351,7 +6351,7 @@ instruct vshiftS(vec dst, vec src, vec shift) %{ // Integers vector left shift instruct vshiftI(vec dst, vec src, vec shift) %{ - predicate(VectorNode::is_vshift_cnt(n->in(2))); + predicate(!n->as_ShiftV()->is_var_shift()); match(Set dst ( LShiftVI src shift)); match(Set dst ( RShiftVI src shift)); match(Set dst (URShiftVI src shift)); @@ -6405,7 +6405,7 @@ instruct vshiftI_imm(vec dst, vec src, immI8 shift) %{ // Longs vector shift instruct vshiftL(vec dst, vec src, vec shift) %{ - predicate(VectorNode::is_vshift_cnt(n->in(2))); + predicate(!n->as_ShiftV()->is_var_shift()); match(Set dst ( LShiftVL src shift)); match(Set dst (URShiftVL src shift)); effect(TEMP dst, USE src, USE shift); @@ -6446,7 +6446,7 @@ instruct vshiftL_imm(vec dst, vec src, immI8 shift) %{ // -------------------ArithmeticRightShift ----------------------------------- // Long vector arithmetic right shift instruct vshiftL_arith_reg(vec dst, vec src, vec shift, vec tmp, rRegI scratch) %{ - predicate(VectorNode::is_vshift_cnt(n->in(2)) && UseAVX <= 2); + predicate(!n->as_ShiftV()->is_var_shift() && UseAVX <= 2); match(Set dst (RShiftVL src shift)); effect(TEMP dst, TEMP tmp, TEMP scratch); format %{ "vshiftq $dst,$src,$shift" %} @@ -6475,7 +6475,7 @@ instruct vshiftL_arith_reg(vec dst, vec src, vec shift, vec tmp, rRegI scratch) %} instruct vshiftL_arith_reg_evex(vec dst, vec src, vec shift) %{ - predicate(VectorNode::is_vshift_cnt(n->in(2)) && UseAVX > 2); + predicate(!n->as_ShiftV()->is_var_shift() && UseAVX > 2); match(Set dst (RShiftVL src shift)); format %{ "vshiftq $dst,$src,$shift" %} ins_encode %{ @@ -6489,7 +6489,7 @@ instruct vshiftL_arith_reg_evex(vec dst, vec src, vec shift) %{ // Byte variable shift instruct vshift8B_var_nobw(vec dst, vec src, vec shift, vec vtmp, rRegP scratch) %{ predicate(Matcher::vector_length(n) <= 8 && - !VectorNode::is_vshift_cnt(n->in(2)) && + n->as_ShiftV()->is_var_shift() && !VM_Version::supports_avx512bw()); match(Set dst ( LShiftVB src shift)); match(Set dst ( RShiftVB src shift)); @@ -6509,7 +6509,7 @@ instruct vshift8B_var_nobw(vec dst, vec src, vec shift, vec vtmp, rRegP scratch) instruct vshift16B_var_nobw(vec dst, vec src, vec shift, vec vtmp1, vec vtmp2, rRegP scratch) %{ predicate(Matcher::vector_length(n) == 16 && - !VectorNode::is_vshift_cnt(n->in(2)) && + n->as_ShiftV()->is_var_shift() && !VM_Version::supports_avx512bw()); match(Set dst ( LShiftVB src shift)); match(Set dst ( RShiftVB src shift)); @@ -6537,7 +6537,7 @@ instruct vshift16B_var_nobw(vec dst, vec src, vec shift, vec vtmp1, vec vtmp2, r instruct vshift32B_var_nobw(vec dst, vec src, vec shift, vec vtmp1, vec vtmp2, vec vtmp3, vec vtmp4, rRegP scratch) %{ predicate(Matcher::vector_length(n) == 32 && - !VectorNode::is_vshift_cnt(n->in(2)) && + n->as_ShiftV()->is_var_shift() && !VM_Version::supports_avx512bw()); match(Set dst ( LShiftVB src shift)); match(Set dst ( RShiftVB src shift)); @@ -6573,7 +6573,7 @@ instruct vshift32B_var_nobw(vec dst, vec src, vec shift, vec vtmp1, vec vtmp2, v instruct vshiftB_var_evex_bw(vec dst, vec src, vec shift, vec vtmp, rRegP scratch) %{ predicate(Matcher::vector_length(n) <= 32 && - !VectorNode::is_vshift_cnt(n->in(2)) && + n->as_ShiftV()->is_var_shift() && VM_Version::supports_avx512bw()); match(Set dst ( LShiftVB src shift)); match(Set dst ( RShiftVB src shift)); @@ -6592,7 +6592,7 @@ instruct vshiftB_var_evex_bw(vec dst, vec src, vec shift, vec vtmp, rRegP scratc instruct vshift64B_var_evex_bw(vec dst, vec src, vec shift, vec vtmp1, vec vtmp2, rRegP scratch) %{ predicate(Matcher::vector_length(n) == 64 && - !VectorNode::is_vshift_cnt(n->in(2)) && + n->as_ShiftV()->is_var_shift() && VM_Version::supports_avx512bw()); match(Set dst ( LShiftVB src shift)); match(Set dst ( RShiftVB src shift)); @@ -6616,7 +6616,7 @@ instruct vshift64B_var_evex_bw(vec dst, vec src, vec shift, vec vtmp1, vec vtmp2 // Short variable shift instruct vshift8S_var_nobw(vec dst, vec src, vec shift, vec vtmp, rRegP scratch) %{ predicate(Matcher::vector_length(n) <= 8 && - !VectorNode::is_vshift_cnt(n->in(2)) && + n->as_ShiftV()->is_var_shift() && !VM_Version::supports_avx512bw()); match(Set dst ( LShiftVS src shift)); match(Set dst ( RShiftVS src shift)); @@ -6641,7 +6641,7 @@ instruct vshift8S_var_nobw(vec dst, vec src, vec shift, vec vtmp, rRegP scratch) instruct vshift16S_var_nobw(vec dst, vec src, vec shift, vec vtmp1, vec vtmp2, rRegP scratch) %{ predicate(Matcher::vector_length(n) == 16 && - !VectorNode::is_vshift_cnt(n->in(2)) && + n->as_ShiftV()->is_var_shift() && !VM_Version::supports_avx512bw()); match(Set dst ( LShiftVS src shift)); match(Set dst ( RShiftVS src shift)); @@ -6676,7 +6676,7 @@ instruct vshift16S_var_nobw(vec dst, vec src, vec shift, vec vtmp1, vec vtmp2, r %} instruct vshift16S_var_evex_bw(vec dst, vec src, vec shift) %{ - predicate(!VectorNode::is_vshift_cnt(n->in(2)) && + predicate(n->as_ShiftV()->is_var_shift() && VM_Version::supports_avx512bw()); match(Set dst ( LShiftVS src shift)); match(Set dst ( RShiftVS src shift)); @@ -6697,7 +6697,7 @@ instruct vshift16S_var_evex_bw(vec dst, vec src, vec shift) %{ //Integer variable shift instruct vshiftI_var(vec dst, vec src, vec shift) %{ - predicate(!VectorNode::is_vshift_cnt(n->in(2))); + predicate(n->as_ShiftV()->is_var_shift()); match(Set dst ( LShiftVI src shift)); match(Set dst ( RShiftVI src shift)); match(Set dst (URShiftVI src shift)); @@ -6714,7 +6714,7 @@ instruct vshiftI_var(vec dst, vec src, vec shift) %{ //Long variable shift instruct vshiftL_var(vec dst, vec src, vec shift) %{ - predicate(!VectorNode::is_vshift_cnt(n->in(2))); + predicate(n->as_ShiftV()->is_var_shift()); match(Set dst ( LShiftVL src shift)); match(Set dst (URShiftVL src shift)); format %{ "vector_varshift_long $dst,$src,$shift\t!" %} @@ -6731,7 +6731,7 @@ instruct vshiftL_var(vec dst, vec src, vec shift) %{ //Long variable right shift arithmetic instruct vshiftL_arith_var(vec dst, vec src, vec shift, vec vtmp) %{ predicate(Matcher::vector_length(n) <= 4 && - !VectorNode::is_vshift_cnt(n->in(2)) && + n->as_ShiftV()->is_var_shift() && UseAVX == 2); match(Set dst (RShiftVL src shift)); effect(TEMP dst, TEMP vtmp); @@ -6746,7 +6746,7 @@ instruct vshiftL_arith_var(vec dst, vec src, vec shift, vec vtmp) %{ %} instruct vshiftL_arith_var_evex(vec dst, vec src, vec shift) %{ - predicate(!VectorNode::is_vshift_cnt(n->in(2)) && + predicate(n->as_ShiftV()->is_var_shift() && UseAVX > 2); match(Set dst (RShiftVL src shift)); format %{ "vector_varfshift_long $dst,$src,$shift\t!" %} @@ -9033,6 +9033,7 @@ instruct vlshift_imm_masked(vec dst, immI8 shift, kReg mask) %{ %} instruct vlshift_reg_masked(vec dst, vec src2, kReg mask) %{ + predicate(!n->as_ShiftV()->is_var_shift()); match(Set dst (LShiftVS (Binary dst src2) mask)); match(Set dst (LShiftVI (Binary dst src2) mask)); match(Set dst (LShiftVL (Binary dst src2) mask)); @@ -9041,9 +9042,24 @@ instruct vlshift_reg_masked(vec dst, vec src2, kReg mask) %{ int vlen_enc = vector_length_encoding(this); BasicType bt = Matcher::vector_element_basic_type(this); int opc = this->ideal_Opcode(); - bool is_varshift = !VectorNode::is_vshift_cnt_opcode(in(2)->isa_Mach()->ideal_Opcode()); __ evmasked_op(opc, bt, $mask$$KRegister, $dst$$XMMRegister, - $dst$$XMMRegister, $src2$$XMMRegister, true, vlen_enc, is_varshift); + $dst$$XMMRegister, $src2$$XMMRegister, true, vlen_enc, false); + %} + ins_pipe( pipe_slow ); +%} + +instruct vlshiftv_reg_masked(vec dst, vec src2, kReg mask) %{ + predicate(n->as_ShiftV()->is_var_shift()); + match(Set dst (LShiftVS (Binary dst src2) mask)); + match(Set dst (LShiftVI (Binary dst src2) mask)); + match(Set dst (LShiftVL (Binary dst src2) mask)); + format %{ "vplshiftv_masked $dst, $dst, $src2, $mask\t! lshift masked operation" %} + ins_encode %{ + int vlen_enc = vector_length_encoding(this); + BasicType bt = Matcher::vector_element_basic_type(this); + int opc = this->ideal_Opcode(); + __ evmasked_op(opc, bt, $mask$$KRegister, $dst$$XMMRegister, + $dst$$XMMRegister, $src2$$XMMRegister, true, vlen_enc, true); %} ins_pipe( pipe_slow ); %} @@ -9079,6 +9095,7 @@ instruct vrshift_imm_masked(vec dst, immI8 shift, kReg mask) %{ %} instruct vrshift_reg_masked(vec dst, vec src2, kReg mask) %{ + predicate(!n->as_ShiftV()->is_var_shift()); match(Set dst (RShiftVS (Binary dst src2) mask)); match(Set dst (RShiftVI (Binary dst src2) mask)); match(Set dst (RShiftVL (Binary dst src2) mask)); @@ -9087,9 +9104,24 @@ instruct vrshift_reg_masked(vec dst, vec src2, kReg mask) %{ int vlen_enc = vector_length_encoding(this); BasicType bt = Matcher::vector_element_basic_type(this); int opc = this->ideal_Opcode(); - bool is_varshift = !VectorNode::is_vshift_cnt_opcode(in(2)->isa_Mach()->ideal_Opcode()); __ evmasked_op(opc, bt, $mask$$KRegister, $dst$$XMMRegister, - $dst$$XMMRegister, $src2$$XMMRegister, true, vlen_enc, is_varshift); + $dst$$XMMRegister, $src2$$XMMRegister, true, vlen_enc, false); + %} + ins_pipe( pipe_slow ); +%} + +instruct vrshiftv_reg_masked(vec dst, vec src2, kReg mask) %{ + predicate(n->as_ShiftV()->is_var_shift()); + match(Set dst (RShiftVS (Binary dst src2) mask)); + match(Set dst (RShiftVI (Binary dst src2) mask)); + match(Set dst (RShiftVL (Binary dst src2) mask)); + format %{ "vprshiftv_masked $dst, $dst, $src2, $mask\t! rshift masked operation" %} + ins_encode %{ + int vlen_enc = vector_length_encoding(this); + BasicType bt = Matcher::vector_element_basic_type(this); + int opc = this->ideal_Opcode(); + __ evmasked_op(opc, bt, $mask$$KRegister, $dst$$XMMRegister, + $dst$$XMMRegister, $src2$$XMMRegister, true, vlen_enc, true); %} ins_pipe( pipe_slow ); %} @@ -9125,6 +9157,7 @@ instruct vurshift_imm_masked(vec dst, immI8 shift, kReg mask) %{ %} instruct vurshift_reg_masked(vec dst, vec src2, kReg mask) %{ + predicate(!n->as_ShiftV()->is_var_shift()); match(Set dst (URShiftVS (Binary dst src2) mask)); match(Set dst (URShiftVI (Binary dst src2) mask)); match(Set dst (URShiftVL (Binary dst src2) mask)); @@ -9133,9 +9166,24 @@ instruct vurshift_reg_masked(vec dst, vec src2, kReg mask) %{ int vlen_enc = vector_length_encoding(this); BasicType bt = Matcher::vector_element_basic_type(this); int opc = this->ideal_Opcode(); - bool is_varshift = !VectorNode::is_vshift_cnt_opcode(in(2)->isa_Mach()->ideal_Opcode()); __ evmasked_op(opc, bt, $mask$$KRegister, $dst$$XMMRegister, - $dst$$XMMRegister, $src2$$XMMRegister, true, vlen_enc, is_varshift); + $dst$$XMMRegister, $src2$$XMMRegister, true, vlen_enc, false); + %} + ins_pipe( pipe_slow ); +%} + +instruct vurshiftv_reg_masked(vec dst, vec src2, kReg mask) %{ + predicate(n->as_ShiftV()->is_var_shift()); + match(Set dst (URShiftVS (Binary dst src2) mask)); + match(Set dst (URShiftVI (Binary dst src2) mask)); + match(Set dst (URShiftVL (Binary dst src2) mask)); + format %{ "vpurshiftv_masked $dst, $dst, $src2, $mask\t! urshift masked operation" %} + ins_encode %{ + int vlen_enc = vector_length_encoding(this); + BasicType bt = Matcher::vector_element_basic_type(this); + int opc = this->ideal_Opcode(); + __ evmasked_op(opc, bt, $mask$$KRegister, $dst$$XMMRegister, + $dst$$XMMRegister, $src2$$XMMRegister, true, vlen_enc, true); %} ins_pipe( pipe_slow ); %} diff --git a/src/hotspot/cpu/zero/globals_zero.hpp b/src/hotspot/cpu/zero/globals_zero.hpp index aa330925c5a18..208fc32940f09 100644 --- a/src/hotspot/cpu/zero/globals_zero.hpp +++ b/src/hotspot/cpu/zero/globals_zero.hpp @@ -69,8 +69,7 @@ define_pd_global(uintx, TypeProfileLevel, 0); define_pd_global(bool, PreserveFramePointer, false); -// No performance work done here yet. -define_pd_global(bool, CompactStrings, false); +define_pd_global(bool, CompactStrings, true); #define ARCH_FLAGS(develop, \ product, \ diff --git a/src/hotspot/share/c1/c1_IR.cpp b/src/hotspot/share/c1/c1_IR.cpp index e4b9d86dcc300..0b7f7a2b2c186 100644 --- a/src/hotspot/share/c1/c1_IR.cpp +++ b/src/hotspot/share/c1/c1_IR.cpp @@ -1266,7 +1266,7 @@ typedef GrowableArray BlockListList; class PredecessorValidator : public BlockClosure { private: - BlockListList* _predecessors; + BlockListList* _predecessors; // Each index i will hold predecessors of block with id i BlockList* _blocks; static int cmp(BlockBegin** a, BlockBegin** b) { @@ -1277,71 +1277,74 @@ class PredecessorValidator : public BlockClosure { PredecessorValidator(IR* hir) { ResourceMark rm; _predecessors = new BlockListList(BlockBegin::number_of_blocks(), BlockBegin::number_of_blocks(), NULL); - _blocks = new BlockList(); + _blocks = new BlockList(BlockBegin::number_of_blocks()); - int i; hir->start()->iterate_preorder(this); if (hir->code() != NULL) { assert(hir->code()->length() == _blocks->length(), "must match"); - for (i = 0; i < _blocks->length(); i++) { + for (int i = 0; i < _blocks->length(); i++) { assert(hir->code()->contains(_blocks->at(i)), "should be in both lists"); } } - for (i = 0; i < _blocks->length(); i++) { + for (int i = 0; i < _blocks->length(); i++) { BlockBegin* block = _blocks->at(i); - BlockList* preds = _predecessors->at(block->block_id()); - if (preds == NULL) { - assert(block->number_of_preds() == 0, "should be the same"); - continue; - } - - // clone the pred list so we can mutate it - BlockList* pred_copy = new BlockList(); - int j; - for (j = 0; j < block->number_of_preds(); j++) { - pred_copy->append(block->pred_at(j)); - } - // sort them in the same order - preds->sort(cmp); - pred_copy->sort(cmp); - int length = MIN2(preds->length(), block->number_of_preds()); - for (j = 0; j < block->number_of_preds(); j++) { - assert(preds->at(j) == pred_copy->at(j), "must match"); - } - - assert(preds->length() == block->number_of_preds(), "should be the same"); + verify_block_preds_against_collected_preds(block); } } virtual void block_do(BlockBegin* block) { _blocks->append(block); - BlockEnd* be = block->end(); - int n = be->number_of_sux(); - int i; - for (i = 0; i < n; i++) { - BlockBegin* sux = be->sux_at(i); - assert(!sux->is_set(BlockBegin::exception_entry_flag), "must not be xhandler"); - - BlockList* preds = _predecessors->at_grow(sux->block_id(), NULL); - if (preds == NULL) { - preds = new BlockList(); - _predecessors->at_put(sux->block_id(), preds); - } - preds->append(block); + verify_successor_xentry_flag(block); + collect_predecessors(block); + } + + private: + void verify_successor_xentry_flag(const BlockBegin* block) const { + for (int i = 0; i < block->end()->number_of_sux(); i++) { + assert(!block->end()->sux_at(i)->is_set(BlockBegin::exception_entry_flag), "must not be xhandler"); + } + for (int i = 0; i < block->number_of_exception_handlers(); i++) { + assert(block->exception_handler_at(i)->is_set(BlockBegin::exception_entry_flag), "must be xhandler"); } + } - n = block->number_of_exception_handlers(); - for (i = 0; i < n; i++) { - BlockBegin* sux = block->exception_handler_at(i); - assert(sux->is_set(BlockBegin::exception_entry_flag), "must be xhandler"); + void collect_predecessors(BlockBegin* block) { + for (int i = 0; i < block->end()->number_of_sux(); i++) { + collect_predecessor(block, block->end()->sux_at(i)); + } + for (int i = 0; i < block->number_of_exception_handlers(); i++) { + collect_predecessor(block, block->exception_handler_at(i)); + } + } - BlockList* preds = _predecessors->at_grow(sux->block_id(), NULL); - if (preds == NULL) { - preds = new BlockList(); - _predecessors->at_put(sux->block_id(), preds); - } - preds->append(block); + void collect_predecessor(BlockBegin* const pred, const BlockBegin* sux) { + BlockList* preds = _predecessors->at_grow(sux->block_id(), NULL); + if (preds == NULL) { + preds = new BlockList(); + _predecessors->at_put(sux->block_id(), preds); + } + preds->append(pred); + } + + void verify_block_preds_against_collected_preds(const BlockBegin* block) const { + BlockList* preds = _predecessors->at(block->block_id()); + if (preds == NULL) { + assert(block->number_of_preds() == 0, "should be the same"); + return; + } + assert(preds->length() == block->number_of_preds(), "should be the same"); + + // clone the pred list so we can mutate it + BlockList* pred_copy = new BlockList(); + for (int j = 0; j < block->number_of_preds(); j++) { + pred_copy->append(block->pred_at(j)); + } + // sort them in the same order + preds->sort(cmp); + pred_copy->sort(cmp); + for (int j = 0; j < block->number_of_preds(); j++) { + assert(preds->at(j) == pred_copy->at(j), "must match"); } } }; diff --git a/src/hotspot/share/c1/c1_Instruction.hpp b/src/hotspot/share/c1/c1_Instruction.hpp index 6adaae6f30f68..8d360b094b835 100644 --- a/src/hotspot/share/c1/c1_Instruction.hpp +++ b/src/hotspot/share/c1/c1_Instruction.hpp @@ -1998,14 +1998,6 @@ LEAF(If, BlockEnd) _cond = mirror(_cond); } - void swap_sux() { - assert(number_of_sux() == 2, "wrong number of successors"); - BlockList* s = sux(); - BlockBegin* t = s->at(0); s->at_put(0, s->at(1)); s->at_put(1, t); - _cond = negate(_cond); - set_flag(UnorderedIsTrueFlag, !check_flag(UnorderedIsTrueFlag)); - } - void set_should_profile(bool value) { set_flag(ProfileMDOFlag, value); } void set_profiled_method(ciMethod* method) { _profiled_method = method; } void set_profiled_bci(int bci) { _profiled_bci = bci; } diff --git a/src/hotspot/share/c1/c1_LIR.cpp b/src/hotspot/share/c1/c1_LIR.cpp index 4ab1d887e3ffe..c4e2b013387e9 100644 --- a/src/hotspot/share/c1/c1_LIR.cpp +++ b/src/hotspot/share/c1/c1_LIR.cpp @@ -880,6 +880,19 @@ void LIR_OpVisitState::visit(LIR_Op* op) { break; } +// LIR_OpLoadKlass + case lir_load_klass: + { + LIR_OpLoadKlass* opLoadKlass = op->as_OpLoadKlass(); + assert(opLoadKlass != NULL, "must be"); + + do_input(opLoadKlass->_obj); + do_output(opLoadKlass->_result); + if (opLoadKlass->_info) do_info(opLoadKlass->_info); + break; + } + + // LIR_OpProfileCall: case lir_profile_call: { assert(op->as_OpProfileCall() != NULL, "must be"); @@ -1049,6 +1062,10 @@ void LIR_OpLock::emit_code(LIR_Assembler* masm) { } } +void LIR_OpLoadKlass::emit_code(LIR_Assembler* masm) { + masm->emit_load_klass(this); +} + #ifdef ASSERT void LIR_OpAssert::emit_code(LIR_Assembler* masm) { masm->emit_assert(this); @@ -1970,6 +1987,11 @@ void LIR_OpLock::print_instr(outputStream* out) const { out->print("[lbl:" INTPTR_FORMAT "]", p2i(stub()->entry())); } +void LIR_OpLoadKlass::print_instr(outputStream* out) const { + obj()->print(out); out->print(" "); + result_opr()->print(out); out->print(" "); +} + #ifdef ASSERT void LIR_OpAssert::print_instr(outputStream* out) const { print_condition(out, condition()); out->print(" "); diff --git a/src/hotspot/share/c1/c1_LIR.hpp b/src/hotspot/share/c1/c1_LIR.hpp index 9334ae273d009..d5eb62aab1fe9 100644 --- a/src/hotspot/share/c1/c1_LIR.hpp +++ b/src/hotspot/share/c1/c1_LIR.hpp @@ -895,6 +895,7 @@ class LIR_OpUpdateCRC32; class LIR_OpLock; class LIR_OpTypeCheck; class LIR_OpCompareAndSwap; +class LIR_OpLoadKlass; class LIR_OpProfileCall; class LIR_OpProfileType; #ifdef ASSERT @@ -939,6 +940,7 @@ enum LIR_Code { , lir_roundfp , lir_safepoint , lir_unwind + , lir_load_klass , end_op1 , begin_op2 , lir_cmp @@ -1148,6 +1150,7 @@ class LIR_Op: public CompilationResourceObj { virtual LIR_OpUpdateCRC32* as_OpUpdateCRC32() { return NULL; } virtual LIR_OpTypeCheck* as_OpTypeCheck() { return NULL; } virtual LIR_OpCompareAndSwap* as_OpCompareAndSwap() { return NULL; } + virtual LIR_OpLoadKlass* as_OpLoadKlass() { return NULL; } virtual LIR_OpProfileCall* as_OpProfileCall() { return NULL; } virtual LIR_OpProfileType* as_OpProfileType() { return NULL; } #ifdef ASSERT @@ -1820,6 +1823,25 @@ class LIR_OpLock: public LIR_Op { void print_instr(outputStream* out) const PRODUCT_RETURN; }; +class LIR_OpLoadKlass: public LIR_Op { + friend class LIR_OpVisitState; + + private: + LIR_Opr _obj; + CodeEmitInfo* _info; + public: + LIR_OpLoadKlass(LIR_Opr obj, LIR_Opr result, CodeEmitInfo* info) + : LIR_Op(lir_load_klass, result, NULL) + , _obj(obj) + , _info(info) {} + + LIR_Opr obj() const { return _obj; } + CodeEmitInfo* info() const { return _info; } + + virtual LIR_OpLoadKlass* as_OpLoadKlass() { return this; } + virtual void emit_code(LIR_Assembler* masm); + void print_instr(outputStream* out) const PRODUCT_RETURN; +}; class LIR_OpDelay: public LIR_Op { friend class LIR_OpVisitState; @@ -2262,6 +2284,9 @@ class LIR_List: public CompilationResourceObj { void xadd(LIR_Opr src, LIR_Opr add, LIR_Opr res, LIR_Opr tmp) { append(new LIR_Op2(lir_xadd, src, add, res, tmp)); } void xchg(LIR_Opr src, LIR_Opr set, LIR_Opr res, LIR_Opr tmp) { append(new LIR_Op2(lir_xchg, src, set, res, tmp)); } + + void load_klass(LIR_Opr obj, LIR_Opr result, CodeEmitInfo* info) { append(new LIR_OpLoadKlass(obj, result, info)); } + #ifdef ASSERT void lir_assert(LIR_Condition condition, LIR_Opr opr1, LIR_Opr opr2, const char* msg, bool halt) { append(new LIR_OpAssert(condition, opr1, opr2, msg, halt)); } #endif diff --git a/src/hotspot/share/c1/c1_LIRAssembler.hpp b/src/hotspot/share/c1/c1_LIRAssembler.hpp index 683e921846242..f27ade60bae28 100644 --- a/src/hotspot/share/c1/c1_LIRAssembler.hpp +++ b/src/hotspot/share/c1/c1_LIRAssembler.hpp @@ -197,6 +197,7 @@ class LIR_Assembler: public CompilationResourceObj { void emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, Label* failure, Label* obj_is_null); void emit_compare_and_swap(LIR_OpCompareAndSwap* op); void emit_lock(LIR_OpLock* op); + void emit_load_klass(LIR_OpLoadKlass* op); void emit_call(LIR_OpJavaCall* op); void emit_rtcall(LIR_OpRTCall* op); void emit_profile_call(LIR_OpProfileCall* op); diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp index bca42cd7cf56e..05aa3587ee2e2 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.cpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp @@ -1231,13 +1231,17 @@ void LIRGenerator::do_isInstance(Intrinsic* x) { __ move(call_result, result); } +void LIRGenerator::load_klass(LIR_Opr obj, LIR_Opr klass, CodeEmitInfo* null_check_info) { + __ load_klass(obj, klass, null_check_info); +} + // Example: object.getClass () void LIRGenerator::do_getClass(Intrinsic* x) { assert(x->number_of_arguments() == 1, "wrong type"); LIRItem rcvr(x->argument_at(0), this); rcvr.load_item(); - LIR_Opr temp = new_register(T_METADATA); + LIR_Opr temp = new_register(T_ADDRESS); LIR_Opr result = rlock_result(x); // need to perform the null check on the rcvr @@ -1246,10 +1250,9 @@ void LIRGenerator::do_getClass(Intrinsic* x) { info = state_for(x); } - // FIXME T_ADDRESS should actually be T_METADATA but it can't because the - // meaning of these two is mixed up (see JDK-8026837). - __ move(new LIR_Address(rcvr.result(), oopDesc::klass_offset_in_bytes(), T_ADDRESS), temp, info); - __ move_wide(new LIR_Address(temp, in_bytes(Klass::java_mirror_offset()), T_ADDRESS), temp); + LIR_Opr klass = new_register(T_METADATA); + load_klass(rcvr.result(), klass, info); + __ move_wide(new LIR_Address(klass, in_bytes(Klass::java_mirror_offset()), T_ADDRESS), temp); // mirror = ((OopHandle)mirror)->resolve(); access_load(IN_NATIVE, T_OBJECT, LIR_OprFact::address(new LIR_Address(temp, T_OBJECT)), result); @@ -1322,7 +1325,7 @@ void LIRGenerator::do_getObjectSize(Intrinsic* x) { value.load_item(); LIR_Opr klass = new_register(T_METADATA); - __ move(new LIR_Address(value.result(), oopDesc::klass_offset_in_bytes(), T_ADDRESS), klass, NULL); + load_klass(value.result(), klass, NULL); LIR_Opr layout = new_register(T_INT); __ move(new LIR_Address(klass, in_bytes(Klass::layout_helper_offset()), T_INT), layout); @@ -3572,7 +3575,7 @@ LIR_Opr LIRGenerator::mask_boolean(LIR_Opr array, LIR_Opr value, CodeEmitInfo*& __ logical_and(value, LIR_OprFact::intConst(1), value_fixed); } LIR_Opr klass = new_register(T_METADATA); - __ move(new LIR_Address(array, oopDesc::klass_offset_in_bytes(), T_ADDRESS), klass, null_check_info); + load_klass(array, klass, null_check_info); null_check_info = NULL; LIR_Opr layout = new_register(T_INT); __ move(new LIR_Address(klass, in_bytes(Klass::layout_helper_offset()), T_INT), layout); diff --git a/src/hotspot/share/c1/c1_LIRGenerator.hpp b/src/hotspot/share/c1/c1_LIRGenerator.hpp index ffe7108c34e57..2c26e7714fdaf 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.hpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.hpp @@ -239,6 +239,8 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure { void move_to_phi(PhiResolver* resolver, Value cur_val, Value sux_val); void move_to_phi(ValueStack* cur_state); + void load_klass(LIR_Opr obj, LIR_Opr klass, CodeEmitInfo* null_check_info); + // platform dependent LIR_Opr getThreadPointer(); diff --git a/src/hotspot/share/c1/c1_Optimizer.cpp b/src/hotspot/share/c1/c1_Optimizer.cpp index e5c5061e334a7..8e32b813ef797 100644 --- a/src/hotspot/share/c1/c1_Optimizer.cpp +++ b/src/hotspot/share/c1/c1_Optimizer.cpp @@ -336,140 +336,135 @@ class BlockMerger: public BlockClosure { bool try_merge(BlockBegin* block) { BlockEnd* end = block->end(); - if (end->as_Goto() != NULL) { - assert(end->number_of_sux() == 1, "end must have exactly one successor"); - // Note: It would be sufficient to check for the number of successors (= 1) - // in order to decide if this block can be merged potentially. That - // would then also include switch statements w/ only a default case. - // However, in that case we would need to make sure the switch tag - // expression is executed if it can produce observable side effects. - // We should probably have the canonicalizer simplifying such switch - // statements and then we are sure we don't miss these merge opportunities - // here (was bug - gri 7/7/99). - BlockBegin* sux = end->default_sux(); - if (sux->number_of_preds() == 1 && !sux->is_entry_block() && !end->is_safepoint()) { - // merge the two blocks + if (end->as_Goto() == NULL) return false; + + assert(end->number_of_sux() == 1, "end must have exactly one successor"); + // Note: It would be sufficient to check for the number of successors (= 1) + // in order to decide if this block can be merged potentially. That + // would then also include switch statements w/ only a default case. + // However, in that case we would need to make sure the switch tag + // expression is executed if it can produce observable side effects. + // We should probably have the canonicalizer simplifying such switch + // statements and then we are sure we don't miss these merge opportunities + // here (was bug - gri 7/7/99). + BlockBegin* sux = end->default_sux(); + if (sux->number_of_preds() != 1 || sux->is_entry_block() || end->is_safepoint()) return false; + // merge the two blocks #ifdef ASSERT - // verify that state at the end of block and at the beginning of sux are equal - // no phi functions must be present at beginning of sux - ValueStack* sux_state = sux->state(); - ValueStack* end_state = end->state(); - - assert(end_state->scope() == sux_state->scope(), "scopes must match"); - assert(end_state->stack_size() == sux_state->stack_size(), "stack not equal"); - assert(end_state->locals_size() == sux_state->locals_size(), "locals not equal"); - - int index; - Value sux_value; - for_each_stack_value(sux_state, index, sux_value) { - assert(sux_value == end_state->stack_at(index), "stack not equal"); - } - for_each_local_value(sux_state, index, sux_value) { - Phi* sux_phi = sux_value->as_Phi(); - if (sux_phi != NULL && sux_phi->is_illegal()) continue; - assert(sux_value == end_state->local_at(index), "locals not equal"); - } - assert(sux_state->caller_state() == end_state->caller_state(), "caller not equal"); + // verify that state at the end of block and at the beginning of sux are equal + // no phi functions must be present at beginning of sux + ValueStack* sux_state = sux->state(); + ValueStack* end_state = end->state(); + + assert(end_state->scope() == sux_state->scope(), "scopes must match"); + assert(end_state->stack_size() == sux_state->stack_size(), "stack not equal"); + assert(end_state->locals_size() == sux_state->locals_size(), "locals not equal"); + + int index; + Value sux_value; + for_each_stack_value(sux_state, index, sux_value) { + assert(sux_value == end_state->stack_at(index), "stack not equal"); + } + for_each_local_value(sux_state, index, sux_value) { + Phi* sux_phi = sux_value->as_Phi(); + if (sux_phi != NULL && sux_phi->is_illegal()) continue; + assert(sux_value == end_state->local_at(index), "locals not equal"); + } + assert(sux_state->caller_state() == end_state->caller_state(), "caller not equal"); #endif - // find instruction before end & append first instruction of sux block - Instruction* prev = end->prev(); - Instruction* next = sux->next(); - assert(prev->as_BlockEnd() == NULL, "must not be a BlockEnd"); - prev->set_next(next); - prev->fixup_block_pointers(); - sux->disconnect_from_graph(); - block->set_end(sux->end()); - // add exception handlers of deleted block, if any - for (int k = 0; k < sux->number_of_exception_handlers(); k++) { - BlockBegin* xhandler = sux->exception_handler_at(k); - block->add_exception_handler(xhandler); - - // also substitute predecessor of exception handler - assert(xhandler->is_predecessor(sux), "missing predecessor"); - xhandler->remove_predecessor(sux); - if (!xhandler->is_predecessor(block)) { - xhandler->add_predecessor(block); - } - } + // find instruction before end & append first instruction of sux block + Instruction* prev = end->prev(); + Instruction* next = sux->next(); + assert(prev->as_BlockEnd() == NULL, "must not be a BlockEnd"); + prev->set_next(next); + prev->fixup_block_pointers(); + sux->disconnect_from_graph(); + block->set_end(sux->end()); + // add exception handlers of deleted block, if any + for (int k = 0; k < sux->number_of_exception_handlers(); k++) { + BlockBegin* xhandler = sux->exception_handler_at(k); + block->add_exception_handler(xhandler); - // debugging output - _merge_count++; - if (PrintBlockElimination) { - tty->print_cr("%d. merged B%d & B%d (stack size = %d)", - _merge_count, block->block_id(), sux->block_id(), sux->state()->stack_size()); - } + // also substitute predecessor of exception handler + assert(xhandler->is_predecessor(sux), "missing predecessor"); + xhandler->remove_predecessor(sux); + if (!xhandler->is_predecessor(block)) { + xhandler->add_predecessor(block); + } + } - _hir->verify(); - - If* if_ = block->end()->as_If(); - if (if_) { - IfOp* ifop = if_->x()->as_IfOp(); - Constant* con = if_->y()->as_Constant(); - bool swapped = false; - if (!con || !ifop) { - ifop = if_->y()->as_IfOp(); - con = if_->x()->as_Constant(); - swapped = true; + // debugging output + _merge_count++; + if (PrintBlockElimination) { + tty->print_cr("%d. merged B%d & B%d (stack size = %d)", + _merge_count, block->block_id(), sux->block_id(), sux->state()->stack_size()); + } + + _hir->verify(); + + If* if_ = block->end()->as_If(); + if (if_) { + IfOp* ifop = if_->x()->as_IfOp(); + Constant* con = if_->y()->as_Constant(); + bool swapped = false; + if (!con || !ifop) { + ifop = if_->y()->as_IfOp(); + con = if_->x()->as_Constant(); + swapped = true; + } + if (con && ifop) { + Constant* tval = ifop->tval()->as_Constant(); + Constant* fval = ifop->fval()->as_Constant(); + if (tval && fval) { + // Find the instruction before if_, starting with ifop. + // When if_ and ifop are not in the same block, prev + // becomes NULL In such (rare) cases it is not + // profitable to perform the optimization. + Value prev = ifop; + while (prev != NULL && prev->next() != if_) { + prev = prev->next(); } - if (con && ifop) { - Constant* tval = ifop->tval()->as_Constant(); - Constant* fval = ifop->fval()->as_Constant(); - if (tval && fval) { - // Find the instruction before if_, starting with ifop. - // When if_ and ifop are not in the same block, prev - // becomes NULL In such (rare) cases it is not - // profitable to perform the optimization. - Value prev = ifop; - while (prev != NULL && prev->next() != if_) { - prev = prev->next(); - } - if (prev != NULL) { - Instruction::Condition cond = if_->cond(); - BlockBegin* tsux = if_->tsux(); - BlockBegin* fsux = if_->fsux(); - if (swapped) { - cond = Instruction::mirror(cond); - } - - BlockBegin* tblock = tval->compare(cond, con, tsux, fsux); - BlockBegin* fblock = fval->compare(cond, con, tsux, fsux); - if (tblock != fblock && !if_->is_safepoint()) { - If* newif = new If(ifop->x(), ifop->cond(), false, ifop->y(), - tblock, fblock, if_->state_before(), if_->is_safepoint()); - newif->set_state(if_->state()->copy()); - - assert(prev->next() == if_, "must be guaranteed by above search"); - NOT_PRODUCT(newif->set_printable_bci(if_->printable_bci())); - prev->set_next(newif); - block->set_end(newif); - - _merge_count++; - if (PrintBlockElimination) { - tty->print_cr("%d. replaced If and IfOp at end of B%d with single If", _merge_count, block->block_id()); - } - - _hir->verify(); - } + if (prev != NULL) { + Instruction::Condition cond = if_->cond(); + BlockBegin* tsux = if_->tsux(); + BlockBegin* fsux = if_->fsux(); + if (swapped) { + cond = Instruction::mirror(cond); + } + + BlockBegin* tblock = tval->compare(cond, con, tsux, fsux); + BlockBegin* fblock = fval->compare(cond, con, tsux, fsux); + if (tblock != fblock && !if_->is_safepoint()) { + If* newif = new If(ifop->x(), ifop->cond(), false, ifop->y(), + tblock, fblock, if_->state_before(), if_->is_safepoint()); + newif->set_state(if_->state()->copy()); + + assert(prev->next() == if_, "must be guaranteed by above search"); + NOT_PRODUCT(newif->set_printable_bci(if_->printable_bci())); + prev->set_next(newif); + block->set_end(newif); + + _merge_count++; + if (PrintBlockElimination) { + tty->print_cr("%d. replaced If and IfOp at end of B%d with single If", _merge_count, block->block_id()); } + + _hir->verify(); } } } - - return true; } } - return false; + + return true; } virtual void block_do(BlockBegin* block) { - _hir->verify(); // repeat since the same block may merge again - while (try_merge(block)) { - _hir->verify(); - } + while (try_merge(block)) ; } }; diff --git a/src/hotspot/share/ci/ciEnv.cpp b/src/hotspot/share/ci/ciEnv.cpp index 7fae95ae63294..e29b56a3f277c 100644 --- a/src/hotspot/share/ci/ciEnv.cpp +++ b/src/hotspot/share/ci/ciEnv.cpp @@ -1654,9 +1654,11 @@ void ciEnv::dump_replay_data_helper(outputStream* out) { GrowableArray* objects = _factory->get_ci_metadata(); out->print_cr("# %d ciObject found", objects->length()); + // The very first entry is the InstanceKlass of the root method of the current compilation in order to get the right // protection domain to load subsequent classes during replay compilation. - out->print_cr("instanceKlass %s", CURRENT_ENV->replay_name(task()->method()->method_holder())); + ciInstanceKlass::dump_replay_instanceKlass(out, task()->method()->method_holder()); + for (int i = 0; i < objects->length(); i++) { objects->at(i)->dump_replay_data(out); } diff --git a/src/hotspot/share/ci/ciInstanceKlass.cpp b/src/hotspot/share/ci/ciInstanceKlass.cpp index 88c475d93d812..8b2806dbf9ed4 100644 --- a/src/hotspot/share/ci/ciInstanceKlass.cpp +++ b/src/hotspot/share/ci/ciInstanceKlass.cpp @@ -732,6 +732,19 @@ const char *ciInstanceKlass::replay_name() const { return CURRENT_ENV->replay_name(get_instanceKlass()); } +void ciInstanceKlass::dump_replay_instanceKlass(outputStream* out, InstanceKlass* ik) { + if (ik->is_hidden()) { + const char *name = CURRENT_ENV->dyno_name(ik); + if (name != NULL) { + out->print_cr("instanceKlass %s # %s", name, ik->name()->as_quoted_ascii()); + } else { + out->print_cr("# instanceKlass %s", ik->name()->as_quoted_ascii()); + } + } else { + out->print_cr("instanceKlass %s", ik->name()->as_quoted_ascii()); + } +} + void ciInstanceKlass::dump_replay_data(outputStream* out) { ResourceMark rm; @@ -743,16 +756,7 @@ void ciInstanceKlass::dump_replay_data(outputStream* out) { while (sub != NULL) { if (sub->is_instance_klass()) { InstanceKlass *isub = InstanceKlass::cast(sub); - if (isub->is_hidden()) { - const char *name = CURRENT_ENV->dyno_name(isub); - if (name != NULL) { - out->print_cr("instanceKlass %s # %s", name, sub->name()->as_quoted_ascii()); - } else { - out->print_cr("# instanceKlass %s", sub->name()->as_quoted_ascii()); - } - } else { - out->print_cr("instanceKlass %s", sub->name()->as_quoted_ascii()); - } + dump_replay_instanceKlass(out, isub); } sub = sub->next_sibling(); } diff --git a/src/hotspot/share/ci/ciInstanceKlass.hpp b/src/hotspot/share/ci/ciInstanceKlass.hpp index 3e79198eddbe1..9afee115488e7 100644 --- a/src/hotspot/share/ci/ciInstanceKlass.hpp +++ b/src/hotspot/share/ci/ciInstanceKlass.hpp @@ -293,6 +293,9 @@ class ciInstanceKlass : public ciKlass { // Dump the current state of this klass for compilation replay. virtual void dump_replay_data(outputStream* out); + static void dump_replay_instanceKlass(outputStream* out, InstanceKlass* ik); + + // Return stable class name suitable for replay file. const char *replay_name() const; diff --git a/src/hotspot/share/ci/ciReplay.cpp b/src/hotspot/share/ci/ciReplay.cpp index 04b9b0a83f1fd..e630278e64976 100644 --- a/src/hotspot/share/ci/ciReplay.cpp +++ b/src/hotspot/share/ci/ciReplay.cpp @@ -103,6 +103,7 @@ typedef struct _ciInlineRecord { int _inline_depth; int _inline_bci; + bool _inline_late; } ciInlineRecord; class CompileReplay; @@ -720,7 +721,7 @@ class CompileReplay : public StackObj { return NULL; } - // compile inline ( )* + // compile inline ( )* void process_compile(TRAPS) { Method* method = parse_method(CHECK); if (had_error()) return; @@ -762,11 +763,19 @@ class CompileReplay : public StackObj { if (had_error()) { break; } + int inline_late = 0; + if (_version >= 2) { + inline_late = parse_int("inline_late"); + if (had_error()) { + break; + } + } + Method* inl_method = parse_method(CHECK); if (had_error()) { break; } - new_ciInlineRecord(inl_method, bci, depth); + new_ciInlineRecord(inl_method, bci, depth, inline_late); } } if (_imethod != NULL) { @@ -1227,13 +1236,14 @@ class CompileReplay : public StackObj { } // Create and initialize a record for a ciInlineRecord - ciInlineRecord* new_ciInlineRecord(Method* method, int bci, int depth) { + ciInlineRecord* new_ciInlineRecord(Method* method, int bci, int depth, int inline_late) { ciInlineRecord* rec = NEW_RESOURCE_OBJ(ciInlineRecord); rec->_klass_name = method->method_holder()->name()->as_utf8(); rec->_method_name = method->name()->as_utf8(); rec->_signature = method->signature()->as_utf8(); rec->_inline_bci = bci; rec->_inline_depth = depth; + rec->_inline_late = inline_late; _ci_inline_records->append(rec); return rec; } @@ -1470,23 +1480,33 @@ bool ciReplay::should_not_inline(ciMethod* method) { return replay_state->find_ciMethodRecord(method->get_Method()) == NULL; } -bool ciReplay::should_inline(void* data, ciMethod* method, int bci, int inline_depth) { +bool ciReplay::should_inline(void* data, ciMethod* method, int bci, int inline_depth, bool& should_delay) { if (data != NULL) { - GrowableArray* records = (GrowableArray*)data; + GrowableArray* records = (GrowableArray*)data; VM_ENTRY_MARK; // Inline record are ordered by bci and depth. - return CompileReplay::find_ciInlineRecord(records, method->get_Method(), bci, inline_depth) != NULL; + ciInlineRecord* record = CompileReplay::find_ciInlineRecord(records, method->get_Method(), bci, inline_depth); + if (record == NULL) { + return false; + } + should_delay = record->_inline_late; + return true; } else if (replay_state != NULL) { VM_ENTRY_MARK; // Inline record are ordered by bci and depth. - return replay_state->find_ciInlineRecord(method->get_Method(), bci, inline_depth) != NULL; + ciInlineRecord* record = replay_state->find_ciInlineRecord(method->get_Method(), bci, inline_depth); + if (record == NULL) { + return false; + } + should_delay = record->_inline_late; + return true; } return false; } bool ciReplay::should_not_inline(void* data, ciMethod* method, int bci, int inline_depth) { if (data != NULL) { - GrowableArray* records = (GrowableArray*)data; + GrowableArray* records = (GrowableArray*)data; VM_ENTRY_MARK; // Inline record are ordered by bci and depth. return CompileReplay::find_ciInlineRecord(records, method->get_Method(), bci, inline_depth) == NULL; diff --git a/src/hotspot/share/ci/ciReplay.hpp b/src/hotspot/share/ci/ciReplay.hpp index 187f47497bdda..e835bb907ba9b 100644 --- a/src/hotspot/share/ci/ciReplay.hpp +++ b/src/hotspot/share/ci/ciReplay.hpp @@ -121,7 +121,7 @@ class ciReplay { static bool is_loaded(Method* method); static bool should_not_inline(ciMethod* method); - static bool should_inline(void* data, ciMethod* method, int bci, int inline_depth); + static bool should_inline(void* data, ciMethod* method, int bci, int inline_depth, bool& should_delay); static bool should_not_inline(void* data, ciMethod* method, int bci, int inline_depth); #endif @@ -135,6 +135,7 @@ class ciReplay { // 0: legacy (no version number) // 1: first instanceKlass sets protection domain (8275868) // replace current_mileage with invocation_count (8276095) -#define REPLAY_VERSION 1 // current version, bump up for incompatible changes +// 2: incremental inlining support (8254108) +#define REPLAY_VERSION 2 // current version, bump up for incompatible changes #endif // SHARE_CI_CIREPLAY_HPP diff --git a/src/hotspot/share/classfile/altHashing.cpp b/src/hotspot/share/classfile/altHashing.cpp index d0672138668f9..98c5502fc1fdd 100644 --- a/src/hotspot/share/classfile/altHashing.cpp +++ b/src/hotspot/share/classfile/altHashing.cpp @@ -26,18 +26,23 @@ * halfsiphash code adapted from reference implementation * (https://github.com/veorq/SipHash/blob/master/halfsiphash.c) * which is distributed with the following copyright: - * - * SipHash reference C implementation - * - * Copyright (c) 2016 Jean-Philippe Aumasson - * - * To the extent possible under law, the author(s) have dedicated all copyright - * and related and neighboring rights to this software to the public domain - * worldwide. This software is distributed without any warranty. - * - * You should have received a copy of the CC0 Public Domain Dedication along - * with this software. If not, see - * . + */ + +/* + SipHash reference C implementation + + Copyright (c) 2012-2021 Jean-Philippe Aumasson + + Copyright (c) 2012-2014 Daniel J. Bernstein + + To the extent possible under law, the author(s) have dedicated all copyright + and related and neighboring rights to this software to the public domain + worldwide. This software is distributed without any warranty. + + You should have received a copy of the CC0 Public Domain Dedication along + with + this software. If not, see + . */ #include "precompiled.hpp" @@ -135,7 +140,9 @@ static uint64_t halfsiphash_finish64(uint32_t v[4], int rounds) { } // HalfSipHash-2-4 (32-bit output) for Symbols -uint32_t AltHashing::halfsiphash_32(uint64_t seed, const uint8_t* data, int len) { +uint32_t AltHashing::halfsiphash_32(uint64_t seed, const void* in, int len) { + + const unsigned char* data = (const unsigned char*)in; uint32_t v[4]; uint32_t newdata; int off = 0; diff --git a/src/hotspot/share/classfile/altHashing.hpp b/src/hotspot/share/classfile/altHashing.hpp index e1726ae5152b7..f2fc52410d163 100644 --- a/src/hotspot/share/classfile/altHashing.hpp +++ b/src/hotspot/share/classfile/altHashing.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,7 +43,7 @@ class AltHashing : AllStatic { static uint64_t compute_seed(); // For Symbols - static uint32_t halfsiphash_32(uint64_t seed, const uint8_t* data, int len); + static uint32_t halfsiphash_32(uint64_t seed, const void* in, int len); // For Strings static uint32_t halfsiphash_32(uint64_t seed, const uint16_t* data, int len); }; diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index 6357019adb775..d8dfcc418cc2f 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -351,7 +351,6 @@ /* Foreign API Support */ \ template(jdk_internal_invoke_NativeEntryPoint, "jdk/internal/invoke/NativeEntryPoint") \ template(jdk_internal_invoke_NativeEntryPoint_signature, "Ljdk/internal/invoke/NativeEntryPoint;") \ - template(jdk_incubator_foreign_MemoryAccess, "jdk/incubator/foreign/MemoryAccess") \ \ /* Support for JVMCI */ \ JVMCI_VM_SYMBOLS_DO(template, do_alias) \ diff --git a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp index fbb295b4c4d9e..eab2751c3b03e 100644 --- a/src/hotspot/share/compiler/compileBroker.cpp +++ b/src/hotspot/share/compiler/compileBroker.cpp @@ -411,6 +411,7 @@ void CompileQueue::free_all() { CompileTask::free(current); } _first = NULL; + _last = NULL; // Wake up all threads that block on the queue. MethodCompileQueue_lock->notify_all(); diff --git a/src/hotspot/share/gc/g1/g1Arguments.cpp b/src/hotspot/share/gc/g1/g1Arguments.cpp index 6750e61a31278..1af2aebc7d29d 100644 --- a/src/hotspot/share/gc/g1/g1Arguments.cpp +++ b/src/hotspot/share/gc/g1/g1Arguments.cpp @@ -135,8 +135,8 @@ void G1Arguments::initialize_card_set_configuration() { uint region_size_log_mb = (uint)MAX2(HeapRegion::LogOfHRGrainBytes - LOG_M, 0); if (FLAG_IS_DEFAULT(G1RemSetArrayOfCardsEntries)) { - uint num_cards_in_inline_ptr = G1CardSetConfiguration::num_cards_in_inline_ptr(HeapRegion::LogOfHRGrainBytes - CardTable::card_shift); - FLAG_SET_ERGO(G1RemSetArrayOfCardsEntries, MAX2(num_cards_in_inline_ptr * 2, + uint max_cards_in_inline_ptr = G1CardSetConfiguration::max_cards_in_inline_ptr(HeapRegion::LogOfHRGrainBytes - CardTable::card_shift); + FLAG_SET_ERGO(G1RemSetArrayOfCardsEntries, MAX2(max_cards_in_inline_ptr * 2, G1RemSetArrayOfCardsEntriesBase * (1u << (region_size_log_mb + 1)))); } diff --git a/src/hotspot/share/gc/g1/g1CardSet.cpp b/src/hotspot/share/gc/g1/g1CardSet.cpp index e9c986ebc5315..370e6a06dca19 100644 --- a/src/hotspot/share/gc/g1/g1CardSet.cpp +++ b/src/hotspot/share/gc/g1/g1CardSet.cpp @@ -45,40 +45,40 @@ G1CardSet::CardSetPtr G1CardSet::FullCardSet = (G1CardSet::CardSetPtr)-1; static uint default_log2_card_region_per_region() { - uint log2_card_region_per_heap_region = 0; + uint log2_card_regions_per_heap_region = 0; const uint card_container_limit = G1CardSetContainer::LogCardsPerRegionLimit; if (card_container_limit < (uint)HeapRegion::LogCardsPerRegion) { - log2_card_region_per_heap_region = (uint)HeapRegion::LogCardsPerRegion - card_container_limit; + log2_card_regions_per_heap_region = (uint)HeapRegion::LogCardsPerRegion - card_container_limit; } - return log2_card_region_per_heap_region; + return log2_card_regions_per_heap_region; } G1CardSetConfiguration::G1CardSetConfiguration() : G1CardSetConfiguration(HeapRegion::LogCardsPerRegion, /* inline_ptr_bits_per_card */ - G1RemSetArrayOfCardsEntries, /* num_cards_in_array */ + G1RemSetArrayOfCardsEntries, /* max_cards_in_array */ (double)G1RemSetCoarsenHowlBitmapToHowlFullPercent / 100, /* cards_in_bitmap_threshold_percent */ G1RemSetHowlNumBuckets, /* num_buckets_in_howl */ (double)G1RemSetCoarsenHowlToFullPercent / 100, /* cards_in_howl_threshold_percent */ (uint)HeapRegion::CardsPerRegion, /* max_cards_in_cardset */ default_log2_card_region_per_region()) /* log2_card_region_per_region */ { - assert((_log2_card_region_per_heap_region + _log2_cards_per_card_region) == (uint)HeapRegion::LogCardsPerRegion, + assert((_log2_card_regions_per_heap_region + _log2_cards_per_card_region) == (uint)HeapRegion::LogCardsPerRegion, "inconsistent heap region virtualization setup"); } -G1CardSetConfiguration::G1CardSetConfiguration(uint num_cards_in_array, +G1CardSetConfiguration::G1CardSetConfiguration(uint max_cards_in_array, double cards_in_bitmap_threshold_percent, uint max_buckets_in_howl, double cards_in_howl_threshold_percent, uint max_cards_in_card_set, uint log2_card_region_per_region) : G1CardSetConfiguration(log2i_exact(max_cards_in_card_set), /* inline_ptr_bits_per_card */ - num_cards_in_array, /* num_cards_in_array */ + max_cards_in_array, /* max_cards_in_array */ cards_in_bitmap_threshold_percent, /* cards_in_bitmap_threshold_percent */ G1CardSetHowl::num_buckets(max_cards_in_card_set, /* num_buckets_in_howl */ - num_cards_in_array, + max_cards_in_array, max_buckets_in_howl), cards_in_howl_threshold_percent, /* cards_in_howl_threshold_percent */ max_cards_in_card_set, /* max_cards_in_cardset */ @@ -86,23 +86,23 @@ G1CardSetConfiguration::G1CardSetConfiguration(uint num_cards_in_array, { } G1CardSetConfiguration::G1CardSetConfiguration(uint inline_ptr_bits_per_card, - uint num_cards_in_array, + uint max_cards_in_array, double cards_in_bitmap_threshold_percent, uint num_buckets_in_howl, double cards_in_howl_threshold_percent, uint max_cards_in_card_set, - uint log2_card_region_per_heap_region) : + uint log2_card_regions_per_heap_region) : _inline_ptr_bits_per_card(inline_ptr_bits_per_card), - _num_cards_in_array(num_cards_in_array), + _max_cards_in_array(max_cards_in_array), _num_buckets_in_howl(num_buckets_in_howl), _max_cards_in_card_set(max_cards_in_card_set), _cards_in_howl_threshold(max_cards_in_card_set * cards_in_howl_threshold_percent), - _num_cards_in_howl_bitmap(G1CardSetHowl::bitmap_size(_max_cards_in_card_set, _num_buckets_in_howl)), - _cards_in_howl_bitmap_threshold(_num_cards_in_howl_bitmap * cards_in_bitmap_threshold_percent), - _log2_num_cards_in_howl_bitmap(log2i_exact(_num_cards_in_howl_bitmap)), - _bitmap_hash_mask(~(~(0) << _log2_num_cards_in_howl_bitmap)), - _log2_card_region_per_heap_region(log2_card_region_per_heap_region), - _log2_cards_per_card_region(log2i_exact(_max_cards_in_card_set) - _log2_card_region_per_heap_region) { + _max_cards_in_howl_bitmap(G1CardSetHowl::bitmap_size(_max_cards_in_card_set, _num_buckets_in_howl)), + _cards_in_howl_bitmap_threshold(_max_cards_in_howl_bitmap * cards_in_bitmap_threshold_percent), + _log2_max_cards_in_howl_bitmap(log2i_exact(_max_cards_in_howl_bitmap)), + _bitmap_hash_mask(~(~(0) << _log2_max_cards_in_howl_bitmap)), + _log2_card_regions_per_heap_region(log2_card_regions_per_heap_region), + _log2_cards_per_card_region(log2i_exact(_max_cards_in_card_set) - _log2_card_regions_per_heap_region) { assert(is_power_of_2(_max_cards_in_card_set), "max_cards_in_card_set must be a power of 2: %u", _max_cards_in_card_set); @@ -118,8 +118,8 @@ G1CardSetConfiguration::~G1CardSetConfiguration() { void G1CardSetConfiguration::init_card_set_alloc_options() { _card_set_alloc_options = NEW_C_HEAP_ARRAY(G1CardSetAllocOptions, num_mem_object_types(), mtGC); new (&_card_set_alloc_options[0]) G1CardSetAllocOptions((uint)CardSetHash::get_node_size()); - new (&_card_set_alloc_options[1]) G1CardSetAllocOptions((uint)G1CardSetArray::size_in_bytes(_num_cards_in_array), 2, 256); - new (&_card_set_alloc_options[2]) G1CardSetAllocOptions((uint)G1CardSetBitMap::size_in_bytes(_num_cards_in_howl_bitmap), 2, 256); + new (&_card_set_alloc_options[1]) G1CardSetAllocOptions((uint)G1CardSetArray::size_in_bytes(_max_cards_in_array), 2, 256); + new (&_card_set_alloc_options[2]) G1CardSetAllocOptions((uint)G1CardSetBitMap::size_in_bytes(_max_cards_in_howl_bitmap), 2, 256); new (&_card_set_alloc_options[3]) G1CardSetAllocOptions((uint)G1CardSetHowl::size_in_bytes(_num_buckets_in_howl), 2, 256); } @@ -130,19 +130,19 @@ void G1CardSetConfiguration::log_configuration() { "Howl #buckets %u coarsen threshold %u " "Howl Bitmap #elems %u size %zu coarsen threshold %u " "Card regions per heap region %u cards per card region %u", - num_cards_in_inline_ptr(), sizeof(void*), - num_cards_in_array(), G1CardSetArray::size_in_bytes(num_cards_in_array()), + max_cards_in_inline_ptr(), sizeof(void*), + max_cards_in_array(), G1CardSetArray::size_in_bytes(max_cards_in_array()), num_buckets_in_howl(), cards_in_howl_threshold(), - num_cards_in_howl_bitmap(), G1CardSetBitMap::size_in_bytes(num_cards_in_howl_bitmap()), cards_in_howl_bitmap_threshold(), - (uint)1 << log2_card_region_per_heap_region(), + max_cards_in_howl_bitmap(), G1CardSetBitMap::size_in_bytes(max_cards_in_howl_bitmap()), cards_in_howl_bitmap_threshold(), + (uint)1 << log2_card_regions_per_heap_region(), (uint)1 << log2_cards_per_card_region()); } -uint G1CardSetConfiguration::num_cards_in_inline_ptr() const { - return num_cards_in_inline_ptr(_inline_ptr_bits_per_card); +uint G1CardSetConfiguration::max_cards_in_inline_ptr() const { + return max_cards_in_inline_ptr(_inline_ptr_bits_per_card); } -uint G1CardSetConfiguration::num_cards_in_inline_ptr(uint bits_per_card) { +uint G1CardSetConfiguration::max_cards_in_inline_ptr(uint bits_per_card) { return G1CardSetInlinePtr::max_cards_in_inline_ptr(bits_per_card); } @@ -508,19 +508,19 @@ G1AddCardResult G1CardSet::add_to_howl(CardSetPtr parent_card_set, G1AddCardResult G1CardSet::add_to_bitmap(CardSetPtr card_set, uint card_in_region) { G1CardSetBitMap* bitmap = card_set_ptr(card_set); uint card_offset = _config->howl_bitmap_offset(card_in_region); - return bitmap->add(card_offset, _config->cards_in_howl_bitmap_threshold(), _config->num_cards_in_howl_bitmap()); + return bitmap->add(card_offset, _config->cards_in_howl_bitmap_threshold(), _config->max_cards_in_howl_bitmap()); } G1AddCardResult G1CardSet::add_to_inline_ptr(CardSetPtr volatile* card_set_addr, CardSetPtr card_set, uint card_in_region) { G1CardSetInlinePtr value(card_set_addr, card_set); - return value.add(card_in_region, _config->inline_ptr_bits_per_card(), _config->num_cards_in_inline_ptr()); + return value.add(card_in_region, _config->inline_ptr_bits_per_card(), _config->max_cards_in_inline_ptr()); } G1CardSet::CardSetPtr G1CardSet::create_coarsened_array_of_cards(uint card_in_region, bool within_howl) { uint8_t* data = nullptr; CardSetPtr new_card_set; if (within_howl) { - uint const size_in_bits = _config->num_cards_in_howl_bitmap(); + uint const size_in_bits = _config->max_cards_in_howl_bitmap(); uint card_offset = _config->howl_bitmap_offset(card_in_region); data = allocate_mem_object(CardSetBitMap); new (data) G1CardSetBitMap(card_offset, size_in_bits); @@ -549,7 +549,7 @@ bool G1CardSet::coarsen_card_set(volatile CardSetPtr* card_set_addr, break; } case CardSetInlinePtr: { - uint const size = _config->num_cards_in_array(); + uint const size = _config->max_cards_in_array(); uint8_t* data = allocate_mem_object(CardSetArrayOfCards); new (data) G1CardSetArray(card_in_region, size); new_card_set = make_card_set_ptr(data, CardSetArrayOfCards); @@ -626,7 +626,7 @@ void G1CardSet::transfer_cards_in_howl(CardSetPtr parent_card_set, G1TransferCard iter(this, card_region); iterate_cards_during_transfer(source_card_set, iter); } else { - uint diff = _config->num_cards_in_howl_bitmap() - card_set_ptr(source_card_set)->num_bits_set(); + uint diff = _config->max_cards_in_howl_bitmap() - card_set_ptr(source_card_set)->num_bits_set(); // Need to correct for that the Full remembered set occupies more cards than the // bitmap before. @@ -755,7 +755,7 @@ bool G1CardSet::contains_card(uint card_region, uint card_in_region) { return ptr.contains(card_in_region, _config->inline_ptr_bits_per_card()); } case CardSetArrayOfCards : return card_set_ptr(card_set)->contains(card_in_region); - case CardSetBitMap: return card_set_ptr(card_set)->contains(card_in_region, _config->num_cards_in_howl_bitmap()); + case CardSetBitMap: return card_set_ptr(card_set)->contains(card_in_region, _config->max_cards_in_howl_bitmap()); case CardSetHowl: { G1CardSetHowl* howling_array = card_set_ptr(card_set); diff --git a/src/hotspot/share/gc/g1/g1CardSet.hpp b/src/hotspot/share/gc/g1/g1CardSet.hpp index c8e09cec12678..465984d713873 100644 --- a/src/hotspot/share/gc/g1/g1CardSet.hpp +++ b/src/hotspot/share/gc/g1/g1CardSet.hpp @@ -50,26 +50,26 @@ class G1CardSetConfiguration { // regions covered by this card set. uint _inline_ptr_bits_per_card; - uint _num_cards_in_array; + uint _max_cards_in_array; uint _num_buckets_in_howl; uint _max_cards_in_card_set; uint _cards_in_howl_threshold; - uint _num_cards_in_howl_bitmap; + uint _max_cards_in_howl_bitmap; uint _cards_in_howl_bitmap_threshold; - uint _log2_num_cards_in_howl_bitmap; + uint _log2_max_cards_in_howl_bitmap; size_t _bitmap_hash_mask; - uint _log2_card_region_per_heap_region; + uint _log2_card_regions_per_heap_region; uint _log2_cards_per_card_region; G1CardSetAllocOptions* _card_set_alloc_options; G1CardSetConfiguration(uint inline_ptr_bits_per_card, - uint num_cards_in_array, + uint max_cards_in_array, double cards_in_bitmap_threshold_percent, uint num_buckets_in_howl, double cards_in_howl_threshold_percent, uint max_cards_in_card_set, - uint log2_card_region_per_heap_region); + uint log2_card_regions_per_heap_region); void init_card_set_alloc_options(); void log_configuration(); @@ -79,7 +79,7 @@ class G1CardSetConfiguration { G1CardSetConfiguration(); // Initialize card set configuration from parameters. // Testing only. - G1CardSetConfiguration(uint num_cards_in_array, + G1CardSetConfiguration(uint max_cards_in_array, double cards_in_bitmap_threshold_percent, uint max_buckets_in_howl, double cards_in_howl_threshold_percent, @@ -90,21 +90,19 @@ class G1CardSetConfiguration { // Inline pointer configuration uint inline_ptr_bits_per_card() const { return _inline_ptr_bits_per_card; } - uint num_cards_in_inline_ptr() const; - static uint num_cards_in_inline_ptr(uint bits_per_card); + uint max_cards_in_inline_ptr() const; + static uint max_cards_in_inline_ptr(uint bits_per_card); // Array of Cards configuration - bool use_cards_in_array() const { return _num_cards_in_array != 0; } // Unused for now - // Number of cards in "Array of Cards" set; 0 to disable. + // Maximum number of cards in "Array of Cards" set; 0 to disable. // Always coarsen to next level if full, so no specific threshold. - uint num_cards_in_array() const { return _num_cards_in_array; } + uint max_cards_in_array() const { return _max_cards_in_array; } // Bitmap within Howl card set container configuration - bool use_cards_in_howl_bitmap() const { return _num_cards_in_howl_bitmap != 0; } // Unused for now - uint num_cards_in_howl_bitmap() const { return _num_cards_in_howl_bitmap; } + uint max_cards_in_howl_bitmap() const { return _max_cards_in_howl_bitmap; } // (Approximate) Number of cards in bitmap to coarsen Howl Bitmap to Howl Full. uint cards_in_howl_bitmap_threshold() const { return _cards_in_howl_bitmap_threshold; } - uint log2_num_cards_in_howl_bitmap() const {return _log2_num_cards_in_howl_bitmap;} + uint log2_max_cards_in_howl_bitmap() const {return _log2_max_cards_in_howl_bitmap;} // Howl card set container configuration uint num_buckets_in_howl() const { return _num_buckets_in_howl; } @@ -112,7 +110,7 @@ class G1CardSetConfiguration { uint cards_in_howl_threshold() const { return _cards_in_howl_threshold; } uint howl_bitmap_offset(uint card_idx) const { return card_idx & _bitmap_hash_mask; } // Given a card index, return the bucket in the array of card sets. - uint howl_bucket_index(uint card_idx) { return card_idx >> _log2_num_cards_in_howl_bitmap; } + uint howl_bucket_index(uint card_idx) { return card_idx >> _log2_max_cards_in_howl_bitmap; } // Full card configuration // Maximum number of cards in a non-full card set for a single region. Card sets @@ -127,8 +125,8 @@ class G1CardSetConfiguration { // The next two members give information about how many card regions are there // per area (heap region) and how many cards each card region has. - // The log2 of the amount of card regions per heap region configured. - uint log2_card_region_per_heap_region() const { return _log2_card_region_per_heap_region; } + // The log2 of the number of card regions per heap region configured. + uint log2_card_regions_per_heap_region() const { return _log2_card_regions_per_heap_region; } // The log2 of the number of cards per card region. This is calculated from max_cards_in_region() // and above. uint log2_cards_per_card_region() const { return _log2_cards_per_card_region; } diff --git a/src/hotspot/share/gc/g1/g1CardSetContainers.hpp b/src/hotspot/share/gc/g1/g1CardSetContainers.hpp index c273695aa638b..0c6270d228f41 100644 --- a/src/hotspot/share/gc/g1/g1CardSetContainers.hpp +++ b/src/hotspot/share/gc/g1/g1CardSetContainers.hpp @@ -228,7 +228,6 @@ class G1CardSetArray : public G1CardSetContainer { void iterate(CardVisitor& found); size_t num_entries() const { return _num_entries & EntryMask; } - size_t max_entries() const { return _size; } static size_t header_size_in_bytes() { return header_size_in_bytes_internal(); } diff --git a/src/hotspot/share/gc/g1/g1CardSetContainers.inline.hpp b/src/hotspot/share/gc/g1/g1CardSetContainers.inline.hpp index e23386d4040eb..7390afc12dd63 100644 --- a/src/hotspot/share/gc/g1/g1CardSetContainers.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CardSetContainers.inline.hpp @@ -252,14 +252,14 @@ inline void G1CardSetBitMap::iterate(CardVisitor& found, size_t size_in_bits, ui inline G1CardSetHowl::G1CardSetHowl(EntryCountType card_in_region, G1CardSetConfiguration* config) : G1CardSetContainer(), - _num_entries((config->num_cards_in_array() + 1)) /* Card Transfer will not increment _num_entries */ { + _num_entries((config->max_cards_in_array() + 1)) /* Card Transfer will not increment _num_entries */ { EntryCountType num_buckets = config->num_buckets_in_howl(); EntryCountType bucket = config->howl_bucket_index(card_in_region); for (uint i = 0; i < num_buckets; ++i) { _buckets[i] = G1CardSetInlinePtr(); if (i == bucket) { G1CardSetInlinePtr value(&_buckets[i], _buckets[i]); - value.add(card_in_region, config->inline_ptr_bits_per_card(), config->num_cards_in_inline_ptr()); + value.add(card_in_region, config->inline_ptr_bits_per_card(), config->max_cards_in_inline_ptr()); } } } @@ -275,7 +275,7 @@ inline bool G1CardSetHowl::contains(uint card_idx, G1CardSetConfiguration* confi } case G1CardSet::CardSetBitMap: { uint card_offset = config->howl_bitmap_offset(card_idx); - return G1CardSet::card_set_ptr(card_set)->contains(card_offset, config->num_cards_in_howl_bitmap()); + return G1CardSet::card_set_ptr(card_set)->contains(card_offset, config->max_cards_in_howl_bitmap()); } case G1CardSet::CardSetInlinePtr: { G1CardSetInlinePtr ptr(card_set); @@ -321,28 +321,28 @@ inline void G1CardSetHowl::iterate_cardset(CardSetPtr const card_set, uint index } case G1CardSet::CardSetBitMap: { if (found.start_iterate(G1GCPhaseTimes::MergeRSHowlBitmap)) { - uint offset = index << config->log2_num_cards_in_howl_bitmap(); - G1CardSet::card_set_ptr(card_set)->iterate(found, config->num_cards_in_howl_bitmap(), offset); + uint offset = index << config->log2_max_cards_in_howl_bitmap(); + G1CardSet::card_set_ptr(card_set)->iterate(found, config->max_cards_in_howl_bitmap(), offset); } return; } case G1CardSet::CardSetHowl: { // actually FullCardSet assert(card_set == G1CardSet::FullCardSet, "Must be"); if (found.start_iterate(G1GCPhaseTimes::MergeRSHowlFull)) { - uint offset = index << config->log2_num_cards_in_howl_bitmap(); - found(offset, config->num_cards_in_howl_bitmap()); + uint offset = index << config->log2_max_cards_in_howl_bitmap(); + found(offset, config->max_cards_in_howl_bitmap()); } return; } } } -inline G1CardSetHowl::EntryCountType G1CardSetHowl::num_buckets(size_t size_in_bits, size_t num_cards_in_array, size_t max_num_buckets) { +inline G1CardSetHowl::EntryCountType G1CardSetHowl::num_buckets(size_t size_in_bits, size_t max_cards_in_array, size_t max_num_buckets) { size_t size_bitmap_bytes = BitMap::calc_size_in_words(size_in_bits) * BytesPerWord; // Ensure that in the worst case arrays consume half the memory size // of storing the entire bitmap size_t max_size_arrays_bytes = size_bitmap_bytes / 2; - size_t size_array_bytes = num_cards_in_array * sizeof(G1CardSetArray::EntryDataType); + size_t size_array_bytes = max_cards_in_array * sizeof(G1CardSetArray::EntryDataType); size_t num_arrays = max_size_arrays_bytes / size_array_bytes; // We use shifts and masks for indexing the array. So round down to the next // power of two to not use more than expected memory. diff --git a/src/hotspot/share/gc/g1/g1CardSetMemory.cpp b/src/hotspot/share/gc/g1/g1CardSetMemory.cpp index 988db3a4a8dd2..f48311a504cd3 100644 --- a/src/hotspot/share/gc/g1/g1CardSetMemory.cpp +++ b/src/hotspot/share/gc/g1/g1CardSetMemory.cpp @@ -24,14 +24,12 @@ #include "precompiled.hpp" +#include "gc/g1/g1CardSetContainers.inline.hpp" #include "gc/g1/g1CardSetMemory.inline.hpp" #include "gc/g1/g1SegmentedArray.inline.hpp" -#include "logging/log.hpp" #include "runtime/atomic.hpp" -#include "utilities/formatBuffer.hpp" #include "utilities/ostream.hpp" - template G1CardSetAllocator::G1CardSetAllocator(const char* name, const G1CardSetAllocOptions* buffer_options, @@ -142,166 +140,6 @@ void G1CardSetAllocator::print(outputStream* os) { mem_size()); } -G1CardSetMemoryStats::G1CardSetMemoryStats() { - clear(); -} - -void G1CardSetMemoryStats::clear() { - for (uint i = 0; i < num_pools(); i++) { - _num_mem_sizes[i] = 0; - _num_buffers[i] = 0; - } -} - -void G1CardSetFreePool::update_unlink_processors(G1ReturnMemoryProcessorSet* unlink_processor) { - uint num_free_lists = _freelist_pool.num_free_lists(); - - for (uint i = 0; i < num_free_lists; i++) { - unlink_processor->at(i)->visit_free_list(_freelist_pool.free_list(i)); - } -} - -void G1CardSetFreePool::G1ReturnMemoryProcessor::visit_free_list(G1CardSetBufferList* source) { - assert(_source == nullptr, "already visited"); - if (_return_to_vm_size > 0) { - _source = source; - } else { - assert(_source == nullptr, "must be"); - } - if (source->mem_size() > _return_to_vm_size) { - _first = source->get_all(_num_unlinked, _unlinked_bytes); - } else { - assert(_first == nullptr, "must be"); - } - // Above we were racing with other threads getting the contents of the free list, - // so while we might have been asked to return something to the OS initially, - // the free list might be empty anyway. In this case just reset internal values - // used for checking whether there is work available. - if (_first == nullptr) { - _source = nullptr; - _return_to_vm_size = 0; - } -} - -bool G1CardSetFreePool::G1ReturnMemoryProcessor::return_to_vm(jlong deadline) { - assert(!finished_return_to_vm(), "already returned everything to the VM"); - assert(_first != nullptr, "must have element to return"); - - size_t keep_size = 0; - size_t keep_num = 0; - - G1CardSetBuffer* cur = _first; - G1CardSetBuffer* last = nullptr; - - while (cur != nullptr && _return_to_vm_size > 0) { - size_t cur_size = cur->mem_size(); - _return_to_vm_size -= MIN2(_return_to_vm_size, cur_size); - - keep_size += cur_size; - keep_num++; - - last = cur; - cur = cur->next(); - // To ensure progress, perform the deadline check here. - if (os::elapsed_counter() > deadline) { - break; - } - } - - assert(_first != nullptr, "must be"); - assert(last != nullptr, "must be"); - - last->set_next(nullptr); - - // Wait for any in-progress pops to avoid ABA for them. - GlobalCounter::write_synchronize(); - _source->bulk_add(*_first, *last, keep_num, keep_size); - _first = cur; - - log_trace(gc, task)("Card Set Free Memory: Returned to VM %zu buffers size %zu", keep_num, keep_size); - - // _return_to_vm_size may be larger than what is available in the list at the - // time we actually get the list. I.e. the list and _return_to_vm_size may be - // inconsistent. - // So also check if we actually already at the end of the list for the exit - // condition. - if (_return_to_vm_size == 0 || _first == nullptr) { - _source = nullptr; - _return_to_vm_size = 0; - } - return _source != nullptr; -} - -bool G1CardSetFreePool::G1ReturnMemoryProcessor::return_to_os(jlong deadline) { - assert(finished_return_to_vm(), "not finished returning to VM"); - assert(!finished_return_to_os(), "already returned everything to the OS"); - - // Now delete the rest. - size_t num_delete = 0; - size_t mem_size_deleted = 0; - - while (_first != nullptr) { - G1CardSetBuffer* next = _first->next(); - num_delete++; - mem_size_deleted += _first->mem_size(); - delete _first; - _first = next; - - // To ensure progress, perform the deadline check here. - if (os::elapsed_counter() > deadline) { - break; - } - } - - log_trace(gc, task)("Card Set Free Memory: Return to OS %zu buffers size %zu", num_delete, mem_size_deleted); - - return _first != nullptr; -} - -G1CardSetFreePool G1CardSetFreePool::_freelist_pool(G1CardSetConfiguration::num_mem_object_types()); - -G1CardSetFreePool::G1CardSetFreePool(uint num_free_lists) : - _num_free_lists(num_free_lists) { - - _free_lists = NEW_C_HEAP_ARRAY(G1CardSetBufferList, _num_free_lists, mtGC); - for (uint i = 0; i < _num_free_lists; i++) { - new (&_free_lists[i]) G1CardSetBufferList(); - } -} - -G1CardSetFreePool::~G1CardSetFreePool() { - for (uint i = 0; i < _num_free_lists; i++) { - _free_lists[i].~G1CardSetBufferList(); - } - FREE_C_HEAP_ARRAY(mtGC, _free_lists); -} - -G1CardSetMemoryStats G1CardSetFreePool::memory_sizes() const { - G1CardSetMemoryStats free_list_stats; - assert(free_list_stats.num_pools() == num_free_lists(), "must be"); - for (uint i = 0; i < num_free_lists(); i++) { - free_list_stats._num_mem_sizes[i] = _free_lists[i].mem_size(); - free_list_stats._num_buffers[i] = _free_lists[i].num_buffers(); - } - return free_list_stats; -} - -size_t G1CardSetFreePool::mem_size() const { - size_t result = 0; - for (uint i = 0; i < _num_free_lists; i++) { - result += _free_lists[i].mem_size(); - } - return result; -} - -void G1CardSetFreePool::print_on(outputStream* out) { - out->print_cr(" Free Pool: size %zu", free_list_pool()->mem_size()); - for (uint i = 0; i < _num_free_lists; i++) { - FormatBuffer<> fmt(" %s", G1CardSetConfiguration::mem_object_type_name_str(i)); - _free_lists[i].print_on(out, fmt); - } -} - G1CardSetMemoryManager::G1CardSetMemoryManager(G1CardSetConfiguration* config, G1CardSetFreePool* free_list_pool) : _config(config) { @@ -363,11 +201,11 @@ size_t G1CardSetMemoryManager::wasted_mem_size() const { return result; } -G1CardSetMemoryStats G1CardSetMemoryManager::memory_stats() const { - G1CardSetMemoryStats result; +G1SegmentedArrayMemoryStats G1CardSetMemoryManager::memory_stats() const { + G1SegmentedArrayMemoryStats result; for (uint i = 0; i < num_mem_object_types(); i++) { result._num_mem_sizes[i] += _allocators[i].mem_size(); - result._num_buffers[i] += _allocators[i].num_buffers(); + result._num_segments[i] += _allocators[i].num_buffers(); } return result; } diff --git a/src/hotspot/share/gc/g1/g1CardSetMemory.hpp b/src/hotspot/share/gc/g1/g1CardSetMemory.hpp index 4167576f9bb89..50cb308f27630 100644 --- a/src/hotspot/share/gc/g1/g1CardSetMemory.hpp +++ b/src/hotspot/share/gc/g1/g1CardSetMemory.hpp @@ -27,8 +27,8 @@ #include "gc/g1/g1CardSet.hpp" #include "gc/g1/g1CardSetContainers.hpp" -#include "gc/g1/g1CardSetContainers.inline.hpp" #include "gc/g1/g1SegmentedArray.hpp" +#include "gc/g1/g1SegmentedArrayFreePool.hpp" #include "memory/allocation.hpp" #include "utilities/growableArray.hpp" #include "utilities/lockFreeStack.hpp" @@ -136,98 +136,7 @@ class G1CardSetAllocator { void print(outputStream* os); }; -// Statistics for a fixed set of buffer lists. Contains the number of buffers and memory -// used for each. Note that statistics are typically not taken atomically so there -// can be inconsistencies. The user must be prepared for them. -class G1CardSetMemoryStats { -public: - - size_t _num_mem_sizes[G1CardSetConfiguration::num_mem_object_types()]; - size_t _num_buffers[G1CardSetConfiguration::num_mem_object_types()]; - - // Returns all-zero statistics. - G1CardSetMemoryStats(); - - void add(G1CardSetMemoryStats const other) { - STATIC_ASSERT(ARRAY_SIZE(_num_buffers) == ARRAY_SIZE(_num_mem_sizes)); - for (uint i = 0; i < ARRAY_SIZE(_num_mem_sizes); i++) { - _num_mem_sizes[i] += other._num_mem_sizes[i]; - _num_buffers[i] += other._num_buffers[i]; - } - } - - void clear(); - - uint num_pools() const { return G1CardSetConfiguration::num_mem_object_types(); } -}; - -// A set of free lists holding memory buffers for use by G1CardSetAllocators. -class G1CardSetFreePool { - // The global free pool. - static G1CardSetFreePool _freelist_pool; - - const uint _num_free_lists; - G1CardSetBufferList* _free_lists; - -public: - static G1CardSetFreePool* free_list_pool() { return &_freelist_pool; } - static G1CardSetMemoryStats free_list_sizes() { return _freelist_pool.memory_sizes(); } - - class G1ReturnMemoryProcessor; - typedef GrowableArrayCHeap G1ReturnMemoryProcessorSet; - - static void update_unlink_processors(G1ReturnMemoryProcessorSet* unlink_processors); - - explicit G1CardSetFreePool(uint num_free_lists); - ~G1CardSetFreePool(); - - G1CardSetBufferList* free_list(uint i) { - assert(i < _num_free_lists, "must be"); - return &_free_lists[i]; - } - - uint num_free_lists() const { return _num_free_lists; } - - G1CardSetMemoryStats memory_sizes() const; - size_t mem_size() const; - - void print_on(outputStream* out); -}; - -// Data structure containing current in-progress state for returning memory to the -// operating system for a single G1CardSetBufferList. -class G1CardSetFreePool::G1ReturnMemoryProcessor : public CHeapObj { - G1CardSetBufferList* _source; - size_t _return_to_vm_size; - - G1CardSetBuffer* _first; - size_t _unlinked_bytes; - size_t _num_unlinked; - -public: - explicit G1ReturnMemoryProcessor(size_t return_to_vm) : - _source(nullptr), _return_to_vm_size(return_to_vm), _first(nullptr), _unlinked_bytes(0), _num_unlinked(0) { - } - - // Updates the instance members about the given card set buffer list for the purpose - // of giving back memory. Only necessary members are updated, e.g. if there is - // nothing to return to the VM, do not set the source list. - void visit_free_list(G1CardSetBufferList* source); - - bool finished_return_to_vm() const { return _return_to_vm_size == 0; } - bool finished_return_to_os() const { return _first == nullptr; } - - // Returns memory to the VM until the given deadline expires. Returns true if - // there is no more work. Guarantees forward progress, i.e. at least one buffer - // has been processed after returning. - // return_to_vm() re-adds buffers to the respective free list. - bool return_to_vm(jlong deadline); - // Returns memory to the VM until the given deadline expires. Returns true if - // there is no more work. Guarantees forward progress, i.e. at least one buffer - // has been processed after returning. - // return_to_os() gives back buffers to the OS. - bool return_to_os(jlong deadline); -}; +typedef G1SegmentedArrayFreePool G1CardSetFreePool; class G1CardSetMemoryManager : public CHeapObj { G1CardSetConfiguration* _config; @@ -256,7 +165,7 @@ class G1CardSetMemoryManager : public CHeapObj { size_t mem_size() const; size_t wasted_mem_size() const; - G1CardSetMemoryStats memory_stats() const; + G1SegmentedArrayMemoryStats memory_stats() const; }; #endif // SHARE_GC_G1_G1CARDSETMEMORY_HPP diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 62bae602b0aa4..b35f48eaa7bad 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -33,7 +33,6 @@ #include "gc/g1/g1Arguments.hpp" #include "gc/g1/g1BarrierSet.hpp" #include "gc/g1/g1BatchedTask.hpp" -#include "gc/g1/g1CardSetFreeMemoryTask.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectionSet.hpp" #include "gc/g1/g1CollectionSetCandidates.hpp" @@ -65,13 +64,14 @@ #include "gc/g1/g1RootClosures.hpp" #include "gc/g1/g1RootProcessor.hpp" #include "gc/g1/g1SATBMarkQueueSet.hpp" +#include "gc/g1/g1SegmentedArrayFreeMemoryTask.hpp" +#include "gc/g1/g1ServiceThread.hpp" #include "gc/g1/g1ThreadLocalData.hpp" #include "gc/g1/g1Trace.hpp" -#include "gc/g1/g1ServiceThread.hpp" #include "gc/g1/g1UncommitRegionTask.hpp" #include "gc/g1/g1VMOperations.hpp" -#include "gc/g1/g1YoungGCEvacFailureInjector.hpp" #include "gc/g1/g1YoungCollector.hpp" +#include "gc/g1/g1YoungGCEvacFailureInjector.hpp" #include "gc/g1/heapRegion.inline.hpp" #include "gc/g1/heapRegionRemSet.inline.hpp" #include "gc/g1/heapRegionSet.inline.hpp" @@ -87,17 +87,17 @@ #include "gc/shared/locationPrinter.inline.hpp" #include "gc/shared/oopStorageParState.hpp" #include "gc/shared/preservedMarks.inline.hpp" -#include "gc/shared/suspendibleThreadSet.hpp" #include "gc/shared/referenceProcessor.inline.hpp" -#include "gc/shared/taskTerminator.hpp" +#include "gc/shared/suspendibleThreadSet.hpp" #include "gc/shared/taskqueue.inline.hpp" +#include "gc/shared/taskTerminator.hpp" #include "gc/shared/tlab_globals.hpp" -#include "gc/shared/weakProcessor.inline.hpp" #include "gc/shared/workerPolicy.hpp" +#include "gc/shared/weakProcessor.inline.hpp" #include "logging/log.hpp" #include "memory/allocation.hpp" -#include "memory/iterator.hpp" #include "memory/heapInspection.hpp" +#include "memory/iterator.hpp" #include "memory/metaspaceUtils.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" @@ -1431,7 +1431,7 @@ G1CollectedHeap::G1CollectedHeap() : CollectedHeap(), _service_thread(NULL), _periodic_gc_task(NULL), - _free_card_set_memory_task(NULL), + _free_segmented_array_memory_task(NULL), _workers(NULL), _card_table(NULL), _collection_pause_end(Ticks::now()), @@ -1723,8 +1723,8 @@ jint G1CollectedHeap::initialize() { _periodic_gc_task = new G1PeriodicGCTask("Periodic GC Task"); _service_thread->register_task(_periodic_gc_task); - _free_card_set_memory_task = new G1CardSetFreeMemoryTask("Card Set Free Memory Task"); - _service_thread->register_task(_free_card_set_memory_task); + _free_segmented_array_memory_task = new G1SegmentedArrayFreeMemoryTask("Card Set Free Memory Task"); + _service_thread->register_task(_free_segmented_array_memory_task); { G1DirtyCardQueueSet& dcqs = G1BarrierSet::dirty_card_queue_set(); @@ -2260,7 +2260,7 @@ void G1CollectedHeap::object_iterate(ObjectClosure* cl) { heap_region_iterate(&blk); } -class G1ParallelObjectIterator : public ParallelObjectIterator { +class G1ParallelObjectIterator : public ParallelObjectIteratorImpl { private: G1CollectedHeap* _heap; HeapRegionClaimer _claimer; @@ -2275,7 +2275,7 @@ class G1ParallelObjectIterator : public ParallelObjectIterator { } }; -ParallelObjectIterator* G1CollectedHeap::parallel_object_iterator(uint thread_num) { +ParallelObjectIteratorImpl* G1CollectedHeap::parallel_object_iterator(uint thread_num) { return new G1ParallelObjectIterator(thread_num); } @@ -2616,8 +2616,8 @@ void G1CollectedHeap::gc_epilogue(bool full) { _collection_pause_end = Ticks::now(); - _free_card_set_memory_task->notify_new_stats(&_young_gen_card_set_stats, - &_collection_set_candidates_card_set_stats); + _free_segmented_array_memory_task->notify_new_stats(&_young_gen_card_set_stats, + &_collection_set_candidates_card_set_stats); } uint G1CollectedHeap::uncommit_regions(uint region_limit) { @@ -2941,11 +2941,11 @@ bool G1CollectedHeap::should_sample_collection_set_candidates() const { return candidates != NULL && candidates->num_remaining() > 0; } -void G1CollectedHeap::set_collection_set_candidates_stats(G1CardSetMemoryStats& stats) { +void G1CollectedHeap::set_collection_set_candidates_stats(G1SegmentedArrayMemoryStats& stats) { _collection_set_candidates_card_set_stats = stats; } -void G1CollectedHeap::set_young_gen_card_set_stats(const G1CardSetMemoryStats& stats) { +void G1CollectedHeap::set_young_gen_card_set_stats(const G1SegmentedArrayMemoryStats& stats) { _young_gen_card_set_stats = stats; } diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 2178dfc9d0fbe..30ff5d19bec92 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -27,7 +27,6 @@ #include "gc/g1/g1BarrierSet.hpp" #include "gc/g1/g1BiasedArray.hpp" -#include "gc/g1/g1CardSetFreeMemoryTask.hpp" #include "gc/g1/g1CardTable.hpp" #include "gc/g1/g1CollectionSet.hpp" #include "gc/g1/g1CollectorState.hpp" @@ -35,12 +34,13 @@ #include "gc/g1/g1EdenRegions.hpp" #include "gc/g1/g1EvacStats.hpp" #include "gc/g1/g1GCPauseType.hpp" +#include "gc/g1/g1HeapRegionAttr.hpp" #include "gc/g1/g1HeapTransition.hpp" #include "gc/g1/g1HeapVerifier.hpp" #include "gc/g1/g1HRPrinter.hpp" -#include "gc/g1/g1HeapRegionAttr.hpp" #include "gc/g1/g1MonitoringSupport.hpp" #include "gc/g1/g1NUMA.hpp" +#include "gc/g1/g1SegmentedArrayFreeMemoryTask.hpp" #include "gc/g1/g1SurvivorRegions.hpp" #include "gc/g1/g1YoungGCEvacFailureInjector.hpp" #include "gc/g1/heapRegionManager.hpp" @@ -143,7 +143,7 @@ class G1CollectedHeap : public CollectedHeap { private: G1ServiceThread* _service_thread; G1ServiceTask* _periodic_gc_task; - G1CardSetFreeMemoryTask* _free_card_set_memory_task; + G1SegmentedArrayFreeMemoryTask* _free_segmented_array_memory_task; WorkerThreads* _workers; G1CardTable* _card_table; @@ -160,9 +160,9 @@ class G1CollectedHeap : public CollectedHeap { HeapRegionSet _humongous_set; // Young gen memory statistics before GC. - G1CardSetMemoryStats _young_gen_card_set_stats; + G1SegmentedArrayMemoryStats _young_gen_card_set_stats; // Collection set candidates memory statistics after GC. - G1CardSetMemoryStats _collection_set_candidates_card_set_stats; + G1SegmentedArrayMemoryStats _collection_set_candidates_card_set_stats; // The block offset table for the G1 heap. G1BlockOffsetTable* _bot; @@ -259,8 +259,8 @@ class G1CollectedHeap : public CollectedHeap { void set_humongous_stats(uint num_humongous_total, uint num_humongous_candidates); bool should_sample_collection_set_candidates() const; - void set_collection_set_candidates_stats(G1CardSetMemoryStats& stats); - void set_young_gen_card_set_stats(const G1CardSetMemoryStats& stats); + void set_collection_set_candidates_stats(G1SegmentedArrayMemoryStats& stats); + void set_young_gen_card_set_stats(const G1SegmentedArrayMemoryStats& stats); private: @@ -1077,7 +1077,7 @@ class G1CollectedHeap : public CollectedHeap { // Iterate over all objects, calling "cl.do_object" on each. void object_iterate(ObjectClosure* cl) override; - ParallelObjectIterator* parallel_object_iterator(uint thread_num) override; + ParallelObjectIteratorImpl* parallel_object_iterator(uint thread_num) override; // Keep alive an object that was loaded with AS_NO_KEEPALIVE. void keep_alive(oop obj) override; diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp index 2e0f6028a5da7..159ee0471c24c 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp @@ -41,6 +41,12 @@ #include "runtime/atomic.hpp" #include "utilities/bitMap.inline.hpp" +inline bool G1STWIsAliveClosure::do_object_b(oop p) { + // An object is reachable if it is outside the collection set, + // or is inside and copied. + return !_g1h->is_in_cset(p) || p->is_forwarded(); +} + G1GCPhaseTimes* G1CollectedHeap::phase_times() const { return _policy->phase_times(); } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index dea7a858cba36..ece2ac0452ade 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -770,7 +770,7 @@ class G1PreConcurrentStartTask::NoteStartOfMarkTask : public G1AbstractSubTask { double worker_cost() const override { // The work done per region is very small, therefore we choose this magic number to cap the number // of threads used when there are few regions. - const uint regions_per_thread = 1000; + const double regions_per_thread = 1000; return _claimer.n_regions() / regions_per_thread; } diff --git a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp index c77dd7196f540..7c3eba2be94b0 100644 --- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp +++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp @@ -116,10 +116,12 @@ G1GCPhaseTimes::G1GCPhaseTimes(STWGCTimer* gc_timer, uint max_gc_threads) : _gc_par_phases[ScanHR]->create_thread_work_items("Scanned Cards:", ScanHRScannedCards); _gc_par_phases[ScanHR]->create_thread_work_items("Scanned Blocks:", ScanHRScannedBlocks); _gc_par_phases[ScanHR]->create_thread_work_items("Claimed Chunks:", ScanHRClaimedChunks); + _gc_par_phases[ScanHR]->create_thread_work_items("Found Roots:", ScanHRFoundRoots); _gc_par_phases[OptScanHR]->create_thread_work_items("Scanned Cards:", ScanHRScannedCards); _gc_par_phases[OptScanHR]->create_thread_work_items("Scanned Blocks:", ScanHRScannedBlocks); _gc_par_phases[OptScanHR]->create_thread_work_items("Claimed Chunks:", ScanHRClaimedChunks); + _gc_par_phases[OptScanHR]->create_thread_work_items("Found Roots:", ScanHRFoundRoots); _gc_par_phases[OptScanHR]->create_thread_work_items("Scanned Refs:", ScanHRScannedOptRefs); _gc_par_phases[OptScanHR]->create_thread_work_items("Used Memory:", ScanHRUsedMemory); diff --git a/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp b/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp index 255aab6f96ee9..9db7f18dd3474 100644 --- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp +++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp @@ -123,6 +123,7 @@ class G1GCPhaseTimes : public CHeapObj { ScanHRScannedCards, ScanHRScannedBlocks, ScanHRClaimedChunks, + ScanHRFoundRoots, ScanHRScannedOptRefs, ScanHRUsedMemory }; diff --git a/src/hotspot/share/gc/g1/g1OopClosures.hpp b/src/hotspot/share/gc/g1/g1OopClosures.hpp index 9f0c4a23a55d4..0dbaabbf6396d 100644 --- a/src/hotspot/share/gc/g1/g1OopClosures.hpp +++ b/src/hotspot/share/gc/g1/g1OopClosures.hpp @@ -61,10 +61,12 @@ class G1ScanClosureBase : public BasicOopIterateClosure { // Used to scan cards from the DCQS or the remembered sets during garbage collection. class G1ScanCardClosure : public G1ScanClosureBase { + size_t& _heap_roots_found; public: G1ScanCardClosure(G1CollectedHeap* g1h, - G1ParScanThreadState* pss) : - G1ScanClosureBase(g1h, pss) { } + G1ParScanThreadState* pss, + size_t& heap_roots_found) : + G1ScanClosureBase(g1h, pss), _heap_roots_found(heap_roots_found) { } template void do_oop_work(T* p); virtual void do_oop(narrowOop* p) { do_oop_work(p); } diff --git a/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp b/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp index ac8c370bfd082..2cd41d4255f2d 100644 --- a/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp +++ b/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp @@ -180,6 +180,7 @@ inline void G1ScanCardClosure::do_oop_work(T* p) { // Since the source is always from outside the collection set, here we implicitly know // that this is a cross-region reference too. prefetch_and_push(p, obj); + _heap_roots_found++; } else if (!HeapRegion::is_in_same_region(p, obj)) { handle_non_cset_obj_common(region_attr, p, obj); _par_scan_state->enqueue_card_if_tracked(region_attr, p, obj); diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index b689e64976255..dcde1bd8fa5b2 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -774,6 +774,7 @@ class G1ScanHRForRegionClosure : public HeapRegionClosure { size_t _cards_scanned; size_t _blocks_scanned; size_t _chunks_claimed; + size_t _heap_roots_found; Tickspan _rem_set_root_scan_time; Tickspan _rem_set_trim_partially_time; @@ -785,7 +786,7 @@ class G1ScanHRForRegionClosure : public HeapRegionClosure { HeapWord* scan_memregion(uint region_idx_for_card, MemRegion mr) { HeapRegion* const card_region = _g1h->region_at(region_idx_for_card); - G1ScanCardClosure card_cl(_g1h, _pss); + G1ScanCardClosure card_cl(_g1h, _pss, _heap_roots_found); HeapWord* const scanned_to = card_region->oops_on_memregion_seq_iterate_careful(mr, &card_cl); assert(scanned_to != NULL, "Should be able to scan range"); @@ -880,6 +881,7 @@ class G1ScanHRForRegionClosure : public HeapRegionClosure { _cards_scanned(0), _blocks_scanned(0), _chunks_claimed(0), + _heap_roots_found(0), _rem_set_root_scan_time(), _rem_set_trim_partially_time(), _scanned_to(NULL), @@ -906,6 +908,7 @@ class G1ScanHRForRegionClosure : public HeapRegionClosure { size_t cards_scanned() const { return _cards_scanned; } size_t blocks_scanned() const { return _blocks_scanned; } size_t chunks_claimed() const { return _chunks_claimed; } + size_t heap_roots_found() const { return _heap_roots_found; } }; void G1RemSet::scan_heap_roots(G1ParScanThreadState* pss, @@ -924,6 +927,7 @@ void G1RemSet::scan_heap_roots(G1ParScanThreadState* pss, p->record_or_add_thread_work_item(scan_phase, worker_id, cl.cards_scanned(), G1GCPhaseTimes::ScanHRScannedCards); p->record_or_add_thread_work_item(scan_phase, worker_id, cl.blocks_scanned(), G1GCPhaseTimes::ScanHRScannedBlocks); p->record_or_add_thread_work_item(scan_phase, worker_id, cl.chunks_claimed(), G1GCPhaseTimes::ScanHRClaimedChunks); + p->record_or_add_thread_work_item(scan_phase, worker_id, cl.heap_roots_found(), G1GCPhaseTimes::ScanHRFoundRoots); } // Heap region closure to be applied to all regions in the current collection set @@ -937,6 +941,7 @@ class G1ScanCollectionSetRegionClosure : public HeapRegionClosure { uint _worker_id; + size_t _opt_roots_scanned; size_t _opt_refs_scanned; size_t _opt_refs_memory_used; @@ -951,7 +956,7 @@ class G1ScanCollectionSetRegionClosure : public HeapRegionClosure { G1OopStarChunkedList* opt_rem_set_list = _pss->oops_into_optional_region(r); - G1ScanCardClosure scan_cl(G1CollectedHeap::heap(), _pss); + G1ScanCardClosure scan_cl(G1CollectedHeap::heap(), _pss, _opt_roots_scanned); G1ScanRSForOptionalClosure cl(G1CollectedHeap::heap(), &scan_cl); _opt_refs_scanned += opt_rem_set_list->oops_do(&cl, _pss->closures()->strong_oops()); _opt_refs_memory_used += opt_rem_set_list->used_memory(); @@ -970,6 +975,7 @@ class G1ScanCollectionSetRegionClosure : public HeapRegionClosure { _scan_phase(scan_phase), _code_roots_phase(code_roots_phase), _worker_id(worker_id), + _opt_roots_scanned(0), _opt_refs_scanned(0), _opt_refs_memory_used(0), _strong_code_root_scan_time(), @@ -1006,6 +1012,7 @@ class G1ScanCollectionSetRegionClosure : public HeapRegionClosure { Tickspan rem_set_opt_root_scan_time() const { return _rem_set_opt_root_scan_time; } Tickspan rem_set_opt_trim_partially_time() const { return _rem_set_opt_trim_partially_time; } + size_t opt_roots_scanned() const { return _opt_roots_scanned; } size_t opt_refs_scanned() const { return _opt_refs_scanned; } size_t opt_refs_memory_used() const { return _opt_refs_memory_used; } }; @@ -1028,6 +1035,7 @@ void G1RemSet::scan_collection_set_regions(G1ParScanThreadState* pss, // At this time we record some metrics only for the evacuations after the initial one. if (scan_phase == G1GCPhaseTimes::OptScanHR) { + p->record_or_add_thread_work_item(scan_phase, worker_id, cl.opt_roots_scanned(), G1GCPhaseTimes::ScanHRFoundRoots); p->record_or_add_thread_work_item(scan_phase, worker_id, cl.opt_refs_scanned(), G1GCPhaseTimes::ScanHRScannedOptRefs); p->record_or_add_thread_work_item(scan_phase, worker_id, cl.opt_refs_memory_used(), G1GCPhaseTimes::ScanHRUsedMemory); } diff --git a/src/hotspot/share/gc/g1/g1CardSetFreeMemoryTask.cpp b/src/hotspot/share/gc/g1/g1SegmentedArrayFreeMemoryTask.cpp similarity index 70% rename from src/hotspot/share/gc/g1/g1CardSetFreeMemoryTask.cpp rename to src/hotspot/share/gc/g1/g1SegmentedArrayFreeMemoryTask.cpp index 09a22728a8947..4752adde74d2d 100644 --- a/src/hotspot/share/gc/g1/g1CardSetFreeMemoryTask.cpp +++ b/src/hotspot/share/gc/g1/g1SegmentedArrayFreeMemoryTask.cpp @@ -23,22 +23,23 @@ */ #include "precompiled.hpp" -#include "gc/g1/g1CardSetFreeMemoryTask.hpp" +#include "ci/ciUtilities.hpp" #include "gc/g1/g1CardSetMemory.inline.hpp" #include "gc/g1/g1CollectedHeap.hpp" +#include "gc/g1/g1SegmentedArrayFreeMemoryTask.hpp" #include "gc/g1/g1_globals.hpp" +#include "gc/g1/heapRegionRemSet.hpp" #include "gc/shared/gcTraceTime.inline.hpp" #include "gc/shared/suspendibleThreadSet.hpp" -#include "heapRegionRemSet.hpp" -#include "ci/ciUtilities.hpp" +#include "runtime/os.hpp" -constexpr const char* G1CardSetFreeMemoryTask::_state_names[]; +constexpr const char* G1SegmentedArrayFreeMemoryTask::_state_names[]; -const char* G1CardSetFreeMemoryTask::get_state_name(State value) const { +const char* G1SegmentedArrayFreeMemoryTask::get_state_name(State value) const { return _state_names[static_cast>(value)]; } -bool G1CardSetFreeMemoryTask::deadline_exceeded(jlong deadline) { +bool G1SegmentedArrayFreeMemoryTask::deadline_exceeded(jlong deadline) { return os::elapsed_counter() >= deadline; } @@ -47,31 +48,31 @@ static size_t keep_size(size_t free, size_t used, double percent) { return MIN2(free, to_keep); } -bool G1CardSetFreeMemoryTask::calculate_return_infos(jlong deadline) { +bool G1SegmentedArrayFreeMemoryTask::calculate_return_infos(jlong deadline) { // Ignore the deadline in this step as it is very short. - G1CardSetMemoryStats used = _total_used; - G1CardSetMemoryStats free = G1CardSetFreePool::free_list_sizes(); + G1SegmentedArrayMemoryStats used = _total_used; + G1SegmentedArrayMemoryStats free = G1SegmentedArrayFreePool::free_list_sizes(); _return_info = new G1ReturnMemoryProcessorSet(used.num_pools()); for (uint i = 0; i < used.num_pools(); i++) { size_t return_to_vm_size = keep_size(free._num_mem_sizes[i], used._num_mem_sizes[i], G1RemSetFreeMemoryKeepExcessRatio); - log_trace(gc, task)("Card Set Free Memory: Type %s: Free: %zu (%zu) " + log_trace(gc, task)("Segmented Array Free Memory: Type %s: Free: %zu (%zu) " "Used: %zu Keep: %zu", G1CardSetConfiguration::mem_object_type_name_str(i), - free._num_mem_sizes[i], free._num_buffers[i], + free._num_mem_sizes[i], free._num_segments[i], used._num_mem_sizes[i], return_to_vm_size); _return_info->append(new G1ReturnMemoryProcessor(return_to_vm_size)); } - G1CardSetFreePool::update_unlink_processors(_return_info); + G1SegmentedArrayFreePool::update_unlink_processors(_return_info); return false; } -bool G1CardSetFreeMemoryTask::return_memory_to_vm(jlong deadline) { +bool G1SegmentedArrayFreeMemoryTask::return_memory_to_vm(jlong deadline) { for (int i = 0; i < _return_info->length(); i++) { G1ReturnMemoryProcessor* info = _return_info->at(i); if (!info->finished_return_to_vm()) { @@ -83,7 +84,7 @@ bool G1CardSetFreeMemoryTask::return_memory_to_vm(jlong deadline) { return false; } -bool G1CardSetFreeMemoryTask::return_memory_to_os(jlong deadline) { +bool G1SegmentedArrayFreeMemoryTask::return_memory_to_os(jlong deadline) { for (int i = 0; i < _return_info->length(); i++) { G1ReturnMemoryProcessor* info = _return_info->at(i); if (!info->finished_return_to_os()) { @@ -95,7 +96,7 @@ bool G1CardSetFreeMemoryTask::return_memory_to_os(jlong deadline) { return false; } -bool G1CardSetFreeMemoryTask::cleanup_return_infos() { +bool G1SegmentedArrayFreeMemoryTask::cleanup_return_infos() { for (int i = 0; i < _return_info->length(); i++) { G1ReturnMemoryProcessor* info = _return_info->at(i); delete info; @@ -106,12 +107,12 @@ bool G1CardSetFreeMemoryTask::cleanup_return_infos() { return false; } -bool G1CardSetFreeMemoryTask::free_excess_card_set_memory() { +bool G1SegmentedArrayFreeMemoryTask::free_excess_segmented_array_memory() { jlong start = os::elapsed_counter(); jlong end = start + (os::elapsed_frequency() / 1000) * G1RemSetFreeMemoryStepDurationMillis; - log_trace(gc, task)("Card Set Free Memory: Step start %1.3f end %1.3f", + log_trace(gc, task)("Segmented Array Free Memory: Step start %1.3f end %1.3f", TimeHelper::counter_to_millis(start), TimeHelper::counter_to_millis(end)); State next_state; @@ -148,7 +149,7 @@ bool G1CardSetFreeMemoryTask::free_excess_card_set_memory() { break; } default: - log_error(gc, task)("Should not try to free excess card set memory in %s state", get_state_name(_state)); + log_error(gc, task)("Should not try to free excess segmented array memory in %s state", get_state_name(_state)); ShouldNotReachHere(); break; } @@ -156,41 +157,41 @@ bool G1CardSetFreeMemoryTask::free_excess_card_set_memory() { set_state(next_state); } while (_state != State::Inactive && !deadline_exceeded(end)); - log_trace(gc, task)("Card Set Free Memory: Step took %1.3fms, done %s", + log_trace(gc, task)("Segmented Array Free Memory: Step took %1.3fms, done %s", TimeHelper::counter_to_millis(os::elapsed_counter() - start), bool_to_str(_state == State::CalculateUsed)); return is_active(); } -void G1CardSetFreeMemoryTask::set_state(State new_state) { - log_trace(gc, task)("Card Set Free Memory: State change from %s to %s", +void G1SegmentedArrayFreeMemoryTask::set_state(State new_state) { + log_trace(gc, task)("Segmented Array Free Memory: State change from %s to %s", get_state_name(_state), get_state_name(new_state)); _state = new_state; } -bool G1CardSetFreeMemoryTask::is_active() const { +bool G1SegmentedArrayFreeMemoryTask::is_active() const { return _state != State::Inactive; } -jlong G1CardSetFreeMemoryTask::reschedule_delay_ms() const { +jlong G1SegmentedArrayFreeMemoryTask::reschedule_delay_ms() const { return G1RemSetFreeMemoryRescheduleDelayMillis; } -G1CardSetFreeMemoryTask::G1CardSetFreeMemoryTask(const char* name) : +G1SegmentedArrayFreeMemoryTask::G1SegmentedArrayFreeMemoryTask(const char* name) : G1ServiceTask(name), _state(State::CalculateUsed), _return_info(nullptr) { } -void G1CardSetFreeMemoryTask::execute() { +void G1SegmentedArrayFreeMemoryTask::execute() { SuspendibleThreadSetJoiner sts; - if (free_excess_card_set_memory()) { + if (free_excess_segmented_array_memory()) { schedule(reschedule_delay_ms()); } } -void G1CardSetFreeMemoryTask::notify_new_stats(G1CardSetMemoryStats* young_gen_stats, - G1CardSetMemoryStats* collection_set_candidate_stats) { +void G1SegmentedArrayFreeMemoryTask::notify_new_stats(G1SegmentedArrayMemoryStats* young_gen_stats, + G1SegmentedArrayMemoryStats* collection_set_candidate_stats) { assert_at_safepoint_on_vm_thread(); _total_used = *young_gen_stats; diff --git a/src/hotspot/share/gc/g1/g1CardSetFreeMemoryTask.hpp b/src/hotspot/share/gc/g1/g1SegmentedArrayFreeMemoryTask.hpp similarity index 72% rename from src/hotspot/share/gc/g1/g1CardSetFreeMemoryTask.hpp rename to src/hotspot/share/gc/g1/g1SegmentedArrayFreeMemoryTask.hpp index 3d617243aa4c3..a4195cd424c66 100644 --- a/src/hotspot/share/gc/g1/g1CardSetFreeMemoryTask.hpp +++ b/src/hotspot/share/gc/g1/g1SegmentedArrayFreeMemoryTask.hpp @@ -22,17 +22,18 @@ * */ -#ifndef SHARE_GC_G1_G1CARDSETFREEMEMORYTASK_HPP -#define SHARE_GC_G1_G1CARDSETFREEMEMORYTASK_HPP +#ifndef SHARE_GC_G1_G1SEGMENTEDARRAYFREEMEMORYTASK_HPP +#define SHARE_GC_G1_G1SEGMENTEDARRAYFREEMEMORYTASK_HPP -#include "gc/g1/g1ServiceThread.hpp" #include "gc/g1/g1CardSetMemory.hpp" +#include "gc/g1/g1SegmentedArrayFreePool.hpp" +#include "gc/g1/g1ServiceThread.hpp" #include "gc/g1/heapRegionRemSet.hpp" #include "utilities/growableArray.hpp" #include "utilities/ticks.hpp" -// Task handling deallocation of free card set memory. -class G1CardSetFreeMemoryTask : public G1ServiceTask { +// Task handling deallocation of free segmented array memory. +class G1SegmentedArrayFreeMemoryTask : public G1ServiceTask { enum class State : uint { Inactive, @@ -52,11 +53,11 @@ class G1CardSetFreeMemoryTask : public G1ServiceTask { State _state; - // Current total card set memory usage. - G1CardSetMemoryStats _total_used; + // Current total segmented array memory usage. + G1SegmentedArrayMemoryStats _total_used; - typedef G1CardSetFreePool::G1ReturnMemoryProcessor G1ReturnMemoryProcessor; - typedef G1CardSetFreePool::G1ReturnMemoryProcessorSet G1ReturnMemoryProcessorSet; + typedef G1SegmentedArrayFreePool::G1ReturnMemoryProcessor G1ReturnMemoryProcessor; + typedef G1SegmentedArrayFreePool::G1ReturnMemoryProcessorSet G1ReturnMemoryProcessorSet; G1ReturnMemoryProcessorSet* _return_info; @@ -70,9 +71,9 @@ class G1CardSetFreeMemoryTask : public G1ServiceTask { bool return_memory_to_os(jlong deadline); bool cleanup_return_infos(); - // Free excess card set memory, main method. Returns true if there is more work + // Free excess segmented array memory, main method. Returns true if there is more work // to do. - bool free_excess_card_set_memory(); + bool free_excess_segmented_array_memory(); void set_state(State new_state); // Returns whether we are currently processing a recent request. @@ -82,14 +83,14 @@ class G1CardSetFreeMemoryTask : public G1ServiceTask { jlong reschedule_delay_ms() const; public: - explicit G1CardSetFreeMemoryTask(const char* name); + explicit G1SegmentedArrayFreeMemoryTask(const char* name); void execute() override; // Notify the task of new used remembered set memory statistics for the young // generation and the collection set candidate sets. - void notify_new_stats(G1CardSetMemoryStats* young_gen_stats, - G1CardSetMemoryStats* collection_set_candidate_stats); + void notify_new_stats(G1SegmentedArrayMemoryStats* young_gen_stats, + G1SegmentedArrayMemoryStats* collection_set_candidate_stats); }; -#endif // SHARE_GC_G1_G1CARDSETFREEMEMORYTASK_HPP +#endif // SHARE_GC_G1_G1SEGMENTEDARRAYFREEMEMORYTASK_HPP diff --git a/src/hotspot/share/gc/g1/g1SegmentedArrayFreePool.cpp b/src/hotspot/share/gc/g1/g1SegmentedArrayFreePool.cpp new file mode 100644 index 0000000000000..b21f4a1b54500 --- /dev/null +++ b/src/hotspot/share/gc/g1/g1SegmentedArrayFreePool.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" + +#include "gc/g1/g1SegmentedArrayFreePool.hpp" +#include "gc/g1/g1SegmentedArray.inline.hpp" +#include "logging/log.hpp" +#include "memory/allocation.hpp" +#include "runtime/os.hpp" +#include "utilities/formatBuffer.hpp" +#include "utilities/ostream.hpp" + +G1SegmentedArrayMemoryStats::G1SegmentedArrayMemoryStats() { + clear(); +} + +void G1SegmentedArrayMemoryStats::clear() { + for (uint i = 0; i < num_pools(); i++) { + _num_mem_sizes[i] = 0; + _num_segments[i] = 0; + } +} + +template +void G1SegmentedArrayFreePool::update_unlink_processors(G1ReturnMemoryProcessorSet* unlink_processor) { + uint num_free_lists = _freelist_pool.num_free_lists(); + + for (uint i = 0; i < num_free_lists; i++) { + unlink_processor->at(i)->visit_free_list(_freelist_pool.free_list(i)); + } +} + +template +void G1SegmentedArrayFreePool::G1ReturnMemoryProcessor::visit_free_list(G1SegmentedArrayBufferList* source) { + assert(_source == nullptr, "already visited"); + if (_return_to_vm_size > 0) { + _source = source; + } else { + assert(_source == nullptr, "must be"); + } + if (source->mem_size() > _return_to_vm_size) { + _first = source->get_all(_num_unlinked, _unlinked_bytes); + } else { + assert(_first == nullptr, "must be"); + } + // Above we were racing with other threads getting the contents of the free list, + // so while we might have been asked to return something to the OS initially, + // the free list might be empty anyway. In this case just reset internal values + // used for checking whether there is work available. + if (_first == nullptr) { + _source = nullptr; + _return_to_vm_size = 0; + } +} + +template +bool G1SegmentedArrayFreePool::G1ReturnMemoryProcessor::return_to_vm(jlong deadline) { + assert(!finished_return_to_vm(), "already returned everything to the VM"); + assert(_first != nullptr, "must have element to return"); + + size_t keep_size = 0; + size_t keep_num = 0; + + G1SegmentedArrayBuffer* cur = _first; + G1SegmentedArrayBuffer* last = nullptr; + + while (cur != nullptr && _return_to_vm_size > 0) { + size_t cur_size = cur->mem_size(); + _return_to_vm_size -= MIN2(_return_to_vm_size, cur_size); + + keep_size += cur_size; + keep_num++; + + last = cur; + cur = cur->next(); + // To ensure progress, perform the deadline check here. + if (os::elapsed_counter() > deadline) { + break; + } + } + + assert(_first != nullptr, "must be"); + assert(last != nullptr, "must be"); + + last->set_next(nullptr); + + // Wait for any in-progress pops to avoid ABA for them. + GlobalCounter::write_synchronize(); + _source->bulk_add(*_first, *last, keep_num, keep_size); + _first = cur; + + log_trace(gc, task)("Segmented Array Free Memory: Returned to VM %zu buffers size %zu", keep_num, keep_size); + + // _return_to_vm_size may be larger than what is available in the list at the + // time we actually get the list. I.e. the list and _return_to_vm_size may be + // inconsistent. + // So also check if we actually already at the end of the list for the exit + // condition. + if (_return_to_vm_size == 0 || _first == nullptr) { + _source = nullptr; + _return_to_vm_size = 0; + } + return _source != nullptr; +} + +template +bool G1SegmentedArrayFreePool::G1ReturnMemoryProcessor::return_to_os(jlong deadline) { + assert(finished_return_to_vm(), "not finished returning to VM"); + assert(!finished_return_to_os(), "already returned everything to the OS"); + + // Now delete the rest. + size_t num_delete = 0; + size_t mem_size_deleted = 0; + + while (_first != nullptr) { + G1SegmentedArrayBuffer* next = _first->next(); + num_delete++; + mem_size_deleted += _first->mem_size(); + delete _first; + _first = next; + + // To ensure progress, perform the deadline check here. + if (os::elapsed_counter() > deadline) { + break; + } + } + + log_trace(gc, task)("Segmented Array Free Memory: Return to OS %zu buffers size %zu", num_delete, mem_size_deleted); + + return _first != nullptr; +} + +template +G1SegmentedArrayFreePool G1SegmentedArrayFreePool::_freelist_pool(G1CardSetConfiguration::num_mem_object_types()); + +template +G1SegmentedArrayFreePool::G1SegmentedArrayFreePool(uint num_free_lists) : + _num_free_lists(num_free_lists) { + + _free_lists = NEW_C_HEAP_ARRAY(G1SegmentedArrayBufferList, _num_free_lists, mtGC); + for (uint i = 0; i < _num_free_lists; i++) { + new (&_free_lists[i]) G1SegmentedArrayBufferList(); + } +} + +template +G1SegmentedArrayFreePool::~G1SegmentedArrayFreePool() { + for (uint i = 0; i < _num_free_lists; i++) { + _free_lists[i].~G1SegmentedArrayBufferList(); + } + FREE_C_HEAP_ARRAY(mtGC, _free_lists); +} + +template +G1SegmentedArrayMemoryStats G1SegmentedArrayFreePool::memory_sizes() const { + G1SegmentedArrayMemoryStats free_list_stats; + assert(free_list_stats.num_pools() == num_free_lists(), "must be"); + for (uint i = 0; i < num_free_lists(); i++) { + free_list_stats._num_mem_sizes[i] = _free_lists[i].mem_size(); + free_list_stats._num_segments[i] = _free_lists[i].num_buffers(); + } + return free_list_stats; +} + +template +size_t G1SegmentedArrayFreePool::mem_size() const { + size_t result = 0; + for (uint i = 0; i < _num_free_lists; i++) { + result += _free_lists[i].mem_size(); + } + return result; +} + +template +void G1SegmentedArrayFreePool::print_on(outputStream* out) { + out->print_cr(" Free Pool: size %zu", free_list_pool()->mem_size()); + for (uint i = 0; i < _num_free_lists; i++) { + FormatBuffer<> fmt(" %s", G1CardSetConfiguration::mem_object_type_name_str(i)); + _free_lists[i].print_on(out, fmt); + } +} + +template class G1SegmentedArrayFreePool; diff --git a/src/hotspot/share/gc/g1/g1SegmentedArrayFreePool.hpp b/src/hotspot/share/gc/g1/g1SegmentedArrayFreePool.hpp new file mode 100644 index 0000000000000..918e92e39119e --- /dev/null +++ b/src/hotspot/share/gc/g1/g1SegmentedArrayFreePool.hpp @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_GC_G1_G1SEGMENTEDARRAYFREEPOOL_HPP +#define SHARE_GC_G1_G1SEGMENTEDARRAYFREEPOOL_HPP + +#include "gc/g1/g1CardSet.hpp" +#include "gc/g1/g1SegmentedArray.hpp" +#include "utilities/growableArray.hpp" + +// Statistics for a segmented array. Contains the number of segments and memory +// used for each. Note that statistics are typically not taken atomically so there +// can be inconsistencies. The user must be prepared for them. +class G1SegmentedArrayMemoryStats { +public: + + size_t _num_mem_sizes[G1CardSetConfiguration::num_mem_object_types()]; + size_t _num_segments[G1CardSetConfiguration::num_mem_object_types()]; + + // Returns all-zero statistics. + G1SegmentedArrayMemoryStats(); + + void add(G1SegmentedArrayMemoryStats const other) { + STATIC_ASSERT(ARRAY_SIZE(_num_segments) == ARRAY_SIZE(_num_mem_sizes)); + for (uint i = 0; i < ARRAY_SIZE(_num_mem_sizes); i++) { + _num_mem_sizes[i] += other._num_mem_sizes[i]; + _num_segments[i] += other._num_segments[i]; + } + } + + void clear(); + + uint num_pools() const { return G1CardSetConfiguration::num_mem_object_types(); } +}; + +// A set of free lists holding memory buffers for use by G1SegmentedArray, +// e.g. G1CardSetAllocators::SegmentedArray +template +class G1SegmentedArrayFreePool { + // The global free pool. + static G1SegmentedArrayFreePool _freelist_pool; + + const uint _num_free_lists; + G1SegmentedArrayBufferList* _free_lists; + +public: + static G1SegmentedArrayFreePool* free_list_pool() { return &_freelist_pool; } + static G1SegmentedArrayMemoryStats free_list_sizes() { return _freelist_pool.memory_sizes(); } + + class G1ReturnMemoryProcessor; + typedef GrowableArrayCHeap G1ReturnMemoryProcessorSet; + + static void update_unlink_processors(G1ReturnMemoryProcessorSet* unlink_processors); + + explicit G1SegmentedArrayFreePool(uint num_free_lists); + ~G1SegmentedArrayFreePool(); + + G1SegmentedArrayBufferList* free_list(uint i) { + assert(i < _num_free_lists, "must be"); + return &_free_lists[i]; + } + + uint num_free_lists() const { return _num_free_lists; } + + G1SegmentedArrayMemoryStats memory_sizes() const; + size_t mem_size() const; + + void print_on(outputStream* out); +}; + +// Data structure containing current in-progress state for returning memory to the +// operating system for a single G1SegmentedArrayBufferList. +template +class G1SegmentedArrayFreePool::G1ReturnMemoryProcessor : public CHeapObj { + G1SegmentedArrayBufferList* _source; + size_t _return_to_vm_size; + + G1SegmentedArrayBuffer* _first; + size_t _unlinked_bytes; + size_t _num_unlinked; + +public: + explicit G1ReturnMemoryProcessor(size_t return_to_vm) : + _source(nullptr), _return_to_vm_size(return_to_vm), _first(nullptr), _unlinked_bytes(0), _num_unlinked(0) { + } + + // Updates the instance members about the given segmented array buffer list for + // the purpose of giving back memory. Only necessary members are updated, + // e.g. if there is nothing to return to the VM, do not set the source list. + void visit_free_list(G1SegmentedArrayBufferList* source); + + bool finished_return_to_vm() const { return _return_to_vm_size == 0; } + bool finished_return_to_os() const { return _first == nullptr; } + + // Returns memory to the VM until the given deadline expires. Returns true if + // there is no more work. Guarantees forward progress, i.e. at least one buffer + // has been processed after returning. + // return_to_vm() re-adds buffers to the respective free list. + bool return_to_vm(jlong deadline); + // Returns memory to the VM until the given deadline expires. Returns true if + // there is no more work. Guarantees forward progress, i.e. at least one buffer + // has been processed after returning. + // return_to_os() gives back buffers to the OS. + bool return_to_os(jlong deadline); +}; + +#endif //SHARE_GC_G1_G1SEGMENTEDARRAYFREEPOOL_HPP diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index 51fbfbb20dd7c..d476c981d7134 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -29,7 +29,7 @@ #include "compiler/oopMap.hpp" #include "gc/g1/g1Allocator.hpp" #include "gc/g1/g1CardSetMemory.hpp" -#include "gc/g1/g1CollectedHeap.hpp" +#include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1ConcurrentMark.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" @@ -330,7 +330,7 @@ class G1PrepareEvacuationTask : public WorkerTask { uint _worker_humongous_total; uint _worker_humongous_candidates; - G1CardSetMemoryStats _card_set_stats; + G1SegmentedArrayMemoryStats _card_set_stats; void sample_card_set_size(HeapRegion* hr) { // Sample card set sizes for young gen and humongous before GC: this makes @@ -446,7 +446,7 @@ class G1PrepareEvacuationTask : public WorkerTask { return false; } - G1CardSetMemoryStats card_set_stats() const { + G1SegmentedArrayMemoryStats card_set_stats() const { return _card_set_stats; } }; @@ -456,7 +456,7 @@ class G1PrepareEvacuationTask : public WorkerTask { volatile uint _humongous_total; volatile uint _humongous_candidates; - G1CardSetMemoryStats _all_card_set_stats; + G1SegmentedArrayMemoryStats _all_card_set_stats; public: G1PrepareEvacuationTask(G1CollectedHeap* g1h) : @@ -490,7 +490,7 @@ class G1PrepareEvacuationTask : public WorkerTask { return _humongous_total; } - const G1CardSetMemoryStats all_card_set_stats() const { + const G1SegmentedArrayMemoryStats all_card_set_stats() const { return _all_card_set_stats; } }; @@ -987,12 +987,6 @@ void G1YoungCollector::process_discovered_references(G1ParScanThreadStateSet* pe phase_times()->record_ref_proc_time((Ticks::now() - start).seconds() * MILLIUNITS); } -bool G1STWIsAliveClosure::do_object_b(oop p) { - // An object is reachable if it is outside the collection set, - // or is inside and copied. - return !_g1h->is_in_cset(p) || p->is_forwarded(); -} - void G1YoungCollector::post_evacuate_cleanup_1(G1ParScanThreadStateSet* per_thread_states) { Ticks start = Ticks::now(); { diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.hpp b/src/hotspot/share/gc/g1/g1YoungCollector.hpp index 4bc1eaf19421c..055803a335228 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.hpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.hpp @@ -33,7 +33,6 @@ class WorkerTask; class G1Allocator; class G1BatchedTask; -class G1CardSetMemoryStats; class G1CollectedHeap; class G1CollectionSet; class G1CollectorState; @@ -49,6 +48,7 @@ class G1ParScanThreadStateSet; class G1Policy; class G1RedirtyCardsQueueSet; class G1RemSet; +class G1SegmentedArrayMemoryStats; class G1SurvivorRegions; class G1YoungGCEvacFailureInjector; class STWGCTimer; diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp index 2df06a5e3246f..f8e260e21e69d 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp @@ -82,7 +82,7 @@ class G1PostEvacuateCollectionSetCleanupTask1::SampleCollectionSetCandidatesTask class G1SampleCollectionSetCandidatesClosure : public HeapRegionClosure { public: - G1CardSetMemoryStats _total; + G1SegmentedArrayMemoryStats _total; bool do_heap_region(HeapRegion* r) override { _total.add(r->rem_set()->card_set_memory_stats()); diff --git a/src/hotspot/share/gc/g1/heapRegion.hpp b/src/hotspot/share/gc/g1/heapRegion.hpp index 2317861388a7f..cac95b4105fa0 100644 --- a/src/hotspot/share/gc/g1/heapRegion.hpp +++ b/src/hotspot/share/gc/g1/heapRegion.hpp @@ -38,7 +38,6 @@ #include "utilities/macros.hpp" class G1CardSetConfiguration; -class G1CardSetMemoryManager; class G1CollectedHeap; class G1CMBitMap; class G1Predictions; diff --git a/src/hotspot/share/gc/g1/heapRegionRemSet.cpp b/src/hotspot/share/gc/g1/heapRegionRemSet.cpp index a0e740ad26790..694496975f5d3 100644 --- a/src/hotspot/share/gc/g1/heapRegionRemSet.cpp +++ b/src/hotspot/share/gc/g1/heapRegionRemSet.cpp @@ -81,7 +81,7 @@ HeapRegionRemSet::HeapRegionRemSet(HeapRegion* hr, G1CardSetConfiguration* config) : _m(Mutex::service - 1, FormatBuffer<128>("HeapRegionRemSet#%u_lock", hr->hrm_index())), _code_roots(), - _card_set_mm(config, G1CardSetFreePool::free_list_pool()), + _card_set_mm(config, G1SegmentedArrayFreePool::free_list_pool()), _card_set(config, &_card_set_mm), _hr(hr), _state(Untracked) { } @@ -105,6 +105,10 @@ void HeapRegionRemSet::clear_locked(bool only_cardset) { assert(occupied() == 0, "Should be clear."); } +G1SegmentedArrayMemoryStats HeapRegionRemSet::card_set_memory_stats() const { + return _card_set_mm.memory_stats(); +} + void HeapRegionRemSet::print_static_mem_size(outputStream* out) { out->print_cr(" Static structures = " SIZE_FORMAT, HeapRegionRemSet::static_mem_size()); } diff --git a/src/hotspot/share/gc/g1/heapRegionRemSet.hpp b/src/hotspot/share/gc/g1/heapRegionRemSet.hpp index 958d6fceaf189..2a1b3c5527be9 100644 --- a/src/hotspot/share/gc/g1/heapRegionRemSet.hpp +++ b/src/hotspot/share/gc/g1/heapRegionRemSet.hpp @@ -33,6 +33,7 @@ #include "runtime/safepoint.hpp" #include "utilities/bitMap.hpp" +class G1CardSetMemoryManager; class outputStream; class HeapRegionRemSet : public CHeapObj { @@ -125,7 +126,7 @@ class HeapRegionRemSet : public CHeapObj { void clear(bool only_cardset = false); void clear_locked(bool only_cardset = false); - G1CardSetMemoryStats card_set_memory_stats() const { return _card_set_mm.memory_stats(); } + G1SegmentedArrayMemoryStats card_set_memory_stats() const; // The actual # of bytes this hr_remset takes up. Also includes the strong code // root set. diff --git a/src/hotspot/share/gc/g1/heapRegionRemSet.inline.hpp b/src/hotspot/share/gc/g1/heapRegionRemSet.inline.hpp index fc7da0994e48d..eaa2d7f1e56fa 100644 --- a/src/hotspot/share/gc/g1/heapRegionRemSet.inline.hpp +++ b/src/hotspot/share/gc/g1/heapRegionRemSet.inline.hpp @@ -110,7 +110,7 @@ template inline void HeapRegionRemSet::iterate_for_merge(CardOrRangeVisitor& cl) { G1HeapRegionRemSetMergeCardClosure cl2(&_card_set, cl, - _card_set.config()->log2_card_region_per_heap_region(), + _card_set.config()->log2_card_regions_per_heap_region(), _card_set.config()->log2_cards_per_card_region()); _card_set.iterate_containers(&cl2, true /* at_safepoint */); } diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index 0013007a923c4..d0c1f29bd97bb 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -600,7 +600,7 @@ void ParallelScavengeHeap::object_iterate_parallel(ObjectClosure* cl, } } -class PSScavengeParallelObjectIterator : public ParallelObjectIterator { +class PSScavengeParallelObjectIterator : public ParallelObjectIteratorImpl { private: ParallelScavengeHeap* _heap; HeapBlockClaimer _claimer; @@ -615,7 +615,7 @@ class PSScavengeParallelObjectIterator : public ParallelObjectIterator { } }; -ParallelObjectIterator* ParallelScavengeHeap::parallel_object_iterator(uint thread_num) { +ParallelObjectIteratorImpl* ParallelScavengeHeap::parallel_object_iterator(uint thread_num) { return new PSScavengeParallelObjectIterator(); } diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp index 9d8368fc3a4b8..0a81c0b1315f9 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp @@ -231,7 +231,7 @@ class ParallelScavengeHeap : public CollectedHeap { void object_iterate(ObjectClosure* cl); void object_iterate_parallel(ObjectClosure* cl, HeapBlockClaimer* claimer); - virtual ParallelObjectIterator* parallel_object_iterator(uint thread_num); + virtual ParallelObjectIteratorImpl* parallel_object_iterator(uint thread_num); HeapWord* block_start(const void* addr) const; bool block_is_obj(const HeapWord* addr) const; diff --git a/src/hotspot/share/gc/shared/blockOffsetTable.hpp b/src/hotspot/share/gc/shared/blockOffsetTable.hpp index ff656823a6c22..fef56d3120553 100644 --- a/src/hotspot/share/gc/shared/blockOffsetTable.hpp +++ b/src/hotspot/share/gc/shared/blockOffsetTable.hpp @@ -67,17 +67,10 @@ class BOTConstants : public AllStatic { static size_t power_to_cards_back(uint i) { return (size_t)1 << (LogBase * i); } - static size_t power_to_words_back(uint i) { - return power_to_cards_back(i) * N_words; - } static size_t entry_to_cards_back(u_char entry) { assert(entry >= N_words, "Precondition"); return power_to_cards_back(entry - N_words); } - static size_t entry_to_words_back(u_char entry) { - assert(entry >= N_words, "Precondition"); - return power_to_words_back(entry - N_words); - } }; ////////////////////////////////////////////////////////////////////////// diff --git a/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp b/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp index 663ff91372b58..b1828fcdb178a 100644 --- a/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp +++ b/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp @@ -334,7 +334,7 @@ void BarrierSetC1::generate_referent_check(LIRAccess& access, LabelObj* cont) { if (gen_type_check) { // We have determined that offset == referent_offset && src != null. // if (src->_klass->_reference_type == REF_NONE) -> continue - __ move(new LIR_Address(base_reg, oopDesc::klass_offset_in_bytes(), T_ADDRESS), src_klass); + gen->load_klass(base_reg, src_klass, NULL); LIR_Address* reference_type_addr = new LIR_Address(src_klass, in_bytes(InstanceKlass::reference_type_offset()), T_BYTE); LIR_Opr reference_type = gen->new_register(T_INT); __ move(reference_type_addr, reference_type); diff --git a/src/hotspot/share/gc/shared/collectedHeap.cpp b/src/hotspot/share/gc/shared/collectedHeap.cpp index 96b3eb94681b3..e33d464764cc3 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.cpp +++ b/src/hotspot/share/gc/shared/collectedHeap.cpp @@ -110,6 +110,18 @@ void GCHeapLog::log_heap(CollectedHeap* heap, bool before) { st.print_cr("}"); } +ParallelObjectIterator::ParallelObjectIterator(uint thread_num) : + _impl(Universe::heap()->parallel_object_iterator(thread_num)) +{} + +ParallelObjectIterator::~ParallelObjectIterator() { + delete _impl; +} + +void ParallelObjectIterator::object_iterate(ObjectClosure* cl, uint worker_id) { + _impl->object_iterate(cl, worker_id); +} + size_t CollectedHeap::unused() const { MutexLocker ml(Heap_lock); return capacity() - used(); diff --git a/src/hotspot/share/gc/shared/collectedHeap.hpp b/src/hotspot/share/gc/shared/collectedHeap.hpp index 4418a9d31ef5a..681a832982b6f 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.hpp +++ b/src/hotspot/share/gc/shared/collectedHeap.hpp @@ -62,10 +62,23 @@ class VirtualSpaceSummary; class WorkerThreads; class nmethod; -class ParallelObjectIterator : public CHeapObj { +class ParallelObjectIteratorImpl : public CHeapObj { public: + virtual ~ParallelObjectIteratorImpl() {} virtual void object_iterate(ObjectClosure* cl, uint worker_id) = 0; - virtual ~ParallelObjectIterator() {} +}; + +// User facing parallel object iterator. This is a StackObj, which ensures that +// the _impl is allocated and deleted in the scope of this object. This ensures +// the life cycle of the implementation is as required by ThreadsListHandle, +// which is sometimes used by the root iterators. +class ParallelObjectIterator : public StackObj { + ParallelObjectIteratorImpl* _impl; + +public: + ParallelObjectIterator(uint thread_num); + ~ParallelObjectIterator(); + void object_iterate(ObjectClosure* cl, uint worker_id); }; // @@ -82,6 +95,7 @@ class CollectedHeap : public CHeapObj { friend class JVMCIVMStructs; friend class IsGCActiveMark; // Block structured external access to _is_gc_active friend class MemAllocator; + friend class ParallelObjectIterator; private: GCHeapLog* _gc_heap_log; @@ -384,10 +398,12 @@ class CollectedHeap : public CHeapObj { // Iterate over all objects, calling "cl.do_object" on each. virtual void object_iterate(ObjectClosure* cl) = 0; - virtual ParallelObjectIterator* parallel_object_iterator(uint thread_num) { + protected: + virtual ParallelObjectIteratorImpl* parallel_object_iterator(uint thread_num) { return NULL; } + public: // Keep alive an object that was loaded with AS_NO_KEEPALIVE. virtual void keep_alive(oop obj) {} diff --git a/src/hotspot/share/gc/shared/referenceProcessor.cpp b/src/hotspot/share/gc/shared/referenceProcessor.cpp index e5fbc4df3ea3e..b36d746486def 100644 --- a/src/hotspot/share/gc/shared/referenceProcessor.cpp +++ b/src/hotspot/share/gc/shared/referenceProcessor.cpp @@ -198,6 +198,8 @@ ReferenceProcessorStats ReferenceProcessor::process_discovered_references(RefPro update_soft_ref_master_clock(); + phase_times.set_processing_is_mt(processing_is_mt()); + { RefProcTotalPhaseTimesTracker tt(SoftWeakFinalRefsPhase, &phase_times); process_soft_weak_final_refs(proxy_task, phase_times); @@ -730,8 +732,6 @@ void ReferenceProcessor::process_soft_weak_final_refs(RefProcProxyTask& proxy_ta phase_times.set_ref_discovered(REF_WEAK, num_weak_refs); phase_times.set_ref_discovered(REF_FINAL, num_final_refs); - phase_times.set_processing_is_mt(processing_is_mt()); - if (num_total_refs == 0) { log_debug(gc, ref)("Skipped SoftWeakFinalRefsPhase of Reference Processing: no references"); return; @@ -764,7 +764,6 @@ void ReferenceProcessor::process_final_keep_alive(RefProcProxyTask& proxy_task, ReferenceProcessorPhaseTimes& phase_times) { size_t const num_final_refs = total_count(_discoveredFinalRefs); - phase_times.set_processing_is_mt(processing_is_mt()); if (num_final_refs == 0) { log_debug(gc, ref)("Skipped KeepAliveFinalRefsPhase of Reference Processing: no references"); @@ -791,7 +790,6 @@ void ReferenceProcessor::process_phantom_refs(RefProcProxyTask& proxy_task, size_t const num_phantom_refs = total_count(_discoveredPhantomRefs); phase_times.set_ref_discovered(REF_PHANTOM, num_phantom_refs); - phase_times.set_processing_is_mt(processing_is_mt()); if (num_phantom_refs == 0) { log_debug(gc, ref)("Skipped PhantomRefsPhase of Reference Processing: no references"); @@ -1054,15 +1052,6 @@ bool ReferenceProcessor::discover_reference(oop obj, ReferenceType rt) { return true; } -bool ReferenceProcessor::has_discovered_references() { - for (uint i = 0; i < _max_num_queues * number_of_subclasses_of_ref(); i++) { - if (!_discovered_refs[i].is_empty()) { - return true; - } - } - return false; -} - void ReferenceProcessor::preclean_discovered_references(BoolObjectClosure* is_alive, EnqueueDiscoveredFieldClosure* enqueue, YieldClosure* yield, diff --git a/src/hotspot/share/gc/shared/referenceProcessor.hpp b/src/hotspot/share/gc/shared/referenceProcessor.hpp index 120a7afe2cf16..a8ea98683e878 100644 --- a/src/hotspot/share/gc/shared/referenceProcessor.hpp +++ b/src/hotspot/share/gc/shared/referenceProcessor.hpp @@ -421,9 +421,6 @@ class ReferenceProcessor : public ReferenceDiscoverer { // Discover a Reference object, using appropriate discovery criteria virtual bool discover_reference(oop obj, ReferenceType rt); - // Has discovered references that need handling - bool has_discovered_references(); - // Process references found during GC (called by the garbage collector) ReferenceProcessorStats process_discovered_references(RefProcProxyTask& proxy_task, diff --git a/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp b/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp index 87d60b1a6c0b7..42c39d70b108e 100644 --- a/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp +++ b/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp @@ -200,7 +200,6 @@ void ReferenceProcessorPhaseTimes::set_phase_time_ms(ReferenceProcessor::RefProc void ReferenceProcessorPhaseTimes::reset() { for (int i = 0; i < ReferenceProcessor::RefSubPhaseMax; i++) { _sub_phases_worker_time_sec[i]->reset(); - _sub_phases_total_time_ms[i] = uninitialized(); } for (int i = 0; i < ReferenceProcessor::RefPhaseMax; i++) { @@ -227,17 +226,6 @@ ReferenceProcessorPhaseTimes::~ReferenceProcessorPhaseTimes() { delete _soft_weak_final_refs_phase_worker_time_sec; } -double ReferenceProcessorPhaseTimes::sub_phase_total_time_ms(ReferenceProcessor::RefProcSubPhases sub_phase) const { - ASSERT_SUB_PHASE(sub_phase); - return _sub_phases_total_time_ms[sub_phase]; -} - -void ReferenceProcessorPhaseTimes::set_sub_phase_total_phase_time_ms(ReferenceProcessor::RefProcSubPhases sub_phase, - double time_ms) { - ASSERT_SUB_PHASE(sub_phase); - _sub_phases_total_time_ms[sub_phase] = time_ms; -} - void ReferenceProcessorPhaseTimes::add_ref_cleared(ReferenceType ref_type, size_t count) { ASSERT_REF_TYPE(ref_type); Atomic::add(&_ref_cleared[ref_type_2_index(ref_type)], count, memory_order_relaxed); diff --git a/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.hpp b/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.hpp index 5d1f2d2eeb7c6..2c062b94c3dbe 100644 --- a/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.hpp +++ b/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.hpp @@ -41,8 +41,6 @@ class ReferenceProcessorPhaseTimes : public CHeapObj { // Records per thread time information of each sub phase. WorkerDataArray* _sub_phases_worker_time_sec[ReferenceProcessor::RefSubPhaseMax]; - // Total time of each sub phase. - double _sub_phases_total_time_ms[ReferenceProcessor::RefSubPhaseMax]; // Records total elapsed time for each phase. double _phases_time_ms[ReferenceProcessor::RefPhaseMax]; @@ -62,7 +60,6 @@ class ReferenceProcessorPhaseTimes : public CHeapObj { GCTimer* _gc_timer; double phase_time_ms(ReferenceProcessor::RefProcPhases phase) const; - double sub_phase_total_time_ms(ReferenceProcessor::RefProcSubPhases sub_phase) const; double total_time_ms() const { return _total_time_ms; } @@ -84,8 +81,6 @@ class ReferenceProcessorPhaseTimes : public CHeapObj { WorkerDataArray* sub_phase_worker_time_sec(ReferenceProcessor::RefProcSubPhases phase) const; void set_phase_time_ms(ReferenceProcessor::RefProcPhases phase, double par_phase_time_ms); - void set_sub_phase_total_phase_time_ms(ReferenceProcessor::RefProcSubPhases sub_phase, double ref_proc_time_ms); - void set_total_time_ms(double total_time_ms) { _total_time_ms = total_time_ms; } void add_ref_cleared(ReferenceType ref_type, size_t count); diff --git a/src/hotspot/share/gc/shared/workerDataArray.cpp b/src/hotspot/share/gc/shared/workerDataArray.cpp index b71406622109f..53f337560d35c 100644 --- a/src/hotspot/share/gc/shared/workerDataArray.cpp +++ b/src/hotspot/share/gc/shared/workerDataArray.cpp @@ -36,16 +36,6 @@ double WorkerDataArray::uninitialized() { return -1.0; } -template <> -void WorkerDataArray::WDAPrinter::summary(outputStream* out, double time) { - out->print_cr(" %.1lfms", time * MILLIUNITS); -} - -template <> -void WorkerDataArray::WDAPrinter::summary(outputStream* out, size_t value) { - out->print_cr(" " SIZE_FORMAT, value); -} - template <> void WorkerDataArray::WDAPrinter::summary(outputStream* out, double min, double avg, double max, double diff, double sum, bool print_sum) { out->print(" Min: %4.1lf, Avg: %4.1lf, Max: %4.1lf, Diff: %4.1lf", min * MILLIUNITS, avg * MILLIUNITS, max * MILLIUNITS, diff* MILLIUNITS); diff --git a/src/hotspot/share/gc/shared/workerDataArray.hpp b/src/hotspot/share/gc/shared/workerDataArray.hpp index 8908afbc8f9fa..b2a81bc948240 100644 --- a/src/hotspot/share/gc/shared/workerDataArray.hpp +++ b/src/hotspot/share/gc/shared/workerDataArray.hpp @@ -46,7 +46,7 @@ class WorkerDataArray : public CHeapObj { WorkerDataArray* _thread_work_items[MaxThreadWorkItems]; public: - WorkerDataArray(const char* short_name, const char* title, uint length, bool is_serial = false); + WorkerDataArray(const char* short_name, const char* title, uint length); ~WorkerDataArray(); // Create an integer sub-item at the given index to this WorkerDataArray. If length_override @@ -91,9 +91,7 @@ class WorkerDataArray : public CHeapObj { private: class WDAPrinter { public: - static void summary(outputStream* out, double time); static void summary(outputStream* out, double min, double avg, double max, double diff, double sum, bool print_sum); - static void summary(outputStream* out, size_t value); static void summary(outputStream* out, size_t min, double avg, size_t max, size_t diff, size_t sum, bool print_sum); static void details(const WorkerDataArray* phase, outputStream* out); diff --git a/src/hotspot/share/gc/shared/workerDataArray.inline.hpp b/src/hotspot/share/gc/shared/workerDataArray.inline.hpp index 3deb734f20d95..283fb97af8d2f 100644 --- a/src/hotspot/share/gc/shared/workerDataArray.inline.hpp +++ b/src/hotspot/share/gc/shared/workerDataArray.inline.hpp @@ -31,14 +31,12 @@ #include "utilities/ostream.hpp" template -WorkerDataArray::WorkerDataArray(const char* short_name, const char* title, uint length, bool is_serial) : +WorkerDataArray::WorkerDataArray(const char* short_name, const char* title, uint length) : _data(NULL), _length(length), _short_name(short_name), - _title(title), - _is_serial(is_serial) { + _title(title) { assert(length > 0, "Must have some workers to store data for"); - assert(!is_serial || length == 1, "Serial phase must only have a single entry."); _data = NEW_C_HEAP_ARRAY(T, _length, mtGC); for (uint i = 0; i < MaxThreadWorkItems; i++) { _thread_work_items[i] = NULL; @@ -158,39 +156,31 @@ void WorkerDataArray::set_all(T value) { template void WorkerDataArray::print_summary_on(outputStream* out, bool print_sum) const { - if (_is_serial) { - out->print("%s:", title()); - } else { - out->print("%-30s", title()); - } + out->print("%-30s", title()); uint start = 0; while (start < _length && get(start) == uninitialized()) { start++; } if (start < _length) { - if (_is_serial) { - WDAPrinter::summary(out, get(0)); - } else { - T min = get(start); - T max = min; - T sum = 0; - uint contributing_threads = 0; - for (uint i = start; i < _length; ++i) { - T value = get(i); - if (value != uninitialized()) { - max = MAX2(max, value); - min = MIN2(min, value); - sum += value; - contributing_threads++; - } + T min = get(start); + T max = min; + T sum = 0; + uint contributing_threads = 0; + for (uint i = start; i < _length; ++i) { + T value = get(i); + if (value != uninitialized()) { + max = MAX2(max, value); + min = MIN2(min, value); + sum += value; + contributing_threads++; } - T diff = max - min; - assert(contributing_threads != 0, "Must be since we found a used value for the start index"); - double avg = sum / (double) contributing_threads; - WDAPrinter::summary(out, min, avg, max, diff, sum, print_sum); - out->print_cr(", Workers: %d", contributing_threads); } + T diff = max - min; + assert(contributing_threads != 0, "Must be since we found a used value for the start index"); + double avg = sum / (double) contributing_threads; + WDAPrinter::summary(out, min, avg, max, diff, sum, print_sum); + out->print_cr(", Workers: %d", contributing_threads); } else { // No data for this phase. out->print_cr(" skipped"); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 7cd9a61b29f43..8cbcc86172b61 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -1366,7 +1366,7 @@ class ShenandoahObjectIterateParScanClosure : public BasicOopIterateClosure { // parallel marking queues. // Every worker processes it's own marking queue. work-stealing is used // to balance workload. -class ShenandoahParallelObjectIterator : public ParallelObjectIterator { +class ShenandoahParallelObjectIterator : public ParallelObjectIteratorImpl { private: uint _num_workers; bool _init_ready; @@ -1465,7 +1465,7 @@ class ShenandoahParallelObjectIterator : public ParallelObjectIterator { } }; -ParallelObjectIterator* ShenandoahHeap::parallel_object_iterator(uint workers) { +ParallelObjectIteratorImpl* ShenandoahHeap::parallel_object_iterator(uint workers) { return new ShenandoahParallelObjectIterator(workers, &_aux_bit_map); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index 79afeb37ab59f..1ec9d7051cfcf 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -484,7 +484,7 @@ class ShenandoahHeap : public CollectedHeap { // Used for native heap walkers: heap dumpers, mostly void object_iterate(ObjectClosure* cl); // Parallel heap iteration support - virtual ParallelObjectIterator* parallel_object_iterator(uint workers); + virtual ParallelObjectIteratorImpl* parallel_object_iterator(uint workers); // Keep alive an object that was loaded with AS_NO_KEEPALIVE. void keep_alive(oop obj); diff --git a/src/hotspot/share/gc/z/zCollectedHeap.cpp b/src/hotspot/share/gc/z/zCollectedHeap.cpp index 1d09912231575..ea883450da084 100644 --- a/src/hotspot/share/gc/z/zCollectedHeap.cpp +++ b/src/hotspot/share/gc/z/zCollectedHeap.cpp @@ -226,7 +226,7 @@ void ZCollectedHeap::object_iterate(ObjectClosure* cl) { _heap.object_iterate(cl, true /* visit_weaks */); } -ParallelObjectIterator* ZCollectedHeap::parallel_object_iterator(uint nworkers) { +ParallelObjectIteratorImpl* ZCollectedHeap::parallel_object_iterator(uint nworkers) { return _heap.parallel_object_iterator(nworkers, true /* visit_weaks */); } diff --git a/src/hotspot/share/gc/z/zCollectedHeap.hpp b/src/hotspot/share/gc/z/zCollectedHeap.hpp index 236609361d02d..12f6902a025a5 100644 --- a/src/hotspot/share/gc/z/zCollectedHeap.hpp +++ b/src/hotspot/share/gc/z/zCollectedHeap.hpp @@ -95,7 +95,7 @@ class ZCollectedHeap : public CollectedHeap { virtual GrowableArray memory_pools(); virtual void object_iterate(ObjectClosure* cl); - virtual ParallelObjectIterator* parallel_object_iterator(uint nworkers); + virtual ParallelObjectIteratorImpl* parallel_object_iterator(uint nworkers); virtual void keep_alive(oop obj); diff --git a/src/hotspot/share/gc/z/zHeap.cpp b/src/hotspot/share/gc/z/zHeap.cpp index 3bf3cf50ed8ee..b937a645f6264 100644 --- a/src/hotspot/share/gc/z/zHeap.cpp +++ b/src/hotspot/share/gc/z/zHeap.cpp @@ -439,7 +439,7 @@ void ZHeap::object_iterate(ObjectClosure* cl, bool visit_weaks) { iter.object_iterate(cl, 0 /* worker_id */); } -ParallelObjectIterator* ZHeap::parallel_object_iterator(uint nworkers, bool visit_weaks) { +ParallelObjectIteratorImpl* ZHeap::parallel_object_iterator(uint nworkers, bool visit_weaks) { assert(SafepointSynchronize::is_at_safepoint(), "Should be at safepoint"); return new ZHeapIterator(nworkers, visit_weaks); } diff --git a/src/hotspot/share/gc/z/zHeap.hpp b/src/hotspot/share/gc/z/zHeap.hpp index f4e36ad738e0b..723ec52d39cd6 100644 --- a/src/hotspot/share/gc/z/zHeap.hpp +++ b/src/hotspot/share/gc/z/zHeap.hpp @@ -141,7 +141,7 @@ class ZHeap { // Iteration void object_iterate(ObjectClosure* cl, bool visit_weaks); - ParallelObjectIterator* parallel_object_iterator(uint nworkers, bool visit_weaks); + ParallelObjectIteratorImpl* parallel_object_iterator(uint nworkers, bool visit_weaks); void pages_do(ZPageClosure* cl); // Serviceability diff --git a/src/hotspot/share/gc/z/zHeapIterator.hpp b/src/hotspot/share/gc/z/zHeapIterator.hpp index bbd3eb203c374..5c3a82d8bb7d9 100644 --- a/src/hotspot/share/gc/z/zHeapIterator.hpp +++ b/src/hotspot/share/gc/z/zHeapIterator.hpp @@ -42,7 +42,7 @@ using ZHeapIteratorQueues = GenericTaskQueueSet; using ZHeapIteratorArrayQueue = OverflowTaskQueue; using ZHeapIteratorArrayQueues = GenericTaskQueueSet; -class ZHeapIterator : public ParallelObjectIterator { +class ZHeapIterator : public ParallelObjectIteratorImpl { friend class ZHeapIteratorContext; private: diff --git a/src/hotspot/share/gc/z/zStat.cpp b/src/hotspot/share/gc/z/zStat.cpp index 0df9d061087c7..540c171ae9f7f 100644 --- a/src/hotspot/share/gc/z/zStat.cpp +++ b/src/hotspot/share/gc/z/zStat.cpp @@ -31,6 +31,7 @@ #include "gc/z/zPageAllocator.inline.hpp" #include "gc/z/zRelocationSetSelector.inline.hpp" #include "gc/z/zStat.hpp" +#include "gc/z/zThread.inline.hpp" #include "gc/z/zTracer.inline.hpp" #include "gc/z/zUtils.hpp" #include "memory/metaspaceUtils.hpp" @@ -736,8 +737,13 @@ ZStatSubPhase::ZStatSubPhase(const char* name) : ZStatPhase("Subphase", name) {} void ZStatSubPhase::register_start(const Ticks& start) const { - LogTarget(Debug, gc, phases, start) log; - log_start(log, true /* thread */); + if (ZThread::is_worker()) { + LogTarget(Trace, gc, phases, start) log; + log_start(log, true /* thread */); + } else { + LogTarget(Debug, gc, phases, start) log; + log_start(log, false /* thread */); + } } void ZStatSubPhase::register_end(const Ticks& start, const Ticks& end) const { @@ -750,8 +756,13 @@ void ZStatSubPhase::register_end(const Ticks& start, const Ticks& end) const { const Tickspan duration = end - start; ZStatSample(_sampler, duration.value()); - LogTarget(Debug, gc, phases) log; - log_end(log, duration, true /* thread */); + if (ZThread::is_worker()) { + LogTarget(Trace, gc, phases) log; + log_end(log, duration, true /* thread */); + } else { + LogTarget(Debug, gc, phases) log; + log_end(log, duration, false /* thread */); + } } ZStatCriticalPhase::ZStatCriticalPhase(const char* name, bool verbose) : diff --git a/src/hotspot/share/gc/z/zTracer.cpp b/src/hotspot/share/gc/z/zTracer.cpp index 2a0d3e11a7937..b7d0bc0a697e5 100644 --- a/src/hotspot/share/gc/z/zTracer.cpp +++ b/src/hotspot/share/gc/z/zTracer.cpp @@ -131,3 +131,16 @@ void ZTracer::send_thread_phase(const char* name, const Ticks& start, const Tick e.commit(); } } + +void ZTracer::send_thread_debug(const char* name, const Ticks& start, const Ticks& end) { + NoSafepointVerifier nsv; + + EventZThreadDebug e(UNTIMED); + if (e.should_commit()) { + e.set_gcId(GCId::current_or_undefined()); + e.set_name(name); + e.set_starttime(start); + e.set_endtime(end); + e.commit(); + } +} diff --git a/src/hotspot/share/gc/z/zTracer.hpp b/src/hotspot/share/gc/z/zTracer.hpp index c39a193168ba2..3ec97dfa4cbcb 100644 --- a/src/hotspot/share/gc/z/zTracer.hpp +++ b/src/hotspot/share/gc/z/zTracer.hpp @@ -39,6 +39,7 @@ class ZTracer : public GCTracer { void send_stat_counter(const ZStatCounter& counter, uint64_t increment, uint64_t value); void send_stat_sampler(const ZStatSampler& sampler, uint64_t value); void send_thread_phase(const char* name, const Ticks& start, const Ticks& end); + void send_thread_debug(const char* name, const Ticks& start, const Ticks& end); public: static ZTracer* tracer(); @@ -47,16 +48,18 @@ class ZTracer : public GCTracer { void report_stat_counter(const ZStatCounter& counter, uint64_t increment, uint64_t value); void report_stat_sampler(const ZStatSampler& sampler, uint64_t value); void report_thread_phase(const char* name, const Ticks& start, const Ticks& end); + void report_thread_debug(const char* name, const Ticks& start, const Ticks& end); }; -class ZTraceThreadPhase : public StackObj { +// For temporary latency measurements during development and debugging +class ZTraceThreadDebug : public StackObj { private: const Ticks _start; const char* const _name; public: - ZTraceThreadPhase(const char* name); - ~ZTraceThreadPhase(); + ZTraceThreadDebug(const char* name); + ~ZTraceThreadDebug(); }; #endif // SHARE_GC_Z_ZTRACER_HPP diff --git a/src/hotspot/share/gc/z/zTracer.inline.hpp b/src/hotspot/share/gc/z/zTracer.inline.hpp index 143eebb4d3b63..cfaf7b43f0c46 100644 --- a/src/hotspot/share/gc/z/zTracer.inline.hpp +++ b/src/hotspot/share/gc/z/zTracer.inline.hpp @@ -50,12 +50,18 @@ inline void ZTracer::report_thread_phase(const char* name, const Ticks& start, c } } -inline ZTraceThreadPhase::ZTraceThreadPhase(const char* name) : +inline void ZTracer::report_thread_debug(const char* name, const Ticks& start, const Ticks& end) { + if (EventZThreadDebug::is_enabled()) { + send_thread_debug(name, start, end); + } +} + +inline ZTraceThreadDebug::ZTraceThreadDebug(const char* name) : _start(Ticks::now()), _name(name) {} -inline ZTraceThreadPhase::~ZTraceThreadPhase() { - ZTracer::tracer()->report_thread_phase(_name, _start, Ticks::now()); +inline ZTraceThreadDebug::~ZTraceThreadDebug() { + ZTracer::tracer()->report_thread_debug(_name, _start, Ticks::now()); } #endif // SHARE_GC_Z_ZTRACER_INLINE_HPP diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp index 6b178d625602d..5e61410ddc204 100644 --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp @@ -407,7 +407,11 @@ JRT_ENTRY(void, InterpreterRuntime::create_klass_exception(JavaThread* current, // lookup exception klass TempNewSymbol s = SymbolTable::new_symbol(name); if (ProfileTraps) { - note_trap(current, Deoptimization::Reason_class_check); + if (s == vmSymbols::java_lang_ArrayStoreException()) { + note_trap(current, Deoptimization::Reason_array_check); + } else { + note_trap(current, Deoptimization::Reason_class_check); + } } // create exception, with klass name as detail message Handle exception = Exceptions::new_exception(current, s, klass_name); @@ -825,7 +829,18 @@ void InterpreterRuntime::resolve_invoke(JavaThread* current, Bytecodes::Code byt JavaThread* THREAD = current; // For exception macros. LinkResolver::resolve_invoke(info, receiver, pool, last_frame.get_index_u2_cpcache(bytecode), bytecode, - CHECK); + THREAD); + + if (HAS_PENDING_EXCEPTION) { + if (ProfileTraps && PENDING_EXCEPTION->klass()->name() == vmSymbols::java_lang_NullPointerException()) { + // Preserve the original exception across the call to note_trap() + PreserveExceptionMark pm(current); + // Recording the trap will help the compiler to potentially recognize this exception as "hot" + note_trap(current, Deoptimization::Reason_null_check); + } + return; + } + if (JvmtiExport::can_hotswap_or_post_breakpoint() && info.resolved_method()->is_old()) { resolved_method = methodHandle(current, info.resolved_method()->get_new_method()); } else { diff --git a/src/hotspot/share/jfr/metadata/metadata.xml b/src/hotspot/share/jfr/metadata/metadata.xml index 6a1f426785c9d..2cc8cc10bea40 100644 --- a/src/hotspot/share/jfr/metadata/metadata.xml +++ b/src/hotspot/share/jfr/metadata/metadata.xml @@ -1034,6 +1034,11 @@ + + + + + diff --git a/src/hotspot/share/memory/heapInspection.cpp b/src/hotspot/share/memory/heapInspection.cpp index b4dd596b50f94..da5a793cb919f 100644 --- a/src/hotspot/share/memory/heapInspection.cpp +++ b/src/hotspot/share/memory/heapInspection.cpp @@ -578,18 +578,12 @@ uintx HeapInspection::populate_table(KlassInfoTable* cit, BoolObjectClosure *fil const uint capped_parallel_thread_num = MIN2(parallel_thread_num, workers->max_workers()); WithActiveWorkers with_active_workers(workers, capped_parallel_thread_num); - ParallelObjectIterator* poi = Universe::heap()->parallel_object_iterator(workers->active_workers()); - if (poi != NULL) { - // The GC supports parallel object iteration. - - ParHeapInspectTask task(poi, cit, filter); - // Run task with the active workers. - workers->run_task(&task); - - delete poi; - if (task.success()) { - return task.missed_count(); - } + ParallelObjectIterator poi(workers->active_workers()); + ParHeapInspectTask task(&poi, cit, filter); + // Run task with the active workers. + workers->run_task(&task); + if (task.success()) { + return task.missed_count(); } } } diff --git a/src/hotspot/share/memory/metaspaceCriticalAllocation.cpp b/src/hotspot/share/memory/metaspaceCriticalAllocation.cpp index 1edb67bf7f42d..08400e5b5d4a7 100644 --- a/src/hotspot/share/memory/metaspaceCriticalAllocation.cpp +++ b/src/hotspot/share/memory/metaspaceCriticalAllocation.cpp @@ -113,10 +113,12 @@ void MetaspaceCriticalAllocation::remove(MetadataAllocationRequest* request) { } bool MetaspaceCriticalAllocation::try_allocate_critical(MetadataAllocationRequest* request) { - MutexLocker ml(MetaspaceCritical_lock, Mutex::_no_safepoint_check_flag); - if (_requests_head == request) { - // The first request can't opportunistically ride on a previous GC - return false; + { + MutexLocker ml(MetaspaceCritical_lock, Mutex::_no_safepoint_check_flag); + if (_requests_head == request) { + // The first request can't opportunistically ride on a previous GC + return false; + } } // Try to ride on a previous GC and hope for early satisfaction wait_for_purge(request); @@ -124,8 +126,12 @@ bool MetaspaceCriticalAllocation::try_allocate_critical(MetadataAllocationReques } void MetaspaceCriticalAllocation::wait_for_purge(MetadataAllocationRequest* request) { - while (!request->has_result()) { - ThreadBlockInVM tbivm(JavaThread::current()); + ThreadBlockInVM tbivm(JavaThread::current()); + MutexLocker ml(MetaspaceCritical_lock, Mutex::_no_safepoint_check_flag); + for (;;) { + if (request->has_result()) { + break; + } MetaspaceCritical_lock->wait_without_safepoint_check(); } } diff --git a/src/hotspot/share/oops/methodData.cpp b/src/hotspot/share/oops/methodData.cpp index 4146f4a820009..bc0b175f51100 100644 --- a/src/hotspot/share/oops/methodData.cpp +++ b/src/hotspot/share/oops/methodData.cpp @@ -1586,18 +1586,6 @@ bool MethodData::profile_unsafe(const methodHandle& m, int bci) { return false; } -bool MethodData::profile_memory_access(const methodHandle& m, int bci) { - Bytecode_invoke inv(m , bci); - if (inv.is_invokestatic()) { - if (inv.klass() == vmSymbols::jdk_incubator_foreign_MemoryAccess()) { - if (inv.name()->starts_with("get") || inv.name()->starts_with("set")) { - return true; - } - } - } - return false; -} - int MethodData::profile_arguments_flag() { return TypeProfileLevel % 10; } @@ -1627,10 +1615,6 @@ bool MethodData::profile_arguments_for_invoke(const methodHandle& m, int bci) { return true; } - if (profile_memory_access(m, bci)) { - return true; - } - assert(profile_arguments_jsr292_only(), "inconsistent"); return profile_jsr292(m, bci); } diff --git a/src/hotspot/share/opto/bytecodeInfo.cpp b/src/hotspot/share/opto/bytecodeInfo.cpp index c6bc8f8eb94db..40d0920153044 100644 --- a/src/hotspot/share/opto/bytecodeInfo.cpp +++ b/src/hotspot/share/opto/bytecodeInfo.cpp @@ -46,6 +46,7 @@ InlineTree::InlineTree(Compile* c, C(c), _caller_jvms(caller_jvms), _method(callee), + _late_inline(false), _caller_tree((InlineTree*) caller_tree), _count_inline_bcs(method()->code_size_for_inlining()), _max_inline_level(max_inline_level), @@ -113,7 +114,7 @@ static bool is_unboxing_method(ciMethod* callee_method, Compile* C) { // positive filter: should callee be inlined? bool InlineTree::should_inline(ciMethod* callee_method, ciMethod* caller_method, - int caller_bci, ciCallProfile& profile) { + int caller_bci, NOT_PRODUCT_ARG(bool& should_delay) ciCallProfile& profile) { // Allows targeted inlining if (C->directive()->should_inline(callee_method)) { set_msg("force inline by CompileCommand"); @@ -128,9 +129,13 @@ bool InlineTree::should_inline(ciMethod* callee_method, ciMethod* caller_method, } #ifndef PRODUCT - int inline_depth = inline_level()+1; - if (ciReplay::should_inline(C->replay_inline_data(), callee_method, caller_bci, inline_depth)) { - set_msg("force inline by ciReplay"); + int inline_depth = inline_level() + 1; + if (ciReplay::should_inline(C->replay_inline_data(), callee_method, caller_bci, inline_depth, should_delay)) { + if (should_delay) { + set_msg("force (incremental) inline by ciReplay"); + } else { + set_msg("force inline by ciReplay"); + } _forced_inline = true; return true; } @@ -194,7 +199,7 @@ bool InlineTree::should_inline(ciMethod* callee_method, ciMethod* caller_method, // negative filter: should callee NOT be inlined? bool InlineTree::should_not_inline(ciMethod* callee_method, ciMethod* caller_method, - int caller_bci, ciCallProfile& profile) { + int caller_bci, NOT_PRODUCT_ARG(bool& should_delay) ciCallProfile& profile) { const char* fail_msg = NULL; // First check all inlining restrictions which are required for correctness @@ -232,9 +237,13 @@ bool InlineTree::should_not_inline(ciMethod* callee_method, ciMethod* caller_met } #ifndef PRODUCT - int inline_depth = inline_level()+1; - if (ciReplay::should_inline(C->replay_inline_data(), callee_method, caller_bci, inline_depth)) { - set_msg("force inline by ciReplay"); + int inline_depth = inline_level() + 1; + if (ciReplay::should_inline(C->replay_inline_data(), callee_method, caller_bci, inline_depth, should_delay)) { + if (should_delay) { + set_msg("force (incremental) inline by ciReplay"); + } else { + set_msg("force inline by ciReplay"); + } return false; } @@ -369,10 +378,13 @@ bool InlineTree::try_to_inline(ciMethod* callee_method, ciMethod* caller_method, } _forced_inline = false; // Reset - if (!should_inline(callee_method, caller_method, caller_bci, profile)) { + + // 'should_delay' can be overridden during replay compilation + if (!should_inline(callee_method, caller_method, caller_bci, NOT_PRODUCT_ARG(should_delay) profile)) { return false; } - if (should_not_inline(callee_method, caller_method, caller_bci, profile)) { + // 'should_delay' can be overridden during replay compilation + if (should_not_inline(callee_method, caller_method, caller_bci, NOT_PRODUCT_ARG(should_delay) profile)) { return false; } @@ -557,9 +569,8 @@ void InlineTree::print_inlining(ciMethod* callee_method, int caller_bci, //------------------------------ok_to_inline----------------------------------- bool InlineTree::ok_to_inline(ciMethod* callee_method, JVMState* jvms, ciCallProfile& profile, bool& should_delay) { - assert(callee_method != NULL, "caller checks for optimized virtual!"); - assert(!should_delay, "should be initialized to false"); #ifdef ASSERT + assert(callee_method != NULL, "caller checks for optimized virtual!"); // Make sure the incoming jvms has the same information content as me. // This means that we can eventually make this whole class AllStatic. if (jvms->caller() == NULL) { @@ -595,7 +606,11 @@ bool InlineTree::ok_to_inline(ciMethod* callee_method, JVMState* jvms, ciCallPro set_msg("inline (hot)"); } print_inlining(callee_method, caller_bci, caller_method, true /* success */); - build_inline_tree_for_callee(callee_method, jvms, caller_bci); + InlineTree* callee_tree = build_inline_tree_for_callee(callee_method, jvms, caller_bci); + if (should_delay) { + // Record late inlining decision in order to dump it for compiler replay + callee_tree->set_late_inline(); + } return true; } else { // Do not inline @@ -700,7 +715,7 @@ int InlineTree::count() const { } void InlineTree::dump_replay_data(outputStream* out) { - out->print(" %d %d ", inline_level(), caller_bci()); + out->print(" %d %d %d ", inline_level(), caller_bci(), _late_inline); method()->dump_name_as_ascii(out); for (int i = 0 ; i < _subtrees.length(); i++) { _subtrees.at(i)->dump_replay_data(out); diff --git a/src/hotspot/share/opto/doCall.cpp b/src/hotspot/share/opto/doCall.cpp index 283a364dfecd4..6746ce02967f4 100644 --- a/src/hotspot/share/opto/doCall.cpp +++ b/src/hotspot/share/opto/doCall.cpp @@ -164,7 +164,7 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool // Try inlining a bytecoded method: if (!call_does_dispatch) { InlineTree* ilt = InlineTree::find_subtree_from_root(this->ilt(), jvms->caller(), jvms->method()); - bool should_delay = false; + bool should_delay = AlwaysIncrementalInline; if (ilt->ok_to_inline(callee, jvms, profile, should_delay)) { CallGenerator* cg = CallGenerator::for_inline(callee, expected_uses); // For optimized virtual calls assert at runtime that receiver object @@ -189,7 +189,7 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool return CallGenerator::for_boxing_late_inline(callee, cg); } else if (should_delay_vector_reboxing_inlining(callee, jvms)) { return CallGenerator::for_vector_reboxing_late_inline(callee, cg); - } else if ((should_delay || AlwaysIncrementalInline)) { + } else if (should_delay) { return CallGenerator::for_late_inline(callee, cg); } else { return cg; diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index cafbc0aaa7ccc..10939bcee6397 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -580,11 +580,10 @@ void GraphKit::builtin_throw(Deoptimization::DeoptReason reason, Node* arg) { ex_obj = env()->ArrayIndexOutOfBoundsException_instance(); break; case Deoptimization::Reason_class_check: - if (java_bc() == Bytecodes::_aastore) { - ex_obj = env()->ArrayStoreException_instance(); - } else { - ex_obj = env()->ClassCastException_instance(); - } + ex_obj = env()->ClassCastException_instance(); + break; + case Deoptimization::Reason_array_check: + ex_obj = env()->ArrayStoreException_instance(); break; default: break; @@ -3340,7 +3339,10 @@ Node* GraphKit::gen_checkcast(Node *obj, Node* superklass, // It needs a null check because a null will *pass* the cast check. // A non-null value will always produce an exception. if (!objtp->maybe_null()) { - builtin_throw(Deoptimization::Reason_class_check, makecon(TypeKlassPtr::make(objtp->klass()))); + bool is_aastore = (java_bc() == Bytecodes::_aastore); + Deoptimization::DeoptReason reason = is_aastore ? + Deoptimization::Reason_array_check : Deoptimization::Reason_class_check; + builtin_throw(reason, makecon(TypeKlassPtr::make(objtp->klass()))); return top(); } else if (!too_many_traps_or_recompiles(Deoptimization::Reason_null_assert)) { return null_assert(obj); @@ -3422,7 +3424,10 @@ Node* GraphKit::gen_checkcast(Node *obj, Node* superklass, if (not_subtype_ctrl != top()) { // If failure is possible PreserveJVMState pjvms(this); set_control(not_subtype_ctrl); - builtin_throw(Deoptimization::Reason_class_check, load_object_klass(not_null_obj)); + bool is_aastore = (java_bc() == Bytecodes::_aastore); + Deoptimization::DeoptReason reason = is_aastore ? + Deoptimization::Reason_array_check : Deoptimization::Reason_class_check; + builtin_throw(reason, load_object_klass(not_null_obj)); } } else { (*failure_control) = not_subtype_ctrl; diff --git a/src/hotspot/share/opto/idealGraphPrinter.cpp b/src/hotspot/share/opto/idealGraphPrinter.cpp index 736e796c16cdf..cb403853f33c8 100644 --- a/src/hotspot/share/opto/idealGraphPrinter.cpp +++ b/src/hotspot/share/opto/idealGraphPrinter.cpp @@ -418,6 +418,14 @@ void IdealGraphPrinter::visit_node(Node *n, bool edges, VectorSet* temp_set) { break; } + Node_Notes* nn = C->node_notes_at(node->_idx); + if (nn != NULL && !nn->is_clear() && nn->jvms() != NULL) { + buffer[0] = 0; + stringStream ss(buffer, sizeof(buffer) - 1); + nn->jvms()->dump_spec(&ss); + print_prop("jvms", buffer); + } + const jushort flags = node->flags(); if (flags & Node::Flag_is_Copy) { print_prop("is_copy", "true"); diff --git a/src/hotspot/share/opto/loopPredicate.cpp b/src/hotspot/share/opto/loopPredicate.cpp index 1378f80331f4d..8b590511f673c 100644 --- a/src/hotspot/share/opto/loopPredicate.cpp +++ b/src/hotspot/share/opto/loopPredicate.cpp @@ -341,9 +341,9 @@ void PhaseIdealLoop::clone_skeleton_predicates_to_unswitched_loop(IdealLoopTree* assert(predicate->is_Proj() && predicate->as_Proj()->is_IfProj(), "predicate must be a projection of an if node"); IfProjNode* predicate_proj = predicate->as_IfProj(); - ProjNode* fast_proj = clone_skeleton_predicate_for_unswitched_loops(iff, predicate_proj, uncommon_proj, reason, iffast_pred, loop); + ProjNode* fast_proj = clone_skeleton_predicate_for_unswitched_loops(iff, predicate_proj, reason, iffast_pred); assert(skeleton_predicate_has_opaque(fast_proj->in(0)->as_If()), "must find skeleton predicate for fast loop"); - ProjNode* slow_proj = clone_skeleton_predicate_for_unswitched_loops(iff, predicate_proj, uncommon_proj, reason, ifslow_pred, loop); + ProjNode* slow_proj = clone_skeleton_predicate_for_unswitched_loops(iff, predicate_proj, reason, ifslow_pred); assert(skeleton_predicate_has_opaque(slow_proj->in(0)->as_If()), "must find skeleton predicate for slow loop"); // Update control dependent data nodes. @@ -397,10 +397,10 @@ void PhaseIdealLoop::get_skeleton_predicates(Node* predicate, Unique_Node_List& // Clone a skeleton predicate for an unswitched loop. OpaqueLoopInit and OpaqueLoopStride nodes are cloned and uncommon // traps are kept for the predicate (a Halt node is used later when creating pre/main/post loops and copying this cloned // predicate again). -ProjNode* PhaseIdealLoop::clone_skeleton_predicate_for_unswitched_loops(Node* iff, ProjNode* predicate, Node* uncommon_proj, - Deoptimization::DeoptReason reason, ProjNode* output_proj, - IdealLoopTree* loop) { - Node* bol = clone_skeleton_predicate_bool(iff, NULL, NULL, predicate, uncommon_proj, output_proj, loop); +ProjNode* PhaseIdealLoop::clone_skeleton_predicate_for_unswitched_loops(Node* iff, ProjNode* predicate, + Deoptimization::DeoptReason reason, + ProjNode* output_proj) { + Node* bol = clone_skeleton_predicate_bool(iff, NULL, NULL, output_proj); ProjNode* proj = create_new_if_for_predicate(output_proj, NULL, reason, iff->Opcode(), predicate->is_IfTrue()); _igvn.replace_input_of(proj->in(0), 1, bol); _igvn.replace_input_of(output_proj->in(0), 0, proj); diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index bbfb5b12d08eb..00147d54ec774 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -1287,10 +1287,12 @@ void PhaseIdealLoop::copy_skeleton_predicates_to_main_loop_helper(Node* predicat // Clone the skeleton predicate twice and initialize one with the initial // value of the loop induction variable. Leave the other predicate // to be initialized when increasing the stride during loop unrolling. - prev_proj = clone_skeleton_predicate_for_main_loop(iff, opaque_init, NULL, predicate, uncommon_proj, current_proj, outer_loop, prev_proj); + prev_proj = clone_skeleton_predicate_for_main_or_post_loop(iff, opaque_init, NULL, predicate, uncommon_proj, + current_proj, outer_loop, prev_proj); assert(skeleton_predicate_has_opaque(prev_proj->in(0)->as_If()), ""); - prev_proj = clone_skeleton_predicate_for_main_loop(iff, init, stride, predicate, uncommon_proj, current_proj, outer_loop, prev_proj); + prev_proj = clone_skeleton_predicate_for_main_or_post_loop(iff, init, stride, predicate, uncommon_proj, + current_proj, outer_loop, prev_proj); assert(!skeleton_predicate_has_opaque(prev_proj->in(0)->as_If()), ""); // Rewire any control inputs from the cloned skeleton predicates down to the main and post loop for data nodes that are part of the @@ -1367,8 +1369,7 @@ bool PhaseIdealLoop::skeleton_predicate_has_opaque(IfNode* iff) { // Clone the skeleton predicate bool for a main or unswitched loop: // Main loop: Set new_init and new_stride nodes as new inputs. // Unswitched loop: new_init and new_stride are both NULL. Clone OpaqueLoopInit and OpaqueLoopStride instead. -Node* PhaseIdealLoop::clone_skeleton_predicate_bool(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj, - Node* control, IdealLoopTree* outer_loop) { +Node* PhaseIdealLoop::clone_skeleton_predicate_bool(Node* iff, Node* new_init, Node* new_stride, Node* control) { Node_Stack to_clone(2); to_clone.push(iff->in(1), 1); uint current = C->unique(); @@ -1444,9 +1445,9 @@ Node* PhaseIdealLoop::clone_skeleton_predicate_bool(Node* iff, Node* new_init, N // Clone a skeleton predicate for the main loop. new_init and new_stride are set as new inputs. Since the predicates cannot fail at runtime, // Halt nodes are inserted instead of uncommon traps. -Node* PhaseIdealLoop::clone_skeleton_predicate_for_main_loop(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj, - Node* control, IdealLoopTree* outer_loop, Node* input_proj) { - Node* result = clone_skeleton_predicate_bool(iff, new_init, new_stride, predicate, uncommon_proj, control, outer_loop); +Node* PhaseIdealLoop::clone_skeleton_predicate_for_main_or_post_loop(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj, + Node* control, IdealLoopTree* outer_loop, Node* input_proj) { + Node* result = clone_skeleton_predicate_bool(iff, new_init, new_stride, control); Node* proj = predicate->clone(); Node* other_proj = uncommon_proj->clone(); Node* new_iff = iff->clone(); @@ -1460,8 +1461,8 @@ Node* PhaseIdealLoop::clone_skeleton_predicate_for_main_loop(Node* iff, Node* ne C->root()->add_req(halt); new_iff->set_req(0, input_proj); - register_control(new_iff, outer_loop->_parent, input_proj); - register_control(proj, outer_loop->_parent, new_iff); + register_control(new_iff, outer_loop == _ltree_root ? _ltree_root : outer_loop->_parent, input_proj); + register_control(proj, outer_loop == _ltree_root ? _ltree_root : outer_loop->_parent, new_iff); register_control(other_proj, _ltree_root, new_iff); register_control(halt, _ltree_root, other_proj); return proj; @@ -1544,7 +1545,8 @@ void PhaseIdealLoop::insert_pre_post_loops(IdealLoopTree *loop, Node_List &old_n // Add the post loop const uint idx_before_pre_post = Compile::current()->unique(); CountedLoopNode *post_head = NULL; - Node *main_exit = insert_post_loop(loop, old_new, main_head, main_end, incr, limit, post_head); + Node* post_incr = incr; + Node* main_exit = insert_post_loop(loop, old_new, main_head, main_end, post_incr, limit, post_head); const uint idx_after_post_before_pre = Compile::current()->unique(); //------------------------------ @@ -1643,6 +1645,7 @@ void PhaseIdealLoop::insert_pre_post_loops(IdealLoopTree *loop, Node_List &old_n assert(post_head->in(1)->is_IfProj(), "must be zero-trip guard If node projection of the post loop"); copy_skeleton_predicates_to_main_loop(pre_head, castii, stride, outer_loop, outer_main_head, dd_main_head, idx_before_pre_post, idx_after_post_before_pre, min_taken, post_head->in(1), old_new); + copy_skeleton_predicates_to_post_loop(outer_main_head, post_head, post_incr, stride); // Step B4: Shorten the pre-loop to run only 1 iteration (for now). // RCE and alignment may change this later. @@ -1765,6 +1768,7 @@ void PhaseIdealLoop::insert_vector_post_loop(IdealLoopTree *loop, Node_List &old // In this case we throw away the result as we are not using it to connect anything else. CountedLoopNode *post_head = NULL; insert_post_loop(loop, old_new, main_head, main_end, incr, limit, post_head); + copy_skeleton_predicates_to_post_loop(main_head->skip_strip_mined(), post_head, incr, main_head->stride()); // It's difficult to be precise about the trip-counts // for post loops. They are usually very short, @@ -1811,6 +1815,7 @@ void PhaseIdealLoop::insert_scalar_rced_post_loop(IdealLoopTree *loop, Node_List // In this case we throw away the result as we are not using it to connect anything else. CountedLoopNode *post_head = NULL; insert_post_loop(loop, old_new, main_head, main_end, incr, limit, post_head); + copy_skeleton_predicates_to_post_loop(main_head->skip_strip_mined(), post_head, incr, main_head->stride()); // It's difficult to be precise about the trip-counts // for post loops. They are usually very short, @@ -1827,9 +1832,9 @@ void PhaseIdealLoop::insert_scalar_rced_post_loop(IdealLoopTree *loop, Node_List //------------------------------insert_post_loop------------------------------- // Insert post loops. Add a post loop to the given loop passed. -Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree *loop, Node_List &old_new, - CountedLoopNode *main_head, CountedLoopEndNode *main_end, - Node *incr, Node *limit, CountedLoopNode *&post_head) { +Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree* loop, Node_List& old_new, + CountedLoopNode* main_head, CountedLoopEndNode* main_end, + Node*& incr, Node* limit, CountedLoopNode*& post_head) { IfNode* outer_main_end = main_end; IdealLoopTree* outer_loop = loop; if (main_head->is_strip_mined()) { @@ -1913,8 +1918,8 @@ Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree *loop, Node_List &old_new, } // CastII for the new post loop: - Node* castii = cast_incr_before_loop(zer_opaq->in(1), zer_taken, post_head); - assert(castii != NULL, "no castII inserted"); + incr = cast_incr_before_loop(zer_opaq->in(1), zer_taken, post_head); + assert(incr != NULL, "no castII inserted"); return new_main_exit; } @@ -1956,7 +1961,8 @@ void PhaseIdealLoop::update_main_loop_skeleton_predicates(Node* ctrl, CountedLoo _igvn.replace_input_of(iff, 1, iff->in(1)->in(2)); } else { // Add back predicates updated for the new stride. - prev_proj = clone_skeleton_predicate_for_main_loop(iff, init, max_value, entry, proj, ctrl, outer_loop, prev_proj); + prev_proj = clone_skeleton_predicate_for_main_or_post_loop(iff, init, max_value, entry, proj, ctrl, outer_loop, + prev_proj); assert(!skeleton_predicate_has_opaque(prev_proj->in(0)->as_If()), "unexpected"); } } @@ -1968,6 +1974,34 @@ void PhaseIdealLoop::update_main_loop_skeleton_predicates(Node* ctrl, CountedLoo } } +void PhaseIdealLoop::copy_skeleton_predicates_to_post_loop(LoopNode* main_loop_head, CountedLoopNode* post_loop_head, Node* init, Node* stride) { + // Go over the skeleton predicates of the main loop and make a copy for the post loop with its initial iv value and + // stride as inputs. + Node* post_loop_entry = post_loop_head->in(LoopNode::EntryControl); + Node* main_loop_entry = main_loop_head->in(LoopNode::EntryControl); + IdealLoopTree* post_loop = get_loop(post_loop_head); + + Node* ctrl = main_loop_entry; + Node* prev_proj = post_loop_entry; + while (ctrl != NULL && ctrl->is_Proj() && ctrl->in(0)->is_If()) { + IfNode* iff = ctrl->in(0)->as_If(); + ProjNode* proj = iff->proj_out(1 - ctrl->as_Proj()->_con); + if (proj->unique_ctrl_out()->Opcode() != Op_Halt) { + break; + } + if (iff->in(1)->Opcode() == Op_Opaque4 && skeleton_predicate_has_opaque(iff)) { + prev_proj = clone_skeleton_predicate_for_main_or_post_loop(iff, init, stride, ctrl, proj, post_loop_entry, + post_loop, prev_proj); + assert(!skeleton_predicate_has_opaque(prev_proj->in(0)->as_If()), "unexpected"); + } + ctrl = ctrl->in(0)->in(0); + } + if (prev_proj != post_loop_entry) { + _igvn.replace_input_of(post_loop_head, LoopNode::EntryControl, prev_proj); + set_idom(post_loop_head, prev_proj, dom_depth(post_loop_head)); + } +} + //------------------------------do_unroll-------------------------------------- // Unroll the loop body one step - make each trip do 2 iterations. void PhaseIdealLoop::do_unroll(IdealLoopTree *loop, Node_List &old_new, bool adjust_min_trip) { diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index f77ddeea4baf5..3e150b9cb9460 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -2389,12 +2389,14 @@ Node* CountedLoopNode::skip_predicates_from_entry(Node* ctrl) { } Node* CountedLoopNode::skip_predicates() { + Node* ctrl = in(LoopNode::EntryControl); if (is_main_loop()) { - Node* ctrl = skip_strip_mined()->in(LoopNode::EntryControl); - + ctrl = skip_strip_mined()->in(LoopNode::EntryControl); + } + if (is_main_loop() || is_post_loop()) { return skip_predicates_from_entry(ctrl); } - return in(LoopNode::EntryControl); + return ctrl; } diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 35db18e0b6eae..1fee41ba62aa6 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -917,13 +917,13 @@ class PhaseIdealLoop : public PhaseTransform { void copy_skeleton_predicates_to_main_loop(CountedLoopNode* pre_head, Node* init, Node* stride, IdealLoopTree* outer_loop, LoopNode* outer_main_head, uint dd_main_head, const uint idx_before_pre_post, const uint idx_after_post_before_pre, Node* zero_trip_guard_proj_main, Node* zero_trip_guard_proj_post, const Node_List &old_new); - Node* clone_skeleton_predicate_for_main_loop(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj, Node* control, - IdealLoopTree* outer_loop, Node* input_proj); - Node* clone_skeleton_predicate_bool(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj, Node* control, - IdealLoopTree* outer_loop); + Node* clone_skeleton_predicate_for_main_or_post_loop(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj, Node* control, + IdealLoopTree* outer_loop, Node* input_proj); + Node* clone_skeleton_predicate_bool(Node* iff, Node* new_init, Node* new_stride, Node* control); static bool skeleton_predicate_has_opaque(IfNode* iff); static void get_skeleton_predicates(Node* predicate, Unique_Node_List& list, bool get_opaque = false); void update_main_loop_skeleton_predicates(Node* ctrl, CountedLoopNode* loop_head, Node* init, int stride_con); + void copy_skeleton_predicates_to_post_loop(LoopNode* main_loop_head, CountedLoopNode* post_loop_head, Node* init, Node* stride); void insert_loop_limit_check(ProjNode* limit_check_proj, Node* cmp_limit, Node* bol); #ifdef ASSERT bool only_has_infinite_loops(); @@ -1246,9 +1246,9 @@ class PhaseIdealLoop : public PhaseTransform { void insert_pre_post_loops( IdealLoopTree *loop, Node_List &old_new, bool peel_only ); // Add post loop after the given loop. - Node *insert_post_loop(IdealLoopTree *loop, Node_List &old_new, - CountedLoopNode *main_head, CountedLoopEndNode *main_end, - Node *incr, Node *limit, CountedLoopNode *&post_head); + Node *insert_post_loop(IdealLoopTree* loop, Node_List& old_new, + CountedLoopNode* main_head, CountedLoopEndNode* main_end, + Node*& incr, Node* limit, CountedLoopNode*& post_head); // Add an RCE'd post loop which we will multi-version adapt for run time test path usage void insert_scalar_rced_post_loop( IdealLoopTree *loop, Node_List &old_new ); @@ -1593,8 +1593,9 @@ class PhaseIdealLoop : public PhaseTransform { Node_List* old_new = NULL); void clone_skeleton_predicates_to_unswitched_loop(IdealLoopTree* loop, const Node_List& old_new, Deoptimization::DeoptReason reason, ProjNode* old_predicate_proj, ProjNode* iffast_pred, ProjNode* ifslow_pred); - ProjNode* clone_skeleton_predicate_for_unswitched_loops(Node* iff, ProjNode* predicate, Node* uncommon_proj, Deoptimization::DeoptReason reason, - ProjNode* output_proj, IdealLoopTree* loop); + ProjNode* clone_skeleton_predicate_for_unswitched_loops(Node* iff, ProjNode* predicate, + Deoptimization::DeoptReason reason, + ProjNode* output_proj); static void check_created_predicate_for_unswitching(const Node* new_entry) PRODUCT_RETURN; bool _created_loop_node; diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index f7b6a9ea66b84..1205d2007d76d 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -2366,6 +2366,7 @@ void PhaseMacroExpand::eliminate_macro_nodes() { assert(n->Opcode() == Op_LoopLimit || n->Opcode() == Op_Opaque2 || n->Opcode() == Op_Opaque3 || + n->Opcode() == Op_Opaque4 || BarrierSet::barrier_set()->barrier_set_c2()->is_gc_barrier_node(n), "unknown node type in macro list"); } @@ -2427,6 +2428,19 @@ bool PhaseMacroExpand::expand_macro_nodes() { _igvn.replace_node(n, repl); success = true; #endif + } else if (n->Opcode() == Op_Opaque4) { + // With Opaque4 nodes, the expectation is that the test of input 1 + // is always equal to the constant value of input 2. So we can + // remove the Opaque4 and replace it by input 2. In debug builds, + // leave the non constant test in instead to sanity check that it + // never fails (if it does, that subgraph was constructed so, at + // runtime, a Halt node is executed). +#ifdef ASSERT + _igvn.replace_node(n, n->in(1)); +#else + _igvn.replace_node(n, n->in(2)); +#endif + success = true; } else if (n->Opcode() == Op_OuterStripMinedLoop) { n->as_OuterStripMinedLoop()->adjust_strip_mined_loop(&_igvn); C->remove_macro_node(n); diff --git a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp index fadfd11f2bfbb..21198087fd9b1 100644 --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -634,18 +634,20 @@ void Matcher::init_first_stack_mask() { if (Matcher::supports_scalable_vector()) { int k = 1; OptoReg::Name in = OptoReg::add(_in_arg_limit, -1); - // Exclude last input arg stack slots to avoid spilling vector register there, - // otherwise RegVectMask spills could stomp over stack slots in caller frame. - for (; (in >= init_in) && (k < scalable_predicate_reg_slots()); k++) { - scalable_stack_mask.Remove(in); - in = OptoReg::add(in, -1); - } + if (Matcher::has_predicated_vectors()) { + // Exclude last input arg stack slots to avoid spilling vector register there, + // otherwise RegVectMask spills could stomp over stack slots in caller frame. + for (; (in >= init_in) && (k < scalable_predicate_reg_slots()); k++) { + scalable_stack_mask.Remove(in); + in = OptoReg::add(in, -1); + } - // For RegVectMask - scalable_stack_mask.clear_to_sets(scalable_predicate_reg_slots()); - assert(scalable_stack_mask.is_AllStack(), "should be infinite stack"); - *idealreg2spillmask[Op_RegVectMask] = *idealreg2regmask[Op_RegVectMask]; - idealreg2spillmask[Op_RegVectMask]->OR(scalable_stack_mask); + // For RegVectMask + scalable_stack_mask.clear_to_sets(scalable_predicate_reg_slots()); + assert(scalable_stack_mask.is_AllStack(), "should be infinite stack"); + *idealreg2spillmask[Op_RegVectMask] = *idealreg2regmask[Op_RegVectMask]; + idealreg2spillmask[Op_RegVectMask]->OR(scalable_stack_mask); + } // Exclude last input arg stack slots to avoid spilling vector register there, // otherwise vector spills could stomp over stack slots in caller frame. diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 1c226cbf2a47b..94fd6ac3385bb 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -175,6 +175,7 @@ class VectorMaskCmpNode; class VectorUnboxNode; class VectorSet; class VectorReinterpretNode; +class ShiftVNode; // The type of all node counts and indexes. // It must hold at least 16 bits, but must also be fast to load and store. @@ -711,6 +712,7 @@ class Node { DEFINE_CLASS_ID(VectorMaskCmp, Vector, 0) DEFINE_CLASS_ID(VectorUnbox, Vector, 1) DEFINE_CLASS_ID(VectorReinterpret, Vector, 2) + DEFINE_CLASS_ID(ShiftV, Vector, 3) DEFINE_CLASS_ID(Proj, Node, 3) DEFINE_CLASS_ID(CatchProj, Proj, 0) @@ -945,6 +947,7 @@ class Node { DEFINE_CLASS_QUERY(LoadVectorGather) DEFINE_CLASS_QUERY(StoreVector) DEFINE_CLASS_QUERY(StoreVectorScatter) + DEFINE_CLASS_QUERY(ShiftV) DEFINE_CLASS_QUERY(Unlock) #undef DEFINE_CLASS_QUERY diff --git a/src/hotspot/share/opto/opaquenode.cpp b/src/hotspot/share/opto/opaquenode.cpp index c1b769e237056..c66df16b2d039 100644 --- a/src/hotspot/share/opto/opaquenode.cpp +++ b/src/hotspot/share/opto/opaquenode.cpp @@ -60,25 +60,6 @@ bool Opaque2Node::cmp( const Node &n ) const { return (&n == this); // Always fail except on self } -Node* Opaque4Node::Identity(PhaseGVN* phase) { - if (phase->C->post_loop_opts_phase()) { - // With Opaque4 nodes, the expectation is that the test of input 1 - // is always equal to the constant value of input 2. So we can - // remove the Opaque4 and replace it by input 2. In debug builds, - // leave the non constant test in instead to sanity check that it - // never fails (if it does, that subgraph was constructed so, at - // runtime, a Halt node is executed). -#ifdef ASSERT - return this->in(1); -#else - return this->in(2); -#endif - } else { - phase->C->record_for_post_loop_opts_igvn(this); - } - return this; -} - const Type* Opaque4Node::Value(PhaseGVN* phase) const { return phase->type(in(1)); } diff --git a/src/hotspot/share/opto/opaquenode.hpp b/src/hotspot/share/opto/opaquenode.hpp index 160f814fd2d6d..cb7ff23764bfd 100644 --- a/src/hotspot/share/opto/opaquenode.hpp +++ b/src/hotspot/share/opto/opaquenode.hpp @@ -114,11 +114,13 @@ class Opaque3Node : public Opaque2Node { // GraphKit::must_be_not_null(). class Opaque4Node : public Node { public: - Opaque4Node(Compile* C, Node *tst, Node* final_tst) : Node(NULL, tst, final_tst) {} + Opaque4Node(Compile* C, Node *tst, Node* final_tst) : Node(NULL, tst, final_tst) { + init_flags(Flag_is_macro); + C->add_macro_node(this); + } virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeInt::BOOL; } - virtual Node* Identity(PhaseGVN* phase); virtual const Type* Value(PhaseGVN* phase) const; }; diff --git a/src/hotspot/share/opto/parse.hpp b/src/hotspot/share/opto/parse.hpp index efed355787b1b..6a4d77479f65c 100644 --- a/src/hotspot/share/opto/parse.hpp +++ b/src/hotspot/share/opto/parse.hpp @@ -46,6 +46,7 @@ class InlineTree : public ResourceObj { Compile* C; // cache JVMState* _caller_jvms; // state of caller ciMethod* _method; // method being called by the caller_jvms + bool _late_inline; // method is inlined incrementally InlineTree* _caller_tree; uint _count_inline_bcs; // Accumulated count of inlined bytecodes const int _max_inline_level; // the maximum inline level for this sub-tree (may be adjusted) @@ -75,10 +76,12 @@ class InlineTree : public ResourceObj { bool should_inline(ciMethod* callee_method, ciMethod* caller_method, int caller_bci, + NOT_PRODUCT_ARG(bool& should_delay) ciCallProfile& profile); bool should_not_inline(ciMethod* callee_method, ciMethod* caller_method, int caller_bci, + NOT_PRODUCT_ARG(bool& should_delay) ciCallProfile& profile); bool is_not_reached(ciMethod* callee_method, ciMethod* caller_method, @@ -112,6 +115,10 @@ class InlineTree : public ResourceObj { // The call_method is an optimized virtual method candidate otherwise. bool ok_to_inline(ciMethod *call_method, JVMState* caller_jvms, ciCallProfile& profile, bool& should_delay); + void set_late_inline() { + _late_inline = true; + } + // Information about inlined method JVMState* caller_jvms() const { return _caller_jvms; } ciMethod *method() const { return _method; } diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp index 95ca0ae6d2bdc..63ee441336e11 100644 --- a/src/hotspot/share/opto/vectorIntrinsics.cpp +++ b/src/hotspot/share/opto/vectorIntrinsics.cpp @@ -526,7 +526,7 @@ bool LibraryCallKit::inline_vector_nary_operation(int n) { switch (n) { case 1: case 2: { - operation = VectorNode::make(sopc, opd1, opd2, vt, is_vector_mask(vbox_klass)); + operation = VectorNode::make(sopc, opd1, opd2, vt, is_vector_mask(vbox_klass), VectorNode::is_shift_opcode(opc)); break; } case 3: { diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index 6d845271e4737..27524b48dcc64 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -480,7 +480,7 @@ VectorNode* VectorNode::make_mask_node(int vopc, Node* n1, Node* n2, uint vlen, } // Make a vector node for binary operation -VectorNode* VectorNode::make(int vopc, Node* n1, Node* n2, const TypeVect* vt, bool is_mask) { +VectorNode* VectorNode::make(int vopc, Node* n1, Node* n2, const TypeVect* vt, bool is_mask, bool is_var_shift) { // This method should not be called for unimplemented vectors. guarantee(vopc > 0, "vopc must be > 0"); @@ -534,20 +534,20 @@ VectorNode* VectorNode::make(int vopc, Node* n1, Node* n2, const TypeVect* vt, b case Op_RotateLeftV: return new RotateLeftVNode(n1, n2, vt); case Op_RotateRightV: return new RotateRightVNode(n1, n2, vt); - case Op_LShiftVB: return new LShiftVBNode(n1, n2, vt); - case Op_LShiftVS: return new LShiftVSNode(n1, n2, vt); - case Op_LShiftVI: return new LShiftVINode(n1, n2, vt); - case Op_LShiftVL: return new LShiftVLNode(n1, n2, vt); + case Op_LShiftVB: return new LShiftVBNode(n1, n2, vt, is_var_shift); + case Op_LShiftVS: return new LShiftVSNode(n1, n2, vt, is_var_shift); + case Op_LShiftVI: return new LShiftVINode(n1, n2, vt, is_var_shift); + case Op_LShiftVL: return new LShiftVLNode(n1, n2, vt, is_var_shift); - case Op_RShiftVB: return new RShiftVBNode(n1, n2, vt); - case Op_RShiftVS: return new RShiftVSNode(n1, n2, vt); - case Op_RShiftVI: return new RShiftVINode(n1, n2, vt); - case Op_RShiftVL: return new RShiftVLNode(n1, n2, vt); + case Op_RShiftVB: return new RShiftVBNode(n1, n2, vt, is_var_shift); + case Op_RShiftVS: return new RShiftVSNode(n1, n2, vt, is_var_shift); + case Op_RShiftVI: return new RShiftVINode(n1, n2, vt, is_var_shift); + case Op_RShiftVL: return new RShiftVLNode(n1, n2, vt, is_var_shift); - case Op_URShiftVB: return new URShiftVBNode(n1, n2, vt); - case Op_URShiftVS: return new URShiftVSNode(n1, n2, vt); - case Op_URShiftVI: return new URShiftVINode(n1, n2, vt); - case Op_URShiftVL: return new URShiftVLNode(n1, n2, vt); + case Op_URShiftVB: return new URShiftVBNode(n1, n2, vt, is_var_shift); + case Op_URShiftVS: return new URShiftVSNode(n1, n2, vt, is_var_shift); + case Op_URShiftVI: return new URShiftVINode(n1, n2, vt, is_var_shift); + case Op_URShiftVL: return new URShiftVLNode(n1, n2, vt, is_var_shift); case Op_AndV: return new AndVNode(n1, n2, vt); case Op_OrV: return new OrVNode (n1, n2, vt); @@ -563,12 +563,12 @@ VectorNode* VectorNode::make(int vopc, Node* n1, Node* n2, const TypeVect* vt, b } // Return the vector version of a scalar binary operation node. -VectorNode* VectorNode::make(int opc, Node* n1, Node* n2, uint vlen, BasicType bt) { +VectorNode* VectorNode::make(int opc, Node* n1, Node* n2, uint vlen, BasicType bt, bool is_var_shift) { const TypeVect* vt = TypeVect::make(bt, vlen); int vopc = VectorNode::opcode(opc, bt); // This method should not be called for unimplemented vectors. guarantee(vopc > 0, "Vector for '%s' is not implemented", NodeClassNames[opc]); - return make(vopc, n1, n2, vt); + return make(vopc, n1, n2, vt, false, is_var_shift); } // Make a vector node for ternary operation @@ -1297,8 +1297,8 @@ Node* VectorNode::degenerate_vector_rotate(Node* src, Node* cnt, bool is_rotate_ shiftRCnt = phase->transform(new RShiftCntVNode(shiftRCnt, vt)); } - return new OrVNode(phase->transform(VectorNode::make(shiftLOpc, src, shiftLCnt, vlen, bt)), - phase->transform(VectorNode::make(shiftROpc, src, shiftRCnt, vlen, bt)), + return new OrVNode(phase->transform(VectorNode::make(shiftLOpc, src, shiftLCnt, vlen, bt, is_binary_vector_op)), + phase->transform(VectorNode::make(shiftROpc, src, shiftRCnt, vlen, bt, is_binary_vector_op)), vt); } diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp index 0b04a053f999e..95079919f78c1 100644 --- a/src/hotspot/share/opto/vectornode.hpp +++ b/src/hotspot/share/opto/vectornode.hpp @@ -72,8 +72,8 @@ class VectorNode : public TypeNode { static VectorNode* scalar2vector(Node* s, uint vlen, const Type* opd_t, bool is_mask = false); static VectorNode* shift_count(int opc, Node* cnt, uint vlen, BasicType bt); - static VectorNode* make(int opc, Node* n1, Node* n2, uint vlen, BasicType bt); - static VectorNode* make(int vopc, Node* n1, Node* n2, const TypeVect* vt, bool is_mask = false); + static VectorNode* make(int opc, Node* n1, Node* n2, uint vlen, BasicType bt, bool is_var_shift = false); + static VectorNode* make(int vopc, Node* n1, Node* n2, const TypeVect* vt, bool is_mask = false, bool is_var_shift = false); static VectorNode* make(int opc, Node* n1, Node* n2, Node* n3, uint vlen, BasicType bt); static VectorNode* make(int vopc, Node* n1, Node* n2, Node* n3, const TypeVect* vt); static VectorNode* make_mask_node(int vopc, Node* n1, Node* n2, uint vlen, BasicType bt); @@ -531,17 +531,24 @@ class SqrtVDNode : public VectorNode { // Class ShiftV functionality. This covers the common behaviors for all kinds // of vector shifts. class ShiftVNode : public VectorNode { + bool _is_var_shift; public: - ShiftVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1,in2,vt) {} + ShiftVNode(Node* in1, Node* in2, const TypeVect* vt, bool is_var_shift) : + VectorNode(in1,in2,vt), _is_var_shift(is_var_shift) { + init_class_id(Class_ShiftV); + } virtual Node* Identity(PhaseGVN* phase); virtual int Opcode() const = 0; + bool is_var_shift() { return _is_var_shift;} + virtual uint size_of() const { return sizeof(ShiftVNode); } }; //------------------------------LShiftVBNode----------------------------------- // Vector left shift bytes class LShiftVBNode : public ShiftVNode { public: - LShiftVBNode(Node* in1, Node* in2, const TypeVect* vt) : ShiftVNode(in1,in2,vt) {} + LShiftVBNode(Node* in1, Node* in2, const TypeVect* vt, bool is_var_shift=false) : + ShiftVNode(in1,in2,vt,is_var_shift) {} virtual int Opcode() const; }; @@ -549,7 +556,8 @@ class LShiftVBNode : public ShiftVNode { // Vector left shift shorts class LShiftVSNode : public ShiftVNode { public: - LShiftVSNode(Node* in1, Node* in2, const TypeVect* vt) : ShiftVNode(in1,in2,vt) {} + LShiftVSNode(Node* in1, Node* in2, const TypeVect* vt, bool is_var_shift=false) : + ShiftVNode(in1,in2,vt,is_var_shift) {} virtual int Opcode() const; }; @@ -557,7 +565,8 @@ class LShiftVSNode : public ShiftVNode { // Vector left shift ints class LShiftVINode : public ShiftVNode { public: - LShiftVINode(Node* in1, Node* in2, const TypeVect* vt) : ShiftVNode(in1,in2,vt) {} + LShiftVINode(Node* in1, Node* in2, const TypeVect* vt, bool is_var_shift=false) : + ShiftVNode(in1,in2,vt,is_var_shift) {} virtual int Opcode() const; }; @@ -565,7 +574,8 @@ class LShiftVINode : public ShiftVNode { // Vector left shift longs class LShiftVLNode : public ShiftVNode { public: - LShiftVLNode(Node* in1, Node* in2, const TypeVect* vt) : ShiftVNode(in1,in2,vt) {} + LShiftVLNode(Node* in1, Node* in2, const TypeVect* vt, bool is_var_shift=false) : + ShiftVNode(in1,in2,vt,is_var_shift) {} virtual int Opcode() const; }; @@ -573,7 +583,8 @@ class LShiftVLNode : public ShiftVNode { // Vector right arithmetic (signed) shift bytes class RShiftVBNode : public ShiftVNode { public: - RShiftVBNode(Node* in1, Node* in2, const TypeVect* vt) : ShiftVNode(in1,in2,vt) {} + RShiftVBNode(Node* in1, Node* in2, const TypeVect* vt, bool is_var_shift=false) : + ShiftVNode(in1,in2,vt,is_var_shift) {} virtual int Opcode() const; }; @@ -581,7 +592,8 @@ class RShiftVBNode : public ShiftVNode { // Vector right arithmetic (signed) shift shorts class RShiftVSNode : public ShiftVNode { public: - RShiftVSNode(Node* in1, Node* in2, const TypeVect* vt) : ShiftVNode(in1,in2,vt) {} + RShiftVSNode(Node* in1, Node* in2, const TypeVect* vt, bool is_var_shift=false) : + ShiftVNode(in1,in2,vt,is_var_shift) {} virtual int Opcode() const; }; @@ -589,7 +601,8 @@ class RShiftVSNode : public ShiftVNode { // Vector right arithmetic (signed) shift ints class RShiftVINode : public ShiftVNode { public: - RShiftVINode(Node* in1, Node* in2, const TypeVect* vt) : ShiftVNode(in1,in2,vt) {} + RShiftVINode(Node* in1, Node* in2, const TypeVect* vt, bool is_var_shift=false) : + ShiftVNode(in1,in2,vt,is_var_shift) {} virtual int Opcode() const; }; @@ -597,7 +610,8 @@ class RShiftVINode : public ShiftVNode { // Vector right arithmetic (signed) shift longs class RShiftVLNode : public ShiftVNode { public: - RShiftVLNode(Node* in1, Node* in2, const TypeVect* vt) : ShiftVNode(in1,in2,vt) {} + RShiftVLNode(Node* in1, Node* in2, const TypeVect* vt, bool is_var_shift=false) : + ShiftVNode(in1,in2,vt,is_var_shift) {} virtual int Opcode() const; }; @@ -605,7 +619,8 @@ class RShiftVLNode : public ShiftVNode { // Vector right logical (unsigned) shift bytes class URShiftVBNode : public ShiftVNode { public: - URShiftVBNode(Node* in1, Node* in2, const TypeVect* vt) : ShiftVNode(in1,in2,vt) {} + URShiftVBNode(Node* in1, Node* in2, const TypeVect* vt, bool is_var_shift=false) : + ShiftVNode(in1,in2,vt,is_var_shift) {} virtual int Opcode() const; }; @@ -613,7 +628,8 @@ class URShiftVBNode : public ShiftVNode { // Vector right logical (unsigned) shift shorts class URShiftVSNode : public ShiftVNode { public: - URShiftVSNode(Node* in1, Node* in2, const TypeVect* vt) : ShiftVNode(in1,in2,vt) {} + URShiftVSNode(Node* in1, Node* in2, const TypeVect* vt, bool is_var_shift=false) : + ShiftVNode(in1,in2,vt,is_var_shift) {} virtual int Opcode() const; }; @@ -621,7 +637,8 @@ class URShiftVSNode : public ShiftVNode { // Vector right logical (unsigned) shift ints class URShiftVINode : public ShiftVNode { public: - URShiftVINode(Node* in1, Node* in2, const TypeVect* vt) : ShiftVNode(in1,in2,vt) {} + URShiftVINode(Node* in1, Node* in2, const TypeVect* vt, bool is_var_shift=false) : + ShiftVNode(in1,in2,vt,is_var_shift) {} virtual int Opcode() const; }; @@ -629,7 +646,8 @@ class URShiftVINode : public ShiftVNode { // Vector right logical (unsigned) shift longs class URShiftVLNode : public ShiftVNode { public: - URShiftVLNode(Node* in1, Node* in2, const TypeVect* vt) : ShiftVNode(in1,in2,vt) {} + URShiftVLNode(Node* in1, Node* in2, const TypeVect* vt, bool is_var_shift=false) : + ShiftVNode(in1,in2,vt,is_var_shift) {} virtual int Opcode() const; }; diff --git a/src/hotspot/share/prims/jvmtiEnvBase.cpp b/src/hotspot/share/prims/jvmtiEnvBase.cpp index 29fb94226af44..0068ea0516926 100644 --- a/src/hotspot/share/prims/jvmtiEnvBase.cpp +++ b/src/hotspot/share/prims/jvmtiEnvBase.cpp @@ -1393,6 +1393,9 @@ SetForceEarlyReturn::doit(Thread *target, bool self) { Thread* current_thread = Thread::current(); HandleMark hm(current_thread); + if (java_thread->is_exiting()) { + return; /* JVMTI_ERROR_THREAD_NOT_ALIVE (default) */ + } if (!self) { if (!java_thread->is_suspended()) { _result = JVMTI_ERROR_THREAD_NOT_SUSPENDED; @@ -1523,6 +1526,10 @@ UpdateForPopTopFrameClosure::doit(Thread *target, bool self) { Thread* current_thread = Thread::current(); HandleMark hm(current_thread); JavaThread* java_thread = JavaThread::cast(target); + + if (java_thread->is_exiting()) { + return; /* JVMTI_ERROR_THREAD_NOT_ALIVE (default) */ + } assert(java_thread == _state->get_thread(), "Must be"); if (!self && !java_thread->is_suspended()) { @@ -1599,14 +1606,12 @@ UpdateForPopTopFrameClosure::doit(Thread *target, bool self) { // It's fine to update the thread state here because no JVMTI events // shall be posted for this PopFrame. - if (!java_thread->is_exiting() && java_thread->threadObj() != NULL) { - _state->update_for_pop_top_frame(); - java_thread->set_popframe_condition(JavaThread::popframe_pending_bit); - // Set pending step flag for this popframe and it is cleared when next - // step event is posted. - _state->set_pending_step_for_popframe(); - _result = JVMTI_ERROR_NONE; - } + _state->update_for_pop_top_frame(); + java_thread->set_popframe_condition(JavaThread::popframe_pending_bit); + // Set pending step flag for this popframe and it is cleared when next + // step event is posted. + _state->set_pending_step_for_popframe(); + _result = JVMTI_ERROR_NONE; } void @@ -1614,6 +1619,9 @@ SetFramePopClosure::doit(Thread *target, bool self) { ResourceMark rm; JavaThread* java_thread = JavaThread::cast(target); + if (java_thread->is_exiting()) { + return; /* JVMTI_ERROR_THREAD_NOT_ALIVE (default) */ + } assert(_state->get_thread() == java_thread, "Must be"); if (!self && !java_thread->is_suspended()) { @@ -1633,9 +1641,6 @@ SetFramePopClosure::doit(Thread *target, bool self) { } assert(vf->frame_pointer() != NULL, "frame pointer mustn't be NULL"); - if (java_thread->is_exiting() || java_thread->threadObj() == NULL) { - return; /* JVMTI_ERROR_THREAD_NOT_ALIVE (default) */ - } int frame_number = _state->count_frames() - _depth; _state->env_thread_state((JvmtiEnvBase*)_env)->set_frame_pop(frame_number); _result = JVMTI_ERROR_NONE; diff --git a/src/hotspot/share/prims/universalUpcallHandler.cpp b/src/hotspot/share/prims/universalUpcallHandler.cpp index 3457d4202ff98..6257036ef9fcc 100644 --- a/src/hotspot/share/prims/universalUpcallHandler.cpp +++ b/src/hotspot/share/prims/universalUpcallHandler.cpp @@ -174,6 +174,7 @@ ProgrammableUpcallHandler::ProgrammableUpcallHandler() { } void ProgrammableUpcallHandler::handle_uncaught_exception(oop exception) { + ResourceMark rm; // Based on CATCH macro tty->print_cr("Uncaught exception:"); exception->print(); diff --git a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp index 814a8e0ed3f2f..dd0dca74c858d 100644 --- a/src/hotspot/share/prims/unsafe.cpp +++ b/src/hotspot/share/prims/unsafe.cpp @@ -41,6 +41,7 @@ #include "oops/objArrayOop.inline.hpp" #include "oops/oop.inline.hpp" #include "oops/typeArrayOop.inline.hpp" +#include "prims/jvmtiExport.hpp" #include "prims/unsafe.hpp" #include "runtime/globals.hpp" #include "runtime/handles.inline.hpp" @@ -329,6 +330,7 @@ UNSAFE_LEAF(void, Unsafe_FullFence(JNIEnv *env, jobject unsafe)) { ////// Allocation requests UNSAFE_ENTRY(jobject, Unsafe_AllocateInstance(JNIEnv *env, jobject unsafe, jclass cls)) { + JvmtiVMObjectAllocEventCollector oam; instanceOop i = InstanceKlass::allocate_instance(JNIHandles::resolve_non_null(cls), CHECK_NULL); return JNIHandles::make_local(THREAD, i); } UNSAFE_END diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 995123c1ef9eb..4411ddc802dce 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -955,6 +955,72 @@ WB_ENTRY(void, WB_MakeMethodNotCompilable(JNIEnv* env, jobject o, jobject method } WB_END +WB_ENTRY(jint, WB_GetMethodDecompileCount(JNIEnv* env, jobject o, jobject method)) + jmethodID jmid = reflected_method_to_jmid(thread, env, method); + CHECK_JNI_EXCEPTION_(env, 0); + methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid)); + uint cnt = 0; + MethodData* mdo = mh->method_data(); + if (mdo != NULL) { + cnt = mdo->decompile_count(); + } + return cnt; +WB_END + +// Get the trap count of a method for a specific reason. If the trap count for +// that reason did overflow, this includes the overflow trap count of the method. +// If 'reason' is NULL, the sum of the traps for all reasons will be returned. +// This number includes the overflow trap count if the trap count for any reason +// did overflow. +WB_ENTRY(jint, WB_GetMethodTrapCount(JNIEnv* env, jobject o, jobject method, jstring reason_obj)) + jmethodID jmid = reflected_method_to_jmid(thread, env, method); + CHECK_JNI_EXCEPTION_(env, 0); + methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid)); + uint cnt = 0; + MethodData* mdo = mh->method_data(); + if (mdo != NULL) { + ResourceMark rm(THREAD); + char* reason_str = (reason_obj == NULL) ? + NULL : java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(reason_obj)); + bool overflow = false; + for (uint reason = 0; reason < mdo->trap_reason_limit(); reason++) { + if (reason_str != NULL && !strcmp(reason_str, Deoptimization::trap_reason_name(reason))) { + cnt = mdo->trap_count(reason); + // Count in the overflow trap count on overflow + if (cnt == (uint)-1) { + cnt = mdo->trap_count_limit() + mdo->overflow_trap_count(); + } + break; + } else if (reason_str == NULL) { + uint c = mdo->trap_count(reason); + if (c == (uint)-1) { + c = mdo->trap_count_limit(); + if (!overflow) { + // Count overflow trap count just once + overflow = true; + c += mdo->overflow_trap_count(); + } + } + cnt += c; + } + } + } + return cnt; +WB_END + +WB_ENTRY(jint, WB_GetDeoptCount(JNIEnv* env, jobject o, jstring reason_obj, jstring action_obj)) + if (reason_obj == NULL && action_obj == NULL) { + return Deoptimization::total_deoptimization_count(); + } + ResourceMark rm(THREAD); + const char *reason_str = (reason_obj == NULL) ? + NULL : java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(reason_obj)); + const char *action_str = (action_obj == NULL) ? + NULL : java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(action_obj)); + + return Deoptimization::deoptimization_count(reason_str, action_str); +WB_END + WB_ENTRY(jint, WB_GetMethodEntryBci(JNIEnv* env, jobject o, jobject method)) jmethodID jmid = reflected_method_to_jmid(thread, env, method); CHECK_JNI_EXCEPTION_(env, InvocationEntryBci); @@ -2527,6 +2593,13 @@ static JNINativeMethod methods[] = { CC"(Ljava/lang/reflect/Executable;Z)Z", (void*)&WB_TestSetDontInlineMethod}, {CC"getMethodCompilationLevel0", CC"(Ljava/lang/reflect/Executable;Z)I", (void*)&WB_GetMethodCompilationLevel}, + {CC"getMethodDecompileCount0", + CC"(Ljava/lang/reflect/Executable;)I", (void*)&WB_GetMethodDecompileCount}, + {CC"getMethodTrapCount0", + CC"(Ljava/lang/reflect/Executable;Ljava/lang/String;)I", + (void*)&WB_GetMethodTrapCount}, + {CC"getDeoptCount0", + CC"(Ljava/lang/String;Ljava/lang/String;)I", (void*)&WB_GetDeoptCount}, {CC"getMethodEntryBci0", CC"(Ljava/lang/reflect/Executable;)I", (void*)&WB_GetMethodEntryBci}, {CC"getCompileQueueSize", diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index d54492032518e..054d0ac9ffe4f 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -2608,6 +2608,30 @@ jint Deoptimization::total_deoptimization_count() { return _deoptimization_hist[Reason_none][0][0]; } +// Get the deopt count for a specific reason and a specific action. If either +// one of 'reason' or 'action' is null, the method returns the sum of all +// deoptimizations with the specific 'action' or 'reason' respectively. +// If both arguments are null, the method returns the total deopt count. +jint Deoptimization::deoptimization_count(const char *reason_str, const char *action_str) { + if (reason_str == NULL && action_str == NULL) { + return total_deoptimization_count(); + } + juint counter = 0; + for (int reason = 0; reason < Reason_LIMIT; reason++) { + if (reason_str == NULL || !strcmp(reason_str, trap_reason_name(reason))) { + for (int action = 0; action < Action_LIMIT; action++) { + if (action_str == NULL || !strcmp(action_str, trap_action_name(action))) { + juint* cases = _deoptimization_hist[reason][1+action]; + for (int bc_case = 0; bc_case < BC_CASE_LIMIT; bc_case++) { + counter += cases[bc_case] >> LSB_BITS; + } + } + } + } + } + return counter; +} + void Deoptimization::print_statistics() { juint total = total_deoptimization_count(); juint account = total; @@ -2661,6 +2685,14 @@ const char* Deoptimization::trap_reason_name(int reason) { return "unknown"; } +jint Deoptimization::total_deoptimization_count() { + return 0; +} + +jint Deoptimization::deoptimization_count(const char *reason_str, const char *action_str) { + return 0; +} + void Deoptimization::print_statistics() { // no output } diff --git a/src/hotspot/share/runtime/deoptimization.hpp b/src/hotspot/share/runtime/deoptimization.hpp index af176ce41fd2e..74b78a853b499 100644 --- a/src/hotspot/share/runtime/deoptimization.hpp +++ b/src/hotspot/share/runtime/deoptimization.hpp @@ -433,6 +433,7 @@ class Deoptimization : AllStatic { int trap_request); static jint total_deoptimization_count(); + static jint deoptimization_count(const char* reason_str, const char* action_str); // JVMTI PopFrame support diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index 52c91be8f25d7..65695f7d1ce95 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -669,13 +669,14 @@ void* os::malloc(size_t size, MEMFLAGS memflags, const NativeCallStack& stack) { // NMT support NMT_TrackingLevel level = MemTracker::tracking_level(); - size_t nmt_header_size = MemTracker::malloc_header_size(level); + const size_t nmt_overhead = + MemTracker::malloc_header_size(level) + MemTracker::malloc_footer_size(level); #ifndef ASSERT - const size_t alloc_size = size + nmt_header_size; + const size_t alloc_size = size + nmt_overhead; #else - const size_t alloc_size = GuardedMemory::get_total_size(size + nmt_header_size); - if (size + nmt_header_size > alloc_size) { // Check for rollover. + const size_t alloc_size = GuardedMemory::get_total_size(size + nmt_overhead); + if (size + nmt_overhead > alloc_size) { // Check for rollover. return NULL; } #endif @@ -693,7 +694,7 @@ void* os::malloc(size_t size, MEMFLAGS memflags, const NativeCallStack& stack) { return NULL; } // Wrap memory with guard - GuardedMemory guarded(ptr, size + nmt_header_size); + GuardedMemory guarded(ptr, size + nmt_overhead); ptr = guarded.get_user_ptr(); if ((intptr_t)ptr == (intptr_t)MallocCatchPtr) { @@ -741,8 +742,9 @@ void* os::realloc(void *memblock, size_t size, MEMFLAGS memflags, const NativeCa // NMT support NMT_TrackingLevel level = MemTracker::tracking_level(); void* membase = MemTracker::record_free(memblock, level); - size_t nmt_header_size = MemTracker::malloc_header_size(level); - void* ptr = ::realloc(membase, size + nmt_header_size); + const size_t nmt_overhead = + MemTracker::malloc_header_size(level) + MemTracker::malloc_footer_size(level); + void* ptr = ::realloc(membase, size + nmt_overhead); return MemTracker::record_malloc(ptr, size, memflags, stack, level); #else if (memblock == NULL) { @@ -761,7 +763,10 @@ void* os::realloc(void *memblock, size_t size, MEMFLAGS memflags, const NativeCa if (ptr != NULL ) { GuardedMemory guarded(MemTracker::malloc_base(memblock)); // Guard's user data contains NMT header - size_t memblock_size = guarded.get_user_size() - MemTracker::malloc_header_size(memblock); + NMT_TrackingLevel level = MemTracker::tracking_level(); + const size_t nmt_overhead = + MemTracker::malloc_header_size(level) + MemTracker::malloc_footer_size(level); + size_t memblock_size = guarded.get_user_size() - nmt_overhead; memcpy(ptr, memblock, MIN2(size, memblock_size)); if (paranoid) { verify_memory(MemTracker::malloc_base(ptr)); diff --git a/src/hotspot/share/runtime/sharedRuntime.hpp b/src/hotspot/share/runtime/sharedRuntime.hpp index ec816928ac79c..1adc96619855c 100644 --- a/src/hotspot/share/runtime/sharedRuntime.hpp +++ b/src/hotspot/share/runtime/sharedRuntime.hpp @@ -384,8 +384,6 @@ class SharedRuntime: AllStatic { uint num_bits, uint total_args_passed); - static size_t trampoline_size(); - // Generate I2C and C2I adapters. These adapters are simple argument marshalling // blobs. Unlike adapters in the tiger and earlier releases the code in these // blobs does not create a new frame and are therefore virtually invisible diff --git a/src/hotspot/share/services/heapDumper.cpp b/src/hotspot/share/services/heapDumper.cpp index c3c939c06d0e6..b14e1223943f6 100644 --- a/src/hotspot/share/services/heapDumper.cpp +++ b/src/hotspot/share/services/heapDumper.cpp @@ -1910,7 +1910,6 @@ class VM_HeapDumper : public VM_GC_Operation, public WorkerTask { // Number of dumper threads that only iterate heap. uint _heap_only_dumper_threads = _num_dumper_threads - 1 /* VMDumper thread */; _dumper_controller = new (std::nothrow) DumperController(_heap_only_dumper_threads); - _poi = Universe::heap()->parallel_object_iterator(_num_dumper_threads); } } @@ -1999,10 +1998,6 @@ class VM_HeapDumper : public VM_GC_Operation, public WorkerTask { } FREE_C_HEAP_ARRAY(ThreadStackTrace*, _stack_traces); } - if (_poi != NULL) { - delete _poi; - _poi = NULL; - } if (_dumper_controller != NULL) { delete _dumper_controller; _dumper_controller = NULL; @@ -2252,7 +2247,14 @@ void VM_HeapDumper::doit() { work(0); } else { prepare_parallel_dump(workers->active_workers()); - workers->run_task(this); + if (_num_dumper_threads > 1) { + ParallelObjectIterator poi(_num_dumper_threads); + _poi = &poi; + workers->run_task(this); + _poi = NULL; + } else { + workers->run_task(this); + } finish_parallel_dump(); } diff --git a/src/hotspot/share/services/mallocSiteTable.cpp b/src/hotspot/share/services/mallocSiteTable.cpp index 869a67385318f..dae1357cef12c 100644 --- a/src/hotspot/share/services/mallocSiteTable.cpp +++ b/src/hotspot/share/services/mallocSiteTable.cpp @@ -39,7 +39,6 @@ volatile int MallocSiteTable::_access_count = 0; // Tracking hashtable contention NOT_PRODUCT(int MallocSiteTable::_peak_count = 0;) - /* * Initialize malloc site table. * Hashtable entry is malloc'd, so it can cause infinite recursion. @@ -49,7 +48,6 @@ NOT_PRODUCT(int MallocSiteTable::_peak_count = 0;) * time, it is in single-threaded mode from JVM perspective. */ bool MallocSiteTable::initialize() { - assert((size_t)table_size <= MAX_MALLOCSITE_TABLE_SIZE, "Hashtable overflow"); // Fake the call stack for hashtable entry allocation assert(NMT_TrackingStackDepth > 1, "At least one tracking stack"); diff --git a/src/hotspot/share/services/mallocSiteTable.hpp b/src/hotspot/share/services/mallocSiteTable.hpp index f401d456b18a9..8aa25310326dc 100644 --- a/src/hotspot/share/services/mallocSiteTable.hpp +++ b/src/hotspot/share/services/mallocSiteTable.hpp @@ -114,6 +114,9 @@ class MallocSiteTable : AllStatic { table_size = (table_base_size * NMT_TrackingStackDepth - 1) }; + // The table must not be wider than the maximum value the bucket_idx field + // in the malloc header can hold. + STATIC_ASSERT(table_size <= MAX_MALLOCSITE_TABLE_SIZE); // This is a very special lock, that allows multiple shared accesses (sharedLock), but // once exclusive access (exclusiveLock) is requested, all shared accesses are diff --git a/src/hotspot/share/services/mallocTracker.cpp b/src/hotspot/share/services/mallocTracker.cpp index e67f2d8a2a909..be1d6eea33078 100644 --- a/src/hotspot/share/services/mallocTracker.cpp +++ b/src/hotspot/share/services/mallocTracker.cpp @@ -23,10 +23,13 @@ */ #include "precompiled.hpp" +#include "runtime/os.hpp" #include "services/mallocSiteTable.hpp" #include "services/mallocTracker.hpp" #include "services/mallocTracker.inline.hpp" #include "services/memTracker.hpp" +#include "utilities/debug.hpp" +#include "utilities/ostream.hpp" size_t MallocMemorySummary::_snapshot[CALC_OBJ_SIZE_IN_TYPE(MallocMemorySnapshot, size_t)]; @@ -103,15 +106,118 @@ void MallocMemorySummary::initialize() { ::new ((void*)_snapshot)MallocMemorySnapshot(); } -void MallocHeader::release() const { +void MallocHeader::mark_block_as_dead() { + _canary = _header_canary_dead_mark; + NOT_LP64(_alt_canary = _header_alt_canary_dead_mark); + set_footer(_footer_canary_dead_mark); +} + +void MallocHeader::release() { // Tracking already shutdown, no housekeeping is needed anymore if (MemTracker::tracking_level() <= NMT_minimal) return; + check_block_integrity(); + MallocMemorySummary::record_free(size(), flags()); MallocMemorySummary::record_free_malloc_header(sizeof(MallocHeader)); if (MemTracker::tracking_level() == NMT_detail) { MallocSiteTable::deallocation_at(size(), _bucket_idx, _pos_idx); } + + mark_block_as_dead(); +} + +void MallocHeader::print_block_on_error(outputStream* st, address bad_address) const { + assert(bad_address >= (address)this, "sanity"); + + // This function prints block information, including hex dump, in case of a detected + // corruption. The hex dump should show both block header and corruption site + // (which may or may not be close together or identical). Plus some surrounding area. + // + // Note that we use os::print_hex_dump(), which is able to cope with unmapped + // memory (it uses SafeFetch). + + st->print_cr("NMT Block at " PTR_FORMAT ", corruption at: " PTR_FORMAT ": ", + p2i(this), p2i(bad_address)); + static const size_t min_dump_length = 256; + address from1 = align_down((address)this, sizeof(void*)) - (min_dump_length / 2); + address to1 = from1 + min_dump_length; + address from2 = align_down(bad_address, sizeof(void*)) - (min_dump_length / 2); + address to2 = from2 + min_dump_length; + if (from2 > to1) { + // Dump gets too large, split up in two sections. + os::print_hex_dump(st, from1, to1, 1); + st->print_cr("..."); + os::print_hex_dump(st, from2, to2, 1); + } else { + // print one hex dump + os::print_hex_dump(st, from1, to2, 1); + } +} + +// Check block integrity. If block is broken, print out a report +// to tty (optionally with hex dump surrounding the broken block), +// then trigger a fatal error. +void MallocHeader::check_block_integrity() const { + +#define PREFIX "NMT corruption: " + // Note: if you modify the error messages here, make sure you + // adapt the associated gtests too. + + // Weed out obviously wrong block addresses of NULL or very low + // values. Note that we should not call this for ::free(NULL), + // which should be handled by os::free() above us. + if (((size_t)p2i(this)) < K) { + fatal(PREFIX "Block at " PTR_FORMAT ": invalid block address", p2i(this)); + } + + // From here on we assume the block pointer to be valid. We could + // use SafeFetch but since this is a hot path we don't. If we are + // wrong, we will crash when accessing the canary, which hopefully + // generates distinct crash report. + + // Weed out obviously unaligned addresses. NMT blocks, being the result of + // malloc calls, should adhere to malloc() alignment. Malloc alignment is + // specified by the standard by this requirement: + // "malloc returns a pointer which is suitably aligned for any built-in type" + // For us it means that it is *at least* 64-bit on all of our 32-bit and + // 64-bit platforms since we have native 64-bit types. It very probably is + // larger than that, since there exist scalar types larger than 64bit. Here, + // we test the smallest alignment we know. + // Should we ever start using std::max_align_t, this would be one place to + // fix up. + if (!is_aligned(this, sizeof(uint64_t))) { + print_block_on_error(tty, (address)this); + fatal(PREFIX "Block at " PTR_FORMAT ": block address is unaligned", p2i(this)); + } + + // Check header canary + if (_canary != _header_canary_life_mark) { + print_block_on_error(tty, (address)this); + fatal(PREFIX "Block at " PTR_FORMAT ": header canary broken.", p2i(this)); + } + +#ifndef _LP64 + // On 32-bit we have a second canary, check that one too. + if (_alt_canary != _header_alt_canary_life_mark) { + print_block_on_error(tty, (address)this); + fatal(PREFIX "Block at " PTR_FORMAT ": header alternate canary broken.", p2i(this)); + } +#endif + + // Does block size seems reasonable? + if (_size >= max_reasonable_malloc_size) { + print_block_on_error(tty, (address)this); + fatal(PREFIX "Block at " PTR_FORMAT ": header looks invalid (weirdly large block size)", p2i(this)); + } + + // Check footer canary + if (get_footer() != _footer_canary_life_mark) { + print_block_on_error(tty, footer_address()); + fatal(PREFIX "Block at " PTR_FORMAT ": footer canary broken at " PTR_FORMAT " (buffer overflow?)", + p2i(this), p2i(footer_address())); + } +#undef PREFIX } bool MallocHeader::record_malloc_site(const NativeCallStack& stack, size_t size, diff --git a/src/hotspot/share/services/mallocTracker.hpp b/src/hotspot/share/services/mallocTracker.hpp index 74fbba3a51c04..9fd3968831be2 100644 --- a/src/hotspot/share/services/mallocTracker.hpp +++ b/src/hotspot/share/services/mallocTracker.hpp @@ -239,31 +239,99 @@ class MallocMemorySummary : AllStatic { /* * Malloc tracking header. - * To satisfy malloc alignment requirement, NMT uses 2 machine words for tracking purpose, - * which ensures 8-bytes alignment on 32-bit systems and 16-bytes on 64-bit systems (Product build). + * + * If NMT is active (state >= minimal), we need to track allocations. A simple and cheap way to + * do this is by using malloc headers. + * + * The user allocation is preceded by a header and is immediately followed by a (possibly unaligned) + * footer canary: + * + * +--------------+------------- .... ------------------+-----+ + * | header | user | can | + * | | allocation | ary | + * +--------------+------------- .... ------------------+-----+ + * 16 bytes user size 2 byte + * + * Alignment: + * + * The start of the user allocation needs to adhere to malloc alignment. We assume 128 bits + * on both 64-bit/32-bit to be enough for that. So the malloc header is 16 bytes long on both + * 32-bit and 64-bit. + * + * Layout on 64-bit: + * + * 0 1 2 3 4 5 6 7 + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * | 64-bit size | ... + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * + * 8 9 10 11 12 13 14 15 16 ++ + * +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------ + * ... | bucket idx | pos idx | flags | unused | canary | ... User payload .... + * +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------ + * + * Layout on 32-bit: + * + * 0 1 2 3 4 5 6 7 + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * | alt. canary | 32-bit size | ... + * +--------+--------+--------+--------+--------+--------+--------+--------+ + * + * 8 9 10 11 12 13 14 15 16 ++ + * +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------ + * ... | bucket idx | pos idx | flags | unused | canary | ... User payload .... + * +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------ + * + * Notes: + * - We have a canary in the two bytes directly preceding the user payload. That allows us to + * catch negative buffer overflows. + * - On 32-bit, due to the smaller size_t, we have some bits to spare. So we also have a second + * canary at the very start of the malloc header (generously sized 32 bits). + * - The footer canary consists of two bytes. Since the footer location may be unaligned to 16 bits, + * the bytes are stored individually. */ class MallocHeader { -#ifdef _LP64 - size_t _size : 64; - size_t _flags : 8; - size_t _pos_idx : 16; - size_t _bucket_idx: 40; -#define MAX_MALLOCSITE_TABLE_SIZE right_n_bits(40) -#define MAX_BUCKET_LENGTH right_n_bits(16) -#else - size_t _size : 32; - size_t _flags : 8; - size_t _pos_idx : 8; - size_t _bucket_idx: 16; -#define MAX_MALLOCSITE_TABLE_SIZE right_n_bits(16) -#define MAX_BUCKET_LENGTH right_n_bits(8) -#endif // _LP64 + + NOT_LP64(uint32_t _alt_canary); + size_t _size; + uint16_t _bucket_idx; + uint16_t _pos_idx; + uint8_t _flags; + uint8_t _unused; + uint16_t _canary; + +#define MAX_MALLOCSITE_TABLE_SIZE (USHRT_MAX - 1) +#define MAX_BUCKET_LENGTH (USHRT_MAX - 1) + + static const uint16_t _header_canary_life_mark = 0xE99E; + static const uint16_t _header_canary_dead_mark = 0xD99D; + static const uint16_t _footer_canary_life_mark = 0xE88E; + static const uint16_t _footer_canary_dead_mark = 0xD88D; + NOT_LP64(static const uint32_t _header_alt_canary_life_mark = 0xE99EE99E;) + NOT_LP64(static const uint32_t _header_alt_canary_dead_mark = 0xD88DD88D;) + + // We discount sizes larger than these + static const size_t max_reasonable_malloc_size = LP64_ONLY(256 * G) NOT_LP64(3500 * M); + + // Check block integrity. If block is broken, print out a report + // to tty (optionally with hex dump surrounding the broken block), + // then trigger a fatal error. + void check_block_integrity() const; + void print_block_on_error(outputStream* st, address bad_address) const; + void mark_block_as_dead(); + + static uint16_t build_footer(uint8_t b1, uint8_t b2) { return ((uint16_t)b1 << 8) | (uint16_t)b2; } + + uint8_t* footer_address() const { return ((address)this) + sizeof(MallocHeader) + _size; } + uint16_t get_footer() const { return build_footer(footer_address()[0], footer_address()[1]); } + void set_footer(uint16_t v) { footer_address()[0] = v >> 8; footer_address()[1] = (uint8_t)v; } public: + MallocHeader(size_t size, MEMFLAGS flags, const NativeCallStack& stack, NMT_TrackingLevel level) { - assert(sizeof(MallocHeader) == sizeof(void*) * 2, - "Wrong header size"); + + assert(size < max_reasonable_malloc_size, "Too large allocation size?"); if (level == NMT_minimal) { return; @@ -277,11 +345,18 @@ class MallocHeader { if (record_malloc_site(stack, size, &bucket_idx, &pos_idx, flags)) { assert(bucket_idx <= MAX_MALLOCSITE_TABLE_SIZE, "Overflow bucket index"); assert(pos_idx <= MAX_BUCKET_LENGTH, "Overflow bucket position index"); - _bucket_idx = bucket_idx; - _pos_idx = pos_idx; + _bucket_idx = (uint16_t)bucket_idx; + _pos_idx = (uint16_t)pos_idx; } } + _unused = 0; + _canary = _header_canary_life_mark; + // On 32-bit we have some bits more, use them for a second canary + // guarding the start of the header. + NOT_LP64(_alt_canary = _header_alt_canary_life_mark;) + set_footer(_footer_canary_life_mark); // set after initializing _size + MallocMemorySummary::record_malloc(size, flags); MallocMemorySummary::record_new_malloc_header(sizeof(MallocHeader)); } @@ -290,8 +365,8 @@ class MallocHeader { inline MEMFLAGS flags() const { return (MEMFLAGS)_flags; } bool get_stack(NativeCallStack& stack) const; - // Cleanup tracking information before the memory is released. - void release() const; + // Cleanup tracking information and mark block as dead before the memory is released. + void release(); private: inline void set_size(size_t size) { @@ -301,6 +376,9 @@ class MallocHeader { size_t* bucket_idx, size_t* pos_idx, MEMFLAGS flags) const; }; +// This needs to be true on both 64-bit and 32-bit platforms +STATIC_ASSERT(sizeof(MallocHeader) == (sizeof(uint64_t) * 2)); + // Main class called from MemTracker to track malloc activities class MallocTracker : AllStatic { @@ -315,6 +393,11 @@ class MallocTracker : AllStatic { return (level == NMT_off) ? 0 : sizeof(MallocHeader); } + // malloc tracking footer size for specific tracking level + static inline size_t malloc_footer_size(NMT_TrackingLevel level) { + return (level == NMT_off) ? 0 : sizeof(uint16_t); + } + // Parameter name convention: // memblock : the beginning address for user data // malloc_base: the beginning address that includes malloc tracking header @@ -349,11 +432,6 @@ class MallocTracker : AllStatic { return header->flags(); } - // Get header size - static inline size_t get_header_size(void* memblock) { - return (memblock == NULL) ? 0 : sizeof(MallocHeader); - } - static inline void record_new_arena(MEMFLAGS flags) { MallocMemorySummary::record_new_arena(flags); } diff --git a/src/hotspot/share/services/memTracker.hpp b/src/hotspot/share/services/memTracker.hpp index d18746f452e32..21d1aa8263082 100644 --- a/src/hotspot/share/services/memTracker.hpp +++ b/src/hotspot/share/services/memTracker.hpp @@ -58,6 +58,7 @@ class MemTracker : AllStatic { const NativeCallStack& stack, NMT_TrackingLevel level) { return mem_base; } static inline size_t malloc_header_size(NMT_TrackingLevel level) { return 0; } static inline size_t malloc_header_size(void* memblock) { return 0; } + static inline size_t malloc_footer_size(NMT_TrackingLevel level) { return 0; } static inline void* malloc_base(void* memblock) { return memblock; } static inline void* record_free(void* memblock, NMT_TrackingLevel level) { return memblock; } @@ -157,11 +158,9 @@ class MemTracker : AllStatic { return MallocTracker::malloc_header_size(level); } - static size_t malloc_header_size(void* memblock) { - if (tracking_level() != NMT_off) { - return MallocTracker::get_header_size(memblock); - } - return 0; + // malloc tracking footer size for specific tracking level + static inline size_t malloc_footer_size(NMT_TrackingLevel level) { + return MallocTracker::malloc_footer_size(level); } // To malloc base address, which is the starting address diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp index 778d1a1b4a4ef..1a0c3a84647fe 100644 --- a/src/hotspot/share/utilities/debug.cpp +++ b/src/hotspot/share/utilities/debug.cpp @@ -237,7 +237,6 @@ void report_vm_error(const char* file, int line, const char* error_msg) static void print_error_for_unit_test(const char* message, const char* detail_fmt, va_list detail_args) { -#ifdef ASSERT if (ExecutingUnitTests) { char detail_msg[256]; if (detail_fmt != NULL) { @@ -262,7 +261,6 @@ static void print_error_for_unit_test(const char* message, const char* detail_fm va_end(detail_args_copy); } } -#endif // ASSERT } void report_vm_error(const char* file, int line, const char* error_msg, const char* detail_fmt, ...) diff --git a/src/java.base/share/classes/java/io/ObjectInputStream.java b/src/java.base/share/classes/java/io/ObjectInputStream.java index d58bf1d91c946..d91939684eb2f 100644 --- a/src/java.base/share/classes/java/io/ObjectInputStream.java +++ b/src/java.base/share/classes/java/io/ObjectInputStream.java @@ -297,6 +297,15 @@ private static class Caches { static final boolean SET_FILTER_AFTER_READ = GetBooleanAction .privilegedGetProperty("jdk.serialSetFilterAfterRead"); + /** + * Property to control {@link GetField#get(String, Object)} conversion of + * {@link ClassNotFoundException} to {@code null}. If set to {@code true} + * {@link GetField#get(String, Object)} returns null otherwise + * throwing {@link ClassNotFoundException}. + */ + private static final boolean GETFIELD_CNFE_RETURNS_NULL = GetBooleanAction + .privilegedGetProperty("jdk.serialGetFieldCnfeReturnsNull"); + /** * Property to override the implementation limit on the number * of interfaces allowed for Proxies. The property value is clamped to 0..65535. @@ -1596,12 +1605,13 @@ public abstract boolean get(String name, boolean val) * @param val the default value to use if {@code name} does not * have a value * @return the value of the named {@code Object} field + * @throws ClassNotFoundException Class of a serialized object cannot be found. * @throws IOException if there are I/O errors while reading from the * underlying {@code InputStream} * @throws IllegalArgumentException if type of {@code name} is * not serializable or if the field type is incorrect */ - public abstract Object get(String name, Object val) throws IOException; + public abstract Object get(String name, Object val) throws IOException, ClassNotFoundException; } /** @@ -2645,13 +2655,19 @@ public double get(String name, double val) { return (off >= 0) ? Bits.getDouble(primValues, off) : val; } - public Object get(String name, Object val) { + public Object get(String name, Object val) throws ClassNotFoundException { int off = getFieldOffset(name, Object.class); if (off >= 0) { int objHandle = objHandles[off]; handles.markDependency(passHandle, objHandle); - return (handles.lookupException(objHandle) == null) ? - objValues[off] : null; + ClassNotFoundException ex = handles.lookupException(objHandle); + if (ex == null) + return objValues[off]; + if (Caches.GETFIELD_CNFE_RETURNS_NULL) { + // Revert to the prior behavior; return null instead of CNFE + return null; + } + throw ex; } else { return val; } diff --git a/src/java.base/share/classes/java/lang/Module.java b/src/java.base/share/classes/java/lang/Module.java index ab7d21038507d..e822c95142066 100644 --- a/src/java.base/share/classes/java/lang/Module.java +++ b/src/java.base/share/classes/java/lang/Module.java @@ -68,6 +68,7 @@ import jdk.internal.org.objectweb.asm.Opcodes; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; +import jdk.internal.vm.annotation.Stable; import sun.security.util.SecurityConstants; /** @@ -110,7 +111,8 @@ public final class Module implements AnnotatedElement { private final ModuleDescriptor descriptor; // true, if this module allows restricted native access - private volatile boolean enableNativeAccess; + @Stable + private boolean enableNativeAccess; /** * Creates a new named Module. The resulting Module will be defined to the diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java index a428380443ee6..404782c047e2a 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java @@ -1667,6 +1667,10 @@ public Lookup defineHiddenClassWithClassData(Lookup caller, String name, byte[] return caller.makeHiddenClassDefiner(name, bytes, Set.of()).defineClassAsLookup(initialize, classData); } + @Override + public Class[] exceptionTypes(MethodHandle handle) { + return VarHandles.exceptionTypes(handle); + } }); } @@ -2273,15 +2277,16 @@ static Object tableSwitch(int input, MethodHandle defaultCase, CasesHolder holde // Indexes into constant method handles: static final int - MH_cast = 0, - MH_selectAlternative = 1, - MH_countedLoopPred = 2, - MH_countedLoopStep = 3, - MH_initIterator = 4, - MH_iteratePred = 5, - MH_iterateNext = 6, - MH_Array_newInstance = 7, - MH_LIMIT = 8; + MH_cast = 0, + MH_selectAlternative = 1, + MH_countedLoopPred = 2, + MH_countedLoopStep = 3, + MH_initIterator = 4, + MH_iteratePred = 5, + MH_iterateNext = 6, + MH_Array_newInstance = 7, + MH_VarHandles_handleCheckedExceptions = 8, + MH_LIMIT = 9; static MethodHandle getConstantHandle(int idx) { MethodHandle handle = HANDLES[idx]; @@ -2331,6 +2336,9 @@ private static MethodHandle makeConstantHandle(int idx) { case MH_Array_newInstance: return IMPL_LOOKUP.findStatic(Array.class, "newInstance", MethodType.methodType(Object.class, Class.class, int.class)); + case MH_VarHandles_handleCheckedExceptions: + return IMPL_LOOKUP.findStatic(VarHandles.class, "handleCheckedExceptions", + MethodType.methodType(void.class, Throwable.class)); } } catch (ReflectiveOperationException ex) { throw newInternalError(ex); diff --git a/src/java.base/share/classes/java/lang/invoke/VarHandles.java b/src/java.base/share/classes/java/lang/invoke/VarHandles.java index fb86bfab0e8c7..45cdb75b2ee30 100644 --- a/src/java.base/share/classes/java/lang/invoke/VarHandles.java +++ b/src/java.base/share/classes/java/lang/invoke/VarHandles.java @@ -31,12 +31,9 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.lang.reflect.Parameter; import java.nio.ByteOrder; import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -45,8 +42,6 @@ import static java.lang.invoke.MethodHandleStatics.UNSAFE; import static java.lang.invoke.MethodHandleStatics.VAR_HANDLE_IDENTITY_ADAPT; import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException; -import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toList; final class VarHandles { @@ -359,13 +354,13 @@ private static VarHandle maybeAdapt(VarHandle target) { return target; } - public static VarHandle filterValue(VarHandle target, MethodHandle filterToTarget, MethodHandle filterFromTarget) { + public static VarHandle filterValue(VarHandle target, MethodHandle pFilterToTarget, MethodHandle pFilterFromTarget) { Objects.requireNonNull(target); - Objects.requireNonNull(filterToTarget); - Objects.requireNonNull(filterFromTarget); + Objects.requireNonNull(pFilterToTarget); + Objects.requireNonNull(pFilterFromTarget); //check that from/to filters do not throw checked exceptions - noCheckedExceptions(filterToTarget); - noCheckedExceptions(filterFromTarget); + MethodHandle filterToTarget = adaptForCheckedExceptions(pFilterToTarget); + MethodHandle filterFromTarget = adaptForCheckedExceptions(pFilterFromTarget); List> newCoordinates = new ArrayList<>(); List> additionalCoordinates = new ArrayList<>(); @@ -473,8 +468,9 @@ public static VarHandle filterCoordinates(VarHandle target, int pos, MethodHandl List> newCoordinates = new ArrayList<>(targetCoordinates); for (int i = 0 ; i < filters.length ; i++) { - noCheckedExceptions(filters[i]); - MethodType filterType = filters[i].type(); + MethodHandle filter = Objects.requireNonNull(filters[i]); + filter = adaptForCheckedExceptions(filter); + MethodType filterType = filter.type(); if (filterType.parameterCount() != 1) { throw newIllegalArgumentException("Invalid filter type " + filterType); } else if (newCoordinates.get(pos + i) != filterType.returnType()) { @@ -564,10 +560,10 @@ private static MethodType methodTypeFor(VarHandle.AccessType at, MethodType oldT return adjustedType; } - public static VarHandle collectCoordinates(VarHandle target, int pos, MethodHandle filter) { + public static VarHandle collectCoordinates(VarHandle target, int pos, MethodHandle pFilter) { Objects.requireNonNull(target); - Objects.requireNonNull(filter); - noCheckedExceptions(filter); + Objects.requireNonNull(pFilter); + MethodHandle filter = adaptForCheckedExceptions(pFilter); List> targetCoordinates = target.coordinateTypes(); if (pos < 0 || pos >= targetCoordinates.size()) { @@ -604,42 +600,55 @@ public static VarHandle dropCoordinates(VarHandle target, int pos, Class... v (mode, modeHandle) -> MethodHandles.dropArguments(modeHandle, 1 + pos, valueTypes)); } - private static void noCheckedExceptions(MethodHandle handle) { + private static MethodHandle adaptForCheckedExceptions(MethodHandle target) { + Class[] exceptionTypes = exceptionTypes(target); + if (exceptionTypes != null) { // exceptions known + if (Stream.of(exceptionTypes).anyMatch(VarHandles::isCheckedException)) { + throw newIllegalArgumentException("Cannot adapt a var handle with a method handle which throws checked exceptions"); + } + return target; // no adaptation needed + } else { + MethodHandle handler = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_VarHandles_handleCheckedExceptions); + MethodHandle zero = MethodHandles.zero(target.type().returnType()); // dead branch + handler = MethodHandles.collectArguments(zero, 0, handler); + return MethodHandles.catchException(target, Throwable.class, handler); + } + } + + static void handleCheckedExceptions(Throwable throwable) throws Throwable { + if (isCheckedException(throwable.getClass())) { + throw new IllegalStateException("Adapter handle threw checked exception", throwable); + } + throw throwable; + } + + static Class[] exceptionTypes(MethodHandle handle) { if (handle instanceof DirectMethodHandle directHandle) { byte refKind = directHandle.member.getReferenceKind(); MethodHandleInfo info = new InfoFromMemberName( MethodHandles.Lookup.IMPL_LOOKUP, directHandle.member, refKind); - final Class[] exceptionTypes; if (MethodHandleNatives.refKindIsMethod(refKind)) { - exceptionTypes = info.reflectAs(Method.class, MethodHandles.Lookup.IMPL_LOOKUP) + return info.reflectAs(Method.class, MethodHandles.Lookup.IMPL_LOOKUP) .getExceptionTypes(); } else if (MethodHandleNatives.refKindIsField(refKind)) { - exceptionTypes = null; + return new Class[0]; } else if (MethodHandleNatives.refKindIsConstructor(refKind)) { - exceptionTypes = info.reflectAs(Constructor.class, MethodHandles.Lookup.IMPL_LOOKUP) + return info.reflectAs(Constructor.class, MethodHandles.Lookup.IMPL_LOOKUP) .getExceptionTypes(); } else { throw new AssertionError("Cannot get here"); } - if (exceptionTypes != null) { - if (Stream.of(exceptionTypes).anyMatch(VarHandles::isCheckedException)) { - throw newIllegalArgumentException("Cannot adapt a var handle with a method handle which throws checked exceptions"); - } - } } else if (handle instanceof DelegatingMethodHandle) { - noCheckedExceptions(((DelegatingMethodHandle)handle).getTarget()); - } else { - //bound - BoundMethodHandle boundHandle = (BoundMethodHandle)handle; - for (int i = 0 ; i < boundHandle.fieldCount() ; i++) { - Object arg = boundHandle.arg(i); - if (arg instanceof MethodHandle){ - noCheckedExceptions((MethodHandle) arg); - } - } + return exceptionTypes(((DelegatingMethodHandle)handle).getTarget()); + } else if (handle instanceof NativeMethodHandle) { + return new Class[0]; } + + assert handle instanceof BoundMethodHandle : "Unexpected handle type: " + handle; + // unknown + return null; } private static boolean isCheckedException(Class clazz) { diff --git a/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java b/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java index 62c81c42f5c8d..5d092328f756e 100644 --- a/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java +++ b/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java @@ -29,9 +29,11 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.lang.invoke.StringConcatFactory; import java.lang.invoke.TypeDescriptor; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -52,6 +54,8 @@ public class ObjectMethods { private ObjectMethods() { } + private static final int MAX_STRING_CONCAT_SLOTS = 20; + private static final MethodType DESCRIPTOR_MT = MethodType.methodType(MethodType.class); private static final MethodType NAMES_MT = MethodType.methodType(List.class); private static final MethodHandle FALSE = MethodHandles.constant(boolean.class, false); @@ -251,44 +255,110 @@ private static MethodHandle makeHashCode(Class receiverClass, * @param names the names * @return the method handle */ - private static MethodHandle makeToString(Class receiverClass, - List getters, + private static MethodHandle makeToString(MethodHandles.Lookup lookup, + Class receiverClass, + MethodHandle[] getters, List names) { - // This is a pretty lousy algorithm; we spread the receiver over N places, - // apply the N getters, apply N toString operations, and concat the result with String.format - // Better to use String.format directly, or delegate to StringConcatFactory - // Also probably want some quoting around String components - - assert getters.size() == names.size(); - - int[] invArgs = new int[getters.size()]; - Arrays.fill(invArgs, 0); - MethodHandle[] filters = new MethodHandle[getters.size()]; - StringBuilder sb = new StringBuilder(); - sb.append(receiverClass.getSimpleName()).append("["); - for (int i=0; i> splits; + MethodHandle[] toSplit = getters; + int namesIndex = 0; + do { + /* StringConcatFactory::makeConcatWithConstants can only deal with 200 slots, longs and double occupy two + * the rest 1 slot, we need to chop the current `getters` into chunks, it could be that for records with + * a lot of components that we need to do a couple of iterations. The main difference between the first + * iteration and the rest would be on the recipe + */ + splits = split(toSplit); + mhs = new MethodHandle[splits.size()]; + for (int splitIndex = 0; splitIndex < splits.size(); splitIndex++) { + String recipe = ""; + if (firstTime && splitIndex == 0) { + recipe = receiverClass.getSimpleName() + "["; + } + for (int i = 0; i < splits.get(splitIndex).size(); i++) { + recipe += firstTime ? names.get(namesIndex) + "=" + "\1" : "\1"; + if (firstTime && namesIndex != names.size() - 1) { + recipe += ", "; + } + namesIndex++; + } + if (firstTime && splitIndex == splits.size() - 1) { + recipe += "]"; + } + Class[] concatTypeArgs = new Class[splits.get(splitIndex).size()]; + // special case: no need to create another getters if there is only one split + MethodHandle[] currentSplitGetters = new MethodHandle[splits.get(splitIndex).size()]; + for (int j = 0; j < splits.get(splitIndex).size(); j++) { + concatTypeArgs[j] = splits.get(splitIndex).get(j).type().returnType(); + currentSplitGetters[j] = splits.get(splitIndex).get(j); + } + MethodType concatMT = MethodType.methodType(String.class, concatTypeArgs); + try { + mhs[splitIndex] = StringConcatFactory.makeConcatWithConstants( + lookup, "", + concatMT, + recipe, + new Object[0] + ).getTarget(); + mhs[splitIndex] = MethodHandles.filterArguments(mhs[splitIndex], 0, currentSplitGetters); + // this will spread the receiver class across all the getters + mhs[splitIndex] = MethodHandles.permuteArguments( + mhs[splitIndex], + MethodType.methodType(String.class, receiverClass), + new int[splits.get(splitIndex).size()] + ); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + toSplit = mhs; + firstTime = false; + } while (splits.size() > 1); + return mhs[0]; + } + + /** + * Chops the getters into smaller chunks according to the maximum number of slots + * StringConcatFactory::makeConcatWithConstants can chew + * @param getters the current getters + * @return chunks that wont surpass the maximum number of slots StringConcatFactory::makeConcatWithConstants can chew + */ + private static List> split(MethodHandle[] getters) { + List> splits = new ArrayList<>(); + + int slots = 0; + + // Need to peel, so that neither call has more than acceptable number + // of slots for the arguments. + List cArgs = new ArrayList<>(); + for (MethodHandle methodHandle : getters) { + Class returnType = methodHandle.type().returnType(); + int needSlots = (returnType == long.class || returnType == double.class) ? 2 : 1; + if (slots + needSlots > MAX_STRING_CONCAT_SLOTS) { + splits.add(cArgs); + cArgs = new ArrayList<>(); + slots = 0; + } + cArgs.add(methodHandle); + slots += needSlots; } - else { - MethodHandle filtered = MethodHandles.filterArguments(formatter, 0, filters); - formatter = MethodHandles.permuteArguments(filtered, MethodType.methodType(String.class, receiverClass), invArgs); + + // Flush the tail slice + if (!cArgs.isEmpty()) { + splits.add(cArgs); } - return formatter; + return splits; } /** @@ -367,7 +437,7 @@ public static Object bootstrap(MethodHandles.Lookup lookup, String methodName, T List nameList = "".equals(names) ? List.of() : List.of(names.split(";")); if (nameList.size() != getterList.size()) throw new IllegalArgumentException("Name list and accessor list do not match"); - yield makeToString(recordClass, getterList, nameList); + yield makeToString(lookup, recordClass, getters, nameList); } default -> throw new IllegalArgumentException(methodName); }; diff --git a/src/java.base/share/classes/java/nio/Buffer.java b/src/java.base/share/classes/java/nio/Buffer.java index 607d5d45183d7..92c9f80dca8c4 100644 --- a/src/java.base/share/classes/java/nio/Buffer.java +++ b/src/java.base/share/classes/java/nio/Buffer.java @@ -820,7 +820,7 @@ public MemorySegmentProxy bufferSegment(Buffer buffer) { } @Override - public Scope.Handle acquireScope(Buffer buffer, boolean async) { + public Runnable acquireScope(Buffer buffer, boolean async) { var scope = buffer.scope(); if (scope == null) { return null; @@ -828,7 +828,8 @@ public Scope.Handle acquireScope(Buffer buffer, boolean async) { if (async && scope.ownerThread() != null) { throw new IllegalStateException("Confined scope not supported"); } - return scope.acquire(); + scope.acquire0(); + return scope::release0; } @Override diff --git a/src/java.base/share/classes/java/util/ImmutableCollections.java b/src/java.base/share/classes/java/util/ImmutableCollections.java index c207e4f1427d6..4c8fd0cfc8fa1 100644 --- a/src/java.base/share/classes/java/util/ImmutableCollections.java +++ b/src/java.base/share/classes/java/util/ImmutableCollections.java @@ -1062,7 +1062,7 @@ public T[] toArray(T[] a) { // ---------- Map Implementations ---------- - @jdk.internal.ValueBased + // Not a jdk.internal.ValueBased class; disqualified by fields in superclass AbstractMap abstract static class AbstractImmutableMap extends AbstractMap implements Serializable { @Override public void clear() { throw uoe(); } @Override public V compute(K key, BiFunction rf) { throw uoe(); } @@ -1093,7 +1093,7 @@ public V getOrDefault(Object key, V defaultValue) { } } - @jdk.internal.ValueBased + // Not a jdk.internal.ValueBased class; disqualified by fields in superclass AbstractMap static final class Map1 extends AbstractImmutableMap { @Stable private final K k0; @@ -1160,7 +1160,7 @@ public int hashCode() { * @param the key type * @param the value type */ - @jdk.internal.ValueBased + // Not a jdk.internal.ValueBased class; disqualified by fields in superclass AbstractMap static final class MapN extends AbstractImmutableMap { @Stable diff --git a/src/java.base/share/classes/java/util/random/RandomGenerator.java b/src/java.base/share/classes/java/util/random/RandomGenerator.java index 9feff71319200..a5e20e55161cc 100644 --- a/src/java.base/share/classes/java/util/random/RandomGenerator.java +++ b/src/java.base/share/classes/java/util/random/RandomGenerator.java @@ -1196,7 +1196,7 @@ default Stream jumps(long streamSize) { * * @return a stream of objects that implement the {@link RandomGenerator} interface * - * @implSpec The default implementation calls {@link JumpableGenerator#jump jump}(). + * @implSpec The default implementation calls {@link JumpableGenerator#jumps jumps}(). */ default Stream rngs() { return this.jumps(); diff --git a/src/java.base/share/classes/java/util/random/package-info.java b/src/java.base/share/classes/java/util/random/package-info.java index 8efaa0bd4c371..102410bffe600 100644 --- a/src/java.base/share/classes/java/util/random/package-info.java +++ b/src/java.base/share/classes/java/util/random/package-info.java @@ -347,12 +347,12 @@ * supported by the interface {@link RandomGenerator.JumpableGenerator}. * Sometimes it is desirable to support two levels of jumping (by long distances * and by really long distances); this strategy is supported by the - * interface {@link RandomGenerator.LeapableGenerator}. There is also an interface + * interface {@link RandomGenerator.LeapableGenerator}. In this package, + * implementations of this interface include "Xoroshiro128PlusPlus" and + * "Xoshiro256PlusPlus". There is also an interface * {@link RandomGenerator.ArbitrarilyJumpableGenerator} for algorithms that allow - * jumping along the state cycle by any user-specified distance. In this package, - * implementations of these interfaces include - * "Xoroshiro128PlusPlus", and - * "Xoshiro256PlusPlus". + * jumping along the state cycle by any user-specified distance; there are currently + * no implementations of this interface in this package. * *

A more recent category of "splittable" pseudorandom generator algorithms * uses a large family of state cycles and makes some attempt to ensure that @@ -382,13 +382,18 @@ * equidistribution, scalability, and better quality. Each of the * specific implementations here combines one of the best currently known * XBG algorithms (xoroshiro128 or xoshiro256, described by Blackman and - * Vigna in "Scrambled Linear Pseudorandom Number Generators", ACM Transactions - * on Mathematical Software, 2021) with an LCG that uses one of the best + * Vigna in "Scrambled Linear Pseudorandom Number Generators", ACM Transactions + * on Mathematical Software, 2021) with an LCG that uses one of the best * currently known multipliers (found by a search for better multipliers - * in 2019 by Steele and Vigna), and then applies either a mixing function - * identified by Doug Lea or a simple scrambler proposed by Blackman and Vigna. - * Testing has confirmed that the LXM algorithm is far superior in quality to - * the SplitMix algorithm (2014) used by {@code SplittableRandom}. + * in 2019 by Steele and Vigna, described in "Computationally Easy, Spectrally + * Good Multipliers for Congruential Pseudorandom Number Generators", + * Software: Practice and Experience (2021), doi:10.1002/spe.3030), + * and then applies either a mixing function identified by Doug Lea or + * or a simple scrambler proposed by Blackman and Vigna. Testing has + * confirmed that the LXM algorithm is far superior in quality to the + * SplitMix algorithm (2014) used by {@code SplittableRandom} + * (see Steele and Vigna, "LXM: Better Splittable Pseudorandom Number + * Generators (and Almost as Fast)", Proc. 2021 ACM OOPSLA Conference). * * Each class with a name of the form * {@code L}p{@code X}q{@code SomethingRandom} diff --git a/src/java.base/share/classes/java/util/regex/Pattern.java b/src/java.base/share/classes/java/util/regex/Pattern.java index 5b2533111f4ef..d52a2b92af939 100644 --- a/src/java.base/share/classes/java/util/regex/Pattern.java +++ b/src/java.base/share/classes/java/util/regex/Pattern.java @@ -5605,50 +5605,69 @@ boolean match(Matcher matcher, int i, CharSequence seq) { } } + private static CharPredicate and(CharPredicate p1, CharPredicate p2, + boolean bmpChar) { + if (bmpChar) { + return (BmpCharPredicate)(ch -> p1.is(ch) && p2.is(ch)); + } else { + return (CharPredicate)(ch -> p1.is(ch) && p2.is(ch)); + } + } + + private static CharPredicate union(CharPredicate p1, CharPredicate p2, + boolean bmpChar) { + if (bmpChar) { + return (BmpCharPredicate)(ch -> p1.is(ch) || p2.is(ch)); + } else { + return (CharPredicate)(ch -> p1.is(ch) || p2.is(ch)); + } + } + + private static CharPredicate union(CharPredicate p1, CharPredicate p2, + CharPredicate p3, boolean bmpChar) { + if (bmpChar) { + return (BmpCharPredicate)(ch -> p1.is(ch) || p2.is(ch) || p3.is(ch)); + } else { + return (CharPredicate)(ch -> p1.is(ch) || p2.is(ch) || p3.is(ch)); + } + } + + private static CharPredicate negate(CharPredicate p1) { + return (CharPredicate)(ch -> !p1.is(ch)); + } + @FunctionalInterface static interface CharPredicate { boolean is(int ch); default CharPredicate and(CharPredicate p) { - return ch -> is(ch) && p.is(ch); + return Pattern.and(this, p, false); } default CharPredicate union(CharPredicate p) { - return ch -> is(ch) || p.is(ch); + return Pattern.union(this, p, false); } default CharPredicate union(CharPredicate p1, CharPredicate p2) { - return ch -> is(ch) || p1.is(ch) || p2.is(ch); + return Pattern.union(this, p1, p2, false); } default CharPredicate negate() { - return ch -> !is(ch); + return Pattern.negate(this); } } static interface BmpCharPredicate extends CharPredicate { default CharPredicate and(CharPredicate p) { - if (p instanceof BmpCharPredicate) - return (BmpCharPredicate)(ch -> is(ch) && p.is(ch)); - return ch -> is(ch) && p.is(ch); + return Pattern.and(this, p, p instanceof BmpCharPredicate); } default CharPredicate union(CharPredicate p) { - if (p instanceof BmpCharPredicate) - return (BmpCharPredicate)(ch -> is(ch) || p.is(ch)); - return ch -> is(ch) || p.is(ch); - } - static CharPredicate union(CharPredicate... predicates) { - CharPredicate cp = ch -> { - for (CharPredicate p : predicates) { - if (!p.is(ch)) - return false; - } - return true; - }; - for (CharPredicate p : predicates) { - if (! (p instanceof BmpCharPredicate)) - return cp; - } - return (BmpCharPredicate)cp; + return Pattern.union(this, p, p instanceof BmpCharPredicate); + } + default CharPredicate union(CharPredicate p1, + CharPredicate p2) { + return Pattern.union(this, p1, p2, + p1 instanceof BmpCharPredicate && + p2 instanceof BmpCharPredicate); } } diff --git a/src/java.base/share/classes/java/util/zip/ZipOutputStream.java b/src/java.base/share/classes/java/util/zip/ZipOutputStream.java index c6dfa69536e39..c43bcd3810339 100644 --- a/src/java.base/share/classes/java/util/zip/ZipOutputStream.java +++ b/src/java.base/share/classes/java/util/zip/ZipOutputStream.java @@ -144,11 +144,14 @@ public ZipOutputStream(OutputStream out, Charset charset) { * ZIP file comment is greater than 0xFFFF bytes */ public void setComment(String comment) { + byte[] bytes = null; if (comment != null) { - this.comment = zc.getBytes(comment); - if (this.comment.length > 0xffff) - throw new IllegalArgumentException("ZIP file comment too long."); + bytes = zc.getBytes(comment); + if (bytes.length > 0xffff) { + throw new IllegalArgumentException("ZIP file comment too long"); + } } + this.comment = bytes; } /** diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java index ee655188c9d3e..20823fcfcad90 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java @@ -181,4 +181,11 @@ VarHandle memoryAccessVarHandle(Class carrier, boolean skipAlignmentMaskCheck * The given bytes is trusted. */ Lookup defineHiddenClassWithClassData(Lookup caller, String name, byte[] bytes, Object classData, boolean initialize); + + /** + * A best-effort method that tries to find any exceptions thrown by the given method handle. + * @param handle the handle to check + * @return an array of exceptions, or {@code null}. + */ + Class[] exceptionTypes(MethodHandle handle); } diff --git a/src/java.base/share/classes/jdk/internal/access/JavaNioAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaNioAccess.java index c639e4ced8321..82739b9bf074c 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaNioAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaNioAccess.java @@ -92,7 +92,7 @@ public interface JavaNioAccess { * scope handle. Null is returned if the buffer has no scope, or * acquiring is not required to guarantee safety. */ - Scope.Handle acquireScope(Buffer buffer, boolean async); + Runnable acquireScope(Buffer buffer, boolean async); /** * Used by {@code jdk.internal.foreign.MappedMemorySegmentImpl} and byte buffer var handle views. diff --git a/src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess.java.template b/src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess.java.template index e4cf2f5b2ed98..be2e8cb24ffc7 100644 --- a/src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess.java.template +++ b/src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess.java.template @@ -103,19 +103,13 @@ public class ScopedMemoryAccess { */ public interface Scope { - interface Handle { - Scope scope(); - } - void checkValidState(); Thread ownerThread(); - boolean isImplicit(); - - Handle acquire(); + void acquire0(); - void release(Handle handle); + void release0(); /** * Error thrown when memory access fails because the memory has already been released. diff --git a/src/java.base/share/classes/jdk/internal/reflect/FieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/FieldAccessorImpl.java index cba836c5edd47..baeb6bf895692 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/FieldAccessorImpl.java +++ b/src/java.base/share/classes/jdk/internal/reflect/FieldAccessorImpl.java @@ -120,7 +120,7 @@ protected void ensureObj(Object o) { } } - private String getQualifiedFieldName() { + protected String getQualifiedFieldName() { return field.getDeclaringClass().getName() + "." +field.getName(); } @@ -223,16 +223,6 @@ protected String getSetMessage(String attemptedType, String attemptedValue) { return err; } - protected String getMessage(boolean getter, String attemptedType) { - String err = "Can not " + (getter ? "get" : "set"); - if (Modifier.isStatic(field.getModifiers())) - err += " static"; - if (Modifier.isFinal(field.getModifiers())) - err += " final"; - err += " " + field.getType().getName() + " field " + getQualifiedFieldName() + " on " + attemptedType; - return err; - } - protected void throwSetIllegalArgumentException(String attemptedType, String attemptedValue) { throw new IllegalArgumentException(getSetMessage(attemptedType,attemptedValue)); diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleBooleanFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleBooleanFieldAccessorImpl.java index 10ec2f0e909d6..2e0609264bd2d 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleBooleanFieldAccessorImpl.java +++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleBooleanFieldAccessorImpl.java @@ -56,7 +56,6 @@ public Object get(Object obj) throws IllegalArgumentException { } public boolean getBoolean(Object obj) throws IllegalArgumentException { - ensureObj(obj); try { if (isStatic()) { return (boolean) getter.invokeExact(); @@ -66,7 +65,7 @@ public boolean getBoolean(Object obj) throws IllegalArgumentException { } catch (IllegalArgumentException|NullPointerException e) { throw e; } catch (ClassCastException e) { - throw newGetIllegalArgumentException(obj.getClass()); + throw newGetIllegalArgumentException(obj); } catch (Throwable e) { throw new InternalError(e); } @@ -122,8 +121,8 @@ public void set(Object obj, Object value) public void setBoolean(Object obj, boolean z) throws IllegalArgumentException, IllegalAccessException { - ensureObj(obj); if (isReadOnly()) { + ensureObj(obj); // throw NPE if obj is null on instance field throwFinalFieldIllegalAccessException(z); } try { @@ -135,7 +134,8 @@ public void setBoolean(Object obj, boolean z) } catch (IllegalArgumentException|NullPointerException e) { throw e; } catch (ClassCastException e) { - throw newSetIllegalArgumentException(obj.getClass()); + // receiver is of invalid type + throw newSetIllegalArgumentException(obj); } catch (Throwable e) { throw new InternalError(e); } diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleByteFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleByteFieldAccessorImpl.java index 7e88e08085466..b56fbbcbcb28e 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleByteFieldAccessorImpl.java +++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleByteFieldAccessorImpl.java @@ -69,7 +69,7 @@ public byte getByte(Object obj) throws IllegalArgumentException { } catch (IllegalArgumentException|NullPointerException e) { throw e; } catch (ClassCastException e) { - throw newGetIllegalArgumentException(obj.getClass()); + throw newGetIllegalArgumentException(obj); } catch (Throwable e) { throw new InternalError(e); } @@ -127,8 +127,8 @@ public void setBoolean(Object obj, boolean z) public void setByte(Object obj, byte b) throws IllegalArgumentException, IllegalAccessException { - ensureObj(obj); if (isReadOnly()) { + ensureObj(obj); // throw NPE if obj is null on instance field throwFinalFieldIllegalAccessException(b); } try { @@ -140,7 +140,8 @@ public void setByte(Object obj, byte b) } catch (IllegalArgumentException|NullPointerException e) { throw e; } catch (ClassCastException e) { - throw newSetIllegalArgumentException(obj.getClass()); + // receiver is of invalid type + throw newSetIllegalArgumentException(obj); } catch (Throwable e) { throw new InternalError(e); } diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleCharacterFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleCharacterFieldAccessorImpl.java index 1e8b4e25525ee..c1f357326f4e2 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleCharacterFieldAccessorImpl.java +++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleCharacterFieldAccessorImpl.java @@ -73,7 +73,7 @@ public char getChar(Object obj) throws IllegalArgumentException { } catch (IllegalArgumentException|NullPointerException e) { throw e; } catch (ClassCastException e) { - throw newGetIllegalArgumentException(obj.getClass()); + throw newGetIllegalArgumentException(obj); } catch (Throwable e) { throw new InternalError(e); } @@ -102,8 +102,8 @@ public double getDouble(Object obj) throws IllegalArgumentException { public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException { + ensureObj(obj); if (isReadOnly()) { - ensureObj(obj); // throw NPE if obj is null on instance field throwFinalFieldIllegalAccessException(value); } @@ -146,7 +146,8 @@ public void setChar(Object obj, char c) } catch (IllegalArgumentException|NullPointerException e) { throw e; } catch (ClassCastException e) { - throw newSetIllegalArgumentException(obj.getClass()); + // receiver is of invalid type + throw newSetIllegalArgumentException(obj); } catch (Throwable e) { throw new InternalError(e); } diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleDoubleFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleDoubleFieldAccessorImpl.java index 2d370c331be69..01652951e4272 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleDoubleFieldAccessorImpl.java +++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleDoubleFieldAccessorImpl.java @@ -93,7 +93,7 @@ public double getDouble(Object obj) throws IllegalArgumentException { } catch (IllegalArgumentException|NullPointerException e) { throw e; } catch (ClassCastException e) { - throw newGetIllegalArgumentException(obj.getClass()); + throw newGetIllegalArgumentException(obj); } catch (Throwable e) { throw new InternalError(e); } @@ -102,8 +102,8 @@ public double getDouble(Object obj) throws IllegalArgumentException { public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException { + ensureObj(obj); if (isReadOnly()) { - ensureObj(obj); // throw NPE if obj is null on instance field throwFinalFieldIllegalAccessException(value); } @@ -195,7 +195,8 @@ public void setDouble(Object obj, double d) } catch (IllegalArgumentException|NullPointerException e) { throw e; } catch (ClassCastException e) { - throw newSetIllegalArgumentException(obj.getClass()); + // receiver is of invalid type + throw newSetIllegalArgumentException(obj); } catch (Throwable e) { throw new InternalError(e); } diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleFieldAccessorImpl.java index ab59b61d1a207..f979d38f9ab73 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleFieldAccessorImpl.java +++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleFieldAccessorImpl.java @@ -27,6 +27,7 @@ import java.lang.invoke.MethodHandle; import java.lang.reflect.Field; +import java.lang.reflect.Modifier; abstract class MethodHandleFieldAccessorImpl extends FieldAccessorImpl { private static final int IS_READ_ONLY_BIT = 0x0001; @@ -64,21 +65,32 @@ protected final void ensureObj(Object o) { } } + private String getMessage(boolean getter, Class type) { + String err = "Can not " + (getter ? "get" : "set"); + if (Modifier.isStatic(field.getModifiers())) + err += " static"; + if (Modifier.isFinal(field.getModifiers())) + err += " final"; + err += " " + field.getType().getName() + " field " + getQualifiedFieldName(); + if (type != null) { + err += " on " + type.getName(); + } + return err; + } + /** * IllegalArgumentException because Field::get on the specified object, which * is not an instance of the class or interface declaring the underlying method */ - protected IllegalArgumentException newGetIllegalArgumentException(Class type) { - return new IllegalArgumentException(getMessage(true, type.getName())); + protected IllegalArgumentException newGetIllegalArgumentException(Object o) { + return new IllegalArgumentException(getMessage(true, o != null ? o.getClass() : null)); } /** * IllegalArgumentException because Field::set on the specified object, which * is not an instance of the class or interface declaring the underlying method */ - protected IllegalArgumentException newSetIllegalArgumentException(Class type) { - return new IllegalArgumentException(getMessage(false, type.getName())); + protected IllegalArgumentException newSetIllegalArgumentException(Object o) { + return new IllegalArgumentException(getMessage(false, o != null ? o.getClass() : null)); } - - } diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleFloatFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleFloatFieldAccessorImpl.java index ee1850f33a841..5ac00ec5ea8e8 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleFloatFieldAccessorImpl.java +++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleFloatFieldAccessorImpl.java @@ -89,7 +89,7 @@ public float getFloat(Object obj) throws IllegalArgumentException { } catch (IllegalArgumentException|NullPointerException e) { throw e; } catch (ClassCastException e) { - throw newGetIllegalArgumentException(obj.getClass()); + throw newGetIllegalArgumentException(obj); } catch (Throwable e) { throw new InternalError(e); } @@ -102,8 +102,8 @@ public double getDouble(Object obj) throws IllegalArgumentException { public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException { + ensureObj(obj); if (isReadOnly()) { - ensureObj(obj); // throw NPE if obj is null on instance field throwFinalFieldIllegalAccessException(value); } @@ -186,7 +186,8 @@ public void setFloat(Object obj, float f) } catch (IllegalArgumentException|NullPointerException e) { throw e; } catch (ClassCastException e) { - throw newSetIllegalArgumentException(obj.getClass()); + // receiver is of invalid type + throw newSetIllegalArgumentException(obj); } catch (Throwable e) { throw new InternalError(e); } diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleIntegerFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleIntegerFieldAccessorImpl.java index 19a17cc1ca38b..62e3ab083dbdc 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleIntegerFieldAccessorImpl.java +++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleIntegerFieldAccessorImpl.java @@ -81,7 +81,7 @@ public int getInt(Object obj) throws IllegalArgumentException { } catch (IllegalArgumentException|NullPointerException e) { throw e; } catch (ClassCastException e) { - throw newGetIllegalArgumentException(obj.getClass()); + throw newGetIllegalArgumentException(obj); } catch (Throwable e) { throw new InternalError(e); } @@ -102,8 +102,8 @@ public double getDouble(Object obj) throws IllegalArgumentException { public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException { + ensureObj(obj); if (isReadOnly()) { - ensureObj(obj); // throw NPE if obj is null on instance field throwFinalFieldIllegalAccessException(value); } @@ -168,7 +168,8 @@ public void setInt(Object obj, int i) } catch (IllegalArgumentException|NullPointerException e) { throw e; } catch (ClassCastException e) { - throw newSetIllegalArgumentException(obj.getClass()); + // receiver is of invalid type + throw newSetIllegalArgumentException(obj); } catch (Throwable e) { throw new InternalError(e); } diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleLongFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleLongFieldAccessorImpl.java index 42ef4c75c0505..a0e02204b3101 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleLongFieldAccessorImpl.java +++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleLongFieldAccessorImpl.java @@ -85,7 +85,7 @@ public long getLong(Object obj) throws IllegalArgumentException { } catch (IllegalArgumentException|NullPointerException e) { throw e; } catch (ClassCastException e) { - throw newGetIllegalArgumentException(obj.getClass()); + throw newGetIllegalArgumentException(obj); } catch (Throwable e) { throw new InternalError(e); } @@ -102,8 +102,8 @@ public double getDouble(Object obj) throws IllegalArgumentException { public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException { + ensureObj(obj); if (isReadOnly()) { - ensureObj(obj); // throw NPE if obj is null on instance field throwFinalFieldIllegalAccessException(value); } @@ -177,7 +177,8 @@ public void setLong(Object obj, long l) } catch (IllegalArgumentException|NullPointerException e) { throw e; } catch (ClassCastException e) { - throw newSetIllegalArgumentException(obj.getClass()); + // receiver is of invalid type + throw newSetIllegalArgumentException(obj); } catch (Throwable e) { throw new InternalError(e); } diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleObjectFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleObjectFieldAccessorImpl.java index 57da6081c84dc..722d73d22a6ad 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleObjectFieldAccessorImpl.java +++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleObjectFieldAccessorImpl.java @@ -58,7 +58,7 @@ public Object get(Object obj) throws IllegalArgumentException { } catch (IllegalArgumentException|NullPointerException e) { throw e; } catch (ClassCastException e) { - throw newGetIllegalArgumentException(obj.getClass()); + throw newGetIllegalArgumentException(obj); } catch (Throwable e) { throw new InternalError(e); } @@ -98,8 +98,8 @@ public double getDouble(Object obj) throws IllegalArgumentException { @Override public void set(Object obj, Object value) throws IllegalAccessException { + ensureObj(obj); if (isReadOnly()) { - ensureObj(obj); // throw NPE if obj is null on instance field throwFinalFieldIllegalAccessException(value); } try { @@ -111,7 +111,8 @@ public void set(Object obj, Object value) throws IllegalAccessException { } catch (IllegalArgumentException|NullPointerException e) { throw e; } catch (ClassCastException e) { - throw newSetIllegalArgumentException(obj.getClass()); + // already ensure the receiver type. So this CCE is due to the value. + throwSetIllegalArgumentException(value); } catch (Throwable e) { throw new InternalError(e); } diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleShortFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleShortFieldAccessorImpl.java index d9ac93d727621..265c555421a3e 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleShortFieldAccessorImpl.java +++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleShortFieldAccessorImpl.java @@ -77,7 +77,7 @@ public short getShort(Object obj) throws IllegalArgumentException { } catch (IllegalArgumentException|NullPointerException e) { throw e; } catch (ClassCastException e) { - throw newGetIllegalArgumentException(obj.getClass()); + throw newGetIllegalArgumentException(obj); } catch (Throwable e) { throw new InternalError(e); } @@ -102,8 +102,8 @@ public double getDouble(Object obj) throws IllegalArgumentException { public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException { + ensureObj(obj); if (isReadOnly()) { - ensureObj(obj); // throw NPE if obj is null on instance field throwFinalFieldIllegalAccessException(value); } @@ -156,7 +156,8 @@ public void setShort(Object obj, short s) } catch (IllegalArgumentException|NullPointerException e) { throw e; } catch (ClassCastException e) { - throw newSetIllegalArgumentException(obj.getClass()); + // receiver is of invalid type + throw newSetIllegalArgumentException(obj); } catch (Throwable e) { throw new InternalError(e); } 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 ce9655f7a7835..76a4bf8fa4a64 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/Reflection.java +++ b/src/java.base/share/classes/jdk/internal/reflect/Reflection.java @@ -32,6 +32,7 @@ import java.util.Set; import jdk.internal.access.SharedSecrets; import jdk.internal.misc.VM; +import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.IntrinsicCandidate; /** Common utility routines used by both java.lang and @@ -106,6 +107,7 @@ public static void ensureMemberAccess(Class currentClass, } } + @ForceInline public static void ensureNativeAccess(Class currentClass) { Module module = currentClass.getModule(); if (!SharedSecrets.getJavaLangAccess().isEnableNativeAccess(module)) { diff --git a/src/java.base/share/classes/sun/nio/ch/IOUtil.java b/src/java.base/share/classes/sun/nio/ch/IOUtil.java index 900bb4e40883d..bea092befaeff 100644 --- a/src/java.base/share/classes/sun/nio/ch/IOUtil.java +++ b/src/java.base/share/classes/sun/nio/ch/IOUtil.java @@ -475,15 +475,15 @@ static long read(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length, private static final JavaNioAccess NIO_ACCESS = SharedSecrets.getJavaNioAccess(); - static Scope.Handle acquireScope(ByteBuffer bb, boolean async) { + static Runnable acquireScope(ByteBuffer bb, boolean async) { return NIO_ACCESS.acquireScope(bb, async); } - private static void releaseScope(Scope.Handle handle) { + private static void releaseScope(Runnable handle) { if (handle == null) return; try { - handle.scope().release(handle); + handle.run(); } catch (Exception e) { throw new IllegalStateException(e); } @@ -535,11 +535,11 @@ static LinkedRunnable of(Runnable first, Runnable second) { } } - static record Releaser(Scope.Handle handle) implements Runnable { + static record Releaser(Runnable handle) implements Runnable { Releaser { Objects.requireNonNull(handle) ; } @Override public void run() { releaseScope(handle); } - static Runnable of(Scope.Handle handle) { return new Releaser(handle); } - static Runnable ofNullable(Scope.Handle handle) { + static Runnable of(Runnable handle) { return new Releaser(handle); } + static Runnable ofNullable(Runnable handle) { if (handle == null) return () -> { }; return new Releaser(handle); diff --git a/src/java.compiler/share/classes/javax/lang/model/util/Elements.java b/src/java.compiler/share/classes/javax/lang/model/util/Elements.java index 5f81e6620d7a8..03dec84feb195 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/Elements.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/Elements.java @@ -68,8 +68,8 @@ public interface Elements { * * * - * If this process leads to a list with a single element, - * the single element is returned, otherwise null is returned. + * If this process leads to a list with a single element, the + * single element is returned, otherwise {@code null} is returned. * * @param name fully qualified package name, * or an empty string for an unnamed package @@ -155,8 +155,8 @@ default Set getAllPackageElements(CharSequence name) { * * * - * If this process leads to a list with a single element, - * the single element is returned, otherwise null is returned. + * If this process leads to a list with a single element, the + * single element is returned, otherwise {@code null} is returned. * * @param name the canonical name * @return the named type element, @@ -735,8 +735,9 @@ default boolean isAutomaticModule(ModuleElement module) { } /** - * Returns the record component for the given accessor. Returns null if the - * given method is not a record component accessor. + * Returns the record component for the given accessor. Returns + * {@code null} if the given method is not a record component + * accessor. * * @implSpec The default implementation of this method checks if the element * enclosing the accessor has kind {@link ElementKind#RECORD RECORD} if that is @@ -747,8 +748,8 @@ default boolean isAutomaticModule(ModuleElement module) { * record component is returned, in any other case {@code null} is returned. * * @param accessor the method for which the record component should be found. - * @return the record component, or null if the given method is not a record - * component accessor + * @return the record component, or {@code null} if the given + * method is not a record component accessor * @since 16 */ default RecordComponentElement recordComponentFor(ExecutableElement accessor) { diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m index 564355f82f676..201fe5c359124 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m @@ -122,7 +122,7 @@ + (void) initializeRolesMap { /* * Here we should keep all the mapping between the accessibility roles and implementing classes */ - rolesMap = [[NSMutableDictionary alloc] initWithCapacity:50]; + rolesMap = [[NSMutableDictionary alloc] initWithCapacity:51]; [rolesMap setObject:@"ButtonAccessibility" forKey:@"pushbutton"]; [rolesMap setObject:@"ImageAccessibility" forKey:@"icon"]; @@ -156,6 +156,7 @@ + (void) initializeRolesMap { [rolesMap setObject:@"MenuAccessibility" forKey:@"menu"]; [rolesMap setObject:@"MenuItemAccessibility" forKey:@"menuitem"]; [rolesMap setObject:@"MenuAccessibility" forKey:@"popupmenu"]; + [rolesMap setObject:@"ProgressIndicatorAccessibility" forKey:@"progressbar"]; /* * All the components below should be ignored by the accessibility subsystem, diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ProgressIndicatorAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ProgressIndicatorAccessibility.h new file mode 100644 index 0000000000000..e5b58ea3f8b6b --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ProgressIndicatorAccessibility.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "JavaComponentAccessibility.h" +#import "GroupAccessibility.h" + +#import + +@interface ProgressIndicatorAccessibility : GroupAccessibility { + +}; +- (NSAccessibilityRole _Nonnull)accessibilityRole; +- (NSString * _Nullable)accessibilityValue; +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ProgressIndicatorAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ProgressIndicatorAccessibility.m new file mode 100644 index 0000000000000..13ba383f166df --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ProgressIndicatorAccessibility.m @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "ProgressIndicatorAccessibility.h" + +/* + * Implementation of the accessibility peer for the NSProgressIndicator role. + * Main usage is JProgressBar + */ +@implementation ProgressIndicatorAccessibility + +- (NSAccessibilityRole _Nonnull)accessibilityRole +{ + return NSAccessibilityProgressIndicatorRole; +} + +- (NSString * _Nullable)accessibilityValue +{ + return [super accessibilityValue]; +} + +@end diff --git a/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java b/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java index e746dad6e7ee0..5b8cf120e1389 100644 --- a/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java +++ b/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java @@ -520,7 +520,7 @@ public static void drawString(JComponent c, Graphics g, String text, * it to fit in the screen width. This distributes the spacing * more evenly than directly laying out to the screen advances. */ - String trimmedText = trimTrailingSpaces(text); + String trimmedText = text.stripTrailing(); if (!trimmedText.isEmpty()) { float screenWidth = (float) g2d.getFont().getStringBounds (trimmedText, getFontRenderContext(c)).getWidth(); @@ -866,7 +866,7 @@ public static float drawChars(JComponent c, Graphics g, String text = new String(data, offset, length); TextLayout layout = new TextLayout(text, g2d.getFont(), deviceFontRenderContext); - String trimmedText = trimTrailingSpaces(text); + String trimmedText = text.stripTrailing(); if (!trimmedText.isEmpty()) { float screenWidth = (float)g2d.getFont(). getStringBounds(trimmedText, frc).getWidth(); @@ -1321,14 +1321,6 @@ static boolean isPrinting(Graphics g) { return (g instanceof PrinterGraphics || g instanceof PrintGraphics); } - private static String trimTrailingSpaces(String s) { - int i = s.length() - 1; - while(i >= 0 && Character.isWhitespace(s.charAt(i))) { - i--; - } - return s.substring(0, i + 1); - } - private static AttributedCharacterIterator getTrimmedTrailingSpacesIterator (AttributedCharacterIterator iterator) { int curIdx = iterator.getIndex(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 32975a193ec2f..4b22fb1b17fda 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -1680,7 +1680,8 @@ private void handleSwitch(JCTree switchTree, // Attribute all cases and // check that there are no duplicate case labels or default clauses. Set labels = new HashSet<>(); // The set of case labels. - List coveredTypes = List.nil(); + List coveredTypesForPatterns = List.nil(); + List coveredTypesForConstants = List.nil(); boolean hasDefault = false; // Is there a default label? boolean hasTotalPattern = false; // Is there a total pattern? boolean hasNullPattern = false; // Is there a null pattern? @@ -1718,7 +1719,7 @@ private void handleSwitch(JCTree switchTree, } else if (!labels.add(sym)) { log.error(pat.pos(), Errors.DuplicateCaseLabel); } else { - checkCaseLabelDominated(pat.pos(), coveredTypes, sym.type); + checkCaseLabelDominated(pat.pos(), coveredTypesForConstants, sym.type); } } else if (errorEnumSwitch) { //error recovery: the selector is erroneous, and all the case labels @@ -1751,7 +1752,7 @@ private void handleSwitch(JCTree switchTree, } else if (!labels.add(pattype.constValue())) { log.error(c.pos(), Errors.DuplicateCaseLabel); } else { - checkCaseLabelDominated(pat.pos(), coveredTypes, types.boxedTypeOrType(pattype)); + checkCaseLabelDominated(pat.pos(), coveredTypesForConstants, types.boxedTypeOrType(pattype)); } } } @@ -1784,9 +1785,12 @@ private void handleSwitch(JCTree switchTree, } hasTotalPattern = true; } - checkCaseLabelDominated(pat.pos(), coveredTypes, patternType); - if (primary.unconditional() && !patternType.isErroneous()) { - coveredTypes = coveredTypes.prepend(patternType); + checkCaseLabelDominated(pat.pos(), coveredTypesForPatterns, patternType); + if (!patternType.isErroneous()) { + coveredTypesForConstants = coveredTypesForConstants.prepend(patternType); + if (primary.unconditional()) { + coveredTypesForPatterns = coveredTypesForPatterns.prepend(patternType); + } } } currentBindings = matchBindingsComputer.switchCase(pat, currentBindings, matchBindings); @@ -3950,14 +3954,14 @@ public void visitUnary(JCUnary tree) { : chk.checkNonVoid(tree.arg.pos(), attribExpr(tree.arg, env)); // Find operator. - Symbol operator = tree.operator = operators.resolveUnary(tree, tree.getTag(), argtype); + OperatorSymbol operator = tree.operator = operators.resolveUnary(tree, tree.getTag(), argtype); Type owntype = types.createErrorType(tree.type); if (operator != operators.noOpSymbol && !argtype.isErroneous()) { owntype = (tree.getTag().isIncOrDecUnaryOp()) ? tree.arg.type : operator.type.getReturnType(); - int opc = ((OperatorSymbol)operator).opcode; + int opc = operator.opcode; // If the argument is constant, fold it. if (argtype.constValue() != null) { @@ -4004,13 +4008,13 @@ public void visitBinary(JCBinary tree) { matchBindings = matchBindingsComputer.binary(tree, lhsBindings, matchBindings); // Find operator. - Symbol operator = tree.operator = operators.resolveBinary(tree, tree.getTag(), left, right); + OperatorSymbol operator = tree.operator = operators.resolveBinary(tree, tree.getTag(), left, right); Type owntype = types.createErrorType(tree.type); if (operator != operators.noOpSymbol && !left.isErroneous() && !right.isErroneous()) { owntype = operator.type.getReturnType(); - int opc = ((OperatorSymbol)operator).opcode; + int opc = operator.opcode; // If both arguments are constants, fold them. if (left.constValue() != null && right.constValue() != null) { Type ctype = cfolder.fold2(opc, left, right); @@ -5494,11 +5498,11 @@ private void attribClassBody(Env env, ClassSymbol c) { c.owner.kind != PCK && ((c.flags() & STATIC) == 0 || c.name == names.empty) && (TreeInfo.flags(l.head) & (STATIC | INTERFACE)) != 0) { - Symbol sym = null; + VarSymbol sym = null; if (l.head.hasTag(VARDEF)) sym = ((JCVariableDecl) l.head).sym; if (sym == null || sym.kind != VAR || - ((VarSymbol) sym).getConstValue() == null) + sym.getConstValue() == null) log.error(l.head.pos(), Errors.IclsCantHaveStaticDecl(c)); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index f4ba0f9bb1735..eaff2f1106d8b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -751,7 +751,7 @@ private void handleConstantCaseLabel(Set constants, JCCaseLabel pat) { } } - private void transitiveCovers(Set covered) { + private void transitiveCovers(Type seltype, Set covered) { List todo = List.from(covered); while (todo.nonEmpty()) { Symbol sym = todo.head; @@ -773,7 +773,7 @@ private void transitiveCovers(Set covered) { case TYP -> { for (Type sup : types.directSupertypes(sym.type)) { if (sup.tsym.kind == TYP) { - if (isTransitivelyCovered(sup.tsym, covered) && + if (isTransitivelyCovered(seltype, sup.tsym, covered) && covered.add(sup.tsym)) { todo = todo.prepend(sup.tsym); } @@ -784,7 +784,7 @@ private void transitiveCovers(Set covered) { } } - private boolean isTransitivelyCovered(Symbol sealed, Set covered) { + private boolean isTransitivelyCovered(Type seltype, Symbol sealed, Set covered) { DeferredCompletionFailureHandler.Handler prevHandler = dcfh.setHandler(dcfh.speculativeCodeHandler); try { @@ -793,7 +793,10 @@ private boolean isTransitivelyCovered(Symbol sealed, Set covered) { if (sealed.kind == TYP && sealed.isAbstract() && sealed.isSealed()) { return ((ClassSymbol) sealed).permitted .stream() - .allMatch(s -> isTransitivelyCovered(s, covered)); + .filter(s -> { + return types.isCastable(seltype, s.type/*, types.noWarnings*/); + }) + .allMatch(s -> isTransitivelyCovered(seltype, s, covered)); } return false; } catch (CompletionFailure cf) { @@ -805,7 +808,7 @@ private boolean isTransitivelyCovered(Symbol sealed, Set covered) { } private boolean isExhaustive(Type seltype, Set covered) { - transitiveCovers(covered); + transitiveCovers(seltype, covered); return switch (seltype.getTag()) { case CLASS -> { if (seltype.isCompound()) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java index c48ac804ac850..55f79c5bc1c56 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java @@ -424,14 +424,14 @@ public void visitLambda(JCLambda tree) { //add captured locals for (Symbol fv : localContext.getSymbolMap(CAPTURED_VAR).keySet()) { if (fv != localContext.self) { - JCTree captured_local = make.Ident(fv).setType(fv.type); - syntheticInits.append((JCExpression) captured_local); + JCExpression captured_local = make.Ident(fv).setType(fv.type); + syntheticInits.append(captured_local); } } // add captured outer this instances (used only when `this' capture itself is illegal) for (Symbol fv : localContext.getSymbolMap(CAPTURED_OUTER_THIS).keySet()) { - JCTree captured_local = make.QualThis(fv.type); - syntheticInits.append((JCExpression) captured_local); + JCExpression captured_local = make.QualThis(fv.type); + syntheticInits.append(captured_local); } //then, determine the arguments to the indy call @@ -1184,13 +1184,13 @@ private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmNam syms.stringType, syms.methodTypeType).appendList(staticArgs.map(types::constantType)); - Symbol bsm = rs.resolveInternalMethod(pos, attrEnv, site, + MethodSymbol bsm = rs.resolveInternalMethod(pos, attrEnv, site, bsmName, bsm_staticArgs, List.nil()); DynamicMethodSymbol dynSym = new DynamicMethodSymbol(methName, syms.noSymbol, - ((MethodSymbol)bsm).asHandle(), + bsm.asHandle(), indyType, staticArgs.toArray(new LoadableConstant[staticArgs.length()])); JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index dd9f550771852..374e18fb0a510 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -98,6 +98,7 @@ public static Lower instance(Context context) { private final boolean debugLower; private final boolean disableProtectedAccessors; // experimental private final PkgInfo pkginfoOpt; + private final boolean optimizeOuterThis; protected Lower(Context context) { context.put(lowerKey, this); @@ -119,6 +120,9 @@ protected Lower(Context context) { Options options = Options.instance(context); debugLower = options.isSet("debuglower"); pkginfoOpt = PkgInfo.get(options); + optimizeOuterThis = + target.optimizeOuterThis() || + options.getBoolean("optimizeOuterThis", false); disableProtectedAccessors = options.isSet("disableProtectedAccessors"); } @@ -1480,8 +1484,12 @@ Name outerThisName(Type type, Symbol owner) { private VarSymbol makeOuterThisVarSymbol(Symbol owner, long flags) { Type target = types.erasure(owner.enclClass().type.getEnclosingType()); + // Set NOOUTERTHIS for all synthetic outer instance variables, and unset + // it when the variable is accessed. If the variable is never accessed, + // we skip creating an outer instance field and saving the constructor + // parameter to it. VarSymbol outerThis = - new VarSymbol(flags, outerThisName(target, owner), target, owner); + new VarSymbol(flags | NOOUTERTHIS, outerThisName(target, owner), target, owner); outerThisStack = outerThisStack.prepend(outerThis); return outerThis; } @@ -1728,6 +1736,7 @@ JCExpression makeOuterThis(DiagnosticPosition pos, TypeSymbol c) { } VarSymbol ot = ots.head; JCExpression tree = access(make.at(pos).Ident(ot)); + ot.flags_field &= ~NOOUTERTHIS; TypeSymbol otc = ot.type.tsym; while (otc != c) { do { @@ -1745,6 +1754,7 @@ JCExpression makeOuterThis(DiagnosticPosition pos, TypeSymbol c) { return makeNull(); } tree = access(make.at(pos).Select(tree, ot)); + ot.flags_field &= ~NOOUTERTHIS; otc = ot.type.tsym; } return tree; @@ -1784,6 +1794,7 @@ JCExpression makeOwnerThisN(DiagnosticPosition pos, Symbol sym, boolean preciseM } VarSymbol ot = ots.head; JCExpression tree = access(make.at(pos).Ident(ot)); + ot.flags_field &= ~NOOUTERTHIS; TypeSymbol otc = ot.type.tsym; while (!(preciseMatch ? sym.isMemberOf(otc, types) : otc.isSubClass(sym.owner, types))) { do { @@ -1796,6 +1807,7 @@ JCExpression makeOwnerThisN(DiagnosticPosition pos, Symbol sym, boolean preciseM ot = ots.head; } while (ot.owner != otc); tree = access(make.at(pos).Select(tree, ot)); + ot.flags_field &= ~NOOUTERTHIS; otc = ot.type.tsym; } return tree; @@ -1817,10 +1829,9 @@ JCStatement initField(int pos, Symbol rhs, Symbol lhs) { /** Return tree simulating the assignment {@code this.this$n = this$n}. */ - JCStatement initOuterThis(int pos) { - VarSymbol rhs = outerThisStack.head; + JCStatement initOuterThis(int pos, VarSymbol rhs) { Assert.check(rhs.owner.kind == MTH); - VarSymbol lhs = outerThisStack.tail.head; + VarSymbol lhs = outerThisStack.head; Assert.check(rhs.owner.owner == lhs.owner); make.at(pos); return @@ -2224,15 +2235,25 @@ public void visitClassDef(JCClassDecl tree) { // Convert name to flat representation, replacing '.' by '$'. tree.name = Convert.shortName(currentClass.flatName()); - // Add this$n and free variables proxy definitions to class. + // Add free variables proxy definitions to class. for (List l = fvdefs; l.nonEmpty(); l = l.tail) { tree.defs = tree.defs.prepend(l.head); enterSynthetic(tree.pos(), l.head.sym, currentClass.members()); } - if (currentClass.hasOuterInstance()) { + // If this$n was accessed, add the field definition and + // update initial constructors to initialize it + if (currentClass.hasOuterInstance() && shouldEmitOuterThis(currentClass)) { tree.defs = tree.defs.prepend(otdef); enterSynthetic(tree.pos(), otdef.sym, currentClass.members()); + + for (JCTree def : tree.defs) { + if (TreeInfo.isInitialConstructor(def)) { + JCMethodDecl mdef = (JCMethodDecl) def; + mdef.body.stats = mdef.body.stats.prepend( + initOuterThis(mdef.body.pos, mdef.params.head.sym)); + } + } } proxies = prevProxies; @@ -2249,6 +2270,39 @@ public void visitClassDef(JCClassDecl tree) { result = make_at(tree.pos()).Block(SYNTHETIC, List.nil()); } + private boolean shouldEmitOuterThis(ClassSymbol sym) { + if (!optimizeOuterThis) { + // Optimization is disabled + return true; + } + if ((outerThisStack.head.flags_field & NOOUTERTHIS) == 0) { + // Enclosing instance field is used + return true; + } + if (rs.isSerializable(sym.type) && !hasSerialVersionUID(sym)) { + // Class is serializable and does not have a stable serialVersionUID + return true; + } + return false; + } + + private boolean hasSerialVersionUID(ClassSymbol sym) { + VarSymbol svuid = (VarSymbol) sym.members().findFirst(names.serialVersionUID, f -> f.kind == VAR); + if (svuid == null) { + return false; + } + if ((svuid.flags() & (STATIC | FINAL)) != (STATIC | FINAL)) { + return false; + } + if (!svuid.type.hasTag(LONG)) { + return false; + } + if (svuid.getConstValue() == null) { + return false; + } + return true; + } + List generateMandatedAccessors(JCClassDecl tree) { List fields = TreeInfo.recordFields(tree); return tree.sym.getRecordComponents().stream() @@ -2315,7 +2369,7 @@ private void visitEnumDef(JCClassDecl tree) { enumDefs.append(make.VarDef(valuesVar, make.App(make.QualIdent(valuesMethod)))); tree.sym.members().enter(valuesVar); - Symbol valuesSym = lookupMethod(tree.pos(), names.values, + MethodSymbol valuesSym = lookupMethod(tree.pos(), names.values, tree.type, List.nil()); List valuesBody; if (useClone()) { @@ -2366,7 +2420,7 @@ private void visitEnumDef(JCClassDecl tree) { } JCMethodDecl valuesDef = - make.MethodDef((MethodSymbol)valuesSym, make.Block(0, valuesBody)); + make.MethodDef(valuesSym, make.Block(0, valuesBody)); enumDefs.append(valuesDef); @@ -2581,7 +2635,7 @@ JCFieldAccess makeIndyQualifier( Name bootstrapName, Name argName, boolean isStatic) { - Symbol bsm = rs.resolveInternalMethod(tree.pos(), attrEnv, site, + MethodSymbol bsm = rs.resolveInternalMethod(tree.pos(), attrEnv, site, bootstrapName, staticArgTypes, List.nil()); MethodType indyType = msym.type.asMethodType(); @@ -2593,7 +2647,7 @@ JCFieldAccess makeIndyQualifier( ); DynamicMethodSymbol dynSym = new DynamicMethodSymbol(argName, syms.noSymbol, - ((MethodSymbol)bsm).asHandle(), + bsm.asHandle(), indyType, staticArgValues); JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), argName); @@ -2703,11 +2757,6 @@ private void visitMethodDefInternal(JCMethodDecl tree) { olderasure.getThrownTypes(), syms.methodClass); } - if (currentClass.hasOuterInstance() && - TreeInfo.isInitialConstructor(tree)) - { - added = added.prepend(initOuterThis(tree.body.pos)); - } // pop local variables from proxy stack proxies = prevProxies; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java index fda59d6ddaadf..f3f304659edca 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java @@ -371,7 +371,7 @@ private void handleSwitch(JCTree tree, boolean enumSelector = seltype.tsym.isEnum(); Name bootstrapName = enumSelector ? names.enumSwitch : names.typeSwitch; - Symbol bsm = rs.resolveInternalMethod(tree.pos(), env, syms.switchBootstrapsType, + MethodSymbol bsm = rs.resolveInternalMethod(tree.pos(), env, syms.switchBootstrapsType, bootstrapName, staticArgTypes, List.nil()); MethodType indyType = new MethodType( @@ -382,7 +382,7 @@ private void handleSwitch(JCTree tree, ); DynamicMethodSymbol dynSym = new DynamicMethodSymbol(bootstrapName, syms.noSymbol, - ((MethodSymbol)bsm).asHandle(), + bsm.asHandle(), indyType, staticArgValues); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/StringConcat.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/StringConcat.java index 2c17b6880e889..0dddb3e5868e4 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/StringConcat.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/StringConcat.java @@ -371,7 +371,7 @@ private void doCall(Type type, JCDiagnostic.DiagnosticPosition pos, List d syms.stringType, syms.methodTypeType); - Symbol bsm = rs.resolveInternalMethod(pos, + MethodSymbol bsm = rs.resolveInternalMethod(pos, gen.getAttrEnv(), syms.stringConcatFactory, names.makeConcat, @@ -380,7 +380,7 @@ private void doCall(Type type, JCDiagnostic.DiagnosticPosition pos, List d Symbol.DynamicMethodSymbol dynSym = new Symbol.DynamicMethodSymbol(names.makeConcat, syms.noSymbol, - ((MethodSymbol)bsm).asHandle(), + bsm.asHandle(), indyType, List.nil().toArray(new LoadableConstant[0])); @@ -487,7 +487,7 @@ private void doCall(Type type, JCDiagnostic.DiagnosticPosition pos, String recip .append(syms.stringType) .appendList(constTypes); - Symbol bsm = rs.resolveInternalMethod(pos, + MethodSymbol bsm = rs.resolveInternalMethod(pos, gen.getAttrEnv(), syms.stringConcatFactory, names.makeConcatWithConstants, @@ -496,7 +496,7 @@ private void doCall(Type type, JCDiagnostic.DiagnosticPosition pos, String recip Symbol.DynamicMethodSymbol dynSym = new Symbol.DynamicMethodSymbol(names.makeConcatWithConstants, syms.noSymbol, - ((MethodSymbol)bsm).asHandle(), + bsm.asHandle(), indyType, List.of(LoadableConstant.String(recipe)) .appendList(constants).toArray(new LoadableConstant[constants.size()])); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java index 27400d12580bb..fba0c2584c9ab 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java @@ -203,4 +203,11 @@ public boolean hasSealedClasses() { public boolean obsoleteAccStrict() { return compareTo(JDK1_17) >= 0; } + + /** Omit unused enclosing instance fields from inner classes that don't access enclosing + * instance state. + */ + public boolean optimizeOuterThis() { + return compareTo(JDK1_18) >= 0; + } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/Main.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/Main.java index 428b3a4a51ec0..76de3372d038c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/Main.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/Main.java @@ -354,6 +354,7 @@ private List getJavacOpts(String... runtimeArgs) throws Fault { javacOpts.add("-Xdiags:verbose"); javacOpts.add("-Xlint:deprecation"); javacOpts.add("-Xlint:unchecked"); + javacOpts.add("-Xlint:-options"); return javacOpts; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java index 033539c2c16b1..d5f1efa566b4a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java @@ -510,6 +510,11 @@ public void process(OptionHelper helper, String option) throws InvalidValueExcep public void process(OptionHelper helper, String option) { throw new AssertionError("the -J flag should be caught by the launcher."); } + + @Override + public void process(OptionHelper helper, String option, String arg) throws InvalidValueException { + throw helper.newInvalidValueException(Errors.InvalidFlag(option + arg)); + } }, MOREINFO("-moreinfo", null, HIDDEN, BASIC) { diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/CommandProcessor.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/CommandProcessor.java index 57098643e6a04..d02909aaea3f8 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/CommandProcessor.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/CommandProcessor.java @@ -52,7 +52,6 @@ import sun.jvm.hotspot.debugger.OopHandle; import sun.jvm.hotspot.classfile.ClassLoaderDataGraph; import sun.jvm.hotspot.memory.FileMapInfo; -import sun.jvm.hotspot.memory.SystemDictionary; import sun.jvm.hotspot.memory.Universe; import sun.jvm.hotspot.gc.shared.CollectedHeap; import sun.jvm.hotspot.gc.g1.G1CollectedHeap; @@ -94,11 +93,9 @@ import sun.jvm.hotspot.ui.tree.SimpleTreeNode; import sun.jvm.hotspot.utilities.AddressOps; import sun.jvm.hotspot.utilities.Assert; -import sun.jvm.hotspot.utilities.CompactHashTable; import sun.jvm.hotspot.utilities.HeapProgressThunk; import sun.jvm.hotspot.utilities.LivenessPathElement; import sun.jvm.hotspot.utilities.MethodArray; -import sun.jvm.hotspot.utilities.ObjectReader; import sun.jvm.hotspot.utilities.PointerFinder; import sun.jvm.hotspot.utilities.PointerLocation; import sun.jvm.hotspot.utilities.ReversePtrs; @@ -132,7 +129,7 @@ public boolean canInclude(InstanceKlass kls) { if (kls.getClassLoader() == null) return false; if (emitted.get(kls.getName()) != null) { // Since multiple class loaders are being shoved - // together duplicate classes are a possibilty. For + // together duplicate classes are a possibility. For // now just ignore them. return false; } @@ -436,7 +433,6 @@ public void doit(Tokens t) { Matcher m2 = args2.matcher(arg); Address start = null; Address end = null; - String format = ""; int formatSize = (int)VM.getVM().getAddressSize(); if (m1.matches()) { diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HSDB.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HSDB.java index 973fd6c3d0bc6..c530cb67b3593 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HSDB.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HSDB.java @@ -40,7 +40,6 @@ import sun.jvm.hotspot.gc.g1.*; import sun.jvm.hotspot.gc.z.*; import sun.jvm.hotspot.interpreter.*; -import sun.jvm.hotspot.memory.*; import sun.jvm.hotspot.oops.*; import sun.jvm.hotspot.runtime.*; import sun.jvm.hotspot.ui.*; @@ -1737,7 +1736,6 @@ private void doHeapIteration(String frameTitle, String progressBarText, HeapVisitor visitor, CleanupThunk cleanup) { - sun.jvm.hotspot.oops.ObjectHistogram histo = new sun.jvm.hotspot.oops.ObjectHistogram(); HeapProgress progress = new HeapProgress(frameTitle, progressBarText, cleanup); diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HotSpotTypeDataBase.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HotSpotTypeDataBase.java index c4c5f43a76c48..573e1968cb47b 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HotSpotTypeDataBase.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HotSpotTypeDataBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -426,7 +426,6 @@ private void readVMStructs() { boolean isStatic = false; long offset = 0; Address staticFieldAddr = null; - long size = 0; long index = 0; String opaqueName = ""; lookupOrCreateClass(opaqueName, false, false, false); diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/NMethod.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/NMethod.java index 83b5535ea11bb..acbd3f90974fd 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/NMethod.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/NMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -350,7 +350,6 @@ PCDesc find_pc_desc_internal(long pc, boolean approximate) { // Take giant steps at first (4096, then 256, then 16, then 1) int LOG2_RADIX = 4; - int RADIX = (1 << LOG2_RADIX); Address mid; for (int step = (1 << (LOG2_RADIX*3)); step > 1; step >>= LOG2_RADIX) { while ((mid = lower.addOffsetTo(step * pcDescSize)).lessThan(upper)) { diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/StubQueue.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/StubQueue.java index ba8501d1e255a..d59971b33ecf0 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/StubQueue.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/StubQueue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,6 @@ package sun.jvm.hotspot.code; -import java.util.*; import sun.jvm.hotspot.debugger.*; import sun.jvm.hotspot.runtime.*; import sun.jvm.hotspot.types.*; @@ -88,7 +87,6 @@ public boolean contains(Address pc) { public Stub getStubContaining(Address pc) { if (contains(pc)) { - int i = 0; for (Stub s = getFirst(); s != null; s = getNext(s)) { if (stubContains(s, pc)) { return s; diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgCDebugInfoBuilder.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgCDebugInfoBuilder.java index f70c8c6e0cce3..80b352183e4e2 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgCDebugInfoBuilder.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgCDebugInfoBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -417,7 +417,6 @@ CDebugInfoDataBase buildDataBase(String dllName, Address base) { // and UDT references to understand how to build the global // database vs. the module-by-module one) DebugVC50SubsectionDirectory dir = vc50.getSubsectionDirectory(); - int moduleNumber = 0; // Debugging for (int i = 0; i < dir.getNumEntries(); i++) { DebugVC50Subsection ss = dir.getSubsection(i); int ssType = ss.getSubsectionType(); diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/GenerateOopMap.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/GenerateOopMap.java index 403b454f0935c..f5b9a821400d8 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/GenerateOopMap.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/GenerateOopMap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -310,10 +310,10 @@ void markAsAlive() { int _max_stack; // Cached value of max. stack depth int _max_monitors; // Cached value of max. monitor stack depth boolean _has_exceptions; // True, if exceptions exist for method - boolean _got_error; // True, if an error occured during interpretation. + boolean _got_error; // True, if an error occurred during interpretation. String _error_msg; // Error message. Set if _got_error is true. // bool _did_rewriting; // was bytecodes rewritten - // bool _did_relocation; // was relocation neccessary + // bool _did_relocation; // was relocation necessary boolean _monitor_safe; // The monitors in this method have been determined // to be safe. @@ -529,7 +529,6 @@ void replaceAllCTSMatches (CellTypeState match, CellTypeState replace) { int i; int len = _max_locals + _stack_top; - boolean change = false; for (i = len - 1; i >= 0; i--) { if (match.equal(_state.get(i))) { @@ -1894,7 +1893,7 @@ int copyCTS (CellTypeState[] dst, CellTypeState[] // Create result set boolean _report_result; - boolean _report_result_for_send; // Unfortunatly, stackmaps for sends are special, so we need some extra + boolean _report_result_for_send; // Unfortunately, stackmaps for sends are special, so we need some extra BytecodeStream _itr_send; // variables to handle them properly. void reportResult () { @@ -1947,7 +1946,7 @@ void addToRefInitSet (int localNo) { } // Conflicts rewrite logic - boolean _conflict; // True, if a conflict occured during interpretation + boolean _conflict; // True, if a conflict occurred during interpretation int _nof_refval_conflicts; // No. of conflicts that require rewrites int[] _new_var_map; @@ -2131,7 +2130,7 @@ boolean jumpTargetsDo (BytecodeStream bcs, JumpClosure closu // Public routines for GenerateOopMap // public GenerateOopMap(Method method) { - // We have to initialize all variables here, that can be queried direcly + // We have to initialize all variables here, that can be queried directly _method = method; _max_locals=0; _init_vars = null; @@ -2204,7 +2203,7 @@ public void computeMap() { if (_got_error) { // We could expand this code to throw somekind of exception (e.g., VerifyException). However, - // an exception thrown in this part of the code is likly to mean that we are executing some + // an exception thrown in this part of the code is likely to mean that we are executing some // illegal bytecodes (that the verifier should have caught if turned on), so we will just exit // with a fatal. throw new RuntimeException("Illegal bytecode sequence encountered while generating interpreter pointer maps - method should be rejected by verifier."); diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java index a6823cf1160f3..118c515b6c65c 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java @@ -1281,8 +1281,6 @@ protected String locationAsString(Location loc) { if (w == Location.Where.ON_STACK) { buf.append("stack[" + loc.getStackOffset() + "]"); } else if (w == Location.Where.IN_REGISTER) { - boolean isFloat = (type == Location.Type.FLOAT_IN_DBL || - type == Location.Type.DBL); int regNum = loc.getRegisterNumber(); VMReg vmReg = new VMReg(regNum); buf.append(VMRegImpl.getRegisterName(vmReg.getValue())); diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/AbstractLayout.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/AbstractLayout.java index 81a20241a7ab1..f33e4cc2c4b96 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/AbstractLayout.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/AbstractLayout.java @@ -25,26 +25,23 @@ */ package jdk.incubator.foreign; +import jdk.internal.foreign.Utils; +import jdk.internal.vm.annotation.Stable; + import java.lang.constant.ClassDesc; -import java.lang.constant.Constable; import java.lang.constant.ConstantDesc; -import java.lang.constant.ConstantDescs; import java.lang.constant.DirectMethodHandleDesc; import java.lang.constant.DynamicConstantDesc; import java.lang.constant.MethodHandleDesc; import java.lang.constant.MethodTypeDesc; import java.nio.ByteOrder; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.OptionalLong; -import java.util.stream.Collectors; -import java.util.stream.Stream; import static java.lang.constant.ConstantDescs.BSM_GET_STATIC_FINAL; import static java.lang.constant.ConstantDescs.BSM_INVOKE; +import static java.lang.constant.ConstantDescs.CD_Class; import static java.lang.constant.ConstantDescs.CD_String; import static java.lang.constant.ConstantDescs.CD_long; @@ -52,50 +49,33 @@ abstract non-sealed class AbstractLayout implements MemoryLayout { private final OptionalLong size; final long alignment; - final Map attributes; + private final Optional name; + @Stable + long cachedSize; - public AbstractLayout(OptionalLong size, long alignment, Map attributes) { + public AbstractLayout(OptionalLong size, long alignment, Optional name) { this.size = size; this.alignment = alignment; - this.attributes = Collections.unmodifiableMap(attributes); + this.name = name; } @Override public AbstractLayout withName(String name) { Objects.requireNonNull(name); - return withAttribute(LAYOUT_NAME, name); + return dup(alignment, Optional.of(name)); } @Override public final Optional name() { - return attribute(LAYOUT_NAME).map(String.class::cast); - } - - @Override - public Optional attribute(String name) { - Objects.requireNonNull(name); - return Optional.ofNullable(attributes.get(name)); - } - - @Override - public Stream attributes() { - return attributes.keySet().stream(); - } - - @Override - public AbstractLayout withAttribute(String name, Constable value) { - Objects.requireNonNull(name); - Map newAttributes = new HashMap<>(attributes); - newAttributes.put(name, value); - return dup(alignment, newAttributes); + return name; } - abstract AbstractLayout dup(long alignment, Map annos); + abstract AbstractLayout dup(long alignment, Optional name); @Override public AbstractLayout withBitAlignment(long alignmentBits) { checkAlignment(alignmentBits); - return dup(alignmentBits, attributes); + return dup(alignmentBits, name); } void checkAlignment(long alignmentBitCount) { @@ -120,6 +100,15 @@ public final long bitAlignment() { return alignment; } + @Override + public long byteSize() { + if (cachedSize == 0) { + cachedSize = Utils.bitsToBytesOrThrow(bitSize(), + () -> new UnsupportedOperationException("Cannot compute byte size; bit size is not a multiple of 8")); + } + return cachedSize; + } + @Override public boolean hasSize() { return size.isPresent(); @@ -145,11 +134,6 @@ String decorateLayoutString(String s) { if (!hasNaturalAlignment()) { s = alignment + "%" + s; } - if (!attributes.isEmpty()) { - s += attributes.entrySet().stream() - .map(e -> e.getKey() + "=" + e.getValue()) - .collect(Collectors.joining(",", "[", "]")); - } return s; } @@ -158,9 +142,9 @@ DynamicConstantDesc decorateLayoutConstant(DynamicConstantDesc desc) { desc = DynamicConstantDesc.ofNamed(BSM_INVOKE, "withBitAlignment", desc.constantType(), MH_WITH_BIT_ALIGNMENT, desc, bitAlignment()); } - for (var e : attributes.entrySet()) { - desc = DynamicConstantDesc.ofNamed(BSM_INVOKE, "withAttribute", desc.constantType(), MH_WITH_ATTRIBUTE, - desc, e.getKey(), e.getValue().describeConstable().orElseThrow()); + if (name().isPresent()) { + desc = DynamicConstantDesc.ofNamed(BSM_INVOKE, "withName", desc.constantType(), MH_WITH_NAME, + desc, name().get().describeConstable().orElseThrow()); } return desc; @@ -177,7 +161,7 @@ public boolean isPadding() { @Override public int hashCode() { - return attributes.hashCode() << Long.hashCode(alignment); + return name.hashCode() << Long.hashCode(alignment); } @Override @@ -190,7 +174,7 @@ public boolean equals(Object other) { return false; } - return Objects.equals(attributes, ((AbstractLayout) other).attributes) && + return Objects.equals(name, ((AbstractLayout) other).name) && Objects.equals(alignment, ((AbstractLayout) other).alignment); } @@ -208,8 +192,6 @@ public boolean equals(Object other) { static final ClassDesc CD_FUNCTION_DESC = FunctionDescriptor.class.describeConstable().get(); - static final ClassDesc CD_Constable = Constable.class.describeConstable().get(); - static final ConstantDesc BIG_ENDIAN = DynamicConstantDesc.ofNamed(BSM_GET_STATIC_FINAL, "BIG_ENDIAN", CD_BYTEORDER, CD_BYTEORDER); static final ConstantDesc LITTLE_ENDIAN = DynamicConstantDesc.ofNamed(BSM_GET_STATIC_FINAL, "LITTLE_ENDIAN", CD_BYTEORDER, CD_BYTEORDER); @@ -217,9 +199,6 @@ public boolean equals(Object other) { static final MethodHandleDesc MH_PADDING = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "paddingLayout", MethodTypeDesc.of(CD_MEMORY_LAYOUT, CD_long)); - static final MethodHandleDesc MH_VALUE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "valueLayout", - MethodTypeDesc.of(CD_VALUE_LAYOUT, CD_long, CD_BYTEORDER)); - static final MethodHandleDesc MH_SIZED_SEQUENCE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "sequenceLayout", MethodTypeDesc.of(CD_SEQUENCE_LAYOUT, CD_long, CD_MEMORY_LAYOUT)); @@ -232,6 +211,9 @@ public boolean equals(Object other) { static final MethodHandleDesc MH_UNION = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "unionLayout", MethodTypeDesc.of(CD_GROUP_LAYOUT, CD_MEMORY_LAYOUT.arrayType())); + static final MethodHandleDesc MH_VALUE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "valueLayout", + MethodTypeDesc.of(CD_VALUE_LAYOUT, CD_Class, CD_BYTEORDER)); + static final MethodHandleDesc MH_VOID_FUNCTION = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.STATIC, CD_FUNCTION_DESC, "ofVoid", MethodTypeDesc.of(CD_FUNCTION_DESC, CD_MEMORY_LAYOUT.arrayType())); @@ -241,6 +223,6 @@ public boolean equals(Object other) { static final MethodHandleDesc MH_WITH_BIT_ALIGNMENT = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_VIRTUAL, CD_MEMORY_LAYOUT, "withBitAlignment", MethodTypeDesc.of(CD_MEMORY_LAYOUT, CD_long)); - static final MethodHandleDesc MH_WITH_ATTRIBUTE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_VIRTUAL, CD_MEMORY_LAYOUT, "withAttribute", - MethodTypeDesc.of(CD_MEMORY_LAYOUT, CD_String, CD_Constable)); + static final MethodHandleDesc MH_WITH_NAME = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_VIRTUAL, CD_MEMORY_LAYOUT, "withName", + MethodTypeDesc.of(CD_MEMORY_LAYOUT, CD_String)); } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/Addressable.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/Addressable.java index b5513c835db3d..39f61e7612244 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/Addressable.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/Addressable.java @@ -27,16 +27,21 @@ /** * Represents a type which is addressable. An addressable type is one which can be projected down to - * a memory address instance (see {@link #address()}). Examples of addressable types are {@link MemorySegment}, - * {@link MemoryAddress} and {@link CLinker.VaList}. + * a {@linkplain #address() memory address}. Examples of addressable types are {@link MemorySegment}, + * {@link MemoryAddress}, {@link VaList} and {@link NativeSymbol}. + *

+ * The {@link Addressable} type is used by the {@link CLinker C linker} to model the types of + * {@link CLinker#downcallHandle(FunctionDescriptor) downcall handle} parameters that must be passed by reference + * (e.g. memory addresses, va lists and upcall stubs). * * @implSpec * Implementations of this interface are value-based. */ -public interface Addressable { +public sealed interface Addressable permits MemorySegment, MemoryAddress, NativeSymbol, VaList { + /** - * Map this object into a {@link MemoryAddress} instance. - * @return the {@link MemoryAddress} instance associated with this object. + * Returns the memory address associated with this addressable. + * @return The memory address associated with this addressable. */ MemoryAddress address(); } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/CLinker.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/CLinker.java index 5bd4ba57c1240..f5f1a6dc31383 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/CLinker.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/CLinker.java @@ -25,93 +25,123 @@ */ package jdk.incubator.foreign; -import jdk.internal.foreign.AbstractCLinker; -import jdk.internal.foreign.NativeMemorySegmentImpl; -import jdk.internal.foreign.PlatformLayouts; import jdk.internal.foreign.SystemLookup; import jdk.internal.foreign.abi.SharedUtils; -import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64VaList; -import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64VaList; -import jdk.internal.foreign.abi.x64.sysv.SysVVaList; -import jdk.internal.foreign.abi.x64.windows.WinVaList; +import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker; +import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker; +import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker; +import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; -import java.lang.constant.Constable; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.Objects; -import java.util.function.Consumer; - -import static jdk.internal.foreign.PlatformLayouts.*; +import java.util.Optional; /** * A C linker implements the C Application Binary Interface (ABI) calling conventions. * Instances of this interface can be used to link foreign functions in native libraries that - * follow the JVM's target platform C ABI. + * follow the JVM's target platform C ABI. A C linker provides two main capabilities: first, it allows Java code + * to link foreign functions into a so called downcall method handle; secondly, it allows + * native code to call Java method handles via the generation of upcall stubs. *

- * Linking a foreign function is a process which requires two components: a method type, and - * a function descriptor. The method type, consists of a set of carrier types, which, together, - * specify the Java signature which clients must adhere to when calling the underlying foreign function. - * The function descriptor contains a set of memory layouts which, together, specify the foreign function - * signature and classification information (via a custom layout attributes, see {@link TypeKind}), so that linking can take place. + * On unsupported platforms this class will fail to initialize with an {@link ExceptionInInitializerError}. *

- * Clients of this API can build function descriptors using the predefined memory layout constants - * (based on a subset of the built-in types provided by the C language), found in this interface; alternatively, - * they can also decorate existing value layouts using the required {@link TypeKind} classification attribute - * (this can be done using the {@link MemoryLayout#withAttribute(String, Constable)} method). A failure to do so might - * result in linkage errors, given that linking requires additional classification information to determine, for instance, - * how arguments should be loaded into registers during a foreign function call. + * Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null} + * elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown.

+ * + *

Downcall method handles

+ *

+ * {@linkplain #downcallHandle(FunctionDescriptor) Linking a foreign function} is a process which requires a function descriptor, + * a set of memory layouts which, together, specify the signature of the foreign function to be linked, and returns, + * when complete, a downcall method handle, that is, a method handle that can be used to invoke the target native function. + * The Java {@link java.lang.invoke.MethodType method type} associated with the returned method handle is + * {@linkplain #downcallType(FunctionDescriptor) derived} from the argument and return layouts in the function descriptor. + * More specifically, given each layout {@code L} in the function descriptor, a corresponding carrier {@code C} is inferred, + * as described below: + *

    + *
  • if {@code L} is a {@link ValueLayout} with carrier {@code E} then there are two cases: + *
      + *
    • if {@code L} occurs in a parameter position and {@code E} is {@code MemoryAddress.class}, + * then {@code C = Addressable.class};
    • + *
    • otherwise, {@code C = E}; + *
  • + *
  • or, if {@code L} is a {@link GroupLayout}, then {@code C} is set to {@code MemorySegment.class}
  • + *
*

- * Implementations of this interface support the following primitive carrier types: - * {@code byte}, {@code short}, {@code char}, {@code int}, {@code long}, {@code float}, - * and {@code double}, as well as {@link MemoryAddress} for passing pointers, and - * {@link MemorySegment} for passing structs and unions. Finally, the {@link VaList} - * carrier type can be used to match the native {@code va_list} type. + * The downcall method handle type, derived as above, might be decorated by additional leading parameters: + *

    + *
  • If the downcall method handle is created {@linkplain #downcallHandle(FunctionDescriptor) without specifying a native symbol}, + * the downcall method handle type features a leading parameter of type {@link NativeSymbol}, from which the + * address of the target native function can be derived.
  • + *
  • If the function descriptor's return layout is a group layout, the resulting downcall method handle accepts + * an additional leading parameter of type {@link SegmentAllocator}, which is used by the linker runtime to allocate the + * memory region associated with the struct returned by the downcall method handle.
  • + *
+ *

Variadic functions, declared in C either with a trailing ellipses ({@code ...}) at the end of the formal parameter + * list or with an empty formal parameter list, are not supported directly. However, it is possible to link a native + * variadic function by using a {@linkplain FunctionDescriptor#asVariadic(MemoryLayout...) variadic} function descriptor, + * in which the specialized signature of a given variable arity callsite is described in full. Alternatively, + * if the foreign library allows it, clients might also be able to interact with variable arity methods + * by passing a trailing parameter of type {@link VaList}. + * + *

Upcall stubs

+ * + * {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, ResourceScope) Creating an upcall stub} requires a method + * handle and a function descriptor; in this case, the set of memory layouts in the function descriptor + * specify the signature of the function pointer associated with the upcall stub. *

- * For the linking process to be successful, some requirements must be satisfied; if {@code M} and {@code F} are - * the method type (obtained after dropping any prefix arguments) and the function descriptor, respectively, - * used during the linking process, then it must be that: + * The type of the provided method handle has to match the Java {@link java.lang.invoke.MethodType method type} + * associated with the upcall stub, which is derived from the argument and return layouts in the function descriptor. + * More specifically, given each layout {@code L} in the function descriptor, a corresponding carrier {@code C} is inferred, as described below: *

    - *
  • The arity of {@code M} is the same as that of {@code F};
  • - *
  • If the return type of {@code M} is {@code void}, then {@code F} should have no return layout - * (see {@link FunctionDescriptor#ofVoid(MemoryLayout...)});
  • - *
  • for each pair of carrier type {@code C} and layout {@code L} in {@code M} and {@code F}, respectively, - * where {@code C} and {@code L} refer to the same argument, or to the return value, the following conditions must hold: + *
  • if {@code L} is a {@link ValueLayout} with carrier {@code E} then there are two cases: *
      - *
    • If {@code C} is a primitve type, then {@code L} must be a {@code ValueLayout}, and the size of the layout must match - * that of the carrier type (see {@link Integer#SIZE} and similar fields in other primitive wrapper classes);
    • - *
    • If {@code C} is {@code MemoryAddress.class}, then {@code L} must be a {@code ValueLayout}, and its size must match - * the platform's address size (see {@link MemoryLayouts#ADDRESS}). For this purpose, the {@link CLinker#C_POINTER} layout - * constant can be used;
    • - *
    • If {@code C} is {@code MemorySegment.class}, then {@code L} must be a {@code GroupLayout}
    • - *
    • If {@code C} is {@code VaList.class}, then {@code L} must be {@link CLinker#C_VA_LIST}
    • - *
    - *
  • + *
  • if {@code L} occurs in a return position and {@code E} is {@code MemoryAddress.class}, + * then {@code C = Addressable.class};
  • + *
  • otherwise, {@code C = E}; + *
+ *
  • or, if {@code L} is a {@link GroupLayout}, then {@code C} is set to {@code MemorySegment.class}
  • * + * Upcall stubs are modelled by instances of type {@link NativeSymbol}; upcall stubs can be passed by reference to other + * downcall method handles (as {@link NativeSymbol} implements the {@link Addressable} interface) and, + * when no longer required, they can be {@link ResourceScope#close() released}, via their {@linkplain NativeSymbol#scope() scope}. * - *

    Variadic functions, declared in C either with a trailing ellipses ({@code ...}) at the end of the formal parameter - * list or with an empty formal parameter list, are not supported directly. It is not possible to create a method handle - * that takes a variable number of arguments, and neither is it possible to create an upcall stub wrapping a method - * handle that accepts a variable number of arguments. However, for downcalls only, it is possible to link a native - * variadic function by using a specialized method type and function descriptor: for each argument that is to be - * passed as a variadic argument, an explicit, additional, carrier type and memory layout must be present in the method type and - * function descriptor objects passed to the linker. Furthermore, as memory layouts corresponding to variadic arguments in - * a function descriptor must contain additional classification information, it is required that - * {@link #asVarArg(MemoryLayout)} is used to create the memory layouts for each parameter corresponding to a variadic - * argument in a specialized function descriptor. + *

    System lookup

    * - *

    On unsupported platforms this class will fail to initialize with an {@link ExceptionInInitializerError}. + * This class implements the {@link SymbolLookup} interface; as such clients can {@linkplain #lookup(String) lookup} symbols + * in the standard libraries associated with this linker. The set of symbols available for lookup is unspecified, + * as it depends on the platform and on the operating system. * - *

    Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null} - * elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown.

    + *

    Safety considerations

    + * + * Obtaining downcall method handle is intrinsically unsafe. A symbol in a native library does not, in general, + * contain enough signature information (e.g. arity and types of native function parameters). As a consequence, + * the linker runtime cannot validate linkage requests. When a client interacts with a downcall method handle obtained + * through an invalid linkage request (e.g. by specifying a function descriptor featuring too many argument layouts), + * the result of such interaction is unspecified and can lead to JVM crashes. On downcall handle invocation, + * the linker runtime guarantees the following for any argument that is a memory resource {@code R} (of type {@link MemorySegment}, + * {@link NativeSymbol} or {@link VaList}): + *
      + *
    • The resource scope of {@code R} is {@linkplain ResourceScope#isAlive() alive}. Otherwise, the invocation throws + * {@link IllegalStateException};
    • + *
    • The invocation occurs in same thread as the one {@link ResourceScope#ownerThread() owning} the resource scope of {@code R}, + * if said scope is confined. Otherwise, the invocation throws {@link IllegalStateException}; and
    • + *
    • The scope of {@code R} is {@linkplain ResourceScope#keepAlive(ResourceScope) kept alive} (and cannot be closed) during the invocation. + *
    + *

    + * When creating upcall stubs the linker runtime validates the type of the target method handle against the provided + * function descriptor and report an error if any mismatch is detected. As for downcalls, JVM crashes might occur, + * if the native code casts the function pointer associated with an upcall stub to a type + * that is incompatible with the provided function descriptor. Moreover, if the target method + * handle associated with an upcall stub returns a {@linkplain MemoryAddress native address}, clients must ensure + * that this address cannot become invalid after the upcall completes. This can lead to unspecified behavior, + * and even JVM crashes, since an upcall is typically executed in the context of a downcall method handle invocation. * * @implSpec * Implementations of this interface are immutable, thread-safe and value-based. */ -public sealed interface CLinker permits AbstractCLinker { +public sealed interface CLinker extends SymbolLookup permits Windowsx64Linker, SysVx64Linker, LinuxAArch64Linker, MacOsAArch64Linker { /** * Returns the C linker for the current platform. @@ -127,96 +157,75 @@ public sealed interface CLinker permits AbstractCLinker { * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. */ @CallerSensitive - static CLinker getInstance() { + static CLinker systemCLinker() { Reflection.ensureNativeAccess(Reflection.getCallerClass()); return SharedUtils.getSystemLinker(); } /** - * Obtains a system lookup which is suitable to find symbols in the standard C libraries. The set of symbols - * available for lookup is unspecified, as it depends on the platform and on the operating system. - *

    - * 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. - * @return a system-specific library lookup which is suitable to find symbols in the standard C libraries. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or - * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + * Lookup a symbol in the standard libraries associated with this linker. + * The set of symbols available for lookup is unspecified, as it depends on the platform and on the operating system. + * @return a symbol in the standard libraries associated with this linker. */ - @CallerSensitive - static SymbolLookup systemLookup() { - Reflection.ensureNativeAccess(Reflection.getCallerClass()); - return SystemLookup.getInstance(); + @Override + default Optional lookup(String name) { + return SystemLookup.getInstance().lookup(name); } /** * Obtains a foreign method handle, with the given type and featuring the given function descriptor, - * which can be used to call a target foreign function at the given address. + * which can be used to call a target foreign function at the address in the given native symbol. *

    * If the provided method type's return type is {@code MemorySegment}, then the resulting method handle features * an additional prefix parameter, of type {@link SegmentAllocator}, which will be used by the linker runtime * to allocate structs returned by-value. + *

    + * Calling this method is equivalent to the following code: +

    {@code
    +    linker.downcallHandle(function).bindTo(symbol);
    +}
    * * @param symbol downcall symbol. - * @param type the method type. * @param function the function descriptor. - * @return the downcall method handle. - * @throws IllegalArgumentException in the case of a method type and function descriptor mismatch, or if the symbol - * is {@link MemoryAddress#NULL} - * - * @see SymbolLookup - */ - MethodHandle downcallHandle(Addressable symbol, MethodType type, FunctionDescriptor function); - - /** - * Obtain a foreign method handle, with the given type and featuring the given function descriptor, - * which can be used to call a target foreign function at the given address. - *

    - * If the provided method type's return type is {@code MemorySegment}, then the provided allocator will be used by - * the linker runtime to allocate structs returned by-value. - * - * @param symbol downcall symbol. - * @param allocator the segment allocator. - * @param type the method type. - * @param function the function descriptor. - * @return the downcall method handle. - * @throws IllegalArgumentException in the case of a method type and function descriptor mismatch, or if the symbol - * is {@link MemoryAddress#NULL} + * @return the downcall method handle. The method handle type is inferred + * @throws IllegalArgumentException if the provided descriptor contains either a sequence or a padding layout, + * or if the symbol is {@link MemoryAddress#NULL} * * @see SymbolLookup */ - MethodHandle downcallHandle(Addressable symbol, SegmentAllocator allocator, MethodType type, FunctionDescriptor function); + default MethodHandle downcallHandle(NativeSymbol symbol, FunctionDescriptor function) { + SharedUtils.checkSymbol(symbol); + return downcallHandle(function).bindTo(symbol); + } /** * Obtains a foreign method handle, with the given type and featuring the given function descriptor, which can be - * used to call a target foreign function at an address. - * The resulting method handle features a prefix parameter (as the first parameter) corresponding to the address, of - * type {@link Addressable}. + * used to call a target foreign function at the address in a dynamically provided native symbol. + * The resulting method handle features a prefix parameter (as the first parameter) corresponding to the foreign function + * entry point, of type {@link NativeSymbol}. *

    - * If the provided method type's return type is {@code MemorySegment}, then the resulting method handle features an + * If the provided function descriptor's return layout is a {@link GroupLayout}, then the resulting method handle features an * additional prefix parameter (inserted immediately after the address parameter), of type {@link SegmentAllocator}), * which will be used by the linker runtime to allocate structs returned by-value. *

    - * The returned method handle will throw an {@link IllegalArgumentException} if the target address passed to it is - * {@link MemoryAddress#NULL}, or a {@link NullPointerException} if the target address is {@code null}. + * The returned method handle will throw an {@link IllegalArgumentException} if the native symbol passed to it is + * associated with the {@link MemoryAddress#NULL} address, or a {@link NullPointerException} if the native symbol is {@code null}. * - * @param type the method type. * @param function the function descriptor. - * @return the downcall method handle. - * @throws IllegalArgumentException in the case of a method type and function descriptor mismatch. + * @return the downcall method handle. The method handle type is inferred + * from the provided function descriptor. + * @throws IllegalArgumentException if the provided descriptor contains either a sequence or a padding layout. * * @see SymbolLookup */ - MethodHandle downcallHandle(MethodType type, FunctionDescriptor function); + MethodHandle downcallHandle(FunctionDescriptor function); /** * Allocates a native stub with given scope which can be passed to other foreign functions (as a function pointer); * calling such a function pointer from native code will result in the execution of the provided method handle. * *

    - * The returned memory address is associated with the provided scope. When such scope is closed, + * The returned function pointer is associated with the provided scope. When such scope is closed, * the corresponding native stub will be deallocated. *

    * The target method handle should not throw any exceptions. If the target method handle does throw an exception, @@ -228,563 +237,34 @@ static SymbolLookup systemLookup() { * @param target the target method handle. * @param function the function descriptor. * @param scope the upcall stub scope. - * @return the native stub segment. - * @throws IllegalArgumentException if the target's method type and the function descriptor mismatch. + * @return the native stub symbol. + * @throws IllegalArgumentException if the provided descriptor contains either a sequence or a padding layout, + * or if it is determined that the target method handle can throw an exception, or if the target method handle + * has a type that does not match the upcall stub inferred type. * @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other * than the thread owning {@code scope}. */ - MemoryAddress upcallStub(MethodHandle target, FunctionDescriptor function, ResourceScope scope); - - /** - * The layout for the {@code char} C type - */ - ValueLayout C_CHAR = pick(SysV.C_CHAR, Win64.C_CHAR, AArch64.C_CHAR); - /** - * The layout for the {@code short} C type - */ - ValueLayout C_SHORT = pick(SysV.C_SHORT, Win64.C_SHORT, AArch64.C_SHORT); - /** - * The layout for the {@code int} C type - */ - ValueLayout C_INT = pick(SysV.C_INT, Win64.C_INT, AArch64.C_INT); - /** - * The layout for the {@code long} C type - */ - ValueLayout C_LONG = pick(SysV.C_LONG, Win64.C_LONG, AArch64.C_LONG); - /** - * The layout for the {@code long long} C type. - */ - ValueLayout C_LONG_LONG = pick(SysV.C_LONG_LONG, Win64.C_LONG_LONG, AArch64.C_LONG_LONG); - /** - * The layout for the {@code float} C type - */ - ValueLayout C_FLOAT = pick(SysV.C_FLOAT, Win64.C_FLOAT, AArch64.C_FLOAT); - /** - * The layout for the {@code double} C type - */ - ValueLayout C_DOUBLE = pick(SysV.C_DOUBLE, Win64.C_DOUBLE, AArch64.C_DOUBLE); - /** - * The {@code T*} native type. - */ - ValueLayout C_POINTER = pick(SysV.C_POINTER, Win64.C_POINTER, AArch64.C_POINTER); - /** - * The layout for the {@code va_list} C type - */ - MemoryLayout C_VA_LIST = pick(SysV.C_VA_LIST, Win64.C_VA_LIST, AArch64.C_VA_LIST); - - /** - * Returns a memory layout that is suitable to use as the layout for variadic arguments in a specialized - * function descriptor. - * @param the memory layout type - * @param layout the layout the adapt - * @return a potentially newly created layout with the right attributes - */ - @SuppressWarnings("unchecked") - static T asVarArg(T layout) { - Objects.requireNonNull(layout); - return (T) PlatformLayouts.asVarArg(layout); - } - - /** - * Converts a Java string into a UTF-8 encoded, null-terminated C string, - * storing the result into a native memory segment allocated using the provided allocator. - *

    - * This method always replaces malformed-input and unmappable-character - * sequences with this charset's default replacement byte array. The - * {@link java.nio.charset.CharsetEncoder} class should be used when more - * control over the encoding process is required. - * - * @param str the Java string to be converted into a C string. - * @param allocator the allocator to be used for the native segment allocation. - * @return a new native memory segment containing the converted C string. - */ - static MemorySegment toCString(String str, SegmentAllocator allocator) { - Objects.requireNonNull(str); - Objects.requireNonNull(allocator); - return toCString(str.getBytes(StandardCharsets.UTF_8), allocator); - } - - /** - * Converts a Java string into a UTF-8 encoded, null-terminated C string, - * storing the result into a native memory segment associated with the provided resource scope. - *

    - * This method always replaces malformed-input and unmappable-character - * sequences with this charset's default replacement byte array. The - * {@link java.nio.charset.CharsetEncoder} class should be used when more - * control over the encoding process is required. - * - * @param str the Java string to be converted into a C string. - * @param scope the resource scope to be associated with the returned segment. - * @return a new native memory segment containing the converted C string. - * @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other - * than the thread owning {@code scope}. - */ - static MemorySegment toCString(String str, ResourceScope scope) { - return toCString(str, SegmentAllocator.ofScope(scope)); - } - - /** - * Converts a UTF-8 encoded, null-terminated C string stored at given address into a Java string. - *

    - * 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. - *

    - * 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. - * - * @param addr the address at which the string is stored. - * @return a Java string with the contents of the null-terminated C string at given address. - * @throws IllegalArgumentException if the size of the native string is greater than the largest string supported by the platform, - * or if {@code addr == MemoryAddress.NULL}. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or - * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. - */ - @CallerSensitive - static String toJavaString(MemoryAddress addr) { - Reflection.ensureNativeAccess(Reflection.getCallerClass()); - SharedUtils.checkAddress(addr); - return SharedUtils.toJavaStringInternal(NativeMemorySegmentImpl.EVERYTHING, addr.toRawLongValue()); - } - - /** - * Converts a UTF-8 encoded, null-terminated C string stored at given address into a Java string. - *

    - * 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 addr the address at which the string is stored. - * @return a Java string with the contents of the null-terminated C string at given address. - * @throws IllegalArgumentException if the size of the native string is greater than the largest string supported by the platform. - * @throws IllegalStateException if the size of the native string is greater than the size of the segment - * associated with {@code addr}, or if {@code addr} is associated with a segment that is not alive. - */ - static String toJavaString(MemorySegment addr) { - Objects.requireNonNull(addr); - return SharedUtils.toJavaStringInternal(addr, 0L); - } - - private static void copy(MemorySegment addr, byte[] bytes) { - var heapSegment = MemorySegment.ofArray(bytes); - addr.copyFrom(heapSegment); - MemoryAccess.setByteAtOffset(addr, bytes.length, (byte)0); - } - - private static MemorySegment toCString(byte[] bytes, SegmentAllocator allocator) { - MemorySegment addr = allocator.allocate(bytes.length + 1, 1L); - copy(addr, bytes); - return addr; - } + NativeSymbol upcallStub(MethodHandle target, FunctionDescriptor function, ResourceScope scope); /** - * Allocates memory of given size using malloc. - *

    - * 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. - * - * @param size memory size to be allocated - * @return addr memory address of the allocated memory - * @throws OutOfMemoryError if malloc could not allocate the required amount of native memory. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or - * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + * Obtains the downcall method handle {@linkplain MethodType type} associated with a given function descriptor. + * @param functionDescriptor a function descriptor. + * @return the downcall method handle {@linkplain MethodType type} associated with a given function descriptor. + * @throws IllegalArgumentException if one or more layouts in the function descriptor are not supported + * (e.g. if they are sequence layouts or padding layouts). */ - @CallerSensitive - static MemoryAddress allocateMemory(long size) { - Reflection.ensureNativeAccess(Reflection.getCallerClass()); - MemoryAddress addr = SharedUtils.allocateMemoryInternal(size); - if (addr.equals(MemoryAddress.NULL)) { - throw new OutOfMemoryError(); - } else { - return addr; - } + static MethodType downcallType(FunctionDescriptor functionDescriptor) { + return SharedUtils.inferMethodType(functionDescriptor, false); } /** - * Frees the memory pointed by the given memory address. - *

    - * 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. - * - * @param addr memory address of the native memory to be freed - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * @throws IllegalArgumentException if {@code addr == MemoryAddress.NULL}. - * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or - * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + * Obtains the method handle {@linkplain MethodType type} associated with an upcall stub with given function descriptor. + * @param functionDescriptor a function descriptor. + * @return the method handle {@linkplain MethodType type} associated with an upcall stub with given function descriptor. + * @throws IllegalArgumentException if one or more layouts in the function descriptor are not supported + * (e.g. if they are sequence layouts or padding layouts). */ - @CallerSensitive - static void freeMemory(MemoryAddress addr) { - Reflection.ensureNativeAccess(Reflection.getCallerClass()); - SharedUtils.checkAddress(addr); - SharedUtils.freeMemoryInternal(addr); - } - - /** - * An interface that models a C {@code va_list}. - *

    - * A va list is a stateful cursor used to iterate over a set of variadic arguments. - *

    - * Per the C specification (see C standard 6.5.2.2 Function calls - item 6), - * arguments to variadic calls are erased by way of 'default argument promotions', - * which erases integral types by way of integer promotion (see C standard 6.3.1.1 - item 2), - * and which erases all {@code float} arguments to {@code double}. - *

    - * As such, this interface only supports reading {@code int}, {@code double}, - * and any other type that fits into a {@code long}. - * - *

    Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null} - * elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown.

    - */ - sealed interface VaList extends Addressable permits WinVaList, SysVVaList, LinuxAArch64VaList, MacOsAArch64VaList, SharedUtils.EmptyVaList { - - /** - * Reads the next value as an {@code int} and advances this va list's position. - * - * @param layout the layout of the value - * @return the value read as an {@code int} - * @throws IllegalStateException if the resource scope associated with this instance has been closed - * (see {@link #scope()}). - * @throws IllegalArgumentException if the given memory layout is not compatible with {@code int} - */ - int vargAsInt(MemoryLayout layout); - - /** - * Reads the next value as a {@code long} and advances this va list's position. - * - * @param layout the layout of the value - * @return the value read as an {@code long} - * @throws IllegalStateException if the resource scope associated with this instance has been closed - * (see {@link #scope()}). - * @throws IllegalArgumentException if the given memory layout is not compatible with {@code long} - */ - long vargAsLong(MemoryLayout layout); - - /** - * Reads the next value as a {@code double} and advances this va list's position. - * - * @param layout the layout of the value - * @return the value read as an {@code double} - * @throws IllegalStateException if the resource scope associated with this instance has been closed - * (see {@link #scope()}). - * @throws IllegalArgumentException if the given memory layout is not compatible with {@code double} - */ - double vargAsDouble(MemoryLayout layout); - - /** - * Reads the next value as a {@code MemoryAddress} and advances this va list's position. - * - * @param layout the layout of the value - * @return the value read as an {@code MemoryAddress} - * @throws IllegalStateException if the resource scope associated with this instance has been closed - * (see {@link #scope()}). - * @throws IllegalArgumentException if the given memory layout is not compatible with {@code MemoryAddress} - */ - MemoryAddress vargAsAddress(MemoryLayout layout); - - /** - * Reads the next value as a {@code MemorySegment}, and advances this va list's position. - *

    - * The memory segment returned by this method will be allocated using the given {@link SegmentAllocator}. - * - * @param layout the layout of the value - * @param allocator the allocator to be used for the native segment allocation - * @return the value read as an {@code MemorySegment} - * @throws IllegalStateException if the resource scope associated with this instance has been closed - * (see {@link #scope()}). - * @throws IllegalArgumentException if the given memory layout is not compatible with {@code MemorySegment} - */ - MemorySegment vargAsSegment(MemoryLayout layout, SegmentAllocator allocator); - - /** - * Reads the next value as a {@code MemorySegment}, and advances this va list's position. - *

    - * The memory segment returned by this method will be associated with the given {@link ResourceScope}. - * - * @param layout the layout of the value - * @param scope the resource scope to be associated with the returned segment - * @return the value read as an {@code MemorySegment} - * @throws IllegalStateException if the resource scope associated with this instance has been closed - * (see {@link #scope()}). - * @throws IllegalArgumentException if the given memory layout is not compatible with {@code MemorySegment} - * @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other - * than the thread owning {@code scope}. - */ - MemorySegment vargAsSegment(MemoryLayout layout, ResourceScope scope); - - /** - * Skips a number of elements with the given memory layouts, and advances this va list's position. - * - * @param layouts the layout of the value - * @throws IllegalStateException if the resource scope associated with this instance has been closed - * (see {@link #scope()}). - */ - void skip(MemoryLayout... layouts); - - /** - * Returns the resource scope associated with this instance. - * @return the resource scope associated with this instance. - */ - ResourceScope scope(); - - /** - * Copies this C {@code va_list} at its current position. Copying is useful to traverse the va list's elements - * starting from the current position, without affecting the state of the original va list, essentially - * allowing the elements to be traversed multiple times. - *

    - * Any native resource required by the execution of this method will be allocated in the resource scope - * associated with this instance (see {@link #scope()}). - *

    - * This method only copies the va list cursor itself and not the memory that may be attached to the - * va list which holds its elements. That means that if this va list was created with the - * {@link #make(Consumer, ResourceScope)} method, closing this va list will also release the native memory that holds its - * elements, making the copy unusable. - * - * @return a copy of this C {@code va_list}. - * @throws IllegalStateException if the resource scope associated with this instance has been closed - * (see {@link #scope()}). - */ - VaList copy(); - - /** - * Returns the memory address of the C {@code va_list} associated with this instance. - * The returned memory address is associated with same resource scope as that associated with this instance. - * - * @return the memory address of the C {@code va_list} associated with this instance. - */ - @Override - MemoryAddress address(); - - /** - * Constructs a new {@code VaList} instance out of a memory address pointing to an existing C {@code va_list}, - * backed by the {@linkplain ResourceScope#globalScope() global} resource scope. - *

    - * 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. - * - * @param address a memory address pointing to an existing C {@code va_list}. - * @return a new {@code VaList} instance backed by the C {@code va_list} at {@code address}. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or - * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. - */ - @CallerSensitive - static VaList ofAddress(MemoryAddress address) { - Reflection.ensureNativeAccess(Reflection.getCallerClass()); - return SharedUtils.newVaListOfAddress(address, ResourceScope.globalScope()); - } - - /** - * Constructs a new {@code VaList} instance out of a memory address pointing to an existing C {@code va_list}, - * with given resource scope. - *

    - * 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. - * - * @param address a memory address pointing to an existing C {@code va_list}. - * @param scope the resource scope to be associated with the returned {@code VaList} instance. - * @return a new {@code VaList} instance backed by the C {@code va_list} at {@code address}. - * @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other - * than the thread owning {@code scope}. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or - * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. - */ - @CallerSensitive - static VaList ofAddress(MemoryAddress address, ResourceScope scope) { - Reflection.ensureNativeAccess(Reflection.getCallerClass()); - Objects.requireNonNull(address); - Objects.requireNonNull(scope); - return SharedUtils.newVaListOfAddress(address, scope); - } - - /** - * Constructs a new {@code VaList} using a builder (see {@link Builder}), associated with a given - * {@linkplain ResourceScope resource scope}. - *

    - * If this method needs to allocate native memory, such memory will be managed by the given - * {@linkplain ResourceScope resource scope}, and will be released when the resource scope is {@linkplain ResourceScope#close closed}. - *

    - * Note that when there are no elements added to the created va list, - * this method will return the same as {@link #empty()}. - * - * @param actions a consumer for a builder (see {@link Builder}) which can be used to specify the elements - * of the underlying C {@code va_list}. - * @param scope the scope to be used for the valist allocation. - * @return a new {@code VaList} instance backed by a fresh C {@code va_list}. - * @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other - * than the thread owning {@code scope}. - */ - static VaList make(Consumer actions, ResourceScope scope) { - Objects.requireNonNull(actions); - Objects.requireNonNull(scope); - return SharedUtils.newVaList(actions, scope); - } - - /** - * Returns an empty C {@code va_list} constant. - *

    - * The returned {@code VaList} can not be closed. - * - * @return a {@code VaList} modelling an empty C {@code va_list}. - */ - static VaList empty() { - return SharedUtils.emptyVaList(); - } - - /** - * A builder interface used to construct a C {@code va_list}. - * - *

    Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null} - * elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown.

    - */ - sealed interface Builder permits WinVaList.Builder, SysVVaList.Builder, LinuxAArch64VaList.Builder, MacOsAArch64VaList.Builder { - - /** - * Adds a native value represented as an {@code int} to the C {@code va_list} being constructed. - * - * @param layout the native layout of the value. - * @param value the value, represented as an {@code int}. - * @return this builder. - * @throws IllegalArgumentException if the given memory layout is not compatible with {@code int} - */ - Builder vargFromInt(ValueLayout layout, int value); - - /** - * Adds a native value represented as a {@code long} to the C {@code va_list} being constructed. - * - * @param layout the native layout of the value. - * @param value the value, represented as a {@code long}. - * @return this builder. - * @throws IllegalArgumentException if the given memory layout is not compatible with {@code long} - */ - Builder vargFromLong(ValueLayout layout, long value); - - /** - * Adds a native value represented as a {@code double} to the C {@code va_list} being constructed. - * - * @param layout the native layout of the value. - * @param value the value, represented as a {@code double}. - * @return this builder. - * @throws IllegalArgumentException if the given memory layout is not compatible with {@code double} - */ - Builder vargFromDouble(ValueLayout layout, double value); - - /** - * Adds a native value represented as a {@code MemoryAddress} to the C {@code va_list} being constructed. - * - * @param layout the native layout of the value. - * @param value the value, represented as a {@code Addressable}. - * @return this builder. - * @throws IllegalArgumentException if the given memory layout is not compatible with {@code MemoryAddress} - */ - Builder vargFromAddress(ValueLayout layout, Addressable value); - - /** - * Adds a native value represented as a {@code MemorySegment} to the C {@code va_list} being constructed. - * - * @param layout the native layout of the value. - * @param value the value, represented as a {@code MemorySegment}. - * @return this builder. - * @throws IllegalArgumentException if the given memory layout is not compatible with {@code MemorySegment} - */ - Builder vargFromSegment(GroupLayout layout, MemorySegment value); - } - } - - /** - * A C type kind. Each kind corresponds to a particular C language builtin type, and can be attached to - * {@link ValueLayout} instances using the {@link MemoryLayout#withAttribute(String, Constable)} in order - * to obtain a layout which can be classified accordingly by {@link CLinker#downcallHandle(Addressable, MethodType, FunctionDescriptor)} - * and {@link CLinker#upcallStub(MethodHandle, FunctionDescriptor, ResourceScope)}. - */ - enum TypeKind { - /** - * A kind corresponding to the integral C {@code char} type - */ - CHAR(true), - /** - * A kind corresponding to the integral C {@code short} type - */ - SHORT(true), - /** - * A kind corresponding to the integral C {@code int} type - */ - INT(true), - /** - * A kind corresponding to the integral C {@code long} type - */ - LONG(true), - /** - * A kind corresponding to the integral C {@code long long} type - */ - LONG_LONG(true), - /** - * A kind corresponding to the floating-point C {@code float} type - */ - FLOAT(false), - /** - * A kind corresponding to the floating-point C {@code double} type - */ - DOUBLE(false), - /** - * A kind corresponding to the an integral C pointer type - */ - POINTER(false); - - private final boolean isIntegral; - - TypeKind(boolean isIntegral) { - this.isIntegral = isIntegral; - } - - /** - * Is this kind integral? - * - * @return true if this kind is integral - */ - public boolean isIntegral() { - return isIntegral; - } - - /** - * Is this kind a floating point type? - * - * @return true if this kind is a floating point type - */ - public boolean isFloat() { - return !isIntegral() && !isPointer(); - } - - /** - * Is this kind a pointer kind? - * - * @return true if this kind is a pointer kind - */ - public boolean isPointer() { - return this == POINTER; - } - - /** - * The layout attribute name associated with this classification kind. Clients can retrieve the type kind - * of a layout using the following code: - *
    {@code
    -        ValueLayout layout = ...
    -        TypeKind = layout.attribute(TypeKind.ATTR_NAME).orElse(null);
    -         * }
    - */ - public static final String ATTR_NAME = "abi/kind"; + static MethodType upcallType(FunctionDescriptor functionDescriptor) { + return SharedUtils.inferMethodType(functionDescriptor, true); } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/FunctionDescriptor.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/FunctionDescriptor.java index 2124eea6ccdec..8c40668d50526 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/FunctionDescriptor.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/FunctionDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,10 +30,7 @@ import java.lang.constant.DynamicConstantDesc; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; @@ -46,60 +43,16 @@ *

    Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null} * elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown.

    */ -public final class FunctionDescriptor implements Constable { - - /** - * The name of the function descriptor attribute (see {@link #attributes()} used to mark trivial functions. The - * attribute value must be a boolean. - */ - public static final String TRIVIAL_ATTRIBUTE_NAME = "abi/trivial"; +public sealed class FunctionDescriptor implements Constable permits FunctionDescriptor.VariadicFunction { private final MemoryLayout resLayout; - private final MemoryLayout[] argLayouts; - private final Map attributes; + private final List argLayouts; - private FunctionDescriptor(MemoryLayout resLayout, Map attributes, MemoryLayout... argLayouts) { + private FunctionDescriptor(MemoryLayout resLayout, List argLayouts) { this.resLayout = resLayout; - this.attributes = attributes; this.argLayouts = argLayouts; } - /** - * Returns the attribute with the given name (if it exists). - * - * @param name the attribute name. - * @return the attribute with the given name (if it exists). - */ - public Optional attribute(String name) { - Objects.requireNonNull(name); - return Optional.ofNullable(attributes.get(name)); - } - - /** - * Returns a stream of the attribute names associated with this function descriptor. - * - * @return a stream of the attribute names associated with this function descriptor. - */ - public Stream attributes() { - return attributes.keySet().stream(); - } - - /** - * Returns a new function descriptor which features the same attributes as this descriptor, plus the newly specified attribute. - * If this descriptor already contains an attribute with the same name, the existing attribute value is overwritten in the returned - * descriptor. - * - * @param name the attribute name. - * @param value the attribute value. - * @return a new function descriptor which features the same attributes as this descriptor, plus the newly specified attribute. - */ - public FunctionDescriptor withAttribute(String name, Constable value) { - Objects.requireNonNull(name); - Map newAttributes = new HashMap<>(attributes); - newAttributes.put(name, value); - return new FunctionDescriptor(resLayout, newAttributes, argLayouts); - } - /** * Returns the return layout associated with this function. * @return the return layout. @@ -113,7 +66,7 @@ public Optional returnLayout() { * @return the argument layouts. */ public List argumentLayouts() { - return Arrays.asList(argLayouts); + return argLayouts; } /** @@ -126,7 +79,7 @@ public static FunctionDescriptor of(MemoryLayout resLayout, MemoryLayout... argL Objects.requireNonNull(resLayout); Objects.requireNonNull(argLayouts); Arrays.stream(argLayouts).forEach(Objects::requireNonNull); - return new FunctionDescriptor(resLayout, Map.of(), argLayouts); + return new FunctionDescriptor(resLayout, List.of(argLayouts)); } /** @@ -137,7 +90,31 @@ public static FunctionDescriptor of(MemoryLayout resLayout, MemoryLayout... argL public static FunctionDescriptor ofVoid(MemoryLayout... argLayouts) { Objects.requireNonNull(argLayouts); Arrays.stream(argLayouts).forEach(Objects::requireNonNull); - return new FunctionDescriptor(null, Map.of(), argLayouts); + return new FunctionDescriptor(null, List.of(argLayouts)); + } + + /** + * Obtain a specialized variadic function descriptor, by appending given variadic layouts to this + * function descriptor argument layouts. The resulting function descriptor can report the position + * of the {@linkplain #firstVariadicArgumentIndex() first variadic argument}, and cannot be altered + * in any way: for instance, calling {@link #withReturnLayout(MemoryLayout)} on the resulting descriptor + * will throw an {@link UnsupportedOperationException}. + * @param variadicLayouts the variadic argument layouts to be appended to this descriptor argument layouts. + * @return a new variadic function descriptor, or this descriptor if {@code variadicLayouts.length == 0}. + */ + public FunctionDescriptor asVariadic(MemoryLayout... variadicLayouts) { + Objects.requireNonNull(variadicLayouts); + Arrays.stream(variadicLayouts).forEach(Objects::requireNonNull); + return variadicLayouts.length == 0 ? this : new VariadicFunction(this, variadicLayouts); + } + + /** + * The index of the first variadic argument layout (where defined). + * @return The index of the first variadic argument layout, or {@code -1} if this is not a + * {@linkplain #asVariadic(MemoryLayout...) variadic} layout. + */ + public int firstVariadicArgumentIndex() { + return -1; } /** @@ -149,9 +126,8 @@ public static FunctionDescriptor ofVoid(MemoryLayout... argLayouts) { public FunctionDescriptor withAppendedArgumentLayouts(MemoryLayout... addedLayouts) { Objects.requireNonNull(addedLayouts); Arrays.stream(addedLayouts).forEach(Objects::requireNonNull); - MemoryLayout[] newLayouts = Arrays.copyOf(argLayouts, argLayouts.length + addedLayouts.length); - System.arraycopy(addedLayouts, 0, newLayouts, argLayouts.length, addedLayouts.length); - return new FunctionDescriptor(resLayout, attributes, newLayouts); + List newLayouts = Stream.concat(argLayouts.stream(), Stream.of(addedLayouts)).toList(); + return new FunctionDescriptor(resLayout, newLayouts); } /** @@ -161,7 +137,7 @@ public FunctionDescriptor withAppendedArgumentLayouts(MemoryLayout... addedLayou */ public FunctionDescriptor withReturnLayout(MemoryLayout newReturn) { Objects.requireNonNull(newReturn); - return new FunctionDescriptor(newReturn, attributes, argLayouts); + return new FunctionDescriptor(newReturn, argLayouts); } /** @@ -169,7 +145,7 @@ public FunctionDescriptor withReturnLayout(MemoryLayout newReturn) { * @return the new function descriptor. */ public FunctionDescriptor withVoidReturnLayout() { - return new FunctionDescriptor(null, attributes, argLayouts); + return new FunctionDescriptor(null, argLayouts); } /** @@ -187,7 +163,7 @@ public String toString() { /** * Compares the specified object with this function descriptor for equality. Returns {@code true} if and only if the specified - * object is also a function descriptor, and all of the following conditions are met: + * object is also a function descriptor, and all the following conditions are met: *
      *
    • the two function descriptors have equals return layouts (see {@link MemoryLayout#equals(Object)}), or both have no return layout
    • *
    • the two function descriptors have argument layouts that are pair-wise equal (see {@link MemoryLayout#equals(Object)}) @@ -201,11 +177,10 @@ public boolean equals(Object other) { if (this == other) { return true; } - if (!(other instanceof FunctionDescriptor)) { + if (!(other instanceof FunctionDescriptor f)) { return false; } - FunctionDescriptor f = (FunctionDescriptor) other; - return Objects.equals(resLayout, f.resLayout) && Arrays.equals(argLayouts, f.argLayouts); + return Objects.equals(resLayout, f.resLayout) && Objects.equals(argLayouts, f.argLayouts); } /** @@ -214,10 +189,18 @@ public boolean equals(Object other) { */ @Override public int hashCode() { - int hashCode = Arrays.hashCode(argLayouts); + int hashCode = Objects.hashCode(argLayouts); return resLayout == null ? hashCode : resLayout.hashCode() ^ hashCode; } + /** + * Returns an {@link Optional} containing the nominal descriptor for this + * function descriptor, if one can be constructed, or an empty {@link Optional} + * if one cannot be constructed. + * + * @return An {@link Optional} containing the resulting nominal descriptor, + * or an empty {@link Optional} if one cannot be constructed. + */ @Override public Optional> describeConstable() { List constants = new ArrayList<>(); @@ -231,4 +214,40 @@ public Optional> describeConstable() { return Optional.of(DynamicConstantDesc.ofNamed( ConstantDescs.BSM_INVOKE, "function", AbstractLayout.CD_FUNCTION_DESC, constants.toArray(new ConstantDesc[0]))); } + + static final class VariadicFunction extends FunctionDescriptor { + + private final int firstVariadicIndex; + + public VariadicFunction(FunctionDescriptor descriptor, MemoryLayout... argLayouts) { + super(descriptor.returnLayout().orElse(null), + Stream.concat(descriptor.argumentLayouts().stream(), Stream.of(argLayouts)).toList()); + this.firstVariadicIndex = descriptor.argumentLayouts().size(); + } + + @Override + public int firstVariadicArgumentIndex() { + return firstVariadicIndex; + } + + @Override + public FunctionDescriptor withAppendedArgumentLayouts(MemoryLayout... addedLayouts) { + throw new UnsupportedOperationException(); + } + + @Override + public FunctionDescriptor withReturnLayout(MemoryLayout newReturn) { + throw new UnsupportedOperationException(); + } + + @Override + public FunctionDescriptor withVoidReturnLayout() { + throw new UnsupportedOperationException(); + } + + @Override + public Optional> describeConstable() { + return Optional.empty(); + } + } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/GroupLayout.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/GroupLayout.java index bcb5605252701..d08bb716ae893 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/GroupLayout.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/GroupLayout.java @@ -25,14 +25,12 @@ */ package jdk.incubator.foreign; -import java.lang.constant.Constable; import java.lang.constant.ConstantDesc; import java.lang.constant.ConstantDescs; import java.lang.constant.DynamicConstantDesc; import java.lang.constant.MethodHandleDesc; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.OptionalLong; @@ -40,7 +38,7 @@ import java.util.stream.Collectors; /** - * A group layout is used to combine together multiple member layouts. There are two ways in which member layouts + * A group layout is used to combine multiple member layouts. There are two ways in which member layouts * can be combined: if member layouts are laid out one after the other, the resulting group layout is said to be a struct * (see {@link MemoryLayout#structLayout(MemoryLayout...)}); conversely, if all member layouts are laid out at the same starting offset, * the resulting group layout is said to be a union (see {@link MemoryLayout#unionLayout(MemoryLayout...)}). @@ -105,11 +103,11 @@ long alignof(List elems) { private final List elements; GroupLayout(Kind kind, List elements) { - this(kind, elements, kind.alignof(elements), Map.of()); + this(kind, elements, kind.alignof(elements), Optional.empty()); } - GroupLayout(Kind kind, List elements, long alignment, Map attributes) { - super(kind.sizeof(elements), alignment, attributes); + GroupLayout(Kind kind, List elements, long alignment, Optional name) { + super(kind.sizeof(elements), alignment, name); this.kind = kind; this.elements = elements; } @@ -160,10 +158,9 @@ public boolean equals(Object other) { if (!super.equals(other)) { return false; } - if (!(other instanceof GroupLayout)) { + if (!(other instanceof GroupLayout g)) { return false; } - GroupLayout g = (GroupLayout)other; return kind.equals(g.kind) && elements.equals(g.elements); } @@ -173,8 +170,8 @@ public int hashCode() { } @Override - GroupLayout dup(long alignment, Map attributes) { - return new GroupLayout(kind, elements, alignment, attributes); + GroupLayout dup(long alignment, Optional name) { + return new GroupLayout(kind, elements, alignment, name); } @Override @@ -212,12 +209,4 @@ public GroupLayout withName(String name) { public GroupLayout withBitAlignment(long alignmentBits) { return (GroupLayout)super.withBitAlignment(alignmentBits); } - - /** - * {@inheritDoc} - */ - @Override - public GroupLayout withAttribute(String name, Constable value) { - return (GroupLayout)super.withAttribute(name, value); - } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAccess.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAccess.java deleted file mode 100644 index 3e2182b596387..0000000000000 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAccess.java +++ /dev/null @@ -1,1455 +0,0 @@ -/* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. 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.incubator.foreign; - -import jdk.internal.access.foreign.MemorySegmentProxy; -import jdk.internal.vm.annotation.ForceInline; - -import java.lang.invoke.VarHandle; -import java.nio.ByteOrder; -import java.util.Objects; - -/** - * This class defines ready-made static accessors which can be used to dereference memory segments in many ways. - *

      - * The most primitive accessors (see {@link #getIntAtOffset(MemorySegment, long, ByteOrder)}) take a segment, an offset - * (expressed in bytes) and a byte order. The final address at which the dereference will occur will be computed by offsetting - * the base address by the specified offset, as if by calling {@link MemoryAddress#addOffset(long)} on the specified base address. - *

      - * In cases where no offset is required, overloads are provided (see {@link #getInt(MemorySegment, ByteOrder)}) so that - * clients can omit the offset coordinate. - *

      - * To help dereferencing in array-like use cases (e.g. where the layout of a given memory segment is a sequence - * layout of given size an element count), higher-level overloads are also provided (see {@link #getIntAtIndex(MemorySegment, long, ByteOrder)}), - * which take a segment and a logical element index. The formula to obtain the byte offset {@code O} from an - * index {@code I} is given by {@code O = I * S} where {@code S} is the size (expressed in bytes) of the element to - * be dereferenced. - *

      - * In cases where native byte order is preferred, overloads are provided (see {@link #getIntAtOffset(MemorySegment, long)}) - * so that clients can omit the byte order parameter. - * - *

      Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null} - * elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown.

      - */ -public final class MemoryAccess { - - private MemoryAccess() { - // just the one - } - - private static final VarHandle byte_handle = MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder()); - private static final VarHandle char_LE_handle = unalignedHandle(MemoryLayouts.BITS_16_LE, char.class); - private static final VarHandle short_LE_handle = unalignedHandle(MemoryLayouts.BITS_16_LE, short.class); - private static final VarHandle int_LE_handle = unalignedHandle(MemoryLayouts.BITS_32_LE, int.class); - private static final VarHandle float_LE_handle = unalignedHandle(MemoryLayouts.BITS_32_LE, float.class); - private static final VarHandle long_LE_handle = unalignedHandle(MemoryLayouts.BITS_64_LE, long.class); - private static final VarHandle double_LE_handle = unalignedHandle(MemoryLayouts.BITS_64_LE, double.class); - private static final VarHandle char_BE_handle = unalignedHandle(MemoryLayouts.BITS_16_BE, char.class); - private static final VarHandle short_BE_handle = unalignedHandle(MemoryLayouts.BITS_16_BE, short.class); - private static final VarHandle int_BE_handle = unalignedHandle(MemoryLayouts.BITS_32_BE, int.class); - private static final VarHandle float_BE_handle = unalignedHandle(MemoryLayouts.BITS_32_BE, float.class); - private static final VarHandle long_BE_handle = unalignedHandle(MemoryLayouts.BITS_64_BE, long.class); - private static final VarHandle double_BE_handle = unalignedHandle(MemoryLayouts.BITS_64_BE, double.class); - private static final VarHandle address_handle; - - static { - Class carrier = switch ((int) MemoryLayouts.ADDRESS.byteSize()) { - case 4 -> int.class; - case 8 -> long.class; - default -> throw new ExceptionInInitializerError("Unsupported pointer size: " + MemoryLayouts.ADDRESS.byteSize()); - }; - address_handle = MemoryHandles.asAddressVarHandle(unalignedHandle(MemoryLayouts.ADDRESS, carrier)); - } - - private static VarHandle unalignedHandle(ValueLayout elementLayout, Class carrier) { - return MemoryHandles.varHandle(carrier, 1, elementLayout.order()); - } - - // Note: all the accessor methods defined below take advantage of argument type profiling - // (see src/hotspot/share/oops/methodData.cpp) which greatly enhances performance when the same accessor - // method is used repeatedly with different segment kinds (e.g. on-heap vs. off-heap). - - /** - * Reads a byte from given segment and offset. - * - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @return a byte value read from {@code segment}. - */ - @ForceInline - public static byte getByteAtOffset(MemorySegment segment, long offset) { - Objects.requireNonNull(segment); - return (byte)byte_handle.get(segment, offset); - } - - /** - * Writes a byte at given segment and offset. - * - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @param value the byte value to be written. - */ - @ForceInline - public static void setByteAtOffset(MemorySegment segment, long offset, byte value) { - Objects.requireNonNull(segment); - byte_handle.set(segment, offset, value); - } - - /** - * Reads a char from given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    getCharAtOffset(segment, offset, ByteOrder.nativeOrder());
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @return a char value read from {@code segment}. - */ - @ForceInline - public static char getCharAtOffset(MemorySegment segment, long offset) { - return getCharAtOffset(segment, offset, ByteOrder.nativeOrder()); - } - - /** - * Writes a char at given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setCharAtOffset(segment, offset, ByteOrder.nativeOrder(), value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @param value the char value to be written. - */ - @ForceInline - public static void setCharAtOffset(MemorySegment segment, long offset, char value) { - setCharAtOffset(segment, offset, ByteOrder.nativeOrder(), value); - } - - /** - * Reads a short from given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    getShortAtOffset(segment, offset, ByteOrder.nativeOrder());
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @return a short value read from {@code segment}. - */ - @ForceInline - public static short getShortAtOffset(MemorySegment segment, long offset) { - return getShortAtOffset(segment, offset, ByteOrder.nativeOrder()); - } - - /** - * Writes a short at given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setShortAtOffset(segment, offset, ByteOrder.nativeOrder(), value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @param value the short value to be written. - */ - @ForceInline - public static void setShortAtOffset(MemorySegment segment, long offset, short value) { - setShortAtOffset(segment, offset, ByteOrder.nativeOrder(), value); - } - - /** - * Reads an int from given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    getIntAtOffset(segment, offset, ByteOrder.nativeOrder());
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @return an int value read from {@code segment}. - */ - @ForceInline - public static int getIntAtOffset(MemorySegment segment, long offset) { - return getIntAtOffset(segment, offset, ByteOrder.nativeOrder()); - } - - /** - * Writes an int at given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setIntAtOffset(segment, offset, ByteOrder.nativeOrder(), value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @param value the int value to be written. - */ - @ForceInline - public static void setIntAtOffset(MemorySegment segment, long offset, int value) { - setIntAtOffset(segment, offset, ByteOrder.nativeOrder(), value); - } - - /** - * Reads a float from given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    getFloatAtOffset(segment, offset, ByteOrder.nativeOrder());
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @return a float value read from {@code segment}. - */ - @ForceInline - public static float getFloatAtOffset(MemorySegment segment, long offset) { - return getFloatAtOffset(segment, offset, ByteOrder.nativeOrder()); - } - - /** - * Writes a float at given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setFloatAtOffset(segment, offset, ByteOrder.nativeOrder(), value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @param value the float value to be written. - */ - @ForceInline - public static void setFloatAtOffset(MemorySegment segment, long offset, float value) { - setFloatAtOffset(segment, offset, ByteOrder.nativeOrder(), value); - } - - /** - * Reads a long from given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    getLongAtOffset(segment, offset, ByteOrder.nativeOrder());
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @return a long value read from {@code segment}. - */ - @ForceInline - public static long getLongAtOffset(MemorySegment segment, long offset) { - return getLongAtOffset(segment, offset, ByteOrder.nativeOrder()); - } - - /** - * Writes a long at given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setLongAtOffset(segment, offset, ByteOrder.nativeOrder(), value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @param value the long value to be written. - */ - @ForceInline - public static void setLongAtOffset(MemorySegment segment, long offset, long value) { - setLongAtOffset(segment, offset, ByteOrder.nativeOrder(), value); - } - - /** - * Reads a double from given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    getDoubleAtOffset(segment, offset, ByteOrder.nativeOrder());
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @return a double value read from {@code segment}. - */ - @ForceInline - public static double getDoubleAtOffset(MemorySegment segment, long offset) { - return getDoubleAtOffset(segment, offset, ByteOrder.nativeOrder()); - } - - /** - * Writes a double at given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setDoubleAtOffset(segment, offset, ByteOrder.nativeOrder(), value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @param value the double value to be written. - */ - @ForceInline - public static void setDoubleAtOffset(MemorySegment segment, long offset, double value) { - setDoubleAtOffset(segment, offset, ByteOrder.nativeOrder(), value); - } - - /** - * Reads a memory address from given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent (e.g. on a 64-bit platform) to the following code: - *

      {@code
      -    VarHandle handle = MemoryHandles.asAddressHandle(MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder()));
      -    MemoryAddress value = (MemoryAddress)handle.get(segment, offset);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @return a memory address read from {@code segment}. - */ - @ForceInline - public static MemoryAddress getAddressAtOffset(MemorySegment segment, long offset) { - Objects.requireNonNull(segment); - return (MemoryAddress)address_handle.get(segment, offset); - } - - /** - * Writes a memory address at given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent (e.g. on a 64-bit platform) to the following code: - *

      {@code
      -    VarHandle handle = MemoryHandles.asAddressHandle(MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder()));
      -    handle.set(segment, offset, value.address());
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @param value the memory address to be written (expressed as an {@link Addressable} instance). - */ - @ForceInline - public static void setAddressAtOffset(MemorySegment segment, long offset, Addressable value) { - Objects.requireNonNull(segment); - Objects.requireNonNull(value); - address_handle.set(segment, offset, value.address()); - } - - /** - * Reads a char from given segment and offset with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    VarHandle handle = MemoryHandles.varHandle(char.class, 1, order);
      -    char value = (char)handle.get(segment, offset);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @param order the specified byte order. - * @return a char value read from {@code segment}. - */ - @ForceInline - public static char getCharAtOffset(MemorySegment segment, long offset, ByteOrder order) { - Objects.requireNonNull(segment); - Objects.requireNonNull(order); - return (char)((order == ByteOrder.BIG_ENDIAN) ? char_BE_handle : char_LE_handle).get(segment, offset); - } - - /** - * Writes a char at given segment and offset with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    VarHandle handle = MemoryHandles.varHandle(char.class, 1, order);
      -    handle.set(segment, offset, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @param order the specified byte order. - * @param value the char value to be written. - */ - @ForceInline - public static void setCharAtOffset(MemorySegment segment, long offset, ByteOrder order, char value) { - Objects.requireNonNull(segment); - Objects.requireNonNull(order); - ((order == ByteOrder.BIG_ENDIAN) ? char_BE_handle : char_LE_handle).set(segment, offset, value); - } - - /** - * Reads a short from given segment and offset with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    VarHandle handle = MemoryHandles.varHandle(short.class, 1, order);
      -    short value = (short)handle.get(segment, offset);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @param order the specified byte order. - * @return a short value read from {@code segment}. - */ - @ForceInline - public static short getShortAtOffset(MemorySegment segment, long offset, ByteOrder order) { - Objects.requireNonNull(segment); - Objects.requireNonNull(order); - return (short)((order == ByteOrder.BIG_ENDIAN) ? short_BE_handle : short_LE_handle).get(segment, offset); - } - - /** - * Writes a short at given segment and offset with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    VarHandle handle = MemoryHandles.varHandle(short.class, 1, order);
      -    handle.set(segment, offset, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @param order the specified byte order. - * @param value the short value to be written. - */ - @ForceInline - public static void setShortAtOffset(MemorySegment segment, long offset, ByteOrder order, short value) { - Objects.requireNonNull(segment); - Objects.requireNonNull(order); - ((order == ByteOrder.BIG_ENDIAN) ? short_BE_handle : short_LE_handle).set(segment, offset, value); - } - - /** - * Reads an int from given segment and offset with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    VarHandle handle = MemoryHandles.varHandle(int.class, 1, order);
      -    int value = (int)handle.get(segment, offset);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @param order the specified byte order. - * @return an int value read from {@code segment}. - */ - @ForceInline - public static int getIntAtOffset(MemorySegment segment, long offset, ByteOrder order) { - Objects.requireNonNull(segment); - Objects.requireNonNull(order); - return (int)((order == ByteOrder.BIG_ENDIAN) ? int_BE_handle : int_LE_handle).get(segment, offset); - } - - /** - * Writes an int at given segment and offset with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    VarHandle handle = MemoryHandles.varHandle(int.class, 1, order);
      -    handle.set(segment, offset, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @param order the specified byte order. - * @param value the int value to be written. - */ - @ForceInline - public static void setIntAtOffset(MemorySegment segment, long offset, ByteOrder order, int value) { - Objects.requireNonNull(segment); - Objects.requireNonNull(order); - ((order == ByteOrder.BIG_ENDIAN) ? int_BE_handle : int_LE_handle).set(segment, offset, value); - } - - /** - * Reads a float from given segment and offset with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    VarHandle handle = MemoryHandles.varHandle(float.class, 1, order);
      -    float value = (float)handle.get(segment, offset);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @param order the specified byte order. - * @return a float value read from {@code segment}. - */ - @ForceInline - public static float getFloatAtOffset(MemorySegment segment, long offset, ByteOrder order) { - Objects.requireNonNull(segment); - Objects.requireNonNull(order); - return (float)((order == ByteOrder.BIG_ENDIAN) ? float_BE_handle : float_LE_handle).get(segment, offset); - } - - /** - * Writes a float at given segment and offset with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    VarHandle handle = MemoryHandles.varHandle(float.class, 1, order);
      -    handle.set(segment, offset, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @param order the specified byte order. - * @param value the float value to be written. - */ - @ForceInline - public static void setFloatAtOffset(MemorySegment segment, long offset, ByteOrder order, float value) { - Objects.requireNonNull(segment); - Objects.requireNonNull(order); - ((order == ByteOrder.BIG_ENDIAN) ? float_BE_handle : float_LE_handle).set(segment, offset, value); - } - - /** - * Reads a long from given segment and offset with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    VarHandle handle = MemoryHandles.varHandle(long.class, 1, order);
      -    long value = (long)handle.get(segment, offset);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @param order the specified byte order. - * @return a long value read from {@code segment}. - */ - @ForceInline - public static long getLongAtOffset(MemorySegment segment, long offset, ByteOrder order) { - Objects.requireNonNull(segment); - Objects.requireNonNull(order); - return (long)((order == ByteOrder.BIG_ENDIAN) ? long_BE_handle : long_LE_handle).get(segment, offset); - } - - /** - * Writes a long at given segment and offset with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    VarHandle handle = MemoryHandles.varHandle(long.class, 1, order);
      -    handle.set(segment, offset, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @param order the specified byte order. - * @param value the long value to be written. - */ - @ForceInline - public static void setLongAtOffset(MemorySegment segment, long offset, ByteOrder order, long value) { - Objects.requireNonNull(segment); - Objects.requireNonNull(order); - ((order == ByteOrder.BIG_ENDIAN) ? long_BE_handle : long_LE_handle).set(segment, offset, value); - } - - /** - * Reads a double from given segment and offset with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    VarHandle handle = MemoryHandles.varHandle(double.class, 1, order);
      -    double value = (double)handle.get(segment, offset);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @param order the specified byte order. - * @return a double value read from {@code segment}. - */ - @ForceInline - public static double getDoubleAtOffset(MemorySegment segment, long offset, ByteOrder order) { - Objects.requireNonNull(segment); - Objects.requireNonNull(order); - return (double)((order == ByteOrder.BIG_ENDIAN) ? double_BE_handle : double_LE_handle).get(segment, offset); - } - - /** - * Writes a double at given segment and offset with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    VarHandle handle = MemoryHandles.varHandle(double.class, 1, order);
      -    handle.set(segment, offset, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. - * @param order the specified byte order. - * @param value the double value to be written. - */ - @ForceInline - public static void setDoubleAtOffset(MemorySegment segment, long offset, ByteOrder order, double value) { - Objects.requireNonNull(segment); - Objects.requireNonNull(order); - ((order == ByteOrder.BIG_ENDIAN) ? double_BE_handle : double_LE_handle).set(segment, offset, value); - } - - /** - * Reads a byte from given segment. - *

      - * This is equivalent to the following code: - *

      {@code
      -    byte value = getByteAtOffset(segment, 0L);
      -     * }
      - * - * @param segment the segment to be dereferenced. - * @return a byte value read from {@code segment}. - */ - @ForceInline - public static byte getByte(MemorySegment segment) { - return getByteAtOffset(segment, 0L); - } - - /** - * Writes a byte at given segment. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setByteAtOffset(segment, 0L, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param value the byte value to be written. - */ - @ForceInline - public static void setByte(MemorySegment segment, byte value) { - setByteAtOffset(segment, 0L, value); - } - - /** - * Reads a char from given segment, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    char value = getCharAtOffset(segment, 0L);
      -     * }
      - * @param segment the segment to be dereferenced. - * @return a char value read from {@code segment}. - */ - @ForceInline - public static char getChar(MemorySegment segment) { - return getCharAtOffset(segment, 0L); - } - - /** - * Writes a char at given segment, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setCharAtOffset(segment, 0L, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param value the char value to be written. - */ - @ForceInline - public static void setChar(MemorySegment segment, char value) { - setCharAtOffset(segment, 0L, value); - } - - /** - * Reads a short from given segment, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    short value = getShortAtOffset(segment, 0L);
      -     * }
      - * @param segment the segment to be dereferenced. - * @return a short value read from {@code segment}. - */ - @ForceInline - public static short getShort(MemorySegment segment) { - return getShortAtOffset(segment, 0L); - } - - /** - * Writes a short at given segment, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setShortAtOffset(segment, 0L, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param value the short value to be written. - */ - @ForceInline - public static void setShort(MemorySegment segment, short value) { - setShortAtOffset(segment, 0L, value); - } - - /** - * Reads an int from given segment, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    int value = getIntAtOffset(segment, 0L);
      -     * }
      - * @param segment the segment to be dereferenced. - * @return an int value read from {@code segment}. - */ - @ForceInline - public static int getInt(MemorySegment segment) { - return getIntAtOffset(segment, 0L); - } - - /** - * Writes an int at given segment, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setIntAtOffset(segment, 0L, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param value the int value to be written. - */ - @ForceInline - public static void setInt(MemorySegment segment, int value) { - setIntAtOffset(segment, 0L, value); - } - - /** - * Reads a float from given segment, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    float value = getFloatAtOffset(segment, 0L);
      -     * }
      - * @param segment the segment to be dereferenced. - * @return a float value read from {@code segment}. - */ - @ForceInline - public static float getFloat(MemorySegment segment) { - return getFloatAtOffset(segment, 0L); - } - - /** - * Writes a float at given segment, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setFloatAtOffset(segment, 0L, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param value the float value to be written. - */ - @ForceInline - public static void setFloat(MemorySegment segment, float value) { - setFloatAtOffset(segment, 0L, value); - } - - /** - * Reads a long from given segment, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    long value = getLongAtOffset(segment, 0L);
      -     * }
      - * @param segment the segment to be dereferenced. - * @return a long value read from {@code segment}. - */ - @ForceInline - public static long getLong(MemorySegment segment) { - return getLongAtOffset(segment, 0L); - } - - /** - * Writes a long at given segment, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setLongAtOffset(segment, 0L, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param value the long value to be written. - */ - @ForceInline - public static void setLong(MemorySegment segment, long value) { - setLongAtOffset(segment, 0L, value); - } - - /** - * Reads a double from given segment, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    double value = getDoubleAtOffset(segment, 0L);
      -     * }
      - * @param segment the segment to be dereferenced. - * @return a double value read from {@code segment}. - */ - @ForceInline - public static double getDouble(MemorySegment segment) { - return getDoubleAtOffset(segment, 0L); - } - - /** - * Writes a double at given segment, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setDoubleAtOffset(segment, 0L, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param value the double value to be written. - */ - @ForceInline - public static void setDouble(MemorySegment segment, double value) { - setDoubleAtOffset(segment, 0L, value); - } - - /** - * Reads a memory address from given segment, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    MemoryAddress value = getAddressAtOffset(segment, 0L);
      -     * }
      - * @param segment the segment to be dereferenced. - * @return a memory address read from {@code segment}. - */ - @ForceInline - public static MemoryAddress getAddress(MemorySegment segment) { - return getAddressAtOffset(segment, 0L); - } - - /** - * Writes a memory address at given segment, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setAddressAtOffset(segment, 0L, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param value the memory address to be written (expressed as an {@link Addressable} instance). - */ - @ForceInline - public static void setAddress(MemorySegment segment, Addressable value) { - setAddressAtOffset(segment, 0L, value); - } - - /** - * Reads a char from given segment, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    char value = getCharAtOffset(segment, 0L, order);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param order the specified byte order. - * @return a char value read from {@code segment}. - */ - @ForceInline - public static char getChar(MemorySegment segment, ByteOrder order) { - return getCharAtOffset(segment, 0L, order); - } - - /** - * Writes a char at given segment, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setCharAtOffset(segment, 0L, order, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param order the specified byte order. - * @param value the char value to be written. - */ - @ForceInline - public static void setChar(MemorySegment segment, ByteOrder order, char value) { - setCharAtOffset(segment, 0L, order, value); - } - - /** - * Reads a short from given segment, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    short value = getShortAtOffset(segment, 0L, order);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param order the specified byte order. - * @return a short value read from {@code segment}. - */ - @ForceInline - public static short getShort(MemorySegment segment, ByteOrder order) { - return getShortAtOffset(segment, 0L, order); - } - - /** - * Writes a short at given segment, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setShortAtOffset(segment, 0L, order, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param order the specified byte order. - * @param value the short value to be written. - */ - @ForceInline - public static void setShort(MemorySegment segment, ByteOrder order, short value) { - setShortAtOffset(segment, 0L, order, value); - } - - /** - * Reads an int from given segment, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    int value = getIntAtOffset(segment, 0L, order);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param order the specified byte order. - * @return an int value read from {@code segment}. - */ - @ForceInline - public static int getInt(MemorySegment segment, ByteOrder order) { - return getIntAtOffset(segment, 0L, order); - } - - /** - * Writes an int at given segment, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setIntAtOffset(segment, 0L, order, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param order the specified byte order. - * @param value the int value to be written. - */ - @ForceInline - public static void setInt(MemorySegment segment, ByteOrder order, int value) { - setIntAtOffset(segment, 0L, order, value); - } - - /** - * Reads a float from given segment, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    float value = getFloatAtOffset(segment, 0L, order);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param order the specified byte order. - * @return a float value read from {@code segment}. - */ - @ForceInline - public static float getFloat(MemorySegment segment, ByteOrder order) { - return getFloatAtOffset(segment, 0L, order); - } - - /** - * Writes a float at given segment, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setFloatAtOffset(segment, 0L, order, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param order the specified byte order. - * @param value the float value to be written. - */ - @ForceInline - public static void setFloat(MemorySegment segment, ByteOrder order, float value) { - setFloatAtOffset(segment, 0L, order, value); - } - - /** - * Reads a long from given segment, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    long value = getLongAtOffset(segment, 0L, order);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param order the specified byte order. - * @return a long value read from {@code segment}. - */ - @ForceInline - public static long getLong(MemorySegment segment, ByteOrder order) { - return getLongAtOffset(segment, 0L, order); - } - - /** - * Writes a long at given segment, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setLongAtOffset(segment, 0L, order, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param order the specified byte order. - * @param value the long value to be written. - */ - @ForceInline - public static void setLong(MemorySegment segment, ByteOrder order, long value) { - setLongAtOffset(segment, 0L, order, value); - } - - /** - * Reads a double from given segment, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    double value = getDoubleAtOffset(segment, 0L, order);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param order the specified byte order. - * @return a double value read from {@code segment}. - */ - @ForceInline - public static double getDouble(MemorySegment segment, ByteOrder order) { - return getDoubleAtOffset(segment, 0L, order); - } - - /** - * Writes a double at given segment, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setDoubleAtOffset(segment, 0L, order, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param order the specified byte order. - * @param value the double value to be written. - */ - @ForceInline - public static void setDouble(MemorySegment segment, ByteOrder order, double value) { - setDoubleAtOffset(segment, 0L, order, value); - } - - /** - * Reads a char from given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    char value = getCharAtOffset(segment, 2 * index);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 2)}. - * @return a char value read from {@code segment} at the element index specified by {@code index}. - */ - @ForceInline - public static char getCharAtIndex(MemorySegment segment, long index) { - return getCharAtOffset(segment, scale(segment, index, 2)); - } - - /** - * Writes a char at given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setCharAtOffset(segment, 2 * index, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 2)}. - * @param value the char value to be written. - */ - @ForceInline - public static void setCharAtIndex(MemorySegment segment, long index, char value) { - setCharAtOffset(segment, scale(segment, index, 2), value); - } - - /** - * Reads a short from given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    short value = getShortAtOffset(segment, 2 * index);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 2)}. - * @return a short value read from {@code segment} at the element index specified by {@code index}. - */ - @ForceInline - public static short getShortAtIndex(MemorySegment segment, long index) { - return getShortAtOffset(segment, scale(segment, index, 2)); - } - - /** - * Writes a short at given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setShortAtOffset(segment, 2 * index, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 2)}. - * @param value the short value to be written. - */ - @ForceInline - public static void setShortAtIndex(MemorySegment segment, long index, short value) { - setShortAtOffset(segment, scale(segment, index, 2), value); - } - - /** - * Reads an int from given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    int value = getIntAtOffset(segment, 4 * index);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 4)}. - * @return an int value read from {@code segment} at the element index specified by {@code index}. - */ - @ForceInline - public static int getIntAtIndex(MemorySegment segment, long index) { - return getIntAtOffset(segment, scale(segment, index, 4)); - } - - /** - * Writes an int at given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setIntAtOffset(segment, 4 * index, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 4)}. - * @param value the int value to be written. - */ - @ForceInline - public static void setIntAtIndex(MemorySegment segment, long index, int value) { - setIntAtOffset(segment, scale(segment, index, 4), value); - } - - /** - * Reads a float from given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    float value = getFloatAtOffset(segment, 4 * index);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 4)}. - * @return a float value read from {@code segment} at the element index specified by {@code index}. - */ - @ForceInline - public static float getFloatAtIndex(MemorySegment segment, long index) { - return getFloatAtOffset(segment, scale(segment, index, 4)); - } - - /** - * Writes a float at given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setFloatAtOffset(segment, 4 * index, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 4)}. - * @param value the float value to be written. - */ - @ForceInline - public static void setFloatAtIndex(MemorySegment segment, long index, float value) { - setFloatAtOffset(segment, scale(segment, index, 4), value); - } - - /** - * Reads a long from given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    return getLongAtOffset(segment, 8 * index);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 8)}. - * @return a long value read from {@code segment} at the element index specified by {@code index}. - */ - @ForceInline - public static long getLongAtIndex(MemorySegment segment, long index) { - return getLongAtOffset(segment, scale(segment, index, 8)); - } - - /** - * Writes a long at given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setLongAtOffset(segment, 8 * index, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 8)}. - * @param value the long value to be written. - */ - @ForceInline - public static void setLongAtIndex(MemorySegment segment, long index, long value) { - setLongAtOffset(segment, scale(segment, index, 8), value); - } - - /** - * Reads a double from given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    return getDoubleAtOffset(segment, 8 * index);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 8)}. - * @return a double value read from {@code segment} at the element index specified by {@code index}. - */ - @ForceInline - public static double getDoubleAtIndex(MemorySegment segment, long index) { - return getDoubleAtOffset(segment, scale(segment, index, 8)); - } - - /** - * Writes a double at given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setDoubleAtOffset(segment, 8 * index, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 8)}. - * @param value the double value to be written. - */ - @ForceInline - public static void setDoubleAtIndex(MemorySegment segment, long index, double value) { - setDoubleAtOffset(segment, scale(segment, index, 8), value); - } - - /** - * Reads a memory address from given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    return getAddressAtOffset(segment, index * MemoryLayouts.ADDRESS.byteSize());
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 8)}. - * @return a memory address read from {@code segment} at the element index specified by {@code index}. - */ - @ForceInline - public static MemoryAddress getAddressAtIndex(MemorySegment segment, long index) { - return getAddressAtOffset(segment, scale(segment, index, (int)MemoryLayouts.ADDRESS.byteSize())); - } - - /** - * Writes a memory address at given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setAddressAtOffset(segment, index * MemoryLayouts.ADDRESS.byteSize(), value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 8)}. - * @param value the memory address to be written (expressed as an {@link Addressable} instance). - */ - @ForceInline - public static void setAddressAtIndex(MemorySegment segment, long index, Addressable value) { - setAddressAtOffset(segment, scale(segment, index, (int)MemoryLayouts.ADDRESS.byteSize()), value); - } - - /** - * Reads a char from given segment and element index, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    char value = getCharAtOffset(segment, 2 * index, order);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 2)}. - * @param order the specified byte order. - * @return a char value read from {@code segment} at the element index specified by {@code index}. - */ - @ForceInline - public static char getCharAtIndex(MemorySegment segment, long index, ByteOrder order) { - return getCharAtOffset(segment, scale(segment, index, 2), order); - } - - /** - * Writes a char at given segment and element index, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setCharAtOffset(segment, 2 * index, order, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 2)}. - * @param order the specified byte order. - * @param value the char value to be written. - */ - @ForceInline - public static void setCharAtIndex(MemorySegment segment, long index, ByteOrder order, char value) { - setCharAtOffset(segment, scale(segment, index, 2), order, value); - } - - /** - * Reads a short from given segment and element index, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    short value = getShortAtOffset(segment, 2 * index, order);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 2)}. - * @param order the specified byte order. - * @return a short value read from {@code segment} at the element index specified by {@code index}. - */ - @ForceInline - public static short getShortAtIndex(MemorySegment segment, long index, ByteOrder order) { - return getShortAtOffset(segment, scale(segment, index, 2), order); - } - - /** - * Writes a short at given segment and element index, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setShortAtOffset(segment, 2 * index, order, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 2)}. - * @param order the specified byte order. - * @param value the short value to be written. - */ - @ForceInline - public static void setShortAtIndex(MemorySegment segment, long index, ByteOrder order, short value) { - setShortAtOffset(segment, scale(segment, index, 2), order, value); - } - - /** - * Reads an int from given segment and element index, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    int value = getIntAtOffset(segment, 4 * index, order);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 4)}. - * @param order the specified byte order. - * @return an int value read from {@code segment} at the element index specified by {@code index}. - */ - @ForceInline - public static int getIntAtIndex(MemorySegment segment, long index, ByteOrder order) { - return getIntAtOffset(segment, scale(segment, index, 4), order); - } - - /** - * Writes an int at given segment and element index, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setIntAtOffset(segment, 4 * index, order, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 4)}. - * @param order the specified byte order. - * @param value the int value to be written. - */ - @ForceInline - public static void setIntAtIndex(MemorySegment segment, long index, ByteOrder order, int value) { - setIntAtOffset(segment, scale(segment, index, 4), order, value); - } - - /** - * Reads a float from given segment and element index, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    float value = getFloatAtOffset(segment, 4 * index, order);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 4)}. - * @param order the specified byte order. - * @return a float value read from {@code segment} at the element index specified by {@code index}. - */ - @ForceInline - public static float getFloatAtIndex(MemorySegment segment, long index, ByteOrder order) { - return getFloatAtOffset(segment, scale(segment, index, 4), order); - } - - /** - * Writes a float at given segment and element index, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setFloatAtOffset(segment, 4 * index, order, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 4)}. - * @param order the specified byte order. - * @param value the float value to be written. - */ - @ForceInline - public static void setFloatAtIndex(MemorySegment segment, long index, ByteOrder order, float value) { - setFloatAtOffset(segment, scale(segment, index, 4), order, value); - } - - /** - * Reads a long from given segment and element index, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    return getLongAtOffset(segment, 8 * index, order);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 8)}. - * @param order the specified byte order. - * @return a long value read from {@code segment} at the element index specified by {@code index}. - */ - @ForceInline - public static long getLongAtIndex(MemorySegment segment, long index, ByteOrder order) { - return getLongAtOffset(segment, scale(segment, index, 8), order); - } - - /** - * Writes a long at given segment and element index, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setLongAtOffset(segment, 8 * index, order, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 8)}. - * @param order the specified byte order. - * @param value the long value to be written. - */ - @ForceInline - public static void setLongAtIndex(MemorySegment segment, long index, ByteOrder order, long value) { - setLongAtOffset(segment, scale(segment, index, 8), order, value); - } - - /** - * Reads a double from given segment and element index, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    return getDoubleAtOffset(segment, 8 * index, order);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 8)}. - * @param order the specified byte order. - * @return a double value read from {@code segment} at the element index specified by {@code index}. - */ - @ForceInline - public static double getDoubleAtIndex(MemorySegment segment, long index, ByteOrder order) { - return getDoubleAtOffset(segment, scale(segment, index, 8), order); - } - - /** - * Writes a double at given segment and element index, with given byte order. - *

      - * This is equivalent to the following code: - *

      {@code
      -    setDoubleAtOffset(segment, 8 * index, order, value);
      -     * }
      - * @param segment the segment to be dereferenced. - * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 8)}. - * @param order the specified byte order. - * @param value the double value to be written. - */ - @ForceInline - public static void setDoubleAtIndex(MemorySegment segment, long index, ByteOrder order, double value) { - setDoubleAtOffset(segment, scale(segment, index, 8), order, value); - } - - @ForceInline - private static long scale(MemorySegment address, long index, int size) { - return MemorySegmentProxy.multiplyOffsets(index, size, (MemorySegmentProxy)address); - } -} diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAddress.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAddress.java index 08290fc911f09..ce1a77c41b3e3 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAddress.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAddress.java @@ -27,33 +27,45 @@ package jdk.incubator.foreign; import jdk.internal.foreign.MemoryAddressImpl; -import jdk.internal.ref.CleanerFactory; import jdk.internal.reflect.CallerSensitive; -import java.lang.ref.Cleaner; +import java.nio.ByteOrder; /** - * A memory address models a reference into a memory location. Memory addresses are typically obtained using the - * {@link MemorySegment#address()} method, and can refer to either off-heap or on-heap memory. Off-heap memory - * addresses are referred to as native memory addresses (see {@link #isNative()}). Native memory addresses - * allow clients to obtain a raw memory address (expressed as a long value) which can then be used e.g. when interacting - * with native code. - *

      - * Given an address, it is possible to compute its offset relative to a given segment, which can be useful - * when performing memory dereference operations using a memory access var handle (see {@link MemoryHandles}). - *

      - * A memory address is associated with a {@linkplain ResourceScope resource scope}; the resource scope determines the - * lifecycle of the memory address, and whether the address can be used from multiple threads. Memory addresses - * obtained from {@linkplain #ofLong(long) numeric values}, or from native code, are associated with the - * {@linkplain ResourceScope#globalScope() global resource scope}. Memory addresses obtained from segments - * are associated with the same scope as the segment from which they have been obtained. + * A memory address models a reference into a memory location. Memory addresses are typically obtained in three ways: + *

        + *
      • By calling {@link Addressable#address()} on an instance of type {@link Addressable} (e.g. a memory segment);
      • + *
      • By invoking a {@linkplain CLinker#downcallHandle(FunctionDescriptor) downcall method handle} which returns a pointer;
      • + *
      • By reading an address from memory, e.g. via {@link MemorySegment#get(ValueLayout.OfAddress, long)}.
      • + *
      + * A memory address is backed by a raw machine pointer, expressed as a {@linkplain #toRawLongValue() long value}. + * + *

      Dereference

      + * + * A memory address can be read or written using various methods provided in this class (e.g. {@link #get(ValueLayout.OfInt, long)}). + * Each dereference method takes a {@linkplain jdk.incubator.foreign.ValueLayout value layout}, which specifies the size, + * alignment constraints, byte order as well as the Java type associated with the dereference operation, and an offset. + * For instance, to read an int from a segment, using {@link ByteOrder#nativeOrder() default endianness}, the following code can be used: + *
      {@code
      +MemoryAddress address = ...
      +int value = address.get(ValueLayout.JAVA_INT, 0);
      + * }
      + * + * If the value to be read is stored in memory using {@link ByteOrder#BIG_ENDIAN big-endian} encoding, the dereference operation + * can be expressed as follows: + *
      {@code
      +MemoryAddress address = ...
      +int value = address.get(ValueLayout.JAVA_INT.withOrder(BIG_ENDIAN), 0);
      + * }
      + * + * All the dereference methods in this class are restricted: since + * a memory address does not feature temporal nor spatial bounds, the runtime has no way to check the correctness + * of the memory dereference operation. *

      * All implementations of this interface must be value-based; * programmers should treat instances that are {@linkplain #equals(Object) equal} as interchangeable and should not * use instances for synchronization, or unpredictable behavior may occur. For example, in a future release, * synchronization may fail. The {@code equals} method should be used for comparisons. - *

      - * Non-platform classes should not implement {@linkplain MemoryAddress} directly. * *

      Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null} * elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown.

      @@ -63,10 +75,11 @@ */ public sealed interface MemoryAddress extends Addressable permits MemoryAddressImpl { - @Override - default MemoryAddress address() { - return this; - } + /** + * Returns the raw long value associated with this memory address. + * @return The raw long value associated with this memory address. + */ + long toRawLongValue(); /** * Creates a new memory address with given offset (in bytes), which might be negative, from current one. @@ -76,155 +89,651 @@ default MemoryAddress address() { MemoryAddress addOffset(long offset); /** - * Returns the resource scope associated with this memory address. - * @return the resource scope associated with this memory address. + * Reads a UTF-8 encoded, null-terminated string from this address and offset. + *

      + * 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. + *

      + * 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. + * + * @param offset offset in bytes (relative to this address). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}. + * @return a Java string constructed from the bytes read from the given starting address ({@code toRowLongValue() + offset}) + * up to (but not including) the first {@code '\0'} terminator character (assuming one is found). + * @throws IllegalArgumentException if the size of the native string is greater than the largest string supported by the platform. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + String getUtf8String(long offset); + + /** + * Writes the given string to this address at given offset, converting it to a null-terminated byte sequence using UTF-8 encoding. + *

      + * 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 address). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}. + * @param str the Java string to be written at this address. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + void setUtf8String(long offset, String str); + + /** + * Compares the specified object with this address for equality. Returns {@code true} if and only if the specified + * object is also an address, and it refers to the same memory location as this address. + * + * @param that the object to be compared for equality with this address. + * @return {@code true} if the specified object is equal to this address. + */ + @Override + boolean equals(Object that); + + /** + * Returns the hash code value for this address. + * @return the hash code value for this address. + */ + @Override + int hashCode(); + + /** + * The native memory address instance modelling the {@code NULL} address. + */ + MemoryAddress NULL = new MemoryAddressImpl(0L); + + /** + * Obtain a native memory address instance from given long address. + * @param value the long address. + * @return the new memory address instance. + */ + static MemoryAddress ofLong(long value) { + return value == 0 ? + NULL : + new MemoryAddressImpl(value); + } + + /** + * Reads a byte from this address and offset with given 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. + * + * @param layout the layout of the memory region to be read. + * @param offset offset in bytes (relative to this address). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}. + * @return a byte value read from this address. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. */ - ResourceScope scope(); + @CallerSensitive + byte get(ValueLayout.OfByte layout, long offset); /** - * Returns the offset of this memory address into the given segment. More specifically, if both the segment's - * base address and this address are native addresses, the result is computed as - * {@code this.toRawLongValue() - segment.address().toRawLongValue()}. Otherwise, if both addresses in the form - * {@code (B, O1)}, {@code (B, O2)}, where {@code B} is the same base heap object and {@code O1}, {@code O2} - * are byte offsets (relative to the base object) associated with this address and the segment's base address, - * the result is computed as {@code O1 - O2}. + * Writes a byte to this address instance and offset with given layout. *

      - * If the segment's base address and this address are both heap addresses, but with different base objects, the result is undefined - * and an exception is thrown. Similarly, if the segment's base address is an heap address (resp. off-heap) and - * this address is an off-heap (resp. heap) address, the result is undefined and an exception is thrown. - * Otherwise, the result is a byte offset {@code SO}. If this address falls within the - * spatial bounds of the given segment, then {@code 0 <= SO < segment.byteSize()}; otherwise, {@code SO < 0 || SO > segment.byteSize()}. - * @return the offset of this memory address into the given segment. - * @param segment the segment relative to which this address offset should be computed - * @throws IllegalArgumentException if {@code segment} is not compatible with this address; this can happen, for instance, - * when {@code segment} models an heap memory region, while this address is a {@linkplain #isNative() native} address. + * 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. + * + * @param layout the layout of the memory region to be written. + * @param offset offset in bytes (relative to this address). The final address of this write operation can be expressed as {@code toRowLongValue() + offset}. + * @param value the byte value to be written. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. */ - long segmentOffset(MemorySegment segment); + @CallerSensitive + void set(ValueLayout.OfByte layout, long offset, byte value); /** - Returns a new native memory segment with given size and resource scope (replacing the scope already associated - * with this address), and whose base address is this address. This method can be useful when interacting with custom - * native memory sources (e.g. custom allocators), where an address to some - * underlying memory region is typically obtained from native code (often as a plain {@code long} value). - * The returned segment is not read-only (see {@link MemorySegment#isReadOnly()}), and is associated with the - * provided resource scope. + * Reads a boolean from this address and offset with given layout. *

      - * Clients should ensure that the address and bounds refers to a valid region of memory that is accessible for reading and, - * if appropriate, writing; an attempt to access an invalid memory location from Java code will either return an arbitrary value, - * have no visible effect, or cause an unspecified exception to be thrown. + * 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. + * + * @param layout the layout of the memory region to be read. + * @param offset offset in bytes (relative to this address). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}. + * @return a boolean value read from this address. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + boolean get(ValueLayout.OfBoolean layout, long offset); + + /** + * Writes a boolean to this address instance and offset with given layout. *

      - * This method is equivalent to the following code: - *

      {@code
      -    asSegment(byteSize, null, scope);
      -     * }
      + * 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. + * + * @param layout the layout of the memory region to be written. + * @param offset offset in bytes (relative to this address). The final address of this write operation can be expressed as {@code toRowLongValue() + offset}. + * @param value the boolean value to be written. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + void set(ValueLayout.OfBoolean layout, long offset, boolean value); + + /** + * Reads a char from this address and offset with given 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. * - * @param bytesSize the desired size. - * @param scope the native segment scope. - * @return a new native memory segment with given base address, size and scope. - * @throws IllegalArgumentException if {@code bytesSize <= 0}. - * @throws IllegalStateException if either the scope associated with this address or the provided scope - * have been already closed, or if access occurs from a thread other than the thread owning either - * scopes. - * @throws UnsupportedOperationException if this address is not a {@linkplain #isNative() native} address. + * @param layout the layout of the memory region to be read. + * @param offset offset in bytes (relative to this address). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}. + * @return a char value read from this address. * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. */ @CallerSensitive - MemorySegment asSegment(long bytesSize, ResourceScope scope); + char get(ValueLayout.OfChar layout, long offset); /** - * Returns a new native memory segment with given size and resource scope (replacing the scope already associated - * with this address), and whose base address is this address. This method can be useful when interacting with custom - * native memory sources (e.g. custom allocators), where an address to some - * underlying memory region is typically obtained from native code (often as a plain {@code long} value). - * The returned segment is associated with the provided resource scope. + * Writes a char to this address instance and offset with given layout. *

      - * Clients should ensure that the address and bounds refers to a valid region of memory that is accessible for reading and, - * if appropriate, writing; an attempt to access an invalid memory location from Java code will either return an arbitrary value, - * have no visible effect, or cause an unspecified exception to be thrown. + * 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. + * + * @param layout the layout of the memory region to be written. + * @param offset offset in bytes (relative to this address). The final address of this write operation can be expressed as {@code toRowLongValue() + offset}. + * @param value the char value to be written. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + void set(ValueLayout.OfChar layout, long offset, char value); + + /** + * Reads a short from this address and offset with given layout. *

      - * Calling {@link ResourceScope#close()} on the scope associated with the returned segment will result in calling - * the provided cleanup action (if any). + * 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. + * + * @param layout the layout of the memory region to be read. + * @param offset offset in bytes (relative to this address). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}. + * @return a short value read from this address. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + short get(ValueLayout.OfShort layout, long offset); + + /** + * Writes a short to this address instance and offset with given 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. * - * @param bytesSize the desired size. - * @param cleanupAction the cleanup action; can be {@code null}. - * @param scope the native segment scope. - * @return a new native memory segment with given base address, size and scope. - * @throws IllegalArgumentException if {@code bytesSize <= 0}. - * @throws IllegalStateException if either the scope associated with this address or the provided scope - * have been already closed, or if access occurs from a thread other than the thread owning either - * scopes. - * @throws UnsupportedOperationException if this address is not a {@linkplain #isNative() native} address. + * @param layout the layout of the memory region to be written. + * @param offset offset in bytes (relative to this address). The final address of this write operation can be expressed as {@code toRowLongValue() + offset}. + * @param value the short value to be written. * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. */ @CallerSensitive - MemorySegment asSegment(long bytesSize, Runnable cleanupAction, ResourceScope scope); + void set(ValueLayout.OfShort layout, long offset, short value); /** - * Is this an off-heap memory address? - * @return true, if this is an off-heap memory address. + * Reads an int from this address and offset with given 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. + * + * @param layout the layout of the memory region to be read. + * @param offset offset in bytes (relative to this address). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}. + * @return an int value read from this address. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. */ - boolean isNative(); + @CallerSensitive + int get(ValueLayout.OfInt layout, long offset); /** - * Returns the raw long value associated with this native memory address. - * @return The raw long value associated with this native memory address. - * @throws UnsupportedOperationException if this memory address is not a {@linkplain #isNative() native} address. - * @throws IllegalStateException if the scope associated with this segment has been already closed, - * or if access occurs from a thread other than the thread owning either segment. + * Writes an int to this address instance and offset with given 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. + * + * @param layout the layout of the memory region to be written. + * @param offset offset in bytes (relative to this address). The final address of this write operation can be expressed as {@code toRowLongValue() + offset}. + * @param value the int value to be written. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. */ - long toRawLongValue(); + @CallerSensitive + void set(ValueLayout.OfInt layout, long offset, int value); /** - * Compares the specified object with this address for equality. Returns {@code true} if and only if the specified - * object is also an address, and it refers to the same memory location as this address. + * Reads a float from this address and offset with given 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. * - * @apiNote two addresses might be considered equal despite their associated resource scopes differ. This - * can happen, for instance, if the same memory address is used to create memory segments with different - * scopes (using {@link #asSegment(long, ResourceScope)}), and the base address of the resulting segments is - * then compared. + * @param layout the layout of the memory region to be read. + * @param offset offset in bytes (relative to this address). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}. + * @return a float value read from this address. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + float get(ValueLayout.OfFloat layout, long offset); + + /** + * Writes a float to this address instance and offset with given 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. * - * @param that the object to be compared for equality with this address. - * @return {@code true} if the specified object is equal to this address. + * @param layout the layout of the memory region to be written. + * @param offset offset in bytes (relative to this address). The final address of this write operation can be expressed as {@code toRowLongValue() + offset}. + * @param value the float value to be written. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. */ - @Override - boolean equals(Object that); + @CallerSensitive + void set(ValueLayout.OfFloat layout, long offset, float value); /** - * Returns the hash code value for this address. - * @return the hash code value for this address. + * Reads a long from this address and offset with given 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. + * + * @param layout the layout of the memory region to be read. + * @param offset offset in bytes (relative to this address). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}. + * @return a long value read from this address. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. */ - @Override - int hashCode(); + @CallerSensitive + long get(ValueLayout.OfLong layout, long offset); /** - * The native memory address instance modelling the {@code NULL} address, associated - * with the {@linkplain ResourceScope#globalScope() global} resource scope. + * Writes a long to this address instance and offset with given 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. + * + * @param layout the layout of the memory region to be written. + * @param offset offset in bytes (relative to this address). The final address of this write operation can be expressed as {@code toRowLongValue() + offset}. + * @param value the long value to be written. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. */ - MemoryAddress NULL = new MemoryAddressImpl(null, 0L); + @CallerSensitive + void set(ValueLayout.OfLong layout, long offset, long value); /** - * Obtain a native memory address instance from given long address. The returned address is associated - * with the {@linkplain ResourceScope#globalScope() global} resource scope. - * @param value the long address. - * @return the new memory address instance. + * Reads a double from this address and offset with given 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. + * + * @param layout the layout of the memory region to be read. + * @param offset offset in bytes (relative to this address). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}. + * @return a double value read from this address. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. */ - static MemoryAddress ofLong(long value) { - return value == 0 ? - NULL : - new MemoryAddressImpl(null, value); - } + @CallerSensitive + double get(ValueLayout.OfDouble layout, long offset); + + /** + * Writes a double to this address instance and offset with given 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. + * + * @param layout the layout of the memory region to be written. + * @param offset offset in bytes (relative to this address). The final address of this write operation can be expressed as {@code toRowLongValue() + offset}. + * @param value the double value to be written. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + void set(ValueLayout.OfDouble layout, long offset, double value); + + /** + * Reads an address from this address and offset with given 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. + * + * @param layout the layout of the memory region to be read. + * @param offset offset in bytes (relative to this address). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}. + * @return an address value read from this address. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + MemoryAddress get(ValueLayout.OfAddress layout, long offset); + + /** + * Writes an address to this address instance and offset with given 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. + * + * @param layout the layout of the memory region to be written. + * @param offset offset in bytes (relative to this address). The final address of this write operation can be expressed as {@code toRowLongValue() + offset}. + * @param value the address value to be written. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + void set(ValueLayout.OfAddress layout, long offset, Addressable value); + + /** + * Reads a char from this address and index, scaled by given layout size. + *

      + * 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. + * + * @param layout the layout of the memory region to be read. + * @param index index in bytes (relative to this address). The final address of this read operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}. + * @return a char value read from this address. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + char getAtIndex(ValueLayout.OfChar layout, long index); + + /** + * Writes a char to this address instance and index, scaled by given layout size. + *

      + * 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. + * + * @param layout the layout of the memory region to be written. + * @param index index in bytes (relative to this address). The final address of this write operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}. + * @param value the char value to be written. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + void setAtIndex(ValueLayout.OfChar layout, long index, char value); + + /** + * Reads a short from this address and index, scaled by given layout size. + *

      + * 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. + * + * @param layout the layout of the memory region to be read. + * @param index index in bytes (relative to this address). The final address of this read operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}. + * @return a short value read from this address. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + short getAtIndex(ValueLayout.OfShort layout, long index); + + /** + * Writes a short to this address instance and index, scaled by given layout size. + *

      + * 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. + * + * @param layout the layout of the memory region to be written. + * @param index index in bytes (relative to this address). The final address of this write operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}. + * @param value the short value to be written. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + void setAtIndex(ValueLayout.OfShort layout, long index, short value); + + /** + * Reads an int from this address and index, scaled by given layout size. + *

      + * 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. + * + * @param layout the layout of the memory region to be read. + * @param index index in bytes (relative to this address). The final address of this read operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}. + * @return an int value read from this address. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + int getAtIndex(ValueLayout.OfInt layout, long index); + + /** + * Writes an int to this address instance and index, scaled by given layout size. + *

      + * 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. + * + * @param layout the layout of the memory region to be written. + * @param index index in bytes (relative to this address). The final address of this write operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}. + * @param value the int value to be written. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + void setAtIndex(ValueLayout.OfInt layout, long index, int value); + + /** + * Reads a float from this address and index, scaled by given layout size. + *

      + * 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. + * + * @param layout the layout of the memory region to be read. + * @param index index in bytes (relative to this address). The final address of this read operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}. + * @return a float value read from this address. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + float getAtIndex(ValueLayout.OfFloat layout, long index); + + /** + * Writes a float to this address instance and index, scaled by given layout size. + *

      + * 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. + * + * @param layout the layout of the memory region to be written. + * @param index index in bytes (relative to this address). The final address of this write operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}. + * @param value the float value to be written. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + void setAtIndex(ValueLayout.OfFloat layout, long index, float value); + + /** + * Reads a long from this address and index, scaled by given layout size. + *

      + * 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. + * + * @param layout the layout of the memory region to be read. + * @param index index in bytes (relative to this address). The final address of this read operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}. + * @return a long value read from this address. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + long getAtIndex(ValueLayout.OfLong layout, long index); + + /** + * Writes a long to this address instance and index, scaled by given layout size. + *

      + * 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. + * + * @param layout the layout of the memory region to be written. + * @param index index in bytes (relative to this address). The final address of this write operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}. + * @param value the long value to be written. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + void setAtIndex(ValueLayout.OfLong layout, long index, long value); + + /** + * Reads a double from this address and index, scaled by given layout size. + *

      + * 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. + * + * @param layout the layout of the memory region to be read. + * @param index index in bytes (relative to this address). The final address of this read operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}. + * @return a double value read from this address. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + double getAtIndex(ValueLayout.OfDouble layout, long index); + + /** + * Writes a double to this address instance and index, scaled by given layout size. + *

      + * 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. + * + * @param layout the layout of the memory region to be written. + * @param index index in bytes (relative to this address). The final address of this write operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}. + * @param value the double value to be written. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + void setAtIndex(ValueLayout.OfDouble layout, long index, double value); + + /** + * Reads an address from this address and index, scaled by given layout size. + *

      + * 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. + * + * @param layout the layout of the memory region to be read. + * @param index index in bytes (relative to this address). The final address of this read operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}. + * @return an address value read from this address. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + MemoryAddress getAtIndex(ValueLayout.OfAddress layout, long index); + + /** + * Writes an address to this address instance and index, scaled by given layout size. + *

      + * 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. + * + * @param layout the layout of the memory region to be written. + * @param index index in bytes (relative to this address). The final address of this write operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}. + * @param value the address value to be written. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + void setAtIndex(ValueLayout.OfAddress layout, long index, Addressable value); } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryHandles.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryHandles.java index 31dbc4e9ce582..f4489ecd601c9 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryHandles.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryHandles.java @@ -27,24 +27,21 @@ import jdk.internal.access.JavaLangInvokeAccess; import jdk.internal.access.SharedSecrets; -import jdk.internal.foreign.Utils; import sun.invoke.util.Wrapper; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.invoke.VarHandle; -import java.nio.ByteOrder; import java.util.List; import java.util.Objects; /** * This class defines several factory methods for constructing and combining memory access var handles. - * To obtain a memory access var handle, clients must start from one of the leaf methods - * (see {@link MemoryHandles#varHandle(Class, ByteOrder)}, - * {@link MemoryHandles#varHandle(Class, long, ByteOrder)}). This determines the variable type - * (all primitive types but {@code void} and {@code boolean} are supported), as well as the alignment constraint and the - * byte order associated with a memory access var handle. The resulting memory access var handle can then be combined in various ways + * Memory access var handles can be obtained using {@link MemoryHandles#varHandle(ValueLayout)}. The provided value layout + * determines the type, as well as the alignment constraint and the byte order associated with the memory access var handle. + *

      + * The resulting memory access var handle can then be combined in various ways * to emulate different addressing modes. The var handles created by this class feature a mandatory coordinate type * (of type {@link MemorySegment}), and one {@code long} coordinate type, which represents the offset, in bytes, relative * to the segment, at which dereference should occur. @@ -53,12 +50,12 @@ *

      {@code
       GroupLayout seq = MemoryLayout.structLayout(
               MemoryLayout.paddingLayout(32),
      -        MemoryLayout.valueLayout(32, ByteOrder.BIG_ENDIAN).withName("value")
      +        ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN).withName("value")
       );
        * }
      * To access the member layout named {@code value}, we can construct a memory access var handle as follows: *
      {@code
      -VarHandle handle = MemoryHandles.varHandle(int.class, ByteOrder.BIG_ENDIAN); //(MemorySegment, long) -> int
      +VarHandle handle = MemoryHandles.varHandle(ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN)); //(MemorySegment, long) -> int
       handle = MemoryHandles.insertCoordinates(handle, 1, 4); //(MemorySegment) -> int
        * }
      * @@ -77,21 +74,21 @@ * 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} or {@code double}. + * {@code float}, {@code double} or {@link MemoryAddress}. * (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} and {@code long}. + *
    • numeric atomic update access modes for {@code int}, {@code long} and {@link MemoryAddress}. * (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} and {@code long}. + *
    • bitwise atomic update access modes for {@code int}, {@code long} and {@link MemoryAddress}. * (Future major platform releases of the JDK may support additional * numeric types for certain currently unsupported access modes.) *
    * - * If {@code T} is {@code float} or {@code double} then atomic + * If {@code T} is {@code float}, {@code double} or {@link MemoryAddress} then atomic * update access modes compare values using their bitwise representation - * (see {@link Float#floatToRawIntBits} and - * {@link Double#doubleToRawLongBits}, respectively). + * (see {@link Float#floatToRawIntBits}, + * {@link Double#doubleToRawLongBits} and {@link MemoryAddress#toRawLongValue()}, 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 @@ -109,8 +106,6 @@ private MemoryHandles() { //sorry, just the one! } - private static final MethodHandle LONG_TO_ADDRESS; - private static final MethodHandle ADDRESS_TO_LONG; private static final MethodHandle INT_TO_BYTE; private static final MethodHandle BYTE_TO_UNSIGNED_INT; private static final MethodHandle INT_TO_SHORT; @@ -124,10 +119,6 @@ private MemoryHandles() { static { try { - LONG_TO_ADDRESS = MethodHandles.lookup().findStatic(MemoryHandles.class, "longToAddress", - MethodType.methodType(MemoryAddress.class, long.class)); - ADDRESS_TO_LONG = MethodHandles.lookup().findStatic(MemoryHandles.class, "addressToLong", - MethodType.methodType(long.class, MemoryAddress.class)); INT_TO_BYTE = MethodHandles.explicitCastArguments(MethodHandles.identity(byte.class), MethodType.methodType(byte.class, int.class)); BYTE_TO_UNSIGNED_INT = MethodHandles.lookup().findStatic(Byte.class, "toUnsignedInt", @@ -154,34 +145,10 @@ private MemoryHandles() { } /** - * Creates a memory access var handle with the given carrier type and byte order. - * - * The returned var handle's type is {@code carrier} and the list of coordinate types is - * {@code (MemorySegment, long)}, where the {@code long} coordinate type corresponds to byte offset into - * a given memory segment. The returned var handle accesses bytes at an offset in a given - * memory segment, composing bytes to or from a value of the type {@code carrier} according to the given endianness; - * the alignment constraint (in bytes) for the resulting memory access var handle is the same as the size (in bytes) of the - * carrier type {@code carrier}. - * - * @apiNote the resulting var handle features certain access mode restrictions, - * which are common to all memory access var handles. - * - * @param carrier the carrier type. Valid carriers are {@code byte}, {@code short}, {@code char}, {@code int}, - * {@code float}, {@code long}, and {@code double}. - * @param byteOrder the required byte order. - * @return the new memory access var handle. - * @throws IllegalArgumentException when an illegal carrier type is used - */ - public static VarHandle varHandle(Class carrier, ByteOrder byteOrder) { - Objects.requireNonNull(carrier); - Objects.requireNonNull(byteOrder); - return varHandle(carrier, - carrierSize(carrier), - byteOrder); - } - - /** - * Creates a memory access var handle with the given carrier type, alignment constraint, and byte order. + * Creates a memory access var handle from given value layout. The provided layout will specify the + * {@linkplain ValueLayout#carrier() carrier type}, the {@linkplain ValueLayout#byteSize() the byte size}, + * the {@linkplain ValueLayout#byteAlignment() byte alignment} and the {@linkplain ValueLayout#order() byte order} + * associated to the returned var handle. * * The returned var handle's type is {@code carrier} and the list of coordinate types is * {@code (MemorySegment, long)}, where the {@code long} coordinate type corresponds to byte offset into @@ -192,56 +159,13 @@ public static VarHandle varHandle(Class carrier, ByteOrder byteOrder) { * @apiNote the resulting var handle features certain access mode restrictions, * which are common to all memory access var handles. * - * @param carrier the carrier type. Valid carriers are {@code byte}, {@code short}, {@code char}, {@code int}, - * {@code float}, {@code long}, and {@code double}. - * @param alignmentBytes the alignment constraint (in bytes). Must be a power of two. - * @param byteOrder the required byte order. + * @param layout the value layout for which a memory access handle is to be obtained. * @return the new memory access var handle. * @throws IllegalArgumentException if an illegal carrier type is used, or if {@code alignmentBytes} is not a power of two. */ - public static VarHandle varHandle(Class carrier, long alignmentBytes, ByteOrder byteOrder) { - Objects.requireNonNull(carrier); - Objects.requireNonNull(byteOrder); - checkCarrier(carrier); - - if (alignmentBytes <= 0 - || (alignmentBytes & (alignmentBytes - 1)) != 0) { // is power of 2? - throw new IllegalArgumentException("Bad alignment: " + alignmentBytes); - } - - return Utils.fixUpVarHandle(JLI.memoryAccessVarHandle(carrier, false, alignmentBytes - 1, byteOrder)); - } - - /** - * Adapt an existing var handle into a new var handle whose carrier type is {@link MemorySegment}. - * That is, when calling {@link VarHandle#get(Object...)} on the returned var handle, - * the read numeric value will be turned into a memory address (as if by calling {@link MemoryAddress#ofLong(long)}); - * similarly, when calling {@link VarHandle#set(Object...)}, the memory address to be set will be converted - * into a numeric value, and then written into memory. The amount of bytes read (resp. written) from (resp. to) - * memory depends on the carrier of the original memory access var handle. - * - * @param target the memory access var handle to be adapted - * @return the adapted var handle. - * @throws IllegalArgumentException if the carrier type of {@code varHandle} is either {@code boolean}, - * {@code float}, or {@code double}, or is not a primitive type. - */ - public static VarHandle asAddressVarHandle(VarHandle target) { - Objects.requireNonNull(target); - Class carrier = target.varType(); - if (!carrier.isPrimitive() || carrier == boolean.class || - carrier == float.class || carrier == double.class) { - throw new IllegalArgumentException("Unsupported carrier type: " + carrier.getName()); - } - - if (carrier != long.class) { - // slow-path, we need to adapt - return filterValue(target, - MethodHandles.explicitCastArguments(ADDRESS_TO_LONG, MethodType.methodType(carrier, MemoryAddress.class)), - MethodHandles.explicitCastArguments(LONG_TO_ADDRESS, MethodType.methodType(MemoryAddress.class, carrier))); - } else { - // fast-path - return filterValue(target, ADDRESS_TO_LONG, LONG_TO_ADDRESS); - } + public static VarHandle varHandle(ValueLayout layout) { + Objects.requireNonNull(layout); + return layout.accessHandle(); } /** @@ -255,7 +179,7 @@ public static VarHandle asAddressVarHandle(VarHandle target) { * the case if modeled as a Java {@code short}. This is illustrated in the following example: *

    {@code
         MemorySegment segment = MemorySegment.allocateNative(2, ResourceScope.newImplicitScope());
    -    VarHandle SHORT_VH = MemoryLayouts.JAVA_SHORT.varHandle(short.class);
    +    VarHandle SHORT_VH = ValueLayout.JAVA_SHORT.varHandle();
         VarHandle INT_VH = MemoryHandles.asUnsigned(SHORT_VH, int.class);
         SHORT_VH.set(segment, (short)-1);
         INT_VH.get(segment); // returns 65535
    @@ -276,7 +200,7 @@ public static VarHandle asAddressVarHandle(VarHandle target) {
          * 

    * The returned var handle will feature the variable type {@code adaptedType}, * and the same access coordinates, the same access modes (see {@link - * java.lang.invoke.VarHandle.AccessMode}, and the same atomic access + * java.lang.invoke.VarHandle.AccessMode}), and the same atomic access * guarantees, as those featured by the {@code target} var handle. * * @param target the memory access var handle to be adapted @@ -284,7 +208,7 @@ public static VarHandle asAddressVarHandle(VarHandle target) { * @return the adapted var handle. * @throws IllegalArgumentException if the carrier type of {@code target} * is not one of {@code byte}, {@code short}, or {@code int}; if {@code - * adaptedType} is not one of {@code int}, or {@code long}; if the bitwidth + * adaptedType} is not one of {@code int}, or {@code long}; if the bit width * of the {@code adaptedType} is not greater than that of the {@code target} * carrier type. * @@ -324,12 +248,15 @@ public static VarHandle asUnsigned(VarHandle target, final Class adaptedType) * is processed using the second filter and returned to the caller. More advanced access mode types, such as * {@link java.lang.invoke.VarHandle.AccessMode#COMPARE_AND_EXCHANGE} might apply both filters at the same time. *

    - * For the boxing and unboxing filters to be well formed, their types must be of the form {@code (A... , S) -> T} and + * For the boxing and unboxing filters to be well-formed, their types must be of the form {@code (A... , S) -> T} and * {@code (A... , T) -> S}, respectively, where {@code T} is the type of the target var handle. If this is the case, * the resulting var handle will have type {@code S} and will feature the additional coordinates {@code A...} (which * will be appended to the coordinates of the target var handle). *

    - * The resulting var handle will feature the same access modes (see {@link java.lang.invoke.VarHandle.AccessMode} and + * If the boxing and unboxing filters throw any checked exceptions when invoked, the resulting var handle will + * throw an {@link IllegalStateException}. + *

    + * The resulting var handle will feature the same access modes (see {@link java.lang.invoke.VarHandle.AccessMode}) and * atomic access guarantees as those featured by the target var handle. * * @param target the target var handle @@ -338,7 +265,7 @@ public static VarHandle asUnsigned(VarHandle target, final Class adaptedType) * @return an adapter var handle which accepts a new type, performing the provided boxing/unboxing conversions. * @throws IllegalArgumentException if {@code filterFromTarget} and {@code filterToTarget} are not well-formed, that is, they have types * 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 either {@code filterFromTarget} or {@code filterToTarget} throws any checked exceptions. + * or if it's determined that either {@code filterFromTarget} or {@code filterToTarget} throws any checked exceptions. */ public static VarHandle filterValue(VarHandle target, MethodHandle filterToTarget, MethodHandle filterFromTarget) { return JLI.filterValue(target, filterToTarget, filterFromTarget); @@ -353,9 +280,12 @@ public static VarHandle filterValue(VarHandle target, MethodHandle filterToTarge * parameter types of the unary filter functions), and then passed (along with any coordinate that was left unaltered * by the adaptation) to the target var handle. *

    - * For the coordinate filters to be well formed, their types must be of the form {@code S1 -> T1, S2 -> T1 ... Sn -> Tn}, + * For the coordinate filters to be well-formed, their types must be of the form {@code S1 -> T1, S2 -> T1 ... Sn -> Tn}, * where {@code T1, T2 ... Tn} are the coordinate types starting at position {@code pos} of the target var handle. *

    + * If any of the filters throws a checked exception when invoked, the resulting var handle will + * throw an {@link IllegalStateException}. + *

    * The resulting var handle will feature the same access modes (see {@link java.lang.invoke.VarHandle.AccessMode}) and * atomic access guarantees as those featured by the target var handle. * @@ -368,7 +298,7 @@ public static VarHandle filterValue(VarHandle target, MethodHandle filterToTarge * other than {@code S1 -> T1, S2 -> T2, ... Sn -> Tn} where {@code T1, T2 ... Tn} are the coordinate types starting * at position {@code pos} of the target var handle, if {@code pos} is not between 0 and the target var handle coordinate arity, inclusive, * or if more filters are provided than the actual number of coordinate types available starting at {@code pos}, - * or if any of the filters throws any checked exceptions. + * or if it's determined that any of the filters throws any checked exceptions. */ public static VarHandle filterCoordinates(VarHandle target, int pos, MethodHandle... filters) { return JLI.filterCoordinates(target, pos, filters); @@ -382,7 +312,7 @@ public static VarHandle filterCoordinates(VarHandle target, int pos, MethodHandl * When calling e.g. {@link VarHandle#get(Object...)} on the resulting var handle, incoming coordinate values * are joined with bound coordinate values, and then passed to the target var handle. *

    - * For the bound coordinates to be well formed, their types must be {@code T1, T2 ... Tn }, + * For the bound coordinates to be well-formed, their types must be {@code T1, T2 ... Tn }, * where {@code T1, T2 ... Tn} are the coordinate types starting at position {@code pos} of the target var handle. *

    * The resulting var handle will feature the same access modes (see {@link java.lang.invoke.VarHandle.AccessMode}) and @@ -409,7 +339,7 @@ public static VarHandle insertCoordinates(VarHandle target, int pos, Object... v *

    * The given array controls the reordering. * Call {@code #I} the number of incoming coordinates (the value - * {@code newCoordinates.size()}, and call {@code #O} the number + * {@code newCoordinates.size()}), and call {@code #O} the number * of outgoing coordinates (the number of coordinates associated with the target var handle). * Then the length of the reordering array must be {@code #O}, * and each element must be a non-negative number less than {@code #I}. @@ -444,7 +374,7 @@ public static VarHandle permuteCoordinates(VarHandle target, List> newC } /** - * Adapts a target var handle handle by pre-processing + * Adapts a target var handle by pre-processing * a sub-sequence of its coordinate values with a filter (a method handle). * The pre-processed coordinates are replaced by the result (if any) of the * filter function and the target var handle is then called on the modified (usually shortened) @@ -464,6 +394,9 @@ public static VarHandle permuteCoordinates(VarHandle target, List> newC * coordinate type of the target var handle at position {@code pos}, and that target var handle * coordinate is supplied by the return value of the filter. *

    + * If any of the filters throws a checked exception when invoked, the resulting var handle will + * throw an {@link IllegalStateException}. + *

    * The resulting var handle will feature the same access modes (see {@link java.lang.invoke.VarHandle.AccessMode}) and * atomic access guarantees as those featured by the target var handle. * @@ -476,7 +409,7 @@ public static VarHandle permuteCoordinates(VarHandle target, List> newC * is void, or it is not the same as the {@code pos} coordinate of the target var handle, * if {@code pos} is not between 0 and the target var handle coordinate arity, inclusive, * if the resulting var handle's type would have too many coordinates, - * or if {@code filter} throws any checked exceptions. + * or if it's determined that {@code filter} throws any checked exceptions. */ public static VarHandle collectCoordinates(VarHandle target, int pos, MethodHandle filter) { return JLI.collectCoordinates(target, pos, filter); @@ -505,24 +438,6 @@ public static VarHandle dropCoordinates(VarHandle target, int pos, Class... v return JLI.dropCoordinates(target, pos, valueTypes); } - private static void checkAddressFirstCoordinate(VarHandle handle) { - if (handle.coordinateTypes().size() < 1 || - handle.coordinateTypes().get(0) != MemorySegment.class) { - throw new IllegalArgumentException("Expected var handle with leading coordinate of type MemorySegment"); - } - } - - private static void checkCarrier(Class carrier) { - if (!carrier.isPrimitive() || carrier == void.class || carrier == boolean.class) { - throw new IllegalArgumentException("Illegal carrier: " + carrier.getSimpleName()); - } - } - - private static long carrierSize(Class carrier) { - long bitsAlignment = Math.max(8, Wrapper.forPrimitiveType(carrier).bitWidth()); - return Utils.bitsToBytesOrThrow(bitsAlignment, IllegalStateException::new); - } - private static void checkWidenable(Class carrier) { if (!(carrier == byte.class || carrier == short.class || carrier == int.class)) { throw new IllegalArgumentException("illegal carrier:" + carrier.getSimpleName()); @@ -541,12 +456,4 @@ private static void checkTargetWiderThanCarrier(Class carrier, Class targe target.getSimpleName() + " is not wider than: " + carrier.getSimpleName()); } } - - private static MemoryAddress longToAddress(long value) { - return MemoryAddress.ofLong(value); - } - - private static long addressToLong(MemoryAddress value) { - return value.toRawLongValue(); - } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java index 44793be750f96..8319f22c2ab04 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java @@ -46,11 +46,11 @@ import java.util.stream.Stream; /** - * A memory layout can be used to describe the contents of a memory segment in a language neutral fashion. + * A memory layout can be used to describe the contents of a memory segment. * There are two leaves in the layout hierarchy, value layouts, which are used to represent values of given size and kind (see * {@link ValueLayout}) and 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 (see {@link MemoryLayout#paddingLayout(long)}). - * Some common value layout constants are defined in the {@link MemoryLayouts} class. + * Some common value layout constants are defined in the {@link ValueLayout} class. *

    * More complex layouts can be derived from simpler ones: a sequence layout denotes a repetition of one or more * element layout (see {@link SequenceLayout}); a group layout denotes an aggregation of (typically) heterogeneous @@ -70,9 +70,9 @@ *

    {@code
     SequenceLayout taggedValues = MemoryLayout.sequenceLayout(5,
         MemoryLayout.structLayout(
    -        MemoryLayout.valueLayout(8, ByteOrder.nativeOrder()).withName("kind"),
    +        ValueLayout.JAVA_BYTE.withName("kind"),
             MemoryLayout.paddingLayout(24),
    -        MemoryLayout.valueLayout(32, ByteOrder.nativeOrder()).withName("value")
    +        ValueLayout.JAVA_INT.withName("value")
         )
     ).withName("TaggedValues");
      * }
    @@ -81,8 +81,6 @@ * programmers should treat instances that are {@linkplain #equals(Object) equal} as interchangeable and should not * use instances for synchronization, or unpredictable behavior may occur. For example, in a future release, * synchronization may fail. The {@code equals} method should be used for comparisons. - *

    - * Non-platform classes should not implement {@linkplain MemoryLayout} directly. * *

    Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null} * elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown.

    @@ -122,11 +120,11 @@ * at a layout nested within the root layout - this is the layout selected by the layout path. * Layout paths are typically expressed as a sequence of one or more {@link PathElement} instances. *

    - * Layout paths are for example useful in order to obtain offsets of arbitrarily nested layouts inside another layout - * (see {@link MemoryLayout#bitOffset(PathElement...)}), to quickly obtain a memory access handle corresponding to the selected - * layout (see {@link MemoryLayout#varHandle(Class, PathElement...)}), to select an arbitrarily nested layout inside - * another layout (see {@link MemoryLayout#select(PathElement...)}, or to transform a nested layout element inside - * another layout (see {@link MemoryLayout#map(UnaryOperator, PathElement...)}). + * Layout paths are for example useful in order to obtain {@linkplain MemoryLayout#bitOffset(PathElement...) offsets} of + * arbitrarily nested layouts inside another layout, to quickly obtain a {@linkplain #varHandle(PathElement...) memory access handle} + * corresponding to the selected layout, to {@linkplain #select(PathElement...) select} an arbitrarily nested layout inside + * another layout, or to {@link #map(UnaryOperator, PathElement...) transform} a nested layout element inside + * another layout. *

    * Such layout paths can be constructed programmatically using the methods in this class. * For instance, given the {@code taggedValues} layout instance constructed as above, we can obtain the offset, @@ -152,7 +150,7 @@ *

    {@code
     MemoryLayout taggedValuesWithHole = MemoryLayout.sequenceLayout(5,
         MemoryLayout.structLayout(
    -        MemoryLayout.valueLayout(8, ByteOrder.nativeOrder()).withName("kind"),
    +        ValueLayout.JAVA_BYTE.withName("kind"),
             MemoryLayout.paddingLayout(32),
             MemoryLayout.paddingLayout(32)
     ));
    @@ -164,8 +162,7 @@
      * This is important when obtaining memory access var handle from layouts, as in the following code:
      *
      * 
    {@code
    -VarHandle valueHandle = taggedValues.varHandle(int.class,
    -                                               PathElement.sequenceElement(),
    +VarHandle valueHandle = taggedValues.varHandle(PathElement.sequenceElement(),
                                                    PathElement.groupElement("value"));
      * }
    * @@ -189,9 +186,7 @@ * *

    Layout attributes

    * - * Layouts can be optionally associated with one or more attributes. A layout attribute forms a name/value - * pair, where the name is a {@link String} and the value is a {@link Constable}. The most common form of layout attribute - * is the layout name (see {@link #LAYOUT_NAME}), a custom name that can be associated with memory layouts and that can be referred to when + * Layouts can be optionally associated with a name. A layout name can be referred to when * constructing layout paths. * * @implSpec @@ -236,18 +231,10 @@ public sealed interface MemoryLayout extends Constable permits AbstractLayout, S * @throws UnsupportedOperationException if the layout is, or contains, a sequence layout with unspecified size (see {@link SequenceLayout}), * or if {@code bitSize()} is not a multiple of 8. */ - default long byteSize() { - return Utils.bitsToBytesOrThrow(bitSize(), - () -> new UnsupportedOperationException("Cannot compute byte size; bit size is not a multiple of 8")); - } + long byteSize(); /** * Return the name (if any) associated with this layout. - *

    - * This is equivalent to the following code: - *

    {@code
    -    attribute(LAYOUT_NAME).map(String.class::cast);
    -     * }
    * * @return the layout name (if any). * @see MemoryLayout#withName(String) @@ -256,11 +243,6 @@ default long byteSize() { /** * Creates a new layout which features the desired layout name. - *

    - * This is equivalent to the following code: - *

    {@code
    -    withAttribute(LAYOUT_NAME, name);
    -     * }
    * * @param name the layout name. * @return a new layout which is the same as this layout, except for the name associated with it. @@ -313,36 +295,10 @@ default long byteAlignment() { * * @param bitAlignment the layout alignment constraint, expressed in bits. * @return a new layout which is the same as this layout, except for the alignment constraint associated with it. - * @throws IllegalArgumentException if {@code bitAlignment} is not a power of two, or if it's less than than 8. + * @throws IllegalArgumentException if {@code bitAlignment} is not a power of two, or if it's less than 8. */ MemoryLayout withBitAlignment(long bitAlignment); - /** - * Returns the attribute with the given name (if it exists). - * - * @param name the attribute name - * @return the attribute with the given name (if it exists). - */ - Optional attribute(String name); - - /** - * Returns a new memory layout which features the same attributes as this layout, plus the newly specified attribute. - * If this layout already contains an attribute with the same name, the existing attribute value is overwritten in the returned - * layout. - * - * @param name the attribute name. - * @param value the attribute value. - * @return a new memory layout which features the same attributes as this layout, plus the newly specified attribute. - */ - MemoryLayout withAttribute(String name, Constable value); - - /** - * Returns a stream of the attribute names associated with this layout. - * - * @return a stream of the attribute names associated with this layout. - */ - Stream attributes(); - /** * Computes the offset, in bits, of the layout selected by a given layout path, where the path is considered rooted in this * layout. @@ -366,7 +322,7 @@ default long bitOffset(PathElement... elements) { * by a given layout path, where the path is considered rooted in this layout. * *

    The returned method handle has a return type of {@code long}, and features as many {@code long} - * parameter types as there are free dimensions in the provided layout path (see {@link PathElement#sequenceElement()}, + * parameter types as there are free dimensions in the provided layout path (see {@link PathElement#sequenceElement()}), * where the order of the parameters corresponds to the order of the path elements. * The returned method handle can be used to compute a layout offset similar to {@link #bitOffset(PathElement...)}, * but where some sequence indices are specified only when invoking the method handle. @@ -417,7 +373,7 @@ default long byteOffset(PathElement... elements) { * by a given layout path, where the path is considered rooted in this layout. * *

    The returned method handle has a return type of {@code long}, and features as many {@code long} - * parameter types as there are free dimensions in the provided layout path (see {@link PathElement#sequenceElement()}, + * parameter types as there are free dimensions in the provided layout path (see {@link PathElement#sequenceElement()}), * where the order of the parameters corresponds to the order of the path elements. * The returned method handle can be used to compute a layout offset similar to {@link #byteOffset(PathElement...)}, * but where some sequence indices are specified only when invoking the method handle. @@ -477,18 +433,14 @@ default MethodHandle byteOffsetHandle(PathElement... elements) { * unspecified sequence access component contained in this layout path. Moreover, the resulting var handle * features certain access mode restrictions, which are common to all memory access var handles. * - * @param carrier the var handle carrier type. * @param elements the layout path elements. * @return a var handle which can be used to dereference memory at the (possibly nested) layout selected by the layout path in {@code elements}. * @throws UnsupportedOperationException if the layout path has one or more elements with incompatible alignment constraints, * or if one of the layouts traversed by the layout path has unspecified size. - * @throws IllegalArgumentException if the carrier does not represent a primitive type, if the carrier is {@code void}, - * {@code boolean}, or if the layout path in {@code elements} does not select a value layout (see {@link ValueLayout}), - * or if the selected value layout has a size that that does not match that of the specified carrier type. + * @throws IllegalArgumentException if the layout path in {@code elements} does not select a value layout (see {@link ValueLayout}). */ - default VarHandle varHandle(Class carrier, PathElement... elements) { - Objects.requireNonNull(carrier); - return computePathOp(LayoutPath.rootPath(this, MemoryLayout::bitSize), path -> path.dereferenceHandle(carrier), + default VarHandle varHandle(PathElement... elements) { + return computePathOp(LayoutPath.rootPath(this, MemoryLayout::bitSize), LayoutPath::dereferenceHandle, Set.of(), elements); } @@ -498,7 +450,7 @@ default VarHandle varHandle(Class carrier, PathElement... elements) { * *

    The returned method handle has a return type of {@code MemorySegment}, features a {@code MemorySegment} * parameter as leading parameter representing the segment to be sliced, and features as many trailing {@code long} - * parameter types as there are free dimensions in the provided layout path (see {@link PathElement#sequenceElement()}, + * parameter types as there are free dimensions in the provided layout path (see {@link PathElement#sequenceElement()}), * where the order of the parameters corresponds to the order of the path elements. * The returned method handle can be used to create a slice similar to using {@link MemorySegment#asSlice(long, long)}, * but where the offset argument is dynamically compute based on indices specified when invoking the method handle. @@ -515,7 +467,7 @@ default VarHandle varHandle(Class carrier, PathElement... elements) { * and {@code s_0}, {@code s_1}, ... {@code s_n} are static stride constants which are derived from * the layout path. * - *

    After the offset is computed, the returned segment is create as if by calling: + *

    After the offset is computed, the returned segment is created as if by calling: *

    {@code
         segment.asSlice(offset, layout.byteSize());
          * }
    @@ -582,7 +534,7 @@ private static Z computePathOp(LayoutPath path, Function fina } /** - * Is this a padding layout (e.g. a layout created from {@link #paddingLayout(long)}) ? + * Is this a {@linkplain #paddingLayout(long) padding layout} ? * @return true, if this layout is a padding layout. */ boolean isPadding(); @@ -595,8 +547,6 @@ private static Z computePathOp(LayoutPath path, Function fina * of sequence element layout can be explicit (see {@link PathElement#sequenceElement(long)}) or * implicit (see {@link PathElement#sequenceElement()}). When a path uses one or more implicit * sequence path elements, it acquires additional free dimensions. - *

    - * Non-platform classes should not implement {@linkplain PathElement} directly. * *

    Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null} * elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown.

    @@ -612,7 +562,7 @@ sealed interface PathElement permits LayoutPath.PathElementImpl { * that is combined with such element. * * @implSpec in case multiple group elements with a matching name exist, the path element returned by this - * method will select the first one; that is, the group element with lowest offset from current path is selected. + * method will select the first one; that is, the group element with the lowest offset from current path is selected. * * @param name the name of the group element to be selected. * @return a path element which selects the group element with given name. @@ -728,17 +678,48 @@ static MemoryLayout paddingLayout(long size) { } /** - * Create a value layout of given byte order and size. - * - * @param size the value layout size. + * Creates a value layout of given Java carrier and byte order. The type of resulting value layout is determined + * by the carrier provided: + *
      + *
    • {@link ValueLayout.OfBoolean}, for {@code boolean.class}
    • + *
    • {@link ValueLayout.OfByte}, for {@code byte.class}
    • + *
    • {@link ValueLayout.OfShort}, for {@code short.class}
    • + *
    • {@link ValueLayout.OfChar}, for {@code char.class}
    • + *
    • {@link ValueLayout.OfInt}, for {@code int.class}
    • + *
    • {@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 MemoryAddress.class}
    • + *
    + * @param carrier the value layout carrier. * @param order the value layout's byte order. * @return a new value layout. - * @throws IllegalArgumentException if {@code size <= 0}. + * @throws IllegalArgumentException if the carrier type is not supported. */ - static ValueLayout valueLayout(long size, ByteOrder order) { + static ValueLayout valueLayout(Class carrier, ByteOrder order) { + Objects.requireNonNull(carrier); Objects.requireNonNull(order); - AbstractLayout.checkSize(size); - return new ValueLayout(order, size); + if (carrier == boolean.class) { + return new ValueLayout.OfBoolean(order); + } else if (carrier == char.class) { + return new ValueLayout.OfChar(order); + } else if (carrier == byte.class) { + return new ValueLayout.OfByte(order); + } else if (carrier == short.class) { + return new ValueLayout.OfShort(order); + } else if (carrier == int.class) { + return new ValueLayout.OfInt(order); + } else if (carrier == float.class) { + return new ValueLayout.OfFloat(order); + } else if (carrier == long.class) { + return new ValueLayout.OfLong(order); + } else if (carrier == double.class) { + return new ValueLayout.OfDouble(order); + } else if (carrier == MemoryAddress.class) { + return new ValueLayout.OfAddress(order); + } else { + throw new IllegalArgumentException("Unsupported carrier: " + carrier.getName()); + } } /** @@ -792,9 +773,4 @@ static GroupLayout unionLayout(MemoryLayout... elements) { .map(Objects::requireNonNull) .collect(Collectors.toList())); } - - /** - * Attribute name used to specify the name property of a memory layout (see {@link #name()} and {@link #withName(String)}). - */ - String LAYOUT_NAME = "layout/name"; } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayouts.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayouts.java deleted file mode 100644 index 4aee085821f1c..0000000000000 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayouts.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. 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.incubator.foreign; - -import jdk.internal.misc.Unsafe; - -import java.nio.ByteOrder; - -/** - * This class defines useful layout constants. Some of the constants defined in this class are explicit in both - * size and byte order (see {@link #BITS_64_BE}), and can therefore be used to explicitly and unambiguously specify the - * contents of a memory segment. Other constants make implicit byte order assumptions (see - * {@link #JAVA_INT}); as such, these constants make it easy to work with other serialization-centric APIs, - * such as {@link java.nio.ByteBuffer}. - */ -public final class MemoryLayouts { - - private MemoryLayouts() { - //just the one, please - } - - /** - * A value layout constant with size of one byte, and byte order set to {@link ByteOrder#LITTLE_ENDIAN}. - */ - public static final ValueLayout BITS_8_LE = MemoryLayout.valueLayout(8, ByteOrder.LITTLE_ENDIAN); - - /** - * A value layout constant with size of two bytes, and byte order set to {@link ByteOrder#LITTLE_ENDIAN}. - */ - public static final ValueLayout BITS_16_LE = MemoryLayout.valueLayout(16, ByteOrder.LITTLE_ENDIAN); - - /** - * A value layout constant with size of four bytes, and byte order set to {@link ByteOrder#LITTLE_ENDIAN}. - */ - public static final ValueLayout BITS_32_LE = MemoryLayout.valueLayout(32, ByteOrder.LITTLE_ENDIAN); - - /** - * A value layout constant with size of eight bytes, and byte order set to {@link ByteOrder#LITTLE_ENDIAN}. - */ - public static final ValueLayout BITS_64_LE = MemoryLayout.valueLayout(64, ByteOrder.LITTLE_ENDIAN); - - /** - * A value layout constant with size of one byte, and byte order set to {@link ByteOrder#BIG_ENDIAN}. - */ - public static final ValueLayout BITS_8_BE = MemoryLayout.valueLayout(8, ByteOrder.BIG_ENDIAN); - - /** - * A value layout constant with size of two bytes, and byte order set to {@link ByteOrder#BIG_ENDIAN}. - */ - public static final ValueLayout BITS_16_BE = MemoryLayout.valueLayout(16, ByteOrder.BIG_ENDIAN); - - /** - * A value layout constant with size of four bytes, and byte order set to {@link ByteOrder#BIG_ENDIAN}. - */ - public static final ValueLayout BITS_32_BE = MemoryLayout.valueLayout(32, ByteOrder.BIG_ENDIAN); - - /** - * A value layout constant with size of eight bytes, and byte order set to {@link ByteOrder#BIG_ENDIAN}. - */ - public static final ValueLayout BITS_64_BE = MemoryLayout.valueLayout(64, ByteOrder.BIG_ENDIAN); - - /** - * A padding layout constant with size of one byte. - */ - public static final MemoryLayout PAD_8 = MemoryLayout.paddingLayout(8); - - /** - * A padding layout constant with size of two bytes. - */ - public static final MemoryLayout PAD_16 = MemoryLayout.paddingLayout(16); - - /** - * A padding layout constant with size of four bytes. - */ - public static final MemoryLayout PAD_32 = MemoryLayout.paddingLayout(32); - - /** - * A padding layout constant with size of eight bytes. - */ - public static final MemoryLayout PAD_64 = MemoryLayout.paddingLayout(64); - - /** - * A value layout constant whose size is the same as that of a machine address (e.g. {@code size_t}), and byte order set to {@link ByteOrder#nativeOrder()}. - */ - public static final ValueLayout ADDRESS = MemoryLayout.valueLayout(Unsafe.ADDRESS_SIZE * 8, ByteOrder.nativeOrder()); - - /** - * A value layout constant whose size is the same as that of a Java {@code byte}, and byte order set to {@link ByteOrder#nativeOrder()}. - */ - public static final ValueLayout JAVA_BYTE = MemoryLayout.valueLayout(8, ByteOrder.nativeOrder()); - - /** - * A value layout constant whose size is the same as that of a Java {@code char}, and byte order set to {@link ByteOrder#nativeOrder()}. - */ - public static final ValueLayout JAVA_CHAR = MemoryLayout.valueLayout(16, ByteOrder.nativeOrder()); - - /** - * A value layout constant whose size is the same as that of a Java {@code short}, and byte order set to {@link ByteOrder#nativeOrder()}. - */ - public static final ValueLayout JAVA_SHORT = MemoryLayout.valueLayout(16, ByteOrder.nativeOrder()); - - /** - * A value layout constant whose size is the same as that of a Java {@code int}, and byte order set to {@link ByteOrder#nativeOrder()}. - */ - public static final ValueLayout JAVA_INT = MemoryLayout.valueLayout(32, ByteOrder.nativeOrder()); - - /** - * A value layout constant whose size is the same as that of a Java {@code long}, and byte order set to {@link ByteOrder#nativeOrder()}. - * The alignment of this layout (see {@link MemoryLayout#byteAlignment()} is platform-dependent, so that the following - * invariant holds: - *
    {@code
    -    MemoryLayouts.JAVA_LONG.byteAlignment() == MemoryLayouts.ADDRESS.byteSize();
    -     * }
    - */ - public static final ValueLayout JAVA_LONG = MemoryLayout.valueLayout(64, ByteOrder.nativeOrder()) - .withBitAlignment(ADDRESS.bitSize()); - - /** - * A value layout constant whose size is the same as that of a Java {@code float}, and byte order set to {@link ByteOrder#nativeOrder()}. - */ - public static final ValueLayout JAVA_FLOAT = MemoryLayout.valueLayout(32, ByteOrder.nativeOrder()); - - /** - * A value layout constant whose size is the same as that of a Java {@code double}, and byte order set to {@link ByteOrder#nativeOrder()}. - * The alignment of this layout (see {@link MemoryLayout#byteAlignment()} is platform-dependent, so that the following - * invariant holds: - *
    {@code
    -    MemoryLayouts.JAVA_DOUBLE.byteAlignment() == MemoryLayouts.ADDRESS.byteSize();
    -     * }
    - */ - public static final ValueLayout JAVA_DOUBLE = MemoryLayout.valueLayout(64, ByteOrder.nativeOrder()) - .withBitAlignment(ADDRESS.bitSize()); -} diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java index 09874cf6dd76f..a2ead463451a2 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ package jdk.incubator.foreign; import java.io.UncheckedIOException; +import java.lang.reflect.Array; import java.nio.ByteBuffer; import jdk.internal.foreign.AbstractMemorySegmentImpl; @@ -34,11 +35,18 @@ import jdk.internal.foreign.MappedMemorySegmentImpl; import jdk.internal.foreign.ResourceScopeImpl; import jdk.internal.foreign.NativeMemorySegmentImpl; +import jdk.internal.foreign.Utils; +import jdk.internal.foreign.abi.SharedUtils; +import jdk.internal.misc.ScopedMemoryAccess; +import jdk.internal.misc.Unsafe; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; +import jdk.internal.vm.annotation.ForceInline; import java.io.IOException; +import java.nio.ByteOrder; import java.nio.channels.FileChannel; +import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.Objects; import java.util.Spliterator; @@ -86,7 +94,7 @@ * called mapped memory segments; mapped memory segments are associated with an underlying file descriptor. *

    * Contents of mapped memory segments can be {@linkplain #force() persisted} and {@linkplain #load() loaded} to and from the underlying file; - * these capabilities are suitable replacements for some of the functionality in the {@link java.nio.MappedByteBuffer} class. + * these capabilities are suitable replacements for some capabilities in the {@link java.nio.MappedByteBuffer} class. * Note that, while it is possible to map a segment into a byte buffer (see {@link MemorySegment#asByteBuffer()}), * and then call e.g. {@link java.nio.MappedByteBuffer#force()} that way, this can only be done when the source segment * is small enough, due to the size limitation inherent to the ByteBuffer API. @@ -94,7 +102,42 @@ * Clients requiring sophisticated, low-level control over mapped memory segments, should consider writing * custom mapped memory segment factories; using {@link CLinker}, e.g. on Linux, it is possible to call {@code mmap} * with the desired parameters; the returned address can be easily wrapped into a memory segment, using - * {@link MemoryAddress#ofLong(long)} and {@link MemoryAddress#asSegment(long, Runnable, ResourceScope)}. + * {@link MemoryAddress#ofLong(long)} and {@link MemorySegment#ofAddress(MemoryAddress, long, ResourceScope)}. + * + *

    Restricted native segments

    + * + * Sometimes it is necessary to turn a memory address obtained from native code into a memory segment with + * full spatial, temporal and confinement bounds. To do this, clients can {@link #ofAddress(MemoryAddress, long, ResourceScope) obtain} + * a native segment unsafely from a give memory address, by providing the segment size, as well as the segment {@linkplain ResourceScope scope}. + * This is a restricted operation and should be used with + * caution: for instance, an incorrect segment size could result in a VM crash when attempting to dereference + * the memory segment. + * + *

    Dereference

    + * + * A memory segment can be read or written using various methods provided in this class (e.g. {@link #get(ValueLayout.OfInt, long)}). + * Each dereference method takes a {@linkplain jdk.incubator.foreign.ValueLayout value layout}, which specifies the size, + * alignment constraints, byte order as well as the Java type associated with the dereference operation, and an offset. + * For instance, to read an int from a segment, using {@link ByteOrder#nativeOrder() default endianness}, the following code can be used: + *
    {@code
    +MemorySegment segment = ...
    +int value = segment.get(ValueLayout.JAVA_INT, 0);
    + * }
    + * + * If the value to be read is stored in memory using {@link ByteOrder#BIG_ENDIAN big-endian} encoding, the dereference operation + * can be expressed as follows: + *
    {@code
    +MemorySegment segment = ...
    +int value = segment.get(ValueLayout.JAVA_INT.withOrder(BIG_ENDIAN), 0);
    + * }
    + * + * For more complex dereference operations (e.g. structured memory access), clients can obtain a memory access var handle, + * that is, a var handle that accepts a segment and, optionally, one or more additional {@code long} coordinates. Memory + * access var handles can be obtained from {@linkplain MemoryLayout#varHandle(MemoryLayout.PathElement...) memory layouts} + * by providing a so called layout path. + * Alternatively, clients can obtain raw memory access var handles from a given + * {@linkplain MemoryHandles#varHandle(ValueLayout) value layout}, and then adapt it using the var handle combinator + * functions defined in the {@link MemoryHandles} class. * *

    Lifecycle and confinement

    * @@ -105,9 +148,9 @@ *
    {@code
     MemorySegment segment = null;
     try (ResourceScope scope = ResourceScope.newConfinedScope()) {
    -    segment = MemorySegment.allocateNative(8, 1, scope);
    +    segment = MemorySegment.allocateNative(8, scope);
     }
    -MemoryAccess.getLong(segment); // already closed!
    +segment.get(ValueLayout.JAVA_LONG, 0); // already closed!
      * }
    * Additionally, access to a memory segment is subject to the thread-confinement checks enforced by the owning scope; that is, * if the segment is associated with a shared scope, it can be accessed by multiple threads; if it is associated with a confined @@ -143,11 +186,10 @@ * *
    {@code
     try (ResourceScope scope = ResourceScope.newSharedScope()) {
    -    SequenceLayout SEQUENCE_LAYOUT = MemoryLayout.sequenceLayout(1024, MemoryLayouts.JAVA_INT);
    +    SequenceLayout SEQUENCE_LAYOUT = MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_INT);
         MemorySegment segment = MemorySegment.allocateNative(SEQUENCE_LAYOUT, scope);
    -    VarHandle VH_int = SEQUENCE_LAYOUT.elementLayout().varHandle(int.class);
    -    int sum = segment.elements(MemoryLayouts.JAVA_INT).parallel()
    -                           .mapToInt(s -> (int)VH_int.get(s.address()))
    +    int sum = segment.elements(ValueLayout.JAVA_INT).parallel()
    +                           .mapToInt(s -> s.get(ValueLayout.JAVA_INT, 0))
                                .sum();
     }
      * }
    @@ -158,8 +200,10 @@ public sealed interface MemorySegment extends Addressable permits AbstractMemorySegmentImpl { /** - * The base memory address associated with this memory segment. - * The returned memory address is associated with same resource scope as that associated with this segment. + * The base memory address associated with this native memory segment. + * @throws UnsupportedOperationException if this segment is not a {@linkplain #isNative() native} segment. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. * @return The base memory address. */ @Override @@ -217,8 +261,6 @@ public sealed interface MemorySegment extends Addressable permits AbstractMemory * and whose new size is specified by the given argument. * * @see #asSlice(long) - * @see #asSlice(MemoryAddress) - * @see #asSlice(MemoryAddress, long) * * @param offset The new segment base offset (relative to the current segment base address), specified in bytes. * @param newSize The new segment size, specified in bytes. @@ -227,28 +269,6 @@ public sealed interface MemorySegment extends Addressable permits AbstractMemory */ MemorySegment asSlice(long offset, long newSize); - /** - * Obtains a new memory segment view whose base address is the given address, and whose new size is specified by the given argument. - *

    - * Equivalent to the following code: - *

    {@code
    -    asSlice(newBase.segmentOffset(this), newSize);
    -     * }
    - * - * @see #asSlice(long) - * @see #asSlice(MemoryAddress) - * @see #asSlice(long, long) - * - * @param newBase The new segment base address. - * @param newSize The new segment size, specified in bytes. - * @return a new memory segment view with updated base/limit addresses. - * @throws IndexOutOfBoundsException if {@code offset < 0}, {@code offset > byteSize()}, {@code newSize < 0}, or {@code newSize > byteSize() - offset} - */ - default MemorySegment asSlice(MemoryAddress newBase, long newSize) { - Objects.requireNonNull(newBase); - return asSlice(newBase.segmentOffset(this), newSize); - } - /** * Obtains a new memory segment view whose base address is the same as the base address of this segment plus a given offset, * and whose new size is computed by subtracting the specified offset from this segment size. @@ -258,8 +278,6 @@ default MemorySegment asSlice(MemoryAddress newBase, long newSize) { asSlice(offset, byteSize() - offset); * }
    * - * @see #asSlice(MemoryAddress) - * @see #asSlice(MemoryAddress, long) * @see #asSlice(long, long) * * @param offset The new segment base offset (relative to the current segment base address), specified in bytes. @@ -270,28 +288,6 @@ default MemorySegment asSlice(long offset) { return asSlice(offset, byteSize() - offset); } - /** - * Obtains a new memory segment view whose base address is the given address, and whose new size is computed by subtracting - * the address offset relative to this segment (see {@link MemoryAddress#segmentOffset(MemorySegment)}) from this segment size. - *

    - * Equivalent to the following code: - *

    {@code
    -    asSlice(newBase.segmentOffset(this));
    -     * }
    - * - * @see #asSlice(long) - * @see #asSlice(MemoryAddress, long) - * @see #asSlice(long, long) - * - * @param newBase The new segment base offset (relative to the current segment base address), specified in bytes. - * @return a new memory segment view with updated base/limit addresses. - * @throws IndexOutOfBoundsException if {@code address.segmentOffset(this) < 0}, or {@code address.segmentOffset(this) > byteSize()}. - */ - default MemorySegment asSlice(MemoryAddress newBase) { - Objects.requireNonNull(newBase); - return asSlice(newBase.segmentOffset(this)); - } - /** * Is this segment read-only? * @return {@code true}, if this segment is read-only. @@ -324,6 +320,42 @@ default MemorySegment asSlice(MemoryAddress newBase) { */ boolean isMapped(); + /** + * Returns a slice of this segment that is the overlap between this and + * the provided segment. + * + *

    Two segments {@code S1} and {@code S2} are said to overlap if it is possible to find + * at least two slices {@code L1} (from {@code S1}) and {@code L2} (from {@code S2}) that are backed by the + * same memory region. As such, it is not possible for a + * {@link #isNative() native} segment to overlap with a heap segment; in + * this case, or when no overlap occurs, {@code null} is returned. + * + * @param other the segment to test for an overlap with this segment. + * @return a slice of this segment, or {@code null} if no overlap occurs. + */ + MemorySegment asOverlappingSlice(MemorySegment other); + + /** + * Returns the offset, in bytes, of the provided segment, relative to this + * segment. + * + *

    The offset is relative to the base address of this segment and can be + * a negative or positive value. For instance, if both segments are native + * segments, the resulting offset can be computed as follows: + * + *

    {@code
    +     * other.baseAddress().toRawLongValue() - segment.baseAddress().toRawLongValue()
    +     * }
    + * + * If the segments share the same base 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. + * @return the relative offset, in bytes, of the provided segment. + */ + long segmentOffset(MemorySegment other); + /** * Fills a value into this memory segment. *

    @@ -331,7 +363,7 @@ default MemorySegment asSlice(MemoryAddress newBase) { * segment. Equivalent to (but likely more efficient than) the following code: * *

    {@code
    -byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE)
    +byteHandle = MemoryLayout.ofSequence(ValueLayout.JAVA_BYTE)
              .varHandle(byte.class, MemoryLayout.PathElement.sequenceElement());
     for (long l = 0; l < segment.byteSize(); l++) {
          byteHandle.set(segment.address(), l, value);
    @@ -346,7 +378,7 @@ default MemorySegment asSlice(MemoryAddress newBase) {
          * @param value the value to fill into this segment
          * @return this memory segment
          * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from
    -     * a thread other than the thread owning that scope,
    +     * a thread other than the thread owning that scope.
          * @throws UnsupportedOperationException if this segment is read-only (see {@link #isReadOnly()}).
          */
         MemorySegment fill(byte value);
    @@ -355,23 +387,23 @@ default MemorySegment asSlice(MemoryAddress newBase) {
          * Performs a bulk copy from given source segment to this segment. More specifically, the bytes at
          * offset {@code 0} through {@code src.byteSize() - 1} in the source segment are copied into this segment
          * at offset {@code 0} through {@code src.byteSize() - 1}.
    -     * If the source segment overlaps with this segment, then the copying is performed as if the bytes at
    -     * offset {@code 0} through {@code src.byteSize() - 1} in the source segment were first copied into a
    -     * temporary segment with size {@code bytes}, and then the contents of the temporary segment were copied into
    -     * this segment at offset {@code 0} through {@code src.byteSize() - 1}.
          * 

    - * The result of a bulk copy is unspecified if, in the uncommon case, the source segment and this segment - * do not overlap, but refer to overlapping regions of the same backing storage using different addresses. - * For example, this may occur if the same file is {@linkplain MemorySegment#mapFile mapped} to two segments. - * + * Calling this method is equivalent to the following code: + *

    {@code
    +    MemorySegment.copy(src, 0, this, 0, src.byteSize);
    +     * }
    * @param src the source segment. * @throws IndexOutOfBoundsException if {@code src.byteSize() > this.byteSize()}. * @throws IllegalStateException if either the scope associated with the source segment or the scope associated * with this segment have been already closed, or if access occurs from a thread other than the thread owning either * scopes. * @throws UnsupportedOperationException if this segment is read-only (see {@link #isReadOnly()}). + * @return this segment. */ - void copyFrom(MemorySegment src); + default MemorySegment copyFrom(MemorySegment src) { + MemorySegment.copy(src, 0, this, 0, src.byteSize()); + return this; + } /** * Finds and returns the offset, in bytes, of the first mismatch between @@ -381,10 +413,10 @@ default MemorySegment asSlice(MemoryAddress newBase) { * the smaller memory segment (exclusive). *

    * If the two segments share a common prefix then the returned offset is - * the length of the common prefix and it follows that there is a mismatch + * the length of the common prefix, and it follows that there is a mismatch * between the two segments at that offset within the respective segments. - * If one segment is a proper prefix of the other then the returned offset is - * the smaller of the segment sizes, and it follows that the offset is only + * If one segment is a proper prefix of the other, then the returned offset is + * the smallest of the segment sizes, and it follows that the offset is only * valid for the larger segment. Otherwise, there is no mismatch and {@code * -1} is returned. * @@ -398,11 +430,11 @@ default MemorySegment asSlice(MemoryAddress newBase) { long mismatch(MemorySegment other); /** - * Tells whether or not the contents of this mapped segment is resident in physical + * Tells whether the contents of this mapped segment is resident in physical * memory. * *

    A return value of {@code true} implies that it is highly likely - * that all of the data in this segment is resident in physical memory and + * that all the data in this segment is resident in physical memory and * may therefore be accessed without incurring any virtual-memory page * faults or I/O operations. A return value of {@code false} does not * necessarily imply that this segment's content is not resident in physical @@ -481,19 +513,19 @@ default MemorySegment asSlice(MemoryAddress newBase) { void force(); /** - * Wraps this segment in a {@link ByteBuffer}. Some of the properties of the returned buffer are linked to + * Wraps this segment in a {@link ByteBuffer}. Some properties of the returned buffer are linked to * the properties of this segment. For instance, if this segment is immutable * (e.g. the segment is a read-only segment, see {@link #isReadOnly()}), then the resulting buffer is read-only - * (see {@link ByteBuffer#isReadOnly()}. Additionally, if this is a native memory segment, the resulting buffer is + * (see {@link ByteBuffer#isReadOnly()}). Additionally, if this is a native memory segment, the resulting buffer is * direct (see {@link ByteBuffer#isDirect()}). *

    - * The returned buffer's position (see {@link ByteBuffer#position()} is initially set to zero, while + * The returned buffer's position (see {@link ByteBuffer#position()}) is initially set to zero, while * the returned buffer's capacity and limit (see {@link ByteBuffer#capacity()} and {@link ByteBuffer#limit()}, respectively) * are set to this segment' size (see {@link MemorySegment#byteSize()}). For this reason, a byte buffer cannot be * returned if this segment' size is greater than {@link Integer#MAX_VALUE}. *

    * The life-cycle of the returned buffer will be tied to that of this segment. That is, accessing the returned buffer - * after the scope associated with this segment has been closed (see {@link ResourceScope#close()}, will throw an {@link IllegalStateException}. + * after the scope associated with this segment has been closed (see {@link ResourceScope#close()}), will throw an {@link IllegalStateException}. *

    * If this segment is associated with a confined scope, calling read/write I/O operations on the resulting buffer * might result in an unspecified exception being thrown. Examples of such problematic operations are @@ -505,83 +537,135 @@ default MemorySegment asSlice(MemoryAddress newBase) { * * @return a {@link ByteBuffer} view of this memory segment. * @throws UnsupportedOperationException if this segment cannot be mapped onto a {@link ByteBuffer} instance, - * e.g. because it models an heap-based segment that is not based on a {@code byte[]}), or if its size is greater + * e.g. because it models a heap-based segment that is not based on a {@code byte[]}), or if its size is greater * than {@link Integer#MAX_VALUE}. */ ByteBuffer asByteBuffer(); /** * Copy the contents of this memory segment into a fresh byte array. + * @param elementLayout the source element layout. If the byte order associated with the layout is + * different from the native order, a byte swap operation will be performed on each array element. * @return a fresh byte array copy of this memory segment. * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from * a thread other than the thread owning that scope, or if this segment's contents cannot be copied into a {@link byte[]} instance, * e.g. its size is greater than {@link Integer#MAX_VALUE}. */ - byte[] toByteArray(); + byte[] toArray(ValueLayout.OfByte elementLayout); /** * Copy the contents of this memory segment into a fresh short array. + * @param elementLayout the source element layout. If the byte order associated with the layout is + * different from the native order, a byte swap operation will be performed on each array element. * @return a fresh short array copy of this memory segment. * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from * a thread other than the thread owning that scope, or if this segment's contents cannot be copied into a {@link short[]} instance, * e.g. because {@code byteSize() % 2 != 0}, or {@code byteSize() / 2 > Integer#MAX_VALUE} */ - short[] toShortArray(); + short[] toArray(ValueLayout.OfShort elementLayout); /** * Copy the contents of this memory segment into a fresh char array. + * @param elementLayout the source element layout. If the byte order associated with the layout is + * different from the native order, a byte swap operation will be performed on each array element. * @return a fresh char array copy of this memory segment. * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from * a thread other than the thread owning that scope, or if this segment's contents cannot be copied into a {@link char[]} instance, * e.g. because {@code byteSize() % 2 != 0}, or {@code byteSize() / 2 > Integer#MAX_VALUE}. */ - char[] toCharArray(); + char[] toArray(ValueLayout.OfChar elementLayout); /** * Copy the contents of this memory segment into a fresh int array. + * @param elementLayout the source element layout. If the byte order associated with the layout is + * different from the native order, a byte swap operation will be performed on each array element. * @return a fresh int array copy of this memory segment. * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from * a thread other than the thread owning that scope, or if this segment's contents cannot be copied into a {@link int[]} instance, * e.g. because {@code byteSize() % 4 != 0}, or {@code byteSize() / 4 > Integer#MAX_VALUE}. */ - int[] toIntArray(); + int[] toArray(ValueLayout.OfInt elementLayout); /** * Copy the contents of this memory segment into a fresh float array. + * @param elementLayout the source element layout. If the byte order associated with the layout is + * different from the native order, a byte swap operation will be performed on each array element. * @return a fresh float array copy of this memory segment. * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from * a thread other than the thread owning that scope, or if this segment's contents cannot be copied into a {@link float[]} instance, * e.g. because {@code byteSize() % 4 != 0}, or {@code byteSize() / 4 > Integer#MAX_VALUE}. */ - float[] toFloatArray(); + float[] toArray(ValueLayout.OfFloat elementLayout); /** * Copy the contents of this memory segment into a fresh long array. + * @param elementLayout the source element layout. If the byte order associated with the layout is + * different from the native order, a byte swap operation will be performed on each array element. * @return a fresh long array copy of this memory segment. * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from * a thread other than the thread owning that scope, or if this segment's contents cannot be copied into a {@link long[]} instance, * e.g. because {@code byteSize() % 8 != 0}, or {@code byteSize() / 8 > Integer#MAX_VALUE}. */ - long[] toLongArray(); + long[] toArray(ValueLayout.OfLong elementLayout); /** * Copy the contents of this memory segment into a fresh double array. + * @param elementLayout the source element layout. If the byte order associated with the layout is + * different from the native order, a byte swap operation will be performed on each array element. * @return a fresh double array copy of this memory segment. * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from * a thread other than the thread owning that scope, or if this segment's contents cannot be copied into a {@link double[]} instance, * e.g. because {@code byteSize() % 8 != 0}, or {@code byteSize() / 8 > Integer#MAX_VALUE}. */ - double[] toDoubleArray(); + double[] toArray(ValueLayout.OfDouble elementLayout); /** - * Creates a new confined buffer memory segment that models the memory associated with the given byte + * Reads a UTF-8 encoded, null-terminated string from this segment at given offset. + *

    + * 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). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this read operation can be expressed as {@code address().toRowLongValue() + offset}. + * @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 native string is greater than the largest string supported by the platform. + * @throws IllegalStateException if the size of the native string is greater than the size of this segment, + * or if the scope associated with this segment has been closed, or if access occurs from a thread other than the thread owning that scope. + */ + default String getUtf8String(long offset) { + return SharedUtils.toJavaStringInternal(this, offset); + } + + /** + * Writes the given string into this segment at given offset, converting it to a null-terminated byte sequence using UTF-8 encoding. + *

    + * 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). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this write operation can be expressed as {@code address().toRowLongValue() + offset}. + * @param str the Java string to be written into this segment. + * @throws IllegalArgumentException if the size of the native string is greater than the largest string supported by the platform. + * @throws IllegalStateException if the size of the native string is greater than the size of this segment, + * or if the scope associated with this segment has been closed, or if access occurs from a thread other than the thread owning that scope. + */ + default void setUtf8String(long offset, String str) { + Utils.toCString(str.getBytes(StandardCharsets.UTF_8), SegmentAllocator.prefixAllocator(asSlice(offset))); + } + + + /** + * Creates a new buffer memory segment that models the memory associated with the given byte * buffer. The segment starts relative to the buffer's position (inclusive) * and ends relative to the buffer's limit (exclusive). *

    * If the buffer is {@link ByteBuffer#isReadOnly() read-only}, the resulting segment will also be * {@link ByteBuffer#isReadOnly() read-only}. The scope associated with this segment can either be the * {@linkplain ResourceScope#globalScope() global} resource scope, in case the buffer has been created independently, - * or to some other (possibly closeable) resource scope, in case the buffer has been obtained using {@link #asByteBuffer()}. + * or some other resource scope, in case the buffer has been obtained using {@link #asByteBuffer()}. *

    * The resulting memory segment keeps a reference to the backing buffer, keeping it reachable. * @@ -593,7 +677,7 @@ static MemorySegment ofByteBuffer(ByteBuffer bb) { } /** - * Creates a new confined array memory segment that models the memory associated with a given heap-allocated byte array. + * Creates a new array memory segment that models the memory associated with a given heap-allocated byte array. * The returned segment's resource scope is set to the {@linkplain ResourceScope#globalScope() global} resource scope. * * @param arr the primitive array backing the array memory segment. @@ -604,7 +688,7 @@ static MemorySegment ofArray(byte[] arr) { } /** - * Creates a new confined array memory segment that models the memory associated with a given heap-allocated char array. + * Creates a new array memory segment that models the memory associated with a given heap-allocated char array. * The returned segment's resource scope is set to the {@linkplain ResourceScope#globalScope() global} resource scope. * * @param arr the primitive array backing the array memory segment. @@ -615,7 +699,7 @@ static MemorySegment ofArray(char[] arr) { } /** - * Creates a new confined array memory segment that models the memory associated with a given heap-allocated short array. + * Creates a new array memory segment that models the memory associated with a given heap-allocated short array. * The returned segment's resource scope is set to the {@linkplain ResourceScope#globalScope() global} resource scope. * * @param arr the primitive array backing the array memory segment. @@ -626,7 +710,7 @@ static MemorySegment ofArray(short[] arr) { } /** - * Creates a new confined array memory segment that models the memory associated with a given heap-allocated int array. + * Creates a new array memory segment that models the memory associated with a given heap-allocated int array. * The returned segment's resource scope is set to the {@linkplain ResourceScope#globalScope() global} resource scope. * * @param arr the primitive array backing the array memory segment. @@ -637,7 +721,7 @@ static MemorySegment ofArray(int[] arr) { } /** - * Creates a new confined array memory segment that models the memory associated with a given heap-allocated float array. + * Creates a new array memory segment that models the memory associated with a given heap-allocated float array. * The returned segment's resource scope is set to the {@linkplain ResourceScope#globalScope() global} resource scope. * * @param arr the primitive array backing the array memory segment. @@ -648,7 +732,7 @@ static MemorySegment ofArray(float[] arr) { } /** - * Creates a new confined array memory segment that models the memory associated with a given heap-allocated long array. + * Creates a new array memory segment that models the memory associated with a given heap-allocated long array. * The returned segment's resource scope is set to the {@linkplain ResourceScope#globalScope() global} resource scope. * * @param arr the primitive array backing the array memory segment. @@ -659,7 +743,7 @@ static MemorySegment ofArray(long[] arr) { } /** - * Creates a new confined array memory segment that models the memory associated with a given heap-allocated double array. + * Creates a new array memory segment that models the memory associated with a given heap-allocated double array. * The returned segment's resource scope is set to the {@linkplain ResourceScope#globalScope() global} resource scope. * * @param arr the primitive array backing the array memory segment. @@ -669,8 +753,49 @@ static MemorySegment ofArray(double[] arr) { return HeapMemorySegmentImpl.OfDouble.fromArray(arr); } + + /** + * Creates a new native memory segment with given size and resource scope, and whose base address is the given address. + * This method can be useful when interacting with custom + * native memory sources (e.g. custom allocators), where an address to some + * underlying memory region is typically obtained from native code (often as a plain {@code long} value). + * The returned segment is not read-only (see {@link MemorySegment#isReadOnly()}), and is associated with the + * provided resource scope. + *

    + * Clients should ensure that the address and bounds refer to a valid region of memory that is accessible for reading and, + * if appropriate, writing; an attempt to access an invalid memory location from Java code will either return an arbitrary value, + * have no visible effect, or cause an unspecified exception to be thrown. + *

    + * 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. + * + * + * @param address the returned segment's base address. + * @param bytesSize the desired size. + * @param scope the native segment scope. + * @return a new native memory segment with given base address, size and scope. + * @throws IllegalArgumentException if {@code bytesSize <= 0}. + * @throws IllegalStateException if the provided scope has been already closed, + * or if access occurs from a thread other than the thread owning the scope. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + static MemorySegment ofAddress(MemoryAddress address, long bytesSize, ResourceScope scope) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + Objects.requireNonNull(address); + Objects.requireNonNull(scope); + if (bytesSize <= 0) { + throw new IllegalArgumentException("Invalid size : " + bytesSize); + } + return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(address, bytesSize, (ResourceScopeImpl)scope); + } + /** - * Creates a new confined native memory segment that models a newly allocated block of off-heap memory with given layout + * Creates a new native memory segment that models a newly allocated block of off-heap memory with given layout * and resource scope. A client is responsible make sure that the resource scope associated with the returned segment is closed * when the segment is no longer in use. Failure to do so will result in off-heap memory leaks. *

    @@ -695,7 +820,7 @@ static MemorySegment allocateNative(MemoryLayout layout, ResourceScope scope) { } /** - * Creates a new confined native memory segment that models a newly allocated block of off-heap memory with given size (in bytes) + * Creates a new native memory segment that models a newly allocated block of off-heap memory with given size (in bytes) * and resource scope. A client is responsible make sure that the resource scope associated with the returned segment is closed * when the segment is no longer in use. Failure to do so will result in off-heap memory leaks. *

    @@ -718,7 +843,7 @@ static MemorySegment allocateNative(long bytesSize, ResourceScope scope) { } /** - * Creates a new confined native memory segment that models a newly allocated block of off-heap memory with given size + * Creates a new native memory segment that models a newly allocated block of off-heap memory with given size * (in bytes), alignment constraint (in bytes) and resource scope. A client is responsible make sure that the resource * scope associated with the returned segment is closed when the segment is no longer in use. * Failure to do so will result in off-heap memory leaks. @@ -756,7 +881,7 @@ static MemorySegment allocateNative(long bytesSize, long alignmentBytes, Resourc *

    * The content of a mapped memory segment can change at any time, for example * if the content of the corresponding region of the mapped file is changed by - * this (or another) program. Whether or not such changes occur, and when they + * this (or another) program. Whether such changes occur, and when they * occur, is operating-system dependent and therefore unspecified. *

    * All or part of a mapped memory segment may become @@ -774,17 +899,17 @@ static MemorySegment allocateNative(long bytesSize, long alignmentBytes, Resourc * @param path the path to the file to memory map. * @param bytesOffset the offset (expressed in bytes) within the file at which the mapped segment is to start. * @param bytesSize the size (in bytes) of the mapped memory backing the memory segment. - * @param mapMode a file mapping mode, see {@link FileChannel#map(FileChannel.MapMode, long, long)}; the chosen mapping mode + * @param mapMode a file mapping mode, see {@link FileChannel#map(FileChannel.MapMode, long, long)}; the mapping mode * might affect the behavior of the returned memory mapped segment (see {@link #force()}). * @param scope the segment scope. - * @return a new confined mapped memory segment. + * @return a new mapped memory segment. * @throws IllegalArgumentException if {@code bytesOffset < 0}, {@code bytesSize < 0}, or if {@code path} is not associated * with the default file system. * @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other * than the thread owning {@code scope}. * @throws UnsupportedOperationException if an unsupported map mode is specified. * @throws IOException if the specified path does not point to an existing file, or if some other I/O error occurs. - * @throws SecurityException If a security manager is installed and it denies an unspecified permission required by the implementation. + * @throws SecurityException If a security manager is installed, and it denies an unspecified permission required by the implementation. * In the case of the default provider, the {@link SecurityManager#checkRead(String)} method is invoked to check * read access if the file is opened for reading. The {@link SecurityManager#checkWrite(String)} method is invoked to check * write access if the file is opened for writing. @@ -795,27 +920,738 @@ static MemorySegment mapFile(Path path, long bytesOffset, long bytesSize, FileCh } /** - * Returns a native memory segment whose base address is {@link MemoryAddress#NULL} and whose size is {@link Long#MAX_VALUE}. - * This method can be very useful when dereferencing memory addresses obtained when interacting with native libraries. - * The returned segment is associated with the global resource scope (see {@link ResourceScope#globalScope()}). - * Equivalent to (but likely more efficient than) the following code: - *

    {@code
    -    MemoryAddress.NULL.asSegment(Long.MAX_VALUE)
    -     * }
    + * Performs a bulk copy from source segment to destination segment. More specifically, the bytes at offset + * {@code srcOffset} through {@code srcOffset + bytes - 1} in the source segment are copied into the destination + * segment at offset {@code dstOffset} through {@code dstOffset + bytes - 1}. *

    - * 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. + * If the source segment overlaps with this segment, then the copying is performed as if the bytes at + * offset {@code srcOffset} through {@code srcOffset + bytes - 1} in the source segment were first copied into a + * temporary segment with size {@code bytes}, and then the contents of the temporary segment were copied into + * the destination segment at offset {@code dstOffset} through {@code dstOffset + bytes - 1}. + *

    + * The result of a bulk copy is unspecified if, in the uncommon case, the source segment and the destination segment + * do not overlap, but refer to overlapping regions of the same backing storage using different addresses. + * For example, this may occur if the same file is {@linkplain MemorySegment#mapFile mapped} to two segments. + *

    + * Calling this method is equivalent to the following code: + *

    {@code
    +    MemorySegment.copy(srcSegment, ValueLayout.JAVA_BYTE, srcOffset, dstSegment, ValueLayout.JAVA_BYTE, dstOffset, bytes);
    +     * }
    + * @param srcSegment the source segment. + * @param srcOffset the starting offset, in bytes, of the source segment. + * @param dstSegment the destination segment. + * @param dstOffset the starting offset, in bytes, of the destination segment. + * @param bytes the number of bytes to be copied. + * @throws IllegalStateException if either the scope associated with the source segment or the scope associated + * with the destination segment have been already closed, or if access occurs from a thread other than the thread + * owning either scopes. + * @throws IndexOutOfBoundsException if {@code srcOffset + bytes > srcSegment.byteSize()} or if + * {@code dstOffset + bytes > dstSegment.byteSize()}, or if either {@code srcOffset}, {@code dstOffset} + * or {@code bytes} are {@code < 0}. + * @throws UnsupportedOperationException if the destination segment is read-only (see {@link #isReadOnly()}). + */ + @ForceInline + static void copy(MemorySegment srcSegment, long srcOffset, MemorySegment dstSegment, long dstOffset, long bytes) { + copy(srcSegment, ValueLayout.JAVA_BYTE, srcOffset, dstSegment, ValueLayout.JAVA_BYTE, dstOffset, bytes); + } + + /** + * Performs a bulk copy from source segment to destination segment. More specifically, if {@code S} is the byte size + * of the element layouts, the bytes at offset {@code srcOffset} through {@code srcOffset + (elementCount * S) - 1} + * in the source segment are copied into the destination segment at offset {@code dstOffset} through {@code dstOffset + (elementCount * S) - 1}. + *

    + * The copy occurs in an element-wise fashion: the bytes in the source segment are interpreted as a sequence of elements + * whose layout is {@code srcElementLayout}, whereas the bytes in the destination segment are interpreted as a sequence of + * elements whose layout is {@code dstElementLayout}. Both element layouts must have same size {@code S}. + * If the byte order of the two element layouts differ, the bytes corresponding to each element to be copied + * are swapped accordingly during the copy operation. + *

    + * If the source segment overlaps with this segment, then the copying is performed as if the bytes at + * offset {@code srcOffset} through {@code srcOffset + (elementCount * S) - 1} in the source segment were first copied into a + * temporary segment with size {@code bytes}, and then the contents of the temporary segment were copied into + * the destination segment at offset {@code dstOffset} through {@code dstOffset + (elementCount * S) - 1}. + *

    + * The result of a bulk copy is unspecified if, in the uncommon case, the source segment and the destination segment + * do not overlap, but refer to overlapping regions of the same backing storage using different addresses. + * For example, this may occur if the same file is {@linkplain MemorySegment#mapFile mapped} to two segments. + * @param srcSegment the source segment. + * @param srcElementLayout the element layout associated with the source segment. + * @param srcOffset the starting offset, in bytes, of the source segment. + * @param dstSegment the destination segment. + * @param dstElementLayout the element layout associated with the destination segment. + * @param dstOffset the starting offset, in bytes, of the destination segment. + * @param elementCount the number of elements to be copied. + * @throws IllegalArgumentException if the element layouts have different sizes, if the source offset is incompatible + * with the alignment constraints in the source element layout, or if the destination offset is incompatible with the + * alignment constraints in the destination element layout. + * @throws IllegalStateException if either the scope associated with the source segment or the scope associated + * with the destination segment have been already closed, or if access occurs from a thread other than the thread + * owning either scopes. + * @throws IndexOutOfBoundsException if {@code srcOffset + (elementCount * S) > srcSegment.byteSize()} or if + * {@code dstOffset + (elementCount * S) > dstSegment.byteSize()}, where {@code S} is the byte size + * of the element layouts, or if either {@code srcOffset}, {@code dstOffset} or {@code elementCount} are {@code < 0}. + * @throws UnsupportedOperationException if the destination segment is read-only (see {@link #isReadOnly()}). + */ + @ForceInline + static void copy(MemorySegment srcSegment, ValueLayout srcElementLayout, long srcOffset, MemorySegment dstSegment, + ValueLayout dstElementLayout, long dstOffset, long elementCount) { + Objects.requireNonNull(srcSegment); + Objects.requireNonNull(srcElementLayout); + Objects.requireNonNull(dstSegment); + Objects.requireNonNull(dstElementLayout); + AbstractMemorySegmentImpl srcImpl = (AbstractMemorySegmentImpl)srcSegment; + AbstractMemorySegmentImpl dstImpl = (AbstractMemorySegmentImpl)dstSegment; + if (srcElementLayout.byteSize() != dstElementLayout.byteSize()) { + throw new IllegalArgumentException("Source and destination layouts must have same sizes"); + } + if (srcOffset % srcElementLayout.byteAlignment() != 0) { + throw new IllegalArgumentException("Source segment incompatible with alignment constraints"); + } + if (dstOffset % dstElementLayout.byteAlignment() != 0) { + throw new IllegalArgumentException("Target segment incompatible with alignment constraints"); + } + long size = elementCount * srcElementLayout.byteSize(); + srcImpl.checkAccess(srcOffset, size, true); + dstImpl.checkAccess(dstOffset, size, false); + if (srcElementLayout.byteSize() == 1 || srcElementLayout.order() == dstElementLayout.order()) { + ScopedMemoryAccess.getScopedMemoryAccess().copyMemory(srcImpl.scope(), dstImpl.scope(), + srcImpl.unsafeGetBase(), srcImpl.unsafeGetOffset() + srcOffset, + dstImpl.unsafeGetBase(), dstImpl.unsafeGetOffset() + dstOffset, size); + } else { + ScopedMemoryAccess.getScopedMemoryAccess().copySwapMemory(srcImpl.scope(), dstImpl.scope(), + srcImpl.unsafeGetBase(), srcImpl.unsafeGetOffset() + srcOffset, + dstImpl.unsafeGetBase(), dstImpl.unsafeGetOffset() + dstOffset, size, srcElementLayout.byteSize()); + } + } + + /** + * Reads a byte from this segment and offset with given layout. * - * @return a memory segment whose base address is {@link MemoryAddress#NULL} and whose size is {@link Long#MAX_VALUE}. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or - * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + * @param layout the layout of the memory region to be read. + * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this read operation can be expressed as {@code address().toRowLongValue() + offset}. + * @return a byte value read from this address. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * memory segment. */ - @CallerSensitive - static MemorySegment globalNativeSegment() { - Reflection.ensureNativeAccess(Reflection.getCallerClass()); - return NativeMemorySegmentImpl.EVERYTHING; + @ForceInline + default byte get(ValueLayout.OfByte layout, long offset) { + return (byte)layout.accessHandle().get(this, offset); + } + + /** + * Writes a byte to this segment and offset with given layout. + * + * @param layout the layout of the memory region to be written. + * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this write operation can be expressed as {@code address().toRowLongValue() + offset}. + * @param value the byte value to be written. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + */ + @ForceInline + default void set(ValueLayout.OfByte layout, long offset, byte value) { + layout.accessHandle().set(this, offset, value); + } + + /** + * Reads a boolean from this segment and offset with given layout. + * + * @param layout the layout of the memory region to be read. + * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this read operation can be expressed as {@code address().toRowLongValue() + offset}. + * @return a boolean value read from this address. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * memory segment. + */ + @ForceInline + default boolean get(ValueLayout.OfBoolean layout, long offset) { + return (boolean)layout.accessHandle().get(this, offset); + } + + /** + * Writes a boolean to this segment and offset with given layout. + * + * @param layout the layout of the memory region to be written. + * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this write operation can be expressed as {@code address().toRowLongValue() + offset}. + * @param value the boolean value to be written. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + */ + @ForceInline + default void set(ValueLayout.OfBoolean layout, long offset, boolean value) { + layout.accessHandle().set(this, offset, value); + } + + /** + * Reads a char from this segment and offset with given layout. + * + * @param layout the layout of the memory region to be read. + * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this read operation can be expressed as {@code address().toRowLongValue() + offset}. + * @return a char value read from this address. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * memory segment. + */ + @ForceInline + default char get(ValueLayout.OfChar layout, long offset) { + return (char)layout.accessHandle().get(this, offset); + } + + /** + * Writes a char to this segment and offset with given layout. + * + * @param layout the layout of the memory region to be written. + * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this write operation can be expressed as {@code address().toRowLongValue() + offset}. + * @param value the char value to be written. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + */ + @ForceInline + default void set(ValueLayout.OfChar layout, long offset, char value) { + layout.accessHandle().set(this, offset, value); + } + + /** + * Reads a short from this segment and offset with given layout. + * + * @param layout the layout of the memory region to be read. + * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this read operation can be expressed as {@code address().toRowLongValue() + offset}. + * @return a short value read from this address. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * memory segment. + */ + @ForceInline + default short get(ValueLayout.OfShort layout, long offset) { + return (short)layout.accessHandle().get(this, offset); + } + + /** + * Writes a short to this segment and offset with given layout. + * + * @param layout the layout of the memory region to be written. + * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this write operation can be expressed as {@code address().toRowLongValue() + offset}. + * @param value the short value to be written. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + */ + @ForceInline + default void set(ValueLayout.OfShort layout, long offset, short value) { + layout.accessHandle().set(this, offset, value); + } + + /** + * Reads an int from this segment and offset with given layout. + * + * @param layout the layout of the memory region to be read. + * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this read operation can be expressed as {@code address().toRowLongValue() + offset}. + * @return an int value read from this address. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * memory segment. + */ + @ForceInline + default int get(ValueLayout.OfInt layout, long offset) { + return (int)layout.accessHandle().get(this, offset); + } + + /** + * Writes an int to this segment and offset with given layout. + * + * @param layout the layout of the memory region to be written. + * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this write operation can be expressed as {@code address().toRowLongValue() + offset}. + * @param value the int value to be written. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + */ + @ForceInline + default void set(ValueLayout.OfInt layout, long offset, int value) { + layout.accessHandle().set(this, offset, value); + } + + /** + * Reads a float from this segment and offset with given layout. + * + * @param layout the layout of the memory region to be read. + * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this read operation can be expressed as {@code address().toRowLongValue() + offset}. + * @return a float value read from this address. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * memory segment. + */ + @ForceInline + default float get(ValueLayout.OfFloat layout, long offset) { + return (float)layout.accessHandle().get(this, offset); + } + + /** + * Writes a float to this segment and offset with given layout. + * + * @param layout the layout of the memory region to be written. + * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this write operation can be expressed as {@code address().toRowLongValue() + offset}. + * @param value the float value to be written. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + */ + @ForceInline + default void set(ValueLayout.OfFloat layout, long offset, float value) { + layout.accessHandle().set(this, offset, value); + } + + /** + * Reads a long from this segment and offset with given layout. + * + * @param layout the layout of the memory region to be read. + * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this read operation can be expressed as {@code address().toRowLongValue() + offset}. + * @return a long value read from this address. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * memory segment. + */ + @ForceInline + default long get(ValueLayout.OfLong layout, long offset) { + return (long)layout.accessHandle().get(this, offset); + } + + /** + * Writes a long to this segment and offset with given layout. + * + * @param layout the layout of the memory region to be written. + * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this write operation can be expressed as {@code address().toRowLongValue() + offset}. + * @param value the long value to be written. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + */ + @ForceInline + default void set(ValueLayout.OfLong layout, long offset, long value) { + layout.accessHandle().set(this, offset, value); + } + + /** + * Reads a double from this segment and offset with given layout. + * + * @param layout the layout of the memory region to be read. + * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this read operation can be expressed as {@code address().toRowLongValue() + offset}. + * @return a double value read from this address. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * memory segment. + */ + @ForceInline + default double get(ValueLayout.OfDouble layout, long offset) { + return (double)layout.accessHandle().get(this, offset); + } + + /** + * Writes a double to this segment and offset with given layout. + * + * @param layout the layout of the memory region to be written. + * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this write operation can be expressed as {@code address().toRowLongValue() + offset}. + * @param value the double value to be written. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + */ + @ForceInline + default void set(ValueLayout.OfDouble layout, long offset, double value) { + layout.accessHandle().set(this, offset, value); + } + + /** + * Reads an address from this segment and offset with given layout. + * + * @param layout the layout of the memory region to be read. + * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this read operation can be expressed as {@code address().toRowLongValue() + offset}. + * @return an address value read from this address. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * memory segment. + */ + @ForceInline + default MemoryAddress get(ValueLayout.OfAddress layout, long offset) { + return (MemoryAddress)layout.accessHandle().get(this, offset); + } + + /** + * Writes an address to this segment and offset with given layout. + * + * @param layout the layout of the memory region to be written. + * @param offset offset in bytes (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this write operation can be expressed as {@code address().toRowLongValue() + offset}. + * @param value the address value to be written. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + */ + @ForceInline + default void set(ValueLayout.OfAddress layout, long offset, Addressable value) { + layout.accessHandle().set(this, offset, value.address()); + } + + /** + * Reads a char from this segment and index, scaled by given layout size. + * + * @param layout the layout of the memory region to be read. + * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this read operation can be expressed as {@code address().toRowLongValue() + (index * layout.byteSize())}. + * @return a char value read from this address. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * memory segment. + */ + @ForceInline + default char getAtIndex(ValueLayout.OfChar layout, long index) { + return (char)layout.accessHandle().get(this, Utils.scaleOffset(this, index, layout.byteSize())); + } + + /** + * Writes a char to this segment and index, scaled by given layout size. + * + * @param layout the layout of the memory region to be written. + * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this write operation can be expressed as {@code address().toRowLongValue() + (index * layout.byteSize())}. + * @param value the char value to be written. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + */ + @ForceInline + default void setAtIndex(ValueLayout.OfChar layout, long index, char value) { + layout.accessHandle().set(this, Utils.scaleOffset(this, index, layout.byteSize()), value); + } + + /** + * Reads a short from this segment and index, scaled by given layout size. + * + * @param layout the layout of the memory region to be read. + * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this read operation can be expressed as {@code address().toRowLongValue() + (index * layout.byteSize())}. + * @return a short value read from this address. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * memory segment. + */ + @ForceInline + default short getAtIndex(ValueLayout.OfShort layout, long index) { + return (short)layout.accessHandle().get(this, Utils.scaleOffset(this, index, layout.byteSize())); + } + + /** + * Writes a short to this segment and index, scaled by given layout size. + * + * @param layout the layout of the memory region to be written. + * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this write operation can be expressed as {@code address().toRowLongValue() + (index * layout.byteSize())}. + * @param value the short value to be written. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + */ + @ForceInline + default void setAtIndex(ValueLayout.OfShort layout, long index, short value) { + layout.accessHandle().set(this, Utils.scaleOffset(this, index, layout.byteSize()), value); + } + + /** + * Reads an int from this segment and index, scaled by given layout size. + * + * @param layout the layout of the memory region to be read. + * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this read operation can be expressed as {@code address().toRowLongValue() + (index * layout.byteSize())}. + * @return an int value read from this address. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * memory segment. + */ + @ForceInline + default int getAtIndex(ValueLayout.OfInt layout, long index) { + return (int)layout.accessHandle().get(this, Utils.scaleOffset(this, index, layout.byteSize())); + } + + /** + * Writes an int to this segment and index, scaled by given layout size. + * + * @param layout the layout of the memory region to be written. + * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this write operation can be expressed as {@code address().toRowLongValue() + (index * layout.byteSize())}. + * @param value the int value to be written. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + */ + @ForceInline + default void setAtIndex(ValueLayout.OfInt layout, long index, int value) { + layout.accessHandle().set(this, Utils.scaleOffset(this, index, layout.byteSize()), value); + } + + /** + * Reads a float from this segment and index, scaled by given layout size. + * + * @param layout the layout of the memory region to be read. + * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this read operation can be expressed as {@code address().toRowLongValue() + (index * layout.byteSize())}. + * @return a float value read from this address. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * memory segment. + */ + @ForceInline + default float getAtIndex(ValueLayout.OfFloat layout, long index) { + return (float)layout.accessHandle().get(this, Utils.scaleOffset(this, index, layout.byteSize())); + } + + /** + * Writes a float to this segment and index, scaled by given layout size. + * + * @param layout the layout of the memory region to be written. + * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this write operation can be expressed as {@code address().toRowLongValue() + (index * layout.byteSize())}. + * @param value the float value to be written. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + */ + @ForceInline + default void setAtIndex(ValueLayout.OfFloat layout, long index, float value) { + layout.accessHandle().set(this, Utils.scaleOffset(this, index, layout.byteSize()), value); + } + + /** + * Reads a long from this segment and index, scaled by given layout size. + * + * @param layout the layout of the memory region to be read. + * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this read operation can be expressed as {@code address().toRowLongValue() + (index * layout.byteSize())}. + * @return a long value read from this address. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * memory segment. + */ + @ForceInline + default long getAtIndex(ValueLayout.OfLong layout, long index) { + return (long)layout.accessHandle().get(this, Utils.scaleOffset(this, index, layout.byteSize())); + } + + /** + * Writes a long to this segment and index, scaled by given layout size. + * + * @param layout the layout of the memory region to be written. + * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this write operation can be expressed as {@code address().toRowLongValue() + (index * layout.byteSize())}. + * @param value the long value to be written. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + */ + @ForceInline + default void setAtIndex(ValueLayout.OfLong layout, long index, long value) { + layout.accessHandle().set(this, Utils.scaleOffset(this, index, layout.byteSize()), value); + } + + /** + * Reads a double from this segment and index, scaled by given layout size. + * + * @param layout the layout of the memory region to be read. + * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this read operation can be expressed as {@code address().toRowLongValue() + (index * layout.byteSize())}. + * @return a double value read from this address. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * memory segment. + */ + @ForceInline + default double getAtIndex(ValueLayout.OfDouble layout, long index) { + return (double)layout.accessHandle().get(this, Utils.scaleOffset(this, index, layout.byteSize())); + } + + /** + * Writes a double to this segment and index, scaled by given layout size. + * + * @param layout the layout of the memory region to be written. + * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this write operation can be expressed as {@code address().toRowLongValue() + (index * layout.byteSize())}. + * @param value the double value to be written. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + */ + @ForceInline + default void setAtIndex(ValueLayout.OfDouble layout, long index, double value) { + layout.accessHandle().set(this, Utils.scaleOffset(this, index, layout.byteSize()), value); + } + + /** + * Reads an address from this segment and index, scaled by given layout size. + * + * @param layout the layout of the memory region to be read. + * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this read operation can be expressed as {@code address().toRowLongValue() + (index * layout.byteSize())}. + * @return an address value read from this address. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * memory segment. + */ + @ForceInline + default MemoryAddress getAtIndex(ValueLayout.OfAddress layout, long index) { + return (MemoryAddress)layout.accessHandle().get(this, Utils.scaleOffset(this, index, layout.byteSize())); + } + + /** + * Writes an address to this segment and index, scaled by given layout size. + * + * @param layout the layout of the memory region to be written. + * @param index index (relative to this segment). For instance, if this segment is a {@linkplain #isNative() native} segment, + * the final address of this write operation can be expressed as {@code address().toRowLongValue() + (index * layout.byteSize())}. + * @param value the address value to be written. + * @throws IllegalStateException if the scope associated with this segment has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + */ + @ForceInline + default void setAtIndex(ValueLayout.OfAddress layout, long index, Addressable value) { + layout.accessHandle().set(this, Utils.scaleOffset(this, index, layout.byteSize()), value.address()); + } + + + /** + * Copies a number of elements from a source segment to a destination array, + * starting at a given segment offset (expressed in bytes), and a given array index, using the given source element layout. + * Supported array types are {@code byte[]}, {@code char[]}, {@code short[]}, {@code int[]}, {@code float[]}, {@code long[]} and {@code double[]}. + * @param srcSegment the source segment. + * @param srcLayout the source element layout. If the byte order associated with the layout is + * different from the native order, a byte swap operation will be performed on each array element. + * @param srcOffset the starting offset, in bytes, of the source segment. + * @param dstArray the destination array. + * @param dstIndex the starting index of the destination array. + * @param elementCount the number of array elements to be copied. + * @throws IllegalArgumentException if {@code dstArray} is not an array, or if it is an array but whose type is not supported, + * or if the destination array component type does not match the carrier of the source element layout. + */ + @ForceInline + static void copy( + MemorySegment srcSegment, ValueLayout srcLayout, long srcOffset, + Object dstArray, int dstIndex, int elementCount) { + Objects.requireNonNull(srcSegment); + Objects.requireNonNull(dstArray); + Objects.requireNonNull(srcLayout); + long baseAndScale = getBaseAndScale(dstArray.getClass()); + if (dstArray.getClass().componentType() != srcLayout.carrier()) { + throw new IllegalArgumentException("Incompatible value layout: " + srcLayout); + } + int dstBase = (int)baseAndScale; + int dstWidth = (int)(baseAndScale >> 32); + AbstractMemorySegmentImpl srcImpl = (AbstractMemorySegmentImpl)srcSegment; + srcImpl.checkAccess(srcOffset, elementCount * dstWidth, true); + Objects.checkFromIndexSize(dstIndex, elementCount, Array.getLength(dstArray)); + if (dstWidth == 1 || srcLayout.order() == ByteOrder.nativeOrder()) { + ScopedMemoryAccess.getScopedMemoryAccess().copyMemory(srcImpl.scope(), null, + srcImpl.unsafeGetBase(), srcImpl.unsafeGetOffset() + srcOffset, + dstArray, dstBase + (dstIndex * dstWidth), elementCount * dstWidth); + } else { + ScopedMemoryAccess.getScopedMemoryAccess().copySwapMemory(srcImpl.scope(), null, + srcImpl.unsafeGetBase(), srcImpl.unsafeGetOffset() + srcOffset, + dstArray, dstBase + (dstIndex * dstWidth), elementCount * dstWidth, dstWidth); + } + } + + /** + * Copies a number of elements from a source array to a destination segment, + * starting at a given array index, and a given segment offset (expressed in bytes), using the given destination element layout. + * Supported array types are {@code byte[]}, {@code char[]}, {@code short[]}, {@code int[]}, {@code float[]}, {@code long[]} and {@code double[]}. + * @param srcArray the source array. + * @param srcIndex the starting index of the source array. + * @param dstSegment the destination segment. + * @param dstLayout the destination element layout. If the byte order associated with the layout is + * different from the native order, a byte swap operation will be performed on each array element. + * @param dstOffset the starting offset, in bytes, of the destination segment. + * @param elementCount the number of array elements to be copied. + * @throws IllegalArgumentException if {@code srcArray} is not an array, or if it is an array but whose type is not supported, + * or if the source array component type does not match the carrier of the destination element layout. + */ + @ForceInline + static void copy( + Object srcArray, int srcIndex, + MemorySegment dstSegment, ValueLayout dstLayout, long dstOffset, int elementCount) { + Objects.requireNonNull(srcArray); + Objects.requireNonNull(dstSegment); + Objects.requireNonNull(dstLayout); + long baseAndScale = getBaseAndScale(srcArray.getClass()); + if (srcArray.getClass().componentType() != dstLayout.carrier()) { + throw new IllegalArgumentException("Incompatible value layout: " + dstLayout); + } + int srcBase = (int)baseAndScale; + int srcWidth = (int)(baseAndScale >> 32); + Objects.checkFromIndexSize(srcIndex, elementCount, Array.getLength(srcArray)); + AbstractMemorySegmentImpl destImpl = (AbstractMemorySegmentImpl)dstSegment; + destImpl.checkAccess(dstOffset, elementCount * srcWidth, false); + if (srcWidth == 1 || dstLayout.order() == ByteOrder.nativeOrder()) { + ScopedMemoryAccess.getScopedMemoryAccess().copyMemory(null, destImpl.scope(), + srcArray, srcBase + (srcIndex * srcWidth), + destImpl.unsafeGetBase(), destImpl.unsafeGetOffset() + dstOffset, elementCount * srcWidth); + } else { + ScopedMemoryAccess.getScopedMemoryAccess().copySwapMemory(null, destImpl.scope(), + srcArray, srcBase + (srcIndex * srcWidth), + destImpl.unsafeGetBase(), destImpl.unsafeGetOffset() + dstOffset, elementCount * srcWidth, srcWidth); + } + } + + private static long getBaseAndScale(Class arrayType) { + if (arrayType.equals(byte[].class)) { + return (long)Unsafe.ARRAY_BYTE_BASE_OFFSET | ((long)Unsafe.ARRAY_BYTE_INDEX_SCALE << 32); + } else if (arrayType.equals(char[].class)) { + return (long)Unsafe.ARRAY_CHAR_BASE_OFFSET | ((long)Unsafe.ARRAY_CHAR_INDEX_SCALE << 32); + } else if (arrayType.equals(short[].class)) { + return (long)Unsafe.ARRAY_SHORT_BASE_OFFSET | ((long)Unsafe.ARRAY_SHORT_INDEX_SCALE << 32); + } else if (arrayType.equals(int[].class)) { + return (long)Unsafe.ARRAY_INT_BASE_OFFSET | ((long) Unsafe.ARRAY_INT_INDEX_SCALE << 32); + } else if (arrayType.equals(float[].class)) { + return (long)Unsafe.ARRAY_FLOAT_BASE_OFFSET | ((long)Unsafe.ARRAY_FLOAT_INDEX_SCALE << 32); + } else if (arrayType.equals(long[].class)) { + return (long)Unsafe.ARRAY_LONG_BASE_OFFSET | ((long)Unsafe.ARRAY_LONG_INDEX_SCALE << 32); + } else if (arrayType.equals(double[].class)) { + return (long)Unsafe.ARRAY_DOUBLE_BASE_OFFSET | ((long)Unsafe.ARRAY_DOUBLE_INDEX_SCALE << 32); + } else { + throw new IllegalArgumentException("Not a supported array class: " + arrayType.getSimpleName()); + } } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/NativeSymbol.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/NativeSymbol.java new file mode 100644 index 0000000000000..bf137e965d74c --- /dev/null +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/NativeSymbol.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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.incubator.foreign; + +import jdk.internal.foreign.NativeSymbolImpl; +import jdk.internal.reflect.CallerSensitive; +import jdk.internal.reflect.Reflection; + +import java.lang.invoke.MethodHandle; +import java.util.Objects; + +/** + * A native symbol models a reference to a location (typically the entry point of a function) in a native library. + * A native symbol has a name, and is associated with a scope, which governs the native symbol's lifecycle. + * This is useful, since the library a native symbol refers to can be unloaded, thus invalidating the native symbol. + * While native symbols are typically obtained using a {@link SymbolLookup#lookup(String) symbol lookup}, it is also possible to obtain an + * anonymous native symbol, in the form of an {@linkplain CLinker#upcallStub(MethodHandle, FunctionDescriptor, ResourceScope) upcall stub}, + * that is, a reference to a dynamically-generated native symbol which can be used to call back into Java code. + */ +sealed public interface NativeSymbol extends Addressable permits NativeSymbolImpl { + + /** + * Returns the name of this symbol. + * @return the name of this symbol. + */ + String name(); + + /** + * Returns the resource scope associated with this symbol. + * @return the resource scope associated with this symbol. + */ + ResourceScope scope(); + + /** + * Returns the memory address associated with this symbol. + * @throws IllegalStateException if the scope associated with this symbol has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @return The memory address associated with this symbol. + */ + @Override + MemoryAddress address(); + + /** + * Creates a new symbol from given name, address and scope. + *

    + * 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. + * @param name the symbol name. + * @param address the symbol address. + * @param scope the symbol scope. + * @return A new symbol from given name, address and scope. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + static NativeSymbol ofAddress(String name, MemoryAddress address, ResourceScope scope) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + Objects.requireNonNull(name); + Objects.requireNonNull(address); + Objects.requireNonNull(scope); + return new NativeSymbolImpl(name, address, scope); + } +} diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/PaddingLayout.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/PaddingLayout.java index db65c48669b31..0b90e9a1d1d84 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/PaddingLayout.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/PaddingLayout.java @@ -25,10 +25,8 @@ */ package jdk.incubator.foreign; -import java.lang.constant.Constable; import java.lang.constant.ConstantDescs; import java.lang.constant.DynamicConstantDesc; -import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.OptionalLong; @@ -53,11 +51,11 @@ /* package-private */ final class PaddingLayout extends AbstractLayout implements MemoryLayout { PaddingLayout(long size) { - this(size, 1, Map.of()); + this(size, 1, Optional.empty()); } - PaddingLayout(long size, long alignment, Map attributes) { - super(OptionalLong.of(size), alignment, attributes); + PaddingLayout(long size, long alignment, Optional name) { + super(OptionalLong.of(size), alignment, name); } @Override @@ -73,10 +71,9 @@ public boolean equals(Object other) { if (!super.equals(other)) { return false; } - if (!(other instanceof PaddingLayout)) { + if (!(other instanceof PaddingLayout p)) { return false; } - PaddingLayout p = (PaddingLayout)other; return bitSize() == p.bitSize(); } @@ -86,8 +83,8 @@ public int hashCode() { } @Override - PaddingLayout dup(long alignment, Map attributes) { - return new PaddingLayout(bitSize(), alignment, attributes); + PaddingLayout dup(long alignment, Optional name) { + return new PaddingLayout(bitSize(), alignment, name); } @Override @@ -119,12 +116,4 @@ public PaddingLayout withName(String name) { public PaddingLayout withBitAlignment(long alignmentBits) { return (PaddingLayout)super.withBitAlignment(alignmentBits); } - - /** - * {@inheritDoc} - */ - @Override - public PaddingLayout withAttribute(String name, Constable value) { - return (PaddingLayout)super.withAttribute(name, value); - } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/ResourceScope.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/ResourceScope.java index 0a46ab705b361..f2fb32a059439 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/ResourceScope.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/ResourceScope.java @@ -26,9 +26,11 @@ package jdk.incubator.foreign; import jdk.internal.foreign.ResourceScopeImpl; +import jdk.internal.ref.CleanerFactory; import java.lang.invoke.MethodHandle; import java.lang.ref.Cleaner; +import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.util.Objects; @@ -36,109 +38,109 @@ /** * A resource scope manages the lifecycle of one or more resources. Resources (e.g. {@link MemorySegment}) associated - * with a resource scope can only be accessed while the resource scope is alive (see {@link #isAlive()}), - * and by the thread associated with the resource scope (if any). + * with a resource scope can only be accessed while the resource scope is {@linkplain #isAlive() alive}, + * and by the {@linkplain #ownerThread() thread} associated with the resource scope (if any). * - *

    Explicit resource scopes

    + *

    Deterministic deallocation

    * - * Resource scopes obtained from {@link #newConfinedScope()}, {@link #newSharedScope()} support deterministic deallocation; - * We call these resource scopes explicit scopes. Explicit resource scopes can be closed explicitly (see {@link ResourceScope#close()}). - * When a resource scope is closed, it is no longer alive (see {@link #isAlive()}, and subsequent operations on - * resources associated with that scope (e.g. attempting to access a {@link MemorySegment} instance) will fail with {@link IllegalStateException}. + * Resource scopes support deterministic deallocation; that is, they can be {@linkplain ResourceScope#close() closed} + * explicitly. When a resource scope is closed, it is no longer {@link #isAlive() alive}, and subsequent + * operations on resources associated with that scope (e.g. attempting to access a {@link MemorySegment} instance) + * will fail with {@link IllegalStateException}. *

    - * Closing a resource scope will cause all the cleanup actions associated with that scope (see {@link #addCloseAction(Runnable)}) to be called. + * Closing a resource scope will cause all the {@linkplain #addCloseAction(Runnable) close actions} associated with that scope to be called. * Moreover, closing a resource scope might trigger the releasing of the underlying memory resources associated with said scope; for instance: *

      - *
    • closing the scope associated with a native memory segment results in freeing the native memory associated with it - * (see {@link MemorySegment#allocateNative(long, ResourceScope)}, or {@link SegmentAllocator#arenaAllocator(ResourceScope)})
    • - *
    • closing the scope associated with a mapped memory segment results in the backing memory-mapped file to be unmapped - * (see {@link MemorySegment#mapFile(Path, long, long, FileChannel.MapMode, ResourceScope)})
    • - *
    • closing the scope associated with an upcall stub results in releasing the stub - * (see {@link CLinker#upcallStub(MethodHandle, FunctionDescriptor, ResourceScope)}
    • + *
    • closing the scope associated with a {@linkplain MemorySegment#allocateNative(long, long, ResourceScope) native memory segment} + * results in freeing the native memory associated with it;
    • + *
    • closing the scope associated with a {@linkplain MemorySegment#mapFile(Path, long, long, FileChannel.MapMode, ResourceScope) mapped memory segment} + * results in the backing memory-mapped file to be unmapped;
    • + *
    • closing the scope associated with an {@linkplain CLinker#upcallStub(MethodHandle, FunctionDescriptor, ResourceScope) upcall stub} + * results in releasing the stub;
    • + *
    • closing the scope associated with a {@linkplain VaList variable arity list} results in releasing the memory + * associated with that variable arity list instance.
    • *
    - *

    - * Sometimes, explicit scopes can be associated with a {@link Cleaner} instance (see {@link #newConfinedScope(Cleaner)} and - * {@link #newSharedScope(Cleaner)}). We call these resource scopes managed resource scopes. A managed resource scope - * is closed automatically once the scope instance becomes unreachable. - *

    - * Managed scopes can be useful to allow for predictable, deterministic resource deallocation, while still prevent accidental native memory leaks. - * In case a managed resource scope is closed explicitly, no further action will be taken when the scope becomes unreachable; - * that is, cleanup actions (see {@link #addCloseAction(Runnable)}) associated with a resource scope, whether managed or not, - * are called exactly once. * - *

    Implicit resource scopes

    + *

    Implicit deallocation

    + * + * Resource scopes can be associated with a {@link Cleaner} instance, so that they are also closed automatically, + * once the scope instance becomes unreachable. + * This can be useful to allow for predictable, deterministic resource deallocation, while still preventing accidental + * native memory leaks. In case a managed resource scope is closed explicitly, no further action will be taken when + * the scope becomes unreachable; that is, {@linkplain #addCloseAction(Runnable) close actions} associated with a + * resource scope, whether managed or not, are called exactly once. + * + *

    Global scope

    * - * Resource scopes obtained from {@link #newImplicitScope()} cannot be closed explicitly. We call these resource scopes - * implicit scopes. Calling {@link #close()} on an implicit resource scope always results in an exception. - * Resources associated with implicit scopes are released once the scope instance becomes - * unreachable. - *

    * An important implicit resource scope is the so called {@linkplain #globalScope() global scope}; the global scope is - * an implicit scope that is guaranteed to never become unreachable. - * As a results, the global scope will never attempt to release resources associated with it. Such resources must, where + * a resource scope that cannot be closed, either explicitly or implicitly. As a result, the global scope will never + * attempt to release resources associated with it. Examples of resources associated with the global scope are: + *

      + *
    • heap segments created from {@linkplain MemorySegment#ofArray(int[]) arrays} or + * {@linkplain MemorySegment#ofByteBuffer(ByteBuffer) buffers};
    • + *
    • variable arity lists {@linkplain VaList#ofAddress(MemoryAddress, ResourceScope) obtained} from raw memory addresses; + *
    • native symbols {@linkplain SymbolLookup#lookup(String) obtained} from a {@linkplain SymbolLookup#loaderLookup() loader lookup}, + * or from the {@link CLinker}.
    • + *
    + * In other words, the global scope is used to indicate that the lifecycle of one or more resources must, where * needed, be managed independently by clients. * *

    Thread confinement

    * - * Resource scopes can be further divided into two categories: thread-confined resource scopes, and shared + * Resource scopes can be divided into two categories: thread-confined resource scopes, and shared * resource scopes. *

    - * Confined resource scopes (see {@link #newConfinedScope()}), support strong thread-confinement guarantees. Upon creation, - * they are assigned an owner thread, typically the thread which initiated the creation operation (see {@link #ownerThread()}). + * {@linkplain #newConfinedScope() Confined resource scopes}, support strong thread-confinement guarantees. Upon creation, + * they are assigned an {@linkplain #ownerThread() owner thread}, typically the thread which initiated the creation operation. * After creating a confined resource scope, only the owner thread will be allowed to directly manipulate the resources * associated with this resource scope. Any attempt to perform resource access from a thread other than the * owner thread will result in a runtime failure. *

    - * Shared resource scopes (see {@link #newSharedScope()} and {@link #newImplicitScope()}), on the other hand, have no owner thread; - * as such resources associated with this shared resource scopes can be accessed by multiple threads. + * {@linkplain #newSharedScope() Shared resource scopes}, on the other hand, have no owner thread; + * as such, resources associated with shared resource scopes can be accessed by multiple threads. * This might be useful when multiple threads need to access the same resource concurrently (e.g. in the case of parallel processing). - * For instance, a client might obtain a {@link Spliterator} from a shared segment, which can then be used to slice the + * For instance, a client might obtain a {@link Spliterator} from a segment backed by a shared scope, which can then be used to slice the * segment and allow multiple threads to work in parallel on disjoint segment slices. The following code can be used to sum * all int values in a memory segment in parallel: * *

    {@code
    -SequenceLayout SEQUENCE_LAYOUT = MemoryLayout.sequenceLayout(1024, MemoryLayouts.JAVA_INT);
     try (ResourceScope scope = ResourceScope.newSharedScope()) {
    +    SequenceLayout SEQUENCE_LAYOUT = MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_INT);
         MemorySegment segment = MemorySegment.allocateNative(SEQUENCE_LAYOUT, scope);
    -    VarHandle VH_int = SEQUENCE_LAYOUT.elementLayout().varHandle(int.class);
    -    int sum = StreamSupport.stream(segment.spliterator(SEQUENCE_LAYOUT), true)
    -        .mapToInt(s -> (int)VH_int.get(s.address()))
    -        .sum();
    +    int sum = segment.elements(ValueLayout.JAVA_INT).parallel()
    +                        .mapToInt(s -> s.get(ValueLayout.JAVA_INT, 0))
    +                        .sum();
     }
      * }
    * *

    - * Explicit shared resource scopes, while powerful, must be used with caution: if one or more threads accesses + * Shared resource scopes, while powerful, must be used with caution: if one or more threads accesses * a resource associated with a shared scope while the scope is being closed from another thread, an exception might occur on both * the accessing and the closing threads. Clients should refrain from attempting to close a shared resource scope repeatedly * (e.g. keep calling {@link #close()} until no exception is thrown). Instead, clients of shared resource scopes - * should always ensure that proper synchronization mechanisms (e.g. using resource scope handles, see below) are put in place + * should always ensure that proper synchronization mechanisms (e.g. using temporal dependencies, see below) are put in place * so that threads closing shared resource scopes can never race against threads accessing resources managed by same scopes. * - *

    Resource scope handles

    + *

    Temporal dependencies

    * - * Resource scopes can be made non-closeable by acquiring one or more resource scope handles (see - * {@link #acquire()}. A resource scope handle can be used to make sure that resources associated with a given resource scope - * (either explicit or implicit) cannot be released for a certain period of time - e.g. during a critical region of code - * involving one or more resources associated with the scope. For instance, an explicit resource scope can only be closed - * after all the handles acquired against that scope have been closed (see {@link Handle#close()}). + * Resource scopes can depend on each other. More specifically, a scope can feature + * {@linkplain #keepAlive(ResourceScope) temporal dependencies} on one or more other resource scopes. + * Such a resource scope cannot be closed (either implicitly or explicitly) until all the scopes it depends on + * have also been closed. + *

    * This can be useful when clients need to perform a critical operation on a memory segment, during which they have - * to ensure that the segment will not be released; this can be done as follows: + * to ensure that the scope associated with that segment will not be closed; this can be done as follows: * *

    {@code
     MemorySegment segment = ...
    -ResourceScope.Handle segmentHandle = segment.scope().acquire()
    -try {
    -   
    -} finally {
    -   segment.scope().release(segmentHandle);
    +try (ResourceScope criticalScope = ResourceScope.newConfinedScope()) {
    +    criticalScope.keepAlive(segment.scope());
    +    
     }
      * }
    * - * Acquiring implicit resource scopes is also possible, but it is often unnecessary: since resources associated with - * an implicit scope will only be released when the scope becomes unreachable, - * clients can use e.g. {@link java.lang.ref.Reference#reachabilityFence(Object)} to make sure that resources associated - * with implicit scopes are not released prematurely. That said, the above code snippet works (trivially) for implicit scopes too. + * Note that a resource scope does not become unreachable + * until all the scopes it depends on have been closed. * * @implSpec * Implementations of this interface are immutable, thread-safe and value-based. @@ -158,15 +160,7 @@ public sealed interface ResourceScope extends AutoCloseable permits ResourceScop Thread ownerThread(); /** - * Is this resource scope an implicit scope? - * @return true if this scope is an implicit scope. - * @see #newImplicitScope() - * @see #globalScope() - */ - boolean isImplicit(); - - /** - * Closes this resource scope. As a side-effect, if this operation completes without exceptions, this scope will be marked + * Closes this resource scope. As a side effect, if this operation completes without exceptions, this scope will be marked * as not alive, and subsequent operations on resources associated with this scope will fail with {@link IllegalStateException}. * Additionally, upon successful closure, all native resources associated with this resource scope will be released. * @@ -180,9 +174,9 @@ public sealed interface ResourceScope extends AutoCloseable permits ResourceScop *
  • this resource scope is not alive *
  • this resource scope is confined, and this method is called from a thread other than the thread owning this resource scope
  • *
  • this resource scope is shared and a resource associated with this scope is accessed while this method is called
  • - *
  • one or more handles (see {@link #acquire()}) associated with this resource scope have not been {@linkplain #release(Handle) released}
  • + *
  • one or more scopes which {@linkplain #keepAlive(ResourceScope) depend} on this resource scope have not been closed. * - * @throws UnsupportedOperationException if this resource scope is {@linkplain #isImplicit() implicit}. + * @throws UnsupportedOperationException if this resource scope is the {@linkplain #globalScope() global scope}. */ void close(); @@ -190,61 +184,42 @@ public sealed interface ResourceScope extends AutoCloseable permits ResourceScop * Add a custom cleanup action which will be executed when the resource scope is closed. * The order in which custom cleanup actions are invoked once the scope is closed is unspecified. * @param runnable the custom cleanup action to be associated with this scope. - * @throws IllegalStateException if this scope has already been closed. + * @throws IllegalStateException if this scope has been closed, or if access occurs from + * a thread other than the thread owning this scope. */ void addCloseAction(Runnable runnable); /** - * Acquires a resource scope handle associated with this resource scope. An explicit resource scope cannot be - * {@linkplain #close() closed} until all the resource scope handles acquired from it have been {@linkplain #release(Handle)} released}. - * @return a resource scope handle. - */ - Handle acquire(); - - /** - * Release the provided resource scope handle. This method is idempotent, that is, releasing the same handle - * multiple times has no effect. - * @param handle the resource scope handle to be released. - * @throws IllegalArgumentException if the provided handle is not associated with this scope. - */ - void release(Handle handle); - - /** - * An abstraction modelling a resource scope handle. A resource scope handle is typically {@linkplain #acquire() acquired} by clients - * in order to prevent an explicit resource scope from being closed while executing a certain operation. - * Once obtained, resource scope handles can be {@linkplain #release(Handle)} released}; an explicit resource scope can - * be closed only after all the resource scope handles acquired from it have been released. + * Creates a temporal dependency between this scope and the target scope. As a result, the target scope cannot + * be {@linkplain #close() closed} before this scope. + * @implNote A given scope can support up to {@link Integer#MAX_VALUE} pending keep alive requests. + * @param target the scope that needs to be kept alive. + * @throws IllegalArgumentException if {@code target == this}. + * @throws IllegalStateException if this scope or {@code target} have been closed, or if access occurs from + * a thread other than the thread owning this scope or {@code target}. */ - sealed interface Handle permits ResourceScopeImpl.HandleImpl { - - /** - * Returns the resource scope associated with this handle. - * @return the resource scope associated with this handle. - */ - ResourceScope scope(); - } + void keepAlive(ResourceScope target); /** - * Create a new confined scope. The resulting scope is closeable, and is not managed by a {@link Cleaner}. + * Creates a new confined scope. * @return a new confined scope. */ static ResourceScope newConfinedScope() { - return ResourceScopeImpl.createConfined( null); + return ResourceScopeImpl.createConfined(Thread.currentThread(), null); } /** - * Create a new confined scope managed by a {@link Cleaner}. + * Creates a new confined scope, managed by the provided cleaner instance. * @param cleaner the cleaner to be associated with the returned scope. * @return a new confined scope, managed by {@code cleaner}. - * @throws NullPointerException if {@code cleaner == null}. */ static ResourceScope newConfinedScope(Cleaner cleaner) { Objects.requireNonNull(cleaner); - return ResourceScopeImpl.createConfined( cleaner); + return ResourceScopeImpl.createConfined(Thread.currentThread(), cleaner); } /** - * Create a new shared scope. The resulting scope is closeable, and is not managed by a {@link Cleaner}. + * Creates a new shared scope. * @return a new shared scope. */ static ResourceScope newSharedScope() { @@ -252,10 +227,9 @@ static ResourceScope newSharedScope() { } /** - * Create a new shared scope managed by a {@link Cleaner}. + * Creates a new shared scope, managed by the provided cleaner instance. * @param cleaner the cleaner to be associated with the returned scope. * @return a new shared scope, managed by {@code cleaner}. - * @throws NullPointerException if {@code cleaner == null}. */ static ResourceScope newSharedScope(Cleaner cleaner) { Objects.requireNonNull(cleaner); @@ -263,21 +237,20 @@ static ResourceScope newSharedScope(Cleaner cleaner) { } /** - * Create a new implicit scope. The implicit scope is a managed, shared, and non-closeable scope which only features - * implicit closure. - * Since implicit scopes can only be closed implicitly by the garbage collector, it is recommended that implicit - * scopes are only used in cases where deallocation performance is not a critical concern, to avoid unnecessary - * memory pressure. - * - * @return a new implicit scope. + * Creates a new shared scope, managed by a private {@link Cleaner} instance. Equivalent to (but likely more efficient than) + * the following code: + *
    {@code
    +    newSharedScope(Cleaner.create());
    +     * }
    + * @return a shared scope, managed by a private {@link Cleaner} instance. */ static ResourceScope newImplicitScope() { - return ResourceScopeImpl.createImplicitScope(); + return newSharedScope(CleanerFactory.cleaner()); } /** - * Returns an implicit scope which is assumed to be always alive. - * @return the global scope. + * Returns the global scope. + * @return the global scope. */ static ResourceScope globalScope() { return ResourceScopeImpl.GLOBAL; diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/SegmentAllocator.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/SegmentAllocator.java index f127dc7033087..622308da08863 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/SegmentAllocator.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/SegmentAllocator.java @@ -25,306 +25,277 @@ package jdk.incubator.foreign; -import jdk.internal.foreign.ArenaAllocator; import jdk.internal.foreign.AbstractMemorySegmentImpl; +import jdk.internal.foreign.ArenaAllocator; import jdk.internal.foreign.ResourceScopeImpl; import jdk.internal.foreign.Utils; import java.lang.invoke.VarHandle; import java.lang.reflect.Array; import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; import java.util.Objects; import java.util.function.Function; -import java.util.stream.Stream; /** * This interface models a memory allocator. Clients implementing this interface * must implement the {@link #allocate(long, long)} method. This interface defines several default methods * which can be useful to create segments from several kinds of Java values such as primitives and arrays. - * This interface can be seen as a thin wrapper around the basic capabilities for creating native segments - * (e.g. {@link MemorySegment#allocateNative(long, long, ResourceScope)}); since {@link SegmentAllocator} is a functional interface, + * This interface can be seen as a thin wrapper around the basic capabilities for + * {@linkplain MemorySegment#allocateNative(long, long, ResourceScope) creating} native segments; + * since {@link SegmentAllocator} is a functional interface, * clients can easily obtain a native allocator by using either a lambda expression or a method reference. *

    - * This interface provides a factory, namely {@link SegmentAllocator#ofScope(ResourceScope)} which can be used to obtain - * a scoped allocator, that is, an allocator which creates segment bound by a given scope. This can be useful - * when working inside a try-with-resources construct: - * - *

    {@code
    -try (ResourceScope scope = ResourceScope.newConfinedScope()) {
    -   SegmentAllocator allocator = SegmentAllocator.ofScope(scope);
    -   ...
    -}
    - * }
    - * - * In addition, this interface also defines factories for commonly used allocators; for instance {@link #arenaAllocator(ResourceScope)} - * and {@link #arenaAllocator(long, ResourceScope)} are arena-style native allocators. Finally {@link #ofSegment(MemorySegment)} - * returns an allocator which wraps a segment (either on-heap or off-heap) and recycles its content upon each new allocation request. + * This interface also defines factories for commonly used allocators: + *
      + *
    • {@link #nativeAllocator(ResourceScope)} creates an allocator which + * {@linkplain MemorySegment#allocateNative(long, long, ResourceScope) allocates} native segments, backed by a given scope;
    • + *
    • {@link #newNativeArena(ResourceScope)} creates a more efficient arena-style native allocator, where memory + * is allocated in bigger blocks, which are then sliced accordingly to fit allocation requests;
    • + *
    • {@link #prefixAllocator(MemorySegment)} creates an allocator which wraps a segment (either on-heap or off-heap) + * and recycles its content upon each new allocation request.
    • + *
    + *

    + * Passing a segment allocator to an API can be especially useful in circumstances where a client wants to communicate where + * the results of a certain operation (performed by the API) should be stored, as a memory segment. For instance, + * {@linkplain CLinker#downcallHandle(FunctionDescriptor) downcall method handles} can accept an additional + * {@link SegmentAllocator} parameter if the underlying native function is known to return a struct by-value. Effectively, + * the allocator parameter tells the linker runtime where to store the return value of the native function. */ @FunctionalInterface public interface SegmentAllocator { /** - * Allocate a block of memory with given layout and initialize it with given byte value. + * Converts a Java string into a UTF-8 encoded, null-terminated C string, + * 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 + * {@link java.nio.charset.CharsetEncoder} class should be used when more + * control over the encoding process is required. + * + * @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. + * @return a new native memory segment containing the converted C string. + */ + default MemorySegment allocateUtf8String(String str) { + Objects.requireNonNull(str); + return Utils.toCString(str.getBytes(StandardCharsets.UTF_8), this); + } + + /** + * Allocate a memory segment with given layout and initialize it with given byte value. * @implSpec the default implementation for this method calls {@code this.allocate(layout)}. * @param layout the layout of the block of memory to be allocated. * @param value the value to be set on the newly allocated memory block. * @return a segment for the newly allocated memory block. - * @throws IllegalArgumentException if {@code layout.byteSize()} does not conform to the size of a byte value. */ - default MemorySegment allocate(ValueLayout layout, byte value) { + default MemorySegment allocate(ValueLayout.OfByte layout, byte value) { Objects.requireNonNull(layout); - VarHandle handle = layout.varHandle(byte.class); + VarHandle handle = layout.varHandle(); MemorySegment addr = allocate(layout); handle.set(addr, value); return addr; } /** - * Allocate a block of memory with given layout and initialize it with given char value. + * Allocate a memory segment with given layout and initialize it with given char value. * @implSpec the default implementation for this method calls {@code this.allocate(layout)}. * @param layout the layout of the block of memory to be allocated. * @param value the value to be set on the newly allocated memory block. * @return a segment for the newly allocated memory block. - * @throws IllegalArgumentException if {@code layout.byteSize()} does not conform to the size of a char value. */ - default MemorySegment allocate(ValueLayout layout, char value) { + default MemorySegment allocate(ValueLayout.OfChar layout, char value) { Objects.requireNonNull(layout); - VarHandle handle = layout.varHandle(char.class); + VarHandle handle = layout.varHandle(); MemorySegment addr = allocate(layout); handle.set(addr, value); return addr; } /** - * Allocate a block of memory with given layout and initialize it with given short value. + * Allocate a memory segment with given layout and initialize it with given short value. * @implSpec the default implementation for this method calls {@code this.allocate(layout)}. * @param layout the layout of the block of memory to be allocated. * @param value the value to be set on the newly allocated memory block. * @return a segment for the newly allocated memory block. - * @throws IllegalArgumentException if {@code layout.byteSize()} does not conform to the size of a short value. */ - default MemorySegment allocate(ValueLayout layout, short value) { + default MemorySegment allocate(ValueLayout.OfShort layout, short value) { Objects.requireNonNull(layout); - VarHandle handle = layout.varHandle(short.class); + VarHandle handle = layout.varHandle(); MemorySegment addr = allocate(layout); handle.set(addr, value); return addr; } /** - * Allocate a block of memory with given layout and initialize it with given int value. + * Allocate a memory segment with given layout and initialize it with given int value. * @implSpec the default implementation for this method calls {@code this.allocate(layout)}. * @param layout the layout of the block of memory to be allocated. * @param value the value to be set on the newly allocated memory block. * @return a segment for the newly allocated memory block. - * @throws IllegalArgumentException if {@code layout.byteSize()} does not conform to the size of a int value. */ - default MemorySegment allocate(ValueLayout layout, int value) { + default MemorySegment allocate(ValueLayout.OfInt layout, int value) { Objects.requireNonNull(layout); - VarHandle handle = layout.varHandle(int.class); + VarHandle handle = layout.varHandle(); MemorySegment addr = allocate(layout); handle.set(addr, value); return addr; } /** - * Allocate a block of memory with given layout and initialize it with given float value. + * Allocate a memory segment with given layout and initialize it with given float value. * @implSpec the default implementation for this method calls {@code this.allocate(layout)}. * @param layout the layout of the block of memory to be allocated. * @param value the value to be set on the newly allocated memory block. * @return a segment for the newly allocated memory block. - * @throws IllegalArgumentException if {@code layout.byteSize()} does not conform to the size of a float value. */ - default MemorySegment allocate(ValueLayout layout, float value) { + default MemorySegment allocate(ValueLayout.OfFloat layout, float value) { Objects.requireNonNull(layout); - VarHandle handle = layout.varHandle(float.class); + VarHandle handle = layout.varHandle(); MemorySegment addr = allocate(layout); handle.set(addr, value); return addr; } /** - * Allocate a block of memory with given layout and initialize it with given long value. + * Allocate a memory segment with given layout and initialize it with given long value. * @implSpec the default implementation for this method calls {@code this.allocate(layout)}. * @param layout the layout of the block of memory to be allocated. * @param value the value to be set on the newly allocated memory block. * @return a segment for the newly allocated memory block. - * @throws IllegalArgumentException if {@code layout.byteSize()} does not conform to the size of a long value. */ - default MemorySegment allocate(ValueLayout layout, long value) { + default MemorySegment allocate(ValueLayout.OfLong layout, long value) { Objects.requireNonNull(layout); - VarHandle handle = layout.varHandle(long.class); + VarHandle handle = layout.varHandle(); MemorySegment addr = allocate(layout); handle.set(addr, value); return addr; } /** - * Allocate a block of memory with given layout and initialize it with given double value. + * Allocate a memory segment with given layout and initialize it with given double value. * @implSpec the default implementation for this method calls {@code this.allocate(layout)}. * @param layout the layout of the block of memory to be allocated. * @param value the value to be set on the newly allocated memory block. * @return a segment for the newly allocated memory block. - * @throws IllegalArgumentException if {@code layout.byteSize()} does not conform to the size of a double value. */ - default MemorySegment allocate(ValueLayout layout, double value) { + default MemorySegment allocate(ValueLayout.OfDouble layout, double value) { Objects.requireNonNull(layout); - VarHandle handle = layout.varHandle(double.class); + VarHandle handle = layout.varHandle(); MemorySegment addr = allocate(layout); handle.set(addr, value); return addr; } /** - * Allocate a block of memory with given layout and initialize it with given address value + * Allocate a memory segment with given layout and initialize it with given address value * (expressed as an {@link Addressable} instance). - * The address value might be narrowed according to the platform address size (see {@link MemoryLayouts#ADDRESS}). + * The address value might be narrowed according to the platform address size (see {@link ValueLayout#ADDRESS}). * @implSpec the default implementation for this method calls {@code this.allocate(layout)}. * @param layout the layout of the block of memory to be allocated. * @param value the value to be set on the newly allocated memory block. * @return a segment for the newly allocated memory block. - * @throws IllegalArgumentException if {@code layout.byteSize() != MemoryLayouts.ADDRESS.byteSize()}. */ - default MemorySegment allocate(ValueLayout layout, Addressable value) { + default MemorySegment allocate(ValueLayout.OfAddress layout, Addressable value) { Objects.requireNonNull(value); Objects.requireNonNull(layout); - if (MemoryLayouts.ADDRESS.byteSize() != layout.byteSize()) { - throw new IllegalArgumentException("Layout size mismatch - " + layout.byteSize() + " != " + MemoryLayouts.ADDRESS.byteSize()); - } - return switch ((int)layout.byteSize()) { - case 4 -> allocate(layout, (int)value.address().toRawLongValue()); - case 8 -> allocate(layout, value.address().toRawLongValue()); - default -> throw new UnsupportedOperationException("Unsupported pointer size"); // should not get here - }; + MemorySegment segment = allocate(layout); + layout.varHandle().set(segment, value.address()); + return segment; } /** - * Allocate a block of memory with given layout and initialize it with given byte array. + * Allocate a memory segment with given layout and initialize it with given byte array. * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}. * @param elementLayout the element layout of the array to be allocated. * @param array the array to be copied on the newly allocated memory block. * @return a segment for the newly allocated memory block. - * @throws IllegalArgumentException if {@code elementLayout.byteSize()} does not conform to the size of a byte value. */ - default MemorySegment allocateArray(ValueLayout elementLayout, byte[] array) { + default MemorySegment allocateArray(ValueLayout.OfByte elementLayout, byte[] array) { return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray); } /** - * Allocate a block of memory with given layout and initialize it with given short array. + * Allocate a memory segment with given layout and initialize it with given short array. * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}. * @param elementLayout the element layout of the array to be allocated. * @param array the array to be copied on the newly allocated memory block. * @return a segment for the newly allocated memory block. - * @throws IllegalArgumentException if {@code elementLayout.byteSize()} does not conform to the size of a short value. */ - default MemorySegment allocateArray(ValueLayout elementLayout, short[] array) { + default MemorySegment allocateArray(ValueLayout.OfShort elementLayout, short[] array) { return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray); } /** - * Allocate a block of memory with given layout and initialize it with given char array. + * Allocate a memory segment with given layout and initialize it with given char array. * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}. * @param elementLayout the element layout of the array to be allocated. * @param array the array to be copied on the newly allocated memory block. * @return a segment for the newly allocated memory block. - * @throws IllegalArgumentException if {@code elementLayout.byteSize()} does not conform to the size of a char value. */ - default MemorySegment allocateArray(ValueLayout elementLayout, char[] array) { + default MemorySegment allocateArray(ValueLayout.OfChar elementLayout, char[] array) { return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray); } /** - * Allocate a block of memory with given layout and initialize it with given int array. + * Allocate a memory segment with given layout and initialize it with given int array. * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}. * @param elementLayout the element layout of the array to be allocated. * @param array the array to be copied on the newly allocated memory block. * @return a segment for the newly allocated memory block. - * @throws IllegalArgumentException if {@code elementLayout.byteSize()} does not conform to the size of a int value. */ - default MemorySegment allocateArray(ValueLayout elementLayout, int[] array) { + default MemorySegment allocateArray(ValueLayout.OfInt elementLayout, int[] array) { return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray); } /** - * Allocate a block of memory with given layout and initialize it with given float array. + * Allocate a memory segment with given layout and initialize it with given float array. * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}. * @param elementLayout the element layout of the array to be allocated. * @param array the array to be copied on the newly allocated memory block. * @return a segment for the newly allocated memory block. - * @throws IllegalArgumentException if {@code elementLayout.byteSize()} does not conform to the size of a float value. */ - default MemorySegment allocateArray(ValueLayout elementLayout, float[] array) { + default MemorySegment allocateArray(ValueLayout.OfFloat elementLayout, float[] array) { return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray); } /** - * Allocate a block of memory with given layout and initialize it with given long array. + * Allocate a memory segment with given layout and initialize it with given long array. * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}. * @param elementLayout the element layout of the array to be allocated. * @param array the array to be copied on the newly allocated memory block. * @return a segment for the newly allocated memory block. - * @throws IllegalArgumentException if {@code elementLayout.byteSize()} does not conform to the size of a long value. */ - default MemorySegment allocateArray(ValueLayout elementLayout, long[] array) { + default MemorySegment allocateArray(ValueLayout.OfLong elementLayout, long[] array) { return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray); } /** - * Allocate a block of memory with given layout and initialize it with given double array. + * Allocate a memory segment with given layout and initialize it with given double array. * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}. * @param elementLayout the element layout of the array to be allocated. * @param array the array to be copied on the newly allocated memory block. * @return a segment for the newly allocated memory block. - * @throws IllegalArgumentException if {@code elementLayout.byteSize()} does not conform to the size of a double value. */ - default MemorySegment allocateArray(ValueLayout elementLayout, double[] array) { + default MemorySegment allocateArray(ValueLayout.OfDouble elementLayout, double[] array) { return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray); } - /** - * Allocate a block of memory with given layout and initialize it with given address array. - * The address value of each array element might be narrowed according to the platform address size (see {@link MemoryLayouts#ADDRESS}). - * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}. - * @param elementLayout the element layout of the array to be allocated. - * @param array the array to be copied on the newly allocated memory block. - * @return a segment for the newly allocated memory block. - * @throws IllegalArgumentException if {@code layout.byteSize() != MemoryLayouts.ADDRESS.byteSize()}. - */ - default MemorySegment allocateArray(ValueLayout elementLayout, Addressable[] array) { - Objects.requireNonNull(elementLayout); - Objects.requireNonNull(array); - Stream.of(array).forEach(Objects::requireNonNull); - if (MemoryLayouts.ADDRESS.byteSize() != elementLayout.byteSize()) { - throw new IllegalArgumentException("Layout size mismatch - " + elementLayout.byteSize() + " != " + MemoryLayouts.ADDRESS.byteSize()); - } - return switch ((int)elementLayout.byteSize()) { - case 4 -> copyArrayWithSwapIfNeeded(Stream.of(array) - .mapToInt(a -> (int)a.address().toRawLongValue()).toArray(), - elementLayout, MemorySegment::ofArray); - case 8 -> copyArrayWithSwapIfNeeded(Stream.of(array) - .mapToLong(a -> a.address().toRawLongValue()).toArray(), - elementLayout, MemorySegment::ofArray); - default -> throw new UnsupportedOperationException("Unsupported pointer size"); // should not get here - }; - } - private MemorySegment copyArrayWithSwapIfNeeded(Z array, ValueLayout elementLayout, Function heapSegmentFactory) { Objects.requireNonNull(array); Objects.requireNonNull(elementLayout); - Utils.checkPrimitiveCarrierCompat(array.getClass().componentType(), elementLayout); - MemorySegment addr = allocate(MemoryLayout.sequenceLayout(Array.getLength(array), elementLayout)); - if (elementLayout.byteSize() == 1 || (elementLayout.order() == ByteOrder.nativeOrder())) { - addr.copyFrom(heapSegmentFactory.apply(array)); - } else { - ((AbstractMemorySegmentImpl)addr).copyFromSwap(heapSegmentFactory.apply(array), elementLayout.byteSize()); - } + int size = Array.getLength(array); + MemorySegment addr = allocate(MemoryLayout.sequenceLayout(size, elementLayout)); + MemorySegment.copy(heapSegmentFactory.apply(array), elementLayout, 0, + addr, elementLayout.withOrder(ByteOrder.nativeOrder()), 0, size); return addr; } /** - * Allocate a block of memory with given layout. + * Allocate a memory segment with given layout. * @implSpec the default implementation for this method calls {@code this.allocate(layout.byteSize(), layout.byteAlignment())}. * @param layout the layout of the block of memory to be allocated. * @return a segment for the newly allocated memory block. @@ -335,7 +306,7 @@ default MemorySegment allocate(MemoryLayout layout) { } /** - * Allocate a block of memory corresponding to an array with given element layout and size. + * Allocate a memory segment with given element layout and size. * @implSpec the default implementation for this method calls {@code this.allocate(MemoryLayout.sequenceLayout(count, elementLayout))}. * @param elementLayout the array element layout. * @param count the array element count. @@ -347,7 +318,8 @@ default MemorySegment allocateArray(MemoryLayout elementLayout, long count) { } /** - * Allocate a block of memory with given size, with default alignment (1-byte aligned). + * Allocate a memory segment with given size + * and default alignment constraints (1-byte aligned). * @implSpec the default implementation for this method calls {@code this.allocate(bytesSize, 1)}. * @param bytesSize the size (in bytes) of the block of memory to be allocated. * @return a segment for the newly allocated memory block. @@ -357,7 +329,7 @@ default MemorySegment allocate(long bytesSize) { } /** - * Allocate a block of memory with given size and alignment constraint. + * Allocate a memory segment with given size and alignment constraints. * @param bytesSize the size (in bytes) of the block of memory to be allocated. * @param bytesAlignment the alignment (in bytes) of the block of memory to be allocated. * @return a segment for the newly allocated memory block. @@ -365,74 +337,91 @@ default MemorySegment allocate(long bytesSize) { MemorySegment allocate(long bytesSize, long bytesAlignment); /** - * Returns a native arena-based allocator which allocates a single memory segment, of given size (using malloc), - * and then responds to allocation request by returning different slices of that same segment - * (until no further allocation is possible). - * This can be useful when clients want to perform multiple allocation requests while avoiding the cost associated - * with allocating a new off-heap memory region upon each allocation request. - *

    - * An allocator associated with a shared resource scope is thread-safe and allocation requests may be - * performed concurrently; conversely, if the arena allocator is associated with a confined resource scope, - * allocation requests can only occur from the thread owning the allocator's resource scope. - *

    - * The returned allocator might throw an {@link OutOfMemoryError} if an incoming allocation request exceeds - * the allocator capacity. + * Returns a native unbounded arena-based allocator, with predefined block size and maximum arena size, + * associated with the provided scope. Equivalent to the following code: + *

    {@code
    +    SegmentAllocator.newNativeArena(Long.MAX_VALUE, predefinedBlockSize, scope);
    +     * }
    * - * @param size the size (in bytes) of the allocation arena. - * @param scope the scope associated with the segments returned by this allocator. - * @return a new bounded arena-based allocator - * @throws IllegalArgumentException if {@code size <= 0}. + * @param scope the scope associated with the segments returned by the arena-based allocator. + * @return a new unbounded arena-based allocator * @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other * than the thread owning {@code scope}. */ - static SegmentAllocator arenaAllocator(long size, ResourceScope scope) { - Objects.requireNonNull(scope); - return scope.ownerThread() == null ? - new ArenaAllocator.BoundedSharedArenaAllocator(scope, size) : - new ArenaAllocator.BoundedArenaAllocator(scope, size); + static SegmentAllocator newNativeArena(ResourceScope scope) { + return newNativeArena(Long.MAX_VALUE, ArenaAllocator.DEFAULT_BLOCK_SIZE, scope); + } + + /** + * Returns a native unbounded arena-based allocator, with block size set to the specified arena size, associated with + * the provided scope, with given arena size. Equivalent to the following code: + *
    {@code
    +    SegmentAllocator.newNativeArena(arenaSize, arenaSize, scope);
    +     * }
    + * + * @param arenaSize the size (in bytes) of the allocation arena. + * @param scope the scope associated with the segments returned by the arena-based allocator. + * @return a new unbounded arena-based allocator + * @throws IllegalArgumentException if {@code arenaSize <= 0}. + * @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other + * than the thread owning {@code scope}. + */ + static SegmentAllocator newNativeArena(long arenaSize, ResourceScope scope) { + return newNativeArena(arenaSize, arenaSize, scope); } /** - * Returns a native unbounded arena-based allocator. + * Returns a native arena-based allocator, associated with the provided scope, with given arena size and block size. *

    - * The returned allocator allocates a memory segment {@code S} of a certain fixed size (using malloc) and then - * responds to allocation requests in one of the following ways: + * The returned allocator {@linkplain MemorySegment#allocateNative(long, ResourceScope) allocates} a memory segment + * {@code S} of the specified block size and then responds to allocation requests in one of the following ways: *

      *
    • if the size of the allocation requests is smaller than the size of {@code S}, and {@code S} has a free * slice {@code S'} which fits that allocation request, return that {@code S'}. *
    • if the size of the allocation requests is smaller than the size of {@code S}, and {@code S} has no free - * slices which fits that allocation request, allocate a new segment {@code S'} (using malloc), which has same size as {@code S} + * slices which fits that allocation request, allocate a new segment {@code S'}, which has same size as {@code S} * and set {@code S = S'}; the allocator then tries to respond to the same allocation request again. - *
    • if the size of the allocation requests is bigger than the size of {@code S}, allocate a new segment {@code S'} - * (using malloc), which has a sufficient size to satisfy the allocation request, and return {@code S'}. + *
    • if the size of the allocation requests is bigger than the size of {@code S}, allocate a new segment {@code S'}, + * which has a sufficient size to satisfy the allocation request, and return {@code S'}. *
    *

    * This segment allocator can be useful when clients want to perform multiple allocation requests while avoiding the * cost associated with allocating a new off-heap memory region upon each allocation request. *

    - * An allocator associated with a shared resource scope is thread-safe and allocation requests may be - * performed concurrently; conversely, if the arena allocator is associated with a confined resource scope, - * allocation requests can only occur from the thread owning the allocator's resource scope. - *

    - * The returned allocator might throw an {@link OutOfMemoryError} if an incoming allocation request exceeds - * the system capacity. + * The returned allocator might throw an {@link OutOfMemoryError} if the total memory allocated with this allocator + * exceeds the arena size, or the system capacity. Furthermore, the returned allocator is not thread safe. + * Concurrent allocation needs to be guarded with synchronization primitives. * - * @param scope the scope associated with the segments returned by this allocator. + * @param arenaSize the size (in bytes) of the allocation arena. + * @param blockSize the block size associated with the arena-based allocator. + * @param scope the scope associated with the segments returned by the arena-based allocator. * @return a new unbounded arena-based allocator + * @throws IllegalArgumentException if {@code blockSize <= 0}, if {@code arenaSize <= 0} or if {@code arenaSize < blockSize}. * @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other * than the thread owning {@code scope}. */ - static SegmentAllocator arenaAllocator(ResourceScope scope) { + static SegmentAllocator newNativeArena(long arenaSize, long blockSize, ResourceScope scope) { Objects.requireNonNull(scope); - return scope.ownerThread() == null ? - new ArenaAllocator.UnboundedSharedArenaAllocator(scope) : - new ArenaAllocator.UnboundedArenaAllocator(scope); + if (blockSize <= 0) { + throw new IllegalArgumentException("Invalid block size: " + blockSize); + } + if (arenaSize <= 0 || arenaSize < blockSize) { + throw new IllegalArgumentException("Invalid arena size: " + arenaSize); + } + return new ArenaAllocator(blockSize, arenaSize, scope); } /** * Returns a segment allocator which responds to allocation requests by recycling a single segment; that is, * each new allocation request will return a new slice starting at the segment offset {@code 0} (alignment - * constraints are ignored by this allocator). This can be useful to limit allocation requests in case a client + * constraints are ignored by this allocator), hence the name prefix allocator. + * Equivalent to (but likely more efficient than) the following code: + *

    {@code
    +    MemorySegment segment = ...
    +    SegmentAllocator prefixAllocator = (size, align) -> segment.asSlice(0, size);
    +     * }
    + *

    + * This allocator can be useful to limit allocation requests in case a client * knows that they have fully processed the contents of the allocated segment before the subsequent allocation request * takes place. *

    @@ -442,25 +431,42 @@ static SegmentAllocator arenaAllocator(ResourceScope scope) { * @param segment the memory segment to be recycled by the returned allocator. * @return an allocator which recycles an existing segment upon each new allocation request. */ - static SegmentAllocator ofSegment(MemorySegment segment) { + static SegmentAllocator prefixAllocator(MemorySegment segment) { Objects.requireNonNull(segment); - return (size, align) -> segment.asSlice(0, size); + return (AbstractMemorySegmentImpl)segment; } /** - * Returns a native allocator which responds to allocation requests by allocating new segments - * bound by the given resource scope, using the {@link MemorySegment#allocateNative(long, long, ResourceScope)} - * factory. This code is equivalent (but likely more efficient) to the following: + * Returns a native allocator, associated with the provided scope. Equivalent to (but likely more efficient than) + * the following code: *

    {@code
    -    Resource scope = ...
    -    SegmentAllocator scoped = (size, align) -> MemorySegment.allocateNative(size, align, scope);
    +    ResourceScope scope = ...
    +    SegmentAllocator nativeAllocator = (size, align) -> MemorySegment.allocateNative(size, align, scope);
          * }
    * - * @param scope the resource scope associated with the segments created by the returned allocator. - * @return an allocator which allocates new memory segment bound by the provided resource scope. + * @param scope the scope associated with the returned allocator. + * @return a native allocator, associated with the provided scope. */ - static SegmentAllocator ofScope(ResourceScope scope) { + static SegmentAllocator nativeAllocator(ResourceScope scope) { Objects.requireNonNull(scope); return (ResourceScopeImpl)scope; } + + /** + * Returns a native allocator which allocates segments in independent {@linkplain ResourceScope#newImplicitScope() implicit scopes}. + * Equivalent to (but likely more efficient than) the following code: + *
    {@code
    +    ResourceScope scope = ...
    +    SegmentAllocator implicitAllocator = (size, align) -> MemorySegment.allocateNative(size, align, ResourceScope.newImplicitScope());
    +     * }
    + * + * @return a native allocator which allocates segments in independent {@linkplain ResourceScope#newImplicitScope() implicit scopes}. + */ + static SegmentAllocator implicitAllocator() { + class Holder { + static final SegmentAllocator IMPLICIT_ALLOCATOR = (size, align) -> + MemorySegment.allocateNative(size, align, ResourceScope.newImplicitScope()); + } + return Holder.IMPLICIT_ALLOCATOR; + } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/SequenceLayout.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/SequenceLayout.java index 8b79cb2fb9f1b..e751830313bb9 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/SequenceLayout.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/SequenceLayout.java @@ -25,31 +25,29 @@ */ package jdk.incubator.foreign; -import java.lang.constant.Constable; import java.lang.constant.ConstantDescs; import java.lang.constant.DynamicConstantDesc; -import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.OptionalLong; /** * A sequence layout. A sequence layout is used to denote a repetition of a given layout, also called the sequence layout's element layout. - * The repetition count, where it exists (e.g. for finite sequence layouts) is said to be the the sequence layout's element count. + * The repetition count, where it exists (e.g. for finite sequence layouts) is said to be the sequence layout's element count. * A finite sequence layout can be thought of as a group layout where the sequence layout's element layout is repeated a number of times * that is equal to the sequence layout's element count. In other words this layout: * *
    {@code
    -MemoryLayout.sequenceLayout(3, MemoryLayout.valueLayout(32, ByteOrder.BIG_ENDIAN));
    +MemoryLayout.sequenceLayout(3, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN));
      * }
    * * is equivalent to the following layout: * *
    {@code
     MemoryLayout.structLayout(
    -    MemoryLayout.valueLayout(32, ByteOrder.BIG_ENDIAN),
    -    MemoryLayout.valueLayout(32, ByteOrder.BIG_ENDIAN),
    -    MemoryLayout.valueLayout(32, ByteOrder.BIG_ENDIAN));
    +    ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN),
    +    ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN),
    +    ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN));
      * }
    * *

    @@ -72,13 +70,13 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout private final MemoryLayout elementLayout; SequenceLayout(OptionalLong elemCount, MemoryLayout elementLayout) { - this(elemCount, elementLayout, elementLayout.bitAlignment(), Map.of()); + this(elemCount, elementLayout, elementLayout.bitAlignment(), Optional.empty()); } - SequenceLayout(OptionalLong elemCount, MemoryLayout elementLayout, long alignment, Map attributes) { + SequenceLayout(OptionalLong elemCount, MemoryLayout elementLayout, long alignment, Optional name) { super(elemCount.isPresent() && AbstractLayout.optSize(elementLayout).isPresent() ? OptionalLong.of(elemCount.getAsLong() * elementLayout.bitSize()) : - OptionalLong.empty(), alignment, attributes); + OptionalLong.empty(), alignment, name); this.elemCount = elemCount; this.elementLayout = elementLayout; } @@ -110,7 +108,7 @@ public OptionalLong elementCount() { */ public SequenceLayout withElementCount(long elementCount) { AbstractLayout.checkSize(elementCount, true); - return new SequenceLayout(OptionalLong.of(elementCount), elementLayout, alignment, attributes); + return new SequenceLayout(OptionalLong.of(elementCount), elementLayout, alignment, name()); } /** @@ -122,11 +120,11 @@ public SequenceLayout withElementCount(long elementCount) { *

    * For instance, given a sequence layout of the kind: *

    {@code
    -    var seq = MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(3, MemoryLayouts.JAVA_INT));
    +    var seq = MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(3, ValueLayout.JAVA_INT));
          * }
    * calling {@code seq.reshape(2, 6)} will yield the following sequence layout: *
    {@code
    -    var reshapeSeq = MemoryLayout.sequenceLayout(2, MemoryLayout.sequenceLayout(6, MemoryLayouts.JAVA_INT));
    +    var reshapeSeq = MemoryLayout.sequenceLayout(2, MemoryLayout.sequenceLayout(6, ValueLayout.JAVA_INT));
          * }
    *

    * If one of the provided element count is the special value {@code -1}, then the element @@ -151,7 +149,7 @@ public SequenceLayout reshape(long... elementCounts) { if (elementCounts.length == 0) { throw new IllegalArgumentException(); } - if (!elementCount().isPresent()) { + if (elementCount().isEmpty()) { throw new UnsupportedOperationException("Cannot reshape a sequence layout whose element count is unspecified"); } SequenceLayout flat = flatten(); @@ -198,11 +196,11 @@ public SequenceLayout reshape(long... elementCounts) { * be dropped and their element counts will be incorporated into that of the returned sequence layout. * For instance, given a sequence layout of the kind: *

    {@code
    -    var seq = MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(3, MemoryLayouts.JAVA_INT));
    +    var seq = MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(3, ValueLayout.JAVA_INT));
          * }
    * calling {@code seq.flatten()} will yield the following sequence layout: *
    {@code
    -    var flattenedSeq = MemoryLayout.sequenceLayout(12, MemoryLayouts.JAVA_INT);
    +    var flattenedSeq = MemoryLayout.sequenceLayout(12, ValueLayout.JAVA_INT);
          * }
    * @return a new sequence layout with the same size as this layout (but, possibly, with different * element count), whose element layout is not a sequence layout. @@ -210,13 +208,12 @@ public SequenceLayout reshape(long... elementCounts) { * flattened, does not have an element count. */ public SequenceLayout flatten() { - if (!elementCount().isPresent()) { + if (elementCount().isEmpty()) { throw badUnboundSequenceLayout(); } long count = elementCount().getAsLong(); MemoryLayout elemLayout = elementLayout(); - while (elemLayout instanceof SequenceLayout) { - SequenceLayout elemSeq = (SequenceLayout)elemLayout; + while (elemLayout instanceof SequenceLayout elemSeq) { count = count * elemSeq.elementCount().orElseThrow(this::badUnboundSequenceLayout); elemLayout = elemSeq.elementLayout(); } @@ -241,10 +238,9 @@ public boolean equals(Object other) { if (!super.equals(other)) { return false; } - if (!(other instanceof SequenceLayout)) { + if (!(other instanceof SequenceLayout s)) { return false; } - SequenceLayout s = (SequenceLayout)other; return elemCount.equals(s.elemCount) && elementLayout.equals(s.elementLayout); } @@ -254,8 +250,8 @@ public int hashCode() { } @Override - SequenceLayout dup(long alignment, Map attributes) { - return new SequenceLayout(elementCount(), elementLayout, alignment, attributes); + SequenceLayout dup(long alignment, Optional name) { + return new SequenceLayout(elementCount(), elementLayout, alignment, name); } @Override @@ -290,12 +286,4 @@ public SequenceLayout withName(String name) { public SequenceLayout withBitAlignment(long alignmentBits) { return (SequenceLayout)super.withBitAlignment(alignmentBits); } - - /** - * {@inheritDoc} - */ - @Override - public SequenceLayout withAttribute(String name, Constable value) { - return (SequenceLayout)super.withAttribute(name, value); - } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/SymbolLookup.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/SymbolLookup.java index 7f6a3d8110c11..5010703ba1515 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/SymbolLookup.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/SymbolLookup.java @@ -26,6 +26,7 @@ import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; +import jdk.internal.foreign.ResourceScopeImpl; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; @@ -34,11 +35,11 @@ /** * A symbol lookup. Exposes a lookup operation for searching symbol addresses by name, see {@link SymbolLookup#lookup(String)}. - * A symbol lookup can be used to lookup a symbol in a loaded library. Clients can obtain a {@linkplain #loaderLookup() loader lookup}, + * A symbol lookup can be used to look up a symbol in a loaded library. Clients can obtain a {@linkplain #loaderLookup() loader lookup}, * which can be used to search symbols in libraries loaded by the current classloader (e.g. using {@link System#load(String)}, * or {@link System#loadLibrary(String)}). - * Alternatively, clients can obtain a {@linkplain CLinker#systemLookup() platform-dependent lookup}, to search symbols - * in the standard C library. + * Alternatively, clients can search symbols in the standard C library using a {@link CLinker}, which conveniently + * implements this interface. *

    Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null} * elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown.

    */ @@ -49,13 +50,15 @@ public interface SymbolLookup { * Looks up a symbol with given name in this lookup. * * @param name the symbol name. - * @return the memory address associated with the symbol (if any). + * @return the lookup symbol (if any). */ - Optional lookup(String name); + Optional lookup(String name); /** * Obtains a symbol lookup suitable to find symbols in native libraries associated with the caller's classloader - * (that is, libraries loaded using {@link System#loadLibrary} or {@link System#load}). + * (that is, libraries loaded using {@link System#loadLibrary} or {@link System#load}). The returned lookup + * returns native symbols backed by a non-closeable, shared scope which keeps the caller's classloader + * reachable. *

    * This method is restricted. * Restricted methods are unsafe, and, if used incorrectly, their use might crash @@ -72,11 +75,13 @@ static SymbolLookup loaderLookup() { Class caller = Reflection.getCallerClass(); Reflection.ensureNativeAccess(caller); ClassLoader loader = Objects.requireNonNull(caller.getClassLoader()); + ResourceScope loaderScope = ResourceScopeImpl.heapScope(loader); return name -> { Objects.requireNonNull(name); JavaLangAccess javaLangAccess = SharedSecrets.getJavaLangAccess(); MemoryAddress addr = MemoryAddress.ofLong(javaLangAccess.findNative(loader, name)); - return addr == MemoryAddress.NULL? Optional.empty() : Optional.of(addr); + return addr == MemoryAddress.NULL? Optional.empty() : Optional.of(NativeSymbol.ofAddress(name, addr, loaderScope)); }; } + } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/VaList.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/VaList.java new file mode 100644 index 0000000000000..7543dac95d0d2 --- /dev/null +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/VaList.java @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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.incubator.foreign; + +import jdk.internal.foreign.abi.SharedUtils; +import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64VaList; +import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64VaList; +import jdk.internal.foreign.abi.x64.sysv.SysVVaList; +import jdk.internal.foreign.abi.x64.windows.WinVaList; +import jdk.internal.reflect.CallerSensitive; +import jdk.internal.reflect.Reflection; + +import java.util.Objects; +import java.util.function.Consumer; + +/** + * An interface that models a variable argument list, similar in functionality to a C {@code va_list}. + *

    + * A variable argument list is a stateful cursor used to iterate over a set of arguments. A variable argument list + * can be passed by reference e.g. to a {@linkplain CLinker#downcallHandle(FunctionDescriptor) downcall method handle}. + *

    + * Per the C specification (see C standard 6.5.2.2 Function calls - item 6), + * arguments to variadic calls are erased by way of 'default argument promotions', + * which erases integral types by way of integer promotion (see C standard 6.3.1.1 - item 2), + * and which erases all {@code float} arguments to {@code double}. + *

    + * As such, this interface only supports reading {@code int}, {@code double}, + * and any other type that fits into a {@code long}. + * + * This class is not thread safe, and all accesses should occur within a single thread + * (regardless of the scope associated with the variable arity list). + * + *

    Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null} + * elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown.

    + */ +sealed public interface VaList extends Addressable permits WinVaList, SysVVaList, LinuxAArch64VaList, MacOsAArch64VaList, SharedUtils.EmptyVaList { + + /** + * Reads the next value as an {@code int} and advances this variable argument list's position. + * + * @param layout the layout of the value to be read. + * @return the {@code int} value read from this variable argument list. + * @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from + * a thread other than the thread owning that scope. + */ + int nextVarg(ValueLayout.OfInt layout); + + /** + * Reads the next value as a {@code long} and advances this variable argument list's position. + * + * @param layout the layout of the value to be read. + * @return the {@code long} value read from this variable argument list. + * @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from + * a thread other than the thread owning that scope. + */ + long nextVarg(ValueLayout.OfLong layout); + + /** + * Reads the next value as a {@code double} and advances this variable argument list's position. + * + * @param layout the layout of the value + * @return the {@code double} value read from this variable argument list. + * @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from + * a thread other than the thread owning that scope. + */ + double nextVarg(ValueLayout.OfDouble layout); + + /** + * Reads the next value as a {@code MemoryAddress} and advances this variable argument list's position. + * + * @param layout the layout of the value to be read. + * @return the {@code MemoryAddress} value read from this variable argument list. + * @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from + * a thread other than the thread owning that scope. + */ + MemoryAddress nextVarg(ValueLayout.OfAddress layout); + + /** + * Reads the next value as a {@code MemorySegment}, and advances this variable argument list's position. + *

    + * The memory segment returned by this method will be allocated using the given {@link SegmentAllocator}. + * + * @param layout the layout of the value to be read. + * @param allocator the allocator to be used to create a segment where the contents of the variable argument list + * will be copied. + * @return the {@code MemorySegment} value read from this variable argument list. + * @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from + * a thread other than the thread owning that scope. + */ + MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator); + + /** + * Skips a number of elements with the given memory layouts, and advances this variable argument list's position. + * + * @param layouts the layouts of the values to be skipped. + * @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from + * a thread other than the thread owning that scope. + */ + void skip(MemoryLayout... layouts); + + /** + * Returns the resource scope associated with this variable argument list. + * @return the resource scope associated with this variable argument list. + */ + ResourceScope scope(); + + /** + * Copies this variable argument list at its current position into a new variable argument list associated + * with the same scope as this variable argument list. Copying is useful to + * traverse the variable argument list elements, starting from the current position, without affecting the state + * of the original variable argument list, essentially allowing the elements to be traversed multiple times. + * + * @return a copy of this variable argument list. + * @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from + * a thread other than the thread owning that scope. + */ + VaList copy(); + + /** + * Returns the memory address associated with this variable argument list. + * @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from + * a thread other than the thread owning that scope. + * @return The memory address associated with this variable argument list. + */ + @Override + MemoryAddress address(); + + /** + * Constructs a new variable argument list from a memory address pointing to an existing variable argument list, + * with given resource scope. + *

    + * 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. + * + * @param address a memory address pointing to an existing variable argument list. + * @param scope the resource scope to be associated with the returned variable argument list. + * @return a new variable argument list backed by the memory region at {@code address}. + * @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other + * than the thread owning {@code scope}. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + static VaList ofAddress(MemoryAddress address, ResourceScope scope) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + Objects.requireNonNull(address); + Objects.requireNonNull(scope); + return SharedUtils.newVaListOfAddress(address, scope); + } + + /** + * Constructs a new variable argument list using a builder (see {@link Builder}), with a given resource scope. + *

    + * If this method needs to allocate native memory, such memory will be managed by the given + * {@linkplain ResourceScope resource scope}, and will be released when the resource scope is {@linkplain ResourceScope#close closed}. + *

    + * Note that when there are no elements added to the created va list, + * this method will return the same as {@link #empty()}. + * + * @param actions a consumer for a builder (see {@link Builder}) which can be used to specify the elements + * of the underlying variable argument list. + * @param scope scope the scope to be associated with the new variable arity list. + * @return a new variable argument list. + * @throws IllegalStateException if the scope associated with {@code allocator} has been already closed, + * or if access occurs from a thread other than the thread owning that scope. + */ + static VaList make(Consumer actions, ResourceScope scope) { + Objects.requireNonNull(actions); + Objects.requireNonNull(scope); + return SharedUtils.newVaList(actions, scope); + } + + /** + * Returns an empty variable argument list, associated with the {@linkplain ResourceScope#globalScope() global} + * scope. The resulting variable argument list does not contain any argument, and throws {@link UnsupportedOperationException} + * on all operations, except for {@link #scope()}, {@link #copy()} and {@link #address()}. + * @return an empty variable argument list. + */ + static VaList empty() { + return SharedUtils.emptyVaList(); + } + + /** + * A builder interface used to construct a variable argument list. + * + *

    Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null} + * elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown.

    + */ + sealed interface Builder permits WinVaList.Builder, SysVVaList.Builder, LinuxAArch64VaList.Builder, MacOsAArch64VaList.Builder { + + /** + * Writes an {@code int} value to the variable argument list being constructed. + * + * @param layout the layout of the value to be written. + * @param value the {@code int} value to be written. + * @return this builder. + */ + Builder addVarg(ValueLayout.OfInt layout, int value); + + /** + * Writes a {@code long} value to the variable argument list being constructed. + * + * @param layout the layout of the value to be written. + * @param value the {@code long} value to be written. + * @return this builder. + */ + Builder addVarg(ValueLayout.OfLong layout, long value); + + /** + * Writes a {@code double} value to the variable argument list being constructed. + * + * @param layout the layout of the value to be written. + * @param value the {@code double} value to be written. + * @return this builder. + */ + Builder addVarg(ValueLayout.OfDouble layout, double value); + + /** + * Writes an {@code Addressable} value to the variable argument list being constructed. + * + * @param layout the layout of the value to be written. + * @param value the {@code Addressable} value to be written. + * @return this builder. + */ + Builder addVarg(ValueLayout.OfAddress layout, Addressable value); + + /** + * Writes a {@code MemorySegment} value, with given layout, to the variable argument list being constructed. + * + * @param layout the layout of the value to be written. + * @param value the {@code MemorySegment} whose contents will be copied. + * @return this builder. + */ + Builder addVarg(GroupLayout layout, MemorySegment value); + } +} diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/ValueLayout.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/ValueLayout.java index 26271da07da6f..36a3c6b5b9d0a 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/ValueLayout.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/ValueLayout.java @@ -25,19 +25,30 @@ */ package jdk.incubator.foreign; -import java.lang.constant.Constable; +import jdk.internal.foreign.Utils; +import jdk.internal.misc.Unsafe; +import jdk.internal.vm.annotation.ForceInline; +import jdk.internal.vm.annotation.Stable; +import sun.invoke.util.Wrapper; + import java.lang.constant.ConstantDescs; import java.lang.constant.DynamicConstantDesc; +import java.lang.invoke.VarHandle; import java.nio.ByteOrder; -import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.OptionalLong; /** * A value layout. A value layout is used to model the memory layout associated with values of basic data types, such as integral types - * (either signed or unsigned) and floating-point types. Each value layout has a size and a byte order (see {@link ByteOrder}). - * + * (either signed or unsigned) and floating-point types. Each value layout has a size, a {@linkplain ByteOrder byte order}) + * and a carrier, that is, the Java type that should be used when {@linkplain MemorySegment#get(OfInt, long) accessing} + * a memory region using the value layout. + *

    + * This class defines useful value layout constants for Java primitive types and addresses. + * The layout constants in this class make implicit alignment and byte-ordering assumption: all layout + * constants in this class are byte-aligned, and their byte order is set to the {@linkplain ByteOrder#nativeOrder() platform default}, + * thus making it easy to work with other APIs, such as arrays and {@link java.nio.ByteBuffer}. *

    * This is a value-based * class; programmers should treat instances that are @@ -52,17 +63,22 @@ * @implSpec * This class is immutable and thread-safe. */ -public final class ValueLayout extends AbstractLayout implements MemoryLayout { +public sealed class ValueLayout extends AbstractLayout implements MemoryLayout { + private final Class carrier; private final ByteOrder order; - ValueLayout(ByteOrder order, long size) { - this(order, size, size, Map.of()); + private static final int ADDRESS_SIZE_BITS = Unsafe.ADDRESS_SIZE * 8; + + ValueLayout(Class carrier, ByteOrder order, long size) { + this(carrier, order, size, size, Optional.empty()); } - ValueLayout(ByteOrder order, long size, long alignment, Map attributes) { - super(OptionalLong.of(size), alignment, attributes); + ValueLayout(Class carrier, ByteOrder order, long size, long alignment, Optional name) { + super(OptionalLong.of(size), alignment, name); + this.carrier = carrier; this.order = order; + checkCarrierSize(carrier, size); } /** @@ -81,7 +97,7 @@ public ByteOrder order() { * @return a new value layout with given byte order. */ public ValueLayout withOrder(ByteOrder order) { - return new ValueLayout(Objects.requireNonNull(order), bitSize(), alignment, attributes); + return new ValueLayout(carrier, Objects.requireNonNull(order), bitSize(), alignment, name()); } @Override @@ -99,29 +115,37 @@ public boolean equals(Object other) { if (!super.equals(other)) { return false; } - if (!(other instanceof ValueLayout)) { + if (!(other instanceof ValueLayout v)) { return false; } - ValueLayout v = (ValueLayout)other; - return order.equals(v.order) && + return carrier.equals(v.carrier) && + order.equals(v.order) && bitSize() == v.bitSize() && alignment == v.alignment; } + /** + * Returns the carrier associated with this value layout. + * @return the carrier associated with this value layout. + */ + public Class carrier() { + return carrier; + } + @Override public int hashCode() { return Objects.hash(super.hashCode(), order, bitSize(), alignment); } @Override - ValueLayout dup(long alignment, Map attributes) { - return new ValueLayout(order, bitSize(), alignment, attributes); + ValueLayout dup(long alignment, Optional name) { + return new ValueLayout(carrier, order, bitSize(), alignment, name()); } @Override public Optional> describeConstable() { return Optional.of(decorateLayoutConstant(DynamicConstantDesc.ofNamed(ConstantDescs.BSM_INVOKE, "value", - CD_VALUE_LAYOUT, MH_VALUE, bitSize(), order == ByteOrder.BIG_ENDIAN ? BIG_ENDIAN : LITTLE_ENDIAN))); + CD_VALUE_LAYOUT, MH_VALUE, carrier().describeConstable().get(), order == ByteOrder.BIG_ENDIAN ? BIG_ENDIAN : LITTLE_ENDIAN))); } //hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout @@ -143,11 +167,439 @@ public ValueLayout withBitAlignment(long alignmentBits) { return (ValueLayout)super.withBitAlignment(alignmentBits); } + static void checkCarrierSize(Class carrier, long size) { + if (!isValidCarrier(carrier)) { + throw new IllegalArgumentException("Invalid carrier: " + carrier.getName()); + } + if (carrier == MemoryAddress.class && size != ADDRESS_SIZE_BITS) { + throw new IllegalArgumentException("Address size mismatch: " + ADDRESS_SIZE_BITS + " != " + size); + } + if (carrier.isPrimitive()) { + int expectedSize = carrier == boolean.class ? 8 : Wrapper.forPrimitiveType(carrier).bitWidth(); + if (size != expectedSize) { + throw new IllegalArgumentException("Carrier size mismatch: " + carrier.getName() + " != " + size); + } + } + } + + static boolean isValidCarrier(Class carrier) { + return carrier == boolean.class + || carrier == byte.class + || carrier == short.class + || carrier == char.class + || carrier == int.class + || carrier == long.class + || carrier == float.class + || carrier == double.class + || carrier == MemoryAddress.class; + } + + @Stable + private VarHandle handle; + + @ForceInline + VarHandle accessHandle() { + if (handle == null) { + // this store to stable field is safe, because return value of 'makeMemoryAccessVarHandle' has stable identity + handle = Utils.makeMemoryAccessVarHandle(this, false); + } + return handle; + } + /** - * {@inheritDoc} + * A value layout whose carrier is {@code boolean.class}. */ - @Override - public ValueLayout withAttribute(String name, Constable value) { - return (ValueLayout)super.withAttribute(name, value); + public static final class OfBoolean extends ValueLayout { + OfBoolean(ByteOrder order) { + super(boolean.class, order, 8); + } + + OfBoolean(ByteOrder order, long alignment, Optional name) { + super(boolean.class, order, 8, alignment, name); + } + + @Override + OfBoolean dup(long alignment, Optional name) { + return new OfBoolean(order(), alignment, name); + } + + @Override + public OfBoolean withName(String name) { + return (OfBoolean)super.withName(name); + } + + @Override + public OfBoolean withBitAlignment(long alignmentBits) { + return (OfBoolean)super.withBitAlignment(alignmentBits); + } + + @Override + public OfBoolean withOrder(ByteOrder order) { + Objects.requireNonNull(order); + return new OfBoolean(order, alignment, name()); + } + } + + /** + * A value layout whose carrier is {@code byte.class}. + */ + public static final class OfByte extends ValueLayout { + OfByte(ByteOrder order) { + super(byte.class, order, 8); + } + + OfByte(ByteOrder order, long alignment, Optional name) { + super(byte.class, order, 8, alignment, name); + } + + @Override + OfByte dup(long alignment, Optional name) { + return new OfByte(order(), alignment, name); + } + + @Override + public OfByte withName(String name) { + return (OfByte)super.withName(name); + } + + @Override + public OfByte withBitAlignment(long alignmentBits) { + return (OfByte)super.withBitAlignment(alignmentBits); + } + + @Override + public OfByte withOrder(ByteOrder order) { + Objects.requireNonNull(order); + return new OfByte(order, alignment, name()); + } } + + /** + * A value layout whose carrier is {@code char.class}. + */ + public static final class OfChar extends ValueLayout { + OfChar(ByteOrder order) { + super(char.class, order, 16); + } + + OfChar(ByteOrder order, long alignment, Optional name) { + super(char.class, order, 16, alignment, name); + } + + @Override + OfChar dup(long alignment, Optional name) { + return new OfChar(order(), alignment, name); + } + + @Override + public OfChar withName(String name) { + return (OfChar)super.withName(name); + } + + @Override + public OfChar withBitAlignment(long alignmentBits) { + return (OfChar)super.withBitAlignment(alignmentBits); + } + + @Override + public OfChar withOrder(ByteOrder order) { + Objects.requireNonNull(order); + return new OfChar(order, alignment, name()); + } + } + + /** + * A value layout whose carrier is {@code short.class}. + */ + public static final class OfShort extends ValueLayout { + OfShort(ByteOrder order) { + super(short.class, order, 16); + } + + OfShort(ByteOrder order, long alignment, Optional name) { + super(short.class, order, 16, alignment, name); + } + + @Override + OfShort dup(long alignment, Optional name) { + return new OfShort(order(), alignment, name); + } + + @Override + public OfShort withName(String name) { + return (OfShort)super.withName(name); + } + + @Override + public OfShort withBitAlignment(long alignmentBits) { + return (OfShort)super.withBitAlignment(alignmentBits); + } + + @Override + public OfShort withOrder(ByteOrder order) { + Objects.requireNonNull(order); + return new OfShort(order, alignment, name()); + } + } + + /** + * A value layout whose carrier is {@code int.class}. + */ + public static final class OfInt extends ValueLayout { + OfInt(ByteOrder order) { + super(int.class, order, 32); + } + + OfInt(ByteOrder order, long alignment, Optional name) { + super(int.class, order, 32, alignment, name); + } + + @Override + OfInt dup(long alignment, Optional name) { + return new OfInt(order(), alignment, name); + } + + @Override + public OfInt withName(String name) { + return (OfInt)super.withName(name); + } + + @Override + public OfInt withBitAlignment(long alignmentBits) { + return (OfInt)super.withBitAlignment(alignmentBits); + } + + @Override + public OfInt withOrder(ByteOrder order) { + Objects.requireNonNull(order); + return new OfInt(order, alignment, name()); + } + } + + /** + * A value layout whose carrier is {@code float.class}. + */ + public static final class OfFloat extends ValueLayout { + OfFloat(ByteOrder order) { + super(float.class, order, 32); + } + + OfFloat(ByteOrder order, long alignment, Optional name) { + super(float.class, order, 32, alignment, name); + } + + @Override + OfFloat dup(long alignment, Optional name) { + return new OfFloat(order(), alignment, name); + } + + @Override + public OfFloat withName(String name) { + return (OfFloat)super.withName(name); + } + + @Override + public OfFloat withBitAlignment(long alignmentBits) { + return (OfFloat)super.withBitAlignment(alignmentBits); + } + + @Override + public OfFloat withOrder(ByteOrder order) { + Objects.requireNonNull(order); + return new OfFloat(order, alignment, name()); + } + } + + /** + * A value layout whose carrier is {@code long.class}. + */ + public static final class OfLong extends ValueLayout { + OfLong(ByteOrder order) { + super(long.class, order, 64); + } + + OfLong(ByteOrder order, long alignment, Optional name) { + super(long.class, order, 64, alignment, name); + } + + @Override + OfLong dup(long alignment, Optional name) { + return new OfLong(order(), alignment, name); + } + + @Override + public OfLong withName(String name) { + return (OfLong)super.withName(name); + } + + @Override + public OfLong withBitAlignment(long alignmentBits) { + return (OfLong)super.withBitAlignment(alignmentBits); + } + + @Override + public OfLong withOrder(ByteOrder order) { + Objects.requireNonNull(order); + return new OfLong(order, alignment, name()); + } + } + + /** + * A value layout whose carrier is {@code double.class}. + */ + public static final class OfDouble extends ValueLayout { + OfDouble(ByteOrder order) { + super(double.class, order, 64); + } + + OfDouble(ByteOrder order, long alignment, Optional name) { + super(double.class, order, 64, alignment, name); + } + + @Override + OfDouble dup(long alignment, Optional name) { + return new OfDouble(order(), alignment, name); + } + + @Override + public OfDouble withName(String name) { + return (OfDouble)super.withName(name); + } + + @Override + public OfDouble withBitAlignment(long alignmentBits) { + return (OfDouble)super.withBitAlignment(alignmentBits); + } + + @Override + public OfDouble withOrder(ByteOrder order) { + Objects.requireNonNull(order); + return new OfDouble(order, alignment, name()); + } + } + + /** + * A value layout whose carrier is {@code MemoryAddress.class}. + */ + public static final class OfAddress extends ValueLayout { + OfAddress(ByteOrder order) { + super(MemoryAddress.class, order, ADDRESS_SIZE_BITS); + } + + OfAddress(ByteOrder order, long size, long alignment, Optional name) { + super(MemoryAddress.class, order, size, alignment, name); + } + + @Override + OfAddress dup(long alignment, Optional name) { + return new OfAddress(order(), bitSize(), alignment, name); + } + + @Override + public OfAddress withName(String name) { + return (OfAddress)super.withName(name); + } + + @Override + public OfAddress withBitAlignment(long alignmentBits) { + return (OfAddress)super.withBitAlignment(alignmentBits); + } + + @Override + public OfAddress withOrder(ByteOrder order) { + Objects.requireNonNull(order); + return new OfAddress(order, bitSize(), alignment, name()); + } + } + + /** + * A value layout constant whose size is the same as that of a machine address (e.g. {@code size_t}), + * bit-alignment set to 8, and byte order set to {@link ByteOrder#nativeOrder()}. + * Equivalent to the following code: + *

    {@code
    +    MemoryLayout.valueLayout(MemoryAddress.class, ByteOrder.nativeOrder()).withBitAlignment(8);
    +     * }
    + */ + public static final OfAddress ADDRESS = new OfAddress(ByteOrder.nativeOrder()).withBitAlignment(8); + + /** + * A value layout constant whose size is the same as that of a Java {@code byte}, + * bit-alignment set to 8, and byte order set to {@link ByteOrder#nativeOrder()}. + * Equivalent to the following code: + *
    {@code
    +    MemoryLayout.valueLayout(byte.class, ByteOrder.nativeOrder()).withBitAlignment(8);
    +     * }
    + */ + public static final OfByte JAVA_BYTE = new OfByte(ByteOrder.nativeOrder()).withBitAlignment(8); + + /** + * A value layout constant whose size is the same as that of a Java {@code boolean}, + * bit-alignment set to 8, and byte order set to {@link ByteOrder#nativeOrder()}. + * Equivalent to the following code: + *
    {@code
    +    MemoryLayout.valueLayout(boolean.class, ByteOrder.nativeOrder()).withBitAlignment(8);
    +     * }
    + */ + public static final OfBoolean JAVA_BOOLEAN = new OfBoolean(ByteOrder.nativeOrder()).withBitAlignment(8); + + /** + * A value layout constant whose size is the same as that of a Java {@code char}, + * bit-alignment set to 8, and byte order set to {@link ByteOrder#nativeOrder()}. + * Equivalent to the following code: + *
    {@code
    +    MemoryLayout.valueLayout(char.class, ByteOrder.nativeOrder()).withBitAlignment(8);
    +     * }
    + */ + public static final OfChar JAVA_CHAR = new OfChar(ByteOrder.nativeOrder()).withBitAlignment(8); + + /** + * A value layout constant whose size is the same as that of a Java {@code short}, + * bit-alignment set to 8, and byte order set to {@link ByteOrder#nativeOrder()}. + * Equivalent to the following code: + *
    {@code
    +    MemoryLayout.valueLayout(short.class, ByteOrder.nativeOrder()).withBitAlignment(8);
    +     * }
    + */ + public static final OfShort JAVA_SHORT = new OfShort(ByteOrder.nativeOrder()).withBitAlignment(8); + + /** + * A value layout constant whose size is the same as that of a Java {@code int}, + * bit-alignment set to 8, and byte order set to {@link ByteOrder#nativeOrder()}. + * Equivalent to the following code: + *
    {@code
    +    MemoryLayout.valueLayout(int.class, ByteOrder.nativeOrder()).withBitAlignment(8);
    +     * }
    + */ + public static final OfInt JAVA_INT = new OfInt(ByteOrder.nativeOrder()).withBitAlignment(8); + + /** + * A value layout constant whose size is the same as that of a Java {@code long}, + * bit-alignment set to 8, and byte order set to {@link ByteOrder#nativeOrder()}. + * Equivalent to the following code: + *
    {@code
    +    MemoryLayout.valueLayout(long.class, ByteOrder.nativeOrder()).withBitAlignment(8);
    +     * }
    + */ + public static final OfLong JAVA_LONG = new OfLong(ByteOrder.nativeOrder()) + .withBitAlignment(8); + + /** + * A value layout constant whose size is the same as that of a Java {@code float}, + * bit-alignment set to 8, and byte order set to {@link ByteOrder#nativeOrder()}. + * Equivalent to the following code: + *
    {@code
    +    MemoryLayout.valueLayout(float.class, ByteOrder.nativeOrder()).withBitAlignment(8);
    +     * }
    + */ + public static final OfFloat JAVA_FLOAT = new OfFloat(ByteOrder.nativeOrder()).withBitAlignment(8); + + /** + * A value layout constant whose size is the same as that of a Java {@code double}, + * bit-alignment set to 8, and byte order set to {@link ByteOrder#nativeOrder()}. + * Equivalent to the following code: + *
    {@code
    +    MemoryLayout.valueLayout(double.class, ByteOrder.nativeOrder()).withBitAlignment(8);
    +     * }
    + */ + public static final OfDouble JAVA_DOUBLE = new OfDouble(ByteOrder.nativeOrder()).withBitAlignment(8); } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/package-info.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/package-info.java index d4d9a9356cef0..82bc2f53d4b2b 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/package-info.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,12 +30,11 @@ *

    Foreign memory access

    * *

    - * The key abstractions introduced to support foreign memory access are {@link jdk.incubator.foreign.MemorySegment} and {@link jdk.incubator.foreign.MemoryAddress}. - * The first models a contiguous memory region, which can reside either inside or outside the Java heap; the latter models an address - which also can - * reside either inside or outside the Java heap (and can sometimes be expressed as an offset into a given segment). + * The main abstractions introduced to support foreign memory access is {@link jdk.incubator.foreign.MemorySegment}, which + * models a contiguous memory region, which can reside either inside or outside the Java heap. * A memory segment represents the main access coordinate of a memory access var handle, which can be obtained * using the combinator methods defined in the {@link jdk.incubator.foreign.MemoryHandles} class; a set of - * common dereference operations is provided also by the {@link jdk.incubator.foreign.MemoryAccess} class, which can + * common dereference and copy operations is provided also by the {@link jdk.incubator.foreign.MemorySegment} class, which can * be useful for simple, non-structured access. Finally, the {@link jdk.incubator.foreign.MemoryLayout} class * hierarchy enables description of memory layouts and basic operations such as computing the size in bytes of a given * layout, obtain its alignment requirements, and so on. Memory layouts also provide an alternate, more abstract way, to produce @@ -47,16 +46,18 @@ *

    {@code
     MemorySegment segment = MemorySegment.allocateNative(10 * 4, ResourceScope.newImplicitScope());
     for (int i = 0 ; i < 10 ; i++) {
    -   MemoryAccess.setIntAtIndex(segment, i, 42);
    +   segment.setAtIndex(ValueLayout.JAVA_INT, i, i);
     }
      * }
    * - * Here create a native memory segment, that is, a memory segment backed by + * This code creates a native memory segment, that is, a memory segment backed by * off-heap memory; the size of the segment is 40 bytes, enough to store 10 values of the primitive type {@code int}. - * Inside a loop, we then initialize the contents of the memory segment using the - * {@link jdk.incubator.foreign.MemoryAccess#setIntAtIndex(jdk.incubator.foreign.MemorySegment, long, int)} helper method; - * more specifically, if we view the memory segment as a set of 10 adjacent slots, - * {@code s[i]}, where {@code 0 <= i < 10}, where the size of each slot is exactly 4 bytes, the initialization logic above will set each slot + * Inside a loop, we then initialize the contents of the memory segment; note how the + * {@linkplain jdk.incubator.foreign.MemorySegment#setAtIndex(ValueLayout.OfInt, long, int) dereference method} + * accepts a {@linkplain jdk.incubator.foreign.ValueLayout value layout}, which specifies the size, alignment constraints, + * byte order as well as the Java type ({@code int}, in this case) associated with the dereference operation. More specifically, + * if we view the memory segment as a set of 10 adjacent slots, {@code s[i]}, where {@code 0 <= i < 10}, + * where the size of each slot is exactly 4 bytes, the initialization logic above will set each slot * so that {@code s[i] = i}, again where {@code 0 <= i < 10}. * *

    Deterministic deallocation

    @@ -72,7 +73,7 @@ try (ResourceScope scope = ResourceScope.newConfinedScope()) { MemorySegment segment = MemorySegment.allocateNative(10 * 4, scope); for (int i = 0 ; i < 10 ; i++) { - MemoryAccess.setIntAtIndex(segment, i, 42); + segment.setAtIndex(ValueLayout.JAVA_INT, i, i); } } * }
  • @@ -96,9 +97,10 @@ * operation either succeeds - and accesses a valid memory location - or fails. * *

    Foreign function access

    - * The key abstractions introduced to support foreign function access are {@link jdk.incubator.foreign.SymbolLookup} and {@link jdk.incubator.foreign.CLinker}. - * The former is used to lookup symbols inside native libraries; the latter - * provides linking capabilities which allow to model foreign functions as {@link java.lang.invoke.MethodHandle} instances, + * The key abstractions introduced to support foreign function access are {@link jdk.incubator.foreign.SymbolLookup}, + * {@link jdk.incubator.foreign.MemoryAddress} and {@link jdk.incubator.foreign.CLinker}. + * The first is used to lookup symbols inside native libraries; the second is used to model native addresses (more on that later), + * while the third provides linking capabilities which allows modelling foreign functions as {@link java.lang.invoke.MethodHandle} instances, * so that clients can perform foreign function calls directly in Java, without the need for intermediate layers of native * code (as it's the case with the Java Native Interface (JNI)). *

    @@ -106,30 +108,32 @@ * we can use the following code: * *

    {@code
    -      MethodHandle strlen = CLinker.getInstance().downcallHandle(
    -        CLinker.systemLookup().lookup("strlen").get(),
    -        MethodType.methodType(long.class, MemoryAddress.class),
    -        FunctionDescriptor.of(CLinker.C_LONG, CLinker.C_POINTER)
    +      var linker = CLinker.systemCLinker();
    +      MethodHandle strlen = linker.downcallHandle(
    +        linker.lookup("strlen").get(),
    +        FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS)
           );
     
           try (var scope = ResourceScope.newConfinedScope()) {
    -         var cString = CLinker.toCString("Hello", scope);
    -         long len = (long)strlen.invokeExact(cString.address()); // 5
    +         var cString = MemorySegment.allocateNative(5 + 1, scope);
    +         cString.setUtf8String("Hello");
    +         long len = (long)strlen.invoke(cString); // 5
           }
      * }
    * - * Here, we lookup the {@code strlen} symbol in the {@linkplain jdk.incubator.foreign.CLinker#systemLookup() system lookup}. - * Then, we obtain a linker instance (see {@link jdk.incubator.foreign.CLinker#getInstance()}) and we use it to - * obtain a method handle which targets the {@code strlen} library symbol. To complete the linking successfully, - * we must provide (i) a {@link java.lang.invoke.MethodType} instance, describing the type of the resulting method handle - * and (ii) a {@link jdk.incubator.foreign.FunctionDescriptor} instance, describing the signature of the {@code strlen} - * function. From this information, the linker will uniquely determine the sequence of steps which will turn - * the method handle invocation (here performed using {@link java.lang.invoke.MethodHandle#invokeExact(java.lang.Object...)}) - * into a foreign function call, according to the rules specified by the platform C ABI. The {@link jdk.incubator.foreign.CLinker} - * class also provides many useful methods for interacting with native code, such as converting Java strings into - * native strings and viceversa (see {@link jdk.incubator.foreign.CLinker#toCString(java.lang.String, ResourceScope)} and - * {@link jdk.incubator.foreign.CLinker#toJavaString(jdk.incubator.foreign.MemorySegment)}, respectively), as - * demonstrated in the above example. + * Here, we obtain a {@linkplain jdk.incubator.foreign.CLinker#systemCLinker() linker instance} and we use it + * to {@linkplain jdk.incubator.foreign.CLinker#lookup(java.lang.String) lookup} the {@code strlen} symbol in the + * standard C library; a downcall method handle targeting said symbol is subsequently + * {@linkplain jdk.incubator.foreign.CLinker#downcallHandle(jdk.incubator.foreign.FunctionDescriptor) obtained}. + * To complete the linking successfully, we must provide a {@link jdk.incubator.foreign.FunctionDescriptor} instance, + * describing the signature of the {@code strlen} function. + * From this information, the linker will uniquely determine the sequence of steps which will turn + * the method handle invocation (here performed using {@link java.lang.invoke.MethodHandle#invoke(java.lang.Object...)}) + * into a foreign function call, according to the rules specified by the platform C ABI. + * The {@link jdk.incubator.foreign.MemorySegment} class also provides many useful methods for + * interacting with native code, such as converting Java strings + * {@linkplain jdk.incubator.foreign.MemorySegment#setUtf8String(long, java.lang.String) into} native strings and + * {@linkplain jdk.incubator.foreign.MemorySegment#getUtf8String(long) back}, as demonstrated in the above example. * *

    Foreign addresses

    * @@ -138,53 +142,40 @@ * such pointers have no spatial bounds (example: does the C type {@code char*} refer to a single {@code char} value, * or an array of {@code char} values, of given size?), no notion of temporal bounds, nor thread-confinement. *

    - * When clients receive a {@link jdk.incubator.foreign.MemoryAddress} instance from a foreign function call, it might be - * necessary to obtain a {@link jdk.incubator.foreign.MemorySegment} instance to dereference the memory pointed to by that address. - * To do that, clients can proceed in three different ways, described below. - *

    - * First, if the memory address is known to belong to a segment the client already owns, a rebase operation can be performed; - * in other words, the client can ask the address what its offset relative to a given segment is, and, then, proceed to dereference - * the original segment accordingly, as follows: + * Raw pointers are modelled using the {@link jdk.incubator.foreign.MemoryAddress} class. When clients receive a + * memory address instance from a foreign function call, they can perform memory dereference on it directly, + * using one of the many unsafe + * {@linkplain jdk.incubator.foreign.MemoryAddress#get(jdk.incubator.foreign.ValueLayout.OfInt, long) dereference methods} + * provided: * *

    {@code
    -MemorySegment segment = MemorySegment.allocateNative(100, scope);
     ...
     MemoryAddress addr = ... //obtain address from native code
    -int x = MemoryAccess.getIntAtOffset(segment, addr.segmentOffset(segment));
    +int x = addr.get(ValueLayout.JAVA_INT, 0);
      * }
    * - * Secondly, if the client does not have a segment which contains a given memory address, it can create one unsafely, - * using the {@link jdk.incubator.foreign.MemoryAddress#asSegment(long, ResourceScope)} factory. This allows the client to - * inject extra knowledge about spatial bounds which might, for instance, be available in the documentation of the foreign function - * which produced the native address. Here is how an unsafe segment can be created from a native address: + * Alternatively, the client can + * {@linkplain jdk.incubator.foreign.MemorySegment#ofAddress(jdk.incubator.foreign.MemoryAddress, long, jdk.incubator.foreign.ResourceScope) create} + * a memory segment unsafely. This allows the client to inject extra knowledge about spatial bounds which might, + * for instance, be available in the documentation of the foreign function which produced the native address. + * Here is how an unsafe segment can be created from a native address: * *
    {@code
     ResourceScope scope = ... // initialize a resource scope object
     MemoryAddress addr = ... //obtain address from native code
    -MemorySegment segment = addr.asSegment(4, scope); // segment is 4 bytes long
    -int x = MemoryAccess.getInt(segment);
    - * }
    - * - * Alternatively, the client can fall back to use the so called everything segment - that is, a primordial segment - * which covers the entire native heap. This segment can be obtained by calling the {@link jdk.incubator.foreign.MemorySegment#globalNativeSegment()} - * method, so that dereference can happen without the need of creating any additional segment instances: - * - *
    {@code
    -MemoryAddress addr = ... //obtain address from native code
    -int x = MemoryAccess.getIntAtOffset(MemorySegment.globalNativeSegment(), addr.toRawLongValue());
    +MemorySegment segment = MemorySegment.ofAddress(addr, 4, scope); // segment is 4 bytes long
    +int x = segment.get(ValueLayout.JAVA_INT, 0);
      * }
    * *

    Upcalls

    * The {@link jdk.incubator.foreign.CLinker} interface also allows to turn an existing method handle (which might point - * to a Java method) into a native memory address (see {@link jdk.incubator.foreign.MemoryAddress}), so that Java code - * can effectively be passed to other foreign functions. For instance, we can write a method that compares two - * integer values, as follows: + * to a Java method) into a memory address, so that Java code can effectively be passed to other foreign functions. + * For instance, we can write a method that compares two integer values, as follows: * *
    {@code
     class IntComparator {
         static int intCompare(MemoryAddress addr1, MemoryAddress addr2) {
    -        return MemoryAccess.getIntAtOffset(MemorySegment.globalNativeSegment(), addr1.toRawLongValue()) -
    -               MemoryAccess.getIntAtOffset(MemorySegment.globalNativeSegment(), addr2.toRawLongValue());
    +        return addr1.get(ValueLayout.JAVA_INT, 0) - addr2.get(ValueLayout.JAVA_INT, 0);
         }
     }
      * }
    @@ -194,39 +185,45 @@ static int intCompare(MemoryAddress addr1, MemoryAddress addr2) { * method, as follows: * *
    {@code
    +FunctionDescriptor intCompareDescriptor = FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.ADDRESS);
     MethodHandle intCompareHandle = MethodHandles.lookup().findStatic(IntComparator.class,
                                                        "intCompare",
    -                                                   MethodType.methodType(int.class, MemoryAddress.class, MemoryAddress.class));
    +                                                   CLinker.upcallType(comparFunction));
      * }
    * - * Now that we have a method handle instance, we can link it into a fresh native memory address, using the {@link jdk.incubator.foreign.CLinker} interface, as follows: + * As before, we need to create a {@link jdk.incubator.foreign.FunctionDescriptor} instance, this time describing the signature + * of the function pointer we want to create. The descriptor can be used to + * {@linkplain jdk.incubator.foreign.CLinker#upcallType(jdk.incubator.foreign.FunctionDescriptor) derive} a method type + * that can be used to lookup the method handle for {@code IntComparator.intCompare}. + *

    + * Now that we have a method handle instance, we can turn it into a fresh function pointer, + * using the {@link jdk.incubator.foreign.CLinker} interface, as follows: * *

    {@code
     ResourceScope scope = ...
    -MemoryAddress comparFunc = CLinker.getInstance().upcallStub(
    -     intCompareHandle,
    -     FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER),
    -     scope
    +Addressable comparFunc = CLinker.systemCLinker().upcallStub(
    +     intCompareHandle, intCompareDescriptor, scope);
     );
      * }
    * - * As before, we need to provide a {@link jdk.incubator.foreign.FunctionDescriptor} instance describing the signature - * of the function pointer we want to create; as before, this, coupled with the method handle type, uniquely determines the - * sequence of steps which will allow foreign code to call {@code intCompareHandle} according to the rules specified - * by the platform C ABI. The lifecycle of the memory address returned by - * {@link jdk.incubator.foreign.CLinker#upcallStub(java.lang.invoke.MethodHandle, jdk.incubator.foreign.FunctionDescriptor, jdk.incubator.foreign.ResourceScope)} - * is tied to the {@linkplain jdk.incubator.foreign.ResourceScope resource scope} parameter passed to that method. + * The {@link jdk.incubator.foreign.FunctionDescriptor} instance created in the previous step is then used to + * {@linkplain jdk.incubator.foreign.CLinker#upcallStub(java.lang.invoke.MethodHandle, jdk.incubator.foreign.FunctionDescriptor, jdk.incubator.foreign.ResourceScope) create} + * a new upcall stub; the layouts in the function descriptors allow the linker to determine the sequence of steps which + * allow foreign code to call the stub for {@code intCompareHandle} according to the rules specified by the platform C ABI. + * The lifecycle of the upcall stub returned by is tied to the {@linkplain jdk.incubator.foreign.ResourceScope resource scope} + * provided when the upcall stub is created. This same scope is made available by the {@link jdk.incubator.foreign.NativeSymbol} + * instance returned by that method. * * *

    Restricted methods

    * Some methods in this package are considered restricted. Restricted methods are typically used to bind native * foreign data and/or functions to first-class Java API elements which can then be used directly by clients. For instance - * the restricted method {@link jdk.incubator.foreign.MemoryAddress#asSegment(long, ResourceScope)} can be used to create - * a fresh segment with given spatial bounds out of a native address. + * the restricted method {@link MemorySegment#ofAddress(MemoryAddress, long, ResourceScope)} + * can be used to create a fresh segment with given spatial bounds out of a native address. *

    * Binding foreign data and/or functions is generally unsafe and, if done incorrectly, can result in VM crashes, or memory corruption when the bound Java API element is accessed. - * For instance, in the case of {@link jdk.incubator.foreign.MemoryAddress#asSegment(long, ResourceScope)}, if the provided - * spatial bounds are incorrect, a client of the segment returned by that method might crash the VM, or corrupt + * For instance, in the case of {@link MemorySegment#ofAddress(MemoryAddress, long, ResourceScope)}, + * if the provided spatial bounds are incorrect, a client of the segment returned by that method might crash the VM, or corrupt * memory when attempting to dereference said segment. For these reasons, it is crucial for code that calls a restricted method * to never pass arguments that might cause incorrect binding of foreign data and/or functions to a Java API. *

    diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/AbstractCLinker.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/AbstractCLinker.java deleted file mode 100644 index 91a7cbff6c616..0000000000000 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/AbstractCLinker.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. 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.incubator.foreign.Addressable; -import jdk.incubator.foreign.CLinker; -import jdk.incubator.foreign.FunctionDescriptor; -import jdk.incubator.foreign.MemorySegment; -import jdk.incubator.foreign.SegmentAllocator; -import jdk.internal.foreign.abi.SharedUtils; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.util.Objects; - -public abstract non-sealed class AbstractCLinker implements CLinker { - - public final MethodHandle downcallHandle(Addressable symbol, MethodType type, FunctionDescriptor function) { - SharedUtils.checkSymbol(symbol); - return MethodHandles.insertArguments(downcallHandle(type, function), 0, symbol); - } - - public final MethodHandle downcallHandle(Addressable symbol, SegmentAllocator allocator, MethodType type, FunctionDescriptor function) { - SharedUtils.checkSymbol(symbol); - Objects.requireNonNull(allocator); - MethodHandle downcall = MethodHandles.insertArguments(downcallHandle(type, function), 0, symbol); - if (type.returnType().equals(MemorySegment.class)) { - downcall = MethodHandles.insertArguments(downcall, 0, allocator); - } - return downcall; - } -} diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java index b54ba68f29f20..eacbeb26a5192 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,6 +36,7 @@ import sun.security.action.GetPropertyAction; import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.*; import java.util.function.Consumer; import java.util.function.Function; @@ -43,6 +44,8 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; +import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE; + /** * This abstract class provides an immutable implementation for the {@code MemorySegment} interface. This class contains information * about the segment's spatial and temporal bounds; each memory segment implementation is associated with an owner thread which is set at creation time. @@ -52,7 +55,7 @@ * are defined for each memory segment kind, see {@link NativeMemorySegmentImpl}, {@link HeapMemorySegmentImpl} and * {@link MappedMemorySegmentImpl}. */ -public abstract non-sealed class AbstractMemorySegmentImpl extends MemorySegmentProxy implements MemorySegment { +public abstract non-sealed class AbstractMemorySegmentImpl extends MemorySegmentProxy implements MemorySegment, SegmentAllocator, Scoped { private static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess(); @@ -140,24 +143,9 @@ public final MemorySegment fill(byte value){ return this; } - public void copyFrom(MemorySegment src) { - AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl)Objects.requireNonNull(src); - long size = that.byteSize(); - checkAccess(0, size, false); - that.checkAccess(0, size, true); - SCOPED_MEMORY_ACCESS.copyMemory(scope, that.scope, - that.base(), that.min(), - base(), min(), size); - } - - public void copyFromSwap(MemorySegment src, long elemSize) { - AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl)src; - long size = that.byteSize(); - checkAccess(0, size, false); - that.checkAccess(0, size, true); - SCOPED_MEMORY_ACCESS.copySwapMemory(scope, that.scope, - that.base(), that.min(), - base(), min(), size, elemSize); + @Override + public MemorySegment allocate(long bytesSize, long bytesAlignment) { + return asSlice(0, bytesSize); } @Override @@ -175,7 +163,7 @@ public long mismatch(MemorySegment other) { long i = 0; if (length > 7) { - if (MemoryAccess.getByte(this) != MemoryAccess.getByte(that)) { + if (get(JAVA_BYTE, 0) != that.get(JAVA_BYTE, 0)) { return 0; } i = vectorizedMismatchLargeForBytes(scope, that.scope, @@ -190,7 +178,7 @@ public long mismatch(MemorySegment other) { i = length - remaining; } for (; i < length; i++) { - if (MemoryAccess.getByteAtOffset(this, i) != MemoryAccess.getByteAtOffset(that, i)) { + if (get(JAVA_BYTE, i) != that.get(JAVA_BYTE, i)) { return i; } } @@ -230,9 +218,8 @@ private static long vectorizedMismatchLargeForBytes(ResourceScopeImpl aScope, Re } @Override - @ForceInline - public final MemoryAddress address() { - return new MemoryAddressImpl(this, 0L); + public MemoryAddress address() { + throw new UnsupportedOperationException("Cannot obtain address of on-heap segment"); } @Override @@ -269,6 +256,33 @@ public boolean isNative() { return false; } + @Override + public final MemorySegment asOverlappingSlice(MemorySegment other) { + AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl)Objects.requireNonNull(other); + if (base() == that.base()) { // both either native or heap + final long thisStart = this.min(); + final long thatStart = that.min(); + final long thisEnd = thisStart + this.byteSize(); + final long thatEnd = thatStart + that.byteSize(); + + if (thisStart < thatEnd && thisEnd > thatStart) { //overlap occurs + long offsetToThat = this.segmentOffset(that); + long newOffset = offsetToThat >= 0 ? offsetToThat : 0; + return asSlice(newOffset, Math.min(this.byteSize() - newOffset, that.byteSize() + offsetToThat)); + } + } + return null; + } + + @Override + public final long segmentOffset(MemorySegment other) { + AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl) Objects.requireNonNull(other); + if (base() == that.base()) { + return that.min() - this.min(); + } + throw new UnsupportedOperationException("Cannot compute offset from native to heap (or vice versa)."); + } + @Override public void load() { throw new UnsupportedOperationException("Not a mapped segment"); @@ -290,45 +304,45 @@ public void force() { } @Override - public final byte[] toByteArray() { - return toArray(byte[].class, 1, byte[]::new, MemorySegment::ofArray); + public final byte[] toArray(ValueLayout.OfByte elementLayout) { + return toArray(byte[].class, elementLayout, byte[]::new, MemorySegment::ofArray); } @Override - public final short[] toShortArray() { - return toArray(short[].class, 2, short[]::new, MemorySegment::ofArray); + public final short[] toArray(ValueLayout.OfShort elementLayout) { + return toArray(short[].class, elementLayout, short[]::new, MemorySegment::ofArray); } @Override - public final char[] toCharArray() { - return toArray(char[].class, 2, char[]::new, MemorySegment::ofArray); + public final char[] toArray(ValueLayout.OfChar elementLayout) { + return toArray(char[].class, elementLayout, char[]::new, MemorySegment::ofArray); } @Override - public final int[] toIntArray() { - return toArray(int[].class, 4, int[]::new, MemorySegment::ofArray); + public final int[] toArray(ValueLayout.OfInt elementLayout) { + return toArray(int[].class, elementLayout, int[]::new, MemorySegment::ofArray); } @Override - public final float[] toFloatArray() { - return toArray(float[].class, 4, float[]::new, MemorySegment::ofArray); + public final float[] toArray(ValueLayout.OfFloat elementLayout) { + return toArray(float[].class, elementLayout, float[]::new, MemorySegment::ofArray); } @Override - public final long[] toLongArray() { - return toArray(long[].class, 8, long[]::new, MemorySegment::ofArray); + public final long[] toArray(ValueLayout.OfLong elementLayout) { + return toArray(long[].class, elementLayout, long[]::new, MemorySegment::ofArray); } @Override - public final double[] toDoubleArray() { - return toArray(double[].class, 8, double[]::new, MemorySegment::ofArray); + public final double[] toArray(ValueLayout.OfDouble elementLayout) { + return toArray(double[].class, elementLayout, double[]::new, MemorySegment::ofArray); } - private Z toArray(Class arrayClass, int elemSize, IntFunction arrayFactory, Function segmentFactory) { - int size = checkArraySize(arrayClass.getSimpleName(), elemSize); + private Z toArray(Class arrayClass, ValueLayout elemLayout, IntFunction arrayFactory, Function segmentFactory) { + int size = checkArraySize(arrayClass.getSimpleName(), (int)elemLayout.byteSize()); Z arr = arrayFactory.apply(size); MemorySegment arrSegment = segmentFactory.apply(arr); - arrSegment.copyFrom(this); + MemorySegment.copy(this, elemLayout, 0, arrSegment, elemLayout.withOrder(ByteOrder.nativeOrder()), 0, size); return arr; } @@ -382,11 +396,12 @@ private int checkArraySize(String typeName, int elemSize) { private void checkBounds(long offset, long length) { if (isSmall() && - offset < Integer.MAX_VALUE && length < Integer.MAX_VALUE && - offset > Integer.MIN_VALUE && length > Integer.MIN_VALUE) { + offset <= Integer.MAX_VALUE && length <= Integer.MAX_VALUE && + offset >= Integer.MIN_VALUE && length >= Integer.MIN_VALUE) { checkBoundsSmall((int)offset, (int)length); - } else { - if (length < 0 || + } else if (this != NativeMemorySegmentImpl.EVERYTHING) { // oob not possible for everything segment + if ( + length < 0 || offset < 0 || offset > this.length - length) { // careful of overflow throw outOfBoundException(offset, length); diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/ArenaAllocator.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/ArenaAllocator.java index 8a8474a3be547..009ea6c703d8d 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/ArenaAllocator.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/ArenaAllocator.java @@ -29,14 +29,23 @@ import jdk.incubator.foreign.SegmentAllocator; import jdk.incubator.foreign.ResourceScope; -public abstract class ArenaAllocator implements SegmentAllocator { +public final class ArenaAllocator implements SegmentAllocator { - protected MemorySegment segment; + public static final long DEFAULT_BLOCK_SIZE = 4 * 1024; - protected long sp = 0L; + private MemorySegment segment; - ArenaAllocator(MemorySegment segment) { - this.segment = segment; + private long sp = 0L; + private long size = 0; + private final long blockSize; + private final long arenaSize; + private final ResourceScope scope; + + public ArenaAllocator(long blockSize, long arenaSize, ResourceScope scope) { + this.blockSize = blockSize; + this.arenaSize = arenaSize; + this.scope = scope; + this.segment = newSegment(blockSize, 1); } MemorySegment trySlice(long bytesSize, long bytesAlignment) { @@ -51,99 +60,37 @@ MemorySegment trySlice(long bytesSize, long bytesAlignment) { } } - void checkConfinementIfNeeded() { - Thread ownerThread = scope().ownerThread(); - if (ownerThread != null && ownerThread != Thread.currentThread()) { - throw new IllegalStateException("Attempt to allocate outside confinement thread"); - } + public ResourceScope scope() { + return scope; } - ResourceScope scope() { - return segment.scope(); - } - - public static class UnboundedArenaAllocator extends ArenaAllocator { - - private static final long DEFAULT_BLOCK_SIZE = 4 * 1024; - - public UnboundedArenaAllocator(ResourceScope scope) { - super(MemorySegment.allocateNative(DEFAULT_BLOCK_SIZE, 1, scope)); - } - - private MemorySegment newSegment(long size, long align) { - return MemorySegment.allocateNative(size, align, segment.scope()); - } - - @Override - public MemorySegment allocate(long bytesSize, long bytesAlignment) { - checkConfinementIfNeeded(); - // try to slice from current segment first... - MemorySegment slice = trySlice(bytesSize, bytesAlignment); - if (slice != null) { - return slice; - } else { - long maxPossibleAllocationSize = bytesSize + bytesAlignment - 1; - if (maxPossibleAllocationSize > DEFAULT_BLOCK_SIZE) { - // too big - return newSegment(bytesSize, bytesAlignment); - } else { - // allocate a new segment and slice from there - sp = 0L; - segment = newSegment(DEFAULT_BLOCK_SIZE, 1L); - return trySlice(bytesSize, bytesAlignment); - } - } + private MemorySegment newSegment(long bytesSize, long bytesAlignment) { + long allocatedSize = Utils.alignUp(bytesSize, bytesAlignment); + if (size + allocatedSize > arenaSize) { + throw new OutOfMemoryError(); } + size += allocatedSize; + return MemorySegment.allocateNative(bytesSize, bytesAlignment, scope); } - public static class BoundedArenaAllocator extends ArenaAllocator { - - public BoundedArenaAllocator(ResourceScope scope, long size) { - super(MemorySegment.allocateNative(size, 1, scope)); - } - - @Override - public MemorySegment allocate(long bytesSize, long bytesAlignment) { - checkConfinementIfNeeded(); - // try to slice from current segment first... - MemorySegment slice = trySlice(bytesSize, bytesAlignment); - if (slice != null) { - return slice; + @Override + public MemorySegment allocate(long bytesSize, long bytesAlignment) { + // try to slice from current segment first... + MemorySegment slice = trySlice(bytesSize, bytesAlignment); + if (slice != null) { + return slice; + } else { + long maxPossibleAllocationSize = bytesSize + bytesAlignment - 1; + if (maxPossibleAllocationSize > blockSize) { + // too big + return newSegment(bytesSize, bytesAlignment); } else { - throw new OutOfMemoryError("Not enough space left to allocate"); - } - } - } - - public static class BoundedSharedArenaAllocator extends BoundedArenaAllocator { - public BoundedSharedArenaAllocator(ResourceScope scope, long size) { - super(scope, size); - } - - @Override - public synchronized MemorySegment allocate(long bytesSize, long bytesAlignment) { - return super.allocate(bytesSize, bytesAlignment); - } - } - - public static class UnboundedSharedArenaAllocator implements SegmentAllocator { - - final ResourceScope scope; - - final ThreadLocal allocators = new ThreadLocal<>() { - @Override - protected ArenaAllocator initialValue() { - return new UnboundedArenaAllocator(scope); + // allocate a new segment and slice from there + sp = 0L; + segment = newSegment(blockSize, 1L); + slice = trySlice(bytesSize, bytesAlignment); + return slice; } - }; - - public UnboundedSharedArenaAllocator(ResourceScope scope) { - this.scope = scope; - } - - @Override - public MemorySegment allocate(long bytesSize, long bytesAlignment) { - return allocators.get().allocate(bytesSize, bytesAlignment); } } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/CABI.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/CABI.java index 977a875521088..24abdede3be68 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/CABI.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/CABI.java @@ -25,9 +25,7 @@ */ package jdk.internal.foreign; -import sun.security.action.GetPropertyAction; - -import static jdk.incubator.foreign.MemoryLayouts.ADDRESS; +import static jdk.incubator.foreign.ValueLayout.ADDRESS; import static sun.security.action.GetPropertyAction.privilegedGetProperty; public enum CABI { diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/ConfinedScope.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/ConfinedScope.java index 16749105bd156..ac8850258c15c 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/ConfinedScope.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/ConfinedScope.java @@ -25,11 +25,11 @@ package jdk.internal.foreign; -import jdk.incubator.foreign.ResourceScope; import jdk.internal.vm.annotation.ForceInline; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.lang.ref.Cleaner; -import java.lang.ref.Reference; /** * A confined scope, which features an owner thread. The liveness check features an additional @@ -41,10 +41,21 @@ final class ConfinedScope extends ResourceScopeImpl { private boolean closed; // = false private int lockCount = 0; + private int asyncReleaseCount = 0; private final Thread owner; + static final VarHandle ASYNC_RELEASE_COUNT; + + static { + try { + ASYNC_RELEASE_COUNT = MethodHandles.lookup().findVarHandle(ConfinedScope.class, "asyncReleaseCount", int.class); + } catch (Throwable ex) { + throw new ExceptionInInitializerError(ex); + } + } + public ConfinedScope(Thread owner, Cleaner cleaner) { - super(cleaner, new ConfinedResourceList()); + super(new ConfinedResourceList(), cleaner); this.owner = owner; } @@ -64,18 +75,35 @@ public boolean isAlive() { } @Override - public HandleImpl acquire() { + @ForceInline + public void acquire0() { checkValidState(); + if (lockCount == MAX_FORKS) { + throw new IllegalStateException("Scope keep alive limit exceeded"); + } lockCount++; - return new ConfinedHandle(); + } + + @Override + @ForceInline + public void release0() { + if (Thread.currentThread() == owner) { + lockCount--; + } else { + // It is possible to end up here in two cases: this scope was kept alive by some other confined scope + // which is implicitly released (in which case the release call comes from the cleaner thread). Or, + // this scope might be kept alive by a shared scope, which means the release call can come from any + // thread. + ASYNC_RELEASE_COUNT.getAndAdd(this, 1); + } } void justClose() { this.checkValidState(); - if (lockCount == 0) { + if (lockCount == 0 || lockCount - ((int)ASYNC_RELEASE_COUNT.getVolatile(this)) == 0) { closed = true; } else { - throw new IllegalStateException("Scope is acquired by " + lockCount + " locks"); + throw new IllegalStateException("Scope is kept alive by " + lockCount + " scopes"); } } @@ -109,25 +137,4 @@ void cleanup() { } } } - - /** - * A confined resource scope handle; no races are possible here. - */ - final class ConfinedHandle implements HandleImpl { - boolean released = false; - - @Override - public ResourceScopeImpl scope() { - return ConfinedScope.this; - } - - @Override - public void release() { - checkValidState(); // thread check - if (!released) { - released = true; - lockCount--; - } - } - } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/LayoutPath.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/LayoutPath.java index 346e6882a45c7..1d8bb265c839f 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/LayoutPath.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/LayoutPath.java @@ -28,8 +28,6 @@ import jdk.incubator.foreign.MemoryHandles; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; -import jdk.internal.access.JavaLangInvokeAccess; -import jdk.internal.access.SharedSecrets; import jdk.internal.access.foreign.MemorySegmentProxy; import jdk.incubator.foreign.GroupLayout; @@ -53,12 +51,10 @@ * (see {@link #sequenceElement()}, {@link #sequenceElement(long)}, {@link #sequenceElement(long, long)}, {@link #groupElement(String)}). * Once a path has been fully constructed, clients can ask for the offset associated with the layout element selected * by the path (see {@link #offset}), or obtain a memory access var handle to access the selected layout element - * given an address pointing to a segment associated with the root layout (see {@link #dereferenceHandle(Class)}). + * given an address pointing to a segment associated with the root layout (see {@link #dereferenceHandle()}). */ public class LayoutPath { - private static final JavaLangInvokeAccess JLI = SharedSecrets.getJavaLangInvokeAccess(); - private static final MethodHandle ADD_STRIDE; private static final MethodHandle MH_ADD_SCALED_OFFSET; private static final MethodHandle MH_SLICE; @@ -156,8 +152,10 @@ public long offset() { return offset; } - public VarHandle dereferenceHandle(Class carrier) { - Utils.checkPrimitiveCarrierCompat(carrier, layout); + public VarHandle dereferenceHandle() { + if (!(layout instanceof ValueLayout valueLayout)) { + throw new IllegalArgumentException("Path does not select a value layout"); + } checkAlignment(this); List> expectedCoordinates = new ArrayList<>(); @@ -165,8 +163,7 @@ public VarHandle dereferenceHandle(Class carrier) { perms.addFirst(0); expectedCoordinates.add(MemorySegment.class); - VarHandle handle = Utils.fixUpVarHandle(JLI.memoryAccessVarHandle(carrier, true, layout.byteAlignment() - 1, - ((ValueLayout)layout).order())); + VarHandle handle = Utils.makeMemoryAccessVarHandle(valueLayout, true); for (int i = 0 ; i < strides.length ; i++) { expectedCoordinates.add(long.class); @@ -226,15 +223,13 @@ public MemoryLayout map(UnaryOperator op) { MemoryLayout newLayout = op.apply(layout); if (enclosing == null) { return newLayout; - } else if (enclosing.layout instanceof SequenceLayout) { - SequenceLayout seq = (SequenceLayout)enclosing.layout; + } else if (enclosing.layout instanceof SequenceLayout seq) { if (seq.elementCount().isPresent()) { return enclosing.map(l -> dup(l, MemoryLayout.sequenceLayout(seq.elementCount().getAsLong(), newLayout))); } else { return enclosing.map(l -> dup(l, MemoryLayout.sequenceLayout(newLayout))); } - } else if (enclosing.layout instanceof GroupLayout) { - GroupLayout g = (GroupLayout)enclosing.layout; + } else if (enclosing.layout instanceof GroupLayout g) { List newElements = new ArrayList<>(g.memberLayouts()); //if we selected a layout in a group we must have a valid index newElements.set((int)elementIndex, newLayout); diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemoryAddressImpl.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemoryAddressImpl.java index 2330feefd4e37..59d7105082cfc 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemoryAddressImpl.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemoryAddressImpl.java @@ -25,128 +25,352 @@ */ package jdk.internal.foreign; +import jdk.incubator.foreign.Addressable; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.ResourceScope; +import jdk.incubator.foreign.ValueLayout; +import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; -import jdk.incubator.foreign.ResourceScope; -import java.util.Objects; +import jdk.internal.vm.annotation.ForceInline; /** * This class provides an immutable implementation for the {@code MemoryAddress} interface. This class contains information * about the segment this address is associated with, as well as an offset into such segment. */ -public final class MemoryAddressImpl implements MemoryAddress { +public final class MemoryAddressImpl implements MemoryAddress, Scoped { - private final AbstractMemorySegmentImpl segment; private final long offset; - public MemoryAddressImpl(AbstractMemorySegmentImpl segment, long offset) { - this.segment = segment; + public MemoryAddressImpl(long offset) { this.offset = offset; } - Object base() { - return segment != null ? segment.base() : null; + // MemoryAddress methods + + @Override + public MemoryAddress addOffset(long offset) { + return new MemoryAddressImpl(this.offset + offset); } - long offset() { - return segment != null ? - segment.min() + offset : offset; + @Override + public long toRawLongValue() { + return offset; } - // MemoryAddress methods + @Override + public final MemoryAddress address() { + return this; + } + + // Object methods + + @Override + public int hashCode() { + return (int) toRawLongValue(); + } + + @Override + public boolean equals(Object that) { + return (that instanceof MemoryAddressImpl addressImpl && + offset == addressImpl.offset); + } + + @Override + public String toString() { + return "MemoryAddress{ offset=0x" + Long.toHexString(offset) + " }"; + } + + public static MemorySegment ofLongUnchecked(long value) { + return ofLongUnchecked(value, Long.MAX_VALUE); + } + + public static MemorySegment ofLongUnchecked(long value, long byteSize, ResourceScopeImpl resourceScope) { + return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(MemoryAddress.ofLong(value), byteSize, resourceScope); + } + + public static MemorySegment ofLongUnchecked(long value, long byteSize) { + return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(MemoryAddress.ofLong(value), byteSize, ResourceScopeImpl.GLOBAL); + } @Override public ResourceScope scope() { - return segment != null ? - segment.scope() : ResourceScope.globalScope(); + return ResourceScopeImpl.GLOBAL; } @Override - public MemoryAddress addOffset(long offset) { - return new MemoryAddressImpl(segment, this.offset + offset); + @CallerSensitive + @ForceInline + public String getUtf8String(long offset) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + SharedUtils.checkAddress(this); + return NativeMemorySegmentImpl.EVERYTHING.getUtf8String(toRawLongValue() + offset); } @Override - public long segmentOffset(MemorySegment segment) { - Objects.requireNonNull(segment); - AbstractMemorySegmentImpl segmentImpl = (AbstractMemorySegmentImpl)segment; - if (segmentImpl.base() != base()) { - throw new IllegalArgumentException("Incompatible segment: " + segment); - } - return offset() - segmentImpl.min(); + @CallerSensitive + @ForceInline + public void setUtf8String(long offset, String str) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + SharedUtils.checkAddress(this); + NativeMemorySegmentImpl.EVERYTHING.setUtf8String(toRawLongValue() + offset, str); } @Override - public boolean isNative() { - return base() == null; + @ForceInline + @CallerSensitive + public byte get(ValueLayout.OfByte layout, long offset) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset); } @Override - public long toRawLongValue() { - if (segment != null) { - if (segment.base() != null) { - throw new UnsupportedOperationException("Not a native address"); - } - segment.checkValidState(); - } - return offset(); + @ForceInline + @CallerSensitive + public void set(ValueLayout.OfByte layout, long offset, byte value) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value); } - // Object methods + @Override + @ForceInline + @CallerSensitive + public boolean get(ValueLayout.OfBoolean layout, long offset) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset); + } @Override - public int hashCode() { - return Objects.hash(base(), offset()); + @ForceInline + @CallerSensitive + public void set(ValueLayout.OfBoolean layout, long offset, boolean value) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value); } @Override - public boolean equals(Object that) { - if (that instanceof MemoryAddressImpl) { - MemoryAddressImpl addr = (MemoryAddressImpl)that; - return Objects.equals(base(), addr.base()) && - offset() == addr.offset(); - } else { - return false; - } + @ForceInline + @CallerSensitive + public char get(ValueLayout.OfChar layout, long offset) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset); } @Override - public String toString() { - return "MemoryAddress{ base: " + base() + " offset=0x" + Long.toHexString(offset()) + " }"; + @ForceInline + @CallerSensitive + public void set(ValueLayout.OfChar layout, long offset, char value) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value); } @Override + @ForceInline @CallerSensitive - public final MemorySegment asSegment(long bytesSize, ResourceScope scope) { + public short get(ValueLayout.OfShort layout, long offset) { Reflection.ensureNativeAccess(Reflection.getCallerClass()); - return asSegment(bytesSize, null, scope); + return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset); } @Override + @ForceInline @CallerSensitive - public final MemorySegment asSegment(long bytesSize, Runnable cleanupAction, ResourceScope scope) { + public void set(ValueLayout.OfShort layout, long offset, short value) { Reflection.ensureNativeAccess(Reflection.getCallerClass()); - Objects.requireNonNull(scope); - if (bytesSize <= 0) { - throw new IllegalArgumentException("Invalid size : " + bytesSize); - } - return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(this, bytesSize, - cleanupAction, - (ResourceScopeImpl) scope); + NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value); } - public static MemorySegment ofLongUnchecked(long value) { - return ofLongUnchecked(value, Long.MAX_VALUE); + @Override + @ForceInline + @CallerSensitive + public int get(ValueLayout.OfInt layout, long offset) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset); } - public static MemorySegment ofLongUnchecked(long value, long byteSize, ResourceScopeImpl resourceScope) { - return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(MemoryAddress.ofLong(value), byteSize, null, resourceScope); + @Override + @ForceInline + @CallerSensitive + public void set(ValueLayout.OfInt layout, long offset, int value) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value); } - public static MemorySegment ofLongUnchecked(long value, long byteSize) { - return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(MemoryAddress.ofLong(value), byteSize, null, ResourceScopeImpl.GLOBAL); + @Override + @ForceInline + @CallerSensitive + public float get(ValueLayout.OfFloat layout, long offset) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset); + } + + @Override + @ForceInline + @CallerSensitive + public void set(ValueLayout.OfFloat layout, long offset, float value) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value); + } + + @Override + @ForceInline + @CallerSensitive + public long get(ValueLayout.OfLong layout, long offset) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset); + } + + @Override + @ForceInline + @CallerSensitive + public void set(ValueLayout.OfLong layout, long offset, long value) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value); + } + + @Override + @ForceInline + @CallerSensitive + public double get(ValueLayout.OfDouble layout, long offset) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset); + } + + @Override + @ForceInline + @CallerSensitive + public void set(ValueLayout.OfDouble layout, long offset, double value) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value); + } + + @Override + @ForceInline + @CallerSensitive + public MemoryAddress get(ValueLayout.OfAddress layout, long offset) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset); + } + + @Override + @ForceInline + @CallerSensitive + public void set(ValueLayout.OfAddress layout, long offset, Addressable value) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value.address()); + } + + @Override + @ForceInline + @CallerSensitive + public char getAtIndex(ValueLayout.OfChar layout, long index) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + (index * layout.byteSize())); + } + + @Override + @ForceInline + @CallerSensitive + public void setAtIndex(ValueLayout.OfChar layout, long index, char value) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value); + } + + @Override + @ForceInline + @CallerSensitive + public short getAtIndex(ValueLayout.OfShort layout, long index) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + (index * layout.byteSize())); + } + + @Override + @ForceInline + @CallerSensitive + public void setAtIndex(ValueLayout.OfShort layout, long index, short value) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value); + } + + @Override + @ForceInline + @CallerSensitive + public int getAtIndex(ValueLayout.OfInt layout, long index) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + (index * layout.byteSize())); + } + + @Override + @ForceInline + @CallerSensitive + public void setAtIndex(ValueLayout.OfInt layout, long index, int value) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value); + } + + @Override + @ForceInline + @CallerSensitive + public float getAtIndex(ValueLayout.OfFloat layout, long index) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + (index * layout.byteSize())); + } + + @Override + @ForceInline + @CallerSensitive + public void setAtIndex(ValueLayout.OfFloat layout, long index, float value) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value); + } + + @Override + @ForceInline + @CallerSensitive + public long getAtIndex(ValueLayout.OfLong layout, long index) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + (index * layout.byteSize())); + } + + @Override + @ForceInline + @CallerSensitive + public void setAtIndex(ValueLayout.OfLong layout, long index, long value) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value); + } + + @Override + @ForceInline + @CallerSensitive + public double getAtIndex(ValueLayout.OfDouble layout, long index) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + (index * layout.byteSize())); + } + + @Override + @ForceInline + @CallerSensitive + public void setAtIndex(ValueLayout.OfDouble layout, long index, double value) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value); + } + + @Override + @ForceInline + @CallerSensitive + public MemoryAddress getAtIndex(ValueLayout.OfAddress layout, long index) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + (index * layout.byteSize())); + } + + @Override + @ForceInline + @CallerSensitive + public void setAtIndex(ValueLayout.OfAddress layout, long index, Addressable value) { + Reflection.ensureNativeAccess(Reflection.getCallerClass()); + NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value.address()); } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java index 8d61c9b79b550..11f795a2f8434 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java @@ -28,8 +28,6 @@ import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemorySegment; -import jdk.incubator.foreign.ResourceScope; -import jdk.incubator.foreign.SegmentAllocator; import jdk.internal.misc.Unsafe; import jdk.internal.misc.VM; import jdk.internal.vm.annotation.ForceInline; @@ -43,12 +41,10 @@ */ public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl { - public static final MemorySegment EVERYTHING = makeNativeSegmentUnchecked(MemoryAddress.NULL, Long.MAX_VALUE, null, ResourceScopeImpl.GLOBAL); + public static final MemorySegment EVERYTHING = makeNativeSegmentUnchecked(MemoryAddress.NULL, Long.MAX_VALUE, ResourceScopeImpl.GLOBAL); private static final Unsafe unsafe = Unsafe.getUnsafe(); - public static final SegmentAllocator IMPLICIT_ALLOCATOR = (size, align) -> MemorySegment.allocateNative(size, align, ResourceScope.newImplicitScope()); - // The maximum alignment supported by malloc - typically 16 on // 64-bit platforms and 8 on 32-bit platforms. private static final long MAX_MALLOC_ALIGN = Unsafe.ADDRESS_SIZE == 4 ? 8 : 16; @@ -63,6 +59,13 @@ public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl { this.min = min; } + @ForceInline + @Override + public MemoryAddress address() { + checkValidState(); + return MemoryAddress.ofLong(unsafeGetOffset()); + } + @Override NativeMemorySegmentImpl dup(long offset, long size, int mask, ResourceScopeImpl scope) { return new NativeMemorySegmentImpl(min + offset, size, mask, scope); @@ -123,12 +126,9 @@ public void cleanup() { return segment; } - public static MemorySegment makeNativeSegmentUnchecked(MemoryAddress min, long bytesSize, Runnable cleanupAction, ResourceScopeImpl scope) { + public static MemorySegment makeNativeSegmentUnchecked(MemoryAddress min, long bytesSize, ResourceScopeImpl scope) { scope.checkValidStateSlow(); AbstractMemorySegmentImpl segment = new NativeMemorySegmentImpl(min.toRawLongValue(), bytesSize, defaultAccessModes(bytesSize), scope); - if (cleanupAction != null) { - scope.addCloseAction(cleanupAction); - } return segment; } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/UpcallHandler.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/NativeSymbolImpl.java similarity index 65% rename from src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/UpcallHandler.java rename to src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/NativeSymbolImpl.java index a2e9c3ea2b9c4..ede6a7ad5f8b3 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/UpcallHandler.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/NativeSymbolImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -18,13 +18,21 @@ * 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 + * 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; -package jdk.internal.foreign.abi; +import jdk.incubator.foreign.MemoryAddress; +import jdk.incubator.foreign.NativeSymbol; +import jdk.incubator.foreign.ResourceScope; -public interface UpcallHandler { - long entryPoint(); +public record NativeSymbolImpl(String name, MemoryAddress address, ResourceScope scope) implements NativeSymbol, Scoped { + @Override + public MemoryAddress address() { + ((ResourceScopeImpl)scope).checkValidState(); + return address; + } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/PlatformLayouts.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/PlatformLayouts.java index c5626f9289691..c18de05c1a5f3 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/PlatformLayouts.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/PlatformLayouts.java @@ -25,14 +25,9 @@ */ package jdk.internal.foreign; -import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.ValueLayout; -import java.nio.ByteOrder; - -import static java.nio.ByteOrder.LITTLE_ENDIAN; - public class PlatformLayouts { public static Z pick(Z sysv, Z win64, Z aarch64) { return switch (CABI.current()) { @@ -42,59 +37,6 @@ public static Z pick(Z sysv, Z win64, Z aarch64) { }; } - public static MemoryLayout asVarArg(MemoryLayout ml) { - return switch (CABI.current()) { - case Win64 -> Win64.asVarArg(ml); - case MacOsAArch64 -> AArch64.asVarArg(ml); - default -> ml; - }; - } - - private static ValueLayout ofChar(ByteOrder order, long bitSize) { - return MemoryLayout.valueLayout(bitSize, order) - .withAttribute(CLinker.TypeKind.ATTR_NAME, CLinker.TypeKind.CHAR); - } - - private static ValueLayout ofShort(ByteOrder order, long bitSize) { - return MemoryLayout.valueLayout(bitSize, order) - .withAttribute(CLinker.TypeKind.ATTR_NAME, CLinker.TypeKind.SHORT); - } - - private static ValueLayout ofInt(ByteOrder order, long bitSize) { - return MemoryLayout.valueLayout(bitSize, order) - .withAttribute(CLinker.TypeKind.ATTR_NAME, CLinker.TypeKind.INT); - } - - private static ValueLayout ofLong(ByteOrder order, long bitSize) { - return MemoryLayout.valueLayout(bitSize, order) - .withAttribute(CLinker.TypeKind.ATTR_NAME, CLinker.TypeKind.LONG); - } - - private static ValueLayout ofLongLong(ByteOrder order, long bitSize) { - return MemoryLayout.valueLayout(bitSize, order) - .withAttribute(CLinker.TypeKind.ATTR_NAME, CLinker.TypeKind.LONG_LONG); - } - - private static ValueLayout ofFloat(ByteOrder order, long bitSize) { - return MemoryLayout.valueLayout(bitSize, order) - .withAttribute(CLinker.TypeKind.ATTR_NAME, CLinker.TypeKind.FLOAT); - } - - private static ValueLayout ofDouble(ByteOrder order, long bitSize) { - return MemoryLayout.valueLayout(bitSize, order) - .withAttribute(CLinker.TypeKind.ATTR_NAME, CLinker.TypeKind.DOUBLE); - } - - private static ValueLayout ofPointer(ByteOrder order, long bitSize) { - return MemoryLayout.valueLayout(bitSize, order) - .withAttribute(CLinker.TypeKind.ATTR_NAME, CLinker.TypeKind.POINTER); - } - - public static CLinker.TypeKind getKind(MemoryLayout layout) { - return (CLinker.TypeKind)layout.attribute(CLinker.TypeKind.ATTR_NAME).orElseThrow( - () -> new IllegalStateException("Unexpected value layout: could not determine ABI class")); - } - /** * This class defines layout constants modelling standard primitive types supported by the x64 SystemV ABI. */ @@ -103,50 +45,55 @@ private SysV() { //just the one } + /** + * The {@code bool} native type. + */ + public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN; + /** * The {@code char} native type. */ - public static final ValueLayout C_CHAR = ofChar(LITTLE_ENDIAN, 8); + public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE; /** * The {@code short} native type. */ - public static final ValueLayout C_SHORT = ofShort(LITTLE_ENDIAN, 16); + public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT.withBitAlignment(16); /** * The {@code int} native type. */ - public static final ValueLayout C_INT = ofInt(LITTLE_ENDIAN, 32); + public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT.withBitAlignment(32); /** * The {@code long} native type. */ - public static final ValueLayout C_LONG = ofLong(LITTLE_ENDIAN, 64); + public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64); /** * The {@code long long} native type. */ - public static final ValueLayout C_LONG_LONG = ofLongLong(LITTLE_ENDIAN, 64); + public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64); /** * The {@code float} native type. */ - public static final ValueLayout C_FLOAT = ofFloat(LITTLE_ENDIAN, 32); + public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT.withBitAlignment(32); /** * The {@code double} native type. */ - public static final ValueLayout C_DOUBLE = ofDouble(LITTLE_ENDIAN, 64); + public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE.withBitAlignment(64); /** * The {@code T*} native type. */ - public static final ValueLayout C_POINTER = ofPointer(LITTLE_ENDIAN, 64); + public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64); /** * The {@code va_list} native type, as it is passed to a function. */ - public static final MemoryLayout C_VA_LIST = SysV.C_POINTER; + public static final ValueLayout.OfAddress C_VA_LIST = SysV.C_POINTER; } /** @@ -159,64 +106,53 @@ private Win64() { } /** - * The name of the layout attribute (see {@link MemoryLayout#attributes()}) used to mark variadic parameters. The - * attribute value must be a boolean. + * The {@code bool} native type. */ - public static final String VARARGS_ATTRIBUTE_NAME = "abi/windows/varargs"; + public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN; /** * The {@code char} native type. */ - public static final ValueLayout C_CHAR = ofChar(LITTLE_ENDIAN, 8); + public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE; /** * The {@code short} native type. */ - public static final ValueLayout C_SHORT = ofShort(LITTLE_ENDIAN, 16); + public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT.withBitAlignment(16); /** * The {@code int} native type. */ - public static final ValueLayout C_INT = ofInt(LITTLE_ENDIAN, 32); + public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT.withBitAlignment(32); /** * The {@code long} native type. */ - public static final ValueLayout C_LONG = ofLong(LITTLE_ENDIAN, 32); + public static final ValueLayout.OfInt C_LONG = ValueLayout.JAVA_INT.withBitAlignment(32); /** * The {@code long long} native type. */ - public static final ValueLayout C_LONG_LONG = ofLongLong(LITTLE_ENDIAN, 64); + public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64); /** * The {@code float} native type. */ - public static final ValueLayout C_FLOAT = ofFloat(LITTLE_ENDIAN, 32); + public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT.withBitAlignment(32); /** * The {@code double} native type. */ - public static final ValueLayout C_DOUBLE = ofDouble(LITTLE_ENDIAN, 64); + public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE.withBitAlignment(64); /** * The {@code T*} native type. */ - public static final ValueLayout C_POINTER = ofPointer(LITTLE_ENDIAN, 64); + public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64); /** * The {@code va_list} native type, as it is passed to a function. */ - public static final MemoryLayout C_VA_LIST = Win64.C_POINTER; - - /** - * Return a new memory layout which describes a variadic parameter to be passed to a function. - * @param layout the original parameter layout. - * @return a layout which is the same as {@code layout}, except for the extra attribute {@link #VARARGS_ATTRIBUTE_NAME}, - * which is set to {@code true}. - */ - public static MemoryLayout asVarArg(MemoryLayout layout) { - return layout.withAttribute(VARARGS_ATTRIBUTE_NAME, true); - } + public static final ValueLayout.OfAddress C_VA_LIST = Win64.C_POINTER; } /** @@ -228,69 +164,54 @@ private AArch64() { //just the one } + /** + * The {@code bool} native type. + */ + public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN; + /** * The {@code char} native type. */ - public static final ValueLayout C_CHAR = ofChar(LITTLE_ENDIAN, 8); + public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE; /** * The {@code short} native type. */ - public static final ValueLayout C_SHORT = ofShort(LITTLE_ENDIAN, 16); + public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT.withBitAlignment(16); /** * The {@code int} native type. */ - public static final ValueLayout C_INT = ofInt(LITTLE_ENDIAN, 32); + public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT.withBitAlignment(32); /** * The {@code long} native type. */ - public static final ValueLayout C_LONG = ofLong(LITTLE_ENDIAN, 64); + public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64); /** * The {@code long long} native type. */ - public static final ValueLayout C_LONG_LONG = ofLongLong(LITTLE_ENDIAN, 64); + public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64); /** * The {@code float} native type. */ - public static final ValueLayout C_FLOAT = ofFloat(LITTLE_ENDIAN, 32); + public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT.withBitAlignment(32); /** * The {@code double} native type. */ - public static final ValueLayout C_DOUBLE = ofDouble(LITTLE_ENDIAN, 64); + public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE.withBitAlignment(64); /** * The {@code T*} native type. */ - public static final ValueLayout C_POINTER = ofPointer(LITTLE_ENDIAN, 64); + public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64); /** * The {@code va_list} native type, as it is passed to a function. */ - public static final MemoryLayout C_VA_LIST = AArch64.C_POINTER; - - /** - * The name of the layout attribute (see {@link MemoryLayout#attributes()}) - * used to mark variadic parameters on systems such as macOS which pass these - * entirely on the stack. The attribute value must be a boolean. - */ - public final static String STACK_VARARGS_ATTRIBUTE_NAME = "abi/aarch64/stack_varargs"; - - /** - * Return a new memory layout which describes a variadic parameter to be - * passed to a function. This is only required on platforms such as macOS - * which pass variadic parameters entirely on the stack. - * @param layout the original parameter layout. - * @return a layout which is the same as {@code layout}, except for - * the extra attribute {@link #STACK_VARARGS_ATTRIBUTE_NAME}, which is set - * to {@code true}. - */ - public static MemoryLayout asVarArg(MemoryLayout layout) { - return layout.withAttribute(STACK_VARARGS_ATTRIBUTE_NAME, true); - } + public static final ValueLayout.OfAddress C_VA_LIST = AArch64.C_POINTER; } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/ResourceScopeImpl.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/ResourceScopeImpl.java index ffd225a6b2626..dbb830a1b531d 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/ResourceScopeImpl.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/ResourceScopeImpl.java @@ -30,7 +30,7 @@ import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.SegmentAllocator; import jdk.internal.misc.ScopedMemoryAccess; -import jdk.internal.ref.CleanerFactory; +import jdk.internal.vm.annotation.ForceInline; import java.lang.ref.Cleaner; import java.lang.ref.Reference; @@ -49,9 +49,12 @@ * shared scopes use a more sophisticated synchronization mechanism, which guarantees that no concurrent * access is possible when a scope is being closed (see {@link jdk.internal.misc.ScopedMemoryAccess}). */ -public abstract non-sealed class ResourceScopeImpl implements ResourceScope, ScopedMemoryAccess.Scope, SegmentAllocator { +public abstract non-sealed class ResourceScopeImpl implements ResourceScope, SegmentAllocator, ScopedMemoryAccess.Scope { final ResourceList resourceList; + final Cleaner.Cleanable cleanable; + + static final int MAX_FORKS = Integer.MAX_VALUE; @Override public void addCloseAction(Runnable runnable) { @@ -59,11 +62,6 @@ public void addCloseAction(Runnable runnable) { addInternal(ResourceList.ResourceCleanup.ofRunnable(runnable)); } - @Override - public boolean isImplicit() { - return false; - } - /** * Add a cleanup action. If a failure occurred (because of a add vs. close race), call the cleanup action. * This semantics is useful when allocating new memory segments, since we first do a malloc/mmap and _then_ @@ -91,72 +89,38 @@ void addInternal(ResourceList.ResourceCleanup resource) { } } - protected ResourceScopeImpl(Cleaner cleaner, ResourceList resourceList) { + protected ResourceScopeImpl(ResourceList resourceList, Cleaner cleaner) { this.resourceList = resourceList; - if (cleaner != null) { - cleaner.register(this, resourceList); - } - } - - public static ResourceScopeImpl createImplicitScope() { - return new ImplicitScopeImpl(CleanerFactory.cleaner()); + cleanable = (cleaner != null) ? + cleaner.register(this, resourceList) : null; } public static ResourceScopeImpl createConfined(Thread thread, Cleaner cleaner) { return new ConfinedScope(thread, cleaner); } - /** - * Creates a confined memory scope with given attachment and cleanup action. The returned scope - * is assumed to be confined on the current thread. - * @return a confined memory scope - */ - public static ResourceScopeImpl createConfined(Cleaner cleaner) { - return new ConfinedScope(Thread.currentThread(), cleaner); - } - - /** - * Creates a shared memory scope with given attachment and cleanup action. - * @return a shared memory scope - */ public static ResourceScopeImpl createShared(Cleaner cleaner) { return new SharedScope(cleaner); } - private final void release0(HandleImpl handle) { - try { - Objects.requireNonNull(handle); - if (handle.scope() != this) { - throw new IllegalArgumentException("Cannot release an handle acquired from another scope"); - } - handle.release(); - } finally { - Reference.reachabilityFence(this); - } - } - @Override - public final void release(ResourceScope.Handle handle) { - release0((HandleImpl)handle); + public MemorySegment allocate(long bytesSize, long bytesAlignment) { + return MemorySegment.allocateNative(bytesSize, bytesAlignment, this); } - @Override - public final void release(ScopedMemoryAccess.Scope.Handle handle) { - release0((HandleImpl)handle); - } + public abstract void release0(); - @Override - public abstract HandleImpl acquire(); + public abstract void acquire0(); - /** - * Internal interface used to implement resource scope handles. - */ - public non-sealed interface HandleImpl extends ResourceScope.Handle, ScopedMemoryAccess.Scope.Handle { - - @Override - ResourceScopeImpl scope(); - - void release(); + @Override + public void keepAlive(ResourceScope target) { + Objects.requireNonNull(target); + if (target == this) { + throw new IllegalArgumentException("Invalid target scope."); + } + ResourceScopeImpl targetImpl = (ResourceScopeImpl)target; + targetImpl.acquire0(); + addCloseAction(targetImpl::release0); } /** @@ -167,7 +131,11 @@ public non-sealed interface HandleImpl extends ResourceScope.Handle, ScopedMemor public void close() { try { justClose(); - resourceList.cleanup(); + if (cleanable != null) { + cleanable.clean(); + } else { + resourceList.cleanup(); + } } finally { Reference.reachabilityFence(this); } @@ -215,32 +183,17 @@ protected Object clone() throws CloneNotSupportedException { } /** - * Allocates a segment using this scope. Used by {@link SegmentAllocator#ofScope(ResourceScope)}. - */ - @Override - public MemorySegment allocate(long bytesSize, long bytesAlignment) { - return MemorySegment.allocateNative(bytesSize, bytesAlignment, this); - } - - /** - * A non-closeable, shared scope. Similar to a shared scope, but its {@link #close()} method throws unconditionally. - * In addition, non-closeable scopes feature a much simpler scheme for generating resource scope handles, where - * the scope itself also acts as a resource scope handle and is returned by {@link #acquire()}. + * The global, always alive, non-closeable, shared scope. Similar to a shared scope, but its {@link #close()} method throws unconditionally. + * Adding new resources to the global scope, does nothing: as the scope can never become not-alive, there is nothing to track. + * Acquiring and or releasing a resource scope similarly does nothing. */ - static class ImplicitScopeImpl extends SharedScope implements HandleImpl { + static class GlobalScopeImpl extends SharedScope { - public ImplicitScopeImpl(Cleaner cleaner) { - super(cleaner); - } + final Object ref; - @Override - public HandleImpl acquire() { - return this; - } - - @Override - public boolean isImplicit() { - return true; + public GlobalScopeImpl(Object ref) { + super(null); + this.ref = ref; } @Override @@ -249,27 +202,28 @@ public void close() { } @Override - public void release() { + @ForceInline + public void release0() { // do nothing } @Override - public ResourceScopeImpl scope() { - return this; + @ForceInline + public void acquire0() { + // do nothing } - } - /** - * The global, always alive, non-closeable, shared scope. This is like a {@link ImplicitScopeImpl non-closeable scope}, - * except that the operation which adds new resources to the global scope does nothing: as the scope can never - * become not-alive, there is nothing to track. - */ - public static final ResourceScopeImpl GLOBAL = new ImplicitScopeImpl( null) { @Override void addInternal(ResourceList.ResourceCleanup resource) { // do nothing } - }; + } + + public static final ResourceScopeImpl GLOBAL = new GlobalScopeImpl(null); + + public static ResourceScopeImpl heapScope(Object ref) { + return new GlobalScopeImpl(ref); + } /** * A list of all cleanup actions associated with a resource scope. Cleanup actions are modelled as instances @@ -296,7 +250,7 @@ static void cleanup(ResourceCleanup first) { } } - public static abstract class ResourceCleanup { + public abstract static class ResourceCleanup { ResourceCleanup next; public abstract void cleanup(); diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/Scoped.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/Scoped.java new file mode 100644 index 0000000000000..7051aacb527b6 --- /dev/null +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/Scoped.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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.incubator.foreign.ResourceScope; + +public interface Scoped { + ResourceScope scope(); +} diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/SharedScope.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/SharedScope.java index 8bbeaaab82949..93ecf08d46850 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/SharedScope.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/SharedScope.java @@ -25,14 +25,12 @@ package jdk.internal.foreign; -import jdk.incubator.foreign.ResourceScope; import jdk.internal.misc.ScopedMemoryAccess; +import jdk.internal.vm.annotation.ForceInline; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.lang.ref.Cleaner; -import java.lang.ref.Reference; -import java.util.concurrent.atomic.AtomicBoolean; /** * A shared scope, which can be shared across multiple threads. Closing a shared scope has to ensure that @@ -50,7 +48,6 @@ class SharedScope extends ResourceScopeImpl { private static final int ALIVE = 0; private static final int CLOSING = -1; private static final int CLOSED = -2; - private static final int MAX_FORKS = Integer.MAX_VALUE; private int state = ALIVE; @@ -65,7 +62,7 @@ class SharedScope extends ResourceScopeImpl { } SharedScope(Cleaner cleaner) { - super(cleaner, new SharedResourceList()); + super(new SharedResourceList(), cleaner); } @Override @@ -81,7 +78,8 @@ public void checkValidState() { } @Override - public HandleImpl acquire() { + @ForceInline + public void acquire0() { int value; do { value = (int) STATE.getVolatile(this); @@ -90,10 +88,22 @@ public HandleImpl acquire() { throw new IllegalStateException("Already closed"); } else if (value == MAX_FORKS) { //overflow - throw new IllegalStateException("Segment acquire limit exceeded"); + throw new IllegalStateException("Scope keep alive limit exceeded"); } } while (!STATE.compareAndSet(this, value, value + 1)); - return new SharedHandle(); + } + + @Override + @ForceInline + public void release0() { + int value; + do { + value = (int) STATE.getVolatile(this); + if (value <= ALIVE) { + //cannot get here - we can't close segment twice + throw new IllegalStateException("Already closed"); + } + } while (!STATE.compareAndSet(this, value, value - 1)); } void justClose() { @@ -101,7 +111,7 @@ void justClose() { if (prevState < 0) { throw new IllegalStateException("Already closed"); } else if (prevState != ALIVE) { - throw new IllegalStateException("Scope is acquired by " + prevState + " locks"); + throw new IllegalStateException("Scope is kept alive by " + prevState + " scopes"); } boolean success = SCOPED_MEMORY_ACCESS.closeScope(this); STATE.setVolatile(this, success ? CLOSED : ALIVE); @@ -133,13 +143,13 @@ static class SharedResourceList extends ResourceList { @Override void add(ResourceCleanup cleanup) { while (true) { - ResourceCleanup prev = (ResourceCleanup) FST.getAcquire(this); - cleanup.next = prev; - ResourceCleanup newSegment = (ResourceCleanup) FST.compareAndExchangeRelease(this, prev, cleanup); - if (newSegment == ResourceCleanup.CLOSED_LIST) { + ResourceCleanup prev = (ResourceCleanup) FST.getVolatile(this); + if (prev == ResourceCleanup.CLOSED_LIST) { // too late throw new IllegalStateException("Already closed"); - } else if (newSegment == prev) { + } + cleanup.next = prev; + if (FST.compareAndSet(this, prev, cleanup)) { return; //victory } // keep trying @@ -155,9 +165,9 @@ void cleanup() { //ok now we're really closing down ResourceCleanup prev = null; while (true) { - prev = (ResourceCleanup) FST.getAcquire(this); + prev = (ResourceCleanup) FST.getVolatile(this); // no need to check for DUMMY, since only one thread can get here! - if (FST.weakCompareAndSetRelease(this, prev, ResourceCleanup.CLOSED_LIST)) { + if (FST.compareAndSet(this, prev, ResourceCleanup.CLOSED_LIST)) { break; } } @@ -167,30 +177,4 @@ void cleanup() { } } } - - /** - * A shared resource scope handle; this implementation has to handle close vs. close races. - */ - class SharedHandle implements HandleImpl { - final AtomicBoolean released = new AtomicBoolean(false); - - @Override - public ResourceScopeImpl scope() { - return SharedScope.this; - } - - @Override - public void release() { - if (released.compareAndSet(false, true)) { - int value; - do { - value = (int) STATE.getVolatile(jdk.internal.foreign.SharedScope.this); - if (value <= ALIVE) { - //cannot get here - we can't close segment twice - throw new IllegalStateException("Already closed"); - } - } while (!STATE.compareAndSet(jdk.internal.foreign.SharedScope.this, value, value - 1)); - } - } - } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/SystemLookup.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/SystemLookup.java index 24743c7deebba..45c637b06bfb2 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/SystemLookup.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/SystemLookup.java @@ -25,8 +25,8 @@ package jdk.internal.foreign; -import jdk.incubator.foreign.MemoryAccess; import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.NativeSymbol; import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.SymbolLookup; import jdk.incubator.foreign.MemoryAddress; @@ -39,13 +39,13 @@ import java.util.Optional; import java.util.function.Function; -import static jdk.incubator.foreign.CLinker.C_POINTER; +import static jdk.incubator.foreign.ValueLayout.ADDRESS; public class SystemLookup implements SymbolLookup { private SystemLookup() { } - final static SystemLookup INSTANCE = new SystemLookup(); + static final SystemLookup INSTANCE = new SystemLookup(); /* * On POSIX systems, dlsym will allow us to lookup symbol in library dependencies; the same trick doesn't work @@ -71,11 +71,11 @@ private static SymbolLookup makeWindowsLookup() { SymbolLookup fallbackLibLookup = libLookup(libs -> libs.loadLibrary("WinFallbackLookup")); int numSymbols = WindowsFallbackSymbols.values().length; - MemorySegment funcs = fallbackLibLookup.lookup("funcs").orElseThrow() - .asSegment(C_POINTER.byteSize() * numSymbols, ResourceScope.newImplicitScope()); + MemorySegment funcs = MemorySegment.ofAddress(fallbackLibLookup.lookup("funcs").orElseThrow().address(), + ADDRESS.byteSize() * numSymbols, ResourceScope.globalScope()); SymbolLookup fallbackLookup = name -> Optional.ofNullable(WindowsFallbackSymbols.valueOfOrNull(name)) - .map(symbol -> MemoryAccess.getAddressAtIndex(funcs, symbol.ordinal())); + .map(symbol -> NativeSymbol.ofAddress(symbol.name(), funcs.getAtIndex(ADDRESS, symbol.ordinal()), ResourceScope.globalScope())); final SymbolLookup finalLookup = lookup; lookup = name -> finalLookup.lookup(name).or(() -> fallbackLookup.lookup(name)); @@ -91,7 +91,8 @@ private static SymbolLookup libLookup(Function l try { long addr = lib.lookup(name); return addr == 0 ? - Optional.empty() : Optional.of(MemoryAddress.ofLong(addr)); + Optional.empty() : + Optional.of(NativeSymbol.ofAddress(name, MemoryAddress.ofLong(addr), ResourceScope.globalScope())); } catch (NoSuchMethodException e) { return Optional.empty(); } @@ -99,7 +100,7 @@ private static SymbolLookup libLookup(Function l } @Override - public Optional lookup(String name) { + public Optional lookup(String name) { return syslookup.lookup(name); } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/Utils.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/Utils.java index f3b0240d2d0ca..6272e6e31005d 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/Utils.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/Utils.java @@ -27,17 +27,19 @@ package jdk.internal.foreign; import jdk.incubator.foreign.*; +import jdk.internal.access.SharedSecrets; import jdk.internal.access.foreign.MemorySegmentProxy; -import jdk.internal.misc.VM; -import sun.invoke.util.Wrapper; +import jdk.internal.vm.annotation.ForceInline; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.invoke.VarHandle; -import java.util.Optional; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; +import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE; import static sun.security.action.GetPropertyAction.*; /** @@ -49,6 +51,10 @@ public final class Utils { = Boolean.parseBoolean(privilegedGetProperty("jdk.internal.foreign.SHOULD_ADAPT_HANDLES", "true")); private static final MethodHandle SEGMENT_FILTER; + private static final MethodHandle BYTE_TO_BOOL; + private static final MethodHandle BOOL_TO_BYTE; + private static final MethodHandle ADDRESS_TO_LONG; + private static final MethodHandle LONG_TO_ADDRESS; public static final MethodHandle MH_bitsToBytesOrThrowForOffset; public static final Supplier bitsToBytesThrowOffset @@ -59,6 +65,14 @@ public final class Utils { MethodHandles.Lookup lookup = MethodHandles.lookup(); SEGMENT_FILTER = lookup.findStatic(Utils.class, "filterSegment", MethodType.methodType(MemorySegmentProxy.class, MemorySegment.class)); + BYTE_TO_BOOL = lookup.findStatic(Utils.class, "byteToBoolean", + MethodType.methodType(boolean.class, byte.class)); + BOOL_TO_BYTE = lookup.findStatic(Utils.class, "booleanToByte", + MethodType.methodType(byte.class, boolean.class)); + ADDRESS_TO_LONG = lookup.findVirtual(MemoryAddress.class, "toRawLongValue", + MethodType.methodType(long.class)); + LONG_TO_ADDRESS = lookup.findStatic(MemoryAddress.class, "ofLong", + MethodType.methodType(MemoryAddress.class, long.class)); MH_bitsToBytesOrThrowForOffset = MethodHandles.insertArguments( lookup.findStatic(Utils.class, "bitsToBytesOrThrow", MethodType.methodType(long.class, long.class, Supplier.class)), @@ -91,38 +105,72 @@ public static long bitsToBytesOrThrow(long bits, Supplier exFa } } - public static VarHandle fixUpVarHandle(VarHandle handle) { + public static VarHandle makeMemoryAccessVarHandle(ValueLayout layout, boolean skipAlignmentCheck) { + class VarHandleCache { + private static final Map handleMap = new ConcurrentHashMap<>(); + private static final Map handleMapNoAlignCheck = new ConcurrentHashMap<>(); + + static VarHandle put(ValueLayout layout, VarHandle handle, boolean skipAlignmentCheck) { + VarHandle prev = (skipAlignmentCheck ? handleMapNoAlignCheck : handleMap).putIfAbsent(layout, handle); + return prev != null ? prev : handle; + } + } + Class baseCarrier = layout.carrier(); + if (layout.carrier() == MemoryAddress.class) { + baseCarrier = switch ((int) ValueLayout.ADDRESS.byteSize()) { + case 8 -> long.class; + case 4 -> int.class; + default -> throw new UnsupportedOperationException("Unsupported address layout"); + }; + } else if (layout.carrier() == boolean.class) { + baseCarrier = byte.class; + } + + VarHandle handle = SharedSecrets.getJavaLangInvokeAccess().memoryAccessVarHandle(baseCarrier, skipAlignmentCheck, + layout.byteAlignment() - 1, layout.order()); + // This adaptation is required, otherwise the memory access var handle will have type MemorySegmentProxy, // and not MemorySegment (which the user expects), which causes performance issues with asType() adaptations. - return SHOULD_ADAPT_HANDLES + handle = SHOULD_ADAPT_HANDLES ? MemoryHandles.filterCoordinates(handle, 0, SEGMENT_FILTER) : handle; + if (layout.carrier() == boolean.class) { + handle = MemoryHandles.filterValue(handle, BOOL_TO_BYTE, BYTE_TO_BOOL); + } else if (layout.carrier() == MemoryAddress.class) { + handle = MemoryHandles.filterValue(handle, + MethodHandles.explicitCastArguments(ADDRESS_TO_LONG, MethodType.methodType(baseCarrier, MemoryAddress.class)), + MethodHandles.explicitCastArguments(LONG_TO_ADDRESS, MethodType.methodType(MemoryAddress.class, baseCarrier))); + } + return VarHandleCache.put(layout, handle, skipAlignmentCheck); } private static MemorySegmentProxy filterSegment(MemorySegment segment) { return (AbstractMemorySegmentImpl)segment; } - public static void checkPrimitiveCarrierCompat(Class carrier, MemoryLayout layout) { - checkLayoutType(layout, ValueLayout.class); - if (!isValidPrimitiveCarrier(carrier)) - throw new IllegalArgumentException("Unsupported carrier: " + carrier); - if (Wrapper.forPrimitiveType(carrier).bitWidth() != layout.bitSize()) - throw new IllegalArgumentException("Carrier size mismatch: " + carrier + " != " + layout); + private static boolean byteToBoolean(byte b) { + return b != 0; + } + + private static byte booleanToByte(boolean b) { + return b ? (byte)1 : (byte)0; + } + + 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 boolean isValidPrimitiveCarrier(Class carrier) { - return carrier == byte.class - || carrier == short.class - || carrier == char.class - || carrier == int.class - || carrier == long.class - || carrier == float.class - || carrier == double.class; + public static MemorySegment toCString(byte[] bytes, SegmentAllocator allocator) { + MemorySegment addr = allocator.allocate(bytes.length + 1, 1L); + copy(addr, bytes); + return addr; } - public static void checkLayoutType(MemoryLayout layout, Class layoutType) { - if (!layoutType.isInstance(layout)) - throw new IllegalArgumentException("Expected a " + layoutType.getSimpleName() + ": " + layout); + @ForceInline + public static long scaleOffset(MemorySegment segment, long index, long size) { + // note: we know size is a small value (as it comes from ValueLayout::byteSize()) + return MemorySegmentProxy.multiplyOffsets(index, (int)size, (AbstractMemorySegmentImpl)segment); } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/Binding.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/Binding.java index 5b8468eafd30f..3062322316985 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/Binding.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/Binding.java @@ -24,17 +24,20 @@ */ package jdk.internal.foreign.abi; +import jdk.incubator.foreign.Addressable; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryHandles; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.SegmentAllocator; +import jdk.incubator.foreign.ValueLayout; import jdk.internal.foreign.MemoryAddressImpl; import jdk.internal.foreign.ResourceScopeImpl; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.util.ArrayList; import java.util.Deque; import java.util.List; @@ -204,7 +207,6 @@ public abstract class Binding { private static final MethodHandle MH_UNBOX_ADDRESS; private static final MethodHandle MH_BOX_ADDRESS; - private static final MethodHandle MH_BASE_ADDRESS; private static final MethodHandle MH_COPY_BUFFER; private static final MethodHandle MH_ALLOCATE_BUFFER; private static final MethodHandle MH_TO_SEGMENT; @@ -216,8 +218,6 @@ public abstract class Binding { methodType(long.class)); MH_BOX_ADDRESS = lookup.findStatic(MemoryAddress.class, "ofLong", methodType(MemoryAddress.class, long.class)); - MH_BASE_ADDRESS = lookup.findVirtual(MemorySegment.class, "address", - methodType(MemoryAddress.class)); MH_COPY_BUFFER = lookup.findStatic(Binding.Copy.class, "copyBuffer", methodType(MemorySegment.class, MemorySegment.class, long.class, long.class, Context.class)); MH_ALLOCATE_BUFFER = lookup.findStatic(Binding.Allocate.class, "allocateBuffer", @@ -262,7 +262,7 @@ public void close() { */ public static Context ofBoundedAllocator(long size) { ResourceScope scope = ResourceScope.newConfinedScope(); - return new Context(SegmentAllocator.arenaAllocator(size, scope), scope); + return new Context(SegmentAllocator.newNativeArena(size, scope), scope); } /** @@ -321,7 +321,6 @@ enum Tag { ALLOC_BUFFER, BOX_ADDRESS, UNBOX_ADDRESS, - BASE_ADDRESS, TO_SEGMENT, DUP } @@ -344,7 +343,7 @@ public abstract void interpret(Deque stack, BindingInterpreter.StoreFunc public abstract MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos); private static void checkType(Class type) { - if (!type.isPrimitive() || type == void.class || type == boolean.class) + if (!type.isPrimitive() || type == void.class) throw new IllegalArgumentException("Illegal type: " + type); } @@ -388,11 +387,11 @@ public static BoxAddress boxAddress() { } public static UnboxAddress unboxAddress() { - return UnboxAddress.INSTANCE; + return UnboxAddress.INSTANCE.get(MemoryAddress.class); } - public static BaseAddress baseAddress() { - return BaseAddress.INSTANCE; + public static UnboxAddress unboxAddress(Class carrier) { + return UnboxAddress.INSTANCE.get(carrier); } public static ToSegment toSegment(MemoryLayout layout) { @@ -467,8 +466,8 @@ public Binding.Builder unboxAddress() { return this; } - public Binding.Builder baseAddress() { - bindings.add(Binding.baseAddress()); + public Binding.Builder unboxAddress(Class carrier) { + bindings.add(Binding.unboxAddress(carrier)); return this; } @@ -487,7 +486,7 @@ public List build() { } } - static abstract class Move extends Binding { + abstract static class Move extends Binding { private final VMStorage storage; private final Class type; @@ -593,7 +592,7 @@ public String toString() { } } - private static abstract class Dereference extends Binding { + private abstract static class Dereference extends Binding { private final long offset; private final Class type; @@ -630,7 +629,8 @@ public VarHandle varHandle() { // alignment is set to 1 byte here to avoid exceptions for cases where we do super word // copies of e.g. 2 int fields of a struct as a single long, while the struct is only // 4-byte-aligned (since it only contains ints) - return MemoryHandles.insertCoordinates(MemoryHandles.varHandle(type, 1, ByteOrder.nativeOrder()), 1, offset); + ValueLayout layout = MemoryLayout.valueLayout(type(), ByteOrder.nativeOrder()).withBitAlignment(8); + return MemoryHandles.insertCoordinates(MemoryHandles.varHandle(layout), 1, offset); } } @@ -740,9 +740,8 @@ private Copy(long size, long alignment) { private static MemorySegment copyBuffer(MemorySegment operand, long size, long alignment, Context context) { - MemorySegment copy = context.allocator().allocate(size, alignment); - copy.copyFrom(operand.asSlice(0, size)); - return copy; + return context.allocator().allocate(size, alignment) + .copyFrom(operand.asSlice(0, size)); } public long size() { @@ -875,27 +874,44 @@ public int hashCode() { * and pushes that onto the operand stack. */ public static class UnboxAddress extends Binding { - private static final UnboxAddress INSTANCE = new UnboxAddress(); - private UnboxAddress() { + + static final ClassValue INSTANCE = new ClassValue<>() { + @Override + protected UnboxAddress computeValue(Class type) { + return new UnboxAddress(type); + } + }; + + final Class carrier; + final MethodHandle toAddress; + + private UnboxAddress(Class carrier) { super(Tag.UNBOX_ADDRESS); + this.carrier = carrier; + try { + this.toAddress = MethodHandles.lookup().findVirtual(carrier, "address", MethodType.methodType(MemoryAddress.class)); + } catch (Throwable ex) { + throw new IllegalArgumentException(ex); + } } @Override public void verify(Deque> stack) { Class actualType = stack.pop(); - SharedUtils.checkType(actualType, MemoryAddress.class); + SharedUtils.checkType(actualType, carrier); stack.push(long.class); } @Override public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFunc, BindingInterpreter.LoadFunc loadFunc, Context context) { - stack.push(((MemoryAddress)stack.pop()).toRawLongValue()); + stack.push(((Addressable)stack.pop()).address().toRawLongValue()); } @Override public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) { - return filterArguments(specializedHandle, insertPos, MH_UNBOX_ADDRESS); + return filterArguments(specializedHandle, insertPos, + MethodHandles.filterReturnValue(toAddress, MH_UNBOX_ADDRESS)); } @Override @@ -939,41 +955,6 @@ public String toString() { } } - /** - * BASE_ADDRESS() - * Pops a MemorySegment from the operand stack, and takes the base address of the segment - * (the MemoryAddress that points to the start), and pushes that onto the operand stack - */ - public static class BaseAddress extends Binding { - private static final BaseAddress INSTANCE = new BaseAddress(); - private BaseAddress() { - super(Tag.BASE_ADDRESS); - } - - @Override - public void verify(Deque> stack) { - Class actualType = stack.pop(); - SharedUtils.checkType(actualType, MemorySegment.class); - stack.push(MemoryAddress.class); - } - - @Override - public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFunc, - BindingInterpreter.LoadFunc loadFunc, Context context) { - stack.push(((MemorySegment) stack.pop()).address()); - } - - @Override - public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) { - return filterArguments(specializedHandle, insertPos, MH_BASE_ADDRESS); - } - - @Override - public String toString() { - return "BaseAddress{}"; - } - } - /** * TO_SEGMENT([size]) * Pops a MemoryAddress from the operand stack, and converts it to a MemorySegment diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/BufferLayout.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/BufferLayout.java index d290a36cc737c..5308533676f29 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/BufferLayout.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/BufferLayout.java @@ -24,8 +24,8 @@ */ package jdk.internal.foreign.abi; -import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.ValueLayout; import jdk.internal.foreign.MemoryAddressImpl; import java.io.PrintStream; @@ -34,7 +34,7 @@ import java.util.Map; class BufferLayout { - static final VarHandle VH_LONG = MemoryLayouts.JAVA_LONG.varHandle(long.class); + static final VarHandle VH_LONG = ValueLayout.JAVA_LONG.varHandle(); final long size; final long arguments_next_pc; diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/CallingSequence.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/CallingSequence.java index 1412304a4cb03..614166b83d1b2 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/CallingSequence.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/CallingSequence.java @@ -28,7 +28,6 @@ import java.lang.invoke.MethodType; import java.util.List; -import java.util.stream.IntStream; import java.util.stream.Stream; public class CallingSequence { diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/CallingSequenceBuilder.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/CallingSequenceBuilder.java index bdc8474d2b949..633b9d366d692 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/CallingSequenceBuilder.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/CallingSequenceBuilder.java @@ -100,7 +100,6 @@ private void verifyBindings(boolean forArguments, Class carrier, List inType, List bindings) ALLOC_BUFFER, BOX_ADDRESS, //UNBOX_ADDRESS, - //BASE_ADDRESS, TO_SEGMENT, DUP ); diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/ProgrammableInvoker.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/ProgrammableInvoker.java index 4e7b4b37ea6b6..304e028c5d731 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/ProgrammableInvoker.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/ProgrammableInvoker.java @@ -25,11 +25,11 @@ package jdk.internal.foreign.abi; import jdk.incubator.foreign.Addressable; -import jdk.incubator.foreign.MemoryAddress; -import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.NativeSymbol; import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.SegmentAllocator; +import jdk.incubator.foreign.ValueLayout; import jdk.internal.access.JavaLangInvokeAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.invoke.NativeEntryPoint; @@ -40,7 +40,6 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.invoke.VarHandle; -import java.lang.ref.Reference; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -70,7 +69,7 @@ public class ProgrammableInvoker { private static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess(); - private static final VarHandle VH_LONG = MemoryLayouts.JAVA_LONG.varHandle(long.class); + private static final VarHandle VH_LONG = ValueLayout.JAVA_LONG.varHandle(); private static final MethodHandle MH_INVOKE_MOVES; private static final MethodHandle MH_INVOKE_INTERP_BINDINGS; @@ -87,10 +86,10 @@ public class ProgrammableInvoker { MH_INVOKE_MOVES = lookup.findVirtual(ProgrammableInvoker.class, "invokeMoves", methodType(Object.class, long.class, Object[].class, Binding.VMStore[].class, Binding.VMLoad[].class)); MH_INVOKE_INTERP_BINDINGS = lookup.findVirtual(ProgrammableInvoker.class, "invokeInterpBindings", - methodType(Object.class, Addressable.class, SegmentAllocator.class, Object[].class, MethodHandle.class, Map.class, Map.class)); + methodType(Object.class, NativeSymbol.class, SegmentAllocator.class, Object[].class, MethodHandle.class, Map.class, Map.class)); MH_WRAP_ALLOCATOR = lookup.findStatic(Binding.Context.class, "ofAllocator", methodType(Binding.Context.class, SegmentAllocator.class)); - MH_ADDR_TO_LONG = lookup.findStatic(ProgrammableInvoker.class, "unboxTargetAddress", methodType(long.class, Addressable.class)); + MH_ADDR_TO_LONG = lookup.findStatic(ProgrammableInvoker.class, "unboxTargetAddress", methodType(long.class, NativeSymbol.class)); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } @@ -172,9 +171,9 @@ public MethodHandle getBoundMethodHandle() { return handle; } - private static long unboxTargetAddress(Addressable addr) { - MemoryAddress ma = SharedUtils.checkSymbol(addr); - return ma.toRawLongValue(); + private static long unboxTargetAddress(NativeSymbol addr) { + SharedUtils.checkSymbol(addr); + return addr.address().toRawLongValue(); } // Funnel from type to Object[] @@ -312,7 +311,7 @@ Object invokeMoves(long addr, Object[] args, Binding.VMStore[] argBindings, Bind } } - Object invokeInterpBindings(Addressable address, SegmentAllocator allocator, Object[] args, MethodHandle leaf, + Object invokeInterpBindings(NativeSymbol symbol, SegmentAllocator allocator, Object[] args, MethodHandle leaf, Map argIndexMap, Map retIndexMap) throws Throwable { Binding.Context unboxContext = bufferCopySize != 0 @@ -321,21 +320,17 @@ Object invokeInterpBindings(Addressable address, SegmentAllocator allocator, Obj try (unboxContext) { // do argument processing, get Object[] as result Object[] leafArgs = new Object[leaf.type().parameterCount()]; - leafArgs[0] = address; // addr + leafArgs[0] = symbol; // symbol for (int i = 0; i < args.length; i++) { Object arg = args[i]; BindingInterpreter.unbox(arg, callingSequence.argumentBindings(i), (storage, type, value) -> { - leafArgs[argIndexMap.get(storage) + 1] = value; // +1 to skip addr + leafArgs[argIndexMap.get(storage) + 1] = value; // +1 to skip symbol }, unboxContext); } // call leaf Object o = leaf.invokeWithArguments(leafArgs); - // make sure arguments are reachable during the call - // technically we only need to do all Addressable parameters here - Reference.reachabilityFence(address); - Reference.reachabilityFence(args); // return value processing if (o == null) { diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/ProgrammableUpcallHandler.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/ProgrammableUpcallHandler.java index 0f1a04619f6a8..ba8090f669ced 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/ProgrammableUpcallHandler.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/ProgrammableUpcallHandler.java @@ -26,10 +26,10 @@ package jdk.internal.foreign.abi; import jdk.incubator.foreign.MemoryAddress; -import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.NativeSymbol; import jdk.incubator.foreign.ResourceScope; -import jdk.incubator.foreign.SegmentAllocator; +import jdk.incubator.foreign.ValueLayout; import jdk.internal.access.JavaLangInvokeAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.foreign.MemoryAddressImpl; @@ -46,6 +46,7 @@ import java.util.stream.Stream; import static java.lang.invoke.MethodHandles.dropArguments; +import static java.lang.invoke.MethodHandles.exactInvoker; import static java.lang.invoke.MethodHandles.filterReturnValue; import static java.lang.invoke.MethodHandles.identity; import static java.lang.invoke.MethodHandles.insertArguments; @@ -69,7 +70,7 @@ public class ProgrammableUpcallHandler { private static final JavaLangInvokeAccess JLI = SharedSecrets.getJavaLangInvokeAccess(); - private static final VarHandle VH_LONG = MemoryLayouts.JAVA_LONG.varHandle(long.class); + private static final VarHandle VH_LONG = ValueLayout.JAVA_LONG.varHandle(); private static final MethodHandle MH_invokeMoves; private static final MethodHandle MH_invokeInterpBindings; @@ -88,7 +89,7 @@ public class ProgrammableUpcallHandler { } } - public static UpcallHandler make(ABIDescriptor abi, MethodHandle target, CallingSequence callingSequence) { + public static NativeSymbol make(ABIDescriptor abi, MethodHandle target, CallingSequence callingSequence, ResourceScope scope) { Binding.VMLoad[] argMoves = argMoveBindings(callingSequence); Binding.VMStore[] retMoves = retMoveBindings(callingSequence); @@ -134,7 +135,7 @@ public static UpcallHandler make(ABIDescriptor abi, MethodHandle target, Calling MethodHandle invokeMoves = insertArguments(MH_invokeMoves, 1, doBindingsErased, argMoves, retMoves, abi, layout); entryPoint = allocateUpcallStub(invokeMoves, abi, layout); } - return () -> entryPoint; + return UpcallStubs.makeUpcall(entryPoint, scope); } private static void checkPrimitive(MethodType type) { diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/SharedUtils.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/SharedUtils.java index d080d1de1910f..967fd203e04b0 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/SharedUtils.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/SharedUtils.java @@ -25,40 +25,45 @@ package jdk.internal.foreign.abi; import jdk.incubator.foreign.Addressable; +import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.FunctionDescriptor; import jdk.incubator.foreign.GroupLayout; -import jdk.incubator.foreign.MemoryAccess; import jdk.incubator.foreign.MemoryAddress; -import jdk.incubator.foreign.MemoryHandles; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.NativeSymbol; import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.SegmentAllocator; import jdk.incubator.foreign.SequenceLayout; -import jdk.incubator.foreign.CLinker; +import jdk.incubator.foreign.VaList; import jdk.incubator.foreign.ValueLayout; import jdk.internal.access.JavaLangAccess; +import jdk.internal.access.JavaLangInvokeAccess; import jdk.internal.access.SharedSecrets; +import jdk.internal.foreign.Scoped; import jdk.internal.foreign.CABI; import jdk.internal.foreign.MemoryAddressImpl; +import jdk.internal.foreign.ResourceScopeImpl; import jdk.internal.foreign.Utils; import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker; import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker; import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker; import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker; +import jdk.internal.vm.annotation.ForceInline; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.invoke.VarHandle; import java.lang.ref.Reference; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.Consumer; +import java.util.function.UnaryOperator; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -67,17 +72,26 @@ import static java.lang.invoke.MethodHandles.dropArguments; import static java.lang.invoke.MethodHandles.dropReturn; import static java.lang.invoke.MethodHandles.empty; -import static java.lang.invoke.MethodHandles.filterArguments; +import static java.lang.invoke.MethodHandles.foldArguments; import static java.lang.invoke.MethodHandles.identity; import static java.lang.invoke.MethodHandles.insertArguments; import static java.lang.invoke.MethodHandles.permuteArguments; import static java.lang.invoke.MethodHandles.tryFinally; import static java.lang.invoke.MethodType.methodType; -import static jdk.incubator.foreign.CLinker.*; +import static jdk.incubator.foreign.ValueLayout.ADDRESS; +import static jdk.incubator.foreign.ValueLayout.JAVA_BOOLEAN; +import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE; +import static jdk.incubator.foreign.ValueLayout.JAVA_CHAR; +import static jdk.incubator.foreign.ValueLayout.JAVA_DOUBLE; +import static jdk.incubator.foreign.ValueLayout.JAVA_FLOAT; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_LONG; +import static jdk.incubator.foreign.ValueLayout.JAVA_SHORT; public class SharedUtils { private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + private static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess(); private static final MethodHandle MH_ALLOC_BUFFER; private static final MethodHandle MH_BASEADDRESS; @@ -87,6 +101,8 @@ public class SharedUtils { private static final MethodHandle MH_CLOSE_CONTEXT; private static final MethodHandle MH_REACHBILITY_FENCE; private static final MethodHandle MH_HANDLE_UNCAUGHT_EXCEPTION; + private static final MethodHandle ACQUIRE_MH; + private static final MethodHandle RELEASE_MH; static { try { @@ -107,13 +123,19 @@ public class SharedUtils { methodType(void.class, Object.class)); MH_HANDLE_UNCAUGHT_EXCEPTION = lookup.findStatic(SharedUtils.class, "handleUncaughtException", methodType(void.class, Throwable.class)); + ACQUIRE_MH = MethodHandles.lookup().findStatic(SharedUtils.class, "acquire", + MethodType.methodType(void.class, Scoped[].class)); + RELEASE_MH = MethodHandles.lookup().findStatic(SharedUtils.class, "release", + MethodType.methodType(void.class, Scoped[].class)); } catch (ReflectiveOperationException e) { throw new BootstrapMethodError(e); } } // this allocator should be used when no allocation is expected - public static final SegmentAllocator THROWING_ALLOCATOR = (size, align) -> { throw new IllegalStateException("Cannot get here"); }; + public static final SegmentAllocator THROWING_ALLOCATOR = (size, align) -> { + throw new IllegalStateException("Cannot get here"); + }; /** * Align the specified type from a given address @@ -225,34 +247,6 @@ private static MemoryAddress bufferCopy(MemoryAddress dest, MemorySegment buffer return dest; } - public static void checkCompatibleType(Class carrier, MemoryLayout layout, long addressSize) { - if (carrier.isPrimitive()) { - Utils.checkPrimitiveCarrierCompat(carrier, layout); - } else if (carrier == MemoryAddress.class) { - Utils.checkLayoutType(layout, ValueLayout.class); - if (layout.bitSize() != addressSize) - throw new IllegalArgumentException("Address size mismatch: " + addressSize + " != " + layout.bitSize()); - } else if (carrier == MemorySegment.class) { - Utils.checkLayoutType(layout, GroupLayout.class); - } else { - throw new IllegalArgumentException("Unsupported carrier: " + carrier); - } - } - - public static void checkFunctionTypes(MethodType mt, FunctionDescriptor cDesc, long addressSize) { - if (mt.returnType() == void.class != cDesc.returnLayout().isEmpty()) - throw new IllegalArgumentException("Return type mismatch: " + mt + " != " + cDesc); - List argLayouts = cDesc.argumentLayouts(); - if (mt.parameterCount() != argLayouts.size()) - throw new IllegalArgumentException("Arity mismatch: " + mt + " != " + cDesc); - - int paramCount = mt.parameterCount(); - for (int i = 0; i < paramCount; i++) { - checkCompatibleType(mt.parameterType(i), argLayouts.get(i), addressSize); - } - cDesc.returnLayout().ifPresent(rl -> checkCompatibleType(mt.returnType(), rl, addressSize)); - } - public static Class primitiveCarrierForSize(long size, boolean useFloat) { if (useFloat) { if (size == 4) { @@ -287,15 +281,14 @@ public static CLinker getSystemLinker() { public static String toJavaStringInternal(MemorySegment segment, long start) { int len = strlen(segment, start); byte[] bytes = new byte[len]; - MemorySegment.ofArray(bytes) - .copyFrom(segment.asSlice(start, len)); + MemorySegment.copy(segment, JAVA_BYTE, start, bytes, 0, len); return new String(bytes, StandardCharsets.UTF_8); } private static int strlen(MemorySegment segment, long start) { // iterate until overflow (String can only hold a byte[], whose length can be expressed as an int) for (int offset = 0; offset >= 0; offset++) { - byte curr = MemoryAccess.getByteAtOffset(segment, start + offset); + byte curr = segment.get(JAVA_BYTE, start + offset); if (curr == 0) { return offset; } @@ -392,31 +385,21 @@ static MethodHandle wrapWithAllocator(MethodHandle specializedHandle, insertPos = 1; } else { closer = identity(specializedHandle.type().returnType()); // (V) -> V - closer = dropArguments(closer, 0, Throwable.class); // (Throwable, V) -> V + if (!upcall) { + closer = dropArguments(closer, 0, Throwable.class); // (Throwable, V) -> V + } else { + closer = collectArguments(closer, 0, MH_HANDLE_UNCAUGHT_EXCEPTION); // (Throwable, V) -> V + } insertPos = 2; } - // downcalls get the leading Addressable/SegmentAllocator param as well + // downcalls get the leading NativeSymbol/SegmentAllocator param as well if (!upcall) { - closer = collectArguments(closer, insertPos++, reachabilityFenceHandle(Addressable.class)); - closer = dropArguments(closer, insertPos++, SegmentAllocator.class); // (Throwable, V?, Addressable, SegmentAllocator) -> V/void + closer = collectArguments(closer, insertPos++, reachabilityFenceHandle(NativeSymbol.class)); + closer = dropArguments(closer, insertPos++, SegmentAllocator.class); // (Throwable, V?, NativeSymbol, SegmentAllocator) -> V/void } - closer = collectArguments(closer, insertPos++, MH_CLOSE_CONTEXT); // (Throwable, V?, Addressable?, BindingContext) -> V/void - - if (!upcall) { - // now for each Addressable parameter, add a reachability fence - MethodType specType = specializedHandle.type(); - // skip 3 for address, segment allocator, and binding context - for (int i = 3; i < specType.parameterCount(); i++) { - Class param = specType.parameterType(i); - if (Addressable.class.isAssignableFrom(param)) { - closer = collectArguments(closer, insertPos++, reachabilityFenceHandle(param)); - } else { - closer = dropArguments(closer, insertPos++, param); - } - } - } + closer = collectArguments(closer, insertPos++, MH_CLOSE_CONTEXT); // (Throwable, V?, NativeSymbol?, BindingContext) -> V/void MethodHandle contextFactory; @@ -434,34 +417,171 @@ static MethodHandle wrapWithAllocator(MethodHandle specializedHandle, return specializedHandle; } + @ForceInline + @SuppressWarnings("fallthrough") + public static void acquire(Scoped[] args) { + ResourceScope scope4 = null; + ResourceScope scope3 = null; + ResourceScope scope2 = null; + ResourceScope scope1 = null; + ResourceScope scope0 = null; + switch (args.length) { + default: + // slow path, acquire all remaining addressable parameters in isolation + for (int i = 5 ; i < args.length ; i++) { + acquire(args[i].scope()); + } + // fast path, acquire only scopes not seen in other parameters + case 5: + scope4 = args[4].scope(); + acquire(scope4); + case 4: + scope3 = args[3].scope(); + if (scope3 != scope4) + acquire(scope3); + case 3: + scope2 = args[2].scope(); + if (scope2 != scope3 && scope2 != scope4) + acquire(scope2); + case 2: + scope1 = args[1].scope(); + if (scope1 != scope2 && scope1 != scope3 && scope1 != scope4) + acquire(scope1); + case 1: + scope0 = args[0].scope(); + if (scope0 != scope1 && scope0 != scope2 && scope0 != scope3 && scope0 != scope4) + acquire(scope0); + case 0: break; + } + } + + @ForceInline + @SuppressWarnings("fallthrough") + public static void release(Scoped[] args) { + ResourceScope scope4 = null; + ResourceScope scope3 = null; + ResourceScope scope2 = null; + ResourceScope scope1 = null; + ResourceScope scope0 = null; + switch (args.length) { + default: + // slow path, release all remaining addressable parameters in isolation + for (int i = 5 ; i < args.length ; i++) { + release(args[i].scope()); + } + // fast path, release only scopes not seen in other parameters + case 5: + scope4 = args[4].scope(); + release(scope4); + case 4: + scope3 = args[3].scope(); + if (scope3 != scope4) + release(scope3); + case 3: + scope2 = args[2].scope(); + if (scope2 != scope3 && scope2 != scope4) + release(scope2); + case 2: + scope1 = args[1].scope(); + if (scope1 != scope2 && scope1 != scope3 && scope1 != scope4) + release(scope1); + case 1: + scope0 = args[0].scope(); + if (scope0 != scope1 && scope0 != scope2 && scope0 != scope3 && scope0 != scope4) + release(scope0); + case 0: break; + } + } + + @ForceInline + private static void acquire(ResourceScope scope) { + ((ResourceScopeImpl)scope).acquire0(); + } + + @ForceInline + private static void release(ResourceScope scope) { + ((ResourceScopeImpl)scope).release0(); + } + + /* + * This method adds a try/finally block to a downcall method handle, to make sure that all by-reference + * parameters (including the target address of the native function) are kept alive for the duration of + * the downcall. + */ + public static MethodHandle wrapDowncall(MethodHandle downcallHandle, FunctionDescriptor descriptor) { + boolean hasReturn = descriptor.returnLayout().isPresent(); + MethodHandle tryBlock = downcallHandle; + MethodHandle cleanup = hasReturn ? + MethodHandles.identity(downcallHandle.type().returnType()) : + MethodHandles.empty(MethodType.methodType(void.class)); + int addressableCount = 0; + List> adapters = new ArrayList<>(); + for (int i = 0 ; i < downcallHandle.type().parameterCount() ; i++) { + Class ptype = downcallHandle.type().parameterType(i); + if (ptype == Addressable.class || ptype == NativeSymbol.class) { + addressableCount++; + } else { + int pos = i; + adapters.add(mh -> dropArguments(mh, pos, ptype)); + } + } + + if (addressableCount > 0) { + cleanup = dropArguments(cleanup, 0, Throwable.class); + + MethodType adapterType = MethodType.methodType(void.class); + for (int i = 0 ; i < addressableCount ; i++) { + adapterType = adapterType.appendParameterTypes(i == 0 ? NativeSymbol.class : Addressable.class); + } + + MethodHandle acquireHandle = ACQUIRE_MH.asCollector(Scoped[].class, addressableCount).asType(adapterType); + MethodHandle releaseHandle = RELEASE_MH.asCollector(Scoped[].class, addressableCount).asType(adapterType); + + for (UnaryOperator adapter : adapters) { + acquireHandle = adapter.apply(acquireHandle); + releaseHandle = adapter.apply(releaseHandle); + } + + tryBlock = foldArguments(tryBlock, acquireHandle); + cleanup = collectArguments(cleanup, hasReturn ? 2 : 1, releaseHandle); + + return tryFinally(tryBlock, cleanup); + } else { + return downcallHandle; + } + } + + public static void checkExceptions(MethodHandle target) { + Class[] exceptions = JLIA.exceptionTypes(target); + if (exceptions != null && exceptions.length != 0) { + throw new IllegalArgumentException("Target handle may throw exceptions: " + Arrays.toString(exceptions)); + } + } + // lazy init MH_ALLOC and MH_FREE handles private static class AllocHolder { private static final CLinker SYS_LINKER = getSystemLinker(); - static final MethodHandle MH_MALLOC = SYS_LINKER.downcallHandle(CLinker.systemLookup().lookup("malloc").get(), - MethodType.methodType(MemoryAddress.class, long.class), - FunctionDescriptor.of(C_POINTER, C_LONG_LONG)); + static final MethodHandle MH_MALLOC = SYS_LINKER.downcallHandle(CLinker.systemCLinker().lookup("malloc").get(), + FunctionDescriptor.of(ADDRESS, JAVA_LONG)); - static final MethodHandle MH_FREE = SYS_LINKER.downcallHandle(CLinker.systemLookup().lookup("free").get(), - MethodType.methodType(void.class, MemoryAddress.class), - FunctionDescriptor.ofVoid(C_POINTER)); + static final MethodHandle MH_FREE = SYS_LINKER.downcallHandle(CLinker.systemCLinker().lookup("free").get(), + FunctionDescriptor.ofVoid(ADDRESS)); } - public static MemoryAddress checkSymbol(Addressable symbol) { - return checkAddressable(symbol, "Symbol is NULL"); + public static void checkSymbol(NativeSymbol symbol) { + checkAddressable(symbol, "Symbol is NULL"); } - public static MemoryAddress checkAddress(MemoryAddress address) { - return checkAddressable(address, "Address is NULL"); + public static void checkAddress(MemoryAddress address) { + checkAddressable(address, "Address is NULL"); } - private static MemoryAddress checkAddressable(Addressable symbol, String msg) { + private static void checkAddressable(Addressable symbol, String msg) { Objects.requireNonNull(symbol); - MemoryAddress symbolAddr = symbol.address(); - if (symbolAddr.equals(MemoryAddress.NULL)) - throw new IllegalArgumentException("Symbol is NULL: " + symbolAddr); - return symbolAddr; + if (symbol.address().toRawLongValue() == 0) + throw new IllegalArgumentException("Symbol is NULL: " + symbol); } public static MemoryAddress allocateMemoryInternal(long size) { @@ -474,7 +594,7 @@ public static MemoryAddress allocateMemoryInternal(long size) { public static void freeMemoryInternal(MemoryAddress addr) { try { - AllocHolder.MH_FREE.invokeExact(addr); + AllocHolder.MH_FREE.invokeExact((Addressable)addr); } catch (Throwable th) { throw new RuntimeException(th); } @@ -489,12 +609,6 @@ public static VaList newVaList(Consumer actions, ResourceScope s }; } - public static VarHandle vhPrimitiveOrAddress(Class carrier, MemoryLayout layout) { - return carrier == MemoryAddress.class - ? MemoryHandles.asAddressVarHandle(layout.varHandle(primitiveCarrierForSize(layout.byteSize(), false))) - : layout.varHandle(carrier); - } - public static VaList newVaListOfAddress(MemoryAddress ma, ResourceScope scope) { return switch (CABI.current()) { case Win64 -> Windowsx64Linker.newVaListOfAddress(ma, scope); @@ -513,34 +627,6 @@ public static VaList emptyVaList() { }; } - public static MethodType convertVaListCarriers(MethodType mt, Class carrier) { - Class[] params = new Class[mt.parameterCount()]; - for (int i = 0; i < params.length; i++) { - Class pType = mt.parameterType(i); - params[i] = ((pType == VaList.class) ? carrier : pType); - } - return methodType(mt.returnType(), params); - } - - public static MethodHandle unboxVaLists(MethodType type, MethodHandle handle, MethodHandle unboxer) { - for (int i = 0; i < type.parameterCount(); i++) { - if (type.parameterType(i) == VaList.class) { - handle = filterArguments(handle, i + 1, unboxer); // +1 for leading address - } - } - return handle; - } - - public static MethodHandle boxVaLists(MethodHandle handle, MethodHandle boxer) { - MethodType type = handle.type(); - for (int i = 0; i < type.parameterCount(); i++) { - if (type.parameterType(i) == VaList.class) { - handle = filterArguments(handle, i, boxer); - } - } - return handle; - } - static void checkType(Class actualType, Class expectedType) { if (expectedType != actualType) { throw new IllegalArgumentException( @@ -549,9 +635,12 @@ static void checkType(Class actualType, Class expectedType) { } public static boolean isTrivial(FunctionDescriptor cDesc) { - return cDesc.attribute(FunctionDescriptor.TRIVIAL_ATTRIBUTE_NAME) - .map(Boolean.class::cast) - .orElse(false); + return false; // FIXME: use system property? + } + + public static boolean isVarargsIndex(FunctionDescriptor descriptor, int argIndex) { + int firstPos = descriptor.firstVariadicArgumentIndex(); + return firstPos != -1 && argIndex >= firstPos; } public static class SimpleVaArg { @@ -566,13 +655,11 @@ public SimpleVaArg(Class carrier, MemoryLayout layout, Object value) { } public VarHandle varHandle() { - return carrier == MemoryAddress.class - ? MemoryHandles.asAddressVarHandle(layout.varHandle(primitiveCarrierForSize(layout.byteSize(), false))) - : layout.varHandle(carrier); + return layout.varHandle(); } } - public static non-sealed class EmptyVaList implements VaList { + public static non-sealed class EmptyVaList implements VaList, Scoped { private final MemoryAddress address; @@ -585,32 +672,27 @@ private static UnsupportedOperationException uoe() { } @Override - public int vargAsInt(MemoryLayout layout) { - throw uoe(); - } - - @Override - public long vargAsLong(MemoryLayout layout) { + public int nextVarg(ValueLayout.OfInt layout) { throw uoe(); } @Override - public double vargAsDouble(MemoryLayout layout) { + public long nextVarg(ValueLayout.OfLong layout) { throw uoe(); } @Override - public MemoryAddress vargAsAddress(MemoryLayout layout) { + public double nextVarg(ValueLayout.OfDouble layout) { throw uoe(); } @Override - public MemorySegment vargAsSegment(MemoryLayout layout, SegmentAllocator allocator) { + public MemoryAddress nextVarg(ValueLayout.OfAddress layout) { throw uoe(); } @Override - public MemorySegment vargAsSegment(MemoryLayout layout, ResourceScope scope) { + public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) { throw uoe(); } @@ -638,19 +720,21 @@ public MemoryAddress address() { 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) { - MemoryAccess.setLong(ptr, (long) o); + ptr.set(JAVA_LONG, 0, (long) o); } else if (type == int.class) { - MemoryAccess.setLong(ptr, (int) o); + ptr.set(JAVA_LONG, 0, (int) o); } else if (type == short.class) { - MemoryAccess.setLong(ptr, (short) o); + ptr.set(JAVA_LONG, 0, (short) o); } else if (type == char.class) { - MemoryAccess.setLong(ptr, (char) o); + ptr.set(JAVA_LONG, 0, (char) o); } else if (type == byte.class) { - MemoryAccess.setLong(ptr, (byte) o); + ptr.set(JAVA_LONG, 0, (byte) o); } else if (type == float.class) { - MemoryAccess.setFloat(ptr, (float) o); + ptr.set(JAVA_FLOAT, 0, (float) o); } else if (type == double.class) { - MemoryAccess.setDouble(ptr, (double) o); + ptr.set(JAVA_DOUBLE, 0, (double) o); + } else if (type == boolean.class) { + ptr.set(JAVA_BOOLEAN, 0, (boolean) o); } else { throw new IllegalArgumentException("Unsupported carrier: " + type); } @@ -658,19 +742,21 @@ static void writeOverSized(MemorySegment ptr, Class type, Object o) { static void write(MemorySegment ptr, Class type, Object o) { if (type == long.class) { - MemoryAccess.setLong(ptr, (long) o); + ptr.set(JAVA_LONG, 0, (long) o); } else if (type == int.class) { - MemoryAccess.setInt(ptr, (int) o); + ptr.set(JAVA_INT, 0, (int) o); } else if (type == short.class) { - MemoryAccess.setShort(ptr, (short) o); + ptr.set(JAVA_SHORT, 0, (short) o); } else if (type == char.class) { - MemoryAccess.setChar(ptr, (char) o); + ptr.set(JAVA_CHAR, 0, (char) o); } else if (type == byte.class) { - MemoryAccess.setByte(ptr, (byte) o); + ptr.set(JAVA_BYTE, 0, (byte) o); } else if (type == float.class) { - MemoryAccess.setFloat(ptr, (float) o); + ptr.set(JAVA_FLOAT, 0, (float) o); } else if (type == double.class) { - MemoryAccess.setDouble(ptr, (double) o); + ptr.set(JAVA_DOUBLE, 0, (double) o); + } else if (type == boolean.class) { + ptr.set(JAVA_BOOLEAN, 0, (boolean) o); } else { throw new IllegalArgumentException("Unsupported carrier: " + type); } @@ -678,21 +764,43 @@ static void write(MemorySegment ptr, Class type, Object o) { static Object read(MemorySegment ptr, Class type) { if (type == long.class) { - return MemoryAccess.getLong(ptr); + return ptr.get(JAVA_LONG, 0); } else if (type == int.class) { - return MemoryAccess.getInt(ptr); + return ptr.get(JAVA_INT, 0); } else if (type == short.class) { - return MemoryAccess.getShort(ptr); + return ptr.get(JAVA_SHORT, 0); } else if (type == char.class) { - return MemoryAccess.getChar(ptr); + return ptr.get(JAVA_CHAR, 0); } else if (type == byte.class) { - return MemoryAccess.getByte(ptr); + return ptr.get(JAVA_BYTE, 0); } else if (type == float.class) { - return MemoryAccess.getFloat(ptr); + return ptr.get(JAVA_FLOAT, 0); } else if (type == double.class) { - return MemoryAccess.getDouble(ptr); + return ptr.get(JAVA_DOUBLE, 0); + } else if (type == boolean.class) { + return ptr.get(JAVA_BOOLEAN, 0); } else { throw new IllegalArgumentException("Unsupported carrier: " + type); } } + + public static MethodType inferMethodType(FunctionDescriptor descriptor, boolean upcall) { + MethodType type = MethodType.methodType(descriptor.returnLayout().isPresent() ? + carrierFor(descriptor.returnLayout().get(), upcall) : void.class); + for (MemoryLayout argLayout : descriptor.argumentLayouts()) { + type = type.appendParameterTypes(carrierFor(argLayout, !upcall)); + } + return type; + } + + static Class carrierFor(MemoryLayout layout, boolean forArg) { + if (layout instanceof ValueLayout valueLayout) { + return (forArg && valueLayout.carrier().equals(MemoryAddress.class)) ? + Addressable.class : valueLayout.carrier(); + } else if (layout instanceof GroupLayout) { + return MemorySegment.class; + } else { + throw new IllegalArgumentException("Unsupported layout: " + layout); + } + } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/UpcallStubs.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/UpcallStubs.java index d417b8ad9abee..9f528dfacd0f6 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/UpcallStubs.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/UpcallStubs.java @@ -25,18 +25,13 @@ package jdk.internal.foreign.abi; import jdk.incubator.foreign.MemoryAddress; -import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.NativeSymbol; +import jdk.incubator.foreign.ResourceScope; +import jdk.internal.foreign.NativeSymbolImpl; import jdk.internal.foreign.ResourceScopeImpl; -import jdk.internal.foreign.NativeMemorySegmentImpl; public class UpcallStubs { - public static MemoryAddress upcallAddress(UpcallHandler handler, ResourceScopeImpl scope) { - long stubAddress = handler.entryPoint(); - return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(MemoryAddress.ofLong(stubAddress), 0, - () -> freeUpcallStub(stubAddress), scope).address(); - } - private static void freeUpcallStub(long stubAddress) { if (!freeUpcallStub0(stubAddress)) { throw new IllegalStateException("Not a stub address: " + stubAddress); @@ -52,4 +47,14 @@ private static void freeUpcallStub(long stubAddress) { static { registerNatives(); } + + static NativeSymbol makeUpcall(long entry, ResourceScope scope) { + ((ResourceScopeImpl)scope).addOrCleanupIfFail(new ResourceScopeImpl.ResourceList.ResourceCleanup() { + @Override + public void cleanup() { + freeUpcallStub(entry); + } + }); + return new NativeSymbolImpl("upcall:" + Long.toHexString(entry), MemoryAddress.ofLong(entry), scope); + } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java index ef12d129bcb6c..abcef36500dd6 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java @@ -30,9 +30,10 @@ import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.NativeSymbol; +import jdk.incubator.foreign.ResourceScope; import jdk.internal.foreign.Utils; import jdk.internal.foreign.abi.CallingSequenceBuilder; -import jdk.internal.foreign.abi.UpcallHandler; import jdk.internal.foreign.abi.ABIDescriptor; import jdk.internal.foreign.abi.Binding; import jdk.internal.foreign.abi.CallingSequence; @@ -40,6 +41,8 @@ import jdk.internal.foreign.abi.ProgrammableUpcallHandler; import jdk.internal.foreign.abi.VMStorage; import jdk.internal.foreign.abi.SharedUtils; +import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64CallArranger; +import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64CallArranger; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; @@ -54,8 +57,12 @@ * to translate a C FunctionDescriptor into a CallingSequence2, which can then be turned into a MethodHandle. * * This includes taking care of synthetic arguments like pointers to return buffers for 'in-memory' returns. + * + * There are minor differences between the ABIs implemented on Linux, macOS, and Windows + * which are handled in sub-classes. Clients should access these through the provided + * public constants CallArranger.LINUX and CallArranger.MACOS. */ -public class CallArranger { +public abstract class CallArranger { private static final int STACK_SLOT_SIZE = 8; public static final int MAX_REGISTER_ARGUMENTS = 8; @@ -95,9 +102,20 @@ public static class Bindings { } } - public static Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall) { - SharedUtils.checkFunctionTypes(mt, cDesc, AArch64.C_POINTER.bitSize()); + public static final CallArranger LINUX = new LinuxAArch64CallArranger(); + public static final CallArranger MACOS = new MacOsAArch64CallArranger(); + + /** + * Are variadic arguments assigned to registers as in the standard calling + * convention, or always passed on the stack? + * + * @returns true if variadic arguments should be spilled to the stack. + */ + protected abstract boolean varArgsOnStack(); + + protected CallArranger() {} + public Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall) { CallingSequenceBuilder csb = new CallingSequenceBuilder(forUpcall); BindingCalculator argCalc = forUpcall ? new BoxBindingCalculator(true) : new UnboxBindingCalculator(true); @@ -116,6 +134,9 @@ public static Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, bool for (int i = 0; i < mt.parameterCount(); i++) { Class carrier = mt.parameterType(i); MemoryLayout layout = cDesc.argumentLayouts().get(i); + if (varArgsOnStack() && SharedUtils.isVarargsIndex(cDesc, i)) { + argCalc.storageCalculator.adjustForVarArgs(); + } csb.addArgumentBindings(carrier, layout, argCalc.getBindings(carrier, layout)); } @@ -124,7 +145,7 @@ public static Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, bool return new Bindings(csb.build(), returnInMemory); } - public static MethodHandle arrangeDowncall(MethodType mt, FunctionDescriptor cDesc) { + public MethodHandle arrangeDowncall(MethodType mt, FunctionDescriptor cDesc) { Bindings bindings = getBindings(mt, cDesc, false); MethodHandle handle = new ProgrammableInvoker(C, bindings.callingSequence).getBoundMethodHandle(); @@ -136,14 +157,14 @@ public static MethodHandle arrangeDowncall(MethodType mt, FunctionDescriptor cDe return handle; } - public static UpcallHandler arrangeUpcall(MethodHandle target, MethodType mt, FunctionDescriptor cDesc) { + public NativeSymbol arrangeUpcall(MethodHandle target, MethodType mt, FunctionDescriptor cDesc, ResourceScope scope) { Bindings bindings = getBindings(mt, cDesc, true); if (bindings.isInMemoryReturn) { target = SharedUtils.adaptUpcallForIMR(target, true /* drop return, since we don't have bindings for it */); } - return ProgrammableUpcallHandler.make(C, target, bindings.callingSequence); + return ProgrammableUpcallHandler.make(C, target, bindings.callingSequence,scope); } private static boolean isInMemoryReturn(Optional returnLayout) { @@ -208,18 +229,15 @@ VMStorage nextStorage(int type, MemoryLayout layout) { return storage[0]; } - void adjustForVarArgs(MemoryLayout layout) { - if (layout.attribute(AArch64.STACK_VARARGS_ATTRIBUTE_NAME) - .map(Boolean.class::cast).orElse(false)) { - // This system passes all variadic parameters on the stack. Ensure - // no further arguments are allocated to registers. - nRegs[StorageClasses.INTEGER] = MAX_REGISTER_ARGUMENTS; - nRegs[StorageClasses.VECTOR] = MAX_REGISTER_ARGUMENTS; - } + void adjustForVarArgs() { + // This system passes all variadic parameters on the stack. Ensure + // no further arguments are allocated to registers. + nRegs[StorageClasses.INTEGER] = MAX_REGISTER_ARGUMENTS; + nRegs[StorageClasses.VECTOR] = MAX_REGISTER_ARGUMENTS; } } - static abstract class BindingCalculator { + abstract static class BindingCalculator { protected final StorageCalculator storageCalculator; protected BindingCalculator(boolean forArguments) { @@ -288,7 +306,6 @@ List getIndirectBindings() { List getBindings(Class carrier, MemoryLayout layout) { TypeClass argumentClass = TypeClass.classifyLayout(layout); Binding.Builder bindings = Binding.builder(); - storageCalculator.adjustForVarArgs(layout); switch (argumentClass) { case STRUCT_REGISTER: { assert carrier == MemorySegment.class; @@ -317,8 +334,7 @@ List getBindings(Class carrier, MemoryLayout layout) { case STRUCT_REFERENCE: { assert carrier == MemorySegment.class; bindings.copy(layout) - .baseAddress() - .unboxAddress(); + .unboxAddress(MemorySegment.class); VMStorage storage = storageCalculator.nextStorage( StorageClasses.INTEGER, AArch64.C_POINTER); bindings.vmStore(storage, long.class); @@ -349,7 +365,7 @@ List getBindings(Class carrier, MemoryLayout layout) { break; } case POINTER: { - bindings.unboxAddress(); + bindings.unboxAddress(carrier); VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER, layout); bindings.vmStore(storage, long.class); @@ -391,7 +407,6 @@ List getIndirectBindings() { List getBindings(Class carrier, MemoryLayout layout) { TypeClass argumentClass = TypeClass.classifyLayout(layout); Binding.Builder bindings = Binding.builder(); - assert !layout.attribute(AArch64.STACK_VARARGS_ATTRIBUTE_NAME).isPresent() : "no variadic upcalls"; switch (argumentClass) { case STRUCT_REGISTER: { assert carrier == MemorySegment.class; diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/TypeClass.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/TypeClass.java index 781d6851b5bcb..5514d300daafa 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/TypeClass.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/TypeClass.java @@ -26,10 +26,10 @@ package jdk.internal.foreign.abi.aarch64; import jdk.incubator.foreign.GroupLayout; +import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.SequenceLayout; import jdk.incubator.foreign.ValueLayout; -import jdk.internal.foreign.PlatformLayouts; public enum TypeClass { STRUCT_REGISTER, @@ -42,11 +42,17 @@ public enum TypeClass { private static final int MAX_AGGREGATE_REGS_SIZE = 2; private static TypeClass classifyValueType(ValueLayout type) { - return switch (PlatformLayouts.getKind(type)) { - case CHAR, SHORT, INT, LONG, LONG_LONG -> INTEGER; - case POINTER -> POINTER; - case FLOAT, DOUBLE -> FLOAT; - }; + Class carrier = type.carrier(); + if (carrier == boolean.class || carrier == byte.class || carrier == char.class || + carrier == short.class || carrier == int.class || carrier == long.class) { + return INTEGER; + } else if (carrier == float.class || carrier == double.class) { + return FLOAT; + } else if (carrier == MemoryAddress.class) { + return POINTER; + } else { + throw new IllegalStateException("Cannot get here: " + carrier.getName()); + } } static boolean isRegisterAggregate(MemoryLayout type) { diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64CallArranger.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64CallArranger.java new file mode 100644 index 0000000000000..044f4a83266d4 --- /dev/null +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64CallArranger.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, Arm Limited. All rights reserved. + * 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.abi.aarch64.linux; + +import jdk.internal.foreign.abi.aarch64.*; + +/** + * AArch64 CallArranger specialized for Linux ABI. + */ +public class LinuxAArch64CallArranger extends CallArranger { + + @Override + protected boolean varArgsOnStack() { + // Variadic arguments are passed as normal arguments + return false; + } + +} diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64Linker.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64Linker.java index d2a0db203bfc1..aca622ab5baed 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64Linker.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64Linker.java @@ -25,14 +25,14 @@ */ package jdk.internal.foreign.abi.aarch64.linux; +import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.FunctionDescriptor; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.NativeSymbol; import jdk.incubator.foreign.ResourceScope; -import jdk.internal.foreign.AbstractCLinker; -import jdk.internal.foreign.ResourceScopeImpl; +import jdk.incubator.foreign.VaList; import jdk.internal.foreign.abi.SharedUtils; -import jdk.internal.foreign.abi.UpcallStubs; import jdk.internal.foreign.abi.aarch64.CallArranger; import java.lang.invoke.MethodHandle; @@ -45,26 +45,11 @@ * ABI implementation based on ARM document "Procedure Call Standard for * the ARM 64-bit Architecture". */ -public final class LinuxAArch64Linker extends AbstractCLinker { +public final class LinuxAArch64Linker implements CLinker { private static LinuxAArch64Linker instance; static final long ADDRESS_SIZE = 64; // bits - private static final MethodHandle MH_unboxVaList; - private static final MethodHandle MH_boxVaList; - - static { - try { - MethodHandles.Lookup lookup = MethodHandles.lookup(); - MH_unboxVaList = lookup.findVirtual(VaList.class, "address", - MethodType.methodType(MemoryAddress.class)); - MH_boxVaList = MethodHandles.insertArguments(lookup.findStatic(LinuxAArch64Linker.class, "newVaListOfAddress", - MethodType.methodType(VaList.class, MemoryAddress.class, ResourceScope.class)), 1, ResourceScope.globalScope()); - } catch (ReflectiveOperationException e) { - throw new ExceptionInInitializerError(e); - } - } - public static LinuxAArch64Linker getInstance() { if (instance == null) { instance = new LinuxAArch64Linker(); @@ -73,26 +58,28 @@ public static LinuxAArch64Linker getInstance() { } @Override - public final MethodHandle downcallHandle(MethodType type, FunctionDescriptor function) { - Objects.requireNonNull(type); + public final MethodHandle downcallHandle(FunctionDescriptor function) { Objects.requireNonNull(function); - MethodType llMt = SharedUtils.convertVaListCarriers(type, LinuxAArch64VaList.CARRIER); - MethodHandle handle = CallArranger.arrangeDowncall(llMt, function); + MethodType type = SharedUtils.inferMethodType(function, false); + MethodHandle handle = CallArranger.LINUX.arrangeDowncall(type, function); if (!type.returnType().equals(MemorySegment.class)) { // not returning segment, just insert a throwing allocator handle = MethodHandles.insertArguments(handle, 1, SharedUtils.THROWING_ALLOCATOR); } - handle = SharedUtils.unboxVaLists(type, handle, MH_unboxVaList); - return handle; + return SharedUtils.wrapDowncall(handle, function); } @Override - public final MemoryAddress upcallStub(MethodHandle target, FunctionDescriptor function, ResourceScope scope) { + public final NativeSymbol upcallStub(MethodHandle target, FunctionDescriptor function, ResourceScope scope) { Objects.requireNonNull(scope); Objects.requireNonNull(target); Objects.requireNonNull(function); - target = SharedUtils.boxVaLists(target, MH_boxVaList); - return UpcallStubs.upcallAddress(CallArranger.arrangeUpcall(target, target.type(), function), (ResourceScopeImpl) scope); + SharedUtils.checkExceptions(target); + MethodType type = SharedUtils.inferMethodType(function, true); + if (!type.equals(target.type())) { + throw new IllegalArgumentException("Wrong method handle type: " + target.type()); + } + return CallArranger.LINUX.arrangeUpcall(target, target.type(), function, scope); } public static VaList newVaList(Consumer actions, ResourceScope scope) { diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64VaList.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64VaList.java index 567ec2d4bd52a..457b798274ffa 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64VaList.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64VaList.java @@ -26,32 +26,30 @@ package jdk.internal.foreign.abi.aarch64.linux; import jdk.incubator.foreign.*; +import jdk.internal.foreign.ResourceScopeImpl; +import jdk.internal.foreign.Scoped; import jdk.internal.foreign.Utils; import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.aarch64.*; import jdk.internal.misc.Unsafe; import java.lang.invoke.VarHandle; -import java.lang.ref.Cleaner; -import java.nio.ByteOrder; import java.util.ArrayList; import java.util.List; import java.util.Objects; import static jdk.internal.foreign.PlatformLayouts.AArch64; -import static jdk.incubator.foreign.CLinker.VaList; + import static jdk.incubator.foreign.MemoryLayout.PathElement.groupElement; import static jdk.internal.foreign.abi.SharedUtils.SimpleVaArg; import static jdk.internal.foreign.abi.SharedUtils.THROWING_ALLOCATOR; -import static jdk.internal.foreign.abi.SharedUtils.checkCompatibleType; -import static jdk.internal.foreign.abi.SharedUtils.vhPrimitiveOrAddress; import static jdk.internal.foreign.abi.aarch64.CallArranger.MAX_REGISTER_ARGUMENTS; /** * Standard va_list implementation as defined by AAPCS document and used on * Linux. Variadic parameters may be passed in registers or on the stack. */ -public non-sealed class LinuxAArch64VaList implements VaList { +public non-sealed class LinuxAArch64VaList implements VaList, Scoped { private static final Unsafe U = Unsafe.getUnsafe(); static final Class CARRIER = MemoryAddress.class; @@ -76,9 +74,9 @@ public non-sealed class LinuxAArch64VaList implements VaList { ).withName("__va_list"); private static final MemoryLayout GP_REG - = MemoryLayout.valueLayout(64, ByteOrder.nativeOrder()); + = MemoryLayout.paddingLayout(64).withBitAlignment(64); private static final MemoryLayout FP_REG - = MemoryLayout.valueLayout(128, ByteOrder.nativeOrder()); + = MemoryLayout.paddingLayout(128).withBitAlignment(128); private static final MemoryLayout LAYOUT_GP_REGS = MemoryLayout.sequenceLayout(MAX_REGISTER_ARGUMENTS, GP_REG); @@ -91,18 +89,14 @@ public non-sealed class LinuxAArch64VaList implements VaList { private static final int MAX_GP_OFFSET = (int) LAYOUT_GP_REGS.byteSize(); private static final int MAX_FP_OFFSET = (int) LAYOUT_FP_REGS.byteSize(); - private static final VarHandle VH_stack - = MemoryHandles.asAddressVarHandle(LAYOUT.varHandle(long.class, groupElement("__stack"))); - private static final VarHandle VH_gr_top - = MemoryHandles.asAddressVarHandle(LAYOUT.varHandle(long.class, groupElement("__gr_top"))); - private static final VarHandle VH_vr_top - = MemoryHandles.asAddressVarHandle(LAYOUT.varHandle(long.class, groupElement("__vr_top"))); + private static final VarHandle VH_stack = LAYOUT.varHandle(groupElement("__stack")); + private static final VarHandle VH_gr_top = LAYOUT.varHandle(groupElement("__gr_top")); + private static final VarHandle VH_vr_top = LAYOUT.varHandle(groupElement("__vr_top")); private static final VarHandle VH_gr_offs - = LAYOUT.varHandle(int.class, groupElement("__gr_offs")); + = LAYOUT.varHandle(groupElement("__gr_offs")); private static final VarHandle VH_vr_offs - = LAYOUT.varHandle(int.class, groupElement("__vr_offs")); + = LAYOUT.varHandle(groupElement("__vr_offs")); - private static final Cleaner cleaner = Cleaner.create(); private static final VaList EMPTY = new SharedUtils.EmptyVaList(emptyListAddress()); @@ -117,19 +111,20 @@ private LinuxAArch64VaList(MemorySegment segment, MemorySegment gpRegsArea, Memo } private static LinuxAArch64VaList readFromSegment(MemorySegment segment) { - MemorySegment gpRegsArea = grTop(segment).addOffset(-MAX_GP_OFFSET).asSegment( + MemorySegment gpRegsArea = MemorySegment.ofAddress(grTop(segment).addOffset(-MAX_GP_OFFSET), MAX_GP_OFFSET, segment.scope()); - MemorySegment fpRegsArea = vrTop(segment).addOffset(-MAX_FP_OFFSET).asSegment( + MemorySegment fpRegsArea = MemorySegment.ofAddress(vrTop(segment).addOffset(-MAX_FP_OFFSET), MAX_FP_OFFSET, segment.scope()); return new LinuxAArch64VaList(segment, gpRegsArea, fpRegsArea); } private static MemoryAddress emptyListAddress() { long ptr = U.allocateMemory(LAYOUT.byteSize()); - MemorySegment ms = MemoryAddress.ofLong(ptr).asSegment( - LAYOUT.byteSize(), () -> U.freeMemory(ptr), ResourceScope.newSharedScope()); - cleaner.register(LinuxAArch64VaList.class, () -> ms.scope().close()); + ResourceScope scope = ResourceScope.newImplicitScope(); + scope.addCloseAction(() -> U.freeMemory(ptr)); + MemorySegment ms = MemorySegment.ofAddress(MemoryAddress.ofLong(ptr), + LAYOUT.byteSize(), scope); VH_stack.set(ms, MemoryAddress.NULL); VH_gr_top.set(ms, MemoryAddress.NULL); VH_vr_top.set(ms, MemoryAddress.NULL); @@ -215,58 +210,51 @@ private void postAlignStack(MemoryLayout layout) { } @Override - public int vargAsInt(MemoryLayout layout) { + public int nextVarg(ValueLayout.OfInt layout) { return (int) read(int.class, layout); } @Override - public long vargAsLong(MemoryLayout layout) { + public long nextVarg(ValueLayout.OfLong layout) { return (long) read(long.class, layout); } @Override - public double vargAsDouble(MemoryLayout layout) { + public double nextVarg(ValueLayout.OfDouble layout) { return (double) read(double.class, layout); } @Override - public MemoryAddress vargAsAddress(MemoryLayout layout) { + public MemoryAddress nextVarg(ValueLayout.OfAddress layout) { return (MemoryAddress) read(MemoryAddress.class, layout); } @Override - public MemorySegment vargAsSegment(MemoryLayout layout, SegmentAllocator allocator) { + public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) { Objects.requireNonNull(allocator); return (MemorySegment) read(MemorySegment.class, layout, allocator); } - @Override - public MemorySegment vargAsSegment(MemoryLayout layout, ResourceScope scope) { - return vargAsSegment(layout, SegmentAllocator.ofScope(scope)); - } - private Object read(Class carrier, MemoryLayout layout) { return read(carrier, layout, THROWING_ALLOCATOR); } private Object read(Class carrier, MemoryLayout layout, SegmentAllocator allocator) { Objects.requireNonNull(layout); - checkCompatibleType(carrier, layout, LinuxAArch64Linker.ADDRESS_SIZE); - TypeClass typeClass = TypeClass.classifyLayout(layout); if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass, layout)) { preAlignStack(layout); return switch (typeClass) { case STRUCT_REGISTER, STRUCT_HFA, STRUCT_REFERENCE -> { - MemorySegment slice = stackPtr().asSegment(layout.byteSize(), scope()); + MemorySegment slice = MemorySegment.ofAddress(stackPtr(), layout.byteSize(), scope()); MemorySegment seg = allocator.allocate(layout); seg.copyFrom(slice); postAlignStack(layout); yield seg; } case POINTER, INTEGER, FLOAT -> { - VarHandle reader = vhPrimitiveOrAddress(carrier, layout); - MemorySegment slice = stackPtr().asSegment(layout.byteSize(), scope()); + VarHandle reader = layout.varHandle(); + MemorySegment slice = MemorySegment.ofAddress(stackPtr(), layout.byteSize(), scope()); Object res = reader.get(slice); postAlignStack(layout); yield res; @@ -280,8 +268,7 @@ private Object read(Class carrier, MemoryLayout layout, SegmentAllocator allo long offset = 0; while (offset < layout.byteSize()) { final long copy = Math.min(layout.byteSize() - offset, 8); - MemorySegment slice = value.asSlice(offset, copy); - slice.copyFrom(gpRegsArea.asSlice(currentGPOffset(), copy)); + MemorySegment.copy(gpRegsArea, currentGPOffset(), value, offset, copy); consumeGPSlots(1); offset += copy; } @@ -296,8 +283,7 @@ private Object read(Class carrier, MemoryLayout layout, SegmentAllocator allo for (MemoryLayout elem : group.memberLayouts()) { assert elem.byteSize() <= 8; final long copy = elem.byteSize(); - MemorySegment slice = value.asSlice(offset, copy); - slice.copyFrom(fpRegsArea.asSlice(currentFPOffset(), copy)); + MemorySegment.copy(fpRegsArea, currentFPOffset(), value, offset, copy); consumeFPSlots(1); offset += copy; } @@ -305,25 +291,24 @@ private Object read(Class carrier, MemoryLayout layout, SegmentAllocator allo } case STRUCT_REFERENCE -> { // Struct is passed indirectly via a pointer in an integer register. - VarHandle ptrReader - = SharedUtils.vhPrimitiveOrAddress(MemoryAddress.class, AArch64.C_POINTER); + VarHandle ptrReader = AArch64.C_POINTER.varHandle(); MemoryAddress ptr = (MemoryAddress) ptrReader.get( gpRegsArea.asSlice(currentGPOffset())); consumeGPSlots(1); - MemorySegment slice = ptr.asSegment(layout.byteSize(), scope()); + MemorySegment slice = MemorySegment.ofAddress(ptr, layout.byteSize(), scope()); MemorySegment seg = allocator.allocate(layout); seg.copyFrom(slice); yield seg; } case POINTER, INTEGER -> { - VarHandle reader = SharedUtils.vhPrimitiveOrAddress(carrier, layout); + VarHandle reader = layout.varHandle(); Object res = reader.get(gpRegsArea.asSlice(currentGPOffset())); consumeGPSlots(1); yield res; } case FLOAT -> { - VarHandle reader = layout.varHandle(carrier); + VarHandle reader = layout.varHandle(); Object res = reader.get(fpRegsArea.asSlice(currentFPOffset())); consumeFPSlots(1); yield res; @@ -335,6 +320,7 @@ private Object read(Class carrier, MemoryLayout layout, SegmentAllocator allo @Override public void skip(MemoryLayout... layouts) { Objects.requireNonNull(layouts); + ((ResourceScopeImpl)segment.scope()).checkValidStateSlow(); for (MemoryLayout layout : layouts) { Objects.requireNonNull(layout); TypeClass typeClass = TypeClass.classifyLayout(layout); @@ -356,7 +342,7 @@ static LinuxAArch64VaList.Builder builder(ResourceScope scope) { } public static VaList ofAddress(MemoryAddress ma, ResourceScope scope) { - return readFromSegment(ma.asSegment(LAYOUT.byteSize(), scope)); + return readFromSegment(MemorySegment.ofAddress(ma, LAYOUT.byteSize(), scope)); } @Override @@ -418,35 +404,33 @@ public static non-sealed class Builder implements VaList.Builder { } @Override - public Builder vargFromInt(ValueLayout layout, int value) { + public Builder addVarg(ValueLayout.OfInt layout, int value) { return arg(int.class, layout, value); } @Override - public Builder vargFromLong(ValueLayout layout, long value) { + public Builder addVarg(ValueLayout.OfLong layout, long value) { return arg(long.class, layout, value); } @Override - public Builder vargFromDouble(ValueLayout layout, double value) { + public Builder addVarg(ValueLayout.OfDouble layout, double value) { return arg(double.class, layout, value); } @Override - public Builder vargFromAddress(ValueLayout layout, Addressable value) { + public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) { return arg(MemoryAddress.class, layout, value.address()); } @Override - public Builder vargFromSegment(GroupLayout layout, MemorySegment value) { + public Builder addVarg(GroupLayout layout, MemorySegment value) { return arg(MemorySegment.class, layout, value); } private Builder arg(Class carrier, MemoryLayout layout, Object value) { Objects.requireNonNull(layout); Objects.requireNonNull(value); - checkCompatibleType(carrier, layout, LinuxAArch64Linker.ADDRESS_SIZE); - TypeClass typeClass = TypeClass.classifyLayout(layout); if (isRegOverflow(currentGPOffset, currentFPOffset, typeClass, layout)) { stackArgs.add(new SimpleVaArg(carrier, layout, value)); @@ -458,8 +442,7 @@ private Builder arg(Class carrier, MemoryLayout layout, Object value) { long offset = 0; while (offset < layout.byteSize()) { final long copy = Math.min(layout.byteSize() - offset, 8); - MemorySegment slice = valueSegment.asSlice(offset, copy); - gpRegs.asSlice(currentGPOffset, copy).copyFrom(slice); + MemorySegment.copy(valueSegment, offset, gpRegs, currentGPOffset, copy); currentGPOffset += GP_SLOT_SIZE; offset += copy; } @@ -473,8 +456,7 @@ private Builder arg(Class carrier, MemoryLayout layout, Object value) { for (MemoryLayout elem : group.memberLayouts()) { assert elem.byteSize() <= 8; final long copy = elem.byteSize(); - MemorySegment slice = valueSegment.asSlice(offset, copy); - fpRegs.asSlice(currentFPOffset, copy).copyFrom(slice); + MemorySegment.copy(valueSegment, offset, fpRegs, currentFPOffset, copy); currentFPOffset += FP_SLOT_SIZE; offset += copy; } @@ -482,20 +464,18 @@ private Builder arg(Class carrier, MemoryLayout layout, Object value) { case STRUCT_REFERENCE -> { // Struct is passed indirectly via a pointer in an integer register. MemorySegment valueSegment = (MemorySegment) value; - VarHandle writer - = SharedUtils.vhPrimitiveOrAddress(MemoryAddress.class, - AArch64.C_POINTER); + VarHandle writer = AArch64.C_POINTER.varHandle(); writer.set(gpRegs.asSlice(currentGPOffset), valueSegment.address()); currentGPOffset += GP_SLOT_SIZE; } case POINTER, INTEGER -> { - VarHandle writer = SharedUtils.vhPrimitiveOrAddress(carrier, layout); + VarHandle writer = layout.varHandle(); writer.set(gpRegs.asSlice(currentGPOffset), value); currentGPOffset += GP_SLOT_SIZE; } case FLOAT -> { - VarHandle writer = layout.varHandle(carrier); + VarHandle writer = layout.varHandle(); writer.set(fpRegs.asSlice(currentFPOffset), value); currentFPOffset += FP_SLOT_SIZE; } @@ -513,7 +493,7 @@ public VaList build() { return EMPTY; } - SegmentAllocator allocator = SegmentAllocator.arenaAllocator(scope); + SegmentAllocator allocator = SegmentAllocator.newNativeArena(scope); MemorySegment vaListSegment = allocator.allocate(LAYOUT); MemoryAddress stackArgsPtr = MemoryAddress.NULL; if (!stackArgs.isEmpty()) { diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64CallArranger.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64CallArranger.java new file mode 100644 index 0000000000000..8b35583e5c3c4 --- /dev/null +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64CallArranger.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, Arm Limited. All rights reserved. + * 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.abi.aarch64.macos; + +import jdk.internal.foreign.abi.aarch64.*; + +/** + * AArch64 CallArranger specialized for macOS ABI. + */ +public class MacOsAArch64CallArranger extends CallArranger { + + @Override + protected boolean varArgsOnStack() { + // Variadic arguments are always passed on the stack + return true; + } + +} diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64Linker.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64Linker.java index 1e644bf876027..13b30a0f68fdf 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64Linker.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64Linker.java @@ -25,16 +25,14 @@ */ package jdk.internal.foreign.abi.aarch64.macos; -import jdk.incubator.foreign.Addressable; +import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.FunctionDescriptor; import jdk.incubator.foreign.MemoryAddress; -import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.NativeSymbol; import jdk.incubator.foreign.ResourceScope; -import jdk.internal.foreign.AbstractCLinker; -import jdk.internal.foreign.ResourceScopeImpl; +import jdk.incubator.foreign.VaList; import jdk.internal.foreign.abi.SharedUtils; -import jdk.internal.foreign.abi.UpcallStubs; import jdk.internal.foreign.abi.aarch64.CallArranger; import java.lang.invoke.MethodHandle; @@ -43,32 +41,15 @@ import java.util.Objects; import java.util.function.Consumer; -import static jdk.internal.foreign.PlatformLayouts.*; - /** * ABI implementation for macOS on Apple silicon. Based on AAPCS with * changes to va_list and passing arguments on the stack. */ -public final class MacOsAArch64Linker extends AbstractCLinker { +public final class MacOsAArch64Linker implements CLinker { private static MacOsAArch64Linker instance; static final long ADDRESS_SIZE = 64; // bits - private static final MethodHandle MH_unboxVaList; - private static final MethodHandle MH_boxVaList; - - static { - try { - MethodHandles.Lookup lookup = MethodHandles.lookup(); - MH_unboxVaList = lookup.findVirtual(VaList.class, "address", - MethodType.methodType(MemoryAddress.class)); - MH_boxVaList = MethodHandles.insertArguments(lookup.findStatic(MacOsAArch64Linker.class, "newVaListOfAddress", - MethodType.methodType(VaList.class, MemoryAddress.class, ResourceScope.class)), 1, ResourceScope.globalScope()); - } catch (ReflectiveOperationException e) { - throw new ExceptionInInitializerError(e); - } - } - public static MacOsAArch64Linker getInstance() { if (instance == null) { instance = new MacOsAArch64Linker(); @@ -77,26 +58,27 @@ public static MacOsAArch64Linker getInstance() { } @Override - public final MethodHandle downcallHandle(MethodType type, FunctionDescriptor function) { - Objects.requireNonNull(type); + public final MethodHandle downcallHandle(FunctionDescriptor function) { Objects.requireNonNull(function); - MethodType llMt = SharedUtils.convertVaListCarriers(type, MacOsAArch64VaList.CARRIER); - MethodHandle handle = CallArranger.arrangeDowncall(llMt, function); + MethodType type = SharedUtils.inferMethodType(function, false); + MethodHandle handle = CallArranger.MACOS.arrangeDowncall(type, function); if (!type.returnType().equals(MemorySegment.class)) { // not returning segment, just insert a throwing allocator handle = MethodHandles.insertArguments(handle, 1, SharedUtils.THROWING_ALLOCATOR); } - handle = SharedUtils.unboxVaLists(type, handle, MH_unboxVaList); - return handle; + return SharedUtils.wrapDowncall(handle, function); } @Override - public final MemoryAddress upcallStub(MethodHandle target, FunctionDescriptor function, ResourceScope scope) { + public final NativeSymbol upcallStub(MethodHandle target, FunctionDescriptor function, ResourceScope scope) { Objects.requireNonNull(scope); Objects.requireNonNull(target); Objects.requireNonNull(function); - target = SharedUtils.boxVaLists(target, MH_boxVaList); - return UpcallStubs.upcallAddress(CallArranger.arrangeUpcall(target, target.type(), function), (ResourceScopeImpl) scope); + MethodType type = SharedUtils.inferMethodType(function, true); + if (!type.equals(target.type())) { + throw new IllegalArgumentException("Wrong method handle type: " + target.type()); + } + return CallArranger.MACOS.arrangeUpcall(target, target.type(), function, scope); } public static VaList newVaList(Consumer actions, ResourceScope scope) { diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64VaList.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64VaList.java index e456983bb16c7..022235eb776ec 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64VaList.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64VaList.java @@ -26,7 +26,7 @@ package jdk.internal.foreign.abi.aarch64.macos; import jdk.incubator.foreign.*; -import jdk.incubator.foreign.CLinker.VaList; +import jdk.internal.foreign.Scoped; import jdk.internal.foreign.ResourceScopeImpl; import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.SharedUtils.SimpleVaArg; @@ -45,10 +45,10 @@ * parameters are passed on the stack and the type of va_list decays to * char* instead of the structure defined in the AAPCS. */ -public non-sealed class MacOsAArch64VaList implements VaList { +public non-sealed class MacOsAArch64VaList implements VaList, Scoped { public static final Class CARRIER = MemoryAddress.class; private static final long VA_SLOT_SIZE_BYTES = 8; - private static final VarHandle VH_address = MemoryHandles.asAddressVarHandle(C_POINTER.varHandle(long.class)); + private static final VarHandle VH_address = C_POINTER.varHandle(); private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemoryAddress.NULL); @@ -65,65 +65,59 @@ public static final VaList empty() { } @Override - public int vargAsInt(MemoryLayout layout) { + public int nextVarg(ValueLayout.OfInt layout) { return (int) read(int.class, layout); } @Override - public long vargAsLong(MemoryLayout layout) { + public long nextVarg(ValueLayout.OfLong layout) { return (long) read(long.class, layout); } @Override - public double vargAsDouble(MemoryLayout layout) { + public double nextVarg(ValueLayout.OfDouble layout) { return (double) read(double.class, layout); } @Override - public MemoryAddress vargAsAddress(MemoryLayout layout) { + public MemoryAddress nextVarg(ValueLayout.OfAddress layout) { return (MemoryAddress) read(MemoryAddress.class, layout); } @Override - public MemorySegment vargAsSegment(MemoryLayout layout, SegmentAllocator allocator) { + public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) { Objects.requireNonNull(allocator); return (MemorySegment) read(MemorySegment.class, layout, allocator); } - @Override - public MemorySegment vargAsSegment(MemoryLayout layout, ResourceScope scope) { - return vargAsSegment(layout, SegmentAllocator.ofScope(scope)); - } - private Object read(Class carrier, MemoryLayout layout) { return read(carrier, layout, SharedUtils.THROWING_ALLOCATOR); } private Object read(Class carrier, MemoryLayout layout, SegmentAllocator allocator) { Objects.requireNonNull(layout); - SharedUtils.checkCompatibleType(carrier, layout, MacOsAArch64Linker.ADDRESS_SIZE); Object res; if (carrier == MemorySegment.class) { TypeClass typeClass = TypeClass.classifyLayout(layout); res = switch (typeClass) { case STRUCT_REFERENCE -> { MemoryAddress structAddr = (MemoryAddress) VH_address.get(segment); - MemorySegment struct = structAddr.asSegment(layout.byteSize(), scope()); + MemorySegment struct = MemorySegment.ofAddress(structAddr, layout.byteSize(), scope()); MemorySegment seg = allocator.allocate(layout); seg.copyFrom(struct); segment = segment.asSlice(VA_SLOT_SIZE_BYTES); yield seg; } case STRUCT_REGISTER, STRUCT_HFA -> { - MemorySegment struct = allocator.allocate(layout); - struct.copyFrom(segment.asSlice(0L, layout.byteSize())); + MemorySegment struct = allocator.allocate(layout) + .copyFrom(segment.asSlice(0, layout.byteSize())); segment = segment.asSlice(alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES)); yield struct; } default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass); }; } else { - VarHandle reader = SharedUtils.vhPrimitiveOrAddress(carrier, layout); + VarHandle reader = layout.varHandle(); res = reader.get(segment); segment = segment.asSlice(VA_SLOT_SIZE_BYTES); } @@ -133,6 +127,7 @@ private Object read(Class carrier, MemoryLayout layout, SegmentAllocator allo @Override public void skip(MemoryLayout... layouts) { Objects.requireNonNull(layouts); + ((ResourceScopeImpl)scope).checkValidStateSlow(); for (MemoryLayout layout : layouts) { Objects.requireNonNull(layout); @@ -144,7 +139,7 @@ public void skip(MemoryLayout... layouts) { } static MacOsAArch64VaList ofAddress(MemoryAddress addr, ResourceScope scope) { - MemorySegment segment = addr.asSegment(Long.MAX_VALUE, scope); + MemorySegment segment = MemorySegment.ofAddress(addr, Long.MAX_VALUE, scope); return new MacOsAArch64VaList(segment, scope); } @@ -181,33 +176,32 @@ public Builder(ResourceScope scope) { private Builder arg(Class carrier, MemoryLayout layout, Object value) { Objects.requireNonNull(layout); Objects.requireNonNull(value); - SharedUtils.checkCompatibleType(carrier, layout, MacOsAArch64Linker.ADDRESS_SIZE); args.add(new SimpleVaArg(carrier, layout, value)); return this; } @Override - public Builder vargFromInt(ValueLayout layout, int value) { + public Builder addVarg(ValueLayout.OfInt layout, int value) { return arg(int.class, layout, value); } @Override - public Builder vargFromLong(ValueLayout layout, long value) { + public Builder addVarg(ValueLayout.OfLong layout, long value) { return arg(long.class, layout, value); } @Override - public Builder vargFromDouble(ValueLayout layout, double value) { + public Builder addVarg(ValueLayout.OfDouble layout, double value) { return arg(double.class, layout, value); } @Override - public Builder vargFromAddress(ValueLayout layout, Addressable value) { + public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) { return arg(MemoryAddress.class, layout, value.address()); } @Override - public Builder vargFromSegment(GroupLayout layout, MemorySegment value) { + public Builder addVarg(GroupLayout layout, MemorySegment value) { return arg(MemorySegment.class, layout, value); } @@ -216,7 +210,7 @@ public VaList build() { return EMPTY; } - SegmentAllocator allocator = SegmentAllocator.arenaAllocator(scope); + SegmentAllocator allocator = SegmentAllocator.newNativeArena(scope); // Each argument may occupy up to four slots MemorySegment segment = allocator.allocate(VA_SLOT_SIZE_BYTES * args.size() * 4); @@ -237,10 +231,9 @@ public VaList build() { VH_address.set(cursor, copy.address()); cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES); } - case STRUCT_REGISTER, STRUCT_HFA -> { - cursor.copyFrom(msArg.asSlice(0, arg.layout.byteSize())); - cursor = cursor.asSlice(alignUp(arg.layout.byteSize(), VA_SLOT_SIZE_BYTES)); - } + case STRUCT_REGISTER, STRUCT_HFA -> + cursor.copyFrom(msArg.asSlice(0, arg.layout.byteSize())) + .asSlice(alignUp(arg.layout.byteSize(), VA_SLOT_SIZE_BYTES)); default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass); } } else { diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/CallArranger.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/CallArranger.java index f7f2ddd813929..188106c2e6fbf 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/CallArranger.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/CallArranger.java @@ -30,8 +30,9 @@ import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.NativeSymbol; +import jdk.incubator.foreign.ResourceScope; import jdk.internal.foreign.abi.CallingSequenceBuilder; -import jdk.internal.foreign.abi.UpcallHandler; import jdk.internal.foreign.abi.ABIDescriptor; import jdk.internal.foreign.abi.Binding; import jdk.internal.foreign.abi.CallingSequence; @@ -86,8 +87,6 @@ public static class Bindings { } public static Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall) { - SharedUtils.checkFunctionTypes(mt, cDesc, SysVx64Linker.ADDRESS_SIZE); - CallingSequenceBuilder csb = new CallingSequenceBuilder(forUpcall); BindingCalculator argCalc = forUpcall ? new BoxBindingCalculator(true) : new UnboxBindingCalculator(true); @@ -134,14 +133,14 @@ public static MethodHandle arrangeDowncall(MethodType mt, FunctionDescriptor cDe return handle; } - public static UpcallHandler arrangeUpcall(MethodHandle target, MethodType mt, FunctionDescriptor cDesc) { + public static NativeSymbol arrangeUpcall(MethodHandle target, MethodType mt, FunctionDescriptor cDesc, ResourceScope scope) { Bindings bindings = getBindings(mt, cDesc, true); if (bindings.isInMemoryReturn) { target = SharedUtils.adaptUpcallForIMR(target, true /* drop return, since we don't have bindings for it */); } - return ProgrammableUpcallHandler.make(CSysV, target, bindings.callingSequence); + return ProgrammableUpcallHandler.make(CSysV, target, bindings.callingSequence, scope); } private static boolean isInMemoryReturn(Optional returnLayout) { @@ -239,7 +238,7 @@ void incrementRegisterCount(int type) { } } - static abstract class BindingCalculator { + abstract static class BindingCalculator { protected final StorageCalculator storageCalculator; protected BindingCalculator(boolean forArguments) { @@ -280,7 +279,7 @@ List getBindings(Class carrier, MemoryLayout layout) { break; } case POINTER: { - bindings.unboxAddress(); + bindings.unboxAddress(carrier); VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER); bindings.vmStore(storage, long.class); break; diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVVaList.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVVaList.java index 4a74ebfa0d5cb..66fe2b97c4d51 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVVaList.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVVaList.java @@ -26,27 +26,25 @@ package jdk.internal.foreign.abi.x64.sysv; import jdk.incubator.foreign.*; +import jdk.internal.foreign.ResourceScopeImpl; +import jdk.internal.foreign.Scoped; import jdk.internal.foreign.Utils; import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.misc.Unsafe; import java.lang.invoke.VarHandle; -import java.lang.ref.Cleaner; -import java.nio.ByteOrder; import java.util.ArrayList; import java.util.List; import java.util.Objects; import static jdk.internal.foreign.PlatformLayouts.SysV; -import static jdk.incubator.foreign.CLinker.VaList; + import static jdk.incubator.foreign.MemoryLayout.PathElement.groupElement; import static jdk.internal.foreign.abi.SharedUtils.SimpleVaArg; import static jdk.internal.foreign.abi.SharedUtils.THROWING_ALLOCATOR; -import static jdk.internal.foreign.abi.SharedUtils.checkCompatibleType; -import static jdk.internal.foreign.abi.SharedUtils.vhPrimitiveOrAddress; // See https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf "3.5.7 Variable Argument Lists" -public non-sealed class SysVVaList implements VaList { +public non-sealed class SysVVaList implements VaList, Scoped { private static final Unsafe U = Unsafe.getUnsafe(); static final Class CARRIER = MemoryAddress.class; @@ -67,8 +65,8 @@ public non-sealed class SysVVaList implements VaList { SysV.C_POINTER.withName("reg_save_area") ).withName("__va_list_tag"); - private static final MemoryLayout GP_REG = MemoryLayout.valueLayout(64, ByteOrder.nativeOrder()); - private static final MemoryLayout FP_REG = MemoryLayout.valueLayout(128, ByteOrder.nativeOrder()); + private static final MemoryLayout GP_REG = MemoryLayout.paddingLayout(64).withBitAlignment(64); + private static final MemoryLayout FP_REG = MemoryLayout.paddingLayout(128).withBitAlignment(128); private static final GroupLayout LAYOUT_REG_SAVE_AREA = MemoryLayout.structLayout( GP_REG.withName("%rdi"), @@ -105,14 +103,11 @@ public non-sealed class SysVVaList implements VaList { private static final int MAX_GP_OFFSET = (int) FP_OFFSET; // 6 regs used private static final int MAX_FP_OFFSET = (int) LAYOUT_REG_SAVE_AREA.byteSize(); // 8 16 byte regs - private static final VarHandle VH_fp_offset = LAYOUT.varHandle(int.class, groupElement("fp_offset")); - private static final VarHandle VH_gp_offset = LAYOUT.varHandle(int.class, groupElement("gp_offset")); - private static final VarHandle VH_overflow_arg_area - = MemoryHandles.asAddressVarHandle(LAYOUT.varHandle(long.class, groupElement("overflow_arg_area"))); - private static final VarHandle VH_reg_save_area - = MemoryHandles.asAddressVarHandle(LAYOUT.varHandle(long.class, groupElement("reg_save_area"))); + private static final VarHandle VH_fp_offset = LAYOUT.varHandle(groupElement("fp_offset")); + private static final VarHandle VH_gp_offset = LAYOUT.varHandle(groupElement("gp_offset")); + private static final VarHandle VH_overflow_arg_area = LAYOUT.varHandle(groupElement("overflow_arg_area")); + private static final VarHandle VH_reg_save_area = LAYOUT.varHandle(groupElement("reg_save_area")); - private static final Cleaner cleaner = Cleaner.create(); private static final VaList EMPTY = new SharedUtils.EmptyVaList(emptyListAddress()); private final MemorySegment segment; @@ -130,9 +125,10 @@ private static SysVVaList readFromSegment(MemorySegment segment) { private static MemoryAddress emptyListAddress() { long ptr = U.allocateMemory(LAYOUT.byteSize()); - MemorySegment base = MemoryAddress.ofLong(ptr).asSegment( - LAYOUT.byteSize(), () -> U.freeMemory(ptr), ResourceScope.newSharedScope()); - cleaner.register(SysVVaList.class, () -> base.scope().close()); + ResourceScope scope = ResourceScope.newImplicitScope(); + scope.addCloseAction(() -> U.freeMemory(ptr)); + MemorySegment base = MemorySegment.ofAddress(MemoryAddress.ofLong(ptr), + LAYOUT.byteSize(), scope); VH_gp_offset.set(base, MAX_GP_OFFSET); VH_fp_offset.set(base, MAX_FP_OFFSET); VH_overflow_arg_area.set(base, MemoryAddress.NULL); @@ -173,7 +169,7 @@ private MemorySegment regSaveArea() { } private static MemorySegment getRegSaveArea(MemorySegment segment) { - return ((MemoryAddress)VH_reg_save_area.get(segment)).asSegment( + return MemorySegment.ofAddress(((MemoryAddress)VH_reg_save_area.get(segment)), LAYOUT_REG_SAVE_AREA.byteSize(), segment.scope()); } @@ -188,59 +184,53 @@ private void postAlignStack(MemoryLayout layout) { } @Override - public int vargAsInt(MemoryLayout layout) { + public int nextVarg(ValueLayout.OfInt layout) { return (int) read(int.class, layout); } @Override - public long vargAsLong(MemoryLayout layout) { + public long nextVarg(ValueLayout.OfLong layout) { return (long) read(long.class, layout); } @Override - public double vargAsDouble(MemoryLayout layout) { + public double nextVarg(ValueLayout.OfDouble layout) { return (double) read(double.class, layout); } @Override - public MemoryAddress vargAsAddress(MemoryLayout layout) { + public MemoryAddress nextVarg(ValueLayout.OfAddress layout) { return (MemoryAddress) read(MemoryAddress.class, layout); } @Override - public MemorySegment vargAsSegment(MemoryLayout layout, SegmentAllocator allocator) { + public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) { Objects.requireNonNull(allocator); return (MemorySegment) read(MemorySegment.class, layout, allocator); } - @Override - public MemorySegment vargAsSegment(MemoryLayout layout, ResourceScope scope) { - return vargAsSegment(layout, SegmentAllocator.ofScope(scope)); - } - private Object read(Class carrier, MemoryLayout layout) { return read(carrier, layout, THROWING_ALLOCATOR); } private Object read(Class carrier, MemoryLayout layout, SegmentAllocator allocator) { Objects.requireNonNull(layout); - checkCompatibleType(carrier, layout, SysVx64Linker.ADDRESS_SIZE); TypeClass typeClass = TypeClass.classifyLayout(layout); if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass) || typeClass.inMemory()) { preAlignStack(layout); return switch (typeClass.kind()) { case STRUCT -> { - MemorySegment slice = stackPtr().asSegment(layout.byteSize(), scope()); + MemorySegment slice = MemorySegment.ofAddress(stackPtr(), layout.byteSize(), scope()); MemorySegment seg = allocator.allocate(layout); seg.copyFrom(slice); postAlignStack(layout); yield seg; } case POINTER, INTEGER, FLOAT -> { - VarHandle reader = vhPrimitiveOrAddress(carrier, layout); + VarHandle reader = layout.varHandle(); try (ResourceScope localScope = ResourceScope.newConfinedScope()) { - MemorySegment slice = stackPtr().asSegment(layout.byteSize(), localScope); + MemorySegment slice = MemorySegment.ofAddress(stackPtr(), layout.byteSize(), localScope); Object res = reader.get(slice); postAlignStack(layout); yield res; @@ -256,12 +246,11 @@ private Object read(Class carrier, MemoryLayout layout, SegmentAllocator allo while (offset < layout.byteSize()) { final long copy = Math.min(layout.byteSize() - offset, 8); boolean isSSE = typeClass.classes.get(classIdx++) == ArgumentClassImpl.SSE; - MemorySegment slice = value.asSlice(offset, copy); if (isSSE) { - slice.copyFrom(regSaveArea.asSlice(currentFPOffset(), copy)); + MemorySegment.copy(regSaveArea, currentFPOffset(), value, offset, copy); currentFPOffset(currentFPOffset() + FP_SLOT_SIZE); } else { - slice.copyFrom(regSaveArea.asSlice(currentGPOffset(), copy)); + MemorySegment.copy(regSaveArea, currentGPOffset(), value, offset, copy); currentGPOffset(currentGPOffset() + GP_SLOT_SIZE); } offset += copy; @@ -269,13 +258,13 @@ private Object read(Class carrier, MemoryLayout layout, SegmentAllocator allo yield value; } case POINTER, INTEGER -> { - VarHandle reader = SharedUtils.vhPrimitiveOrAddress(carrier, layout); + VarHandle reader = layout.varHandle(); Object res = reader.get(regSaveArea.asSlice(currentGPOffset())); currentGPOffset(currentGPOffset() + GP_SLOT_SIZE); yield res; } case FLOAT -> { - VarHandle reader = layout.varHandle(carrier); + VarHandle reader = layout.varHandle(); Object res = reader.get(regSaveArea.asSlice(currentFPOffset())); currentFPOffset(currentFPOffset() + FP_SLOT_SIZE); yield res; @@ -287,6 +276,7 @@ private Object read(Class carrier, MemoryLayout layout, SegmentAllocator allo @Override public void skip(MemoryLayout... layouts) { Objects.requireNonNull(layouts); + ((ResourceScopeImpl)segment.scope()).checkValidStateSlow(); for (MemoryLayout layout : layouts) { Objects.requireNonNull(layout); TypeClass typeClass = TypeClass.classifyLayout(layout); @@ -305,7 +295,7 @@ static SysVVaList.Builder builder(ResourceScope scope) { } public static VaList ofAddress(MemoryAddress ma, ResourceScope scope) { - return readFromSegment(ma.asSegment(LAYOUT.byteSize(), scope)); + return readFromSegment(MemorySegment.ofAddress(ma, LAYOUT.byteSize(), scope)); } @Override @@ -353,34 +343,33 @@ public Builder(ResourceScope scope) { } @Override - public Builder vargFromInt(ValueLayout layout, int value) { + public Builder addVarg(ValueLayout.OfInt layout, int value) { return arg(int.class, layout, value); } @Override - public Builder vargFromLong(ValueLayout layout, long value) { + public Builder addVarg(ValueLayout.OfLong layout, long value) { return arg(long.class, layout, value); } @Override - public Builder vargFromDouble(ValueLayout layout, double value) { + public Builder addVarg(ValueLayout.OfDouble layout, double value) { return arg(double.class, layout, value); } @Override - public Builder vargFromAddress(ValueLayout layout, Addressable value) { + public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) { return arg(MemoryAddress.class, layout, value.address()); } @Override - public Builder vargFromSegment(GroupLayout layout, MemorySegment value) { + public Builder addVarg(GroupLayout layout, MemorySegment value) { return arg(MemorySegment.class, layout, value); } private Builder arg(Class carrier, MemoryLayout layout, Object value) { Objects.requireNonNull(layout); Objects.requireNonNull(value); - checkCompatibleType(carrier, layout, SysVx64Linker.ADDRESS_SIZE); TypeClass typeClass = TypeClass.classifyLayout(layout); if (isRegOverflow(currentGPOffset, currentFPOffset, typeClass) || typeClass.inMemory()) { @@ -395,24 +384,23 @@ private Builder arg(Class carrier, MemoryLayout layout, Object value) { while (offset < layout.byteSize()) { final long copy = Math.min(layout.byteSize() - offset, 8); boolean isSSE = typeClass.classes.get(classIdx++) == ArgumentClassImpl.SSE; - MemorySegment slice = valueSegment.asSlice(offset, copy); if (isSSE) { - reg_save_area.asSlice(currentFPOffset, copy).copyFrom(slice); + MemorySegment.copy(valueSegment, offset, reg_save_area, currentFPOffset, copy); currentFPOffset += FP_SLOT_SIZE; } else { - reg_save_area.asSlice(currentGPOffset, copy).copyFrom(slice); + MemorySegment.copy(valueSegment, offset, reg_save_area, currentGPOffset, copy); currentGPOffset += GP_SLOT_SIZE; } offset += copy; } } case POINTER, INTEGER -> { - VarHandle writer = SharedUtils.vhPrimitiveOrAddress(carrier, layout); + VarHandle writer = layout.varHandle(); writer.set(reg_save_area.asSlice(currentGPOffset), value); currentGPOffset += GP_SLOT_SIZE; } case FLOAT -> { - VarHandle writer = layout.varHandle(carrier); + VarHandle writer = layout.varHandle(); writer.set(reg_save_area.asSlice(currentFPOffset), value); currentFPOffset += FP_SLOT_SIZE; } @@ -430,7 +418,7 @@ public VaList build() { return EMPTY; } - SegmentAllocator allocator = SegmentAllocator.arenaAllocator(scope); + SegmentAllocator allocator = SegmentAllocator.newNativeArena(scope); MemorySegment vaListSegment = allocator.allocate(LAYOUT); MemoryAddress stackArgsPtr = MemoryAddress.NULL; if (!stackArgs.isEmpty()) { diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVx64Linker.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVx64Linker.java index 10d4a61c11ec9..0192168378c20 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVx64Linker.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVx64Linker.java @@ -25,14 +25,14 @@ package jdk.internal.foreign.abi.x64.sysv; +import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.FunctionDescriptor; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.NativeSymbol; import jdk.incubator.foreign.ResourceScope; -import jdk.internal.foreign.AbstractCLinker; -import jdk.internal.foreign.ResourceScopeImpl; +import jdk.incubator.foreign.VaList; import jdk.internal.foreign.abi.SharedUtils; -import jdk.internal.foreign.abi.UpcallStubs; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -43,7 +43,7 @@ /** * ABI implementation based on System V ABI AMD64 supplement v.0.99.6 */ -public final class SysVx64Linker extends AbstractCLinker { +public final class SysVx64Linker implements CLinker { public static final int MAX_INTEGER_ARGUMENT_REGISTERS = 6; public static final int MAX_INTEGER_RETURN_REGISTERS = 2; public static final int MAX_VECTOR_ARGUMENT_REGISTERS = 8; @@ -54,21 +54,6 @@ public final class SysVx64Linker extends AbstractCLinker { static final long ADDRESS_SIZE = 64; // bits - private static final MethodHandle MH_unboxVaList; - private static final MethodHandle MH_boxVaList; - - static { - try { - MethodHandles.Lookup lookup = MethodHandles.lookup(); - MH_unboxVaList = lookup.findVirtual(VaList.class, "address", - MethodType.methodType(MemoryAddress.class)); - MH_boxVaList = MethodHandles.insertArguments(lookup.findStatic(SysVx64Linker.class, "newVaListOfAddress", - MethodType.methodType(VaList.class, MemoryAddress.class, ResourceScope.class)), 1, ResourceScope.globalScope()); - } catch (ReflectiveOperationException e) { - throw new ExceptionInInitializerError(e); - } - } - public static SysVx64Linker getInstance() { if (instance == null) { instance = new SysVx64Linker(); @@ -83,26 +68,28 @@ public static VaList newVaList(Consumer actions, ResourceScope s } @Override - public final MethodHandle downcallHandle(MethodType type, FunctionDescriptor function) { - Objects.requireNonNull(type); + public final MethodHandle downcallHandle(FunctionDescriptor function) { Objects.requireNonNull(function); - MethodType llMt = SharedUtils.convertVaListCarriers(type, SysVVaList.CARRIER); - MethodHandle handle = CallArranger.arrangeDowncall(llMt, function); + MethodType type = SharedUtils.inferMethodType(function, false); + MethodHandle handle = CallArranger.arrangeDowncall(type, function); if (!type.returnType().equals(MemorySegment.class)) { // not returning segment, just insert a throwing allocator handle = MethodHandles.insertArguments(handle, 1, SharedUtils.THROWING_ALLOCATOR); } - handle = SharedUtils.unboxVaLists(type, handle, MH_unboxVaList); - return handle; + return SharedUtils.wrapDowncall(handle, function); } @Override - public final MemoryAddress upcallStub(MethodHandle target, FunctionDescriptor function, ResourceScope scope) { + public final NativeSymbol upcallStub(MethodHandle target, FunctionDescriptor function, ResourceScope scope) { Objects.requireNonNull(scope); Objects.requireNonNull(target); Objects.requireNonNull(function); - target = SharedUtils.boxVaLists(target, MH_boxVaList); - return UpcallStubs.upcallAddress(CallArranger.arrangeUpcall(target, target.type(), function), (ResourceScopeImpl) scope); + SharedUtils.checkExceptions(target); + MethodType type = SharedUtils.inferMethodType(function, true); + if (!type.equals(target.type())) { + throw new IllegalArgumentException("Wrong method handle type: " + target.type()); + } + return CallArranger.arrangeUpcall(target, target.type(), function, scope); } public static VaList newVaListOfAddress(MemoryAddress ma, ResourceScope scope) { diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/TypeClass.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/TypeClass.java index 568b3d6378a4d..1421072d50164 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/TypeClass.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/sysv/TypeClass.java @@ -25,10 +25,10 @@ package jdk.internal.foreign.abi.x64.sysv; import jdk.incubator.foreign.GroupLayout; +import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.SequenceLayout; import jdk.incubator.foreign.ValueLayout; -import jdk.internal.foreign.PlatformLayouts; import jdk.internal.foreign.Utils; import java.util.ArrayList; @@ -107,11 +107,17 @@ private static List createMemoryClassArray(long size) { } private static ArgumentClassImpl argumentClassFor(MemoryLayout layout) { - return switch (PlatformLayouts.getKind(layout)) { - case CHAR, SHORT, INT, LONG, LONG_LONG -> ArgumentClassImpl.INTEGER; - case FLOAT, DOUBLE -> ArgumentClassImpl.SSE; - case POINTER -> ArgumentClassImpl.POINTER; - }; + Class carrier = ((ValueLayout)layout).carrier(); + if (carrier == boolean.class || carrier == byte.class || carrier == char.class || + carrier == short.class || carrier == int.class || carrier == long.class) { + return ArgumentClassImpl.INTEGER; + } else if (carrier == float.class || carrier == double.class) { + return ArgumentClassImpl.SSE; + } else if (carrier == MemoryAddress.class) { + return ArgumentClassImpl.POINTER; + } else { + throw new IllegalStateException("Cannot get here: " + carrier.getName()); + } } // TODO: handle zero length arrays diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/CallArranger.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/CallArranger.java index 5ff77e3928a1a..df5fa2b08a9a9 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/CallArranger.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/CallArranger.java @@ -29,9 +29,10 @@ import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.NativeSymbol; +import jdk.incubator.foreign.ResourceScope; import jdk.internal.foreign.Utils; import jdk.internal.foreign.abi.CallingSequenceBuilder; -import jdk.internal.foreign.abi.UpcallHandler; import jdk.internal.foreign.abi.ABIDescriptor; import jdk.internal.foreign.abi.Binding; import jdk.internal.foreign.abi.CallingSequence; @@ -82,8 +83,6 @@ public static class Bindings { } public static Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall) { - SharedUtils.checkFunctionTypes(mt, cDesc, Windowsx64Linker.ADDRESS_SIZE); - class CallingSequenceBuilderHelper { final CallingSequenceBuilder csb = new CallingSequenceBuilder(forUpcall); final BindingCalculator argCalc = @@ -91,12 +90,12 @@ class CallingSequenceBuilderHelper { final BindingCalculator retCalc = forUpcall ? new UnboxBindingCalculator(false) : new BoxBindingCalculator(false); - void addArgumentBindings(Class carrier, MemoryLayout layout) { - csb.addArgumentBindings(carrier, layout, argCalc.getBindings(carrier, layout)); + void addArgumentBindings(Class carrier, MemoryLayout layout, boolean isVararg) { + csb.addArgumentBindings(carrier, layout, argCalc.getBindings(carrier, layout, isVararg)); } void setReturnBindings(Class carrier, MemoryLayout layout) { - csb.setReturnBindings(carrier, layout, retCalc.getBindings(carrier, layout)); + csb.setReturnBindings(carrier, layout, retCalc.getBindings(carrier, layout, false)); } } var csb = new CallingSequenceBuilderHelper(); @@ -105,7 +104,7 @@ void setReturnBindings(Class carrier, MemoryLayout layout) { if (returnInMemory) { Class carrier = MemoryAddress.class; MemoryLayout layout = Win64.C_POINTER; - csb.addArgumentBindings(carrier, layout); + csb.addArgumentBindings(carrier, layout, false); if (forUpcall) { csb.setReturnBindings(carrier, layout); } @@ -114,7 +113,7 @@ void setReturnBindings(Class carrier, MemoryLayout layout) { } for (int i = 0; i < mt.parameterCount(); i++) { - csb.addArgumentBindings(mt.parameterType(i), cDesc.argumentLayouts().get(i)); + csb.addArgumentBindings(mt.parameterType(i), cDesc.argumentLayouts().get(i), SharedUtils.isVarargsIndex(cDesc, i)); } csb.csb.setTrivial(SharedUtils.isTrivial(cDesc)); @@ -134,14 +133,14 @@ public static MethodHandle arrangeDowncall(MethodType mt, FunctionDescriptor cDe return handle; } - public static UpcallHandler arrangeUpcall(MethodHandle target, MethodType mt, FunctionDescriptor cDesc) { + public static NativeSymbol arrangeUpcall(MethodHandle target, MethodType mt, FunctionDescriptor cDesc, ResourceScope scope) { Bindings bindings = getBindings(mt, cDesc, true); if (bindings.isInMemoryReturn) { target = SharedUtils.adaptUpcallForIMR(target, false /* need the return value as well */); } - return ProgrammableUpcallHandler.make(CWindows, target, bindings.callingSequence); + return ProgrammableUpcallHandler.make(CWindows, target, bindings.callingSequence, scope); } private static boolean isInMemoryReturn(Optional returnLayout) { @@ -185,7 +184,7 @@ public VMStorage extraVarargsStorage() { } private interface BindingCalculator { - List getBindings(Class carrier, MemoryLayout layout); + List getBindings(Class carrier, MemoryLayout layout, boolean isVararg); } static class UnboxBindingCalculator implements BindingCalculator { @@ -196,8 +195,8 @@ static class UnboxBindingCalculator implements BindingCalculator { } @Override - public List getBindings(Class carrier, MemoryLayout layout) { - TypeClass argumentClass = TypeClass.typeClassFor(layout); + public List getBindings(Class carrier, MemoryLayout layout, boolean isVararg) { + TypeClass argumentClass = TypeClass.typeClassFor(layout, isVararg); Binding.Builder bindings = Binding.builder(); switch (argumentClass) { case STRUCT_REGISTER: { @@ -211,14 +210,13 @@ public List getBindings(Class carrier, MemoryLayout layout) { case STRUCT_REFERENCE: { assert carrier == MemorySegment.class; bindings.copy(layout) - .baseAddress() - .unboxAddress(); + .unboxAddress(MemorySegment.class); VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER, layout); bindings.vmStore(storage, long.class); break; } case POINTER: { - bindings.unboxAddress(); + bindings.unboxAddress(carrier); VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER, layout); bindings.vmStore(storage, long.class); break; @@ -259,8 +257,8 @@ static class BoxBindingCalculator implements BindingCalculator { } @Override - public List getBindings(Class carrier, MemoryLayout layout) { - TypeClass argumentClass = TypeClass.typeClassFor(layout); + public List getBindings(Class carrier, MemoryLayout layout, boolean isVararg) { + TypeClass argumentClass = TypeClass.typeClassFor(layout, isVararg); Binding.Builder bindings = Binding.builder(); switch (argumentClass) { case STRUCT_REGISTER: { diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/TypeClass.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/TypeClass.java index 5a96c1b008ef2..d24963fc564a9 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/TypeClass.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/TypeClass.java @@ -25,11 +25,9 @@ package jdk.internal.foreign.abi.x64.windows; import jdk.incubator.foreign.GroupLayout; +import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.ValueLayout; -import jdk.internal.foreign.PlatformLayouts; - -import static jdk.internal.foreign.PlatformLayouts.Win64.VARARGS_ATTRIBUTE_NAME; enum TypeClass { STRUCT_REGISTER, @@ -39,7 +37,7 @@ enum TypeClass { FLOAT, VARARG_FLOAT; - private static TypeClass classifyValueType(ValueLayout type) { + private static TypeClass classifyValueType(ValueLayout type, boolean isVararg) { // No 128 bit integers in the Windows C ABI. There are __m128(i|d) intrinsic types but they act just // like a struct when passing as an argument (passed by pointer). // https://docs.microsoft.com/en-us/cpp/cpp/m128?view=vs-2019 @@ -49,17 +47,21 @@ private static TypeClass classifyValueType(ValueLayout type) { // but must be considered volatile across function calls." // https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=vs-2019 - return switch (PlatformLayouts.getKind(type)) { - case CHAR, SHORT, INT, LONG, LONG_LONG -> INTEGER; - case POINTER -> POINTER; - case FLOAT, DOUBLE -> { - if (type.attribute(VARARGS_ATTRIBUTE_NAME) - .map(Boolean.class::cast).orElse(false)) { - yield VARARG_FLOAT; - } - yield FLOAT; + Class carrier = type.carrier(); + if (carrier == boolean.class || carrier == byte.class || carrier == char.class || + carrier == short.class || carrier == int.class || carrier == long.class) { + return INTEGER; + } else if (carrier == float.class || carrier == double.class) { + if (isVararg) { + return VARARG_FLOAT; + } else { + return FLOAT; } - }; + } else if (carrier == MemoryAddress.class) { + return POINTER; + } else { + throw new IllegalStateException("Cannot get here: " + carrier.getName()); + } } static boolean isRegisterAggregate(MemoryLayout type) { @@ -77,9 +79,9 @@ private static TypeClass classifyStructType(MemoryLayout layout) { return STRUCT_REFERENCE; } - static TypeClass typeClassFor(MemoryLayout type) { + static TypeClass typeClassFor(MemoryLayout type, boolean isVararg) { if (type instanceof ValueLayout) { - return classifyValueType((ValueLayout) type); + return classifyValueType((ValueLayout) type, isVararg); } else if (type instanceof GroupLayout) { return classifyStructType(type); } else { diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/WinVaList.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/WinVaList.java index debdf9c6b5d6a..f74acf568b799 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/WinVaList.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/WinVaList.java @@ -26,7 +26,7 @@ package jdk.internal.foreign.abi.x64.windows; import jdk.incubator.foreign.*; -import jdk.incubator.foreign.CLinker.VaList; +import jdk.internal.foreign.Scoped; import jdk.internal.foreign.ResourceScopeImpl; import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.SharedUtils.SimpleVaArg; @@ -55,10 +55,10 @@ // ? **(t**)((ap += sizeof(__int64)) - sizeof(__int64)) \ // : *(t* )((ap += sizeof(__int64)) - sizeof(__int64))) // -public non-sealed class WinVaList implements VaList { +public non-sealed class WinVaList implements VaList, Scoped { public static final Class CARRIER = MemoryAddress.class; private static final long VA_SLOT_SIZE_BYTES = 8; - private static final VarHandle VH_address = MemoryHandles.asAddressVarHandle(C_POINTER.varHandle(long.class)); + private static final VarHandle VH_address = C_POINTER.varHandle(); private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemoryAddress.NULL); @@ -75,63 +75,54 @@ public static final VaList empty() { } @Override - public int vargAsInt(MemoryLayout layout) { + public int nextVarg(ValueLayout.OfInt layout) { return (int) read(int.class, layout); } @Override - public long vargAsLong(MemoryLayout layout) { + public long nextVarg(ValueLayout.OfLong layout) { return (long) read(long.class, layout); } @Override - public double vargAsDouble(MemoryLayout layout) { + public double nextVarg(ValueLayout.OfDouble layout) { return (double) read(double.class, layout); } @Override - public MemoryAddress vargAsAddress(MemoryLayout layout) { + public MemoryAddress nextVarg(ValueLayout.OfAddress layout) { return (MemoryAddress) read(MemoryAddress.class, layout); } @Override - public MemorySegment vargAsSegment(MemoryLayout layout, SegmentAllocator allocator) { + public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) { Objects.requireNonNull(allocator); return (MemorySegment) read(MemorySegment.class, layout, allocator); } - @Override - public MemorySegment vargAsSegment(MemoryLayout layout, ResourceScope scope) { - return vargAsSegment(layout, SegmentAllocator.ofScope(scope)); - } - private Object read(Class carrier, MemoryLayout layout) { return read(carrier, layout, SharedUtils.THROWING_ALLOCATOR); } private Object read(Class carrier, MemoryLayout layout, SegmentAllocator allocator) { Objects.requireNonNull(layout); - SharedUtils.checkCompatibleType(carrier, layout, Windowsx64Linker.ADDRESS_SIZE); Object res; if (carrier == MemorySegment.class) { - TypeClass typeClass = TypeClass.typeClassFor(layout); + TypeClass typeClass = TypeClass.typeClassFor(layout, false); res = switch (typeClass) { case STRUCT_REFERENCE -> { MemoryAddress structAddr = (MemoryAddress) VH_address.get(segment); - MemorySegment struct = structAddr.asSegment(layout.byteSize(), scope()); + MemorySegment struct = MemorySegment.ofAddress(structAddr, layout.byteSize(), scope()); MemorySegment seg = allocator.allocate(layout); seg.copyFrom(struct); yield seg; } - case STRUCT_REGISTER -> { - MemorySegment struct = allocator.allocate(layout); - struct.copyFrom(segment.asSlice(0L, layout.byteSize())); - yield struct; - } + case STRUCT_REGISTER -> + allocator.allocate(layout).copyFrom(segment.asSlice(0, layout.byteSize())); default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass); }; } else { - VarHandle reader = SharedUtils.vhPrimitiveOrAddress(carrier, layout); + VarHandle reader = layout.varHandle(); res = reader.get(segment); } segment = segment.asSlice(VA_SLOT_SIZE_BYTES); @@ -141,12 +132,13 @@ private Object read(Class carrier, MemoryLayout layout, SegmentAllocator allo @Override public void skip(MemoryLayout... layouts) { Objects.requireNonNull(layouts); + ((ResourceScopeImpl)scope).checkValidStateSlow(); Stream.of(layouts).forEach(Objects::requireNonNull); segment = segment.asSlice(layouts.length * VA_SLOT_SIZE_BYTES); } static WinVaList ofAddress(MemoryAddress addr, ResourceScope scope) { - MemorySegment segment = addr.asSegment(Long.MAX_VALUE, scope); + MemorySegment segment = MemorySegment.ofAddress(addr, Long.MAX_VALUE, scope); return new WinVaList(segment, scope); } @@ -183,33 +175,32 @@ public Builder(ResourceScope scope) { private Builder arg(Class carrier, MemoryLayout layout, Object value) { Objects.requireNonNull(layout); Objects.requireNonNull(value); - SharedUtils.checkCompatibleType(carrier, layout, Windowsx64Linker.ADDRESS_SIZE); args.add(new SimpleVaArg(carrier, layout, value)); return this; } @Override - public Builder vargFromInt(ValueLayout layout, int value) { + public Builder addVarg(ValueLayout.OfInt layout, int value) { return arg(int.class, layout, value); } @Override - public Builder vargFromLong(ValueLayout layout, long value) { + public Builder addVarg(ValueLayout.OfLong layout, long value) { return arg(long.class, layout, value); } @Override - public Builder vargFromDouble(ValueLayout layout, double value) { + public Builder addVarg(ValueLayout.OfDouble layout, double value) { return arg(double.class, layout, value); } @Override - public Builder vargFromAddress(ValueLayout layout, Addressable value) { + public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) { return arg(MemoryAddress.class, layout, value.address()); } @Override - public Builder vargFromSegment(GroupLayout layout, MemorySegment value) { + public Builder addVarg(GroupLayout layout, MemorySegment value) { return arg(MemorySegment.class, layout, value); } @@ -217,7 +208,7 @@ public VaList build() { if (args.isEmpty()) { return EMPTY; } - SegmentAllocator allocator = SegmentAllocator.arenaAllocator(scope); + SegmentAllocator allocator = SegmentAllocator.newNativeArena(scope); MemorySegment segment = allocator.allocate(VA_SLOT_SIZE_BYTES * args.size()); List attachedSegments = new ArrayList<>(); attachedSegments.add(segment); @@ -226,7 +217,7 @@ public VaList build() { for (SimpleVaArg arg : args) { if (arg.carrier == MemorySegment.class) { MemorySegment msArg = ((MemorySegment) arg.value); - TypeClass typeClass = TypeClass.typeClassFor(arg.layout); + TypeClass typeClass = TypeClass.typeClassFor(arg.layout, false); switch (typeClass) { case STRUCT_REFERENCE -> { MemorySegment copy = allocator.allocate(arg.layout); @@ -234,10 +225,8 @@ public VaList build() { attachedSegments.add(copy); VH_address.set(cursor, copy.address()); } - case STRUCT_REGISTER -> { - MemorySegment slice = cursor.asSlice(0, VA_SLOT_SIZE_BYTES); - slice.copyFrom(msArg); - } + case STRUCT_REGISTER -> + cursor.copyFrom(msArg.asSlice(0, VA_SLOT_SIZE_BYTES)); default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass); } } else { diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/Windowsx64Linker.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/Windowsx64Linker.java index d40aabd1e7d36..6d7c8b23cacd0 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/Windowsx64Linker.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/x64/windows/Windowsx64Linker.java @@ -24,14 +24,14 @@ */ package jdk.internal.foreign.abi.x64.windows; +import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.FunctionDescriptor; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.NativeSymbol; import jdk.incubator.foreign.ResourceScope; -import jdk.internal.foreign.AbstractCLinker; -import jdk.internal.foreign.ResourceScopeImpl; +import jdk.incubator.foreign.VaList; import jdk.internal.foreign.abi.SharedUtils; -import jdk.internal.foreign.abi.UpcallStubs; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -42,7 +42,7 @@ /** * ABI implementation based on Windows ABI AMD64 supplement v.0.99.6 */ -public final class Windowsx64Linker extends AbstractCLinker { +public final class Windowsx64Linker implements CLinker { public static final int MAX_INTEGER_ARGUMENT_REGISTERS = 4; public static final int MAX_INTEGER_RETURN_REGISTERS = 1; @@ -55,21 +55,6 @@ public final class Windowsx64Linker extends AbstractCLinker { static final long ADDRESS_SIZE = 64; // bits - private static final MethodHandle MH_unboxVaList; - private static final MethodHandle MH_boxVaList; - - static { - try { - MethodHandles.Lookup lookup = MethodHandles.lookup(); - MH_unboxVaList = lookup.findVirtual(VaList.class, "address", - MethodType.methodType(MemoryAddress.class)); - MH_boxVaList = MethodHandles.insertArguments(lookup.findStatic(Windowsx64Linker.class, "newVaListOfAddress", - MethodType.methodType(VaList.class, MemoryAddress.class, ResourceScope.class)), 1, ResourceScope.globalScope()); - } catch (ReflectiveOperationException e) { - throw new ExceptionInInitializerError(e); - } - } - public static Windowsx64Linker getInstance() { if (instance == null) { instance = new Windowsx64Linker(); @@ -84,26 +69,28 @@ public static VaList newVaList(Consumer actions, ResourceScope s } @Override - public final MethodHandle downcallHandle(MethodType type, FunctionDescriptor function) { - Objects.requireNonNull(type); + public final MethodHandle downcallHandle(FunctionDescriptor function) { Objects.requireNonNull(function); - MethodType llMt = SharedUtils.convertVaListCarriers(type, WinVaList.CARRIER); - MethodHandle handle = CallArranger.arrangeDowncall(llMt, function); + MethodType type = SharedUtils.inferMethodType(function, false); + MethodHandle handle = CallArranger.arrangeDowncall(type, function); if (!type.returnType().equals(MemorySegment.class)) { // not returning segment, just insert a throwing allocator handle = MethodHandles.insertArguments(handle, 1, SharedUtils.THROWING_ALLOCATOR); } - handle = SharedUtils.unboxVaLists(type, handle, MH_unboxVaList); - return handle; + return SharedUtils.wrapDowncall(handle, function); } @Override - public final MemoryAddress upcallStub(MethodHandle target, FunctionDescriptor function, ResourceScope scope) { + public final NativeSymbol upcallStub(MethodHandle target, FunctionDescriptor function, ResourceScope scope) { Objects.requireNonNull(scope); Objects.requireNonNull(target); Objects.requireNonNull(function); - target = SharedUtils.boxVaLists(target, MH_boxVaList); - return UpcallStubs.upcallAddress(CallArranger.arrangeUpcall(target, target.type(), function), (ResourceScopeImpl) scope); + SharedUtils.checkExceptions(target); + MethodType type = SharedUtils.inferMethodType(function, true); + if (!type.equals(target.type())) { + throw new IllegalArgumentException("Wrong method handle type: " + target.type()); + } + return CallArranger.arrangeUpcall(target, target.type(), function, scope); } public static VaList newVaListOfAddress(MemoryAddress ma, ResourceScope scope) { diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractTerminal.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractTerminal.java index e92233c7b4f47..8a464a6726565 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractTerminal.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractTerminal.java @@ -55,7 +55,7 @@ public AbstractTerminal(String name, String type) throws IOException { public AbstractTerminal(String name, String type, Charset encoding, SignalHandler signalHandler) throws IOException { this.name = name; this.type = type != null ? type : "ansi"; - this.encoding = encoding != null ? encoding : Charset.defaultCharset(); + this.encoding = encoding != null ? encoding : System.out.charset(); for (Signal signal : Signal.values()) { handlers.put(signal, signalHandler); } diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java index 4458cd00b445e..4e6df7e39bf40 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java +++ b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,6 +44,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.FileTime; import java.text.MessageFormat; import java.util.*; import java.util.function.Consumer; @@ -120,14 +121,17 @@ public int hashCode() { Set entries = new LinkedHashSet<>(); // module-info.class entries need to be added/updated. - Map moduleInfos = new HashMap<>(); + Map moduleInfos = new HashMap<>(); // A paths Set for each version, where each Set contains directories // specified by the "-C" operation. Map> pathsMap = new HashMap<>(); // There's also a files array per version - Map filesMap = new HashMap<>(); + // base version is the first entry and then follow with the version given + // from the --release option in the command-line order. + // The value of each entry is the files given in the command-line order. + Map filesMap = new LinkedHashMap<>(); // Do we think this is a multi-release jar? Set to true // if --release option found followed by at least file @@ -772,15 +776,17 @@ private void expand() throws IOException { private void expand(File dir, String[] files, Set cpaths, int version) throws IOException { - if (files == null) + if (files == null) { return; + } for (int i = 0; i < files.length; i++) { File f; - if (dir == null) + if (dir == null) { f = new File(files[i]); - else + } else { f = new File(dir, files[i]); + } boolean isDir = f.isDirectory(); String name = toEntryName(f.getPath(), cpaths, isDir); @@ -801,19 +807,24 @@ private void expand(File dir, String[] files, Set cpaths, int version) if (f.isFile()) { Entry e = new Entry(f, name, false); if (isModuleInfoEntry(name)) { - moduleInfos.putIfAbsent(name, Files.readAllBytes(f.toPath())); - if (uflag) + Path path = f.toPath(); + byte[] fileContent = Files.readAllBytes(path); + ModuleInfoEntry mie = new StreamedModuleInfoEntry(name, fileContent, Files.getLastModifiedTime(path)); + moduleInfos.putIfAbsent(name, mie); + if (uflag) { entryMap.put(name, e); + } } else if (entries.add(e)) { - if (uflag) + if (uflag) { entryMap.put(name, e); + } } } else if (isDir) { Entry e = new Entry(f, name, true); if (entries.add(e)) { // utilize entryMap for the duplicate dir check even in // case of cflag == true. - // dir name confilict/duplicate could happen with -C option. + // dir name conflict/duplicate could happen with -C option. // just remove the last "e" from the "entries" (zos will fail // with "duplicated" entries), but continue expanding the // sub tree @@ -822,7 +833,12 @@ private void expand(File dir, String[] files, Set cpaths, int version) } else { entryMap.put(name, e); } - expand(f, f.list(), cpaths, version); + String[] dirFiles = f.list(); + // Ensure files list is sorted for reproducible jar content + if (dirFiles != null) { + Arrays.sort(dirFiles); + } + expand(f, dirFiles, cpaths, version); } } else { error(formatMsg("error.nosuch.fileordir", String.valueOf(f))); @@ -895,7 +911,7 @@ private boolean equalsIgnoreCase(String s, String upper) { */ boolean update(InputStream in, OutputStream out, InputStream newManifest, - Map moduleInfos, + Map moduleInfos, JarIndex jarIndex) throws IOException { ZipInputStream zis = new ZipInputStream(in); @@ -944,7 +960,7 @@ boolean update(InputStream in, OutputStream out, return false; } } else if (moduleInfos != null && isModuleInfoEntry) { - moduleInfos.putIfAbsent(name, zis.readAllBytes()); + moduleInfos.putIfAbsent(name, new StreamedModuleInfoEntry(name, zis.readAllBytes(), e.getLastModifiedTime())); } else { boolean isDir = e.isDirectory(); if (!entryMap.containsKey(name)) { // copy the old stuff @@ -1028,15 +1044,21 @@ private void addIndex(JarIndex index, ZipOutputStream zos) zos.closeEntry(); } - private void updateModuleInfo(Map moduleInfos, ZipOutputStream zos) + private void updateModuleInfo(Map moduleInfos, ZipOutputStream zos) throws IOException { String fmt = uflag ? "out.update.module-info": "out.added.module-info"; - for (Map.Entry mi : moduleInfos.entrySet()) { + for (Map.Entry mi : moduleInfos.entrySet()) { String name = mi.getKey(); - byte[] bytes = mi.getValue(); + ModuleInfoEntry mie = mi.getValue(); + byte[] bytes = mie.readAllBytes(); ZipEntry e = new ZipEntry(name); - e.setTime(System.currentTimeMillis()); + FileTime lastModified = mie.getLastModifiedTime(); + if (lastModified != null) { + e.setLastModifiedTime(lastModified); + } else { + e.setLastModifiedTime(FileTime.fromMillis(System.currentTimeMillis())); + } if (flag0) { crc32ModuleInfo(e, bytes); } @@ -1731,12 +1753,23 @@ private File createTemporaryFile(String tmpbase, String suffix) { /** * Associates a module descriptor's zip entry name along with its - * bytes and an optional URI. Used when describing modules. + * bytes and an optional URI. */ interface ModuleInfoEntry { - String name(); - Optional uriString(); - InputStream bytes() throws IOException; + String name(); + Optional uriString(); + InputStream bytes() throws IOException; + /** + * @return Returns the last modified time of the module-info.class. + * Returns null if the last modified time is unknown or cannot be + * determined. + */ + FileTime getLastModifiedTime(); + default byte[] readAllBytes() throws IOException { + try (InputStream is = bytes()) { + return is.readAllBytes(); + } + } } static class ZipFileModuleInfoEntry implements ModuleInfoEntry { @@ -1750,6 +1783,12 @@ static class ZipFileModuleInfoEntry implements ModuleInfoEntry { @Override public InputStream bytes() throws IOException { return zipFile.getInputStream(entry); } + + @Override + public FileTime getLastModifiedTime() { + return entry.getLastModifiedTime(); + } + /** Returns an optional containing the effective URI. */ @Override public Optional uriString() { String uri = (Paths.get(zipFile.getName())).toUri().toString(); @@ -1761,14 +1800,28 @@ static class ZipFileModuleInfoEntry implements ModuleInfoEntry { static class StreamedModuleInfoEntry implements ModuleInfoEntry { private final String name; private final byte[] bytes; - StreamedModuleInfoEntry(String name, byte[] bytes) { + private final FileTime lastModifiedTime; + + StreamedModuleInfoEntry(String name, byte[] bytes, FileTime lastModifiedTime) { this.name = name; this.bytes = bytes; + this.lastModifiedTime = lastModifiedTime; } @Override public String name() { return name; } @Override public InputStream bytes() throws IOException { return new ByteArrayInputStream(bytes); } + + @Override + public byte[] readAllBytes() throws IOException { + return bytes; + } + + @Override + public FileTime getLastModifiedTime() { + return lastModifiedTime; + } + /** Returns an empty optional. */ @Override public Optional uriString() { return Optional.empty(); // no URI can be derived @@ -1820,7 +1873,7 @@ private boolean describeModuleFromStream(FileInputStream fis) while ((e = zis.getNextEntry()) != null) { String ename = e.getName(); if (isModuleInfoEntry(ename)) { - infos.add(new StreamedModuleInfoEntry(ename, zis.readAllBytes())); + infos.add(new StreamedModuleInfoEntry(ename, zis.readAllBytes(), e.getLastModifiedTime())); } } } @@ -2033,14 +2086,14 @@ static String toBinaryName(String classname) { return (classname.replace('.', '/')) + ".class"; } - private boolean checkModuleInfo(byte[] moduleInfoBytes, Set entries) + private boolean checkModuleInfo(ModuleInfoEntry moduleInfoEntry, Set entries) throws IOException { boolean ok = true; - if (moduleInfoBytes != null) { // no root module-info.class if null + if (moduleInfoEntry != null) { // no root module-info.class if null try { // ModuleDescriptor.read() checks open/exported pkgs vs packages - ModuleDescriptor md = ModuleDescriptor.read(ByteBuffer.wrap(moduleInfoBytes)); + ModuleDescriptor md = ModuleDescriptor.read(moduleInfoEntry.bytes()); // A module must have the implementation class of the services it 'provides'. if (md.provides().stream().map(Provides::providers).flatMap(List::stream) .filter(p -> !entries.contains(toBinaryName(p))) @@ -2058,15 +2111,19 @@ private boolean checkModuleInfo(byte[] moduleInfoBytes, Set entries) /** * Adds extended modules attributes to the given module-info's. The given - * Map values are updated in-place. Returns false if an error occurs. + * Map values are updated in-place. */ - private void addExtendedModuleAttributes(Map moduleInfos, + private void addExtendedModuleAttributes(Map moduleInfos, Set packages) throws IOException { - for (Map.Entry e: moduleInfos.entrySet()) { - ModuleDescriptor md = ModuleDescriptor.read(ByteBuffer.wrap(e.getValue())); - e.setValue(extendedInfoBytes(md, e.getValue(), packages)); + for (Map.Entry e: moduleInfos.entrySet()) { + ModuleInfoEntry mie = e.getValue(); + byte[] bytes = mie.readAllBytes(); + ModuleDescriptor md = ModuleDescriptor.read(ByteBuffer.wrap(bytes)); + byte[] extended = extendedInfoBytes(md, bytes, packages); + // replace the entry value with the extended bytes + e.setValue(new StreamedModuleInfoEntry(mie.name(), extended, mie.getLastModifiedTime())); } } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/SnippetTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/SnippetTaglet.java index 754a5bf06182b..925359bf4ae81 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/SnippetTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/SnippetTaglet.java @@ -30,6 +30,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.stream.Collectors; import javax.lang.model.element.Element; @@ -63,6 +64,38 @@ */ public class SnippetTaglet extends BaseTaglet { + public enum Language { + + JAVA("java"), + PROPERTIES("properties"); + + private static final Map languages; + + static { + Map tmp = new HashMap<>(); + for (var language : values()) { + String id = Objects.requireNonNull(language.identifier); + if (tmp.put(id, language) != null) + throw new IllegalStateException(); // 1-1 correspondence + } + languages = Map.copyOf(tmp); + } + + Language(String id) { + identifier = id; + } + + private final String identifier; + + public static Optional of(String identifier) { + if (identifier == null) + return Optional.empty(); + return Optional.ofNullable(languages.get(identifier)); + } + + public String getIdentifier() {return identifier;} + } + public SnippetTaglet() { super(DocTree.Kind.SNIPPET, true, EnumSet.allOf(Taglet.Location.class)); } @@ -217,6 +250,19 @@ private Content generateContent(Element holder, DocTree tag, TagletWriter writer } } + String lang = null; + AttributeTree langAttr = attributes.get("lang"); + if (langAttr != null) { + lang = stringValueOf(langAttr); + } else if (containsClass) { + lang = "java"; + } else if (containsFile) { + lang = languageFromFileName(fileObject.getName()); + } + + Optional language = Language.of(lang); + + // TODO cache parsed external snippet (WeakHashMap) StyledText inlineSnippet = null; @@ -224,7 +270,7 @@ private Content generateContent(Element holder, DocTree tag, TagletWriter writer try { if (inlineContent != null) { - inlineSnippet = parse(writer.configuration().getDocResources(), inlineContent); + inlineSnippet = parse(writer.configuration().getDocResources(), language, inlineContent); } } catch (ParseException e) { var path = writer.configuration().utils.getCommentHelper(holder) @@ -239,7 +285,7 @@ private Content generateContent(Element holder, DocTree tag, TagletWriter writer try { if (externalContent != null) { - externalSnippet = parse(writer.configuration().getDocResources(), externalContent); + externalSnippet = parse(writer.configuration().getDocResources(), language, externalContent); } } catch (ParseException e) { assert fileObject != null; @@ -289,15 +335,6 @@ private Content generateContent(Element holder, DocTree tag, TagletWriter writer assert inlineSnippet != null || externalSnippet != null; StyledText text = inlineSnippet != null ? inlineSnippet : externalSnippet; - String lang = null; - AttributeTree langAttr = attributes.get("lang"); - if (langAttr != null) { - lang = stringValueOf(langAttr); - } else if (containsClass) { - lang = "java"; - } else if (containsFile) { - lang = languageFromFileName(fileObject.getName()); - } AttributeTree idAttr = attributes.get("id"); String id = idAttr == null ? null @@ -326,8 +363,8 @@ private static String diff(String inline, String external) { """.formatted(inline, external); } - private StyledText parse(Resources resources, String content) throws ParseException { - Parser.Result result = new Parser(resources).parse(content); + private StyledText parse(Resources resources, Optional language, String content) throws ParseException { + Parser.Result result = new Parser(resources).parse(language, content); result.actions().forEach(Action::perform); return result.text(); } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/snippet/Parser.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/snippet/Parser.java index 3a1fbaade375c..fea60c0d956af 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/snippet/Parser.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/snippet/Parser.java @@ -39,6 +39,7 @@ import java.util.regex.PatternSyntaxException; import jdk.javadoc.internal.doclets.toolkit.Resources; +import jdk.javadoc.internal.doclets.toolkit.taglets.SnippetTaglet; /* * Semantics of a EOL comment; plus @@ -76,10 +77,10 @@ */ public final class Parser { - // next-line tag behaves as if it were specified on the next line - - private String eolMarker; - private Matcher markedUpLine; + private static final Pattern JAVA_COMMENT = Pattern.compile( + "^(?.*)//(?\\s*@\\s*\\w+.+?)$"); + private static final Pattern PROPERTIES_COMMENT = Pattern.compile( + "^(?[ \t]*([#!].*)?)[#!](?\\s*@\\s*\\w+.+?)$"); private final Resources resources; private final MarkupParser markupParser; @@ -93,32 +94,23 @@ public Parser(Resources resources) { this.markupParser = new MarkupParser(resources); } - public Result parse(String source) throws ParseException { - return parse("//", source); + public Result parse(Optional language, String source) throws ParseException { + SnippetTaglet.Language lang = language.orElse(SnippetTaglet.Language.JAVA); + var p = switch (lang) { + case JAVA -> JAVA_COMMENT; + case PROPERTIES -> PROPERTIES_COMMENT; + }; + return parse(p, source); } /* * Newline characters in the returned text are of the \n form. */ - public Result parse(String eolMarker, String source) throws ParseException { - Objects.requireNonNull(eolMarker); + private Result parse(Pattern commentPattern, String source) throws ParseException { + Objects.requireNonNull(commentPattern); Objects.requireNonNull(source); - if (!Objects.equals(eolMarker, this.eolMarker)) { - if (eolMarker.length() < 1) { - throw new IllegalArgumentException(); - } - for (int i = 0; i < eolMarker.length(); i++) { - switch (eolMarker.charAt(i)) { - case '\f', '\n', '\r' -> throw new IllegalArgumentException(); - } - } - this.eolMarker = eolMarker; - // capture the rightmost eolMarker (e.g. "//") - // The below Pattern.compile should never throw PatternSyntaxException - Pattern pattern = Pattern.compile("^(.*)(" + Pattern.quote(eolMarker) - + "(\\s*@\\s*\\w+.+?))$"); - this.markedUpLine = pattern.matcher(""); // reusable matcher - } + + Matcher markedUpLine = commentPattern.matcher(""); // reusable matcher tags.clear(); regions.clear(); @@ -151,17 +143,17 @@ record OffsetAndLine(int offset, String line) { } if (!markedUpLine.matches()) { // (1) line = rawLine + (addLineTerminator ? "\n" : ""); } else { - String maybeMarkup = markedUpLine.group(3); + String maybeMarkup = rawLine.substring(markedUpLine.start("markup")); List parsedTags; try { parsedTags = markupParser.parse(maybeMarkup); } catch (ParseException e) { // translate error position from markup to file line - throw new ParseException(e::getMessage, markedUpLine.start(3) + e.getPosition()); + throw new ParseException(e::getMessage, markedUpLine.start("markup") + e.getPosition()); } for (Tag t : parsedTags) { t.lineSourceOffset = next.offset(); - t.markupLineOffset = markedUpLine.start(3); + t.markupLineOffset = markedUpLine.start("markup"); } thisLineTags.addAll(parsedTags); for (var tagIterator = thisLineTags.iterator(); tagIterator.hasNext(); ) { @@ -176,7 +168,7 @@ record OffsetAndLine(int offset, String line) { } // TODO: log this with NOTICE; line = rawLine + (addLineTerminator ? "\n" : ""); } else { // (3) - String payload = markedUpLine.group(1); + String payload = rawLine.substring(0, markedUpLine.end("payload")); line = payload + (addLineTerminator ? "\n" : ""); } } diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ClassFileReader.java b/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ClassFileReader.java index 1c4df796279f3..b03c175da240f 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ClassFileReader.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/ClassFileReader.java @@ -356,7 +356,8 @@ public ClassFile getClassFile(String name) throws IOException { protected ClassFile readClassFile(JarFile jarfile, JarEntry e) throws IOException { try (InputStream is = jarfile.getInputStream(e)) { ClassFile cf = ClassFile.read(is); - if (jarfile.isMultiRelease()) { + // exclude module-info.class since this jarFile is on classpath + if (jarfile.isMultiRelease() && !cf.getName().equals("module-info")) { VersionHelper.add(jarfile, e, cf); } return cf; @@ -437,5 +438,4 @@ public void remove() { throw new UnsupportedOperationException("Not supported yet."); } } - private static final String MODULE_INFO = "module-info.class"; } diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DependencyFinder.java b/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DependencyFinder.java index 514e2ef607a12..d9a07700134ad 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DependencyFinder.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DependencyFinder.java @@ -268,7 +268,14 @@ private Set waitForTasksCompleted() { } return targets; } catch (InterruptedException|ExecutionException e) { - throw new Error(e); + Throwable cause = e.getCause(); + if (cause instanceof RuntimeException x) { + throw x; + } else if (cause instanceof Error x) { + throw x; + } else { + throw new Error(e); + } } } diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/MultiReleaseException.java b/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/MultiReleaseException.java index 0341923f926ef..0943287b93398 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/MultiReleaseException.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/MultiReleaseException.java @@ -47,7 +47,7 @@ class MultiReleaseException extends RuntimeException { * The detail message array */ public MultiReleaseException(String key, Object... params) { - super(); + super(JdepsTask.getMessage(key, params)); this.key = key; this.params = params; } diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/VersionHelper.java b/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/VersionHelper.java index 9ff358a63d38d..227c9ccd264db 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/VersionHelper.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/VersionHelper.java @@ -55,15 +55,10 @@ public static void add(JarFile jarfile, JarEntry e, ClassFile cf) String version = realName.substring(len, n); assert (Integer.parseInt(version) > 8); String name = cf.getName().replace('/', '.'); - if (nameToVersion.containsKey(name)) { - if (!version.equals(nameToVersion.get(name))) { - throw new MultiReleaseException( - "err.multirelease.version.associated", - name, nameToVersion.get(name), version - ); - } - } else { - nameToVersion.put(name, version); + String v = nameToVersion.computeIfAbsent(name, _n -> version); + if (!version.equals(v)) { + throw new MultiReleaseException("err.multirelease.version.associated", + name, nameToVersion.get(name), version); } } else { throw new MultiReleaseException("err.multirelease.jar.malformed", diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/RequestEngine.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/RequestEngine.java index 49aa2bcd3f246..ff728fbf4f2a7 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/RequestEngine.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/RequestEngine.java @@ -28,7 +28,6 @@ import java.security.AccessControlContext; import java.security.AccessController; import java.security.PrivilegedAction; -import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; @@ -162,10 +161,8 @@ public static boolean removeHook(Runnable hook) { // Only to be used for JVM events. No access control contest // or check if hook already exists static void addHooks(List newEntries) { - List addEntries = new ArrayList<>(); for (RequestHook rh : newEntries) { rh.type.setEventHook(true); - addEntries.add(rh); logHook("Added", rh.type); } entries.addAll(newEntries); diff --git a/src/jdk.jfr/share/conf/jfr/default.jfc b/src/jdk.jfr/share/conf/jfr/default.jfc index b168791f479a7..7ffacfc024888 100644 --- a/src/jdk.jfr/share/conf/jfr/default.jfc +++ b/src/jdk.jfr/share/conf/jfr/default.jfc @@ -775,6 +775,11 @@ 0 ms + + true + 0 ms + + true 0 ms diff --git a/src/jdk.jfr/share/conf/jfr/profile.jfc b/src/jdk.jfr/share/conf/jfr/profile.jfc index 8d1bada4638f0..03eac2e8669e8 100644 --- a/src/jdk.jfr/share/conf/jfr/profile.jfc +++ b/src/jdk.jfr/share/conf/jfr/profile.jfc @@ -775,6 +775,11 @@ 0 ms + + true + 0 ms + + true 0 ms diff --git a/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java b/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java index eb541bba63fb2..f4248bd16cef3 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -767,6 +767,10 @@ void processSection(JmodOutputStream out, Section section, List paths) void processSection(JmodOutputStream out, Section section, Path path) throws IOException { + // Keep a sorted set of files to be processed, so that the jmod + // content is reproducible as Files.walkFileTree order is not defined + SortedMap filesToProcess = new TreeMap(); + Files.walkFileTree(path, Set.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new SimpleFileVisitor() { @Override @@ -782,14 +786,21 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) if (out.contains(section, name)) { warning("warn.ignore.duplicate.entry", name, section); } else { - try (InputStream in = Files.newInputStream(file)) { - out.writeEntry(in, section, name); - } + filesToProcess.put(name, file); } } return FileVisitResult.CONTINUE; } }); + + // Process files in sorted order for deterministic jmod content + for (Map.Entry entry : filesToProcess.entrySet()) { + String name = entry.getKey(); + Path file = entry.getValue(); + try (InputStream in = Files.newInputStream(file)) { + out.writeEntry(in, section, name); + } + } } boolean matches(Path path, List matchers) { diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppImageBuilder.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppImageBuilder.java index a12ed59c10ec1..7291d56218a21 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppImageBuilder.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppImageBuilder.java @@ -101,7 +101,7 @@ public void prepareApplicationFiles(Map params) private void createLauncherLib() throws IOException { Path path = appLayout.pathGroup().getPath( ApplicationLayout.PathRole.LINUX_APPLAUNCHER_LIB); - try (InputStream resource = getResourceAsStream("libjpackageapplauncher.so")) { + try (InputStream resource = getResourceAsStream("libjpackageapplauncheraux.so")) { writeEntry(resource, path); } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppImageBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppImageBuilder.java index f1eed5f5f1538..fd33ffbb3db61 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppImageBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppImageBuilder.java @@ -593,7 +593,6 @@ public static void addNewKeychain(Map params) Log.error(I18N.getString("message.keychain.error")); return; } - boolean contains = keychainList.stream().anyMatch( str -> str.trim().equals("\""+keyChainPath.trim()+"\"")); if (contains) { @@ -608,7 +607,9 @@ public static void addNewKeychain(Map params) if (path.startsWith("\"") && path.endsWith("\"")) { path = path.substring(1, path.length()-1); } - keyChains.add(path); + if (!keyChains.contains(path)) { + keyChains.add(path); + } }); List args = new ArrayList<>(); @@ -682,27 +683,23 @@ static void signAppBundle( Log.verbose(MessageFormat.format(I18N.getString( "message.ignoring.symlink"), p.toString())); } else { - List args; - // runtime and Framework files will be signed below - // but they need to be unsigned first here - if ((p.toString().contains("/Contents/runtime")) || - (p.toString().contains("/Contents/Frameworks"))) { - - args = new ArrayList<>(); - args.addAll(Arrays.asList("/usr/bin/codesign", - "--remove-signature", p.toString())); - try { - Set oldPermissions = - Files.getPosixFilePermissions(p); - p.toFile().setWritable(true, true); - ProcessBuilder pb = new ProcessBuilder(args); - IOUtils.exec(pb); - Files.setPosixFilePermissions(p,oldPermissions); - } catch (IOException ioe) { - Log.verbose(ioe); - toThrow.set(ioe); - return; - } + // unsign everything before signing + List args = new ArrayList<>(); + args.addAll(Arrays.asList("/usr/bin/codesign", + "--remove-signature", p.toString())); + try { + Set oldPermissions = + Files.getPosixFilePermissions(p); + p.toFile().setWritable(true, true); + ProcessBuilder pb = new ProcessBuilder(args); + // run quietly + IOUtils.exec(pb, false, null, false, + Executor.INFINITE_TIMEOUT, true); + Files.setPosixFilePermissions(p,oldPermissions); + } catch (IOException ioe) { + Log.verbose(ioe); + toThrow.set(ioe); + return; } args = new ArrayList<>(); args.addAll(Arrays.asList("/usr/bin/codesign", @@ -727,7 +724,9 @@ static void signAppBundle( Files.getPosixFilePermissions(p); p.toFile().setWritable(true, true); ProcessBuilder pb = new ProcessBuilder(args); - IOUtils.exec(pb); + // run quietly + IOUtils.exec(pb, false, null, false, + Executor.INFINITE_TIMEOUT, true); Files.setPosixFilePermissions(p, oldPermissions); } catch (IOException ioe) { toThrow.set(ioe); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java index bc32df6a9bae3..b52285bc0ff03 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java @@ -190,14 +190,24 @@ static void exec(ProcessBuilder pb, boolean testForPresenceOnly, static void exec(ProcessBuilder pb, boolean testForPresenceOnly, PrintStream consumer, boolean writeOutputToFile, long timeout) throws IOException { + exec(pb, testForPresenceOnly, consumer, writeOutputToFile, + Executor.INFINITE_TIMEOUT, false); + } + + static void exec(ProcessBuilder pb, boolean testForPresenceOnly, + PrintStream consumer, boolean writeOutputToFile, + long timeout, boolean quiet) throws IOException { List output = new ArrayList<>(); - Executor exec = Executor.of(pb).setWriteOutputToFile(writeOutputToFile) - .setTimeout(timeout).setOutputConsumer(lines -> { - lines.forEach(output::add); - if (consumer != null) { - output.forEach(consumer::println); - } - }); + Executor exec = Executor.of(pb) + .setWriteOutputToFile(writeOutputToFile) + .setTimeout(timeout) + .setQuiet(quiet) + .setOutputConsumer(lines -> { + lines.forEach(output::add); + if (consumer != null) { + output.forEach(consumer::println); + } + }); if (testForPresenceOnly) { exec.execute(); diff --git a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java index 532454b7cbcc1..b257b13236330 100644 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java @@ -274,6 +274,7 @@ private enum OptionKind { ADD_EXPORTS("--add-exports", false), ENABLE_PREVIEW("--enable-preview", true), SOURCE_RELEASE("-source", true, true, true, false, false), // virtual option, generated by --enable-preview + ENABLE_NATIVE_ACCESS("--enable-native-access", true, true, false, true, true), TO_COMPILER("-C", false, false, true, false, false), TO_REMOTE_VM("-R", false, false, false, true, false),; final String optionFlag; @@ -363,6 +364,7 @@ private class OptionParserBase { private final OptionSpec argAddModules = parser.accepts("add-modules").withRequiredArg(); private final OptionSpec argAddExports = parser.accepts("add-exports").withRequiredArg(); private final OptionSpecBuilder argEnablePreview = parser.accepts("enable-preview"); + private final OptionSpecBuilder argEnableNativeAccess = parser.accepts("enable-native-access"); private final NonOptionArgumentSpec argNonOptions = parser.nonOptions(); private Options opts = new Options(); @@ -471,6 +473,10 @@ Options parse(OptionSet options) { OptionKind.SOURCE_RELEASE.optionFlag, System.getProperty("java.specification.version"))); } + if (options.has(argEnableNativeAccess)) { + opts.addAll(OptionKind.ENABLE_NATIVE_ACCESS, List.of( + OptionKind.ENABLE_NATIVE_ACCESS.optionFlag, "ALL-UNNAMED")); + } if (failed) { exitCode = 1; diff --git a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties index dd21e32031786..8606527e557da 100644 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties @@ -207,6 +207,8 @@ where possible options include:\n\ \ --add-modules (,)*\n\ \ Specify modules to resolve, or all modules on the\n\ \ module path if is ALL-MODULE-PATHs\n\ +\ --enable-native-access\n\ +\ Allow code to run restricted native methods\n\ \ --enable-preview Allow code to depend on preview features of this release\n\ \ --startup One run replacement for the startup definitions\n\ \ --no-startup Do not run the startup definitions\n\ diff --git a/src/jdk.jshell/share/classes/jdk/jshell/execution/RemoteExecutionControl.java b/src/jdk.jshell/share/classes/jdk/jshell/execution/RemoteExecutionControl.java index 810e80acf47d9..d8818dee5417e 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/execution/RemoteExecutionControl.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/execution/RemoteExecutionControl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -63,8 +63,8 @@ public static void main(String[] args) throws Exception { InputStream inStream = socket.getInputStream(); OutputStream outStream = socket.getOutputStream(); Map> outputs = new HashMap<>(); - outputs.put("out", st -> System.setOut(new PrintStream(st, true))); - outputs.put("err", st -> System.setErr(new PrintStream(st, true))); + outputs.put("out", st -> System.setOut(new PrintStream(st, true, System.out.charset()))); + outputs.put("err", st -> System.setErr(new PrintStream(st, true, System.err.charset()))); Map> input = new HashMap<>(); input.put("in", System::setIn); forwardExecutionControlAndIO(new RemoteExecutionControl(), inStream, outStream, outputs, input); diff --git a/src/jdk.management.jfr/share/classes/jdk/management/jfr/RecordingInfo.java b/src/jdk.management.jfr/share/classes/jdk/management/jfr/RecordingInfo.java index 16a710ee14aec..8d2f1e061ccdc 100644 --- a/src/jdk.management.jfr/share/classes/jdk/management/jfr/RecordingInfo.java +++ b/src/jdk.management.jfr/share/classes/jdk/management/jfr/RecordingInfo.java @@ -300,10 +300,10 @@ public boolean isToDisk() { /** * Returns the desired duration, measured in seconds, of the recording - * associated with this {@link RecordingInfo}, or {code 0} if no duration + * associated with this {@link RecordingInfo}, or {@code 0} if no duration * has been set. * - * @return the desired duration, or {code 0} if no duration has been set + * @return the desired duration, or {@code 0} if no duration has been set * * @see Recording#getDuration() */ diff --git a/src/utils/IdealGraphVisualizer/.java-version b/src/utils/IdealGraphVisualizer/.java-version new file mode 100644 index 0000000000000..60d3b2f4a4cd5 --- /dev/null +++ b/src/utils/IdealGraphVisualizer/.java-version @@ -0,0 +1 @@ +15 diff --git a/src/utils/IdealGraphVisualizer/BatikSVGProxy/pom.xml b/src/utils/IdealGraphVisualizer/BatikSVGProxy/pom.xml deleted file mode 100644 index dccb16b356b95..0000000000000 --- a/src/utils/IdealGraphVisualizer/BatikSVGProxy/pom.xml +++ /dev/null @@ -1,85 +0,0 @@ - - - - - 4.0.0 - - IdealGraphVisualizer-parent - com.sun.hotspot.igv - 1.0-SNAPSHOT - - com.sun.hotspot.igv - BatikSVGProxy - 1.0-SNAPSHOT - nbm - BatikSVGProxy - - UTF-8 - - - - - - - org.apache.netbeans.utilities - nbm-maven-plugin - ${nbmmvnplugin.version} - true - - - com.sun.hotspot.igv.svg - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${mvncompilerplugin.version} - - 1.8 - 1.8 - - - - org.apache.maven.plugins - maven-jar-plugin - ${mvnjarplugin.version} - - - - ${project.build.outputDirectory}/META-INF/MANIFEST.MF - - - - - - diff --git a/src/utils/IdealGraphVisualizer/BatikSVGProxy/src/main/java/com/sun/hotspot/igv/svg/BatikSVG.java b/src/utils/IdealGraphVisualizer/BatikSVGProxy/src/main/java/com/sun/hotspot/igv/svg/BatikSVG.java deleted file mode 100644 index 8c3d92dc0642e..0000000000000 --- a/src/utils/IdealGraphVisualizer/BatikSVGProxy/src/main/java/com/sun/hotspot/igv/svg/BatikSVG.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ -package com.sun.hotspot.igv.svg; - -import java.awt.Graphics2D; -import java.io.Writer; -import java.io.File; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import org.w3c.dom.DOMImplementation; - -/** - * Utility class - * @author Thomas Wuerthinger - */ -public class BatikSVG { - - private BatikSVG() { - } - - private static Constructor SVGGraphics2DConstructor; - private static Method streamMethod; - private static Method createDefaultMethod; - private static Method getDOMImplementationMethod; - private static Method setEmbeddedFontsOnMethod; - private static Class classSVGGraphics2D; - - /** - * Creates a graphics object that allows to be exported to SVG data using the {@link #printToStream(Graphics2D, Writer, boolean) printToStream} method. - * @return the newly created Graphics2D object or null if the library does not exist - */ - public static Graphics2D createGraphicsObject() { - try { - if (SVGGraphics2DConstructor == null) { - String batikJar = System.getenv().get("IGV_BATIK_JAR"); - if (batikJar == null) { - return null; - } - // Load batik in it's own class loader since some it's support jars interfere with the JDK - URL url = new File(batikJar).toURI().toURL(); - ClassLoader cl = new URLClassLoader(new URL[] { url }); - Class classGenericDOMImplementation = cl.loadClass("org.apache.batik.dom.GenericDOMImplementation"); - Class classSVGGeneratorContext = cl.loadClass("org.apache.batik.svggen.SVGGeneratorContext"); - classSVGGraphics2D = cl.loadClass("org.apache.batik.svggen.SVGGraphics2D"); - getDOMImplementationMethod = classGenericDOMImplementation.getDeclaredMethod("getDOMImplementation", new Class[0]); - createDefaultMethod = classSVGGeneratorContext.getDeclaredMethod("createDefault", new Class[]{org.w3c.dom.Document.class}); - setEmbeddedFontsOnMethod = classSVGGeneratorContext.getDeclaredMethod("setEmbeddedFontsOn", new Class[]{boolean.class}); - streamMethod = classSVGGraphics2D.getDeclaredMethod("stream", Writer.class, boolean.class); - SVGGraphics2DConstructor = classSVGGraphics2D.getConstructor(classSVGGeneratorContext, boolean.class); - } - DOMImplementation dom = (DOMImplementation) getDOMImplementationMethod.invoke(null); - org.w3c.dom.Document document = dom.createDocument("http://www.w3.org/2000/svg", "svg", null); - Object ctx = createDefaultMethod.invoke(null, document); - setEmbeddedFontsOnMethod.invoke(ctx, true); - Graphics2D svgGenerator = (Graphics2D) SVGGraphics2DConstructor.newInstance(ctx, true); - return svgGenerator; - } catch (ClassNotFoundException e) { - return null; - } catch (NoSuchMethodException e) { - return null; - } catch (IllegalAccessException e) { - return null; - } catch (InvocationTargetException e) { - return null; - } catch (InstantiationException e) { - return null; - } catch (MalformedURLException e) { - return null; - } - } - - /** - * Serializes a graphics object to a stream in SVG format. - * @param svgGenerator the graphics object. Only graphics objects created by the {@link #createGraphicsObject() createGraphicsObject} method are valid. - * @param stream the stream to which the data is written - * @param useCSS whether to use CSS styles in the SVG output - */ - public static void printToStream(Graphics2D svgGenerator, Writer stream, boolean useCSS) { - assert classSVGGraphics2D != null; - assert classSVGGraphics2D.isInstance(svgGenerator); - try { - streamMethod.invoke(svgGenerator, stream, useCSS); - } catch (IllegalAccessException e) { - assert false; - } catch (InvocationTargetException e) { - assert false; - } - } -} diff --git a/src/utils/IdealGraphVisualizer/BatikSVGProxy/src/main/nbm/manifest.mf b/src/utils/IdealGraphVisualizer/BatikSVGProxy/src/main/nbm/manifest.mf deleted file mode 100644 index 175014d653e04..0000000000000 --- a/src/utils/IdealGraphVisualizer/BatikSVGProxy/src/main/nbm/manifest.mf +++ /dev/null @@ -1,5 +0,0 @@ -Manifest-Version: 1.0 -OpenIDE-Module: com.sun.hotspot.igv.svg -OpenIDE-Module-Localizing-Bundle: com/sun/hotspot/igv/svg/Bundle.properties -OpenIDE-Module-Specification-Version: 1.0 - diff --git a/src/utils/IdealGraphVisualizer/BatikSVGProxy/src/main/resources/com/sun/hotspot/igv/svg/Bundle.properties b/src/utils/IdealGraphVisualizer/BatikSVGProxy/src/main/resources/com/sun/hotspot/igv/svg/Bundle.properties deleted file mode 100644 index e579912925b2e..0000000000000 --- a/src/utils/IdealGraphVisualizer/BatikSVGProxy/src/main/resources/com/sun/hotspot/igv/svg/Bundle.properties +++ /dev/null @@ -1 +0,0 @@ -OpenIDE-Module-Name=BatikSVGProxy diff --git a/src/utils/IdealGraphVisualizer/View/pom.xml b/src/utils/IdealGraphVisualizer/View/pom.xml index 12b2a8678b41c..0b1b0d77f31b3 100644 --- a/src/utils/IdealGraphVisualizer/View/pom.xml +++ b/src/utils/IdealGraphVisualizer/View/pom.xml @@ -81,11 +81,6 @@ SelectionCoordinator ${project.version} - - com.sun.hotspot.igv - BatikSVGProxy - ${project.version} - com.sun.hotspot.igv Settings @@ -151,6 +146,21 @@ org-netbeans-api-visual ${netbeans.version} + + org.apache.xmlgraphics + batik-dom + ${batik.version} + + + org.apache.xmlgraphics + batik-svggen + ${batik.version} + + + com.github.librepdf + openpdf + ${openpdf.version} + diff --git a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/DiagramViewer.java b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/DiagramViewer.java index 42697d025fe9b..2c58ac4dd26ad 100644 --- a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/DiagramViewer.java +++ b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/DiagramViewer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ import com.sun.hotspot.igv.graph.Figure; import java.awt.Component; import java.awt.Graphics2D; +import java.awt.Rectangle; import java.util.Collection; import java.util.List; import javax.swing.JComponent; @@ -44,7 +45,7 @@ enum InteractionMode { PANNING, } - public void paint(Graphics2D svgGenerator); + public void paint(Graphics2D generator); public Lookup getLookup(); @@ -70,4 +71,6 @@ enum InteractionMode { public void setInteractionMode(InteractionMode mode); + public Rectangle getBounds(); + } diff --git a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/EditorTopComponent.java b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/EditorTopComponent.java index 76a73b9706837..cf5e965de69b5 100644 --- a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/EditorTopComponent.java +++ b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/EditorTopComponent.java @@ -36,7 +36,6 @@ import com.sun.hotspot.igv.graph.Diagram; import com.sun.hotspot.igv.graph.Figure; import com.sun.hotspot.igv.graph.services.DiagramProvider; -import com.sun.hotspot.igv.svg.BatikSVG; import com.sun.hotspot.igv.util.LookupHistory; import com.sun.hotspot.igv.util.RangeSlider; import com.sun.hotspot.igv.view.actions.*; @@ -48,10 +47,21 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.*; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.*; import javax.swing.*; import javax.swing.border.Border; +import org.apache.batik.dom.GenericDOMImplementation; +import org.apache.batik.svggen.SVGGeneratorContext; +import org.apache.batik.svggen.SVGGraphics2D; +import com.lowagie.text.Document; +import com.lowagie.text.Rectangle; +import com.lowagie.text.pdf.PdfWriter; +import com.lowagie.text.pdf.PdfContentByte; +import com.lowagie.text.pdf.PdfTemplate; +import com.lowagie.text.pdf.PdfGraphics2D; +import org.w3c.dom.DOMImplementation; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; import org.openide.actions.RedoAction; @@ -103,30 +113,14 @@ public final class EditorTopComponent extends TopComponent implements PropertyCh @Override public void export(File f) { - Graphics2D svgGenerator = BatikSVG.createGraphicsObject(); - - if (svgGenerator == null) { - NotifyDescriptor message = new NotifyDescriptor.Message("For export to SVG files the Batik SVG Toolkit must be intalled.", NotifyDescriptor.ERROR_MESSAGE); - DialogDisplayer.getDefault().notifyLater(message); + String lcFileName = f.getName().toLowerCase(); + if (lcFileName.endsWith(".pdf")) { + exportToPDF(scene, f); + } else if (lcFileName.endsWith(".svg")) { + exportToSVG(scene, f); } else { - scene.paint(svgGenerator); - FileOutputStream os = null; - try { - os = new FileOutputStream(f); - Writer out = new OutputStreamWriter(os, UTF_8); - BatikSVG.printToStream(svgGenerator, out, true); - } catch (FileNotFoundException e) { - NotifyDescriptor message = new NotifyDescriptor.Message("For export to SVG files the Batik SVG Toolkit must be intalled.", NotifyDescriptor.ERROR_MESSAGE); - DialogDisplayer.getDefault().notifyLater(message); - } finally { - if (os != null) { - try { - os.close(); - } catch (IOException e) { - } - } - } - + NotifyDescriptor message = new NotifyDescriptor.Message("Unknown image file extension: expected either '.pdf' or '.svg'", NotifyDescriptor.ERROR_MESSAGE); + DialogDisplayer.getDefault().notifyLater(message); } } }; @@ -639,5 +633,47 @@ public UndoRedo getUndoRedo() { @Override protected Object writeReplace() throws ObjectStreamException { throw new NotSerializableException(); -} + } + + private static void exportToPDF(DiagramViewer scene, File f) { + int width = scene.getBounds().width; + int height = scene.getBounds().height; + com.lowagie.text.Document document = new Document(new Rectangle(width, height)); + PdfWriter writer = null; + try { + writer = PdfWriter.getInstance(document, new FileOutputStream(f)); + writer.setCloseStream(true); + document.open(); + PdfContentByte contentByte = writer.getDirectContent(); + PdfTemplate template = contentByte.createTemplate(width, height); + PdfGraphics2D pdfGenerator = new PdfGraphics2D(contentByte, width, height); + scene.paint(pdfGenerator); + pdfGenerator.dispose(); + contentByte.addTemplate(template, 0, 0); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (document.isOpen()) { + document.close(); + } + if (writer != null) { + writer.close(); + } + } + } + + private static void exportToSVG(DiagramViewer scene, File f) { + DOMImplementation dom = GenericDOMImplementation.getDOMImplementation(); + org.w3c.dom.Document document = dom.createDocument("http://www.w3.org/2000/svg", "svg", null); + SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(document); + ctx.setEmbeddedFontsOn(true); + SVGGraphics2D svgGenerator = new SVGGraphics2D(ctx, true); + scene.paint(svgGenerator); + try (FileOutputStream os = new FileOutputStream(f)) { + Writer out = new OutputStreamWriter(os, StandardCharsets.UTF_8); + svgGenerator.stream(out, true); + } catch (IOException e) { + e.printStackTrace(); + } + } } diff --git a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/actions/ExportAction.java b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/actions/ExportAction.java index 8583a6fbf3503..e5ea4025c3364 100644 --- a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/actions/ExportAction.java +++ b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/actions/ExportAction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,7 +45,7 @@ public final class ExportAction extends CallableSystemAction implements LookupLi private final Lookup.Result result; public ExportAction() { - putValue(Action.SHORT_DESCRIPTION, "Export current graph as SVG file"); + putValue(Action.SHORT_DESCRIPTION, "Export current graph as image file"); putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.CTRL_MASK)); lookup = Utilities.actionsGlobalContext(); result = lookup.lookup(new Lookup.Template<>(ExportCookie.class)); @@ -66,12 +66,15 @@ public void performAction() { @Override public boolean accept(File f) { - return true; + String lcFileName = f.getName().toLowerCase(); + return lcFileName.endsWith(".pdf") || + lcFileName.endsWith(".svg") || + f.isDirectory(); } @Override public String getDescription() { - return "SVG files (*.svg)"; + return "Image files (*.pdf, *.svg)"; } }); fc.setCurrentDirectory(new File(Settings.get().get(Settings.DIRECTORY, Settings.DIRECTORY_DEFAULT))); @@ -80,7 +83,7 @@ public String getDescription() { if (fc.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) { File file = fc.getSelectedFile(); if (!file.getName().contains(".")) { - file = new File(file.getAbsolutePath() + ".svg"); + file = new File(file.getAbsolutePath() + ".pdf"); } File dir = file; diff --git a/src/utils/IdealGraphVisualizer/application/pom.xml b/src/utils/IdealGraphVisualizer/application/pom.xml index b5422fecb52ab..5fcb7252c5d0b 100644 --- a/src/utils/IdealGraphVisualizer/application/pom.xml +++ b/src/utils/IdealGraphVisualizer/application/pom.xml @@ -142,11 +142,6 @@ Graal ${project.version} - - ${project.groupId} - BatikSVGProxy - ${project.version} - ${project.groupId} View diff --git a/src/utils/IdealGraphVisualizer/pom.xml b/src/utils/IdealGraphVisualizer/pom.xml index c1b1896dde4ed..daaae24a5fd14 100644 --- a/src/utils/IdealGraphVisualizer/pom.xml +++ b/src/utils/IdealGraphVisualizer/pom.xml @@ -86,16 +86,17 @@ ServerCompiler FilterWindow Graal - BatikSVGProxy View RELEASE123 1.0.2 - 4.3 + 4.6 3.8.1 - 3.1.2 + 3.2.0 4.13.2 + 1.14 + 1.3.26 idealgraphvisualizer diff --git a/test/hotspot/gtest/gc/g1/test_g1CardSet.cpp b/test/hotspot/gtest/gc/g1/test_g1CardSet.cpp index f009fc4adea4e..914c64aacc593 100644 --- a/test/hotspot/gtest/gc/g1/test_g1CardSet.cpp +++ b/test/hotspot/gtest/gc/g1/test_g1CardSet.cpp @@ -25,10 +25,12 @@ #include "gc/g1/g1CardSet.inline.hpp" #include "gc/g1/g1CardSetContainers.hpp" #include "gc/g1/g1CardSetMemory.hpp" +#include "gc/g1/g1SegmentedArrayFreePool.hpp" #include "gc/g1/heapRegionRemSet.hpp" #include "gc/shared/gcTraceTime.inline.hpp" #include "gc/shared/workerThread.hpp" #include "logging/log.hpp" +#include "memory/allocation.hpp" #include "unittest.hpp" #include "utilities/powerOfTwo.hpp" @@ -343,17 +345,17 @@ void G1CardSetTest::cardset_basic_test() { ASSERT_TRUE(count == card_set.occupied()); } - G1AddCardResult res = card_set.add_card(99, config.num_cards_in_howl_bitmap() - 1); + G1AddCardResult res = card_set.add_card(99, config.max_cards_in_howl_bitmap() - 1); // Adding above card should have coarsened Bitmap -> Full. ASSERT_TRUE(res == Added); - ASSERT_TRUE(config.num_cards_in_howl_bitmap() == card_set.occupied()); + ASSERT_TRUE(config.max_cards_in_howl_bitmap() == card_set.occupied()); - res = card_set.add_card(99, config.num_cards_in_howl_bitmap() - 2); + res = card_set.add_card(99, config.max_cards_in_howl_bitmap() - 2); ASSERT_TRUE(res == Found); uint threshold = config.cards_in_howl_threshold(); uint adjusted_threshold = config.cards_in_howl_bitmap_threshold() * config.num_buckets_in_howl(); - i = config.num_cards_in_howl_bitmap(); + i = config.max_cards_in_howl_bitmap(); count = i; for (; i < threshold; i++) { G1AddCardResult res = card_set.add_card(99, i); diff --git a/test/hotspot/gtest/nmt/test_nmt_buffer_overflow_detection.cpp b/test/hotspot/gtest/nmt/test_nmt_buffer_overflow_detection.cpp new file mode 100644 index 0000000000000..c1aeb3e845ced --- /dev/null +++ b/test/hotspot/gtest/nmt/test_nmt_buffer_overflow_detection.cpp @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2021 SAP SE. All rights reserved. + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "precompiled.hpp" +#include "memory/allocation.hpp" +#include "runtime/os.hpp" +#include "services/memTracker.hpp" +#include "utilities/debug.hpp" +#include "utilities/ostream.hpp" +#include "unittest.hpp" +#include "testutils.hpp" + +#if INCLUDE_NMT + +// This prefix shows up on any c heap corruption NMT detects. If unsure which assert will +// come, just use this one. +#define COMMON_NMT_HEAP_CORRUPTION_MESSAGE_PREFIX "NMT corruption" + + + +#define DEFINE_TEST(test_function, expected_assertion_message) \ + TEST_VM_FATAL_ERROR_MSG(NMT, test_function, ".*" expected_assertion_message ".*") { \ + if (MemTracker::tracking_level() > NMT_off) { \ + tty->print_cr("NMT overwrite death test, please ignore subsequent error dump."); \ + test_function (); \ + } else { \ + /* overflow detection requires NMT to be on. If off, fake assert. */ \ + guarantee(false, \ + "fake message ignore this - " expected_assertion_message); \ + } \ + } + +/////// + +static void test_overwrite_front() { + address p = (address) os::malloc(1, mtTest); + *(p - 1) = 'a'; + os::free(p); +} + +DEFINE_TEST(test_overwrite_front, "header canary broken") + +/////// + +static void test_overwrite_back() { + address p = (address) os::malloc(1, mtTest); + *(p + 1) = 'a'; + os::free(p); +} + +DEFINE_TEST(test_overwrite_back, "footer canary broken") + +/////// + +// A overwrite farther away from the NMT header; the report should show the hex dump split up +// in two parts, containing both header and corruption site. +static void test_overwrite_back_long(size_t distance) { + address p = (address) os::malloc(distance, mtTest); + *(p + distance) = 'a'; + os::free(p); +} +static void test_overwrite_back_long_aligned_distance() { test_overwrite_back_long(0x2000); } +DEFINE_TEST(test_overwrite_back_long_aligned_distance, "footer canary broken") +static void test_overwrite_back_long_unaligned_distance() { test_overwrite_back_long(0x2001); } +DEFINE_TEST(test_overwrite_back_long_unaligned_distance, "footer canary broken") + +/////// + +static void test_double_free() { + address p = (address) os::malloc(1, mtTest); + os::free(p); + // Now a double free. Note that this is susceptible to concurrency issues should + // a concurrent thread have done a malloc and gotten the same address after the + // first free. To decrease chance of this happening, we repeat the double free + // several times. + for (int i = 0; i < 100; i ++) { + os::free(p); + } +} + +// What assertion message we will see depends on whether the VM wipes the memory-to-be-freed +// on the first free(), and whether the libc uses the freed memory to store bookkeeping information. +// If the death marker in the header is still intact after the first free, we will recognize this as +// double free; if it got wiped, we should at least see a broken header canary. +// The message would be either +// - "header canary broken" or +// - "header canary dead (double free?)". +// However, since gtest regex expressions do not support unions (a|b), I search for a reasonable +// subset here. +DEFINE_TEST(test_double_free, "header canary") + +/////// + +static void test_invalid_block_address() { + // very low, like the result of an overflow or of accessing a NULL this pointer + os::free((void*)0x100); +} +DEFINE_TEST(test_invalid_block_address, "invalid block address") + +/////// + +static void test_unaliged_block_address() { + address p = (address) os::malloc(1, mtTest); + os::free(p + 6); +} +DEFINE_TEST(test_unaliged_block_address, "block address is unaligned"); + +/////// + +// Test that we notice block corruption on realloc too +static void test_corruption_on_realloc(size_t s1, size_t s2) { + address p1 = (address) os::malloc(s1, mtTest); + *(p1 + s1) = 'a'; + address p2 = (address) os::realloc(p1, s2, mtTest); + + // Still here? + tty->print_cr("NMT did not detect corruption on os::realloc?"); + // Note: don't use ASSERT here, that does not work as expected in death tests. Just + // let the test run its course, it should notice something is amiss. +} +static void test_corruption_on_realloc_growing() { test_corruption_on_realloc(0x10, 0x11); } +DEFINE_TEST(test_corruption_on_realloc_growing, COMMON_NMT_HEAP_CORRUPTION_MESSAGE_PREFIX); +static void test_corruption_on_realloc_shrinking() { test_corruption_on_realloc(0x11, 0x10); } +DEFINE_TEST(test_corruption_on_realloc_shrinking, COMMON_NMT_HEAP_CORRUPTION_MESSAGE_PREFIX); + +/////// + +// realloc is the trickiest of the bunch. Test that realloc works and correctly takes over +// NMT header and footer to the resized block. We just test that nothing crashes - if the +// header/footer get corrupted, NMT heap corruption checker will trigger alert on os::free()). +TEST_VM(NMT, test_realloc) { + // We test both directions (growing and shrinking) and a small range for each to cover all + // size alignment variants. Should not matter, but this should be cheap. + for (size_t s1 = 0xF0; s1 < 0x110; s1 ++) { + for (size_t s2 = 0x100; s2 > 0xF0; s2 --) { + address p1 = (address) os::malloc(s1, mtTest); + ASSERT_NOT_NULL(p1); + GtestUtils::mark_range(p1, s1); // mark payload range... + address p2 = (address) os::realloc(p1, s2, mtTest); + ASSERT_NOT_NULL(p2); + ASSERT_RANGE_IS_MARKED(p2, MIN2(s1, s2)) // ... and check that it survived the resize + << s1 << "->" << s2 << std::endl; + os::free(p2); // <- if NMT headers/footers got corrupted this asserts + } + } +} + +#endif // INCLUDE_NMT diff --git a/test/hotspot/gtest/testutils.cpp b/test/hotspot/gtest/testutils.cpp index 47351f056affa..eb2898a526dc8 100644 --- a/test/hotspot/gtest/testutils.cpp +++ b/test/hotspot/gtest/testutils.cpp @@ -57,7 +57,8 @@ bool GtestUtils::check_range(const void* p, size_t s, uint8_t expected) { } if (first_wrong != NULL) { - tty->print_cr("wrong pattern around " PTR_FORMAT, p2i(first_wrong)); + tty->print_cr("check_range [" PTR_FORMAT ".." PTR_FORMAT "), 0x%X, : wrong pattern around " PTR_FORMAT, + p2i(p), p2i(p) + s, expected, p2i(first_wrong)); // Note: We deliberately print the surroundings too without bounds check. Might be interesting, // and os::print_hex_dump uses SafeFetch, so this is fine without bounds checks. os::print_hex_dump(tty, (address)(align_down(p2, 0x10) - 0x10), diff --git a/test/hotspot/gtest/testutils.hpp b/test/hotspot/gtest/testutils.hpp index 8ed72f4644fab..9852e87cadae2 100644 --- a/test/hotspot/gtest/testutils.hpp +++ b/test/hotspot/gtest/testutils.hpp @@ -51,8 +51,8 @@ class GtestUtils : public AllStatic { #define ASSERT_RANGE_IS_MARKED(p, size) ASSERT_TRUE(GtestUtils::check_range(p, size)) // Convenience asserts -#define ASSERT_NOT_NULL(p) ASSERT_NE(p, (char*)NULL) -#define ASSERT_NULL(p) ASSERT_EQ(p, (char*)NULL) +#define ASSERT_NOT_NULL(p) ASSERT_NE(p2i(p), 0) +#define ASSERT_NULL(p) ASSERT_EQ(p2i(p), 0) #define ASSERT_ALIGN(p, n) ASSERT_TRUE(is_aligned(p, n)) diff --git a/test/hotspot/gtest/unittest.hpp b/test/hotspot/gtest/unittest.hpp index b414b2fbdd97c..fefcc8efc3985 100644 --- a/test/hotspot/gtest/unittest.hpp +++ b/test/hotspot/gtest/unittest.hpp @@ -149,4 +149,21 @@ TEST_VM_ASSERT_MSG is only available in debug builds #endif +#define TEST_VM_FATAL_ERROR_MSG(category, name, msg) \ + static void test_ ## category ## _ ## name ## _(); \ + \ + static void child_ ## category ## _ ## name ## _() { \ + ::testing::GTEST_FLAG(throw_on_failure) = true; \ + test_ ## category ## _ ## name ## _(); \ + exit(0); \ + } \ + \ + TEST(category, CONCAT(name, _vm_assert)) { \ + ASSERT_EXIT(child_ ## category ## _ ## name ## _(), \ + ::testing::ExitedWithCode(1), \ + msg); \ + } \ + \ + void test_ ## category ## _ ## name ## _() + #endif // UNITTEST_HPP diff --git a/test/hotspot/jtreg/ProblemList-Xcomp.txt b/test/hotspot/jtreg/ProblemList-Xcomp.txt index 9647d4d2cf8a3..dab2ab3326f37 100644 --- a/test/hotspot/jtreg/ProblemList-Xcomp.txt +++ b/test/hotspot/jtreg/ProblemList-Xcomp.txt @@ -35,8 +35,6 @@ vmTestbase/vm/mlvm/mixed/stress/regression/b6969574/INDIFY_Test.java 8265295 lin vmTestbase/nsk/jvmti/scenarios/sampling/SP07/sp07t002/TestDescription.java 8245680 windows-x64 -vmTestbase/vm/mlvm/hiddenloader/stress/oome/heap/Test.java 8273095 generic-all - serviceability/sa/TestJhsdbJstackMixed.java 8248675 linux-aarch64 compiler/codegen/aes/TestAESMain.java 8274323 linux-x64,windows-x64 diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index fcc12d1667778..8878a66156262 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -38,9 +38,6 @@ # ############################################################################# -vmTestbase/nsk/jvmti/AttachOnDemand/attach002a/TestDescription.java 8265795 generic-all -vmTestbase/nsk/jvmti/AttachOnDemand/attach022/TestDescription.java 8265795 generic-all -vmTestbase/nsk/jdi/ObjectReference/referringObjects/referringObjects002/referringObjects002.java 8265796 generic-all ############################################################################# @@ -92,6 +89,8 @@ gc/stress/gclocker/TestGCLockerWithG1.java 8180622 generic-all gc/stress/TestJNIBlockFullGC/TestJNIBlockFullGC.java 8192647 generic-all gc/metaspace/CompressedClassSpaceSizeInJmapHeap.java 8241293 macosx-x64 +applications/jcstress/acqrel.java 8277434 linux-aarch64 + ############################################################################# # :hotspot_runtime @@ -105,7 +104,7 @@ runtime/os/TestTracePageSizes.java#compiler-options 8267460 linux-aarch64 runtime/os/TestTracePageSizes.java#G1 8267460 linux-aarch64 runtime/os/TestTracePageSizes.java#Parallel 8267460 linux-aarch64 runtime/os/TestTracePageSizes.java#Serial 8267460 linux-aarch64 -runtime/jni/checked/TestPrimitiveArrayCriticalWithBadParam.java 8277350 macosx-x64 +runtime/ErrorHandling/CreateCoredumpOnCrash.java 8267433 macosx-x64 applications/jcstress/copy.java 8229852 linux-all @@ -150,6 +149,7 @@ vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Deadlock/JavaDeadlock001/TestD vmTestbase/nsk/jdi/HiddenClass/events/events001.java 8257705 generic-all vmTestbase/nsk/jdi/ThreadReference/stop/stop001/TestDescription.java 7034630 generic-all +vmTestbase/nsk/jdi/TypeComponent/isSynthetic/issynthetic001/TestDescription.java 8277803 generic-all vmTestbase/metaspace/gc/firstGC_10m/TestDescription.java 8208250 generic-all vmTestbase/metaspace/gc/firstGC_50m/TestDescription.java 8208250 generic-all @@ -160,8 +160,7 @@ vmTestbase/nsk/jvmti/AttachOnDemand/attach045/TestDescription.java 8202971 gener vmTestbase/nsk/jvmti/scenarios/jni_interception/JI05/ji05t001/TestDescription.java 8219652 aix-ppc64 vmTestbase/nsk/jvmti/scenarios/jni_interception/JI06/ji06t001/TestDescription.java 8219652 aix-ppc64 vmTestbase/nsk/jvmti/SetJNIFunctionTable/setjniftab001/TestDescription.java 8219652 aix-ppc64 -vmTestbase/nsk/jvmti/SuspendThread/suspendthrd003/TestDescription.java 8264605 generic-all -vmTestbase/nsk/jvmti/PopFrame/popframe011/TestDescription.java 8266593 generic-all +vmTestbase/nsk/jvmti/AttachOnDemand/attach002a/TestDescription.java 8277812 generic-all vmTestbase/gc/lock/jni/jnilock002/TestDescription.java 8192647 generic-all diff --git a/test/hotspot/jtreg/compiler/ciReplay/CiReplayBase.java b/test/hotspot/jtreg/compiler/ciReplay/CiReplayBase.java index 2fccc385ee1af..a025962081cc3 100644 --- a/test/hotspot/jtreg/compiler/ciReplay/CiReplayBase.java +++ b/test/hotspot/jtreg/compiler/ciReplay/CiReplayBase.java @@ -24,10 +24,17 @@ package compiler.ciReplay; import compiler.whitebox.CompilerWhiteBoxTest; -import java.io.IOException; -import java.io.File; +import jdk.test.lib.Asserts; +import jdk.test.lib.Platform; +import jdk.test.lib.Utils; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.util.CoreUtils; + import java.io.BufferedReader; +import java.io.File; import java.io.FileReader; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -36,14 +43,6 @@ import java.util.Arrays; import java.util.List; import java.util.Optional; -import java.util.regex.Pattern; -import java.util.regex.Matcher; -import jdk.test.lib.Platform; -import jdk.test.lib.process.ProcessTools; -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.Asserts; -import jdk.test.lib.Utils; -import jdk.test.lib.util.CoreUtils; public abstract class CiReplayBase { public static final String REPLAY_FILE_NAME = "test_replay.txt"; @@ -296,4 +295,41 @@ private String[] getTestJvmCommandlineWithPrefix(String prefix, String... args) throw new Error("Can't create process builder: " + t, t); } } + + protected void removeVersionFromReplayFile() { + setNewVersionLineInReplayFile(null); + } + + protected void setNewVersionInReplayFile(int newVersionNumber) { + setNewVersionLineInReplayFile("version " + newVersionNumber); + } + + private void setNewVersionLineInReplayFile(String firstLineString) { + List newLines = new ArrayList<>(); + Path replayFilePath = Paths.get(getReplayFileName()); + try (var br = Files.newBufferedReader(replayFilePath)) { + String line; + boolean firstLine = true; + while ((line = br.readLine()) != null) { + if (firstLine) { + firstLine = false; + Asserts.assertTrue(line.startsWith("version"), "version number must exist in a proper replay file"); + if (firstLineString != null) { + newLines.add(firstLineString); + } + // Else: Remove first line by skipping it. + } else { + newLines.add(line); + } + } + Asserts.assertFalse(firstLine, replayFilePath + " should not be empty"); + } catch (IOException e) { + throw new Error("Failed to read replay data: " + e, e); + } + try { + Files.write(replayFilePath, newLines, StandardOpenOption.TRUNCATE_EXISTING); + } catch (IOException e) { + throw new Error("Failed to write replay data: " + e, e); + } + } } diff --git a/test/hotspot/jtreg/compiler/ciReplay/InliningBase.java b/test/hotspot/jtreg/compiler/ciReplay/InliningBase.java new file mode 100644 index 0000000000000..b89e9b7cb5f03 --- /dev/null +++ b/test/hotspot/jtreg/compiler/ciReplay/InliningBase.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.ciReplay; + +import jdk.test.lib.Asserts; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public abstract class InliningBase extends DumpReplayBase { + public static final String LOG_FILE_NORMAL = "hotspot_normal.log"; + public static final String LOG_FILE_REPLAY = "hotspot_replay.log"; + protected final String[] commandLineReplay; + protected final List commandLineNormal; + protected final Class testClass; + + protected InliningBase(Class testClass) { + this.testClass = testClass; + commandLineNormal = new ArrayList<>(List.of("-XX:LogFile=" + LOG_FILE_NORMAL + "", "-XX:+LogCompilation", "-XX:-TieredCompilation", + "-XX:CompileCommand=exclude," + testClass.getName() + "::main", + "-XX:CompileCommand=option," + testClass.getName() + "::test,bool,PrintInlining,true")); + commandLineReplay = new String[] + {"-XX:LogFile=" + LOG_FILE_REPLAY, "-XX:+LogCompilation", + "-XX:CompileCommand=option," + testClass.getName() + "::test,bool,PrintInlining,true"}; + } + + protected void runTest() { + runTest(commandLineNormal.toArray(new String[0])); + } + + @Override + public String getTestClass() { + return testClass.getName(); + } + + @Override + public void cleanup() { + super.cleanup(); + remove(LOG_FILE_NORMAL); + remove(LOG_FILE_REPLAY); + } + + static class InlineEntry { + String klass; + String method; + String reason; + + public InlineEntry(String klass, String method, String reason) { + this.klass = klass; + this.method = method; + this.reason = reason; + } + + public boolean isNormalInline() { + return reason.equals("inline (hot)"); + } + + public boolean isForcedByReplay() { + return reason.equals("force inline by ciReplay"); + } + + public boolean isDisallowedByReplay() { + return reason.equals("disallowed by ciReplay"); + } + + public boolean isUnloadedSignatureClasses() { + return reason.equals("unloaded signature classes"); + } + + public boolean isForcedIncrementalInlineByReplay() { + return reason.equals("force (incremental) inline by ciReplay"); + } + + public boolean isForcedInline() { + return reason.equals("force inline by annotation"); + } + + public boolean isTooDeep() { + return reason.equals("inlining too deep"); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof InlineEntry)) { + return false; + } + + InlineEntry e = (InlineEntry)other; + return klass.equals(e.klass) && method.equals(e.method); + } + + public boolean compare(String klass, String method, boolean kind) { + return this.klass.equals(klass) && this.method.equals(method) && kind; + } + } + + protected static List parseLogFile(String logFile, String rootMethod, String nmethodMatch, int inlineeCount) { + String nmethodStart = " inlinees = new ArrayList<>(); + int foundLines = 0; + try (var br = Files.newBufferedReader(Paths.get(logFile))) { + String line; + boolean nmethodLine = false; + boolean inlinineLine = false; + while ((line = br.readLine()) != null) { + if (nmethodLine) { + // Ignore other entries which could be in between nmethod entry and inlining statements + if (line.startsWith(" ")) { + inlinineLine = true; + Pattern p = Pattern.compile("(\\S+)::(\\S+).*bytes\\)\s+(.*)"); + Matcher matcher = p.matcher(line); + Asserts.assertTrue(matcher.find(), "must find inlinee method"); + inlinees.add(new InlineEntry(matcher.group(1), matcher.group(2), matcher.group(3).trim())); + foundLines++; + } else if (inlinineLine) { + Asserts.assertEQ(foundLines, inlineeCount, "did not find all inlinees"); + return inlinees; + } + } else { + nmethodLine = line.startsWith(nmethodStart) && line.contains(nmethodMatch); + if (nmethodLine) { + Asserts.assertTrue(line.contains(rootMethod), "should only dump inline information for " + rootMethod); + } + } + } + } catch (IOException e) { + throw new Error("Failed to read " + logFile + " data: " + e, e); + } + Asserts.fail("Should have found inlinees"); + return inlinees; + } + + protected void verifyLists(List inlineesNormal, List inlineesReplay, int expectedSize) { + if (!inlineesNormal.equals(inlineesReplay)) { + System.err.println("Normal entries:"); + inlineesNormal.forEach(System.err::println); + System.err.println("Replay entries:"); + inlineesReplay.forEach(System.err::println); + Asserts.fail("different inlining decision in normal run vs. replay run"); + } + Asserts.assertEQ(expectedSize, inlineesNormal.size(), "unexpected number of inlinees found"); + } +} + diff --git a/test/hotspot/jtreg/compiler/ciReplay/TestIncrementalInlining.java b/test/hotspot/jtreg/compiler/ciReplay/TestIncrementalInlining.java new file mode 100644 index 0000000000000..8bac1ea283c86 --- /dev/null +++ b/test/hotspot/jtreg/compiler/ciReplay/TestIncrementalInlining.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8254108 + * @library / /test/lib + * @summary Testing of ciReplay with incremental inlining. + * @requires vm.flightRecorder != true & vm.compMode != "Xint" & vm.compMode != "Xcomp" & vm.debug == true & vm.compiler2.enabled + * @modules java.base/jdk.internal.misc + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * compiler.ciReplay.TestIncrementalInlining + */ + +package compiler.ciReplay; + +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; +import jdk.test.whitebox.WhiteBox; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class TestIncrementalInlining extends InliningBase { + + private List inlineesNormal; + private List inlineesReplay; + public static void main(String[] args) { + new TestIncrementalInlining(); + } + + TestIncrementalInlining() { + super(IncrementalInliningTest.class); + // Enable Whitebox access for test VM. + commandLineNormal.add("-Dtest.jdk=" + Utils.TEST_JDK); + commandLineNormal.add("-cp"); + commandLineNormal.add(Utils.TEST_CLASS_PATH); + commandLineNormal.add("-Xbootclasspath/a:."); + commandLineNormal.add("-XX:+UnlockDiagnosticVMOptions"); + commandLineNormal.add("-XX:+WhiteBoxAPI"); + commandLineNormal.add("-XX:MaxInlineLevel=2"); + commandLineNormal.add("-XX:-AlwaysIncrementalInline"); + runTest(); + } + + @Override + public void testAction() { + positiveTest(commandLineReplay); + inlineesNormal = parseLogFile(LOG_FILE_NORMAL, getTestClass() + " " + "test", "compile_id='" + getCompileIdFromFile(getReplayFileName()), 5); + verify(true); + + // Incremental inlining is supported in version 2+ + // Test replay file version 1. + removeIncrementalInlineInfo(); + setNewVersionInReplayFile(1); + positiveTest(commandLineReplay); + verify(false); + + // Test replay file without version. + removeVersionFromReplayFile(); + positiveTest(commandLineReplay); + verify(false); + } + + private void verify(boolean isNewFormat) { + inlineesReplay = parseLogFile(LOG_FILE_REPLAY, getTestClass() + " " + "test", "test ()V", 5); + verifyLists(inlineesNormal, inlineesReplay, 5); + checkInlining(isNewFormat); + } + + // Check if inlining is done correctly in ciReplay. + private void checkInlining(boolean isNewFormat) { + String klass = getTestClass(); + Asserts.assertTrue(inlineesNormal.get(0).compare(klass, "level0", inlineesNormal.get(0).isForcedInline())); + Asserts.assertTrue(inlineesReplay.get(0).compare(klass, "level0", inlineesReplay.get(0).isForcedByReplay())); + Asserts.assertTrue(inlineesNormal.get(1).compare(klass, "level1", inlineesNormal.get(1).isNormalInline())); + Asserts.assertTrue(inlineesReplay.get(1).compare(klass, "level1", inlineesReplay.get(1).isForcedByReplay())); + Asserts.assertTrue(inlineesNormal.get(2).compare(klass, "level2", inlineesNormal.get(2).isForcedInline())); + Asserts.assertTrue(inlineesReplay.get(2).compare(klass, "level2", inlineesReplay.get(2).isForcedByReplay())); + Asserts.assertTrue(inlineesNormal.get(3).compare(klass, "late", inlineesNormal.get(3).isForcedInline())); + Asserts.assertTrue(inlineesReplay.get(3).compare(klass, "late", isNewFormat ? + inlineesReplay.get(3).isForcedIncrementalInlineByReplay() + : inlineesReplay.get(3).isForcedByReplay())); + Asserts.assertTrue(inlineesNormal.get(4).compare(klass, "level4", inlineesNormal.get(4).isTooDeep())); + Asserts.assertTrue(inlineesReplay.get(4).compare(klass, "level4", inlineesReplay.get(4).isDisallowedByReplay())); + } + + private void removeIncrementalInlineInfo() { + try { + Path replayFilePath = Paths.get(getReplayFileName()); + List replayContent = Files.readAllLines(replayFilePath); + for (int i = 0; i < replayContent.size(); i++) { + String line = replayContent.get(i); + if (line.startsWith("compile ")) { + int lastIndex = 0; + StringBuilder newLine = new StringBuilder(); + Pattern p = Pattern.compile("(\\d (-?\\d)) \\d compiler"); + Matcher m = p.matcher(line); + boolean firstMatch = true; + while (m.find()) { + newLine.append(line, lastIndex, m.start()) + .append(m.group(1)) + .append(" compiler"); + lastIndex = m.end(); + String bci = m.group(2); + Asserts.assertTrue(firstMatch ? bci.equals("-1") : bci.equals("0"), "only root has -1"); + firstMatch = false; + } + Asserts.assertLessThan(lastIndex, line.length(), "not reached end of line, yet"); + newLine.append(line, lastIndex, line.length()); + replayContent.set(i, newLine.toString()); + } + } + Files.write(replayFilePath, replayContent, StandardOpenOption.TRUNCATE_EXISTING); + } catch (IOException ioe) { + throw new Error("Failed to read/write replay data: " + ioe, ioe); + } + } +} + +class IncrementalInliningTest { + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + private static String s; + + public static void main(String[] args) throws NoSuchMethodException { + WB.testSetForceInlineMethod(IncrementalInliningTest.class.getDeclaredMethod("level0"), true); + WB.testSetForceInlineMethod(IncrementalInliningTest.class.getDeclaredMethod("level2"), true); + WB.testSetForceInlineMethod(IncrementalInliningTest.class.getDeclaredMethod("late"), true); + for (int i = 0; i < 10000; i++) { + test(); + } + } + + private static void test() { + level0(); + } + + public static void level0() { + level1(); + } + + public static void level1() { + level2(); + } + + public static void level2() { + late(); + } + + // Reached max inline level but forced to be inlined -> inline late. + public static void late() { + level4(); + } + + // Reached max inline level and not forced to be inlined -> no inline. + public static void level4() { + s = "HelloWorld"; + } + +} diff --git a/test/hotspot/jtreg/compiler/ciReplay/TestInliningProtectionDomain.java b/test/hotspot/jtreg/compiler/ciReplay/TestInliningProtectionDomain.java index 6693295a5d5e3..0a900fda8e58c 100644 --- a/test/hotspot/jtreg/compiler/ciReplay/TestInliningProtectionDomain.java +++ b/test/hotspot/jtreg/compiler/ciReplay/TestInliningProtectionDomain.java @@ -28,173 +28,49 @@ * @summary Testing that ciReplay inlining does not fail with unresolved signature classes. * @requires vm.flightRecorder != true & vm.compMode != "Xint" & vm.compMode != "Xcomp" & vm.debug == true & vm.compiler2.enabled * @modules java.base/jdk.internal.misc - * @build sun.hotspot.WhiteBox - * @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * compiler.ciReplay.TestInliningProtectionDomain + * @run driver compiler.ciReplay.TestInliningProtectionDomain */ package compiler.ciReplay; import jdk.test.lib.Asserts; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.ArrayList; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -public class TestInliningProtectionDomain extends DumpReplayBase { - public static final String LOG_FILE_NORMAL = "hotspot_normal.log"; - public static final String LOG_FILE_REPLAY = "hotspot_replay.log"; - private final String[] commandLineReplay; - - private final String className; +public class TestInliningProtectionDomain extends InliningBase { public static void main(String[] args) { - new TestInliningProtectionDomain("ProtectionDomainTestCompiledBefore", true); - new TestInliningProtectionDomain("ProtectionDomainTestNoOtherCompilationPublic", false); - new TestInliningProtectionDomain("ProtectionDomainTestNoOtherCompilationPrivate", false); - new TestInliningProtectionDomain("ProtectionDomainTestNoOtherCompilationPrivateString", false); + new TestInliningProtectionDomain(ProtectionDomainTestCompiledBefore.class, true); + new TestInliningProtectionDomain(ProtectionDomainTestNoOtherCompilationPublic.class, false); + new TestInliningProtectionDomain(ProtectionDomainTestNoOtherCompilationPrivate.class, false); + new TestInliningProtectionDomain(ProtectionDomainTestNoOtherCompilationPrivateString.class, false); } - public TestInliningProtectionDomain(String className, boolean compileBar) { - this.className = className; - List commandLineNormal = new ArrayList<>(List.of("-XX:LogFile=" + LOG_FILE_NORMAL + "", "-XX:+LogCompilation", "-XX:-TieredCompilation", - "-XX:CompileCommand=exclude," + getTestClass() + "::main", - "-XX:CompileCommand=option," + getTestClass() + "::test,bool,PrintInlining,true")); + public TestInliningProtectionDomain(Class testClass, boolean compileBar) { + super(testClass); if (compileBar) { - commandLineNormal.add("-XX:CompileCommand=compileonly," + getTestClass() + "::bar"); + commandLineNormal.add("-XX:CompileCommand=compileonly," + testClass.getName() + "::bar"); } - commandLineReplay = new String[] - {"-XX:LogFile=" + LOG_FILE_REPLAY + "", "-XX:+LogCompilation", - "-XX:CompileCommand=option," + getTestClass() + "::test,bool,PrintInlining,true"}; - runTest(commandLineNormal.toArray(new String[0])); + runTest(); } @Override public void testAction() { positiveTest(commandLineReplay); - String klass = "compiler.ciReplay." + className; - String entryString = klass + " " + "test"; - boolean inlineFails = className.equals("ProtectionDomainTestNoOtherCompilationPrivate"); + String entryString = getTestClass() + " " + "test"; + boolean inlineFails = testClass == ProtectionDomainTestNoOtherCompilationPrivate.class; int inlineeCount = inlineFails ? 1 : 5; - List inlineesNormal = parseLogFile(LOG_FILE_NORMAL, entryString, "compile_id='" + getCompileIdFromFile(getReplayFileName()), inlineeCount); - List inlineesReplay = parseLogFile(LOG_FILE_REPLAY, entryString, "test ()V", inlineeCount); + List inlineesNormal = parseLogFile(LOG_FILE_NORMAL, entryString, "compile_id='" + getCompileIdFromFile(getReplayFileName()), inlineeCount); + List inlineesReplay = parseLogFile(LOG_FILE_REPLAY, entryString, "test ()V", inlineeCount); verifyLists(inlineesNormal, inlineesReplay, inlineeCount); if (inlineFails) { - Asserts.assertTrue(compare(inlineesNormal.get(0), "compiler.ciReplay.ProtectionDomainTestNoOtherCompilationPrivate", - "bar", inlineesNormal.get(0).isUnloadedSignatureClasses())); - Asserts.assertTrue(compare(inlineesReplay.get(0), "compiler.ciReplay.ProtectionDomainTestNoOtherCompilationPrivate", - "bar", inlineesReplay.get(0).isDisallowedByReplay())); + Asserts.assertTrue(inlineesNormal.get(0).compare("compiler.ciReplay.ProtectionDomainTestNoOtherCompilationPrivate", "bar", inlineesNormal.get(0).isUnloadedSignatureClasses())); + Asserts.assertTrue(inlineesReplay.get(0).compare("compiler.ciReplay.ProtectionDomainTestNoOtherCompilationPrivate", "bar", inlineesReplay.get(0).isDisallowedByReplay())); } else { - Asserts.assertTrue(compare(inlineesNormal.get(4), "compiler.ciReplay.InliningBar", "bar2", inlineesNormal.get(4).isNormalInline())); - Asserts.assertTrue(compare(inlineesReplay.get(4), "compiler.ciReplay.InliningBar", "bar2", inlineesReplay.get(4).isForcedByReplay())); - } - remove(LOG_FILE_NORMAL); - remove(LOG_FILE_REPLAY); - } - - private void verifyLists(List inlineesNormal, List inlineesReplay, int expectedSize) { - if (!inlineesNormal.equals(inlineesReplay)) { - System.err.println("Normal entries:"); - inlineesNormal.forEach(System.err::println); - System.err.println("Replay entries:"); - inlineesReplay.forEach(System.err::println); - Asserts.fail("different inlining decision in normal run vs. replay run"); - } - Asserts.assertEQ(expectedSize, inlineesNormal.size(), "unexpected number of inlinees found"); - } - - public static boolean compare(Entry e, String klass, String method, boolean kind) { - return e.klass.equals(klass) && e.method.equals(method) && kind; - } - - public static List parseLogFile(String logFile, String rootMethod, String nmethodMatch, int inlineeCount) { - String nmethodStart = " inlinees = new ArrayList<>(); - int foundLines = 0; - try (var br = Files.newBufferedReader(Paths.get(logFile))) { - String line; - boolean nmethodLine = false; - boolean inlinineLine = false; - while ((line = br.readLine()) != null) { - if (nmethodLine) { - // Ignore other entries which could be in between nmethod entry and inlining statements - if (line.startsWith(" ")) { - inlinineLine = true; - Pattern p = Pattern.compile("(\\S+)::(\\S+).*bytes\\)\s+(.*)"); - Matcher matcher = p.matcher(line); - Asserts.assertTrue(matcher.find(), "must find inlinee method"); - inlinees.add(new Entry(matcher.group(1), matcher.group(2), matcher.group(3).trim())); - foundLines++; - } else if (inlinineLine) { - Asserts.assertEQ(foundLines, inlineeCount, "did not find all inlinees"); - return inlinees; - } - } else { - nmethodLine = line.startsWith(nmethodStart) && line.contains(nmethodMatch); - if (nmethodLine) { - Asserts.assertTrue(line.contains(rootMethod), "should only dump inline information for " + rootMethod); - } - } - } - } catch (IOException e) { - throw new Error("Failed to read " + logFile + " data: " + e, e); - } - Asserts.fail("Should have found inlinees"); - return inlinees; - } - - - @Override - public String getTestClass() { - return "compiler.ciReplay." + className; - } - - static class Entry { - String klass; - String method; - String reason; - - public Entry(String klass, String method, String reason) { - this.klass = klass; - this.method = method; - this.reason = reason; - } - - public boolean isNormalInline() { - return reason.equals("inline (hot)"); - } - - public boolean isForcedByReplay() { - return reason.equals("force inline by ciReplay"); - } - - public boolean isDisallowedByReplay() { - return reason.equals("disallowed by ciReplay"); - } - - public boolean isUnloadedSignatureClasses() { - return reason.equals("unloaded signature classes"); - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - if (!(other instanceof Entry)) { - return false; - } - - Entry e = (Entry)other; - return klass.equals(e.klass) && method.equals(e.method); + Asserts.assertTrue(inlineesNormal.get(4).compare("compiler.ciReplay.InliningBar", "bar2", inlineesNormal.get(4).isNormalInline())); + Asserts.assertTrue(inlineesReplay.get(4).compare("compiler.ciReplay.InliningBar", "bar2", inlineesReplay.get(4).isForcedByReplay())); } } } diff --git a/test/hotspot/jtreg/compiler/codecache/CodeCacheFullCountTest.java b/test/hotspot/jtreg/compiler/codecache/CodeCacheFullCountTest.java new file mode 100644 index 0000000000000..1fee39144ff93 --- /dev/null +++ b/test/hotspot/jtreg/compiler/codecache/CodeCacheFullCountTest.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.net.URL; +import java.net.URLClassLoader; +import java.lang.reflect.Field; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +/* + * @test + * @bug 8276036 8277213 8277441 + * @summary test for the value of full_count in the message of insufficient codecache + * @library /test/lib + */ +public class CodeCacheFullCountTest { + public static void main(String args[]) throws Throwable { + if (args.length == 1) { + wasteCodeCache(); + } else { + runTest(); + } + } + + public static void wasteCodeCache() throws Exception { + URL url = CodeCacheFullCountTest.class.getProtectionDomain().getCodeSource().getLocation(); + + for (int i = 0; i < 500; i++) { + ClassLoader cl = new MyClassLoader(url); + refClass(cl.loadClass("SomeClass")); + } + } + + public static void runTest() throws Throwable { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:ReservedCodeCacheSize=2496k", "-XX:-UseCodeCacheFlushing", "CodeCacheFullCountTest", "WasteCodeCache"); + OutputAnalyzer oa = ProcessTools.executeProcess(pb); + oa.shouldHaveExitValue(0); + String stdout = oa.getStdout(); + + Pattern pattern = Pattern.compile("full_count=(\\d)"); + Matcher stdoutMatcher = pattern.matcher(stdout); + if (stdoutMatcher.find()) { + int fullCount = Integer.parseInt(stdoutMatcher.group(1)); + if (fullCount != 1) { + throw new RuntimeException("the value of full_count is wrong."); + } + } else { + throw new RuntimeException("codecache shortage did not occur."); + } + } + + private static void refClass(Class clazz) throws Exception { + Field name = clazz.getDeclaredField("NAME"); + name.setAccessible(true); + name.get(null); + } + + private static class MyClassLoader extends URLClassLoader { + public MyClassLoader(URL url) { + super(new URL[]{url}, null); + } + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + try { + return super.loadClass(name, resolve); + } catch (ClassNotFoundException e) { + return Class.forName(name, resolve, CodeCacheFullCountTest.class.getClassLoader()); + } + } + } +} + +abstract class Foo { + public abstract int foo(); +} + +class Foo1 extends Foo { + private int a; + public int foo() { return a; } +} + +class Foo2 extends Foo { + private int a; + public int foo() { return a; } +} + +class Foo3 extends Foo { + private int a; + public int foo() { return a; } +} + +class Foo4 extends Foo { + private int a; + public int foo() { return a; } +} + +class SomeClass { + static final String NAME = "name"; + + static { + int res =0; + Foo[] foos = new Foo[] { new Foo1(), new Foo2(), new Foo3(), new Foo4() }; + for (int i = 0; i < 100000; i++) { + res = foos[i % foos.length].foo(); + } + } +} diff --git a/test/hotspot/jtreg/compiler/exceptions/OptimizeImplicitExceptions.java b/test/hotspot/jtreg/compiler/exceptions/OptimizeImplicitExceptions.java new file mode 100644 index 0000000000000..289353a38a240 --- /dev/null +++ b/test/hotspot/jtreg/compiler/exceptions/OptimizeImplicitExceptions.java @@ -0,0 +1,275 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + /* + * @test + * @bug 8275908 + * @summary Record null_check traps for calls and array_check traps in the interpreter + * + * @requires vm.compiler2.enabled & vm.compMode != "Xcomp" + * + * @library /test/lib + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xbatch -XX:-UseOnStackReplacement -XX:-TieredCompilation + * -XX:CompileCommand=compileonly,compiler.exceptions.OptimizeImplicitExceptions::throwImplicitException + * compiler.exceptions.OptimizeImplicitExceptions + */ + +package compiler.exceptions; + +import java.lang.reflect.Method; +import java.util.HashMap; + +import jdk.test.lib.Asserts; +import jdk.test.whitebox.WhiteBox; + +public class OptimizeImplicitExceptions { + // ImplicitException represents the various implicit (aka. 'built-in') exceptions + // which can be thrown implicitely by the JVM when executing bytecodes. + public enum ImplicitException { + // NullPointerException during field access + NULL_POINTER_EXCEPTION("null_check"), + // NullPointerException during invoke + INVOKE_NULL_POINTER_EXCEPTION("null_check"), + ARITHMETIC_EXCEPTION("div0_check"), + ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION("range_check"), + ARRAY_STORE_EXCEPTION("array_check"), + CLASS_CAST_EXCEPTION("class_check"); + private final String reason; + ImplicitException(String reason) { + this.reason = reason; + } + public String getReason() { + return reason; + } + } + // TestMode represents a specific combination of the OmitStackTraceInFastThrow command line options. + // They will be set up in 'setFlags(TestMode testMode)' before a new test run starts. + public enum TestMode { + OMIT_STACKTRACES_IN_FASTTHROW, + STACKTRACES_IN_FASTTHROW + } + + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + // The number of deoptimizations after which a method will be made not-entrant + private static final int PerBytecodeTrapLimit = WB.getIntxVMFlag("PerBytecodeTrapLimit").intValue(); + // The number of interpreter invocations after which a decompiled method will be re-compiled. + private static final int Tier0InvokeNotifyFreq = (int)Math.pow(2, WB.getIntxVMFlag("Tier0InvokeNotifyFreqLog")); + // The following variables are used to track the value of the global deopt counters between the various test phases. + private static int oldDeoptCount = 0; + private static HashMap oldDeoptCountReason = new HashMap(ImplicitException.values().length); + // The following two objects are declared statically to simplify the test method. + private static String[] string_a = new String[1]; + private static final Object o = new Object(); + + // This is the main test method. It will repeatedly called with the same ImplicitException 'type' to + // JIT-compile it, deoptimized it, re-compile it again and do various checks on the way. + // This process will be repeated then for each kind of ImplicitException 'type'. + public static Object throwImplicitException(ImplicitException type, Object[] object_a) { + switch (type) { + case NULL_POINTER_EXCEPTION: { + return object_a.length; + } + case INVOKE_NULL_POINTER_EXCEPTION: { + return object_a.hashCode(); + } + case ARITHMETIC_EXCEPTION: { + return ((42 / (object_a.length - 1)) > 2) ? null : object_a[0]; + } + case ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION: { + return object_a[5]; + } + case ARRAY_STORE_EXCEPTION: { + return (object_a[0] = o); + } + case CLASS_CAST_EXCEPTION: { + return (ImplicitException[])object_a; + } + } + return null; + } + + // Completely unload (i.e. make "not-entrant"->"zombie"->"unload/free") a JIT-compiled + // version of a method and clear the method's profiling counters. + private static void unloadAndClean(Method m) { + WB.deoptimizeMethod(m); // Makes the nmethod "not entrant". + WB.forceNMethodSweep(); // Makes all "not entrant" nmethods "zombie". This requires + WB.forceNMethodSweep(); // two sweeps, see 'nmethod::can_convert_to_zombie()' for why. + WB.forceNMethodSweep(); // Need third sweep to actually unload/free all "zombie" nmethods. + System.gc(); + WB.clearMethodState(m); + } + + // Set '-XX' flags according to 'TestMode' + private static void setFlags(TestMode testMode) { + if (testMode == TestMode.OMIT_STACKTRACES_IN_FASTTHROW) { + WB.setBooleanVMFlag("OmitStackTraceInFastThrow", true); + } else { + WB.setBooleanVMFlag("OmitStackTraceInFastThrow", false); + } + + System.out.println("=========================================================="); + System.out.println("testMode=" + testMode + + " OmitStackTraceInFastThrow=" + WB.getBooleanVMFlag("OmitStackTraceInFastThrow")); + System.out.println("=========================================================="); + } + + private static void printCounters(TestMode testMode, ImplicitException impExcp, Method throwImplicitException_m, int invocations) { + System.out.println("testMode=" + testMode + " exception=" + impExcp + " invocations=" + invocations + "\n" + + "decompilecount=" + WB.getMethodDecompileCount(throwImplicitException_m) + " " + + "trapCount=" + WB.getMethodTrapCount(throwImplicitException_m) + " " + + "trapCount(" + impExcp.getReason() + ")=" + + WB.getMethodTrapCount(throwImplicitException_m, impExcp.getReason()) + " " + + "globalDeoptCount=" + WB.getDeoptCount() + " " + + "globalDeoptCount(" + impExcp.getReason() + ")=" + WB.getDeoptCount(impExcp.getReason(), null)); + System.out.println("method compiled=" + WB.isMethodCompiled(throwImplicitException_m)); + } + + // Checks after the test method has been JIT-compiled but before the compiled version has been invoked. + private static void checkSimple(TestMode testMode, ImplicitException impExcp, Exception ex, Method throwImplicitException_m, int invocations) { + + printCounters(testMode, impExcp, throwImplicitException_m, invocations); + // At this point, throwImplicitException() has been compiled but the compiled version has not been invoked yet. + Asserts.assertEQ(WB.getMethodCompilationLevel(throwImplicitException_m), 4, "Method should be compiled at level 4."); + + int trapCount = WB.getMethodTrapCount(throwImplicitException_m); + int trapCountSpecific = WB.getMethodTrapCount(throwImplicitException_m, impExcp.getReason()); + Asserts.assertEQ(trapCount, invocations, "Trap count must much invocation count."); + Asserts.assertEQ(trapCountSpecific, invocations, "Trap count must much invocation count."); + Asserts.assertNotNull(ex.getMessage(), "Exceptions thrown in the interpreter should have a message."); + } + + // Checks after the JIT-compiled test method has been invoked 'invocations' times. + private static void check(TestMode testMode, ImplicitException impExcp, Exception ex, + Method throwImplicitException_m, int invocations, int totalInvocations) { + + printCounters(testMode, impExcp, throwImplicitException_m, totalInvocations); + // At this point, the compiled version of 'throwImplicitException()' has been invoked 'invocations' times. + Asserts.assertEQ(WB.getMethodCompilationLevel(throwImplicitException_m), 4, "Method should be compiled at level 4."); + int deoptCount = WB.getDeoptCount(); + int deoptCountReason = WB.getDeoptCount(impExcp.getReason(), null/*action*/); + if (testMode == TestMode.OMIT_STACKTRACES_IN_FASTTHROW) { + // No deoptimizations for '-XX:+OmitStackTraceInFastThrow' + Asserts.assertEQ(oldDeoptCount, deoptCount, "Wrong number of deoptimizations."); + Asserts.assertEQ(oldDeoptCountReason.get(impExcp.getReason()), deoptCountReason, "Wrong number of deoptimizations."); + // '-XX:+OmitStackTraceInFastThrow' never has message because it is using a global singleton exception. + Asserts.assertNull(ex.getMessage(), "Optimized exceptions have no message."); + } else if (testMode == TestMode.STACKTRACES_IN_FASTTHROW) { + // We always deoptimize for '-XX:-OmitStackTraceInFastThrow + Asserts.assertEQ(oldDeoptCount + invocations, deoptCount, "Wrong number of deoptimizations."); + Asserts.assertEQ(oldDeoptCountReason.get(impExcp.getReason()) + invocations, deoptCountReason, "Wrong number of deoptimizations."); + Asserts.assertNotNull(ex.getMessage(), "Exceptions thrown in the interpreter should have a message."); + } else { + Asserts.fail("Unknown test mode."); + } + oldDeoptCount = deoptCount; + oldDeoptCountReason.put(impExcp.getReason(), deoptCountReason); + } + + public static void main(String[] args) throws Exception { + + if (!WB.getBooleanVMFlag("ProfileTraps")) { + // The fast-throw optimzation only works if we're running with -XX:+ProfileTraps + return; + } + + // Initialize global deopt counts to zero. + for (ImplicitException impExcp : ImplicitException.values()) { + oldDeoptCountReason.put(impExcp.getReason(), 0); + } + // Get a handle of the test method for usage with the WhiteBox API. + Method throwImplicitException_m = OptimizeImplicitExceptions.class + .getDeclaredMethod("throwImplicitException", new Class[] { ImplicitException.class, Object[].class}); + + for (TestMode testMode : TestMode.values()) { + setFlags(testMode); + for (ImplicitException impExcp : ImplicitException.values()) { + int invocations = 0; + Exception lastException = null; + + // Warmup and compile, but don't invoke compiled code. + while(!WB.isMethodCompiled(throwImplicitException_m)) { + invocations++; + try { + throwImplicitException(impExcp, impExcp.getReason().equals("null_check") ? null : string_a); + } catch (Exception catchedExcp) { + lastException = catchedExcp; + continue; + } + throw new Exception("Should not happen"); + } + + checkSimple(testMode, impExcp, lastException, throwImplicitException_m, invocations); + + // Invoke compiled code 'PerBytecodeTrapLimit' times. + for (int i = 0; i < PerBytecodeTrapLimit; i++) { + invocations++; + try { + throwImplicitException(impExcp, impExcp.getReason().equals("null_check") ? null : string_a); + } catch (Exception catchedExcp) { + lastException = catchedExcp; + continue; + } + throw new Exception("Should not happen"); + } + + check(testMode, impExcp, lastException, throwImplicitException_m, PerBytecodeTrapLimit, invocations); + + // Invoke compiled code 'Tier0InvokeNotifyFreq' times. + // If the method was de-compiled before, this will re-compile it again. + for (int i = 0; i < Tier0InvokeNotifyFreq; i++) { + invocations++; + try { + throwImplicitException(impExcp, impExcp.getReason().equals("null_check") ? null : string_a); + } catch (Exception catchedExcp) { + lastException = catchedExcp; + continue; + } + throw new Exception("Should not happen"); + } + + check(testMode, impExcp, lastException, throwImplicitException_m, Tier0InvokeNotifyFreq, invocations); + + // Invoke compiled code 'PerBytecodeTrapLimit' times. + for (int i = 0; i < PerBytecodeTrapLimit; i++) { + invocations++; + try { + throwImplicitException(impExcp, impExcp.getReason().equals("null_check") ? null : string_a); + } catch (Exception catchedExcp) { + lastException = catchedExcp; + continue; + } + throw new Exception("Should not happen"); + } + + check(testMode, impExcp, lastException, throwImplicitException_m, PerBytecodeTrapLimit, invocations); + + System.out.println("------------------------------------------------------------------"); + + unloadAndClean(throwImplicitException_m); + } + } + } +} diff --git a/test/hotspot/jtreg/compiler/loopopts/TestDeadPostLoopBecausePredicate.java b/test/hotspot/jtreg/compiler/loopopts/TestDeadPostLoopBecausePredicate.java new file mode 100644 index 0000000000000..719de54204f2c --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestDeadPostLoopBecausePredicate.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021, Red Hat, Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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 8275330 + * @summary C2: assert(n->is_Root() || n->is_Region() || n->is_Phi() || n->is_MachMerge() || def_block->dominates(block)) failed: uses must be dominated by definitions + * + * @run main/othervm -Xmx512m -XX:+UnlockDiagnosticVMOptions -Xcomp -XX:CompileOnly=TestDeadPostLoopBecausePredicate TestDeadPostLoopBecausePredicate + * + */ + + +public class TestDeadPostLoopBecausePredicate { + + public static final int N = 400; + + public static int iFld=54270; + public static int iFld1=-4; + public int iFld2=201; + + public int mainTest(String[] strArr1) { + + int i=0, i17=8052, i19=22380, i20=60894, iArr[]=new int[N]; + init(iArr, 4); + + i = 1; + do { + for (i17 = 5; i17 < 114; i17++) { + switch ((i17 % 7) + 126) { + case 126: + for (i19 = 2; i19 > i; i19 -= 3) { + try { + i20 = (iFld2 % TestDeadPostLoopBecausePredicate.iFld1); + i20 = (iArr[i19 - 1] % TestDeadPostLoopBecausePredicate.iFld); + TestDeadPostLoopBecausePredicate.iFld = (TestDeadPostLoopBecausePredicate.iFld1 % iArr[i19]); + } catch (ArithmeticException a_e) {} + } + break; + } + } + } while (++i < 220); + + return i20; + } + + public static void init(int[] a, int seed) { + for (int j = 0; j < a.length; j++) { + a[j] = (j % 2 == 0) ? seed + j : seed - j; + } + } + + public static void main(String[] strArr) { + TestDeadPostLoopBecausePredicate _instance = new TestDeadPostLoopBecausePredicate(); + for (int i = 0; i < 10; i++ ) { + _instance.mainTest(strArr); + } + } +} diff --git a/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitAArch64DefaultFlags.java b/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitAArch64DefaultFlags.java index 9d9dbc6ea2f2a..67a286c6cd909 100644 --- a/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitAArch64DefaultFlags.java +++ b/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitAArch64DefaultFlags.java @@ -87,7 +87,8 @@ public static void main(String[] args) throws Exception { final String cpuModel = cpuFeatures.get(0); if (isCPUModelNeoverseN1(cpuModel)) { - checkFinalFlagsEqualTo(ProcessTools.createJavaProcessBuilder("-XX:+PrintFlagsFinal", "-version"), "isb", "1"); + checkFinalFlagsEqualTo(ProcessTools.createJavaProcessBuilder("-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintFlagsFinal", "-version"), + "isb", "1"); checkFinalFlagsEqualTo(ProcessTools.createJavaProcessBuilder("-XX:+UnlockDiagnosticVMOptions", "-XX:OnSpinWaitInstCount=2", "-XX:+PrintFlagsFinal", "-version"), "isb", "2"); } else { diff --git a/test/hotspot/jtreg/compiler/uncommontrap/Decompile.java b/test/hotspot/jtreg/compiler/uncommontrap/Decompile.java new file mode 100644 index 0000000000000..3ea6bb5cb29da --- /dev/null +++ b/test/hotspot/jtreg/compiler/uncommontrap/Decompile.java @@ -0,0 +1,167 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + /* + * @test + * @bug 8275908 + * @summary Quick test for the new WhiteBox methods of JDK-8275908 + * + * @requires vm.compiler2.enabled & vm.compMode != "Xcomp" + * + * @library /test/lib + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xbatch -XX:-UseOnStackReplacement -XX:-TieredCompilation + * -XX:CompileCommand=compileonly,compiler.uncommontrap.Decompile::uncommonTrap + * -XX:CompileCommand=inline,compiler.uncommontrap.Decompile*::foo + * compiler.uncommontrap.Decompile + */ + +package compiler.uncommontrap; + +import java.lang.reflect.Method; + +import jdk.test.lib.Asserts; +import jdk.test.whitebox.WhiteBox; + +public class Decompile { + + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + // The number of deoptimizations after which a method will be made not-entrant + private static final int PerBytecodeTrapLimit = WB.getIntxVMFlag("PerBytecodeTrapLimit").intValue(); + // The number of interpreter invocations after which a decompiled method will be re-compiled. + private static final int Tier0InvokeNotifyFreq = (int)Math.pow(2, WB.getIntxVMFlag("Tier0InvokeNotifyFreqLog")); + // VM builds without JVMCI like x86_32 call the bimorphic inlining trap just 'bimorphic' + // while all the other builds with JVMCI call it 'bimorphic_or_optimized_type_check'. + // Only builds with JVMCI have the "EnableJVMCI" flag. + private static final boolean isJVMCISupported = (WB.getBooleanVMFlag("EnableJVMCI") != null); + private static final String bimorphicTrapName = isJVMCISupported ? "bimorphic_or_optimized_type_check" : "bimorphic"; + + static class Base { + void foo() {} + } + static class X extends Base { + void foo() {} + } + static class Y extends Base { + void foo() {} + } + + static void uncommonTrap(Base t) { + t.foo(); + } + + private static void printCounters(Method uncommonTrap_m, int invocations) { + System.out.println("-----------------------------------------------------------------"); + System.out.println("invocations=" + invocations + " " + + "method compiled=" + WB.isMethodCompiled(uncommonTrap_m) + " " + + "decompileCount=" + WB.getMethodDecompileCount(uncommonTrap_m) + "\n" + + "trapCount=" + WB.getMethodTrapCount(uncommonTrap_m) + " " + + "trapCount(class_check)=" + WB.getMethodTrapCount(uncommonTrap_m, "class_check") + " " + + "trapCount(" + bimorphicTrapName + ")=" + + WB.getMethodTrapCount(uncommonTrap_m, bimorphicTrapName) + "\n" + + "globalDeoptCount=" + WB.getDeoptCount() + " " + + "globalDeoptCount(class_check)=" + WB.getDeoptCount("class_check", null) + " " + + "globalDeoptCount(" + bimorphicTrapName + ")=" + + WB.getDeoptCount(bimorphicTrapName, null)); + System.out.println("-----------------------------------------------------------------"); + } + + private static void check(Method uncommonTrap_m, int invocations, boolean isCompiled, int decompileCount, + int trapCount, int trapCountClassCheck, int trapCountBimorphic, + int deoptCount, int deoptCountClassCheck, int deoptCountBimorphic) { + + printCounters(uncommonTrap_m, invocations); + + Asserts.assertEQ(isCompiled, WB.isMethodCompiled(uncommonTrap_m), + "Wrong compilation status."); + Asserts.assertEQ(decompileCount, WB.getMethodDecompileCount(uncommonTrap_m), + "Wrong number of decompilations."); + Asserts.assertEQ(trapCount, WB.getMethodTrapCount(uncommonTrap_m), + "Wrong number of traps."); + Asserts.assertEQ(trapCountClassCheck, WB.getMethodTrapCount(uncommonTrap_m, "class_check"), + "Wrong number of traps."); + Asserts.assertEQ(trapCountBimorphic, WB.getMethodTrapCount(uncommonTrap_m, bimorphicTrapName), + "Wrong number of traps."); + Asserts.assertEQ(deoptCount, WB.getDeoptCount(), + "Wrong number of deoptimizations."); + Asserts.assertEQ(deoptCountClassCheck, WB.getDeoptCount("class_check", null), + "Wrong number of class_check deoptimizations."); + Asserts.assertEQ(deoptCountBimorphic, WB.getDeoptCount(bimorphicTrapName, null), + "Wrong number of " + bimorphicTrapName + "deoptimizations."); + } + public static void main(String[] args) throws Exception { + + // Get a handle of the test method for usage with the WhiteBox API. + Method uncommonTrap_m = Decompile.class + .getDeclaredMethod("uncommonTrap", new Class[] { Base.class }); + + int invocations = 0; + Base b = new Base(); + // This is a little tricky :) We have to define 'x' already here otherwise + // the class 'X' won't be loaded and 'uncommonTrap()' will be compiled without + // a class check but a CHA dependency that class 'B' has no subtypes. + X x = new X(); + Y y = new Y(); + + // Warmup and compile with an object of type 'Base' as receiver, but don't invoke compiled code. + while(!WB.isMethodCompiled(uncommonTrap_m)) { + invocations++; + uncommonTrap(b); + } + check(uncommonTrap_m, invocations, true /* is_compiled */, 0 /* decompileCount */, + 0 /* trapCount */, 0 /* trapCountClassCheck */, 0 /* trapCountBimorphic */, + 0 /* deoptCount */, 0 /* deoptCountClassCheck */, 0 /* deoptCountBimorphic */); + + // Invoke compiled code 'PerBytecodeTrapLimit' times with an receiver object of type 'X'. + // This should deoptimize 'PerBytecodeTrapLimit' times and finally decompile the method. + for (int i = 0; i < PerBytecodeTrapLimit; i++) { + invocations++; + uncommonTrap(x); + } + check(uncommonTrap_m, invocations, false /* is_compiled */, 1 /* decompileCount */, + PerBytecodeTrapLimit /* trapCount */, PerBytecodeTrapLimit /* trapCountClassCheck */, 0 /* trapCountBimorphic */, + PerBytecodeTrapLimit /* deoptCount */, PerBytecodeTrapLimit /* deoptCountClassCheck */, 0 /* deoptCountBimorphic */); + + // Invoke the method 'Tier0InvokeNotifyFreq' more times with an receiver object of type 'X'. + // This should re-compile the method again with bimorphic inlining for receiver types 'Base' and 'X'. + for (int i = 0; i < Tier0InvokeNotifyFreq; i++) { + invocations++; + uncommonTrap(x); + } + check(uncommonTrap_m, invocations, true /* is_compiled */, 1 /* decompileCount */, + PerBytecodeTrapLimit /* trapCount */, PerBytecodeTrapLimit /* trapCountClassCheck */, 0 /* trapCountBimorphic */, + PerBytecodeTrapLimit /* deoptCount */, PerBytecodeTrapLimit /* deoptCountClassCheck */, 0 /* deoptCountBimorphic */); + + // Invoke compiled code 'PerBytecodeTrapLimit' times with an receiver object of type 'Y'. + // This should deoptimize 'PerBytecodeTrapLimit' times and finally decompile the method. + for (int i = 0; i < PerBytecodeTrapLimit; i++) { + invocations++; + uncommonTrap(y); + } + check(uncommonTrap_m, invocations, false /* is_compiled */, 2 /* decompileCount */, + 2*PerBytecodeTrapLimit /* trapCount */, PerBytecodeTrapLimit /* trapCountClassCheck */, PerBytecodeTrapLimit /* trapCountBimorphic */, + 2*PerBytecodeTrapLimit /* deoptCount */, PerBytecodeTrapLimit /* deoptCountClassCheck */, PerBytecodeTrapLimit /* deoptCountBimorphic */); + } +} \ No newline at end of file diff --git a/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java b/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java index a9f347dbb073f..3b997d74dc16f 100644 --- a/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java +++ b/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java @@ -142,6 +142,7 @@ public boolean isAvailable() { new LogMessageWithLevel("Scanned Cards", Level.DEBUG), new LogMessageWithLevel("Scanned Blocks", Level.DEBUG), new LogMessageWithLevel("Claimed Chunks", Level.DEBUG), + new LogMessageWithLevel("Found Roots", Level.DEBUG), // Code Roots Scan new LogMessageWithLevel("Code Root Scan", Level.DEBUG), // Object Copy diff --git a/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java b/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java index 4d5203167d3df..a5ebe6f5de8a1 100644 --- a/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java +++ b/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java @@ -40,22 +40,19 @@ import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.FunctionDescriptor; import jdk.incubator.foreign.SymbolLookup; +import jdk.incubator.foreign.ValueLayout; import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; - -import static jdk.incubator.foreign.CLinker.C_INT; public class TestLinkToNativeRBP { static { System.loadLibrary("LinkToNativeRBP"); } - final static CLinker abi = CLinker.getInstance(); + final static CLinker abi = CLinker.systemCLinker(); static final SymbolLookup lookup = SymbolLookup.loaderLookup(); final static MethodHandle foo = abi.downcallHandle(lookup.lookup("foo").get(), - MethodType.methodType(int.class), - FunctionDescriptor.of(C_INT)); + FunctionDescriptor.of(ValueLayout.JAVA_INT)); static int foo() throws Throwable { return (int)foo.invokeExact(); diff --git a/test/hotspot/jtreg/runtime/CommandLine/PrintTouchedMethodsJcmd.java b/test/hotspot/jtreg/runtime/CommandLine/PrintTouchedMethodsJcmd.java index f3188007941f3..26f0c3ca0dc2b 100644 --- a/test/hotspot/jtreg/runtime/CommandLine/PrintTouchedMethodsJcmd.java +++ b/test/hotspot/jtreg/runtime/CommandLine/PrintTouchedMethodsJcmd.java @@ -41,10 +41,6 @@ public static void main(String args[]) throws Exception { var pb = new ProcessBuilder(); pb.command(new String[] {JDKToolFinder.getJDKTool("jcmd"), pid, "VM.print_touched_methods"}); var output = new OutputAnalyzer(pb.start()); - try { - output.shouldContain("PrintTouchedMethodsJcmd.main:([Ljava/lang/String;)V"); - } catch (RuntimeException e) { - output.shouldContain("Unknown diagnostic command"); - } - } + output.shouldContain("PrintTouchedMethodsJcmd.main:([Ljava/lang/String;)V"); + } } diff --git a/test/hotspot/jtreg/runtime/Monitor/MonitorUsedDeflationThresholdTest.java b/test/hotspot/jtreg/runtime/Monitor/MonitorUsedDeflationThresholdTest.java index 6bae009a84daf..5dd01e4262bcd 100644 --- a/test/hotspot/jtreg/runtime/Monitor/MonitorUsedDeflationThresholdTest.java +++ b/test/hotspot/jtreg/runtime/Monitor/MonitorUsedDeflationThresholdTest.java @@ -81,6 +81,11 @@ public static void main(String[] args) throws Exception { // of monitors for threads that call Object.wait(). "-XX:+UnlockDiagnosticVMOptions", "-XX:AvgMonitorsPerThreadEstimate=1", + // MonitorUsedDeflationThreshold == 10 means we'll request + // deflations when 10% of monitors are used rather than the + // default 90%. This should allow the test to tolerate a burst + // of used monitors by threads not under this test's control. + "-XX:MonitorUsedDeflationThreshold=10", // Enable monitorinflation logging so we can see that // MonitorUsedDeflationThreshold and // NoAsyncDeflationProgressMaxoption are working. @@ -89,8 +94,9 @@ public static void main(String[] args) throws Exception { "-Xlog:safepoint+cleanup=info", "-Xlog:safepoint+stats=debug", // Run the test with inflate_count == 33 since that - // reproduced the bug with JDK13. Anything above the - // in_use_list_ceiling will do the trick. + // reproduced the bug with JDK13. With inflate_count == 33, an + // initial ceiling == 12 and MonitorUsedDeflationThreshold == 10, + // we should hit NoAsyncDeflationProgressMax at least 3 times. "MonitorUsedDeflationThresholdTest", "33"); OutputAnalyzer output_detail = new OutputAnalyzer(pb.start()); @@ -111,6 +117,8 @@ public static void main(String[] args) throws Exception { throw new RuntimeException("Did not find too_many string in output.\n"); } System.out.println("too_many='" + too_many + "'"); + // Uncomment the following line for dumping test output in passing runs: + // output_detail.reportDiagnosticSummary(); System.out.println("PASSED."); return; diff --git a/test/hotspot/jtreg/runtime/jni/checked/TestPrimitiveArrayCriticalWithBadParam.java b/test/hotspot/jtreg/runtime/jni/checked/TestPrimitiveArrayCriticalWithBadParam.java index a341c5be877de..c64b21c7d1ba9 100644 --- a/test/hotspot/jtreg/runtime/jni/checked/TestPrimitiveArrayCriticalWithBadParam.java +++ b/test/hotspot/jtreg/runtime/jni/checked/TestPrimitiveArrayCriticalWithBadParam.java @@ -55,6 +55,7 @@ public static void main(String[] args) { private static void runTest() { List pbArgs = new ArrayList<>(); + pbArgs.add("-XX:-CreateCoredumpOnCrash"); pbArgs.add("-Xcheck:jni"); pbArgs.add("-Djava.library.path=" + Utils.TEST_NATIVE_PATH); pbArgs.add(TestPrimitiveArrayCriticalWithBadParam.class.getName()); diff --git a/test/hotspot/jtreg/serviceability/jvmti/VMObjectAlloc/VMObjectAllocTest.java b/test/hotspot/jtreg/serviceability/jvmti/VMObjectAlloc/VMObjectAllocTest.java new file mode 100644 index 0000000000000..42e50b4e45dfb --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/VMObjectAlloc/VMObjectAllocTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @summary Verifies that a VMObjectAlloc event is generated for object created using MethodHandle + * @requires vm.jvmti + * @run main/othervm/native -agentlib:VMObjectAlloc VMObjectAllocTest + */ + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +public class VMObjectAllocTest { + + private static native int getNumberOfAllocation(); + + public VMObjectAllocTest(String str) { + } + + public static void main(String[] args) throws Throwable { + + MethodHandles.Lookup publicLookup = MethodHandles.publicLookup(); + MethodType mt = MethodType.methodType(void.class, String.class); + MethodHandle mh = publicLookup.findConstructor(VMObjectAllocTest.class, mt); + mh.invoke("str"); + + if (getNumberOfAllocation() != 1) { + throw new Exception("Number of allocation != 1"); + } + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/VMObjectAlloc/libVMObjectAlloc.cpp b/test/hotspot/jtreg/serviceability/jvmti/VMObjectAlloc/libVMObjectAlloc.cpp new file mode 100644 index 0000000000000..fc5113f1aadc6 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/VMObjectAlloc/libVMObjectAlloc.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include "jvmti.h" + +extern "C" { + +static int number_of_allocation = 0; + +extern JNIEXPORT void JNICALL +VMObjectAlloc(jvmtiEnv *jvmti, + JNIEnv* jni, + jthread thread, + jobject object, + jclass cls, + jlong size) { + char *signature = NULL; + jvmtiError err = jvmti->GetClassSignature(cls, &signature, NULL); + if (err != JVMTI_ERROR_NONE) { + jni->FatalError("Failed during the GetClassSignature call"); + } + + printf("VMObjectAlloc called for %s\n", signature); + + if (!strcmp(signature, "LVMObjectAllocTest;")) { + number_of_allocation++; + } +} + + +JNIEXPORT jint JNICALL +Java_VMObjectAllocTest_getNumberOfAllocation(JNIEnv *env, jclass cls) { + return number_of_allocation; +} + +extern JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { + jvmtiEnv *jvmti; + jvmtiEventCallbacks callbacks; + jvmtiError err; + jvmtiCapabilities caps; + + if (jvm->GetEnv((void **) &jvmti, JVMTI_VERSION) != JNI_OK) { + return JNI_ERR; + } + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.VMObjectAlloc = &VMObjectAlloc; + memset(&caps, 0, sizeof(caps)); + caps.can_generate_vm_object_alloc_events = 1; + + err = jvmti->AddCapabilities( &caps); + if (err != JVMTI_ERROR_NONE) { + return JNI_ERR; + } + + err = jvmti->SetEventCallbacks(&callbacks, sizeof(jvmtiEventCallbacks)); + if (err != JVMTI_ERROR_NONE) { + return JNI_ERR; + } + + err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_OBJECT_ALLOC , NULL); + if (err != JVMTI_ERROR_NONE) { + return JNI_ERR; + } + + return JNI_OK; +} + +} //extern "C" diff --git a/test/hotspot/jtreg/vmTestbase/gc/gctests/LoadUnloadGC/LoadUnloadGC.java b/test/hotspot/jtreg/vmTestbase/gc/gctests/LoadUnloadGC/LoadUnloadGC.java index edd65b81145d6..9b79865462d6f 100644 --- a/test/hotspot/jtreg/vmTestbase/gc/gctests/LoadUnloadGC/LoadUnloadGC.java +++ b/test/hotspot/jtreg/vmTestbase/gc/gctests/LoadUnloadGC/LoadUnloadGC.java @@ -28,11 +28,11 @@ * @summary converted from VM Testbase gc/gctests/LoadUnloadGC. * VM Testbase keywords: [gc, stress, stressopt, nonconcurrent, monitoring] * VM Testbase readme: - * In this test a 1000 classes are loaded and unloaded in a loop. + * In this test 1000 classes are loaded and unloaded in a loop. * Class0 gets loaded which results in Class1 getting loaded and so on all - * the way uptill class1000. The classes should be unloaded whenever a + * the way up to class1000. The classes should be unloaded whenever a * garbage collection takes place because their classloader is made unreachable - * at the end of the each loop iteration. The loop is repeated 1000 times. + * at the end of each loop iteration. The loop is repeated 1000 times. * * @requires vm.opt.final.ClassUnloading * @library /vmTestbase @@ -45,6 +45,30 @@ * gc.gctests.LoadUnloadGC.LoadUnloadGC */ +/* + * @test + * @key stress + * + * @summary converted from VM Testbase gc/gctests/LoadUnloadGC. + * VM Testbase keywords: [gc, stress, stressopt, nonconcurrent, monitoring] + * VM Testbase readme: + * In this test 1000 classes are loaded and unloaded in a loop. + * Class0 gets loaded which results in Class1 getting loaded and so on all + * the way up to class1000. The classes should be unloaded whenever a + * garbage collection takes place because their classloader is made unreachable + * at the end of each loop iteration. The loop is repeated 1000 times. + * + * @requires vm.opt.final.ClassUnloading + * @library /vmTestbase + * /test/lib + * @build nsk.share.gc.ClassChain + * @run main/othervm + * -XX:MaxMetaspaceSize=64M + * -XX:MetaspaceSize=64M + * -XX:CompressedClassSpaceSize=32M + * gc.gctests.LoadUnloadGC.LoadUnloadGC + */ + package gc.gctests.LoadUnloadGC; import nsk.share.test.*; diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ObjectReference/referringObjects/referringObjects002/referringObjects002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ObjectReference/referringObjects/referringObjects002/referringObjects002.java index 95d3edf1050fa..f1a2432b57362 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ObjectReference/referringObjects/referringObjects002/referringObjects002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ObjectReference/referringObjects/referringObjects002/referringObjects002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,6 +56,8 @@ * @build nsk.jdi.ObjectReference.referringObjects.referringObjects002.referringObjects002 * nsk.jdi.ObjectReference.referringObjects.referringObjects002.referringObjects002a * nsk.share.jdi.TestClass1 + * @build sun.hotspot.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox * @run main/othervm/native * nsk.jdi.ObjectReference.referringObjects.referringObjects002.referringObjects002 * -verbose @@ -63,7 +65,8 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="-Xmx256M ${test.vm.opts} ${test.java.opts}" + * -debugee.vmkeys="-Xmx256M -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI ${test.vm.opts} ${test.java.opts}" * -testClassPath ${test.class.path} */ @@ -97,6 +100,10 @@ protected String debuggeeClassName() { public void checkClassObjectReferrersCount(ClassObjectReference classObjectReference, int expectedCount) { int referrersCount = classObjectReference.referringObjects(0).size(); + log.display("References:"); + for (ObjectReference ref: classObjectReference.referringObjects(0)) { + log.display(ref); + } if (referrersCount != expectedCount) { setSuccess(false); @@ -137,7 +144,6 @@ protected void doTest() { expectedReferrersCount = 3; checkClassObjectReferrersCount(classObjectReference, expectedReferrersCount); - // disable collection and try unload class object classObjectReference.disableCollection(); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ObjectReference/referringObjects/referringObjects002/referringObjects002a.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ObjectReference/referringObjects/referringObjects002/referringObjects002a.java index 1e90e159b58ab..6a0489d62ead5 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ObjectReference/referringObjects/referringObjects002/referringObjects002a.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ObjectReference/referringObjects/referringObjects002/referringObjects002a.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ import nsk.share.jdi.HeapwalkingDebuggee; import java.io.*; import java.util.*; +import sun.hotspot.WhiteBox; /* * Class create and save given number of class instances @@ -56,6 +57,8 @@ public class referringObjects002a extends HeapwalkingDebuggee { final static public String COMMAND_DELETE_CLASS_OBJECT_REFERRERS = "deleteClassObjectReferrers"; + private final WhiteBox WB = WhiteBox.getWhiteBox(); + public static void main(String args[]) { new referringObjects002a().doTest(args); } @@ -76,6 +79,9 @@ public void createClassObjectReferrers(String className, int instanceCount) { for (String referenceType : ObjectInstancesManager.allReferenceTypes) classReferrers.add(new ReferringObject(klass, referenceType)); + // force full GC with WB to ensure that weak refernces are collected + // j.l.i.MethodType has ConcurrentWeakInternSet which might contain references to TestClass1 + WB.fullGC(); } // delete all created references to class object diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/jvmti/agent_tools.cpp b/test/hotspot/jtreg/vmTestbase/nsk/share/jvmti/agent_tools.cpp index 660e2fc54e51b..2412bd630401a 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/jvmti/agent_tools.cpp +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/jvmti/agent_tools.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -138,34 +138,25 @@ jvmtiEnv* nsk_jvmti_getAgentJVMTIEnv() { return jvmti_env; } -/* ============================================================================= */ -static void set_agent_thread_state(thread_state_t value) { - rawMonitorEnter(jvmti_env, agent_data.monitor); - agent_data.thread_state = value; - rawMonitorNotify(jvmti_env, agent_data.monitor); - rawMonitorExit(jvmti_env, agent_data.monitor); -} - /** Wrapper for user agent thread. */ static void JNICALL agentThreadWrapper(jvmtiEnv* jvmti_env, JNIEnv* agentJNI, void* arg) { jni_env = agentJNI; - /* run user agent proc */ - { - set_agent_thread_state(RUNNABLE); + rawMonitorEnter(jvmti_env, agent_data.monitor); + agent_data.thread_state = RUNNABLE; + rawMonitorNotify(jvmti_env, agent_data.monitor); + rawMonitorExit(jvmti_env, agent_data.monitor); - NSK_TRACE((*agentThreadProc)(jvmti_env, agentJNI, agentThreadArg)); + NSK_TRACE((*agentThreadProc)(jvmti_env, agentJNI, agentThreadArg)); - set_agent_thread_state(TERMINATED); - } + rawMonitorEnter(jvmti_env, agent_data.monitor); + agent_data.thread_state = TERMINATED; + agentJNI->DeleteGlobalRef(agentThread); + agentThread = NULL; + rawMonitorNotify(jvmti_env, agent_data.monitor); + rawMonitorExit(jvmti_env, agent_data.monitor); - /* finalize agent thread */ - { - /* gelete global ref for agent thread */ - agentJNI->DeleteGlobalRef(agentThread); - agentThread = NULL; - } } /** Start wrapper for user agent thread. */ diff --git a/test/hotspot/jtreg/vmTestbase/vm/mlvm/hiddenloader/stress/oome/heap/Test.java b/test/hotspot/jtreg/vmTestbase/vm/mlvm/hiddenloader/stress/oome/heap/Test.java index 9d0a25b00e433..ea59685dfd3df 100644 --- a/test/hotspot/jtreg/vmTestbase/vm/mlvm/hiddenloader/stress/oome/heap/Test.java +++ b/test/hotspot/jtreg/vmTestbase/vm/mlvm/hiddenloader/stress/oome/heap/Test.java @@ -63,7 +63,7 @@ public class Test extends MlvmOOMTest { @Override protected void checkOOME(OutOfMemoryError oome) { String message = oome.getMessage(); - if (!"Java heap space".equals(message)) { + if (!message.startsWith("Java heap space")) { throw new RuntimeException("TEST FAIL : wrong OOME", oome); } } diff --git a/test/jdk/java/foreign/CallGeneratorHelper.java b/test/jdk/java/foreign/CallGeneratorHelper.java index 9a795525e1f32..a69fe1a5d81b9 100644 --- a/test/jdk/java/foreign/CallGeneratorHelper.java +++ b/test/jdk/java/foreign/CallGeneratorHelper.java @@ -22,14 +22,19 @@ * */ +import jdk.incubator.foreign.Addressable; +import jdk.incubator.foreign.CLinker; +import jdk.incubator.foreign.FunctionDescriptor; import jdk.incubator.foreign.GroupLayout; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.NativeSymbol; import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.SegmentAllocator; import jdk.incubator.foreign.ValueLayout; +import java.lang.invoke.MethodHandle; import java.lang.invoke.VarHandle; import java.util.ArrayList; import java.util.List; @@ -40,12 +45,13 @@ import org.testng.annotations.*; -import static jdk.incubator.foreign.CLinker.*; import static org.testng.Assert.*; public class CallGeneratorHelper extends NativeTestHelper { - static SegmentAllocator IMPLICIT_ALLOCATOR = (size, align) -> MemorySegment.allocateNative(size, align, ResourceScope.newImplicitScope()); + static SegmentAllocator THROWING_ALLOCATOR = (size, align) -> { + throw new UnsupportedOperationException(); + }; static final int SAMPLE_FACTOR = Integer.parseInt((String)System.getProperties().getOrDefault("generator.sample.factor", "-1")); @@ -58,7 +64,7 @@ public static void assertStructEquals(MemorySegment actual, MemorySegment expect GroupLayout g = (GroupLayout) layout; for (MemoryLayout field : g.memberLayouts()) { if (field instanceof ValueLayout) { - VarHandle vh = g.varHandle(vhCarrier(field), MemoryLayout.PathElement.groupElement(field.name().orElseThrow())); + VarHandle vh = g.varHandle(MemoryLayout.PathElement.groupElement(field.name().orElseThrow())); assertEquals(vh.get(actual), vh.get(expected)); } } @@ -410,12 +416,9 @@ static Object makeArg(MemoryLayout layout, List> checks, boolea static void initStruct(MemorySegment str, GroupLayout g, List> checks, boolean check) throws ReflectiveOperationException { for (MemoryLayout l : g.memberLayouts()) { if (l.isPadding()) continue; - VarHandle accessor = g.varHandle(structFieldCarrier(l), MemoryLayout.PathElement.groupElement(l.name().get())); + VarHandle accessor = g.varHandle(MemoryLayout.PathElement.groupElement(l.name().get())); List> fieldsCheck = new ArrayList<>(); Object value = makeArg(l, fieldsCheck, check); - if (isPointer(l)) { - value = ((MemoryAddress)value).toRawLongValue(); - } //set value accessor.set(str, value); //add check @@ -424,11 +427,7 @@ static void initStruct(MemorySegment str, GroupLayout g, List> checks.add(o -> { MemorySegment actual = (MemorySegment)o; try { - if (isPointer(l)) { - fieldsCheck.get(0).accept(MemoryAddress.ofLong((long)accessor.get(actual))); - } else { - fieldsCheck.get(0).accept(accessor.get(actual)); - } + fieldsCheck.get(0).accept(accessor.get(actual)); } catch (Throwable ex) { throw new IllegalStateException(ex); } @@ -437,37 +436,23 @@ static void initStruct(MemorySegment str, GroupLayout g, List> } } - static Class structFieldCarrier(MemoryLayout layout) { - if (isPointer(layout)) { - return long.class; - } else if (layout instanceof ValueLayout) { - if (isIntegral(layout)) { - return int.class; - } else if (layout.bitSize() == 32) { - return float.class; - } else { - return double.class; - } - } else { - throw new IllegalStateException("Unexpected layout: " + layout); - } - } - - static Class paramCarrier(MemoryLayout layout) { + static Class carrier(MemoryLayout layout, boolean param) { if (layout instanceof GroupLayout) { return MemorySegment.class; } if (isPointer(layout)) { - return MemoryAddress.class; - } else if (layout instanceof ValueLayout) { - if (isIntegral(layout)) { - return int.class; - } else if (layout.bitSize() == 32) { - return float.class; - } else { - return double.class; - } + return param ? Addressable.class : MemoryAddress.class; + } else if (layout instanceof ValueLayout valueLayout) { + return valueLayout.carrier(); } else { throw new IllegalStateException("Unexpected layout: " + layout); } } + + MethodHandle downcallHandle(CLinker abi, NativeSymbol symbol, SegmentAllocator allocator, FunctionDescriptor descriptor) { + MethodHandle mh = abi.downcallHandle(symbol, descriptor); + if (descriptor.returnLayout().isPresent() && descriptor.returnLayout().get() instanceof GroupLayout) { + mh = mh.bindTo(allocator); + } + return mh; + } } diff --git a/test/jdk/java/foreign/NativeTestHelper.java b/test/jdk/java/foreign/NativeTestHelper.java index 9b57beac732ee..ca789f984f814 100644 --- a/test/jdk/java/foreign/NativeTestHelper.java +++ b/test/jdk/java/foreign/NativeTestHelper.java @@ -22,58 +22,90 @@ * */ +import jdk.incubator.foreign.Addressable; import jdk.incubator.foreign.CLinker; +import jdk.incubator.foreign.FunctionDescriptor; +import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.SegmentAllocator; +import jdk.incubator.foreign.ValueLayout; + +import java.lang.invoke.MethodHandle; public class NativeTestHelper { - static CLinker.TypeKind kind(MemoryLayout layout) { - return (CLinker.TypeKind)layout.attribute(CLinker.TypeKind.ATTR_NAME).orElseThrow( - () -> new IllegalStateException("Unexpected value layout: could not determine ABI class")); + public static boolean isIntegral(MemoryLayout layout) { + return layout instanceof ValueLayout valueLayout && isIntegral(valueLayout.carrier()); } - public static boolean isIntegral(MemoryLayout layout) { - return kind(layout).isIntegral(); + static boolean isIntegral(Class clazz) { + return clazz == byte.class || clazz == char.class || clazz == short.class + || clazz == int.class || clazz == long.class; } public static boolean isPointer(MemoryLayout layout) { - return kind(layout).isPointer(); + return layout instanceof ValueLayout valueLayout && valueLayout.carrier() == MemoryAddress.class; } - public static class NativeScope implements SegmentAllocator, AutoCloseable { - final ResourceScope resourceScope; - final ResourceScope.Handle scopeHandle; - final SegmentAllocator allocator; + // the constants below are useful aliases for C types. The type/carrier association is only valid for 64-bit platforms. - long allocatedBytes = 0; + /** + * The layout for the {@code bool} C type + */ + public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN; + /** + * The layout for the {@code char} C type + */ + public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE; + /** + * The layout for the {@code short} C type + */ + public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT.withBitAlignment(16); + /** + * The layout for the {@code int} C type + */ + public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT.withBitAlignment(32); - public NativeScope() { - this.resourceScope = ResourceScope.newConfinedScope(); - this.scopeHandle = resourceScope.acquire(); - this.allocator = SegmentAllocator.arenaAllocator(resourceScope); - } + /** + * The layout for the {@code long long} C type. + */ + public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64); + /** + * The layout for the {@code float} C type + */ + public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT.withBitAlignment(32); + /** + * The layout for the {@code double} C type + */ + public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE.withBitAlignment(64); + /** + * The {@code T*} native type. + */ + public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64); - @Override - public MemorySegment allocate(long bytesSize, long bytesAlignment) { - allocatedBytes += bytesSize; - return allocator.allocate(bytesSize, bytesAlignment); - } + private static CLinker LINKER = CLinker.systemCLinker(); - public ResourceScope scope() { - return resourceScope; - } + private static final MethodHandle FREE = LINKER.downcallHandle( + LINKER.lookup("free").get(), FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)); - public long allocatedBytes() { - return allocatedBytes; + private static final MethodHandle MALLOC = LINKER.downcallHandle( + LINKER.lookup("malloc").get(), FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.JAVA_LONG)); + + public static void freeMemory(Addressable address) { + try { + FREE.invokeExact(address); + } catch (Throwable ex) { + throw new IllegalStateException(ex); } + } - @Override - public void close() { - resourceScope.release(scopeHandle); - resourceScope.close(); + public static MemoryAddress allocateMemory(long size) { + try { + return (MemoryAddress)MALLOC.invokeExact(size); + } catch (Throwable ex) { + throw new IllegalStateException(ex); } } } diff --git a/test/jdk/java/foreign/SafeFunctionAccessTest.java b/test/jdk/java/foreign/SafeFunctionAccessTest.java index b6801ad888807..5c45f42cc2635 100644 --- a/test/jdk/java/foreign/SafeFunctionAccessTest.java +++ b/test/jdk/java/foreign/SafeFunctionAccessTest.java @@ -27,27 +27,31 @@ * @run testng/othervm --enable-native-access=ALL-UNNAMED SafeFunctionAccessTest */ +import jdk.incubator.foreign.Addressable; import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.FunctionDescriptor; +import jdk.incubator.foreign.NativeSymbol; import jdk.incubator.foreign.SymbolLookup; -import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import jdk.incubator.foreign.VaList; import org.testng.annotations.*; + import static org.testng.Assert.*; -public class SafeFunctionAccessTest { +public class SafeFunctionAccessTest extends NativeTestHelper { static { System.loadLibrary("SafeAccess"); } static MemoryLayout POINT = MemoryLayout.structLayout( - CLinker.C_INT, CLinker.C_INT + C_INT, C_INT ); static final SymbolLookup LOOKUP = SymbolLookup.loaderLookup(); @@ -59,26 +63,135 @@ public void testClosedStruct() throws Throwable { segment = MemorySegment.allocateNative(POINT, scope); } assertFalse(segment.scope().isAlive()); - MethodHandle handle = CLinker.getInstance().downcallHandle( + MethodHandle handle = CLinker.systemCLinker().downcallHandle( LOOKUP.lookup("struct_func").get(), - MethodType.methodType(void.class, MemorySegment.class), FunctionDescriptor.ofVoid(POINT)); handle.invokeExact(segment); } + @Test + public void testClosedStructAddr_6() throws Throwable { + MethodHandle handle = CLinker.systemCLinker().downcallHandle( + LOOKUP.lookup("addr_func_6").get(), + FunctionDescriptor.ofVoid(C_POINTER, C_POINTER, C_POINTER, C_POINTER, C_POINTER, C_POINTER)); + for (int i = 0 ; i < 6 ; i++) { + MemorySegment[] segments = new MemorySegment[]{ + MemorySegment.allocateNative(POINT, ResourceScope.newImplicitScope()), + MemorySegment.allocateNative(POINT, ResourceScope.newImplicitScope()), + MemorySegment.allocateNative(POINT, ResourceScope.newImplicitScope()), + MemorySegment.allocateNative(POINT, ResourceScope.newImplicitScope()), + MemorySegment.allocateNative(POINT, ResourceScope.newImplicitScope()), + MemorySegment.allocateNative(POINT, ResourceScope.newImplicitScope()) + }; + // check liveness + segments[i].scope().close(); + for (int j = 0 ; j < 6 ; j++) { + if (i == j) { + assertFalse(segments[j].scope().isAlive()); + } else { + assertTrue(segments[j].scope().isAlive()); + } + } + try { + handle.invokeWithArguments(segments); + fail(); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("Already closed")); + } + for (int j = 0 ; j < 6 ; j++) { + if (i != j) { + segments[j].scope().close(); // should succeed! + } + } + } + } + @Test(expectedExceptions = IllegalStateException.class) - public void testClosedPointer() throws Throwable { - MemoryAddress address; + public void testClosedVaList() throws Throwable { + VaList list; try (ResourceScope scope = ResourceScope.newConfinedScope()) { - address = MemorySegment.allocateNative(POINT, scope).address(); + list = VaList.make(b -> b.addVarg(C_INT, 42), scope); } - assertFalse(address.scope().isAlive()); - MethodHandle handle = CLinker.getInstance().downcallHandle( + assertFalse(list.scope().isAlive()); + MethodHandle handle = CLinker.systemCLinker().downcallHandle( LOOKUP.lookup("addr_func").get(), - MethodType.methodType(void.class, MemoryAddress.class), - FunctionDescriptor.ofVoid(CLinker.C_POINTER)); + FunctionDescriptor.ofVoid(C_POINTER)); - handle.invokeExact(address); + handle.invokeExact((Addressable)list); + } + + @Test(expectedExceptions = IllegalStateException.class) + public void testClosedUpcall() throws Throwable { + NativeSymbol upcall; + try (ResourceScope scope = ResourceScope.newConfinedScope()) { + MethodHandle dummy = MethodHandles.lookup().findStatic(SafeFunctionAccessTest.class, "dummy", MethodType.methodType(void.class)); + upcall = CLinker.systemCLinker().upcallStub(dummy, FunctionDescriptor.ofVoid(), scope); + } + assertFalse(upcall.scope().isAlive()); + MethodHandle handle = CLinker.systemCLinker().downcallHandle( + LOOKUP.lookup("addr_func").get(), + FunctionDescriptor.ofVoid(C_POINTER)); + + handle.invokeExact((Addressable)upcall); + } + + static void dummy() { } + + @Test + public void testClosedVaListCallback() throws Throwable { + MethodHandle handle = CLinker.systemCLinker().downcallHandle( + LOOKUP.lookup("addr_func_cb").get(), + FunctionDescriptor.ofVoid(C_POINTER, C_POINTER)); + + try (ResourceScope scope = ResourceScope.newConfinedScope()) { + VaList list = VaList.make(b -> b.addVarg(C_INT, 42), scope); + handle.invoke(list, scopeChecker(scope)); + } + } + + @Test + public void testClosedStructCallback() throws Throwable { + MethodHandle handle = CLinker.systemCLinker().downcallHandle( + LOOKUP.lookup("addr_func_cb").get(), + FunctionDescriptor.ofVoid(C_POINTER, C_POINTER)); + + try (ResourceScope scope = ResourceScope.newConfinedScope()) { + MemorySegment segment = MemorySegment.allocateNative(POINT, scope); + handle.invoke(segment, scopeChecker(scope)); + } + } + + @Test + public void testClosedUpcallCallback() throws Throwable { + MethodHandle handle = CLinker.systemCLinker().downcallHandle( + LOOKUP.lookup("addr_func_cb").get(), + FunctionDescriptor.ofVoid(C_POINTER, C_POINTER)); + + try (ResourceScope scope = ResourceScope.newConfinedScope()) { + MethodHandle dummy = MethodHandles.lookup().findStatic(SafeFunctionAccessTest.class, "dummy", MethodType.methodType(void.class)); + NativeSymbol upcall = CLinker.systemCLinker().upcallStub(dummy, FunctionDescriptor.ofVoid(), scope); + handle.invoke(upcall, scopeChecker(scope)); + } + } + + NativeSymbol scopeChecker(ResourceScope scope) { + try { + MethodHandle handle = MethodHandles.lookup().findStatic(SafeFunctionAccessTest.class, "checkScope", + MethodType.methodType(void.class, ResourceScope.class)); + handle = handle.bindTo(scope); + return CLinker.systemCLinker().upcallStub(handle, FunctionDescriptor.ofVoid(), ResourceScope.newImplicitScope()); + } catch (Throwable ex) { + throw new AssertionError(ex); + } + } + + static void checkScope(ResourceScope scope) { + try { + scope.close(); + fail("Scope closed unexpectedly!"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("kept alive")); //if acquired, fine + } } } diff --git a/test/jdk/java/foreign/StdLibTest.java b/test/jdk/java/foreign/StdLibTest.java index 4495ed159211a..1246a260b1d72 100644 --- a/test/jdk/java/foreign/StdLibTest.java +++ b/test/jdk/java/foreign/StdLibTest.java @@ -39,7 +39,6 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; -import java.util.Optional; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Function; @@ -48,17 +47,14 @@ import jdk.incubator.foreign.*; -import static jdk.incubator.foreign.MemoryAccess.*; - import org.testng.annotations.*; -import static jdk.incubator.foreign.CLinker.*; import static org.testng.Assert.*; @Test -public class StdLibTest { +public class StdLibTest extends NativeTestHelper { - final static CLinker abi = CLinker.getInstance(); + final static CLinker abi = CLinker.systemCLinker(); private StdLibHelper stdLibHelper = new StdLibHelper(); @@ -155,45 +151,36 @@ void test_vprintf(List args) throws Throwable { static class StdLibHelper { - static final SymbolLookup LOOKUP = CLinker.systemLookup(); - - final static MethodHandle strcat = abi.downcallHandle(LOOKUP.lookup("strcat").get(), - MethodType.methodType(MemoryAddress.class, MemoryAddress.class, MemoryAddress.class), - FunctionDescriptor.of(C_POINTER, C_POINTER, C_POINTER)); + final static MethodHandle strcat = abi.downcallHandle(abi.lookup("strcat").get(), + FunctionDescriptor.of(C_POINTER, C_POINTER, C_POINTER)) + .asType(MethodType.methodType(MemoryAddress.class, MemorySegment.class, MemorySegment.class)); // exact signature match - final static MethodHandle strcmp = abi.downcallHandle(LOOKUP.lookup("strcmp").get(), - MethodType.methodType(int.class, MemoryAddress.class, MemoryAddress.class), + final static MethodHandle strcmp = abi.downcallHandle(abi.lookup("strcmp").get(), FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER)); - final static MethodHandle puts = abi.downcallHandle(LOOKUP.lookup("puts").get(), - MethodType.methodType(int.class, MemoryAddress.class), + final static MethodHandle puts = abi.downcallHandle(abi.lookup("puts").get(), FunctionDescriptor.of(C_INT, C_POINTER)); - final static MethodHandle strlen = abi.downcallHandle(LOOKUP.lookup("strlen").get(), - MethodType.methodType(int.class, MemoryAddress.class), + final static MethodHandle strlen = abi.downcallHandle(abi.lookup("strlen").get(), FunctionDescriptor.of(C_INT, C_POINTER)); - final static MethodHandle gmtime = abi.downcallHandle(LOOKUP.lookup("gmtime").get(), - MethodType.methodType(MemoryAddress.class, MemoryAddress.class), + final static MethodHandle gmtime = abi.downcallHandle(abi.lookup("gmtime").get(), FunctionDescriptor.of(C_POINTER, C_POINTER)); - final static MethodHandle qsort = abi.downcallHandle(LOOKUP.lookup("qsort").get(), - MethodType.methodType(void.class, MemoryAddress.class, long.class, long.class, MemoryAddress.class), + final static MethodHandle qsort = abi.downcallHandle(abi.lookup("qsort").get(), FunctionDescriptor.ofVoid(C_POINTER, C_LONG_LONG, C_LONG_LONG, C_POINTER)); final static FunctionDescriptor qsortComparFunction = FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER); final static MethodHandle qsortCompar; - final static MethodHandle rand = abi.downcallHandle(LOOKUP.lookup("rand").get(), - MethodType.methodType(int.class), + final static MethodHandle rand = abi.downcallHandle(abi.lookup("rand").get(), FunctionDescriptor.of(C_INT)); - final static MethodHandle vprintf = abi.downcallHandle(LOOKUP.lookup("vprintf").get(), - MethodType.methodType(int.class, MemoryAddress.class, VaList.class), - FunctionDescriptor.of(C_INT, C_POINTER, C_VA_LIST)); + final static MethodHandle vprintf = abi.downcallHandle(abi.lookup("vprintf").get(), + FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER)); - final static MemoryAddress printfAddr = LOOKUP.lookup("printf").get(); + final static NativeSymbol printfAddr = abi.lookup("printf").get(); final static FunctionDescriptor printfBase = FunctionDescriptor.of(C_INT, C_POINTER); @@ -201,7 +188,7 @@ static class StdLibHelper { try { //qsort upcall handle qsortCompar = MethodHandles.lookup().findStatic(StdLibTest.StdLibHelper.class, "qsortCompare", - MethodType.methodType(int.class, MemorySegment.class, MemoryAddress.class, MemoryAddress.class)); + CLinker.upcallType(qsortComparFunction)); } catch (ReflectiveOperationException ex) { throw new IllegalStateException(ex); } @@ -209,44 +196,44 @@ static class StdLibHelper { String strcat(String s1, String s2) throws Throwable { try (ResourceScope scope = ResourceScope.newConfinedScope()) { - MemorySegment buf = MemorySegment.allocateNative(s1.length() + s2.length() + 1, scope); - MemorySegment other = toCString(s2, scope); - char[] chars = s1.toCharArray(); - for (long i = 0 ; i < chars.length ; i++) { - setByteAtOffset(buf, i, (byte)chars[(int)i]); - } - setByteAtOffset(buf, chars.length, (byte)'\0'); - return toJavaString(((MemoryAddress)strcat.invokeExact(buf.address(), other.address()))); + var malloc = SegmentAllocator.nativeAllocator(scope); + MemorySegment buf = malloc.allocate(s1.length() + s2.length() + 1); + buf.setUtf8String(0, s1); + MemorySegment other = malloc.allocateUtf8String(s2); + return ((MemoryAddress)strcat.invokeExact(buf, other)).getUtf8String(0); } } int strcmp(String s1, String s2) throws Throwable { try (ResourceScope scope = ResourceScope.newConfinedScope()) { - MemorySegment ns1 = toCString(s1, scope); - MemorySegment ns2 = toCString(s2, scope); - return (int)strcmp.invokeExact(ns1.address(), ns2.address()); + var malloc = SegmentAllocator.nativeAllocator(scope); + MemorySegment ns1 = malloc.allocateUtf8String(s1); + MemorySegment ns2 = malloc.allocateUtf8String(s2); + return (int)strcmp.invoke(ns1, ns2); } } int puts(String msg) throws Throwable { try (ResourceScope scope = ResourceScope.newConfinedScope()) { - MemorySegment s = toCString(msg, scope); - return (int)puts.invokeExact(s.address()); + var malloc = SegmentAllocator.nativeAllocator(scope); + MemorySegment s = malloc.allocateUtf8String(msg); + return (int)puts.invoke(s); } } int strlen(String msg) throws Throwable { try (ResourceScope scope = ResourceScope.newConfinedScope()) { - MemorySegment s = toCString(msg, scope); - return (int)strlen.invokeExact(s.address()); + var malloc = SegmentAllocator.nativeAllocator(scope); + MemorySegment s = malloc.allocateUtf8String(msg); + return (int)strlen.invoke(s); } } Tm gmtime(long arg) throws Throwable { try (ResourceScope scope = ResourceScope.newConfinedScope()) { MemorySegment time = MemorySegment.allocateNative(8, scope); - setLong(time, arg); - return new Tm((MemoryAddress)gmtime.invokeExact(time.address())); + time.set(C_LONG_LONG, 0, arg); + return new Tm((MemoryAddress)gmtime.invoke(time)); } } @@ -258,58 +245,57 @@ static class Tm { static final long SIZE = 56; Tm(MemoryAddress addr) { - this.base = addr.asSegment(SIZE, ResourceScope.globalScope()); + this.base = MemorySegment.ofAddress(addr, SIZE, ResourceScope.globalScope()); } int sec() { - return getIntAtOffset(base, 0); + return base.get(C_INT, 0); } int min() { - return getIntAtOffset(base, 4); + return base.get(C_INT, 4); } int hour() { - return getIntAtOffset(base, 8); + return base.get(C_INT, 8); } int mday() { - return getIntAtOffset(base, 12); + return base.get(C_INT, 12); } int mon() { - return getIntAtOffset(base, 16); + return base.get(C_INT, 16); } int year() { - return getIntAtOffset(base, 20); + return base.get(C_INT, 20); } int wday() { - return getIntAtOffset(base, 24); + return base.get(C_INT, 24); } int yday() { - return getIntAtOffset(base, 28); + return base.get(C_INT, 28); } boolean isdst() { - byte b = getByteAtOffset(base, 32); - return b != 0; + return base.get(C_BOOL, 32); } } int[] qsort(int[] arr) throws Throwable { //init native array try (ResourceScope scope = ResourceScope.newConfinedScope()) { - SegmentAllocator allocator = SegmentAllocator.ofScope(scope); - MemorySegment nativeArr = allocator.allocateArray(C_INT, arr); + var malloc = SegmentAllocator.nativeAllocator(scope); + MemorySegment nativeArr = malloc.allocateArray(C_INT, arr); //call qsort - MemoryAddress qsortUpcallStub = abi.upcallStub(qsortCompar.bindTo(nativeArr), qsortComparFunction, scope); + NativeSymbol qsortUpcallStub = abi.upcallStub(qsortCompar, qsortComparFunction, scope); - qsort.invokeExact(nativeArr.address(), (long)arr.length, C_INT.byteSize(), qsortUpcallStub); + qsort.invoke(nativeArr, (long)arr.length, C_INT.byteSize(), qsortUpcallStub); //convert back to Java array - return nativeArr.toIntArray(); + return nativeArr.toArray(C_INT); } } - static int qsortCompare(MemorySegment base, MemoryAddress addr1, MemoryAddress addr2) { - return getIntAtOffset(base, addr1.segmentOffset(base)) - - getIntAtOffset(base, addr2.segmentOffset(base)); + static int qsortCompare(MemoryAddress addr1, MemoryAddress addr2) { + return addr1.get(C_INT, 0) - + addr2.get(C_INT, 0); } int rand() throws Throwable { @@ -318,17 +304,19 @@ int rand() throws Throwable { int printf(String format, List args) throws Throwable { try (ResourceScope scope = ResourceScope.newConfinedScope()) { - MemorySegment formatStr = toCString(format, scope); - return (int)specializedPrintf(args).invokeExact(formatStr.address(), + var malloc = SegmentAllocator.nativeAllocator(scope); + MemorySegment formatStr = malloc.allocateUtf8String(format); + return (int)specializedPrintf(args).invoke(formatStr, args.stream().map(a -> a.nativeValue(scope)).toArray()); } } int vprintf(String format, List args) throws Throwable { try (ResourceScope scope = ResourceScope.newConfinedScope()) { - MemorySegment formatStr = toCString(format, scope); + var malloc = SegmentAllocator.nativeAllocator(scope); + MemorySegment formatStr = malloc.allocateUtf8String(format); VaList vaList = VaList.make(b -> args.forEach(a -> a.accept(b, scope)), scope); - return (int)vprintf.invokeExact(formatStr.address(), vaList); + return (int)vprintf.invoke(formatStr, vaList); } } @@ -336,11 +324,13 @@ private MethodHandle specializedPrintf(List args) { //method type MethodType mt = MethodType.methodType(int.class, MemoryAddress.class); FunctionDescriptor fd = printfBase; + List variadicLayouts = new ArrayList<>(args.size()); for (PrintfArg arg : args) { mt = mt.appendParameterTypes(arg.carrier); - fd = fd.withAppendedArgumentLayouts(arg.layout); + variadicLayouts.add(arg.layout); } - MethodHandle mh = abi.downcallHandle(printfAddr, mt, fd); + MethodHandle mh = abi.downcallHandle(printfAddr, + fd.asVariadic(variadicLayouts.toArray(new MemoryLayout[args.size()]))); return mh.asSpreader(1, Object[].class, args.size()); } } @@ -401,10 +391,14 @@ public static Object[][] printfArgs() { enum PrintfArg implements BiConsumer { - INTEGRAL(int.class, asVarArg(C_INT), "%d", scope -> 42, 42, VaList.Builder::vargFromInt), - STRING(MemoryAddress.class, asVarArg(C_POINTER), "%s", scope -> toCString("str", scope).address(), "str", VaList.Builder::vargFromAddress), - CHAR(byte.class, asVarArg(C_CHAR), "%c", scope -> (byte) 'h', 'h', (builder, layout, value) -> builder.vargFromInt(C_INT, (int)value)), - DOUBLE(double.class, asVarArg(C_DOUBLE), "%.4f", scope ->1.2345d, 1.2345d, VaList.Builder::vargFromDouble); + INTEGRAL(int.class, C_INT, "%d", scope -> 42, 42, VaList.Builder::addVarg), + STRING(MemoryAddress.class, C_POINTER, "%s", scope -> { + var segment = MemorySegment.allocateNative(4, scope); + segment.setUtf8String(0, "str"); + return segment.address(); + }, "str", VaList.Builder::addVarg), + CHAR(byte.class, C_CHAR, "%c", scope -> (byte) 'h', 'h', (builder, layout, value) -> builder.addVarg(C_INT, (int)value)), + DOUBLE(double.class, C_DOUBLE, "%.4f", scope ->1.2345d, 1.2345d, VaList.Builder::addVarg); final Class carrier; final ValueLayout layout; @@ -414,7 +408,7 @@ enum PrintfArg implements BiConsumer { @SuppressWarnings("rawtypes") final VaListBuilderCall builderCall; - PrintfArg(Class carrier, ValueLayout layout, String format, Function nativeValueFactory, Object javaValue, VaListBuilderCall builderCall) { + PrintfArg(Class carrier, L layout, String format, Function nativeValueFactory, Object javaValue, VaListBuilderCall builderCall) { this.carrier = carrier; this.layout = layout; this.format = format; @@ -429,8 +423,8 @@ public void accept(VaList.Builder builder, ResourceScope scope) { builderCall.build(builder, layout, nativeValueFactory.apply(scope)); } - interface VaListBuilderCall { - void build(VaList.Builder builder, ValueLayout layout, V value); + interface VaListBuilderCall { + void build(VaList.Builder builder, L layout, V value); } public Object nativeValue(ResourceScope scope) { diff --git a/test/jdk/java/foreign/TestAdaptVarHandles.java b/test/jdk/java/foreign/TestAdaptVarHandles.java index de88626948a68..9c0e6c0e55430 100644 --- a/test/jdk/java/foreign/TestAdaptVarHandles.java +++ b/test/jdk/java/foreign/TestAdaptVarHandles.java @@ -32,7 +32,6 @@ import jdk.incubator.foreign.MemoryHandles; import jdk.incubator.foreign.MemoryLayout; -import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.ValueLayout; @@ -86,18 +85,18 @@ public class TestAdaptVarHandles { } } - static final VarHandle intHandleIndexed = MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT) - .varHandle(int.class, MemoryLayout.PathElement.sequenceElement()); + static final VarHandle intHandleIndexed = MemoryLayout.sequenceLayout(ValueLayout.JAVA_INT) + .varHandle(MemoryLayout.PathElement.sequenceElement()); - static final VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); + static final VarHandle intHandle = ValueLayout.JAVA_INT.varHandle(); - static final VarHandle floatHandle = MemoryLayouts.JAVA_FLOAT.varHandle(float.class); + static final VarHandle floatHandle = ValueLayout.JAVA_FLOAT.varHandle(); @Test public void testFilterValue() throws Throwable { - ValueLayout layout = MemoryLayouts.JAVA_INT; + ValueLayout layout = ValueLayout.JAVA_INT; MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope()); - VarHandle intHandle = layout.varHandle(int.class); + VarHandle intHandle = layout.varHandle(); VarHandle i2SHandle = MemoryHandles.filterValue(intHandle, S2I, I2S); i2SHandle.set(segment, "1"); String oldValue = (String)i2SHandle.getAndAdd(segment, "42"); @@ -114,9 +113,9 @@ public void testFilterValue() throws Throwable { @Test public void testFilterValueComposite() throws Throwable { - ValueLayout layout = MemoryLayouts.JAVA_INT; + ValueLayout layout = ValueLayout.JAVA_INT; MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope()); - VarHandle intHandle = layout.varHandle(int.class); + VarHandle intHandle = layout.varHandle(); MethodHandle CTX_S2I = MethodHandles.dropArguments(S2I, 0, String.class, String.class); VarHandle i2SHandle = MemoryHandles.filterValue(intHandle, CTX_S2I, CTX_I2S); i2SHandle = MemoryHandles.insertCoordinates(i2SHandle, 1, "a", "b"); @@ -135,9 +134,9 @@ public void testFilterValueComposite() throws Throwable { @Test public void testFilterValueLoose() throws Throwable { - ValueLayout layout = MemoryLayouts.JAVA_INT; + ValueLayout layout = ValueLayout.JAVA_INT; MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope()); - VarHandle intHandle = layout.varHandle(int.class); + VarHandle intHandle = layout.varHandle(); VarHandle i2SHandle = MemoryHandles.filterValue(intHandle, O2I, I2O); i2SHandle.set(segment, "1"); String oldValue = (String)i2SHandle.getAndAdd(segment, "42"); @@ -159,19 +158,19 @@ public void testBadFilterCarrier() { @Test(expectedExceptions = IllegalArgumentException.class) public void testBadFilterUnboxArity() { - VarHandle floatHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); + VarHandle floatHandle = ValueLayout.JAVA_INT.varHandle(); MemoryHandles.filterValue(floatHandle, S2I.bindTo(""), I2S); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadFilterBoxArity() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); + VarHandle intHandle = ValueLayout.JAVA_INT.varHandle(); MemoryHandles.filterValue(intHandle, S2I, I2S.bindTo(42)); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadFilterBoxPrefixCoordinates() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); + VarHandle intHandle = ValueLayout.JAVA_INT.varHandle(); MemoryHandles.filterValue(intHandle, MethodHandles.dropArguments(S2I, 1, int.class), MethodHandles.dropArguments(I2S, 1, long.class)); @@ -179,31 +178,40 @@ public void testBadFilterBoxPrefixCoordinates() { @Test(expectedExceptions = IllegalArgumentException.class) public void testBadFilterBoxException() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); + VarHandle intHandle = ValueLayout.JAVA_INT.varHandle(); MemoryHandles.filterValue(intHandle, I2S, S2L_EX); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadFilterUnboxException() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); + VarHandle intHandle = ValueLayout.JAVA_INT.varHandle(); MemoryHandles.filterValue(intHandle, S2L_EX, I2S); } - @Test(expectedExceptions = IllegalArgumentException.class) + @Test(expectedExceptions = IllegalStateException.class) public void testBadFilterBoxHandleException() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); - MemoryHandles.filterValue(intHandle, S2I, I2S_EX); + VarHandle intHandle = ValueLayout.JAVA_INT.varHandle(); + VarHandle vh = MemoryHandles.filterValue(intHandle, S2I, I2S_EX); + try (ResourceScope scope = ResourceScope.newConfinedScope()) { + MemorySegment seg = MemorySegment.allocateNative(ValueLayout.JAVA_INT, scope); + vh.set(seg, "42"); + String x = (String) vh.get(seg); // should throw + } } - @Test(expectedExceptions = IllegalArgumentException.class) + @Test(expectedExceptions = IllegalStateException.class) public void testBadFilterUnboxHandleException() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); - MemoryHandles.filterValue(intHandle, S2I_EX, I2S); + VarHandle intHandle = ValueLayout.JAVA_INT.varHandle(); + VarHandle vh = MemoryHandles.filterValue(intHandle, S2I_EX, I2S); + try (ResourceScope scope = ResourceScope.newConfinedScope()) { + MemorySegment seg = MemorySegment.allocateNative(ValueLayout.JAVA_INT, scope); + vh.set(seg, "42"); // should throw + } } @Test public void testFilterCoordinates() throws Throwable { - ValueLayout layout = MemoryLayouts.JAVA_INT; + ValueLayout layout = ValueLayout.JAVA_INT; MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope()); VarHandle intHandle_longIndex = MemoryHandles.filterCoordinates(intHandleIndexed, 0, BASE_ADDR, S2L); intHandle_longIndex.set(segment, "0", 1); @@ -246,7 +254,7 @@ public void testBadFilterCoordinatesTooManyFilters() { @Test public void testInsertCoordinates() throws Throwable { - ValueLayout layout = MemoryLayouts.JAVA_INT; + ValueLayout layout = ValueLayout.JAVA_INT; MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope()); VarHandle intHandle_longIndex = MemoryHandles.insertCoordinates(intHandleIndexed, 0, segment, 0L); intHandle_longIndex.set(1); @@ -284,7 +292,7 @@ public void testBadInsertCoordinatesTooManyValues() { @Test public void testPermuteCoordinates() throws Throwable { - ValueLayout layout = MemoryLayouts.JAVA_INT; + ValueLayout layout = ValueLayout.JAVA_INT; MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope()); VarHandle intHandle_swap = MemoryHandles.permuteCoordinates(intHandleIndexed, List.of(long.class, MemorySegment.class), 1, 0); @@ -323,7 +331,7 @@ public void testBadPermuteCoordinatesIndexTooSmall() { @Test public void testCollectCoordinates() throws Throwable { - ValueLayout layout = MemoryLayouts.JAVA_INT; + ValueLayout layout = ValueLayout.JAVA_INT; MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope()); VarHandle intHandle_sum = MemoryHandles.collectCoordinates(intHandleIndexed, 1, SUM_OFFSETS); intHandle_sum.set(segment, -2L, 2L, 1); @@ -366,7 +374,7 @@ public void testBadCollectCoordinatesWrongFilterException() { @Test public void testDropCoordinates() throws Throwable { - ValueLayout layout = MemoryLayouts.JAVA_INT; + ValueLayout layout = ValueLayout.JAVA_INT; MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope()); VarHandle intHandle_dummy = MemoryHandles.dropCoordinates(intHandleIndexed, 1, float.class, String.class); intHandle_dummy.set(segment, 1f, "hello", 0L, 1); diff --git a/test/jdk/java/foreign/TestAddressHandle.java b/test/jdk/java/foreign/TestAddressHandle.java deleted file mode 100644 index de9bde07a5b26..0000000000000 --- a/test/jdk/java/foreign/TestAddressHandle.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -/* - * @test - * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false -Xverify:all TestAddressHandle - * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true -Xverify:all TestAddressHandle - * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false -Xverify:all TestAddressHandle - * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true -Xverify:all TestAddressHandle - */ - -import java.lang.invoke.*; -import java.nio.ByteOrder; -import jdk.incubator.foreign.*; - -import org.testng.annotations.*; -import static org.testng.Assert.*; - -public class TestAddressHandle { - - static final MethodHandle INT_TO_BOOL; - static final MethodHandle BOOL_TO_INT; - static final MethodHandle INT_TO_STRING; - static final MethodHandle STRING_TO_INT; - - static { - try { - INT_TO_BOOL = MethodHandles.lookup().findStatic(TestAddressHandle.class, "intToBool", - MethodType.methodType(boolean.class, int.class)); - BOOL_TO_INT = MethodHandles.lookup().findStatic(TestAddressHandle.class, "boolToInt", - MethodType.methodType(int.class, boolean.class)); - INT_TO_STRING = MethodHandles.lookup().findStatic(TestAddressHandle.class, "intToString", - MethodType.methodType(String.class, int.class)); - STRING_TO_INT = MethodHandles.lookup().findStatic(TestAddressHandle.class, "stringToInt", - MethodType.methodType(int.class, String.class)); - } catch (Throwable ex) { - throw new ExceptionInInitializerError(ex); - } - } - - @Test(dataProvider = "addressHandles") - public void testAddressHandle(VarHandle addrHandle, int byteSize) { - VarHandle longHandle = MemoryLayouts.JAVA_LONG.varHandle(long.class); - try (ResourceScope scope = ResourceScope.newConfinedScope()) { - MemorySegment segment = MemorySegment.allocateNative(8, scope); - MemorySegment target = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN ? - segment.asSlice(8 - byteSize) : - segment; - longHandle.set(segment, 42L); - MemoryAddress address = (MemoryAddress)addrHandle.get(target); - assertEquals(address.toRawLongValue(), 42L); - addrHandle.set(target, address.addOffset(1)); - long result = (long)longHandle.get(segment); - assertEquals(43L, result); - } - } - - @Test(dataProvider = "addressHandles") - public void testNull(VarHandle addrHandle, int byteSize) { - VarHandle longHandle = MemoryLayouts.JAVA_LONG.varHandle(long.class); - try (ResourceScope scope = ResourceScope.newConfinedScope()) { - MemorySegment segment = MemorySegment.allocateNative(8, scope); - longHandle.set(segment, 0L); - MemoryAddress address = (MemoryAddress)addrHandle.get(segment); - assertTrue(address == MemoryAddress.NULL); - } - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void testBadAdaptFloat() { - VarHandle floatHandle = MemoryLayouts.JAVA_FLOAT.varHandle(float.class); - MemoryHandles.asAddressVarHandle(floatHandle); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void testBadAdaptDouble() { - VarHandle doubleHandle = MemoryLayouts.JAVA_DOUBLE.varHandle(double.class); - MemoryHandles.asAddressVarHandle(doubleHandle); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void testBadAdaptBoolean() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); - VarHandle boolHandle = MemoryHandles.filterValue(intHandle, BOOL_TO_INT, INT_TO_BOOL); - MemoryHandles.asAddressVarHandle(boolHandle); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void testBadAdaptString() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); - VarHandle stringHandle = MemoryHandles.filterValue(intHandle, STRING_TO_INT, INT_TO_STRING); - MemoryHandles.asAddressVarHandle(stringHandle); - } - - @DataProvider(name = "addressHandles") - static Object[][] addressHandles() { - return new Object[][] { - // long - { MemoryHandles.asAddressVarHandle(at(MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder()), 0)), 8 }, - { MemoryHandles.asAddressVarHandle(MemoryLayouts.JAVA_LONG.varHandle(long.class)), 8 }, - - // int - { MemoryHandles.asAddressVarHandle(at(MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder()), 0)), 4 }, - { MemoryHandles.asAddressVarHandle(MemoryLayouts.JAVA_INT.varHandle(int.class)), 4 }, - - // short - { MemoryHandles.asAddressVarHandle(at(MemoryHandles.varHandle(short.class, ByteOrder.nativeOrder()), 0)), 2 }, - { MemoryHandles.asAddressVarHandle(MemoryLayouts.JAVA_SHORT.varHandle(short.class)), 2 }, - - // char - { MemoryHandles.asAddressVarHandle(at(MemoryHandles.varHandle(char.class, ByteOrder.nativeOrder()), 0)), 2 }, - { MemoryHandles.asAddressVarHandle(MemoryLayouts.JAVA_CHAR.varHandle(char.class)), 2 }, - - // byte - { MemoryHandles.asAddressVarHandle(at(MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder()), 0)), 1 }, - { MemoryHandles.asAddressVarHandle(MemoryLayouts.JAVA_BYTE.varHandle(byte.class)), 1 } - }; - } - - static VarHandle at(VarHandle handle, long offset) { - return MemoryHandles.insertCoordinates(handle, 1, offset); - } - - static int boolToInt(boolean value) { - return value ? 1 : 0; - } - - static boolean intToBool(int value) { - return value != 0; - } - - static int stringToInt(String value) { - return value.length(); - } - - static String intToString(int value) { - return String.valueOf(value); - } -} diff --git a/test/jdk/java/foreign/TestArrayCopy.java b/test/jdk/java/foreign/TestArrayCopy.java new file mode 100644 index 0000000000000..b8a36307fdeec --- /dev/null +++ b/test/jdk/java/foreign/TestArrayCopy.java @@ -0,0 +1,542 @@ +/* + * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @run testng TestArrayCopy + */ + +import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.List; + +import jdk.incubator.foreign.MemoryLayout; +import jdk.incubator.foreign.MemorySegment; + +import jdk.incubator.foreign.ValueLayout; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * These tests exercise the MemoryCopy copyFromArray(...) and copyToArray(...). + * To make these tests more challenging the segment is a view of the given array, + * which makes the copy operations overlapping self-copies. Thus, this checks the claim: + * + *

    If the source (destination) segment is actually a view of the destination (source) array, + * and if the copy region of the source overlaps with the copy region of the destination, + * the copy of the overlapping region is performed as if the data in the overlapping region + * were first copied into a temporary segment before being copied to the destination.

    + */ +public class TestArrayCopy { + private static final ByteOrder NATIVE_ORDER = ByteOrder.nativeOrder(); + private static final ByteOrder NON_NATIVE_ORDER = NATIVE_ORDER == ByteOrder.LITTLE_ENDIAN + ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN; + + private static final int SEG_LENGTH_BYTES = 32; + private static final int SEG_OFFSET_BYTES = 8; + + @Test(dataProvider = "copyModesAndHelpers") + public void testSelfCopy(CopyMode mode, CopyHelper helper, String helperDebugString) { + int bytesPerElement = (int)helper.elementLayout.byteSize(); + int indexShifts = SEG_OFFSET_BYTES / bytesPerElement; + MemorySegment base = srcSegment(SEG_LENGTH_BYTES); + MemorySegment truth = truthSegment(base, helper, indexShifts, mode); + ByteOrder bo = mode.swap ? NON_NATIVE_ORDER : NATIVE_ORDER; + //CopyFrom + Object srcArr = helper.toArray(base); + int srcIndex = mode.direction ? 0 : indexShifts; + int srcCopyLen = helper.length(srcArr) - indexShifts; + MemorySegment dstSeg = helper.fromArray(srcArr); + long dstOffsetBytes = mode.direction ? SEG_OFFSET_BYTES : 0; + helper.copyFromArray(srcArr, srcIndex, srcCopyLen, dstSeg, dstOffsetBytes, bo); + assertEquals(truth.mismatch(dstSeg), -1); + //CopyTo + long srcOffsetBytes = mode.direction ? 0 : SEG_OFFSET_BYTES; + Object dstArr = helper.toArray(base); + MemorySegment srcSeg = helper.fromArray(dstArr).asReadOnly(); + int dstIndex = mode.direction ? indexShifts : 0; + int dstCopyLen = helper.length(dstArr) - indexShifts; + helper.copyToArray(srcSeg, srcOffsetBytes, dstArr, dstIndex, dstCopyLen, bo); + MemorySegment result = helper.fromArray(dstArr); + assertEquals(truth.mismatch(result), -1); + } + + @Test(dataProvider = "copyModesAndHelpers") + public void testUnalignedCopy(CopyMode mode, CopyHelper helper, String helperDebugString) { + int bytesPerElement = (int)helper.elementLayout.byteSize(); + int indexShifts = SEG_OFFSET_BYTES / bytesPerElement; + MemorySegment base = srcSegment(SEG_LENGTH_BYTES); + ByteOrder bo = mode.swap ? NON_NATIVE_ORDER : NATIVE_ORDER; + //CopyFrom + Object srcArr = helper.toArray(base); + int srcIndex = mode.direction ? 0 : indexShifts; + int srcCopyLen = helper.length(srcArr) - indexShifts; + MemorySegment dstSeg = helper.fromArray(srcArr); + long dstOffsetBytes = mode.direction ? (SEG_OFFSET_BYTES - 1) : 0; + helper.copyFromArray(srcArr, srcIndex, srcCopyLen, dstSeg, dstOffsetBytes, bo); + //CopyTo + long srcOffsetBytes = mode.direction ? 0 : (SEG_OFFSET_BYTES - 1); + Object dstArr = helper.toArray(base); + MemorySegment srcSeg = helper.fromArray(dstArr).asReadOnly(); + int dstIndex = mode.direction ? indexShifts : 0; + int dstCopyLen = helper.length(dstArr) - indexShifts; + helper.copyToArray(srcSeg, srcOffsetBytes, dstArr, dstIndex, dstCopyLen, bo); + } + + @Test(dataProvider = "copyModesAndHelpers") + public void testCopyOobLength(CopyMode mode, CopyHelper helper, String helperDebugString) { + int bytesPerElement = (int)helper.elementLayout.byteSize(); + MemorySegment base = srcSegment(SEG_LENGTH_BYTES); + //CopyFrom + Object srcArr = helper.toArray(base); + MemorySegment dstSeg = helper.fromArray(srcArr); + try { + helper.copyFromArray(srcArr, 0, (SEG_LENGTH_BYTES / bytesPerElement) * 2, dstSeg, 0, ByteOrder.nativeOrder()); + fail(); + } catch (IndexOutOfBoundsException ex) { + //ok + } + //CopyTo + Object dstArr = helper.toArray(base); + MemorySegment srcSeg = helper.fromArray(dstArr).asReadOnly(); + try { + helper.copyToArray(srcSeg, 0, dstArr, 0, (SEG_LENGTH_BYTES / bytesPerElement) * 2, ByteOrder.nativeOrder()); + fail(); + } catch (IndexOutOfBoundsException ex) { + //ok + } + } + + @Test(dataProvider = "copyModesAndHelpers") + public void testCopyNegativeIndices(CopyMode mode, CopyHelper helper, String helperDebugString) { + int bytesPerElement = (int)helper.elementLayout.byteSize(); + MemorySegment base = srcSegment(SEG_LENGTH_BYTES); + //CopyFrom + Object srcArr = helper.toArray(base); + MemorySegment dstSeg = helper.fromArray(srcArr); + try { + helper.copyFromArray(srcArr, -1, SEG_LENGTH_BYTES / bytesPerElement, dstSeg, 0, ByteOrder.nativeOrder()); + fail(); + } catch (IndexOutOfBoundsException ex) { + //ok + } + //CopyTo + Object dstArr = helper.toArray(base); + MemorySegment srcSeg = helper.fromArray(dstArr).asReadOnly(); + try { + helper.copyToArray(srcSeg, 0, dstArr, -1, SEG_LENGTH_BYTES / bytesPerElement, ByteOrder.nativeOrder()); + fail(); + } catch (IndexOutOfBoundsException ex) { + //ok + } + } + + @Test(dataProvider = "copyModesAndHelpers") + public void testCopyNegativeOffsets(CopyMode mode, CopyHelper helper, String helperDebugString) { + int bytesPerElement = (int)helper.elementLayout.byteSize(); + MemorySegment base = srcSegment(SEG_LENGTH_BYTES); + //CopyFrom + Object srcArr = helper.toArray(base); + MemorySegment dstSeg = helper.fromArray(srcArr); + try { + helper.copyFromArray(srcArr, 0, SEG_LENGTH_BYTES / bytesPerElement, dstSeg, -1, ByteOrder.nativeOrder()); + fail(); + } catch (IndexOutOfBoundsException ex) { + //ok + } + //CopyTo + Object dstArr = helper.toArray(base); + MemorySegment srcSeg = helper.fromArray(dstArr).asReadOnly(); + try { + helper.copyToArray(srcSeg, -1, dstArr, 0, SEG_LENGTH_BYTES / bytesPerElement, ByteOrder.nativeOrder()); + fail(); + } catch (IndexOutOfBoundsException ex) { + //ok + } + } + + @Test(dataProvider = "copyModesAndHelpers") + public void testCopyOobIndices(CopyMode mode, CopyHelper helper, String helperDebugString) { + int bytesPerElement = (int)helper.elementLayout.byteSize(); + MemorySegment base = srcSegment(SEG_LENGTH_BYTES); + //CopyFrom + Object srcArr = helper.toArray(base); + MemorySegment dstSeg = helper.fromArray(srcArr); + try { + helper.copyFromArray(srcArr, helper.length(srcArr) + 1, SEG_LENGTH_BYTES / bytesPerElement, dstSeg, 0, ByteOrder.nativeOrder()); + fail(); + } catch (IndexOutOfBoundsException ex) { + //ok + } + //CopyTo + Object dstArr = helper.toArray(base); + MemorySegment srcSeg = helper.fromArray(dstArr).asReadOnly(); + try { + helper.copyToArray(srcSeg, 0, dstArr, helper.length(dstArr) + 1, SEG_LENGTH_BYTES / bytesPerElement, ByteOrder.nativeOrder()); + fail(); + } catch (IndexOutOfBoundsException ex) { + //ok + } + } + + @Test(dataProvider = "copyModesAndHelpers") + public void testCopyOobOffsets(CopyMode mode, CopyHelper helper, String helperDebugString) { + int bytesPerElement = (int)helper.elementLayout.byteSize(); + MemorySegment base = srcSegment(SEG_LENGTH_BYTES); + //CopyFrom + Object srcArr = helper.toArray(base); + MemorySegment dstSeg = helper.fromArray(srcArr); + try { + helper.copyFromArray(srcArr, 0, SEG_LENGTH_BYTES / bytesPerElement, dstSeg, SEG_LENGTH_BYTES + 1, ByteOrder.nativeOrder()); + fail(); + } catch (IndexOutOfBoundsException ex) { + //ok + } + //CopyTo + Object dstArr = helper.toArray(base); + MemorySegment srcSeg = helper.fromArray(dstArr).asReadOnly(); + try { + helper.copyToArray(srcSeg, SEG_OFFSET_BYTES + 1, dstArr, 0, SEG_LENGTH_BYTES / bytesPerElement, ByteOrder.nativeOrder()); + fail(); + } catch (IndexOutOfBoundsException ex) { + //ok + } + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNotAnArraySrc() { + MemorySegment segment = MemorySegment.ofArray(new int[] {1, 2, 3, 4}); + MemorySegment.copy(segment, JAVA_BYTE, 0, new String[] { "hello" }, 0, 4); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNotAnArrayDst() { + MemorySegment segment = MemorySegment.ofArray(new int[] {1, 2, 3, 4}); + MemorySegment.copy(new String[] { "hello" }, 0, segment, JAVA_BYTE, 0, 4); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testCarrierMismatchSrc() { + MemorySegment segment = MemorySegment.ofArray(new int[] {1, 2, 3, 4}); + MemorySegment.copy(segment, JAVA_INT, 0, new byte[] { 1, 2, 3, 4 }, 0, 4); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testCarrierMismatchDst() { + MemorySegment segment = MemorySegment.ofArray(new int[] {1, 2, 3, 4}); + MemorySegment.copy(new byte[] { 1, 2, 3, 4 }, 0, segment, JAVA_INT, 0, 4); + } + + /***** Utilities *****/ + + public static MemorySegment srcSegment(int bytesLength) { + byte[] arr = new byte[bytesLength]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (byte)i; + } + return MemorySegment.ofArray(arr); + } + + public static MemorySegment truthSegment(MemorySegment srcSeg, CopyHelper helper, int indexShifts, CopyMode mode) { + VarHandle indexedHandleNO = MemoryLayout.sequenceLayout(helper.elementLayout.withOrder(NATIVE_ORDER)) + .varHandle(MemoryLayout.PathElement.sequenceElement()); + VarHandle indexedHandleNNO = MemoryLayout.sequenceLayout(helper.elementLayout.withOrder(NON_NATIVE_ORDER)) + .varHandle(MemoryLayout.PathElement.sequenceElement()); + MemorySegment dstSeg = MemorySegment.ofArray(srcSeg.toArray(JAVA_BYTE)); + int indexLength = (int) dstSeg.byteSize() / (int)helper.elementLayout.byteSize(); + if (mode.direction) { + if (mode.swap) { + for (int i = indexLength - 1; i >= indexShifts; i--) { + Object v = indexedHandleNNO.get(dstSeg, i - indexShifts); + indexedHandleNO.set(dstSeg, i, v); + } + } else { + for (int i = indexLength - 1; i >= indexShifts; i--) { + Object v = indexedHandleNO.get(dstSeg, i - indexShifts); + indexedHandleNO.set(dstSeg, i, v); + } + } + } else { //down + if (mode.swap) { + for (int i = indexShifts; i < indexLength; i++) { + Object v = indexedHandleNNO.get(dstSeg, i); + indexedHandleNO.set(dstSeg, i - indexShifts, v); + } + } else { + for (int i = indexShifts; i < indexLength; i++) { + Object v = indexedHandleNO.get(dstSeg, i); + indexedHandleNO.set(dstSeg, i - indexShifts, v); + } + } + } + return dstSeg; + } + + enum CopyMode { + UP_NO_SWAP(true, false), + UP_SWAP(true, true), + DOWN_NO_SWAP(false, false), + DOWN_SWAP(false, true); + + final boolean direction; + final boolean swap; + + CopyMode(boolean direction, boolean swap) { + this.direction = direction; + this.swap = swap; + } + } + + abstract static class CopyHelper { + + final L elementLayout; + final Class carrier; + + public CopyHelper(L elementLayout, Class carrier) { + this.elementLayout = elementLayout; + this.carrier = carrier; + } + + abstract void copyFromArray(X srcArr, int srcIndex, int srcCopyLen, MemorySegment dstSeg, long dstOffsetBytes, ByteOrder bo); + abstract void copyToArray(MemorySegment srcSeg, long srcOffsetBytes, X dstArr, int dstIndex, int dstCopyLen, ByteOrder bo); + abstract X toArray(MemorySegment segment); + abstract MemorySegment fromArray(X array); + abstract int length(X arr); + + @Override + public String toString() { + return "CopyHelper{" + + "elementLayout=" + elementLayout + + ", carrier=" + carrier.getName() + + '}'; + } + + static final CopyHelper BYTE = new CopyHelper<>(JAVA_BYTE, byte[].class) { + @Override + void copyFromArray(byte[] srcArr, int srcIndex, int srcCopyLen, MemorySegment dstSeg, long dstOffsetBytes, ByteOrder bo) { + MemorySegment.copy(srcArr, srcIndex, dstSeg, elementLayout.withOrder(bo), dstOffsetBytes, srcCopyLen); + } + + @Override + void copyToArray(MemorySegment srcSeg, long srcOffsetBytes, byte[] dstArr, int dstIndex, int dstCopyLen, ByteOrder bo) { + MemorySegment.copy(srcSeg, elementLayout.withOrder(bo), srcOffsetBytes, dstArr, dstIndex, dstCopyLen); + } + + @Override + byte[] toArray(MemorySegment segment) { + return segment.toArray(elementLayout); + } + + @Override + MemorySegment fromArray(byte[] array) { + return MemorySegment.ofArray(array); + } + + @Override + int length(byte[] arr) { + return arr.length; + } + }; + + static final CopyHelper CHAR = new CopyHelper<>(ValueLayout.JAVA_CHAR, char[].class) { + @Override + void copyFromArray(char[] srcArr, int srcIndex, int srcCopyLen, MemorySegment dstSeg, long dstOffsetBytes, ByteOrder bo) { + MemorySegment.copy(srcArr, srcIndex, dstSeg, elementLayout.withOrder(bo), dstOffsetBytes, srcCopyLen); + } + + @Override + void copyToArray(MemorySegment srcSeg, long srcOffsetBytes, char[] dstArr, int dstIndex, int dstCopyLen, ByteOrder bo) { + MemorySegment.copy(srcSeg, elementLayout.withOrder(bo), srcOffsetBytes, dstArr, dstIndex, dstCopyLen); + } + + @Override + char[] toArray(MemorySegment segment) { + return segment.toArray(elementLayout); + } + + @Override + MemorySegment fromArray(char[] array) { + return MemorySegment.ofArray(array); + } + + @Override + int length(char[] arr) { + return arr.length; + } + }; + + static final CopyHelper SHORT = new CopyHelper<>(ValueLayout.JAVA_SHORT, short[].class) { + @Override + void copyFromArray(short[] srcArr, int srcIndex, int srcCopyLen, MemorySegment dstSeg, long dstOffsetBytes, ByteOrder bo) { + MemorySegment.copy(srcArr, srcIndex, dstSeg, elementLayout.withOrder(bo), dstOffsetBytes, srcCopyLen); + } + + @Override + void copyToArray(MemorySegment srcSeg, long srcOffsetBytes, short[] dstArr, int dstIndex, int dstCopyLen, ByteOrder bo) { + MemorySegment.copy(srcSeg, elementLayout.withOrder(bo), srcOffsetBytes, dstArr, dstIndex, dstCopyLen); + } + + @Override + short[] toArray(MemorySegment segment) { + return segment.toArray(elementLayout); + } + + @Override + MemorySegment fromArray(short[] array) { + return MemorySegment.ofArray(array); + } + + @Override + int length(short[] arr) { + return arr.length; + } + }; + + static final CopyHelper INT = new CopyHelper<>(ValueLayout.JAVA_INT, int[].class) { + @Override + void copyFromArray(int[] srcArr, int srcIndex, int srcCopyLen, MemorySegment dstSeg, long dstOffsetBytes, ByteOrder bo) { + MemorySegment.copy(srcArr, srcIndex, dstSeg, elementLayout.withOrder(bo), dstOffsetBytes, srcCopyLen); + } + + @Override + void copyToArray(MemorySegment srcSeg, long srcOffsetBytes, int[] dstArr, int dstIndex, int dstCopyLen, ByteOrder bo) { + MemorySegment.copy(srcSeg, elementLayout.withOrder(bo), srcOffsetBytes, dstArr, dstIndex, dstCopyLen); + } + + @Override + int[] toArray(MemorySegment segment) { + return segment.toArray(elementLayout); + } + + @Override + MemorySegment fromArray(int[] array) { + return MemorySegment.ofArray(array); + } + + @Override + int length(int[] arr) { + return arr.length; + } + }; + + static final CopyHelper FLOAT = new CopyHelper<>(ValueLayout.JAVA_FLOAT, float[].class) { + @Override + void copyFromArray(float[] srcArr, int srcIndex, int srcCopyLen, MemorySegment dstSeg, long dstOffsetBytes, ByteOrder bo) { + MemorySegment.copy(srcArr, srcIndex, dstSeg, elementLayout.withOrder(bo), dstOffsetBytes, srcCopyLen); + } + + @Override + void copyToArray(MemorySegment srcSeg, long srcOffsetBytes, float[] dstArr, int dstIndex, int dstCopyLen, ByteOrder bo) { + MemorySegment.copy(srcSeg, elementLayout.withOrder(bo), srcOffsetBytes, dstArr, dstIndex, dstCopyLen); + } + + @Override + float[] toArray(MemorySegment segment) { + return segment.toArray(elementLayout); + } + + @Override + MemorySegment fromArray(float[] array) { + return MemorySegment.ofArray(array); + } + + @Override + int length(float[] arr) { + return arr.length; + } + }; + + static final CopyHelper LONG = new CopyHelper<>(ValueLayout.JAVA_LONG, long[].class) { + @Override + void copyFromArray(long[] srcArr, int srcIndex, int srcCopyLen, MemorySegment dstSeg, long dstOffsetBytes, ByteOrder bo) { + MemorySegment.copy(srcArr, srcIndex, dstSeg, elementLayout.withOrder(bo), dstOffsetBytes, srcCopyLen); + } + + @Override + void copyToArray(MemorySegment srcSeg, long srcOffsetBytes, long[] dstArr, int dstIndex, int dstCopyLen, ByteOrder bo) { + MemorySegment.copy(srcSeg, elementLayout.withOrder(bo), srcOffsetBytes, dstArr, dstIndex, dstCopyLen); + } + + @Override + long[] toArray(MemorySegment segment) { + return segment.toArray(elementLayout); + } + + @Override + MemorySegment fromArray(long[] array) { + return MemorySegment.ofArray(array); + } + + @Override + int length(long[] arr) { + return arr.length; + } + }; + + static final CopyHelper DOUBLE = new CopyHelper<>(ValueLayout.JAVA_DOUBLE, double[].class) { + @Override + void copyFromArray(double[] srcArr, int srcIndex, int srcCopyLen, MemorySegment dstSeg, long dstOffsetBytes, ByteOrder bo) { + MemorySegment.copy(srcArr, srcIndex, dstSeg, elementLayout.withOrder(bo), dstOffsetBytes, srcCopyLen); + } + + @Override + void copyToArray(MemorySegment srcSeg, long srcOffsetBytes, double[] dstArr, int dstIndex, int dstCopyLen, ByteOrder bo) { + MemorySegment.copy(srcSeg, elementLayout.withOrder(bo), srcOffsetBytes, dstArr, dstIndex, dstCopyLen); + } + + @Override + double[] toArray(MemorySegment segment) { + return segment.toArray(elementLayout); + } + + @Override + MemorySegment fromArray(double[] array) { + return MemorySegment.ofArray(array); + } + + @Override + int length(double[] arr) { + return arr.length; + } + }; + } + + @DataProvider + Object[][] copyModesAndHelpers() { + CopyHelper[] helpers = { CopyHelper.BYTE, CopyHelper.CHAR, CopyHelper.SHORT, CopyHelper.INT, + CopyHelper.FLOAT, CopyHelper.LONG, CopyHelper.DOUBLE }; + List results = new ArrayList<>(); + for (CopyHelper helper : helpers) { + for (CopyMode mode : CopyMode.values()) { + results.add(new Object[] { mode, helper, helper.toString() }); + } + } + return results.stream().toArray(Object[][]::new); + } +} diff --git a/test/jdk/java/foreign/TestArrays.java b/test/jdk/java/foreign/TestArrays.java index 451beeb4544dc..ca412554cb427 100644 --- a/test/jdk/java/foreign/TestArrays.java +++ b/test/jdk/java/foreign/TestArrays.java @@ -30,7 +30,6 @@ import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout.PathElement; -import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.SequenceLayout; @@ -43,45 +42,52 @@ import org.testng.annotations.*; +import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE; +import static jdk.incubator.foreign.ValueLayout.JAVA_CHAR; +import static jdk.incubator.foreign.ValueLayout.JAVA_DOUBLE; +import static jdk.incubator.foreign.ValueLayout.JAVA_FLOAT; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_LONG; +import static jdk.incubator.foreign.ValueLayout.JAVA_SHORT; import static org.testng.Assert.*; public class TestArrays { static SequenceLayout bytes = MemoryLayout.sequenceLayout(100, - MemoryLayouts.JAVA_BYTE + JAVA_BYTE ); static SequenceLayout chars = MemoryLayout.sequenceLayout(100, - MemoryLayouts.JAVA_CHAR + JAVA_CHAR ); static SequenceLayout shorts = MemoryLayout.sequenceLayout(100, - MemoryLayouts.JAVA_SHORT + JAVA_SHORT ); static SequenceLayout ints = MemoryLayout.sequenceLayout(100, - MemoryLayouts.JAVA_INT + JAVA_INT ); static SequenceLayout floats = MemoryLayout.sequenceLayout(100, - MemoryLayouts.JAVA_FLOAT + JAVA_FLOAT ); static SequenceLayout longs = MemoryLayout.sequenceLayout(100, - MemoryLayouts.JAVA_LONG + JAVA_LONG ); static SequenceLayout doubles = MemoryLayout.sequenceLayout(100, - MemoryLayouts.JAVA_DOUBLE + JAVA_DOUBLE ); - static VarHandle byteHandle = bytes.varHandle(byte.class, PathElement.sequenceElement()); - static VarHandle charHandle = chars.varHandle(char.class, PathElement.sequenceElement()); - static VarHandle shortHandle = shorts.varHandle(short.class, PathElement.sequenceElement()); - static VarHandle intHandle = ints.varHandle(int.class, PathElement.sequenceElement()); - static VarHandle floatHandle = floats.varHandle(float.class, PathElement.sequenceElement()); - static VarHandle longHandle = longs.varHandle(long.class, PathElement.sequenceElement()); - static VarHandle doubleHandle = doubles.varHandle(double.class, PathElement.sequenceElement()); + static VarHandle byteHandle = bytes.varHandle(PathElement.sequenceElement()); + static VarHandle charHandle = chars.varHandle(PathElement.sequenceElement()); + static VarHandle shortHandle = shorts.varHandle(PathElement.sequenceElement()); + static VarHandle intHandle = ints.varHandle(PathElement.sequenceElement()); + static VarHandle floatHandle = floats.varHandle(PathElement.sequenceElement()); + static VarHandle longHandle = longs.varHandle(PathElement.sequenceElement()); + static VarHandle doubleHandle = doubles.varHandle(PathElement.sequenceElement()); static void initBytes(MemorySegment base, SequenceLayout seq, BiConsumer handleSetter) { for (long i = 0; i < seq.elementCount().getAsLong() ; i++) { @@ -112,7 +118,7 @@ public void testArrays(Consumer init, Consumer che public void testTooBigForArray(MemoryLayout layout, Function arrayFactory) { MemoryLayout seq = MemoryLayout.sequenceLayout((Integer.MAX_VALUE * layout.byteSize()) + 1, layout); //do not really allocate here, as it's way too much memory - MemorySegment segment = MemoryAddress.NULL.asSegment(seq.byteSize(), ResourceScope.globalScope()); + MemorySegment segment = MemorySegment.ofAddress(MemoryAddress.NULL, seq.byteSize(), ResourceScope.globalScope()); arrayFactory.apply(segment); } @@ -152,19 +158,19 @@ public Object[][] nativeAccessOps() { (base) -> initBytes(base, doubles, (addr, pos) -> doubleHandle.set(addr, pos, (double)(long)pos)); Consumer byteChecker = - (base) -> checkBytes(base, bytes, MemorySegment::toByteArray, (addr, pos) -> (byte)byteHandle.get(addr, pos)); + (base) -> checkBytes(base, bytes, s -> s.toArray(JAVA_BYTE), (addr, pos) -> (byte)byteHandle.get(addr, pos)); Consumer shortChecker = - (base) -> checkBytes(base, shorts, MemorySegment::toShortArray, (addr, pos) -> (short)shortHandle.get(addr, pos)); + (base) -> checkBytes(base, shorts, s -> s.toArray(JAVA_SHORT), (addr, pos) -> (short)shortHandle.get(addr, pos)); Consumer charChecker = - (base) -> checkBytes(base, chars, MemorySegment::toCharArray, (addr, pos) -> (char)charHandle.get(addr, pos)); + (base) -> checkBytes(base, chars, s -> s.toArray(JAVA_CHAR), (addr, pos) -> (char)charHandle.get(addr, pos)); Consumer intChecker = - (base) -> checkBytes(base, ints, MemorySegment::toIntArray, (addr, pos) -> (int)intHandle.get(addr, pos)); + (base) -> checkBytes(base, ints, s -> s.toArray(JAVA_INT), (addr, pos) -> (int)intHandle.get(addr, pos)); Consumer floatChecker = - (base) -> checkBytes(base, floats, MemorySegment::toFloatArray, (addr, pos) -> (float)floatHandle.get(addr, pos)); + (base) -> checkBytes(base, floats, s -> s.toArray(JAVA_FLOAT), (addr, pos) -> (float)floatHandle.get(addr, pos)); Consumer longChecker = - (base) -> checkBytes(base, longs, MemorySegment::toLongArray, (addr, pos) -> (long)longHandle.get(addr, pos)); + (base) -> checkBytes(base, longs, s -> s.toArray(JAVA_LONG), (addr, pos) -> (long)longHandle.get(addr, pos)); Consumer doubleChecker = - (base) -> checkBytes(base, doubles, MemorySegment::toDoubleArray, (addr, pos) -> (double)doubleHandle.get(addr, pos)); + (base) -> checkBytes(base, doubles, s -> s.toArray(JAVA_DOUBLE), (addr, pos) -> (double)doubleHandle.get(addr, pos)); return new Object[][]{ {byteInitializer, byteChecker, bytes}, @@ -180,13 +186,13 @@ public Object[][] nativeAccessOps() { @DataProvider(name = "elemLayouts") public Object[][] elemLayouts() { return new Object[][] { - { MemoryLayouts.JAVA_BYTE, (Function) MemorySegment::toByteArray }, - { MemoryLayouts.JAVA_SHORT, (Function) MemorySegment::toShortArray }, - { MemoryLayouts.JAVA_CHAR, (Function) MemorySegment::toCharArray }, - { MemoryLayouts.JAVA_INT, (Function) MemorySegment::toIntArray }, - { MemoryLayouts.JAVA_FLOAT, (Function) MemorySegment::toFloatArray }, - { MemoryLayouts.JAVA_LONG, (Function) MemorySegment::toLongArray }, - { MemoryLayouts.JAVA_DOUBLE, (Function) MemorySegment::toDoubleArray } + { JAVA_BYTE, (Function)s -> s.toArray(JAVA_BYTE)}, + { JAVA_SHORT, (Function) s -> s.toArray(JAVA_SHORT)}, + { JAVA_CHAR, (Function) s -> s.toArray(JAVA_CHAR)}, + { JAVA_INT, (Function)s -> s.toArray(JAVA_INT)}, + { JAVA_FLOAT, (Function)s -> s.toArray(JAVA_FLOAT)}, + { JAVA_LONG, (Function)s -> s.toArray(JAVA_LONG)}, + { JAVA_DOUBLE, (Function)s -> s.toArray(JAVA_DOUBLE)} }; } } diff --git a/test/jdk/java/foreign/TestByteBuffer.java b/test/jdk/java/foreign/TestByteBuffer.java index cdc9addb38132..57d953f771d9a 100644 --- a/test/jdk/java/foreign/TestByteBuffer.java +++ b/test/jdk/java/foreign/TestByteBuffer.java @@ -28,8 +28,6 @@ * @run testng/othervm --enable-native-access=ALL-UNNAMED TestByteBuffer */ -import jdk.incubator.foreign.MemoryAccess; -import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemorySegment; @@ -82,6 +80,13 @@ import org.testng.annotations.*; import sun.nio.ch.DirectBuffer; +import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE; +import static jdk.incubator.foreign.ValueLayout.JAVA_CHAR; +import static jdk.incubator.foreign.ValueLayout.JAVA_DOUBLE; +import static jdk.incubator.foreign.ValueLayout.JAVA_FLOAT; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_LONG; +import static jdk.incubator.foreign.ValueLayout.JAVA_SHORT; import static org.testng.Assert.*; public class TestByteBuffer { @@ -102,40 +107,40 @@ public class TestByteBuffer { static SequenceLayout tuples = MemoryLayout.sequenceLayout(500, MemoryLayout.structLayout( - MemoryLayouts.BITS_32_BE.withName("index"), - MemoryLayouts.BITS_32_BE.withName("value") + JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN).withName("index"), + JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN).withName("value") )); static SequenceLayout bytes = MemoryLayout.sequenceLayout(100, - MemoryLayouts.BITS_8_BE + JAVA_BYTE ); static SequenceLayout chars = MemoryLayout.sequenceLayout(100, - MemoryLayouts.BITS_16_BE + JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN) ); static SequenceLayout shorts = MemoryLayout.sequenceLayout(100, - MemoryLayouts.BITS_16_BE + JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN) ); static SequenceLayout ints = MemoryLayout.sequenceLayout(100, - MemoryLayouts.BITS_32_BE + JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN) ); static SequenceLayout floats = MemoryLayout.sequenceLayout(100, - MemoryLayouts.BITS_32_BE + JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN) ); static SequenceLayout longs = MemoryLayout.sequenceLayout(100, - MemoryLayouts.BITS_64_BE + JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN) ); static SequenceLayout doubles = MemoryLayout.sequenceLayout(100, - MemoryLayouts.BITS_64_BE + JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN) ); - static VarHandle indexHandle = tuples.varHandle(int.class, PathElement.sequenceElement(), PathElement.groupElement("index")); - static VarHandle valueHandle = tuples.varHandle(float.class, PathElement.sequenceElement(), PathElement.groupElement("value")); + static VarHandle indexHandle = tuples.varHandle(PathElement.sequenceElement(), PathElement.groupElement("index")); + static VarHandle valueHandle = tuples.varHandle(PathElement.sequenceElement(), PathElement.groupElement("value")); static void initTuples(MemorySegment base, long count) { for (long i = 0; i < count ; i++) { @@ -263,7 +268,7 @@ public void testMappedSegment() throws Throwable { } } - @Test(dataProvider = "mappedOps", expectedExceptions = UnsupportedOperationException.class) + @Test(dataProvider = "mappedOps", expectedExceptions = IllegalStateException.class) public void testMappedSegmentOperations(MappedSegmentOp mappedBufferOp) throws Throwable { File f = new File("test3.out"); f.createNewFile(); @@ -324,6 +329,9 @@ public void testLargeMappedSegment() throws Throwable { segment.isLoaded(); segment.unload(); segment.isLoaded(); + } catch(IOException e) { + if (e.getMessage().equals("Function not implemented")) + throw new SkipException(e.getMessage(), e); } } @@ -480,7 +488,7 @@ public void testBufferOnClosedScope() { @Test(expectedExceptions = IllegalStateException.class) public void testTooBigForByteBuffer() { - MemorySegment segment = MemoryAddress.NULL.asSegment(Integer.MAX_VALUE + 10L, ResourceScope.globalScope()); + MemorySegment segment = MemorySegment.ofAddress(MemoryAddress.NULL, Integer.MAX_VALUE + 10L, ResourceScope.newImplicitScope()); segment.asByteBuffer(); } @@ -511,7 +519,7 @@ public void testMapOffset() throws IOException { try (ResourceScope scope = ResourceScope.newConfinedScope()) { MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0, SIZE, FileChannel.MapMode.READ_WRITE, scope); for (byte offset = 0; offset < SIZE; offset++) { - MemoryAccess.setByteAtOffset(segment, offset, offset); + segment.set(JAVA_BYTE, offset, offset); } segment.force(); } @@ -519,7 +527,7 @@ public void testMapOffset() throws IOException { for (int offset = 0 ; offset < SIZE ; offset++) { try (ResourceScope scope = ResourceScope.newConfinedScope()) { MemorySegment segment = MemorySegment.mapFile(f.toPath(), offset, SIZE - offset, FileChannel.MapMode.READ_ONLY, scope); - assertEquals(MemoryAccess.getByte(segment), offset); + assertEquals(segment.get(JAVA_BYTE, 0), offset); } } } @@ -636,13 +644,13 @@ public void testRoundTripAccess() { @Test(expectedExceptions = IllegalStateException.class) public void testDeadAccessOnClosedBufferSegment() { - MemorySegment s1 = MemorySegment.allocateNative(MemoryLayouts.JAVA_INT, ResourceScope.newConfinedScope()); + MemorySegment s1 = MemorySegment.allocateNative(JAVA_INT, ResourceScope.newConfinedScope()); MemorySegment s2 = MemorySegment.ofByteBuffer(s1.asByteBuffer()); // memory freed s1.scope().close(); - MemoryAccess.setInt(s2, 10); // Dead access! + s2.set(JAVA_INT, 0, 10); // Dead access! } @Test(dataProvider = "allScopes") @@ -654,7 +662,7 @@ public void testIOOnSegmentBuffer(Supplier scopeSupplier) throws ResourceScope scp = closeableScopeOrNull(scope = scopeSupplier.get())) { MemorySegment segment = MemorySegment.allocateNative(10, 1, scope); for (int i = 0; i < 10; i++) { - MemoryAccess.setByteAtOffset(segment, i, (byte) i); + segment.set(JAVA_BYTE, i, (byte) i); } ByteBuffer bb = segment.asByteBuffer(); assertEquals(channel.write(bb), 10); @@ -674,7 +682,7 @@ public void testIOOnClosedSegmentBuffer(Supplier scopeSupplier) t try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { MemorySegment segment = MemorySegment.allocateNative(10, scopeSupplier.get()); for (int i = 0; i < 10; i++) { - MemoryAccess.setByteAtOffset(segment, i, (byte) i); + segment.set(JAVA_BYTE, i, (byte) i); } ByteBuffer bb = segment.asByteBuffer(); segment.scope().close(); @@ -694,7 +702,7 @@ public void buffersAndArraysFromSlices() { int newSize = 8; var slice = segment.asSlice(4, newSize); - var bytes = slice.toByteArray(); + var bytes = slice.toArray(JAVA_BYTE); assertEquals(newSize, bytes.length); var buffer = slice.asByteBuffer(); @@ -719,6 +727,7 @@ public void viewsFromSharedSegment() { public static Object[][] segments() throws Throwable { return new Object[][] { { (Supplier) () -> MemorySegment.allocateNative(16, ResourceScope.newImplicitScope()) }, + { (Supplier) () -> MemorySegment.allocateNative(16, ResourceScope.newConfinedScope()) }, { (Supplier) () -> MemorySegment.ofArray(new byte[16]) } }; } @@ -729,27 +738,20 @@ public static Object[][] closeableScopes() { { (Supplier) () -> ResourceScope.newSharedScope() }, { (Supplier) () -> ResourceScope.newConfinedScope() }, { (Supplier) () -> ResourceScope.newSharedScope(Cleaner.create()) }, - { (Supplier) () -> ResourceScope.newConfinedScope(Cleaner.create()) } - }; - } - - @DataProvider(name = "implicitScopes") - public static Object[][] implicitScopes() { - return new Object[][] { - { (Supplier) ResourceScope::newImplicitScope }, - { (Supplier) ResourceScope::globalScope }, + { (Supplier) () -> ResourceScope.newConfinedScope(Cleaner.create()) }, + { (Supplier) () -> ResourceScope.newImplicitScope() } }; } @DataProvider(name = "allScopes") public static Object[][] allScopes() { - return Stream.of(implicitScopes(), closeableScopes()) + return Stream.of(new Object[][] { { (Supplier)ResourceScope::globalScope } }, closeableScopes()) .flatMap(Arrays::stream) .toArray(Object[][]::new); } static ResourceScope closeableScopeOrNull(ResourceScope scope) { - if (scope.isImplicit()) + if (scope == ResourceScope.globalScope()) return null; return scope; } @@ -812,34 +814,34 @@ static Map varHandleMembers(ByteBuffer bb, VarHandle han @DataProvider(name = "resizeOps") public Object[][] resizeOps() { Consumer byteInitializer = - (base) -> initBytes(base, bytes, (addr, pos) -> MemoryAccess.setByteAtOffset(addr, pos, (byte)(long)pos)); + (base) -> initBytes(base, bytes, (addr, pos) -> addr.set(JAVA_BYTE, pos, (byte)(long)pos)); Consumer charInitializer = - (base) -> initBytes(base, chars, (addr, pos) -> MemoryAccess.setCharAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (char)(long)pos)); + (base) -> initBytes(base, chars, (addr, pos) -> addr.setAtIndex(JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), pos, (char)(long)pos)); Consumer shortInitializer = - (base) -> initBytes(base, shorts, (addr, pos) -> MemoryAccess.setShortAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (short)(long)pos)); + (base) -> initBytes(base, shorts, (addr, pos) -> addr.setAtIndex(JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), pos, (short)(long)pos)); Consumer intInitializer = - (base) -> initBytes(base, ints, (addr, pos) -> MemoryAccess.setIntAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (int)(long)pos)); + (base) -> initBytes(base, ints, (addr, pos) -> addr.setAtIndex(JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), pos, (int)(long)pos)); Consumer floatInitializer = - (base) -> initBytes(base, floats, (addr, pos) -> MemoryAccess.setFloatAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (float)(long)pos)); + (base) -> initBytes(base, floats, (addr, pos) -> addr.setAtIndex(JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), pos, (float)(long)pos)); Consumer longInitializer = - (base) -> initBytes(base, longs, (addr, pos) -> MemoryAccess.setLongAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (long)pos)); + (base) -> initBytes(base, longs, (addr, pos) -> addr.setAtIndex(JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), pos, (long)pos)); Consumer doubleInitializer = - (base) -> initBytes(base, doubles, (addr, pos) -> MemoryAccess.setDoubleAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (double)(long)pos)); + (base) -> initBytes(base, doubles, (addr, pos) -> addr.setAtIndex(JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), pos, (double)(long)pos)); Consumer byteChecker = - (base) -> checkBytes(base, bytes, Function.identity(), (addr, pos) -> MemoryAccess.getByteAtOffset(addr, pos), ByteBuffer::get); + (base) -> checkBytes(base, bytes, Function.identity(), (addr, pos) -> addr.get(JAVA_BYTE, pos), ByteBuffer::get); Consumer charChecker = - (base) -> checkBytes(base, chars, ByteBuffer::asCharBuffer, (addr, pos) -> MemoryAccess.getCharAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), CharBuffer::get); + (base) -> checkBytes(base, chars, ByteBuffer::asCharBuffer, (addr, pos) -> addr.getAtIndex(JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), pos), CharBuffer::get); Consumer shortChecker = - (base) -> checkBytes(base, shorts, ByteBuffer::asShortBuffer, (addr, pos) -> MemoryAccess.getShortAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), ShortBuffer::get); + (base) -> checkBytes(base, shorts, ByteBuffer::asShortBuffer, (addr, pos) -> addr.getAtIndex(JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), pos), ShortBuffer::get); Consumer intChecker = - (base) -> checkBytes(base, ints, ByteBuffer::asIntBuffer, (addr, pos) -> MemoryAccess.getIntAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), IntBuffer::get); + (base) -> checkBytes(base, ints, ByteBuffer::asIntBuffer, (addr, pos) -> addr.getAtIndex(JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), pos), IntBuffer::get); Consumer floatChecker = - (base) -> checkBytes(base, floats, ByteBuffer::asFloatBuffer, (addr, pos) -> MemoryAccess.getFloatAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), FloatBuffer::get); + (base) -> checkBytes(base, floats, ByteBuffer::asFloatBuffer, (addr, pos) -> addr.getAtIndex(JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), pos), FloatBuffer::get); Consumer longChecker = - (base) -> checkBytes(base, longs, ByteBuffer::asLongBuffer, (addr, pos) -> MemoryAccess.getLongAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), LongBuffer::get); + (base) -> checkBytes(base, longs, ByteBuffer::asLongBuffer, (addr, pos) -> addr.getAtIndex(JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), pos), LongBuffer::get); Consumer doubleChecker = - (base) -> checkBytes(base, doubles, ByteBuffer::asDoubleBuffer, (addr, pos) -> MemoryAccess.getDoubleAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), DoubleBuffer::get); + (base) -> checkBytes(base, doubles, ByteBuffer::asDoubleBuffer, (addr, pos) -> addr.getAtIndex(JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), pos), DoubleBuffer::get); return new Object[][]{ {byteChecker, byteInitializer, bytes}, diff --git a/test/jdk/java/foreign/TestCircularInit1.java b/test/jdk/java/foreign/TestCircularInit1.java deleted file mode 100644 index 46a98bd23f8d8..0000000000000 --- a/test/jdk/java/foreign/TestCircularInit1.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" - * @modules jdk.incubator.foreign/jdk.internal.foreign - * @run testng/othervm TestCircularInit1 - */ - -import jdk.incubator.foreign.CLinker; -import jdk.internal.foreign.PlatformLayouts; -import org.testng.annotations.Test; - -import static org.testng.Assert.assertNotNull; - -public class TestCircularInit1 { - - @Test - public void testCircularInit() { - System.out.println(PlatformLayouts.Win64.C_CHAR); // trigger clinit - assertNotNull(CLinker.C_CHAR); // should not be null - } - -} diff --git a/test/jdk/java/foreign/TestCircularInit2.java b/test/jdk/java/foreign/TestCircularInit2.java deleted file mode 100644 index 0eeed8184cf84..0000000000000 --- a/test/jdk/java/foreign/TestCircularInit2.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" - * @modules jdk.incubator.foreign/jdk.internal.foreign - * @run testng/othervm TestCircularInit2 - */ - -import jdk.incubator.foreign.CLinker; -import jdk.internal.foreign.PlatformLayouts; -import org.testng.annotations.Test; - -import static org.testng.Assert.assertNotNull; - -public class TestCircularInit2 { - - @Test - public void testCircularInit() { - System.out.println(CLinker.C_CHAR); // trigger clinit - assertNotNull(PlatformLayouts.Win64.C_CHAR); - assertNotNull(PlatformLayouts.SysV.C_CHAR); - assertNotNull(PlatformLayouts.AArch64.C_CHAR); - } - -} diff --git a/test/jdk/java/foreign/TestCondy.java b/test/jdk/java/foreign/TestCondy.java index 0f262214799d0..42191dd4a2491 100644 --- a/test/jdk/java/foreign/TestCondy.java +++ b/test/jdk/java/foreign/TestCondy.java @@ -39,7 +39,15 @@ import java.util.Arrays; import java.util.List; -import static jdk.incubator.foreign.CLinker.*; +import static jdk.incubator.foreign.ValueLayout.ADDRESS; +import static jdk.incubator.foreign.ValueLayout.JAVA_BOOLEAN; +import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE; +import static jdk.incubator.foreign.ValueLayout.JAVA_CHAR; +import static jdk.incubator.foreign.ValueLayout.JAVA_DOUBLE; +import static jdk.incubator.foreign.ValueLayout.JAVA_FLOAT; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_LONG; +import static jdk.incubator.foreign.ValueLayout.JAVA_SHORT; import static org.testng.Assert.assertEquals; public class TestCondy { @@ -53,14 +61,15 @@ public void testPublicResolve(Constable constable) throws ReflectiveOperationExc private static final MemoryLayout[] constants = { - C_CHAR, - C_SHORT, - C_INT, - C_LONG, - C_LONG_LONG, - C_FLOAT, - C_DOUBLE, - C_POINTER + JAVA_BOOLEAN, + JAVA_CHAR, + JAVA_BYTE, + JAVA_SHORT, + JAVA_INT, + JAVA_FLOAT, + JAVA_LONG, + JAVA_DOUBLE, + ADDRESS }; @DataProvider @@ -78,7 +87,7 @@ public static Object[][] constables() { } testValues.add(FunctionDescriptor.ofVoid(constants)); - testValues.add(FunctionDescriptor.of(C_CHAR, constants)); + testValues.add(FunctionDescriptor.of(JAVA_BYTE, constants)); return testValues.stream().map(e -> new Object[] { e }).toArray(Object[][]::new); } diff --git a/test/jdk/java/foreign/TestDowncall.java b/test/jdk/java/foreign/TestDowncall.java index 8a78a20e1fbaa..252e14c69513d 100644 --- a/test/jdk/java/foreign/TestDowncall.java +++ b/test/jdk/java/foreign/TestDowncall.java @@ -33,8 +33,11 @@ * TestDowncall */ +import jdk.incubator.foreign.Addressable; import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.FunctionDescriptor; +import jdk.incubator.foreign.NativeSymbol; +import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.SymbolLookup; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; @@ -52,7 +55,7 @@ public class TestDowncall extends CallGeneratorHelper { - static CLinker abi = CLinker.getInstance(); + static CLinker abi = CLinker.systemCLinker(); static { System.loadLibrary("TestDowncall"); } @@ -62,64 +65,37 @@ public class TestDowncall extends CallGeneratorHelper { @Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class) public void testDowncall(int count, String fName, Ret ret, List paramTypes, List fields) throws Throwable { List> checks = new ArrayList<>(); - MemoryAddress addr = LOOKUP.lookup(fName).get(); + NativeSymbol addr = LOOKUP.lookup(fName).get(); MethodType mt = methodType(ret, paramTypes, fields); FunctionDescriptor descriptor = function(ret, paramTypes, fields); Object[] args = makeArgs(paramTypes, fields, checks); - try (NativeScope scope = new NativeScope()) { + try (ResourceScope scope = ResourceScope.newSharedScope()) { boolean needsScope = mt.returnType().equals(MemorySegment.class); - Object res = doCall(addr, scope, mt, descriptor, args); + SegmentAllocator allocator = needsScope ? + SegmentAllocator.newNativeArena(scope) : + THROWING_ALLOCATOR; + Object res = doCall(addr, allocator, descriptor, args); if (ret == Ret.NON_VOID) { checks.forEach(c -> c.accept(res)); if (needsScope) { // check that return struct has indeed been allocated in the native scope - assertEquals(((MemorySegment) res).scope(), scope.scope()); - assertEquals(scope.allocatedBytes(), descriptor.returnLayout().get().byteSize()); - } else { - // if here, there should be no allocation through the scope! - assertEquals(scope.allocatedBytes(), 0L); + assertEquals(((MemorySegment) res).scope(), scope); } - } else { - // if here, there should be no allocation through the scope! - assertEquals(scope.allocatedBytes(), 0L); } } } - @Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class) - public void testDowncallNoScope(int count, String fName, Ret ret, List paramTypes, List fields) throws Throwable { - List> checks = new ArrayList<>(); - MemoryAddress addr = LOOKUP.lookup(fName).get(); - MethodType mt = methodType(ret, paramTypes, fields); - FunctionDescriptor descriptor = function(ret, paramTypes, fields); - Object[] args = makeArgs(paramTypes, fields, checks); - boolean needsScope = mt.returnType().equals(MemorySegment.class); - Object res = doCall(addr, IMPLICIT_ALLOCATOR, mt, descriptor, args); - if (ret == Ret.NON_VOID) { - checks.forEach(c -> c.accept(res)); - if (needsScope) { - // check that return struct has indeed been allocated in the default scope - try { - ((MemorySegment)res).scope().close(); // should throw - fail("Expected exception!"); - } catch (UnsupportedOperationException ex) { - // ok - } - } - } - } - - Object doCall(MemoryAddress addr, SegmentAllocator allocator, MethodType type, FunctionDescriptor descriptor, Object[] args) throws Throwable { - MethodHandle mh = abi.downcallHandle(addr, allocator, type, descriptor); + Object doCall(NativeSymbol symbol, SegmentAllocator allocator, FunctionDescriptor descriptor, Object[] args) throws Throwable { + MethodHandle mh = downcallHandle(abi, symbol, allocator, descriptor); Object res = mh.invokeWithArguments(args); return res; } static MethodType methodType(Ret ret, List params, List fields) { MethodType mt = ret == Ret.VOID ? - MethodType.methodType(void.class) : MethodType.methodType(paramCarrier(params.get(0).layout(fields))); + MethodType.methodType(void.class) : MethodType.methodType(carrier(params.get(0).layout(fields), false)); for (ParamType p : params) { - mt = mt.appendParameterTypes(paramCarrier(p.layout(fields))); + mt = mt.appendParameterTypes(carrier(p.layout(fields), true)); } return mt; } diff --git a/test/jdk/java/foreign/TestFree.java b/test/jdk/java/foreign/TestFree.java index ad26e1eb5715b..b009cee0ac4e5 100644 --- a/test/jdk/java/foreign/TestFree.java +++ b/test/jdk/java/foreign/TestFree.java @@ -29,18 +29,16 @@ * @run testng/othervm --enable-native-access=ALL-UNNAMED TestFree */ -import jdk.incubator.foreign.MemoryAccess; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; -import static jdk.incubator.foreign.CLinker.*; import static org.testng.Assert.assertEquals; -public class TestFree { +public class TestFree extends NativeTestHelper { private static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements) { - return addr.asSegment(numElements * layout.byteSize(), ResourceScope.globalScope()); + return MemorySegment.ofAddress(addr, numElements * layout.byteSize(), ResourceScope.globalScope()); } public void test() throws Throwable { @@ -48,8 +46,8 @@ public void test() throws Throwable { MemoryAddress addr = allocateMemory(str.length() + 1); MemorySegment seg = asArray(addr, C_CHAR, str.length() + 1); seg.copyFrom(MemorySegment.ofArray(str.getBytes())); - MemoryAccess.setByteAtOffset(seg, str.length(), (byte)0); - assertEquals(str, toJavaString(seg)); + seg.set(C_CHAR, str.length(), (byte)0); + assertEquals(str, seg.getUtf8String(0)); freeMemory(addr); } } diff --git a/test/jdk/java/foreign/TestFunctionDescriptor.java b/test/jdk/java/foreign/TestFunctionDescriptor.java index d8b82254dafcd..e67c5b9a3fad0 100644 --- a/test/jdk/java/foreign/TestFunctionDescriptor.java +++ b/test/jdk/java/foreign/TestFunctionDescriptor.java @@ -25,7 +25,7 @@ /* * @test * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" - * @run testng TestFunctionDescriptor + * @run testng/othervm --enable-native-access=ALL-UNNAMED TestFunctionDescriptor */ import jdk.incubator.foreign.FunctionDescriptor; @@ -37,15 +37,11 @@ import java.util.Optional; import java.util.stream.Collectors; -import static jdk.incubator.foreign.CLinker.C_DOUBLE; -import static jdk.incubator.foreign.CLinker.C_INT; -import static jdk.incubator.foreign.CLinker.C_LONG_LONG; -import static jdk.incubator.foreign.CLinker.C_POINTER; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; -public class TestFunctionDescriptor { +public class TestFunctionDescriptor extends NativeTestHelper { static final String DUMMY_ATTR = "dummy"; @@ -68,56 +64,35 @@ public void testOfVoid() { assertFalse(returnLayoutOp.isPresent()); } - @Test - public void testAttribute() { - FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG); - fd = fd.withAttribute(DUMMY_ATTR, true); - - assertEquals(fd.argumentLayouts(), List.of(C_DOUBLE, C_LONG_LONG)); - Optional returnLayoutOp = fd.returnLayout(); - assertTrue(returnLayoutOp.isPresent()); - assertEquals(returnLayoutOp.get(), C_INT); - assertEquals(fd.attributes().collect(Collectors.toList()), List.of(DUMMY_ATTR)); - Optional attr = fd.attribute(DUMMY_ATTR); - assertTrue(attr.isPresent()); - assertEquals(attr.get(), true); - } - @Test public void testAppendArgumentLayouts() { - FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG) - .withAttribute(DUMMY_ATTR, true); + FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG); fd = fd.withAppendedArgumentLayouts(C_POINTER); assertEquals(fd.argumentLayouts(), List.of(C_DOUBLE, C_LONG_LONG, C_POINTER)); Optional returnLayoutOp = fd.returnLayout(); assertTrue(returnLayoutOp.isPresent()); assertEquals(returnLayoutOp.get(), C_INT); - assertEquals(fd.attributes().collect(Collectors.toList()), List.of(DUMMY_ATTR)); } @Test public void testChangeReturnLayout() { - FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG) - .withAttribute(DUMMY_ATTR, true); + FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG); fd = fd.withReturnLayout(C_INT); assertEquals(fd.argumentLayouts(), List.of(C_DOUBLE, C_LONG_LONG)); Optional returnLayoutOp = fd.returnLayout(); assertTrue(returnLayoutOp.isPresent()); assertEquals(returnLayoutOp.get(), C_INT); - assertEquals(fd.attributes().collect(Collectors.toList()), List.of(DUMMY_ATTR)); } @Test public void testDropReturnLayout() { - FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG) - .withAttribute(DUMMY_ATTR, true); + FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG); fd = fd.withVoidReturnLayout(); assertEquals(fd.argumentLayouts(), List.of(C_DOUBLE, C_LONG_LONG)); Optional returnLayoutOp = fd.returnLayout(); assertFalse(returnLayoutOp.isPresent()); - assertEquals(fd.attributes().collect(Collectors.toList()), List.of(DUMMY_ATTR)); } } diff --git a/test/jdk/java/foreign/TestHandshake.java b/test/jdk/java/foreign/TestHandshake.java index ee9b739459465..94fdd8cbf1828 100644 --- a/test/jdk/java/foreign/TestHandshake.java +++ b/test/jdk/java/foreign/TestHandshake.java @@ -31,7 +31,6 @@ * @run testng/othervm -XX:-TieredCompilation TestHandshake */ -import jdk.incubator.foreign.MemoryAccess; import jdk.incubator.foreign.MemorySegment; import java.lang.invoke.MethodHandles; @@ -48,6 +47,8 @@ import jdk.incubator.foreign.ResourceScope; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; + +import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE; import static org.testng.Assert.*; public class TestHandshake { @@ -150,7 +151,7 @@ static class SegmentAccessor extends AbstractSegmentAccessor { void doAccess() { int sum = 0; for (int i = 0; i < segment.byteSize(); i++) { - sum += MemoryAccess.getByteAtOffset(segment, i); + sum += segment.get(JAVA_BYTE, i); } } } @@ -193,7 +194,7 @@ static class SegmentMismatchAccessor extends AbstractSegmentAccessor { super(id, segment); this.copy = MemorySegment.allocateNative(SEGMENT_SIZE, 1, segment.scope()); copy.copyFrom(segment); - MemoryAccess.setByteAtOffset(copy, ThreadLocalRandom.current().nextInt(SEGMENT_SIZE), (byte)42); + copy.set(JAVA_BYTE, ThreadLocalRandom.current().nextInt(SEGMENT_SIZE), (byte)42); } @Override diff --git a/test/jdk/java/foreign/TestIllegalLink.java b/test/jdk/java/foreign/TestIllegalLink.java index f63019bc4ffb9..7afa01689bc5a 100644 --- a/test/jdk/java/foreign/TestIllegalLink.java +++ b/test/jdk/java/foreign/TestIllegalLink.java @@ -32,26 +32,23 @@ import jdk.incubator.foreign.FunctionDescriptor; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; -import jdk.incubator.foreign.MemoryLayouts; -import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.NativeSymbol; +import jdk.incubator.foreign.ResourceScope; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -import java.lang.invoke.MethodType; - -import static jdk.incubator.foreign.CLinker.C_INT; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; -public class TestIllegalLink { +public class TestIllegalLink extends NativeTestHelper { - private static final MemoryAddress DUMMY_TARGET = MemoryAddress.ofLong(1); - private static final CLinker ABI = CLinker.getInstance(); + private static final NativeSymbol DUMMY_TARGET = NativeSymbol.ofAddress("dummy", MemoryAddress.ofLong(1), ResourceScope.globalScope()); + private static final CLinker ABI = CLinker.systemCLinker(); @Test(dataProvider = "types") - public void testTypeMismatch(MethodType mt, FunctionDescriptor desc, String expectedExceptionMessage) { + public void testTypeMismatch(FunctionDescriptor desc, String expectedExceptionMessage) { try { - ABI.downcallHandle(DUMMY_TARGET, mt, desc); + ABI.downcallHandle(DUMMY_TARGET, desc); fail("Expected IllegalArgumentException was not thrown"); } catch (IllegalArgumentException e) { assertTrue(e.getMessage().contains(expectedExceptionMessage)); @@ -62,49 +59,20 @@ public void testTypeMismatch(MethodType mt, FunctionDescriptor desc, String expe public static Object[][] types() { return new Object[][]{ { - MethodType.methodType(void.class), - FunctionDescriptor.of(C_INT), - "Return type mismatch" - }, - { - MethodType.methodType(void.class), - FunctionDescriptor.ofVoid(C_INT), - "Arity mismatch" + FunctionDescriptor.of(MemoryLayout.paddingLayout(64)), + "Unsupported layout: x64" }, { - MethodType.methodType(void.class, int.class), - FunctionDescriptor.ofVoid(MemoryLayout.paddingLayout(32)), - "Expected a ValueLayout" - }, - { - MethodType.methodType(void.class, boolean.class), - FunctionDescriptor.ofVoid(MemoryLayouts.BITS_8_LE), - "Unsupported carrier" - }, - { - MethodType.methodType(void.class, int.class), - FunctionDescriptor.ofVoid(MemoryLayouts.BITS_64_LE), - "Carrier size mismatch" - }, - { - MethodType.methodType(void.class, MemoryAddress.class), FunctionDescriptor.ofVoid(MemoryLayout.paddingLayout(64)), - "Expected a ValueLayout" - }, - { - MethodType.methodType(void.class, MemoryAddress.class), - FunctionDescriptor.ofVoid(MemoryLayouts.BITS_16_LE), - "Address size mismatch" + "Unsupported layout: x64" }, { - MethodType.methodType(void.class, MemorySegment.class), - FunctionDescriptor.ofVoid(MemoryLayouts.BITS_64_LE), - "Expected a GroupLayout" + FunctionDescriptor.of(MemoryLayout.sequenceLayout(C_INT)), + "Unsupported layout: [:b32]" }, { - MethodType.methodType(void.class, String.class), - FunctionDescriptor.ofVoid(MemoryLayouts.BITS_64_LE), - "Unsupported carrier" + FunctionDescriptor.ofVoid(MemoryLayout.sequenceLayout(C_INT)), + "Unsupported layout: [:b32]" }, }; } diff --git a/test/jdk/java/foreign/TestIntrinsics.java b/test/jdk/java/foreign/TestIntrinsics.java index 98e6829e4b2be..dfafdfc3a21fc 100644 --- a/test/jdk/java/foreign/TestIntrinsics.java +++ b/test/jdk/java/foreign/TestIntrinsics.java @@ -42,17 +42,17 @@ import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; +import jdk.incubator.foreign.NativeSymbol; import jdk.incubator.foreign.SymbolLookup; import org.testng.annotations.*; import static java.lang.invoke.MethodType.methodType; -import static jdk.incubator.foreign.CLinker.*; -import static jdk.incubator.foreign.FunctionDescriptor.TRIVIAL_ATTRIBUTE_NAME; +import static jdk.incubator.foreign.ValueLayout.JAVA_CHAR; import static org.testng.Assert.assertEquals; -public class TestIntrinsics { +public class TestIntrinsics extends NativeTestHelper { - static final CLinker abi = CLinker.getInstance(); + static final CLinker abi = CLinker.systemCLinker(); static { System.loadLibrary("Intrinsics"); } @@ -88,52 +88,48 @@ interface AddIdentity { } AddIdentity addIdentity = (name, carrier, layout, arg) -> { - MemoryAddress ma = LOOKUP.lookup(name).get(); + NativeSymbol ma = LOOKUP.lookup(name).get(); MethodType mt = methodType(carrier, carrier); FunctionDescriptor fd = FunctionDescriptor.of(layout, layout); - tests.add(abi.downcallHandle(ma, mt, fd), arg, arg); - tests.add(abi.downcallHandle(ma, mt, fd.withAttribute(TRIVIAL_ATTRIBUTE_NAME, true)), arg, arg); - tests.add(abi.downcallHandle(mt, fd), arg, ma, arg); + tests.add(abi.downcallHandle(ma, fd), arg, arg); + tests.add(abi.downcallHandle(fd), arg, ma, arg); }; { // empty - MemoryAddress ma = LOOKUP.lookup("empty").get(); + NativeSymbol ma = LOOKUP.lookup("empty").get(); MethodType mt = methodType(void.class); FunctionDescriptor fd = FunctionDescriptor.ofVoid(); - tests.add(abi.downcallHandle(ma, mt, fd), null); - tests.add(abi.downcallHandle(ma, mt, fd.withAttribute(TRIVIAL_ATTRIBUTE_NAME, true)), null); + tests.add(abi.downcallHandle(ma, fd), null); } - addIdentity.add("identity_char", byte.class, C_CHAR, (byte) 10); - addIdentity.add("identity_short", short.class, C_SHORT, (short) 10); - addIdentity.add("identity_int", int.class, C_INT, 10); - addIdentity.add("identity_long", long.class, C_LONG_LONG, 10L); - addIdentity.add("identity_float", float.class, C_FLOAT, 10F); - addIdentity.add("identity_double", double.class, C_DOUBLE, 10D); + addIdentity.add("identity_bool", boolean.class, C_BOOL, true); + addIdentity.add("identity_char", byte.class, C_CHAR, (byte) 10); + addIdentity.add("identity_short", short.class, C_SHORT, (short) 10); + addIdentity.add("identity_int", int.class, C_INT, 10); + addIdentity.add("identity_long", long.class, C_LONG_LONG, 10L); + addIdentity.add("identity_float", float.class, C_FLOAT, 10F); + addIdentity.add("identity_double", double.class, C_DOUBLE, 10D); { // identity_va - MemoryAddress ma = LOOKUP.lookup("identity_va").get(); + NativeSymbol ma = LOOKUP.lookup("identity_va").get(); MethodType mt = methodType(int.class, int.class, double.class, int.class, float.class, long.class); - FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_INT, asVarArg(C_DOUBLE), - asVarArg(C_INT), asVarArg(C_FLOAT), asVarArg(C_LONG_LONG)); - tests.add(abi.downcallHandle(ma, mt, fd), 1, 1, 10D, 2, 3F, 4L); - tests.add(abi.downcallHandle(ma, mt, fd.withAttribute(TRIVIAL_ATTRIBUTE_NAME, true)), 1, 1, 10D, 2, 3F, 4L); + FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_INT).asVariadic(C_DOUBLE, C_INT, C_FLOAT, C_LONG_LONG); + tests.add(abi.downcallHandle(ma, fd), 1, 1, 10D, 2, 3F, 4L); } { // high_arity MethodType baseMT = methodType(void.class, int.class, double.class, long.class, float.class, byte.class, short.class, char.class); FunctionDescriptor baseFD = FunctionDescriptor.ofVoid(C_INT, C_DOUBLE, C_LONG_LONG, C_FLOAT, C_CHAR, - C_SHORT, C_SHORT); + C_SHORT, JAVA_CHAR); Object[] args = {1, 10D, 2L, 3F, (byte) 0, (short) 13, 'a'}; for (int i = 0; i < args.length; i++) { - MemoryAddress ma = LOOKUP.lookup("invoke_high_arity" + i).get(); + NativeSymbol ma = LOOKUP.lookup("invoke_high_arity" + i).get(); MethodType mt = baseMT.changeReturnType(baseMT.parameterType(i)); FunctionDescriptor fd = baseFD.withReturnLayout(baseFD.argumentLayouts().get(i)); Object expected = args[i]; - tests.add(abi.downcallHandle(ma, mt, fd), expected, args); - tests.add(abi.downcallHandle(ma, mt, fd.withAttribute(TRIVIAL_ATTRIBUTE_NAME, true)), expected, args); + tests.add(abi.downcallHandle(ma, fd), expected, args); } } diff --git a/test/jdk/java/foreign/TestLayoutAttributes.java b/test/jdk/java/foreign/TestLayoutAttributes.java deleted file mode 100644 index 9def5a3b8237f..0000000000000 --- a/test/jdk/java/foreign/TestLayoutAttributes.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @run testng TestLayoutAttributes - */ - -import jdk.incubator.foreign.MemoryLayout; -import jdk.incubator.foreign.MemoryLayouts; -import org.testng.annotations.Test; - -import java.util.List; -import java.util.stream.Collectors; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; - -public class TestLayoutAttributes { - - @Test - public void testAttribute() { - MemoryLayout ml = MemoryLayouts.BITS_32_LE - .withAttribute("MyAttribute", 10L); - assertEquals((long) ml.attribute("MyAttribute").orElseThrow(), 10L); - } - - @Test - public void testAttributeOverwrite() { - MemoryLayout ml = MemoryLayouts.BITS_32_LE - .withAttribute("MyAttribute", 10L); - assertEquals((long) ml.attribute("MyAttribute").orElseThrow(), 10L); - ml = ml.withAttribute("MyAttribute", 11L); - assertEquals((long) ml.attribute("MyAttribute").orElseThrow(), 11L); - } - - @Test - public void testAttributeNonExistent() { - MemoryLayout ml = MemoryLayouts.BITS_32_LE - .withAttribute("MyAttribute", 10L); - assertTrue(ml.attribute("Foo").isEmpty()); - } - - @Test - public void testNameAttribute() { - MemoryLayout ml = MemoryLayouts.BITS_32_LE - .withName("foo"); - assertEquals(ml.name().orElseThrow(), "foo"); - assertEquals(ml.attribute(MemoryLayout.LAYOUT_NAME).orElseThrow(), "foo"); - } - - @Test - public void testAttributesStream() { - MemoryLayout ml = MemoryLayouts.BITS_32_LE - .withName("foo") - .withAttribute("MyAttribute", 10L); - List attribs = ml.attributes().collect(Collectors.toList()); - assertEquals(attribs.size(), 2); - assertTrue(attribs.contains("MyAttribute")); - assertTrue(attribs.contains(MemoryLayout.LAYOUT_NAME)); - } -} diff --git a/test/jdk/java/foreign/TestLayoutConstants.java b/test/jdk/java/foreign/TestLayoutConstants.java index 2ea2967923448..59d26ca2700b7 100644 --- a/test/jdk/java/foreign/TestLayoutConstants.java +++ b/test/jdk/java/foreign/TestLayoutConstants.java @@ -27,11 +27,12 @@ */ import jdk.incubator.foreign.FunctionDescriptor; -import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemoryLayout; import java.lang.invoke.MethodHandles; +import java.nio.ByteOrder; +import jdk.incubator.foreign.ValueLayout; import org.testng.annotations.*; import static org.testng.Assert.*; @@ -66,49 +67,48 @@ public void testDescribeResolveFunction(MemoryLayout layout, boolean isVoid) { public Object[][] createLayouts() { return new Object[][] { //padding - { MemoryLayouts.PAD_32 }, - { MemoryLayout.sequenceLayout(MemoryLayouts.PAD_32) }, - { MemoryLayout.sequenceLayout(5, MemoryLayouts.PAD_32) }, - { MemoryLayout.structLayout(MemoryLayouts.PAD_32, MemoryLayouts.PAD_32) }, - { MemoryLayout.unionLayout(MemoryLayouts.PAD_32, MemoryLayouts.PAD_32) }, + {MemoryLayout.paddingLayout(32)}, + { MemoryLayout.sequenceLayout(MemoryLayout.paddingLayout(32)) }, + { MemoryLayout.sequenceLayout(5, MemoryLayout.paddingLayout(32)) }, + { MemoryLayout.structLayout(MemoryLayout.paddingLayout(32), MemoryLayout.paddingLayout(32)) }, + { MemoryLayout.unionLayout(MemoryLayout.paddingLayout(32), MemoryLayout.paddingLayout(32)) }, //values, big endian - { MemoryLayouts.BITS_32_BE }, + { ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN) }, { MemoryLayout.structLayout( - MemoryLayouts.BITS_32_BE, - MemoryLayouts.BITS_32_BE) }, + ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), + ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN)) }, { MemoryLayout.unionLayout( - MemoryLayouts.BITS_32_BE, - MemoryLayouts.BITS_32_BE) }, + ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), + ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN)) }, //values, little endian - { MemoryLayouts.BITS_32_LE }, + { ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN) }, { MemoryLayout.structLayout( - MemoryLayouts.BITS_32_LE, - MemoryLayouts.BITS_32_LE) }, + ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), + ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN)) }, { MemoryLayout.unionLayout( - MemoryLayouts.BITS_32_LE, - MemoryLayouts.BITS_32_LE) }, + ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), + ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN)) }, //deeply nested { MemoryLayout.structLayout( - MemoryLayouts.PAD_16, + MemoryLayout.paddingLayout(16), MemoryLayout.structLayout( - MemoryLayouts.PAD_8, - MemoryLayouts.BITS_32_BE)) }, + MemoryLayout.paddingLayout(8), + ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN))) }, { MemoryLayout.unionLayout( - MemoryLayouts.PAD_16, + MemoryLayout.paddingLayout(16), MemoryLayout.structLayout( - MemoryLayouts.PAD_8, - MemoryLayouts.BITS_32_BE)) }, + MemoryLayout.paddingLayout(8), + ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN))) }, { MemoryLayout.sequenceLayout( MemoryLayout.structLayout( - MemoryLayouts.PAD_8, - MemoryLayouts.BITS_32_BE)) }, + MemoryLayout.paddingLayout(8), + ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN))) }, { MemoryLayout.sequenceLayout(5, MemoryLayout.structLayout( - MemoryLayouts.PAD_8, - MemoryLayouts.BITS_32_BE)) }, - { MemoryLayouts.BITS_32_LE.withName("myInt") }, - { MemoryLayouts.BITS_32_LE.withBitAlignment(8) }, - { MemoryLayouts.BITS_32_LE.withAttribute("xyz", "abc") }, + MemoryLayout.paddingLayout(8), + ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN))) }, + { ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN).withName("myInt") }, + { ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN).withBitAlignment(8) }, }; } diff --git a/test/jdk/java/foreign/TestLayoutEquality.java b/test/jdk/java/foreign/TestLayoutEquality.java index 8eb59ec340ee0..7a4d3124f0b86 100644 --- a/test/jdk/java/foreign/TestLayoutEquality.java +++ b/test/jdk/java/foreign/TestLayoutEquality.java @@ -29,7 +29,7 @@ * @run testng TestLayoutEquality */ -import jdk.incubator.foreign.MemoryLayout; +import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.ValueLayout; import jdk.internal.foreign.PlatformLayouts; import org.testng.annotations.DataProvider; @@ -39,23 +39,32 @@ import java.util.ArrayList; import java.util.List; +import static jdk.incubator.foreign.ValueLayout.ADDRESS; +import static jdk.incubator.foreign.ValueLayout.JAVA_BOOLEAN; +import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE; +import static jdk.incubator.foreign.ValueLayout.JAVA_CHAR; +import static jdk.incubator.foreign.ValueLayout.JAVA_DOUBLE; +import static jdk.incubator.foreign.ValueLayout.JAVA_FLOAT; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_LONG; +import static jdk.incubator.foreign.ValueLayout.JAVA_SHORT; import static org.testng.Assert.*; public class TestLayoutEquality { @Test(dataProvider = "layoutConstants") public void testReconstructedEquality(ValueLayout layout) { - ValueLayout newLayout = MemoryLayout.valueLayout(layout.bitSize(), layout.order()); + ValueLayout newLayout = valueLayoutForCarrier(layout.carrier()); + newLayout = newLayout.withBitAlignment(layout.bitAlignment()); + newLayout = newLayout.withOrder(layout.order()); // properties should be equal assertEquals(newLayout.bitSize(), layout.bitSize()); assertEquals(newLayout.bitAlignment(), layout.bitAlignment()); assertEquals(newLayout.name(), layout.name()); - assertEquals(newLayout.attributes().toArray().length, 0); - assertEquals(layout.attributes().toArray().length, 1); - // but equals should return false, because one is a ValueLayout with a CLinker kind - assertNotEquals(newLayout, layout); + // layouts should be equals + assertEquals(newLayout, layout); } @DataProvider @@ -76,4 +85,27 @@ private static void addLayoutConstants(List testValues, Class cl } } + static ValueLayout valueLayoutForCarrier(Class carrier) { + if (carrier == boolean.class) { + return JAVA_BOOLEAN; + } else if (carrier == char.class) { + return JAVA_CHAR; + } else if (carrier == byte.class) { + return JAVA_BYTE; + } else if (carrier == short.class) { + return JAVA_SHORT; + } else if (carrier == int.class) { + return JAVA_INT; + } else if (carrier == long.class) { + return JAVA_LONG; + } else if (carrier == float.class) { + return JAVA_FLOAT; + } else if (carrier == double.class) { + return JAVA_DOUBLE; + } else if (carrier == MemoryAddress.class) { + return ADDRESS; + } else { + throw new UnsupportedOperationException(); + } + } } diff --git a/test/jdk/java/foreign/TestLayoutPaths.java b/test/jdk/java/foreign/TestLayoutPaths.java index 1cf3798c249f1..438dc86ada6ae 100644 --- a/test/jdk/java/foreign/TestLayoutPaths.java +++ b/test/jdk/java/foreign/TestLayoutPaths.java @@ -28,24 +28,23 @@ */ import jdk.incubator.foreign.GroupLayout; -import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout.PathElement; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.SequenceLayout; +import jdk.incubator.foreign.ValueLayout; import org.testng.SkipException; import org.testng.annotations.*; import java.lang.invoke.MethodHandle; -import java.nio.ByteOrder; import java.util.ArrayList; import java.util.List; import static jdk.incubator.foreign.MemoryLayout.PathElement.groupElement; import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement; -import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; import static org.testng.Assert.*; public class TestLayoutPaths { @@ -153,7 +152,7 @@ public void testByteNegativeSeqRange() { @Test(expectedExceptions = IllegalArgumentException.class) public void testIncompleteAccess() { SequenceLayout seq = MemoryLayout.sequenceLayout(5, MemoryLayout.structLayout(JAVA_INT)); - seq.varHandle(int.class, sequenceElement()); + seq.varHandle(sequenceElement()); } @Test(expectedExceptions = IllegalArgumentException.class) @@ -221,7 +220,7 @@ public void testBadContainerAlign() { throw new AssertionError(ex); // should be ok! } try { - g.varHandle(int.class, groupElement("foo")); //ok + g.varHandle(groupElement("foo")); //ok assertTrue(false); //should fail! } catch (UnsupportedOperationException ex) { //ok @@ -232,7 +231,7 @@ public void testBadContainerAlign() { @Test public void testBadAlignOffset() { - GroupLayout g = MemoryLayout.structLayout(MemoryLayouts.PAD_8, JAVA_INT.withBitAlignment(16).withName("foo")); + GroupLayout g = MemoryLayout.structLayout(MemoryLayout.paddingLayout(8), JAVA_INT.withBitAlignment(16).withName("foo")); try { g.bitOffset(groupElement("foo")); g.byteOffset(groupElement("foo")); @@ -240,7 +239,7 @@ public void testBadAlignOffset() { throw new AssertionError(ex); // should be ok! } try { - g.varHandle(int.class, groupElement("foo")); //ok + g.varHandle(groupElement("foo")); //ok assertTrue(false); //should fail! } catch (UnsupportedOperationException ex) { //ok @@ -299,10 +298,10 @@ public void testBadSequencePathInMap() { public void testStructPaths() { long[] offsets = { 0, 8, 24, 56 }; GroupLayout g = MemoryLayout.structLayout( - MemoryLayouts.JAVA_BYTE.withName("1"), - MemoryLayouts.JAVA_CHAR.withName("2"), - MemoryLayouts.JAVA_FLOAT.withName("3"), - MemoryLayouts.JAVA_LONG.withName("4") + ValueLayout.JAVA_BYTE.withName("1"), + ValueLayout.JAVA_CHAR.withName("2"), + ValueLayout.JAVA_FLOAT.withName("3"), + ValueLayout.JAVA_LONG.withName("4") ); // test select @@ -324,11 +323,11 @@ public void testStructPaths() { // test map for (int i = 1 ; i <= 4 ; i++) { - GroupLayout g2 = (GroupLayout)g.map(l -> MemoryLayouts.JAVA_DOUBLE, groupElement(String.valueOf(i))); + GroupLayout g2 = (GroupLayout)g.map(l -> ValueLayout.JAVA_DOUBLE, groupElement(String.valueOf(i))); assertTrue(g2.isStruct()); for (int j = 0 ; j < 4 ; j++) { if (j == i - 1) { - assertEquals(g2.memberLayouts().get(j), MemoryLayouts.JAVA_DOUBLE); + assertEquals(g2.memberLayouts().get(j), ValueLayout.JAVA_DOUBLE); } else { assertEquals(g2.memberLayouts().get(j), g.memberLayouts().get(j)); } @@ -340,10 +339,10 @@ public void testStructPaths() { public void testUnionPaths() { long[] offsets = { 0, 0, 0, 0 }; GroupLayout g = MemoryLayout.unionLayout( - MemoryLayouts.JAVA_BYTE.withName("1"), - MemoryLayouts.JAVA_CHAR.withName("2"), - MemoryLayouts.JAVA_FLOAT.withName("3"), - MemoryLayouts.JAVA_LONG.withName("4") + ValueLayout.JAVA_BYTE.withName("1"), + ValueLayout.JAVA_CHAR.withName("2"), + ValueLayout.JAVA_FLOAT.withName("3"), + ValueLayout.JAVA_LONG.withName("4") ); // test select @@ -365,11 +364,11 @@ public void testUnionPaths() { // test map for (int i = 1 ; i <= 4 ; i++) { - GroupLayout g2 = (GroupLayout)g.map(l -> MemoryLayouts.JAVA_DOUBLE, groupElement(String.valueOf(i))); + GroupLayout g2 = (GroupLayout)g.map(l -> ValueLayout.JAVA_DOUBLE, groupElement(String.valueOf(i))); assertTrue(g2.isUnion()); for (int j = 0 ; j < 4 ; j++) { if (j == i - 1) { - assertEquals(g2.memberLayouts().get(j), MemoryLayouts.JAVA_DOUBLE); + assertEquals(g2.memberLayouts().get(j), ValueLayout.JAVA_DOUBLE); } else { assertEquals(g2.memberLayouts().get(j), g.memberLayouts().get(j)); } @@ -380,12 +379,12 @@ public void testUnionPaths() { @Test public void testSequencePaths() { long[] offsets = { 0, 8, 16, 24 }; - SequenceLayout g = MemoryLayout.sequenceLayout(4, MemoryLayouts.JAVA_BYTE); + SequenceLayout g = MemoryLayout.sequenceLayout(4, ValueLayout.JAVA_BYTE); // test select MemoryLayout selected = g.select(sequenceElement()); - assertTrue(selected == MemoryLayouts.JAVA_BYTE); + assertTrue(selected == ValueLayout.JAVA_BYTE); // test offset @@ -398,8 +397,8 @@ public void testSequencePaths() { // test map - SequenceLayout seq2 = (SequenceLayout)g.map(l -> MemoryLayouts.JAVA_DOUBLE, sequenceElement()); - assertTrue(seq2.elementLayout() == MemoryLayouts.JAVA_DOUBLE); + SequenceLayout seq2 = (SequenceLayout)g.map(l -> ValueLayout.JAVA_DOUBLE, sequenceElement()); + assertTrue(seq2.elementLayout() == ValueLayout.JAVA_DOUBLE); } @Test(dataProvider = "testLayouts") @@ -506,26 +505,16 @@ public void testSliceHandle(MemoryLayout layout, PathElement[] pathElements, lon try (ResourceScope scope = ResourceScope.newConfinedScope()) { MemorySegment segment = MemorySegment.allocateNative(layout, scope); MemorySegment slice = (MemorySegment) sliceHandle.invokeExact(segment, indexes); - assertEquals(slice.address().segmentOffset(segment), expectedBitOffset / 8); + assertEquals(slice.address().toRawLongValue() - segment.address().toRawLongValue(), expectedBitOffset / 8); assertEquals(slice.byteSize(), selected.byteSize()); } } - @Test(expectedExceptions = UnsupportedOperationException.class) - public void testSliceHandleUOEInvalidSize() { - MemoryLayout layout = MemoryLayout.structLayout( - MemoryLayout.valueLayout(32, ByteOrder.nativeOrder()).withName("x"), - MemoryLayout.valueLayout(31, ByteOrder.nativeOrder()).withName("y") // size not a multiple of 8 - ); - - layout.sliceHandle(groupElement("y")); // should throw - } - @Test(expectedExceptions = UnsupportedOperationException.class) public void testSliceHandleUOEInvalidOffsetEager() throws Throwable { MemoryLayout layout = MemoryLayout.structLayout( MemoryLayout.paddingLayout(5), - MemoryLayout.valueLayout(32, ByteOrder.nativeOrder()).withName("y") // offset not a multiple of 8 + JAVA_INT.withName("y") // offset not a multiple of 8 ); layout.sliceHandle(groupElement("y")); // should throw @@ -536,7 +525,7 @@ public void testSliceHandleUOEInvalidOffsetLate() throws Throwable { MemoryLayout layout = MemoryLayout.sequenceLayout(3, MemoryLayout.structLayout( MemoryLayout.paddingLayout(4), - MemoryLayout.valueLayout(32, ByteOrder.nativeOrder()).withName("y") // offset not a multiple of 8 + JAVA_INT.withName("y") // offset not a multiple of 8 ) ); diff --git a/test/jdk/java/foreign/TestLayouts.java b/test/jdk/java/foreign/TestLayouts.java index 006163d3d503a..6d54bef722d2b 100644 --- a/test/jdk/java/foreign/TestLayouts.java +++ b/test/jdk/java/foreign/TestLayouts.java @@ -34,15 +34,15 @@ import java.util.stream.Stream; import org.testng.annotations.*; + +import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_LONG; +import static jdk.incubator.foreign.ValueLayout.JAVA_SHORT; import static org.testng.Assert.*; public class TestLayouts { - @Test(dataProvider = "badLayoutSizes", expectedExceptions = IllegalArgumentException.class) - public void testBadLayoutSize(SizedLayoutFactory factory, long size) { - factory.make(size); - } - @Test(dataProvider = "badAlignments", expectedExceptions = IllegalArgumentException.class) public void testBadLayoutAlignment(MemoryLayout layout, long alignment) { layout.withBitAlignment(alignment); @@ -51,12 +51,12 @@ public void testBadLayoutAlignment(MemoryLayout layout, long alignment) { @Test public void testVLAInStruct() { MemoryLayout layout = MemoryLayout.structLayout( - MemoryLayouts.JAVA_INT.withName("size"), + ValueLayout.JAVA_INT.withName("size"), MemoryLayout.paddingLayout(32), - MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_DOUBLE).withName("arr")); + MemoryLayout.sequenceLayout(ValueLayout.JAVA_DOUBLE).withName("arr")); assertFalse(layout.hasSize()); - VarHandle size_handle = layout.varHandle(int.class, MemoryLayout.PathElement.groupElement("size")); - VarHandle array_elem_handle = layout.varHandle(double.class, + VarHandle size_handle = layout.varHandle(MemoryLayout.PathElement.groupElement("size")); + VarHandle array_elem_handle = layout.varHandle( MemoryLayout.PathElement.groupElement("arr"), MemoryLayout.PathElement.sequenceElement()); try (ResourceScope scope = ResourceScope.newConfinedScope()) { @@ -77,12 +77,12 @@ public void testVLAInStruct() { @Test public void testVLAInSequence() { MemoryLayout layout = MemoryLayout.structLayout( - MemoryLayouts.JAVA_INT.withName("size"), + ValueLayout.JAVA_INT.withName("size"), MemoryLayout.paddingLayout(32), - MemoryLayout.sequenceLayout(1, MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_DOUBLE)).withName("arr")); + MemoryLayout.sequenceLayout(1, MemoryLayout.sequenceLayout(ValueLayout.JAVA_DOUBLE)).withName("arr")); assertFalse(layout.hasSize()); - VarHandle size_handle = layout.varHandle(int.class, MemoryLayout.PathElement.groupElement("size")); - VarHandle array_elem_handle = layout.varHandle(double.class, + VarHandle size_handle = layout.varHandle(MemoryLayout.PathElement.groupElement("size")); + VarHandle array_elem_handle = layout.varHandle( MemoryLayout.PathElement.groupElement("arr"), MemoryLayout.PathElement.sequenceElement(0), MemoryLayout.PathElement.sequenceElement()); @@ -103,17 +103,17 @@ public void testVLAInSequence() { @Test public void testIndexedSequencePath() { - MemoryLayout seq = MemoryLayout.sequenceLayout(10, MemoryLayouts.JAVA_INT); + MemoryLayout seq = MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_INT); try (ResourceScope scope = ResourceScope.newConfinedScope()) { MemorySegment segment = MemorySegment.allocateNative(seq, scope); - VarHandle indexHandle = seq.varHandle(int.class, MemoryLayout.PathElement.sequenceElement()); + VarHandle indexHandle = seq.varHandle(MemoryLayout.PathElement.sequenceElement()); // init segment for (int i = 0 ; i < 10 ; i++) { indexHandle.set(segment, (long)i, i); } //check statically indexed handles for (int i = 0 ; i < 10 ; i++) { - VarHandle preindexHandle = seq.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(i)); + VarHandle preindexHandle = seq.varHandle(MemoryLayout.PathElement.sequenceElement(i)); int expected = (int)indexHandle.get(segment, (long)i); int found = (int)preindexHandle.get(segment); assertEquals(expected, found); @@ -143,13 +143,13 @@ public void testUnboundHash(MemoryLayout layout, long align) { @Test(expectedExceptions = IllegalArgumentException.class) public void testBadUnboundSequenceLayoutResize() { - SequenceLayout seq = MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT); + SequenceLayout seq = MemoryLayout.sequenceLayout(ValueLayout.JAVA_INT); seq.withElementCount(-1); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadBoundSequenceLayoutResize() { - SequenceLayout seq = MemoryLayout.sequenceLayout(10, MemoryLayouts.JAVA_INT); + SequenceLayout seq = MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_INT); seq.withElementCount(-1); } @@ -168,13 +168,13 @@ public void testEmptyGroup() { public void testStructSizeAndAlign() { MemoryLayout struct = MemoryLayout.structLayout( MemoryLayout.paddingLayout(8), - MemoryLayouts.JAVA_BYTE, - MemoryLayouts.JAVA_CHAR, - MemoryLayouts.JAVA_INT, - MemoryLayouts.JAVA_LONG + ValueLayout.JAVA_BYTE, + ValueLayout.JAVA_CHAR, + ValueLayout.JAVA_INT, + ValueLayout.JAVA_LONG ); assertEquals(struct.byteSize(), 1 + 1 + 2 + 4 + 8); - assertEquals(struct.byteAlignment(), MemoryLayouts.ADDRESS.byteAlignment()); + assertEquals(struct.byteAlignment(), ValueLayout.ADDRESS.byteAlignment()); } @Test(dataProvider="basicLayouts") @@ -199,13 +199,13 @@ public void testUnionPaddingAndAlign(MemoryLayout layout) { @Test public void testUnionSizeAndAlign() { MemoryLayout struct = MemoryLayout.unionLayout( - MemoryLayouts.JAVA_BYTE, - MemoryLayouts.JAVA_CHAR, - MemoryLayouts.JAVA_INT, - MemoryLayouts.JAVA_LONG + ValueLayout.JAVA_BYTE, + ValueLayout.JAVA_CHAR, + ValueLayout.JAVA_INT, + ValueLayout.JAVA_LONG ); assertEquals(struct.byteSize(), 8); - assertEquals(struct.byteAlignment(), MemoryLayouts.ADDRESS.byteAlignment()); + assertEquals(struct.byteAlignment(), ValueLayout.ADDRESS.byteAlignment()); } @Test(dataProvider = "layoutKinds") @@ -224,31 +224,19 @@ public void testAlignmentString(MemoryLayout layout, long bitAlign) { } } - @DataProvider(name = "badLayoutSizes") - public Object[][] factoriesAndSizes() { - return new Object[][] { - { SizedLayoutFactory.VALUE_BE, 0 }, - { SizedLayoutFactory.VALUE_BE, -1 }, - { SizedLayoutFactory.VALUE_LE, 0 }, - { SizedLayoutFactory.VALUE_LE, -1 }, - { SizedLayoutFactory.PADDING, 0 }, - { SizedLayoutFactory.PADDING, -1 }, - { SizedLayoutFactory.SEQUENCE, -1 } - }; - } - @DataProvider(name = "unboundLayouts") public Object[][] unboundLayouts() { + ValueLayout alignedInt = JAVA_INT.withBitAlignment(32); return new Object[][] { - { MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT), 32 }, - { MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT)), 32 }, - { MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT)), 32 }, - { MemoryLayout.structLayout(MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT)), 32 }, - { MemoryLayout.structLayout(MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT))), 32 }, - { MemoryLayout.structLayout(MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT))), 32 }, - { MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT)), 32 }, - { MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT))), 32 }, - { MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT))), 32 }, + { MemoryLayout.sequenceLayout(alignedInt), 32 }, + { MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(alignedInt)), 32 }, + { MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(alignedInt)), 32 }, + { MemoryLayout.structLayout(MemoryLayout.sequenceLayout(alignedInt)), 32 }, + { MemoryLayout.structLayout(MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(alignedInt))), 32 }, + { MemoryLayout.structLayout(MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(alignedInt))), 32 }, + { MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(alignedInt)), 32 }, + { MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(alignedInt))), 32 }, + { MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(alignedInt))), 32 }, }; } @@ -271,10 +259,10 @@ public Object[][] layoutsKinds() { } enum SizedLayoutFactory { - VALUE_LE(size -> MemoryLayout.valueLayout(size, ByteOrder.LITTLE_ENDIAN)), - VALUE_BE(size -> MemoryLayout.valueLayout(size, ByteOrder.BIG_ENDIAN)), + VALUE_LE(size -> valueLayoutForSize((int)size).withOrder(ByteOrder.LITTLE_ENDIAN)), + VALUE_BE(size -> valueLayoutForSize((int)size).withOrder(ByteOrder.BIG_ENDIAN)), PADDING(MemoryLayout::paddingLayout), - SEQUENCE(size -> MemoryLayout.sequenceLayout(size, MemoryLayouts.PAD_8)); + SEQUENCE(size -> MemoryLayout.sequenceLayout(size, MemoryLayout.paddingLayout(8))); private final LongFunction factory; @@ -287,13 +275,22 @@ MemoryLayout make(long size) { } } + static ValueLayout valueLayoutForSize(int size) { + return switch (size) { + case 1 -> JAVA_BYTE; + case 2 -> JAVA_SHORT; + case 4 -> JAVA_INT; + case 8 -> JAVA_LONG; + default -> throw new UnsupportedOperationException(); + }; + } + enum LayoutKind { - VALUE_LE(MemoryLayouts.BITS_8_LE), - VALUE_BE(MemoryLayouts.BITS_8_BE), - PADDING(MemoryLayouts.PAD_8), - SEQUENCE(MemoryLayout.sequenceLayout(1, MemoryLayouts.PAD_8)), - STRUCT(MemoryLayout.structLayout(MemoryLayouts.PAD_8, MemoryLayouts.PAD_8)), - UNION(MemoryLayout.unionLayout(MemoryLayouts.PAD_8, MemoryLayouts.PAD_8)); + VALUE(ValueLayout.JAVA_BYTE), + PADDING(MemoryLayout.paddingLayout(8)), + SEQUENCE(MemoryLayout.sequenceLayout(1, MemoryLayout.paddingLayout(8))), + STRUCT(MemoryLayout.structLayout(MemoryLayout.paddingLayout(8), MemoryLayout.paddingLayout(8))), + UNION(MemoryLayout.unionLayout(MemoryLayout.paddingLayout(8), MemoryLayout.paddingLayout(8))); final MemoryLayout layout; @@ -333,12 +330,12 @@ public Object[][] layoutsAndAlignments() { } static MemoryLayout[] basicLayouts = { - MemoryLayouts.JAVA_BYTE, - MemoryLayouts.JAVA_CHAR, - MemoryLayouts.JAVA_SHORT, - MemoryLayouts.JAVA_INT, - MemoryLayouts.JAVA_FLOAT, - MemoryLayouts.JAVA_LONG, - MemoryLayouts.JAVA_DOUBLE, + ValueLayout.JAVA_BYTE, + ValueLayout.JAVA_CHAR, + ValueLayout.JAVA_SHORT, + ValueLayout.JAVA_INT, + ValueLayout.JAVA_FLOAT, + ValueLayout.JAVA_LONG, + ValueLayout.JAVA_DOUBLE, }; } diff --git a/test/jdk/java/foreign/TestMemoryAccess.java b/test/jdk/java/foreign/TestMemoryAccess.java index d843c7b0b3921..7eade5f8147d4 100644 --- a/test/jdk/java/foreign/TestMemoryAccess.java +++ b/test/jdk/java/foreign/TestMemoryAccess.java @@ -30,7 +30,7 @@ */ import jdk.incubator.foreign.GroupLayout; -import jdk.incubator.foreign.MemoryLayouts; +import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout.PathElement; import jdk.incubator.foreign.MemorySegment; @@ -39,6 +39,7 @@ import jdk.incubator.foreign.ValueLayout; import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; import java.util.function.Function; import org.testng.annotations.*; @@ -47,39 +48,39 @@ public class TestMemoryAccess { @Test(dataProvider = "elements") - public void testAccess(Function viewFactory, ValueLayout elemLayout, Class carrier, Checker checker) { + public void testAccess(Function viewFactory, ValueLayout elemLayout, Checker checker) { ValueLayout layout = elemLayout.withName("elem"); - testAccessInternal(viewFactory, layout, layout.varHandle(carrier), checker); + testAccessInternal(viewFactory, layout, layout.varHandle(), checker); } @Test(dataProvider = "elements") - public void testPaddedAccessByName(Function viewFactory, MemoryLayout elemLayout, Class carrier, Checker checker) { + public void testPaddedAccessByName(Function viewFactory, MemoryLayout elemLayout, Checker checker) { GroupLayout layout = MemoryLayout.structLayout(MemoryLayout.paddingLayout(elemLayout.bitSize()), elemLayout.withName("elem")); - testAccessInternal(viewFactory, layout, layout.varHandle(carrier, PathElement.groupElement("elem")), checker); + testAccessInternal(viewFactory, layout, layout.varHandle(PathElement.groupElement("elem")), checker); } @Test(dataProvider = "elements") - public void testPaddedAccessByIndexSeq(Function viewFactory, MemoryLayout elemLayout, Class carrier, Checker checker) { + public void testPaddedAccessByIndexSeq(Function viewFactory, MemoryLayout elemLayout, Checker checker) { SequenceLayout layout = MemoryLayout.sequenceLayout(2, elemLayout); - testAccessInternal(viewFactory, layout, layout.varHandle(carrier, PathElement.sequenceElement(1)), checker); + testAccessInternal(viewFactory, layout, layout.varHandle(PathElement.sequenceElement(1)), checker); } @Test(dataProvider = "arrayElements") - public void testArrayAccess(Function viewFactory, MemoryLayout elemLayout, Class carrier, ArrayChecker checker) { + public void testArrayAccess(Function viewFactory, MemoryLayout elemLayout, ArrayChecker checker) { SequenceLayout seq = MemoryLayout.sequenceLayout(10, elemLayout.withName("elem")); - testArrayAccessInternal(viewFactory, seq, seq.varHandle(carrier, PathElement.sequenceElement()), checker); + testArrayAccessInternal(viewFactory, seq, seq.varHandle(PathElement.sequenceElement()), checker); } @Test(dataProvider = "arrayElements") - public void testPaddedArrayAccessByName(Function viewFactory, MemoryLayout elemLayout, Class carrier, ArrayChecker checker) { + public void testPaddedArrayAccessByName(Function viewFactory, MemoryLayout elemLayout, ArrayChecker checker) { SequenceLayout seq = MemoryLayout.sequenceLayout(10, MemoryLayout.structLayout(MemoryLayout.paddingLayout(elemLayout.bitSize()), elemLayout.withName("elem"))); - testArrayAccessInternal(viewFactory, seq, seq.varHandle(carrier, MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("elem")), checker); + testArrayAccessInternal(viewFactory, seq, seq.varHandle(MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("elem")), checker); } @Test(dataProvider = "arrayElements") - public void testPaddedArrayAccessByIndexSeq(Function viewFactory, MemoryLayout elemLayout, Class carrier, ArrayChecker checker) { + public void testPaddedArrayAccessByIndexSeq(Function viewFactory, MemoryLayout elemLayout, ArrayChecker checker) { SequenceLayout seq = MemoryLayout.sequenceLayout(10, MemoryLayout.sequenceLayout(2, elemLayout)); - testArrayAccessInternal(viewFactory, seq, seq.varHandle(carrier, PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement(1)), checker); + testArrayAccessInternal(viewFactory, seq, seq.varHandle(PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement(1)), checker); } private void testAccessInternal(Function viewFactory, MemoryLayout layout, VarHandle handle, Checker checker) { @@ -149,40 +150,33 @@ private void testArrayAccessInternal(Function view } @Test(dataProvider = "matrixElements") - public void testMatrixAccess(Function viewFactory, MemoryLayout elemLayout, Class carrier, MatrixChecker checker) { + public void testMatrixAccess(Function viewFactory, MemoryLayout elemLayout, MatrixChecker checker) { SequenceLayout seq = MemoryLayout.sequenceLayout(20, MemoryLayout.sequenceLayout(10, elemLayout.withName("elem"))); - testMatrixAccessInternal(viewFactory, seq, seq.varHandle(carrier, + testMatrixAccessInternal(viewFactory, seq, seq.varHandle( PathElement.sequenceElement(), PathElement.sequenceElement()), checker); } @Test(dataProvider = "matrixElements") - public void testPaddedMatrixAccessByName(Function viewFactory, MemoryLayout elemLayout, Class carrier, MatrixChecker checker) { + public void testPaddedMatrixAccessByName(Function viewFactory, MemoryLayout elemLayout, MatrixChecker checker) { SequenceLayout seq = MemoryLayout.sequenceLayout(20, MemoryLayout.sequenceLayout(10, MemoryLayout.structLayout(MemoryLayout.paddingLayout(elemLayout.bitSize()), elemLayout.withName("elem")))); testMatrixAccessInternal(viewFactory, seq, - seq.varHandle(carrier, + seq.varHandle( PathElement.sequenceElement(), PathElement.sequenceElement(), PathElement.groupElement("elem")), checker); } @Test(dataProvider = "matrixElements") - public void testPaddedMatrixAccessByIndexSeq(Function viewFactory, MemoryLayout elemLayout, Class carrier, MatrixChecker checker) { + public void testPaddedMatrixAccessByIndexSeq(Function viewFactory, MemoryLayout elemLayout, MatrixChecker checker) { SequenceLayout seq = MemoryLayout.sequenceLayout(20, MemoryLayout.sequenceLayout(10, MemoryLayout.sequenceLayout(2, elemLayout))); testMatrixAccessInternal(viewFactory, seq, - seq.varHandle(carrier, + seq.varHandle( PathElement.sequenceElement(), PathElement.sequenceElement(), PathElement.sequenceElement(1)), checker); } - @Test(dataProvider = "badCarriers", - expectedExceptions = IllegalArgumentException.class) - public void testBadCarriers(Class carrier) { - ValueLayout l = MemoryLayouts.BITS_32_LE.withName("elem"); - l.varHandle(carrier); - } - private void testMatrixAccessInternal(Function viewFactory, SequenceLayout seq, VarHandle handle, MatrixChecker checker) { MemorySegment outer_segment; try (ResourceScope scope = ResourceScope.newConfinedScope()) { @@ -227,37 +221,37 @@ private void testMatrixAccessInternal(Function vie public Object[][] createData() { return new Object[][] { //BE, RW - { ID, MemoryLayouts.BITS_8_BE, byte.class, Checker.BYTE }, - { ID, MemoryLayouts.BITS_16_BE, short.class, Checker.SHORT }, - { ID, MemoryLayouts.BITS_16_BE, char.class, Checker.CHAR }, - { ID, MemoryLayouts.BITS_32_BE, int.class, Checker.INT }, - { ID, MemoryLayouts.BITS_64_BE, long.class, Checker.LONG }, - { ID, MemoryLayouts.BITS_32_BE, float.class, Checker.FLOAT }, - { ID, MemoryLayouts.BITS_64_BE, double.class, Checker.DOUBLE }, + { ID, ValueLayout.JAVA_BYTE, Checker.BYTE }, + { ID, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), Checker.SHORT }, + { ID, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), Checker.CHAR }, + { ID, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), Checker.INT }, + { ID, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), Checker.LONG }, + { ID, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), Checker.FLOAT }, + { ID, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), Checker.DOUBLE }, //BE, RO - { IMMUTABLE, MemoryLayouts.BITS_8_BE, byte.class, Checker.BYTE }, - { IMMUTABLE, MemoryLayouts.BITS_16_BE, short.class, Checker.SHORT }, - { IMMUTABLE, MemoryLayouts.BITS_16_BE, char.class, Checker.CHAR }, - { IMMUTABLE, MemoryLayouts.BITS_32_BE, int.class, Checker.INT }, - { IMMUTABLE, MemoryLayouts.BITS_64_BE, long.class, Checker.LONG }, - { IMMUTABLE, MemoryLayouts.BITS_32_BE, float.class, Checker.FLOAT }, - { IMMUTABLE, MemoryLayouts.BITS_64_BE, double.class, Checker.DOUBLE }, + { IMMUTABLE, ValueLayout.JAVA_BYTE, Checker.BYTE }, + { IMMUTABLE, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), Checker.SHORT }, + { IMMUTABLE, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), Checker.CHAR }, + { IMMUTABLE, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), Checker.INT }, + { IMMUTABLE, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), Checker.LONG }, + { IMMUTABLE, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), Checker.FLOAT }, + { IMMUTABLE, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), Checker.DOUBLE }, //LE, RW - { ID, MemoryLayouts.BITS_8_LE, byte.class, Checker.BYTE }, - { ID, MemoryLayouts.BITS_16_LE, short.class, Checker.SHORT }, - { ID, MemoryLayouts.BITS_16_LE, char.class, Checker.CHAR }, - { ID, MemoryLayouts.BITS_32_LE, int.class, Checker.INT }, - { ID, MemoryLayouts.BITS_64_LE, long.class, Checker.LONG }, - { ID, MemoryLayouts.BITS_32_LE, float.class, Checker.FLOAT }, - { ID, MemoryLayouts.BITS_64_LE, double.class, Checker.DOUBLE }, + { ID, ValueLayout.JAVA_BYTE, Checker.BYTE }, + { ID, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.SHORT }, + { ID, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.CHAR }, + { ID, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.INT }, + { ID, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.LONG }, + { ID, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.FLOAT }, + { ID, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.DOUBLE }, //LE, RO - { IMMUTABLE, MemoryLayouts.BITS_8_LE, byte.class, Checker.BYTE }, - { IMMUTABLE, MemoryLayouts.BITS_16_LE, short.class, Checker.SHORT }, - { IMMUTABLE, MemoryLayouts.BITS_16_LE, char.class, Checker.CHAR }, - { IMMUTABLE, MemoryLayouts.BITS_32_LE, int.class, Checker.INT }, - { IMMUTABLE, MemoryLayouts.BITS_64_LE, long.class, Checker.LONG }, - { IMMUTABLE, MemoryLayouts.BITS_32_LE, float.class, Checker.FLOAT }, - { IMMUTABLE, MemoryLayouts.BITS_64_LE, double.class, Checker.DOUBLE }, + { IMMUTABLE, ValueLayout.JAVA_BYTE, Checker.BYTE }, + { IMMUTABLE, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.SHORT }, + { IMMUTABLE, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.CHAR }, + { IMMUTABLE, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.INT }, + { IMMUTABLE, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.LONG }, + { IMMUTABLE, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.FLOAT }, + { IMMUTABLE, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.DOUBLE }, }; } @@ -304,37 +298,37 @@ interface Checker { public Object[][] createArrayData() { return new Object[][] { //BE, RW - { ID, MemoryLayouts.BITS_8_BE, byte.class, ArrayChecker.BYTE }, - { ID, MemoryLayouts.BITS_16_BE, short.class, ArrayChecker.SHORT }, - { ID, MemoryLayouts.BITS_16_BE, char.class, ArrayChecker.CHAR }, - { ID, MemoryLayouts.BITS_32_BE, int.class, ArrayChecker.INT }, - { ID, MemoryLayouts.BITS_64_BE, long.class, ArrayChecker.LONG }, - { ID, MemoryLayouts.BITS_32_BE, float.class, ArrayChecker.FLOAT }, - { ID, MemoryLayouts.BITS_64_BE, double.class, ArrayChecker.DOUBLE }, + { ID, ValueLayout.JAVA_BYTE, ArrayChecker.BYTE }, + { ID, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.SHORT }, + { ID, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.CHAR }, + { ID, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.INT }, + { ID, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.LONG }, + { ID, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.FLOAT }, + { ID, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.DOUBLE }, //BE, RO - { IMMUTABLE, MemoryLayouts.BITS_8_BE, byte.class, ArrayChecker.BYTE }, - { IMMUTABLE, MemoryLayouts.BITS_16_BE, short.class, ArrayChecker.SHORT }, - { IMMUTABLE, MemoryLayouts.BITS_16_BE, char.class, ArrayChecker.CHAR }, - { IMMUTABLE, MemoryLayouts.BITS_32_BE, int.class, ArrayChecker.INT }, - { IMMUTABLE, MemoryLayouts.BITS_64_BE, long.class, ArrayChecker.LONG }, - { IMMUTABLE, MemoryLayouts.BITS_32_BE, float.class, ArrayChecker.FLOAT }, - { IMMUTABLE, MemoryLayouts.BITS_64_BE, double.class, ArrayChecker.DOUBLE }, + { IMMUTABLE, ValueLayout.JAVA_BYTE, ArrayChecker.BYTE }, + { IMMUTABLE, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.SHORT }, + { IMMUTABLE, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.CHAR }, + { IMMUTABLE, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.INT }, + { IMMUTABLE, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.LONG }, + { IMMUTABLE, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.FLOAT }, + { IMMUTABLE, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.DOUBLE }, //LE, RW - { ID, MemoryLayouts.BITS_8_LE, byte.class, ArrayChecker.BYTE }, - { ID, MemoryLayouts.BITS_16_LE, short.class, ArrayChecker.SHORT }, - { ID, MemoryLayouts.BITS_16_LE, char.class, ArrayChecker.CHAR }, - { ID, MemoryLayouts.BITS_32_LE, int.class, ArrayChecker.INT }, - { ID, MemoryLayouts.BITS_64_LE, long.class, ArrayChecker.LONG }, - { ID, MemoryLayouts.BITS_32_LE, float.class, ArrayChecker.FLOAT }, - { ID, MemoryLayouts.BITS_64_LE, double.class, ArrayChecker.DOUBLE }, + { ID, ValueLayout.JAVA_BYTE, ArrayChecker.BYTE }, + { ID, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.SHORT }, + { ID, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.CHAR }, + { ID, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.INT }, + { ID, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.LONG }, + { ID, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.FLOAT }, + { ID, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.DOUBLE }, //LE, RO - { IMMUTABLE, MemoryLayouts.BITS_8_LE, byte.class, ArrayChecker.BYTE }, - { IMMUTABLE, MemoryLayouts.BITS_16_LE, short.class, ArrayChecker.SHORT }, - { IMMUTABLE, MemoryLayouts.BITS_16_LE, char.class, ArrayChecker.CHAR }, - { IMMUTABLE, MemoryLayouts.BITS_32_LE, int.class, ArrayChecker.INT }, - { IMMUTABLE, MemoryLayouts.BITS_64_LE, long.class, ArrayChecker.LONG }, - { IMMUTABLE, MemoryLayouts.BITS_32_LE, float.class, ArrayChecker.FLOAT }, - { IMMUTABLE, MemoryLayouts.BITS_64_LE, double.class, ArrayChecker.DOUBLE }, + { IMMUTABLE, ValueLayout.JAVA_BYTE, ArrayChecker.BYTE }, + { IMMUTABLE, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.SHORT }, + { IMMUTABLE, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.CHAR }, + { IMMUTABLE, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.INT }, + { IMMUTABLE, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.LONG }, + { IMMUTABLE, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.FLOAT }, + { IMMUTABLE, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.DOUBLE }, }; } @@ -381,37 +375,45 @@ interface ArrayChecker { public Object[][] createMatrixData() { return new Object[][] { //BE, RW - { ID, MemoryLayouts.BITS_8_BE, byte.class, MatrixChecker.BYTE }, - { ID, MemoryLayouts.BITS_16_BE, short.class, MatrixChecker.SHORT }, - { ID, MemoryLayouts.BITS_16_BE, char.class, MatrixChecker.CHAR }, - { ID, MemoryLayouts.BITS_32_BE, int.class, MatrixChecker.INT }, - { ID, MemoryLayouts.BITS_64_BE, long.class, MatrixChecker.LONG }, - { ID, MemoryLayouts.BITS_32_BE, float.class, MatrixChecker.FLOAT }, - { ID, MemoryLayouts.BITS_64_BE, double.class, MatrixChecker.DOUBLE }, + { ID, ValueLayout.JAVA_BYTE, MatrixChecker.BYTE }, + { ID, ValueLayout.JAVA_BOOLEAN, MatrixChecker.BOOLEAN }, + { ID, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.SHORT }, + { ID, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.CHAR }, + { ID, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.INT }, + { ID, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.LONG }, + { ID, ValueLayout.ADDRESS.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.ADDR }, + { ID, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.FLOAT }, + { ID, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.DOUBLE }, //BE, RO - { IMMUTABLE, MemoryLayouts.BITS_8_BE, byte.class, MatrixChecker.BYTE }, - { IMMUTABLE, MemoryLayouts.BITS_16_BE, short.class, MatrixChecker.SHORT }, - { IMMUTABLE, MemoryLayouts.BITS_16_BE, char.class, MatrixChecker.CHAR }, - { IMMUTABLE, MemoryLayouts.BITS_32_BE, int.class, MatrixChecker.INT }, - { IMMUTABLE, MemoryLayouts.BITS_64_BE, long.class, MatrixChecker.LONG }, - { IMMUTABLE, MemoryLayouts.BITS_32_BE, float.class, MatrixChecker.FLOAT }, - { IMMUTABLE, MemoryLayouts.BITS_64_BE, double.class, MatrixChecker.DOUBLE }, + { IMMUTABLE, ValueLayout.JAVA_BYTE, MatrixChecker.BYTE }, + { IMMUTABLE, ValueLayout.JAVA_BOOLEAN, MatrixChecker.BOOLEAN }, + { IMMUTABLE, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.SHORT }, + { IMMUTABLE, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.CHAR }, + { IMMUTABLE, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.INT }, + { IMMUTABLE, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.LONG }, + { IMMUTABLE, ValueLayout.ADDRESS.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.ADDR }, + { IMMUTABLE, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.FLOAT }, + { IMMUTABLE, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.DOUBLE }, //LE, RW - { ID, MemoryLayouts.BITS_8_LE, byte.class, MatrixChecker.BYTE }, - { ID, MemoryLayouts.BITS_16_LE, short.class, MatrixChecker.SHORT }, - { ID, MemoryLayouts.BITS_16_LE, char.class, MatrixChecker.CHAR }, - { ID, MemoryLayouts.BITS_32_LE, int.class, MatrixChecker.INT }, - { ID, MemoryLayouts.BITS_64_LE, long.class, MatrixChecker.LONG }, - { ID, MemoryLayouts.BITS_32_LE, float.class, MatrixChecker.FLOAT }, - { ID, MemoryLayouts.BITS_64_LE, double.class, MatrixChecker.DOUBLE }, + { ID, ValueLayout.JAVA_BYTE, MatrixChecker.BYTE }, + { ID, ValueLayout.JAVA_BOOLEAN, MatrixChecker.BOOLEAN }, + { ID, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.SHORT }, + { ID, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.CHAR }, + { ID, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.INT }, + { ID, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.LONG }, + { ID, ValueLayout.ADDRESS.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.ADDR }, + { ID, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.FLOAT }, + { ID, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.DOUBLE }, //LE, RO - { IMMUTABLE, MemoryLayouts.BITS_8_LE, byte.class, MatrixChecker.BYTE }, - { IMMUTABLE, MemoryLayouts.BITS_16_LE, short.class, MatrixChecker.SHORT }, - { IMMUTABLE, MemoryLayouts.BITS_16_LE, char.class, MatrixChecker.CHAR }, - { IMMUTABLE, MemoryLayouts.BITS_32_LE, int.class, MatrixChecker.INT }, - { IMMUTABLE, MemoryLayouts.BITS_64_LE, long.class, MatrixChecker.LONG }, - { IMMUTABLE, MemoryLayouts.BITS_32_LE, float.class, MatrixChecker.FLOAT }, - { IMMUTABLE, MemoryLayouts.BITS_64_LE, double.class, MatrixChecker.DOUBLE }, + { IMMUTABLE, ValueLayout.JAVA_BYTE, MatrixChecker.BYTE }, + { IMMUTABLE, ValueLayout.JAVA_BOOLEAN, MatrixChecker.BOOLEAN }, + { IMMUTABLE, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.SHORT }, + { IMMUTABLE, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.CHAR }, + { IMMUTABLE, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.INT }, + { IMMUTABLE, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.LONG }, + { IMMUTABLE, ValueLayout.ADDRESS.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.ADDR }, + { IMMUTABLE, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.FLOAT }, + { IMMUTABLE, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.DOUBLE }, }; } @@ -423,6 +425,11 @@ interface MatrixChecker { assertEquals(r + c, (byte)handle.get(segment, r, c)); }; + MatrixChecker BOOLEAN = (handle, segment, r, c) -> { + handle.set(segment, r, c, (r + c) != 0); + assertEquals((r + c) != 0, (boolean)handle.get(segment, r, c)); + }; + MatrixChecker SHORT = (handle, segment, r, c) -> { handle.set(segment, r, c, (short)(r + c)); assertEquals(r + c, (short)handle.get(segment, r, c)); @@ -443,6 +450,11 @@ interface MatrixChecker { assertEquals(r + c, (long)handle.get(segment, r, c)); }; + MatrixChecker ADDR = (handle, segment, r, c) -> { + handle.set(segment, r, c, MemoryAddress.ofLong(r + c)); + assertEquals(MemoryAddress.ofLong(r + c), (MemoryAddress)handle.get(segment, r, c)); + }; + MatrixChecker FLOAT = (handle, segment, r, c) -> { handle.set(segment, r, c, (float)(r + c)); assertEquals((float)(r + c), (float)handle.get(segment, r, c)); @@ -453,14 +465,4 @@ interface MatrixChecker { assertEquals((double)(r + c), (double)handle.get(segment, r, c)); }; } - - @DataProvider(name = "badCarriers") - public Object[][] createBadCarriers() { - return new Object[][] { - { void.class }, - { boolean.class }, - { Object.class }, - { int[].class } - }; - } } diff --git a/test/jdk/java/foreign/TestMemoryAccessInstance.java b/test/jdk/java/foreign/TestMemoryAccessInstance.java new file mode 100644 index 0000000000000..702e95bf8da38 --- /dev/null +++ b/test/jdk/java/foreign/TestMemoryAccessInstance.java @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @run testng/othervm --enable-native-access=ALL-UNNAMED TestMemoryAccessInstance + */ + +import jdk.incubator.foreign.MemoryAddress; +import jdk.incubator.foreign.MemorySegment; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.function.Function; + +import jdk.incubator.foreign.ResourceScope; +import jdk.incubator.foreign.ValueLayout; +import org.testng.annotations.*; +import static org.testng.Assert.*; + +public class TestMemoryAccessInstance { + + static class Accessor { + + interface SegmentGetter { + X get(T buffer, L layout, long offset); + } + + interface SegmentSetter { + void set(T buffer, L layout, long offset, X o); + } + + interface BufferGetter { + X get(ByteBuffer segment, int offset); + } + + interface BufferSetter { + void set(ByteBuffer buffer, int offset, X o); + } + + final X value; + final L layout; + final Function transform; + final SegmentGetter segmentGetter; + final SegmentSetter segmentSetter; + final BufferGetter bufferGetter; + final BufferSetter bufferSetter; + + Accessor(Function transform, L layout, X value, + SegmentGetter segmentGetter, SegmentSetter segmentSetter, + BufferGetter bufferGetter, BufferSetter bufferSetter) { + this.transform = transform; + this.layout = layout; + this.value = value; + this.segmentGetter = segmentGetter; + this.segmentSetter = segmentSetter; + this.bufferGetter = bufferGetter; + this.bufferSetter = bufferSetter; + } + + void test() { + try (ResourceScope scope = ResourceScope.newConfinedScope()) { + MemorySegment segment = MemorySegment.allocateNative(64, scope); + ByteBuffer buffer = segment.asByteBuffer(); + T t = transform.apply(segment); + segmentSetter.set(t, layout, 4, value); + assertEquals(bufferGetter.get(buffer, 4), value); + bufferSetter.set(buffer, 4, value); + assertEquals(value, segmentGetter.get(t, layout, 4)); + } + } + + static Accessor ofSegment(L layout, X value, + SegmentGetter segmentGetter, SegmentSetter segmentSetter, + BufferGetter bufferGetter, BufferSetter bufferSetter) { + return new Accessor<>(Function.identity(), layout, value, segmentGetter, segmentSetter, bufferGetter, bufferSetter); + } + + static Accessor ofAddress(L layout, X value, + SegmentGetter segmentGetter, SegmentSetter segmentSetter, + BufferGetter bufferGetter, BufferSetter bufferSetter) { + return new Accessor<>(MemorySegment::address, layout, value, segmentGetter, segmentSetter, bufferGetter, bufferSetter); + } + } + + @Test(dataProvider = "segmentAccessors") + public void testSegmentAccess(String testName, Accessor accessor) { + accessor.test(); + } + + @Test(dataProvider = "addressAccessors") + public void testAddressAccess(String testName, Accessor accessor) { + accessor.test(); + } + + static final ByteOrder NE = ByteOrder.nativeOrder(); + + @DataProvider(name = "segmentAccessors") + static Object[][] segmentAccessors() { + return new Object[][]{ + + {"byte", Accessor.ofSegment(ValueLayout.JAVA_BYTE, (byte) 42, + MemorySegment::get, MemorySegment::set, + ByteBuffer::get, ByteBuffer::put) + }, + {"bool", Accessor.ofSegment(ValueLayout.JAVA_BOOLEAN, false, + MemorySegment::get, MemorySegment::set, + (bb, pos) -> bb.get(pos) != 0, (bb, pos, v) -> bb.put(pos, v ? (byte)1 : (byte)0)) + }, + {"char", Accessor.ofSegment(ValueLayout.JAVA_CHAR, (char) 42, + MemorySegment::get, MemorySegment::set, + (bb, pos) -> bb.order(NE).getChar(pos), (bb, pos, v) -> bb.order(NE).putChar(pos, v)) + }, + {"int", Accessor.ofSegment(ValueLayout.JAVA_INT, 42, + MemorySegment::get, MemorySegment::set, + (bb, pos) -> bb.order(NE).getInt(pos), (bb, pos, v) -> bb.order(NE).putInt(pos, v)) + }, + {"float", Accessor.ofSegment(ValueLayout.JAVA_FLOAT, 42f, + MemorySegment::get, MemorySegment::set, + (bb, pos) -> bb.order(NE).getFloat(pos), (bb, pos, v) -> bb.order(NE).putFloat(pos, v)) + }, + {"long", Accessor.ofSegment(ValueLayout.JAVA_LONG, 42L, + MemorySegment::get, MemorySegment::set, + (bb, pos) -> bb.order(NE).getLong(pos), (bb, pos, v) -> bb.order(NE).putLong(pos, v)) + }, + {"double", Accessor.ofSegment(ValueLayout.JAVA_DOUBLE, 42d, + MemorySegment::get, MemorySegment::set, + (bb, pos) -> bb.order(NE).getDouble(pos), (bb, pos, v) -> bb.order(NE).putDouble(pos, v)) + }, + { "address", Accessor.ofSegment(ValueLayout.ADDRESS, MemoryAddress.ofLong(42), + MemorySegment::get, MemorySegment::set, + (bb, pos) -> { + ByteBuffer nb = bb.order(NE); + long addr = ValueLayout.ADDRESS.byteSize() == 8 ? + nb.getLong(pos) : nb.getInt(pos); + return MemoryAddress.ofLong(addr); + }, + (bb, pos, v) -> { + ByteBuffer nb = bb.order(NE); + if (ValueLayout.ADDRESS.byteSize() == 8) { + nb.putLong(pos, v.toRawLongValue()); + } else { + nb.putInt(pos, (int)v.toRawLongValue()); + } + }) + }, + + {"char/index", Accessor.ofSegment(ValueLayout.JAVA_CHAR, (char) 42, + MemorySegment::getAtIndex, MemorySegment::setAtIndex, + (bb, pos) -> bb.order(NE).getChar(pos * 2), (bb, pos, v) -> bb.order(NE).putChar(pos * 2, v)) + }, + {"int/index", Accessor.ofSegment(ValueLayout.JAVA_INT, 42, + MemorySegment::getAtIndex, MemorySegment::setAtIndex, + (bb, pos) -> bb.order(NE).getInt(pos * 4), (bb, pos, v) -> bb.order(NE).putInt(pos * 4, v)) + }, + {"float/index", Accessor.ofSegment(ValueLayout.JAVA_FLOAT, 42f, + MemorySegment::getAtIndex, MemorySegment::setAtIndex, + (bb, pos) -> bb.order(NE).getFloat(pos * 4), (bb, pos, v) -> bb.order(NE).putFloat(pos * 4, v)) + }, + {"long/index", Accessor.ofSegment(ValueLayout.JAVA_LONG, 42L, + MemorySegment::getAtIndex, MemorySegment::setAtIndex, + (bb, pos) -> bb.order(NE).getLong(pos * 8), (bb, pos, v) -> bb.order(NE).putLong(pos * 8, v)) + }, + {"double/index", Accessor.ofSegment(ValueLayout.JAVA_DOUBLE, 42d, + MemorySegment::getAtIndex, MemorySegment::setAtIndex, + (bb, pos) -> bb.order(NE).getDouble(pos * 8), (bb, pos, v) -> bb.order(NE).putDouble(pos * 8, v)) + }, + { "address/index", Accessor.ofSegment(ValueLayout.ADDRESS, MemoryAddress.ofLong(42), + MemorySegment::getAtIndex, MemorySegment::setAtIndex, + (bb, pos) -> { + ByteBuffer nb = bb.order(NE); + long addr = ValueLayout.ADDRESS.byteSize() == 8 ? + nb.getLong(pos * 8) : nb.getInt(pos * 4); + return MemoryAddress.ofLong(addr); + }, + (bb, pos, v) -> { + ByteBuffer nb = bb.order(NE); + if (ValueLayout.ADDRESS.byteSize() == 8) { + nb.putLong(pos * 8, v.toRawLongValue()); + } else { + nb.putInt(pos * 4, (int)v.toRawLongValue()); + } + }) + }, + }; + } + + @DataProvider(name = "addressAccessors") + static Object[][] addressAccessors() { + return new Object[][]{ + + {"byte", Accessor.ofAddress(ValueLayout.JAVA_BYTE, (byte) 42, + MemoryAddress::get, MemoryAddress::set, + ByteBuffer::get, ByteBuffer::put) + }, + {"bool", Accessor.ofAddress(ValueLayout.JAVA_BOOLEAN, false, + MemoryAddress::get, MemoryAddress::set, + (bb, pos) -> bb.get(pos) != 0, (bb, pos, v) -> bb.put(pos, v ? (byte)1 : (byte)0)) + }, + {"char", Accessor.ofAddress(ValueLayout.JAVA_CHAR, (char) 42, + MemoryAddress::get, MemoryAddress::set, + (bb, pos) -> bb.order(NE).getChar(pos), (bb, pos, v) -> bb.order(NE).putChar(pos, v)) + }, + {"int", Accessor.ofAddress(ValueLayout.JAVA_INT, 42, + MemoryAddress::get, MemoryAddress::set, + (bb, pos) -> bb.order(NE).getInt(pos), (bb, pos, v) -> bb.order(NE).putInt(pos, v)) + }, + {"float", Accessor.ofAddress(ValueLayout.JAVA_FLOAT, 42f, + MemoryAddress::get, MemoryAddress::set, + (bb, pos) -> bb.order(NE).getFloat(pos), (bb, pos, v) -> bb.order(NE).putFloat(pos, v)) + }, + {"long", Accessor.ofAddress(ValueLayout.JAVA_LONG, 42L, + MemoryAddress::get, MemoryAddress::set, + (bb, pos) -> bb.order(NE).getLong(pos), (bb, pos, v) -> bb.order(NE).putLong(pos, v)) + }, + {"double", Accessor.ofAddress(ValueLayout.JAVA_DOUBLE, 42d, + MemoryAddress::get, MemoryAddress::set, + (bb, pos) -> bb.order(NE).getDouble(pos), (bb, pos, v) -> bb.order(NE).putDouble(pos, v)) + }, + { "address", Accessor.ofAddress(ValueLayout.ADDRESS, MemoryAddress.ofLong(42), + MemoryAddress::get, MemoryAddress::set, + (bb, pos) -> { + ByteBuffer nb = bb.order(NE); + long addr = ValueLayout.ADDRESS.byteSize() == 8 ? + nb.getLong(pos) : nb.getInt(pos); + return MemoryAddress.ofLong(addr); + }, + (bb, pos, v) -> { + ByteBuffer nb = bb.order(NE); + if (ValueLayout.ADDRESS.byteSize() == 8) { + nb.putLong(pos, v.toRawLongValue()); + } else { + nb.putInt(pos, (int)v.toRawLongValue()); + } + }) + }, + {"char/index", Accessor.ofAddress(ValueLayout.JAVA_CHAR, (char) 42, + MemoryAddress::getAtIndex, MemoryAddress::setAtIndex, + (bb, pos) -> bb.order(NE).getChar(pos * 2), (bb, pos, v) -> bb.order(NE).putChar(pos * 2, v)) + }, + {"int/index", Accessor.ofAddress(ValueLayout.JAVA_INT, 42, + MemoryAddress::getAtIndex, MemoryAddress::setAtIndex, + (bb, pos) -> bb.order(NE).getInt(pos * 4), (bb, pos, v) -> bb.order(NE).putInt(pos * 4, v)) + }, + {"float/index", Accessor.ofAddress(ValueLayout.JAVA_FLOAT, 42f, + MemoryAddress::getAtIndex, MemoryAddress::setAtIndex, + (bb, pos) -> bb.order(NE).getFloat(pos * 4), (bb, pos, v) -> bb.order(NE).putFloat(pos * 4, v)) + }, + {"long/index", Accessor.ofAddress(ValueLayout.JAVA_LONG, 42L, + MemoryAddress::getAtIndex, MemoryAddress::setAtIndex, + (bb, pos) -> bb.order(NE).getLong(pos * 8), (bb, pos, v) -> bb.order(NE).putLong(pos * 8, v)) + }, + {"double/index", Accessor.ofAddress(ValueLayout.JAVA_DOUBLE, 42d, + MemoryAddress::getAtIndex, MemoryAddress::setAtIndex, + (bb, pos) -> bb.order(NE).getDouble(pos * 8), (bb, pos, v) -> bb.order(NE).putDouble(pos * 8, v)) + }, + { "address/index", Accessor.ofAddress(ValueLayout.ADDRESS, MemoryAddress.ofLong(42), + MemoryAddress::getAtIndex, MemoryAddress::setAtIndex, + (bb, pos) -> { + ByteBuffer nb = bb.order(NE); + long addr = ValueLayout.ADDRESS.byteSize() == 8 ? + nb.getLong(pos * 8) : nb.getInt(pos * 4); + return MemoryAddress.ofLong(addr); + }, + (bb, pos, v) -> { + ByteBuffer nb = bb.order(NE); + if (ValueLayout.ADDRESS.byteSize() == 8) { + nb.putLong(pos * 8, v.toRawLongValue()); + } else { + nb.putInt(pos * 4, (int)v.toRawLongValue()); + } + }) + } + }; + } +} diff --git a/test/jdk/java/foreign/TestMemoryAccessStatics.java b/test/jdk/java/foreign/TestMemoryAccessStatics.java deleted file mode 100644 index 4115bf58146a1..0000000000000 --- a/test/jdk/java/foreign/TestMemoryAccessStatics.java +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @run testng TestMemoryAccessStatics - */ - -import jdk.incubator.foreign.MemoryAccess; -import jdk.incubator.foreign.MemoryAddress; -import jdk.incubator.foreign.MemoryLayouts; -import jdk.incubator.foreign.MemorySegment; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -import org.testng.annotations.*; -import static org.testng.Assert.*; - -public class TestMemoryAccessStatics { - - static class Accessor { - - interface SegmentGetter { - X get(MemorySegment segment); - } - - interface SegmentSetter { - void set(MemorySegment segment, X o); - } - - interface BufferGetter { - X get(ByteBuffer segment); - } - - interface BufferSetter { - void set(ByteBuffer buffer, X o); - } - - final X value; - final SegmentGetter segmentGetter; - final SegmentSetter segmentSetter; - final BufferGetter bufferGetter; - final BufferSetter bufferSetter; - - Accessor(X value, - SegmentGetter segmentGetter, SegmentSetter segmentSetter, - BufferGetter bufferGetter, BufferSetter bufferSetter) { - this.value = value; - this.segmentGetter = segmentGetter; - this.segmentSetter = segmentSetter; - this.bufferGetter = bufferGetter; - this.bufferSetter = bufferSetter; - } - - void test() { - MemorySegment segment = MemorySegment.ofArray(new byte[32]); - ByteBuffer buffer = segment.asByteBuffer(); - segmentSetter.set(segment, value); - assertEquals(bufferGetter.get(buffer), value); - bufferSetter.set(buffer, value); - assertEquals(value, segmentGetter.get(segment)); - } - - Accessor of(Z value, - SegmentGetter segmentGetter, SegmentSetter segmentSetter, - BufferGetter bufferGetter, BufferSetter bufferSetter) { - return new Accessor<>(value, segmentGetter, segmentSetter, bufferGetter, bufferSetter); - } - } - - @Test(dataProvider = "accessors") - public void testMemoryAccess(String testName, Accessor accessor) { - accessor.test(); - } - - static final ByteOrder BE = ByteOrder.BIG_ENDIAN; - static final ByteOrder LE = ByteOrder.LITTLE_ENDIAN; - static final ByteOrder NE = ByteOrder.nativeOrder(); - - @DataProvider(name = "accessors") - static Object[][] accessors() { - return new Object[][]{ - - {"byte", new Accessor<>((byte) 42, - MemoryAccess::getByte, MemoryAccess::setByte, - (bb) -> bb.get(0), (bb, v) -> bb.put(0, v)) - }, - {"char", new Accessor<>((char) 42, - MemoryAccess::getChar, MemoryAccess::setChar, - (bb) -> bb.order(NE).getChar(0), (bb, v) -> bb.order(NE).putChar(0, v)) - }, - {"char/LE", new Accessor<>((char) 42, - s -> MemoryAccess.getChar(s, LE), (s, x) -> MemoryAccess.setChar(s, LE, x), - (bb) -> bb.order(LE).getChar(0), (bb, v) -> bb.order(LE).putChar(0, v)) - }, - {"char/BE", new Accessor<>((char) 42, - s -> MemoryAccess.getChar(s, BE), (s, x) -> MemoryAccess.setChar(s, BE, x), - (bb) -> bb.order(BE).getChar(0), (bb, v) -> bb.order(BE).putChar(0, v)) - }, - {"short", new Accessor<>((short) 42, - MemoryAccess::getShort, MemoryAccess::setShort, - (bb) -> bb.order(NE).getShort(0), (bb, v) -> bb.order(NE).putShort(0, v)) - }, - {"short/LE", new Accessor<>((short) 42, - s -> MemoryAccess.getShort(s, LE), (s, x) -> MemoryAccess.setShort(s, LE, x), - (bb) -> bb.order(LE).getShort(0), (bb, v) -> bb.order(LE).putShort(0, v)) - }, - {"short/BE", new Accessor<>((short) 42, - s -> MemoryAccess.getShort(s, BE), (s, x) -> MemoryAccess.setShort(s, BE, x), - (bb) -> bb.order(BE).getShort(0), (bb, v) -> bb.order(BE).putShort(0, v)) - }, - {"int", new Accessor<>(42, - MemoryAccess::getInt, MemoryAccess::setInt, - (bb) -> bb.order(NE).getInt(0), (bb, v) -> bb.order(NE).putInt(0, v)) - }, - {"int/LE", new Accessor<>(42, - s -> MemoryAccess.getInt(s, LE), (s, x) -> MemoryAccess.setInt(s, LE, x), - (bb) -> bb.order(LE).getInt(0), (bb, v) -> bb.order(LE).putInt(0, v)) - }, - {"int/BE", new Accessor<>(42, - s -> MemoryAccess.getInt(s, BE), (s, x) -> MemoryAccess.setInt(s, BE, x), - (bb) -> bb.order(BE).getInt(0), (bb, v) -> bb.order(BE).putInt(0, v)) - }, - // float, no offset - {"float", new Accessor<>(42f, - MemoryAccess::getFloat, MemoryAccess::setFloat, - (bb) -> bb.order(NE).getFloat(0), (bb, v) -> bb.order(NE).putFloat(0, v)) - }, - {"float/LE", new Accessor<>(42f, - s -> MemoryAccess.getFloat(s, LE), (s, x) -> MemoryAccess.setFloat(s, LE, x), - (bb) -> bb.order(LE).getFloat(0), (bb, v) -> bb.order(LE).putFloat(0, v)) - }, - {"float/BE", new Accessor<>(42f, - s -> MemoryAccess.getFloat(s, BE), (s, x) -> MemoryAccess.setFloat(s, BE, x), - (bb) -> bb.order(BE).getFloat(0), (bb, v) -> bb.order(BE).putFloat(0, v)) - }, - // double, no offset - {"double", new Accessor<>(42d, - MemoryAccess::getDouble, MemoryAccess::setDouble, - (bb) -> bb.order(NE).getDouble(0), (bb, v) -> bb.order(NE).putDouble(0, v)) - }, - {"double/LE", new Accessor<>(42d, - s -> MemoryAccess.getDouble(s, LE), (s, x) -> MemoryAccess.setDouble(s, LE, x), - (bb) -> bb.order(LE).getDouble(0), (bb, v) -> bb.order(LE).putDouble(0, v)) - }, - {"double/BE", new Accessor<>(42d, - s -> MemoryAccess.getDouble(s, BE), (s, x) -> MemoryAccess.setDouble(s, BE, x), - (bb) -> bb.order(BE).getDouble(0), (bb, v) -> bb.order(BE).putDouble(0, v)) - }, - - - // byte, offset - {"byte/offset", new Accessor<>((byte) 42, - s -> MemoryAccess.getByteAtOffset(s, 4), (s, x) -> MemoryAccess.setByteAtOffset(s, 4, x), - (bb) -> bb.get(4), (bb, v) -> bb.put(4, v)) - }, - // char, offset - {"char/offset", new Accessor<>((char) 42, - s -> MemoryAccess.getCharAtOffset(s, 4), (s, x) -> MemoryAccess.setCharAtOffset(s, 4, x), - (bb) -> bb.order(NE).getChar(4), (bb, v) -> bb.order(NE).putChar(4, v)) - }, - {"char/offset/LE", new Accessor<>((char) 42, - s -> MemoryAccess.getCharAtOffset(s, 4, LE), (s, x) -> MemoryAccess.setCharAtOffset(s, 4, LE, x), - (bb) -> bb.order(LE).getChar(4), (bb, v) -> bb.order(LE).putChar(4, v)) - }, - {"char/offset/BE", new Accessor<>((char) 42, - s -> MemoryAccess.getCharAtOffset(s, 4, BE), (s, x) -> MemoryAccess.setCharAtOffset(s, 4, BE, x), - (bb) -> bb.order(BE).getChar(4), (bb, v) -> bb.order(BE).putChar(4, v)) - }, - // short, offset - {"short/offset", new Accessor<>((short) 42, - s -> MemoryAccess.getShortAtOffset(s, 4), (s, x) -> MemoryAccess.setShortAtOffset(s, 4, x), - (bb) -> bb.order(NE).getShort(4), (bb, v) -> bb.order(NE).putShort(4, v)) - }, - {"short/offset/LE", new Accessor<>((short) 42, - s -> MemoryAccess.getShortAtOffset(s, 4, LE), (s, x) -> MemoryAccess.setShortAtOffset(s, 4, LE, x), - (bb) -> bb.order(LE).getShort(4), (bb, v) -> bb.order(LE).putShort(4, v)) - }, - {"short/offset/BE", new Accessor<>((short) 42, - s -> MemoryAccess.getShortAtOffset(s, 4, BE), (s, x) -> MemoryAccess.setShortAtOffset(s, 4, BE, x), - (bb) -> bb.order(BE).getShort(4), (bb, v) -> bb.order(BE).putShort(4, v)) - }, - // int, offset - {"int/offset", new Accessor<>(42, - s -> MemoryAccess.getIntAtOffset(s, 4), (s, x) -> MemoryAccess.setIntAtOffset(s, 4, x), - (bb) -> bb.order(NE).getInt(4), (bb, v) -> bb.order(NE).putInt(4, v)) - }, - {"int/offset/LE", new Accessor<>(42, - s -> MemoryAccess.getIntAtOffset(s, 4, LE), (s, x) -> MemoryAccess.setIntAtOffset(s, 4, LE, x), - (bb) -> bb.order(LE).getInt(4), (bb, v) -> bb.order(LE).putInt(4, v)) - }, - {"int/offset/BE", new Accessor<>(42, - s -> MemoryAccess.getIntAtOffset(s, 4, BE), (s, x) -> MemoryAccess.setIntAtOffset(s, 4, BE, x), - (bb) -> bb.order(BE).getInt(4), (bb, v) -> bb.order(BE).putInt(4, v)) - }, - // float, offset - {"float/offset", new Accessor<>(42f, - s -> MemoryAccess.getFloatAtOffset(s, 4), (s, x) -> MemoryAccess.setFloatAtOffset(s, 4, x), - (bb) -> bb.order(NE).getFloat(4), (bb, v) -> bb.order(NE).putFloat(4, v)) - }, - {"float/offset/LE", new Accessor<>(42f, - s -> MemoryAccess.getFloatAtOffset(s, 4, LE), (s, x) -> MemoryAccess.setFloatAtOffset(s, 4, LE, x), - (bb) -> bb.order(LE).getFloat(4), (bb, v) -> bb.order(LE).putFloat(4, v)) - }, - {"float/offset/BE", new Accessor<>(42f, - s -> MemoryAccess.getFloatAtOffset(s, 4, BE), (s, x) -> MemoryAccess.setFloatAtOffset(s, 4, BE, x), - (bb) -> bb.order(BE).getFloat(4), (bb, v) -> bb.order(BE).putFloat(4, v)) - }, - // double, offset - {"double/offset", new Accessor<>(42d, - s -> MemoryAccess.getDoubleAtOffset(s, 4), (s, x) -> MemoryAccess.setDoubleAtOffset(s, 4, x), - (bb) -> bb.order(NE).getDouble(4), (bb, v) -> bb.order(NE).putDouble(4, v)) - }, - {"double/offset/LE", new Accessor<>(42d, - s -> MemoryAccess.getDoubleAtOffset(s, 4, LE), (s, x) -> MemoryAccess.setDoubleAtOffset(s, 4, LE, x), - (bb) -> bb.order(LE).getDouble(4), (bb, v) -> bb.order(LE).putDouble(4, v)) - }, - {"double/offset/BE", new Accessor<>(42d, - s -> MemoryAccess.getDoubleAtOffset(s, 4, BE), (s, x) -> MemoryAccess.setDoubleAtOffset(s, 4, BE, x), - (bb) -> bb.order(BE).getDouble(4), (bb, v) -> bb.order(BE).putDouble(4, v)) - }, - - - // char, index - {"char/index", new Accessor<>((char) 42, - s -> MemoryAccess.getCharAtIndex(s, 2), (s, x) -> MemoryAccess.setCharAtIndex(s, 2, x), - (bb) -> bb.order(NE).asCharBuffer().get(2), (bb, v) -> bb.order(NE).asCharBuffer().put(2, v)) - }, - {"char/index/LE", new Accessor<>((char) 42, - s -> MemoryAccess.getCharAtIndex(s, 2, LE), (s, x) -> MemoryAccess.setCharAtIndex(s, 2, LE, x), - (bb) -> bb.order(LE).asCharBuffer().get(2), (bb, v) -> bb.order(LE).asCharBuffer().put(2, v)) - }, - {"char/index/BE", new Accessor<>((char) 42, - s -> MemoryAccess.getCharAtIndex(s, 2, BE), (s, x) -> MemoryAccess.setCharAtIndex(s, 2, BE, x), - (bb) -> bb.order(BE).asCharBuffer().get(2), (bb, v) -> bb.order(BE).asCharBuffer().put(2, v)) - }, - // short, index - {"short/index", new Accessor<>((short) 42, - s -> MemoryAccess.getShortAtIndex(s, 2), (s, x) -> MemoryAccess.setShortAtIndex(s, 2, x), - (bb) -> bb.order(NE).asShortBuffer().get(2), (bb, v) -> bb.order(NE).asShortBuffer().put(2, v)) - }, - {"short/index/LE", new Accessor<>((short) 42, - s -> MemoryAccess.getShortAtIndex(s, 2, LE), (s, x) -> MemoryAccess.setShortAtIndex(s, 2, LE, x), - (bb) -> bb.order(LE).asShortBuffer().get(2), (bb, v) -> bb.order(LE).asShortBuffer().put(2, v)) - }, - {"short/index/BE", new Accessor<>((short) 42, - s -> MemoryAccess.getShortAtIndex(s, 2, BE), (s, x) -> MemoryAccess.setShortAtIndex(s, 2, BE, x), - (bb) -> bb.order(BE).asShortBuffer().get(2), (bb, v) -> bb.order(BE).asShortBuffer().put(2, v)) - }, - {"int/index", new Accessor<>(42, - s -> MemoryAccess.getIntAtIndex(s, 2), (s, x) -> MemoryAccess.setIntAtIndex(s, 2, x), - (bb) -> bb.order(NE).asIntBuffer().get(2), (bb, v) -> bb.order(NE).asIntBuffer().put(2, v)) - }, - {"int/index/LE", new Accessor<>(42, - s -> MemoryAccess.getIntAtIndex(s, 2, LE), (s, x) -> MemoryAccess.setIntAtIndex(s, 2, LE, x), - (bb) -> bb.order(LE).asIntBuffer().get(2), (bb, v) -> bb.order(LE).asIntBuffer().put(2, v)) - }, - {"int/index/BE", new Accessor<>(42, - s -> MemoryAccess.getIntAtIndex(s, 2, BE), (s, x) -> MemoryAccess.setIntAtIndex(s, 2, BE, x), - (bb) -> bb.order(BE).asIntBuffer().get(2), (bb, v) -> bb.order(BE).asIntBuffer().put(2, v)) - }, - {"float/index", new Accessor<>(42f, - s -> MemoryAccess.getFloatAtIndex(s, 2), (s, x) -> MemoryAccess.setFloatAtIndex(s, 2, x), - (bb) -> bb.order(NE).asFloatBuffer().get(2), (bb, v) -> bb.order(NE).asFloatBuffer().put(2, v)) - }, - {"float/index/LE", new Accessor<>(42f, - s -> MemoryAccess.getFloatAtIndex(s, 2, LE), (s, x) -> MemoryAccess.setFloatAtIndex(s, 2, LE, x), - (bb) -> bb.order(LE).asFloatBuffer().get(2), (bb, v) -> bb.order(LE).asFloatBuffer().put(2, v)) - }, - {"float/index/BE", new Accessor<>(42f, - s -> MemoryAccess.getFloatAtIndex(s, 2, BE), (s, x) -> MemoryAccess.setFloatAtIndex(s, 2, BE, x), - (bb) -> bb.order(BE).asFloatBuffer().get(2), (bb, v) -> bb.order(BE).asFloatBuffer().put(2, v)) - }, - {"double/index", new Accessor<>(42d, - s -> MemoryAccess.getDoubleAtIndex(s, 2), (s, x) -> MemoryAccess.setDoubleAtIndex(s, 2, x), - (bb) -> bb.order(NE).asDoubleBuffer().get(2), (bb, v) -> bb.order(NE).asDoubleBuffer().put(2, v)) - }, - {"double/index/LE", new Accessor<>(42d, - s -> MemoryAccess.getDoubleAtIndex(s, 2, LE), (s, x) -> MemoryAccess.setDoubleAtIndex(s, 2, LE, x), - (bb) -> bb.order(LE).asDoubleBuffer().get(2), (bb, v) -> bb.order(LE).asDoubleBuffer().put(2, v)) - }, - {"double/index/BE", new Accessor<>(42d, - s -> MemoryAccess.getDoubleAtIndex(s, 2, BE), (s, x) -> MemoryAccess.setDoubleAtIndex(s, 2, BE, x), - (bb) -> bb.order(BE).asDoubleBuffer().get(2), (bb, v) -> bb.order(BE).asDoubleBuffer().put(2, v)) - }, - - { "address", new Accessor<>(MemoryAddress.ofLong(42), - MemoryAccess::getAddress, MemoryAccess::setAddress, - (bb) -> { - ByteBuffer nb = bb.order(NE); - long addr = MemoryLayouts.ADDRESS.byteSize() == 8 ? - nb.getLong(0) : nb.getInt(0); - return MemoryAddress.ofLong(addr); - }, - (bb, v) -> { - ByteBuffer nb = bb.order(NE); - if (MemoryLayouts.ADDRESS.byteSize() == 8) { - nb.putLong(0, v.toRawLongValue()); - } else { - nb.putInt(0, (int)v.toRawLongValue()); - } - }) - }, - { "address/offset", new Accessor<>(MemoryAddress.ofLong(42), - s -> MemoryAccess.getAddressAtOffset(s, 4), (s, x) -> MemoryAccess.setAddressAtOffset(s, 4, x), - (bb) -> { - ByteBuffer nb = bb.order(NE); - long addr = MemoryLayouts.ADDRESS.byteSize() == 8 ? - nb.getLong(4) : nb.getInt(4); - return MemoryAddress.ofLong(addr); - }, - (bb, v) -> { - ByteBuffer nb = bb.order(NE); - if (MemoryLayouts.ADDRESS.byteSize() == 8) { - nb.putLong(4, v.toRawLongValue()); - } else { - nb.putInt(4, (int)v.toRawLongValue()); - } - }) - }, - { "address/index", new Accessor<>(MemoryAddress.ofLong(42), - s -> MemoryAccess.getAddressAtIndex(s, 2), (s, x) -> MemoryAccess.setAddressAtIndex(s, 2, x), - (bb) -> { - ByteBuffer nb = bb.order(NE); - long addr = MemoryLayouts.ADDRESS.byteSize() == 8 ? - nb.asLongBuffer().get(2) : nb.asIntBuffer().get(2); - return MemoryAddress.ofLong(addr); - }, - (bb, v) -> { - ByteBuffer nb = bb.order(NE); - if (MemoryLayouts.ADDRESS.byteSize() == 8) { - nb.asLongBuffer().put(2, v.toRawLongValue()); - } else { - nb.asIntBuffer().put(2, (int)v.toRawLongValue()); - } - }) - }, - }; - } -} diff --git a/test/jdk/java/foreign/TestMemoryAlignment.java b/test/jdk/java/foreign/TestMemoryAlignment.java index 93f218245c8d1..d486581435a7e 100644 --- a/test/jdk/java/foreign/TestMemoryAlignment.java +++ b/test/jdk/java/foreign/TestMemoryAlignment.java @@ -26,7 +26,6 @@ * @run testng TestMemoryAlignment */ -import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.GroupLayout; @@ -36,6 +35,7 @@ import jdk.incubator.foreign.SequenceLayout; import jdk.incubator.foreign.ValueLayout; import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; import java.util.stream.LongStream; import org.testng.annotations.*; @@ -45,11 +45,13 @@ public class TestMemoryAlignment { @Test(dataProvider = "alignments") public void testAlignedAccess(long align) { - ValueLayout layout = MemoryLayouts.BITS_32_BE; + ValueLayout layout = ValueLayout.JAVA_INT + .withBitAlignment(32) + .withOrder(ByteOrder.BIG_ENDIAN); assertEquals(layout.bitAlignment(), 32); ValueLayout aligned = layout.withBitAlignment(align); assertEquals(aligned.bitAlignment(), align); //unreasonable alignment here, to make sure access throws - VarHandle vh = aligned.varHandle(int.class); + VarHandle vh = aligned.varHandle(); try (ResourceScope scope = ResourceScope.newConfinedScope()) { MemorySegment segment = MemorySegment.allocateNative(aligned, scope); vh.set(segment, -42); @@ -60,12 +62,14 @@ public void testAlignedAccess(long align) { @Test(dataProvider = "alignments") public void testUnalignedAccess(long align) { - ValueLayout layout = MemoryLayouts.BITS_32_BE; + ValueLayout layout = ValueLayout.JAVA_INT + .withBitAlignment(32) + .withOrder(ByteOrder.BIG_ENDIAN); assertEquals(layout.bitAlignment(), 32); ValueLayout aligned = layout.withBitAlignment(align); - MemoryLayout alignedGroup = MemoryLayout.structLayout(MemoryLayouts.PAD_8, aligned); + MemoryLayout alignedGroup = MemoryLayout.structLayout(MemoryLayout.paddingLayout(8), aligned); assertEquals(alignedGroup.bitAlignment(), align); - VarHandle vh = aligned.varHandle(int.class); + VarHandle vh = aligned.varHandle(); try (ResourceScope scope = ResourceScope.newConfinedScope()) { MemorySegment segment = MemorySegment.allocateNative(alignedGroup, scope); vh.set(segment.asSlice(1L), -42); @@ -77,11 +81,11 @@ public void testUnalignedAccess(long align) { @Test(dataProvider = "alignments") public void testUnalignedPath(long align) { - MemoryLayout layout = MemoryLayouts.BITS_32_BE; + MemoryLayout layout = ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN); MemoryLayout aligned = layout.withBitAlignment(align).withName("value"); - GroupLayout alignedGroup = MemoryLayout.structLayout(MemoryLayouts.PAD_8, aligned); + GroupLayout alignedGroup = MemoryLayout.structLayout(MemoryLayout.paddingLayout(8), aligned); try { - alignedGroup.varHandle(int.class, PathElement.groupElement("value")); + alignedGroup.varHandle(PathElement.groupElement("value")); assertEquals(align, 8); //this is the only case where path is aligned } catch (UnsupportedOperationException ex) { assertNotEquals(align, 8); //if align != 8, path is always unaligned @@ -90,9 +94,9 @@ public void testUnalignedPath(long align) { @Test(dataProvider = "alignments") public void testUnalignedSequence(long align) { - SequenceLayout layout = MemoryLayout.sequenceLayout(5, MemoryLayouts.BITS_32_BE.withBitAlignment(align)); + SequenceLayout layout = MemoryLayout.sequenceLayout(5, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN).withBitAlignment(align)); try { - VarHandle vh = layout.varHandle(int.class, PathElement.sequenceElement()); + VarHandle vh = layout.varHandle(PathElement.sequenceElement()); try (ResourceScope scope = ResourceScope.newConfinedScope()) { MemorySegment segment = MemorySegment.allocateNative(layout, scope); for (long i = 0 ; i < 5 ; i++) { @@ -106,17 +110,17 @@ public void testUnalignedSequence(long align) { @Test public void testPackedAccess() { - ValueLayout vChar = MemoryLayouts.BITS_8_BE; - ValueLayout vShort = MemoryLayouts.BITS_16_BE; - ValueLayout vInt = MemoryLayouts.BITS_32_BE; + ValueLayout vChar = ValueLayout.JAVA_BYTE; + ValueLayout vShort = ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN); + ValueLayout vInt = ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN); //mimic pragma pack(1) GroupLayout g = MemoryLayout.structLayout(vChar.withBitAlignment(8).withName("a"), vShort.withBitAlignment(8).withName("b"), vInt.withBitAlignment(8).withName("c")); assertEquals(g.bitAlignment(), 8); - VarHandle vh_c = g.varHandle(byte.class, PathElement.groupElement("a")); - VarHandle vh_s = g.varHandle(short.class, PathElement.groupElement("b")); - VarHandle vh_i = g.varHandle(int.class, PathElement.groupElement("c")); + VarHandle vh_c = g.varHandle(PathElement.groupElement("a")); + VarHandle vh_s = g.varHandle(PathElement.groupElement("b")); + VarHandle vh_i = g.varHandle(PathElement.groupElement("c")); try (ResourceScope scope = ResourceScope.newConfinedScope()) { MemorySegment segment = MemorySegment.allocateNative(g, scope); vh_c.set(segment, Byte.MIN_VALUE); diff --git a/test/jdk/java/foreign/TestMemoryCopy.java b/test/jdk/java/foreign/TestMemoryCopy.java deleted file mode 100644 index cba9186f124ac..0000000000000 --- a/test/jdk/java/foreign/TestMemoryCopy.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -/* - * @test - * @run testng TestMemoryCopy - */ - -import jdk.incubator.foreign.MemoryLayouts; -import jdk.incubator.foreign.MemorySegment; -import jdk.incubator.foreign.ResourceScope; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import java.lang.invoke.VarHandle; -import java.util.ArrayList; -import java.util.List; -import java.util.function.IntFunction; - -import static org.testng.Assert.*; - -public class TestMemoryCopy { - - final static VarHandle BYTE_HANDLE = MemoryLayouts.JAVA_BYTE.varHandle(byte.class); - - @Test(dataProvider = "slices") - public void testCopy(SegmentSlice s1, SegmentSlice s2) { - int size = Math.min(s1.size(), s2.size()); - //prepare source and target segments - for (int i = 0 ; i < size ; i++) { - BYTE_HANDLE.set(s2.segment.asSlice(i), (byte)0); - } - for (int i = 0 ; i < size ; i++) { - BYTE_HANDLE.set(s1.segment.asSlice(i), (byte) i); - } - //perform copy - s2.segment.copyFrom(s1.segment.asSlice(0, size)); - //check that copy actually worked - for (int i = 0 ; i < size ; i++) { - assertEquals((byte)i, BYTE_HANDLE.get(s2.segment.asSlice(i))); - } - } - - static class SegmentSlice { - - enum Kind { - NATIVE(i -> MemorySegment.allocateNative(i, ResourceScope.newImplicitScope())), - ARRAY(i -> MemorySegment.ofArray(new byte[i])); - - final IntFunction segmentFactory; - - Kind(IntFunction segmentFactory) { - this.segmentFactory = segmentFactory; - } - - MemorySegment makeSegment(int elems) { - return segmentFactory.apply(elems); - } - } - - final Kind kind; - final int first; - final int last; - final MemorySegment segment; - - public SegmentSlice(Kind kind, int first, int last, MemorySegment segment) { - this.kind = kind; - this.first = first; - this.last = last; - this.segment = segment; - } - - int size() { - return last - first + 1; - } - } - - @DataProvider(name = "slices") - static Object[][] slices() { - int[] sizes = { 16, 8, 4, 2, 1 }; - List slices = new ArrayList<>(); - for (SegmentSlice.Kind kind : SegmentSlice.Kind.values()) { - MemorySegment segment = kind.makeSegment(16); - //compute all slices - for (int size : sizes) { - for (int index = 0 ; index < 16 ; index += size) { - MemorySegment slice = segment.asSlice(index, size); - slices.add(new SegmentSlice(kind, index, index + size - 1, slice)); - } - } - } - Object[][] sliceArray = new Object[slices.size() * slices.size()][]; - for (int i = 0 ; i < slices.size() ; i++) { - for (int j = 0 ; j < slices.size() ; j++) { - sliceArray[i * slices.size() + j] = new Object[] { slices.get(i), slices.get(j) }; - } - } - return sliceArray; - } -} diff --git a/test/jdk/java/foreign/TestMemoryDereference.java b/test/jdk/java/foreign/TestMemoryDereference.java new file mode 100644 index 0000000000000..c62380cc355dc --- /dev/null +++ b/test/jdk/java/foreign/TestMemoryDereference.java @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @run testng TestMemoryDereference + */ + +import jdk.incubator.foreign.MemoryAddress; +import jdk.incubator.foreign.MemorySegment; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import jdk.incubator.foreign.ValueLayout; +import org.testng.annotations.*; + +import static jdk.incubator.foreign.ValueLayout.ADDRESS; +import static jdk.incubator.foreign.ValueLayout.JAVA_BOOLEAN; +import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE; +import static jdk.incubator.foreign.ValueLayout.JAVA_CHAR; +import static jdk.incubator.foreign.ValueLayout.JAVA_DOUBLE; +import static jdk.incubator.foreign.ValueLayout.JAVA_FLOAT; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_SHORT; +import static org.testng.Assert.*; + +public class TestMemoryDereference { + + static class Accessor { + + interface SegmentGetter { + X get(MemorySegment segment); + } + + interface SegmentSetter { + void set(MemorySegment segment, X o); + } + + interface BufferGetter { + X get(ByteBuffer segment); + } + + interface BufferSetter { + void set(ByteBuffer buffer, X o); + } + + final X value; + final SegmentGetter segmentGetter; + final SegmentSetter segmentSetter; + final BufferGetter bufferGetter; + final BufferSetter bufferSetter; + + Accessor(X value, + SegmentGetter segmentGetter, SegmentSetter segmentSetter, + BufferGetter bufferGetter, BufferSetter bufferSetter) { + this.value = value; + this.segmentGetter = segmentGetter; + this.segmentSetter = segmentSetter; + this.bufferGetter = bufferGetter; + this.bufferSetter = bufferSetter; + } + + void test() { + MemorySegment segment = MemorySegment.ofArray(new byte[32]); + ByteBuffer buffer = segment.asByteBuffer(); + segmentSetter.set(segment, value); + assertEquals(bufferGetter.get(buffer), value); + bufferSetter.set(buffer, value); + assertEquals(value, segmentGetter.get(segment)); + } + + Accessor of(Z value, + SegmentGetter segmentGetter, SegmentSetter segmentSetter, + BufferGetter bufferGetter, BufferSetter bufferSetter) { + return new Accessor<>(value, segmentGetter, segmentSetter, bufferGetter, bufferSetter); + } + } + + @Test(dataProvider = "accessors") + public void testMemoryAccess(String testName, Accessor accessor) { + accessor.test(); + } + + static final ByteOrder BE = ByteOrder.BIG_ENDIAN; + static final ByteOrder LE = ByteOrder.LITTLE_ENDIAN; + static final ByteOrder NE = ByteOrder.nativeOrder(); + + @DataProvider(name = "accessors") + static Object[][] accessors() { + return new Object[][]{ + + // byte, offset + {"byte/offset", new Accessor<>((byte) 42, + s -> s.get(JAVA_BYTE, 8), (s, x) -> s.set(JAVA_BYTE, 8, x), + (bb) -> bb.get(8), (bb, v) -> bb.put(8, v)) + }, + // bool, offset + {"bool", new Accessor<>(false, + s -> s.get(JAVA_BOOLEAN, 8), (s, x) -> s.set(JAVA_BOOLEAN, 8, x), + (bb) -> bb.get(8) != 0, (bb, v) -> bb.put(8, v ? (byte)1 : (byte)0)) + }, + // char, offset + {"char/offset", new Accessor<>((char) 42, + s -> s.get(JAVA_CHAR, 8), (s, x) -> s.set(JAVA_CHAR, 8, x), + (bb) -> bb.order(NE).getChar(8), (bb, v) -> bb.order(NE).putChar(8, v)) + }, + {"char/offset/LE", new Accessor<>((char) 42, + s -> s.get(JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), 8), + (s, x) -> s.set(JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), 8, x), + (bb) -> bb.order(LE).getChar(8), (bb, v) -> bb.order(LE).putChar(8, v)) + }, + {"char/offset/BE", new Accessor<>((char) 42, + s -> s.get(JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), 8), + (s, x) -> s.set(JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), 8, x), + (bb) -> bb.order(BE).getChar(8), (bb, v) -> bb.order(BE).putChar(8, v)) + }, + // short, offset + {"short/offset", new Accessor<>((short) 42, + s -> s.get(JAVA_SHORT, 8), (s, x) -> s.set(JAVA_SHORT, 8, x), + (bb) -> bb.order(NE).getShort(8), (bb, v) -> bb.order(NE).putShort(8, v)) + }, + {"short/offset/LE", new Accessor<>((short) 42, + s -> s.get(JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), 8), + (s, x) -> s.set(JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), 8, x), + (bb) -> bb.order(LE).getShort(8), (bb, v) -> bb.order(LE).putShort(8, v)) + }, + {"short/offset/BE", new Accessor<>((short) 42, + s -> s.get(JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), 8), + (s, x) -> s.set(JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), 8, x), + (bb) -> bb.order(BE).getShort(8), (bb, v) -> bb.order(BE).putShort(8, v)) + }, + // int, offset + {"int/offset", new Accessor<>(42, + s -> s.get(JAVA_INT, 8), (s, x) -> s.set(JAVA_INT, 8, x), + (bb) -> bb.order(NE).getInt(8), (bb, v) -> bb.order(NE).putInt(8, v)) + }, + {"int/offset/LE", new Accessor<>(42, + s -> s.get(JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), 8), + (s, x) -> s.set(JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), 8, x), + (bb) -> bb.order(LE).getInt(8), (bb, v) -> bb.order(LE).putInt(8, v)) + }, + {"int/offset/BE", new Accessor<>(42, + s -> s.get(JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), 8), + (s, x) -> s.set(JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), 8, x), + (bb) -> bb.order(BE).getInt(8), (bb, v) -> bb.order(BE).putInt(8, v)) + }, + // float, offset + {"float/offset", new Accessor<>(42f, + s -> s.get(JAVA_FLOAT, 8), (s, x) -> s.set(JAVA_FLOAT, 8, x), + (bb) -> bb.order(NE).getFloat(8), (bb, v) -> bb.order(NE).putFloat(8, v)) + }, + {"float/offset/LE", new Accessor<>(42f, + s -> s.get(ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN), 8), + (s, x) -> s.set(ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN), 8, x), + (bb) -> bb.order(LE).getFloat(8), (bb, v) -> bb.order(LE).putFloat(8, v)) + }, + {"float/offset/BE", new Accessor<>(42f, + s -> s.get(ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), 8), + (s, x) -> s.set(ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), 8, x), + (bb) -> bb.order(BE).getFloat(8), (bb, v) -> bb.order(BE).putFloat(8, v)) + }, + // double, offset + {"double/offset", new Accessor<>(42d, + s -> s.get(JAVA_DOUBLE, 8), (s, x) -> s.set(JAVA_DOUBLE, 8, x), + (bb) -> bb.order(NE).getDouble(8), (bb, v) -> bb.order(NE).putDouble(8, v)) + }, + {"double/offset/LE", new Accessor<>(42d, + s -> s.get(ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN), 8), + (s, x) -> s.set(ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN), 8, x), + (bb) -> bb.order(LE).getDouble(8), (bb, v) -> bb.order(LE).putDouble(8, v)) + }, + {"double/offset/BE", new Accessor<>(42d, + s -> s.get(ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), 8), + (s, x) -> s.set(ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), 8, x), + (bb) -> bb.order(BE).getDouble(8), (bb, v) -> bb.order(BE).putDouble(8, v)) + }, + { "address/offset", new Accessor<>(MemoryAddress.ofLong(42), + s -> s.get(ADDRESS, 8), (s, x) -> s.set(ADDRESS, 8, x), + (bb) -> { + ByteBuffer nb = bb.order(NE); + long addr = ValueLayout.ADDRESS.byteSize() == 8 ? + nb.getLong(8) : nb.getInt(8); + return MemoryAddress.ofLong(addr); + }, + (bb, v) -> { + ByteBuffer nb = bb.order(NE); + if (ValueLayout.ADDRESS.byteSize() == 8) { + nb.putLong(8, v.toRawLongValue()); + } else { + nb.putInt(8, (int)v.toRawLongValue()); + } + }) + }, + }; + } +} diff --git a/test/jdk/java/foreign/TestMemoryHandleAsUnsigned.java b/test/jdk/java/foreign/TestMemoryHandleAsUnsigned.java index d4a6e536234a8..71ef6fe2999ec 100644 --- a/test/jdk/java/foreign/TestMemoryHandleAsUnsigned.java +++ b/test/jdk/java/foreign/TestMemoryHandleAsUnsigned.java @@ -25,14 +25,15 @@ import jdk.incubator.foreign.MemoryHandles; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout.PathElement; -import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; import java.util.Arrays; import java.util.stream.IntStream; import java.util.stream.LongStream; import jdk.incubator.foreign.ResourceScope; +import jdk.incubator.foreign.ValueLayout; import org.testng.annotations.*; import static java.nio.ByteOrder.BIG_ENDIAN; import static org.testng.Assert.*; @@ -54,8 +55,8 @@ public Object[][] unsignedIntToByteData() { public void testUnsignedIntToByte(int intValue) { byte byteValue = (byte) (intValue & 0xFF); - MemoryLayout layout = MemoryLayouts.BITS_8_BE; - VarHandle byteHandle = layout.varHandle(byte.class); + MemoryLayout layout = ValueLayout.JAVA_BYTE; + VarHandle byteHandle = layout.varHandle(); VarHandle intHandle = MemoryHandles.asUnsigned(byteHandle, int.class); try (ResourceScope scope = ResourceScope.newConfinedScope()) { @@ -77,8 +78,8 @@ public Object[][] unsignedLongToByteData() { public void testUnsignedLongToByte(long longValue) { byte byteValue = (byte) (longValue & 0xFFL); - MemoryLayout layout = MemoryLayouts.BITS_8_BE; - VarHandle byteHandle = layout.varHandle(byte.class); + MemoryLayout layout = ValueLayout.JAVA_BYTE; + VarHandle byteHandle = layout.varHandle(); VarHandle longHandle = MemoryHandles.asUnsigned(byteHandle, long.class); try (ResourceScope scope = ResourceScope.newConfinedScope()) { @@ -100,8 +101,8 @@ public Object[][] unsignedIntToShortData() { public void testUnsignedIntToShort(int intValue) { short shortValue = (short) (intValue & 0xFFFF); - MemoryLayout layout = MemoryLayouts.BITS_16_BE; - VarHandle shortHandle = layout.varHandle(short.class); + MemoryLayout layout = ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN); + VarHandle shortHandle = layout.varHandle(); VarHandle intHandle = MemoryHandles.asUnsigned(shortHandle, int.class); try (ResourceScope scope = ResourceScope.newConfinedScope()) { @@ -123,8 +124,8 @@ public Object[][] unsignedLongToShortData() { public void testUnsignedLongToShort(long longValue) { short shortValue = (short) (longValue & 0xFFFFL); - MemoryLayout layout = MemoryLayouts.BITS_16_BE; - VarHandle shortHandle = layout.varHandle(short.class); + MemoryLayout layout = ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN); + VarHandle shortHandle = layout.varHandle(); VarHandle longHandle = MemoryHandles.asUnsigned(shortHandle, long.class); try (ResourceScope scope = ResourceScope.newConfinedScope()) { @@ -150,8 +151,8 @@ public Object[][] unsignedLongToIntData() { public void testUnsignedLongToInt(long longValue) { int intValue = (int) (longValue & 0xFFFF_FFFFL); - MemoryLayout layout = MemoryLayouts.BITS_32_BE; - VarHandle intHandle = layout.varHandle(int.class); + MemoryLayout layout = ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN); + VarHandle intHandle = layout.varHandle(); VarHandle longHandle = MemoryHandles.asUnsigned(intHandle, long.class); try (ResourceScope scope = ResourceScope.newConfinedScope()) { @@ -165,8 +166,8 @@ public void testUnsignedLongToInt(long longValue) { @Test public void testCoordinatesSequenceLayout() { - MemoryLayout layout = MemoryLayout.sequenceLayout(2, MemoryLayouts.BITS_8_BE); - VarHandle byteHandle = layout.varHandle(byte.class, PathElement.sequenceElement()); + MemoryLayout layout = MemoryLayout.sequenceLayout(2, ValueLayout.JAVA_BYTE); + VarHandle byteHandle = layout.varHandle(PathElement.sequenceElement()); VarHandle intHandle = MemoryHandles.asUnsigned(byteHandle, int.class); try (ResourceScope scope = ResourceScope.newConfinedScope()) { @@ -184,14 +185,14 @@ public void testCoordinatesStride() { MemorySegment segment = MemorySegment.ofArray(arr); { - VarHandle byteHandle = MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_BYTE) - .varHandle(byte.class, PathElement.sequenceElement()); + VarHandle byteHandle = MemoryLayout.sequenceLayout(ValueLayout.JAVA_BYTE) + .varHandle(PathElement.sequenceElement()); VarHandle intHandle = MemoryHandles.asUnsigned(byteHandle, int.class); assertEquals((int) intHandle.get(segment, 2L), 129); } { - VarHandle byteHandle = MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_BYTE) - .varHandle(byte.class, PathElement.sequenceElement()); + VarHandle byteHandle = MemoryLayout.sequenceLayout(ValueLayout.JAVA_BYTE) + .varHandle(PathElement.sequenceElement()); VarHandle intHandle = MemoryHandles.asUnsigned(byteHandle, int.class); assertEquals((int) intHandle.get(segment, 2L), 129); } @@ -201,7 +202,7 @@ public void testCoordinatesStride() { @Test public void testNull() { - VarHandle handle = MemoryHandles.varHandle(byte.class, BIG_ENDIAN); + VarHandle handle = MemoryHandles.varHandle(ValueLayout.JAVA_BYTE.withOrder(BIG_ENDIAN)); assertThrows(NPE, () -> MemoryHandles.asUnsigned(handle, null)); assertThrows(NPE, () -> MemoryHandles.asUnsigned(null, short.class)); assertThrows(NPE, () -> MemoryHandles.asUnsigned(null, null)); @@ -209,22 +210,22 @@ public void testNull() { static final Class IAE = IllegalArgumentException.class; - static void assertIllegalArgumentExceptionIllegalCarrier(Class carrier, Class adaptedType) { - var vh = MemoryHandles.varHandle(carrier, BIG_ENDIAN); + static void assertIllegalArgumentExceptionIllegalCarrier(ValueLayout layout, Class adaptedType) { + var vh = MemoryHandles.varHandle(layout.withOrder(BIG_ENDIAN)); var exception = expectThrows(IAE, () -> MemoryHandles.asUnsigned(vh, adaptedType)); var msg = exception.getMessage(); assertTrue(msg.contains("illegal carrier"), "Expected \"illegal carrier\" in:[" + msg +"]"); } - static void assertIllegalArgumentExceptionIllegalAdapter(Class carrier, Class adaptedType) { - var vh = MemoryHandles.varHandle(carrier, BIG_ENDIAN); + static void assertIllegalArgumentExceptionIllegalAdapter(ValueLayout layout, Class adaptedType) { + var vh = MemoryHandles.varHandle(layout.withOrder(BIG_ENDIAN)); var exception = expectThrows(IAE, () -> MemoryHandles.asUnsigned(vh, adaptedType)); var msg = exception.getMessage(); assertTrue(msg.contains("illegal adapter type"), "Expected \"illegal adapter type\" in:[" + msg +"]"); } - static void assertIllegalArgumentExceptionIsNotWiderThan(Class carrier, Class adaptedType) { - var vh = MemoryHandles.varHandle(carrier, BIG_ENDIAN); + static void assertIllegalArgumentExceptionIsNotWiderThan(ValueLayout layout, Class adaptedType) { + var vh = MemoryHandles.varHandle(layout.withOrder(BIG_ENDIAN)); var exception = expectThrows(IAE, () -> MemoryHandles.asUnsigned(vh, adaptedType)); var msg = exception.getMessage(); assertTrue(msg.contains("is not wider than"), "Expected \"is not wider than\" in:[" + msg +"]"); @@ -232,25 +233,25 @@ static void assertIllegalArgumentExceptionIsNotWiderThan(Class carrier, Class @Test public void testIllegalArgumentException() { - assertIllegalArgumentExceptionIllegalCarrier(char.class, long.class); - assertIllegalArgumentExceptionIllegalCarrier(double.class, long.class); - assertIllegalArgumentExceptionIllegalCarrier(float.class, long.class); - assertIllegalArgumentExceptionIllegalCarrier(long.class, long.class); - - assertIllegalArgumentExceptionIllegalAdapter(byte.class, void.class); - assertIllegalArgumentExceptionIllegalAdapter(byte.class, byte.class); - assertIllegalArgumentExceptionIllegalAdapter(byte.class, short.class); - assertIllegalArgumentExceptionIllegalAdapter(byte.class, char.class); - assertIllegalArgumentExceptionIllegalAdapter(byte.class, double.class); - assertIllegalArgumentExceptionIllegalAdapter(byte.class, float.class); - assertIllegalArgumentExceptionIllegalAdapter(byte.class, Object.class); - assertIllegalArgumentExceptionIllegalAdapter(byte.class, Integer.class); - assertIllegalArgumentExceptionIllegalAdapter(byte.class, Long.class); - assertIllegalArgumentExceptionIllegalAdapter(byte.class, long[].class); - assertIllegalArgumentExceptionIllegalAdapter(byte.class, int[].class); - assertIllegalArgumentExceptionIllegalAdapter(byte.class, Integer[].class); - assertIllegalArgumentExceptionIllegalAdapter(byte.class, Long[].class); - - assertIllegalArgumentExceptionIsNotWiderThan(int.class, int.class); + assertIllegalArgumentExceptionIllegalCarrier(ValueLayout.JAVA_CHAR, long.class); + assertIllegalArgumentExceptionIllegalCarrier(ValueLayout.JAVA_DOUBLE, long.class); + assertIllegalArgumentExceptionIllegalCarrier(ValueLayout.JAVA_FLOAT, long.class); + assertIllegalArgumentExceptionIllegalCarrier(ValueLayout.JAVA_LONG, long.class); + + assertIllegalArgumentExceptionIllegalAdapter(ValueLayout.JAVA_BYTE, void.class); + assertIllegalArgumentExceptionIllegalAdapter(ValueLayout.JAVA_BYTE, byte.class); + assertIllegalArgumentExceptionIllegalAdapter(ValueLayout.JAVA_BYTE, short.class); + assertIllegalArgumentExceptionIllegalAdapter(ValueLayout.JAVA_BYTE, char.class); + assertIllegalArgumentExceptionIllegalAdapter(ValueLayout.JAVA_BYTE, double.class); + assertIllegalArgumentExceptionIllegalAdapter(ValueLayout.JAVA_BYTE, float.class); + assertIllegalArgumentExceptionIllegalAdapter(ValueLayout.JAVA_BYTE, Object.class); + assertIllegalArgumentExceptionIllegalAdapter(ValueLayout.JAVA_BYTE, Integer.class); + assertIllegalArgumentExceptionIllegalAdapter(ValueLayout.JAVA_BYTE, Long.class); + assertIllegalArgumentExceptionIllegalAdapter(ValueLayout.JAVA_BYTE, long[].class); + assertIllegalArgumentExceptionIllegalAdapter(ValueLayout.JAVA_BYTE, int[].class); + assertIllegalArgumentExceptionIllegalAdapter(ValueLayout.JAVA_BYTE, Integer[].class); + assertIllegalArgumentExceptionIllegalAdapter(ValueLayout.JAVA_BYTE, Long[].class); + + assertIllegalArgumentExceptionIsNotWiderThan(ValueLayout.JAVA_INT, int.class); } } diff --git a/test/jdk/java/foreign/TestMismatch.java b/test/jdk/java/foreign/TestMismatch.java index 5f4dd4c3195c3..a399a20d9b82b 100644 --- a/test/jdk/java/foreign/TestMismatch.java +++ b/test/jdk/java/foreign/TestMismatch.java @@ -32,9 +32,9 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.IntFunction; -import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; +import jdk.incubator.foreign.ValueLayout; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import static java.lang.System.out; @@ -43,7 +43,7 @@ public class TestMismatch { - final static VarHandle BYTE_HANDLE = MemoryLayouts.JAVA_BYTE.varHandle(byte.class); + final static VarHandle BYTE_HANDLE = ValueLayout.JAVA_BYTE.varHandle(); // stores a increasing sequence of values into the memory of the given segment static MemorySegment initializeSegment(MemorySegment segment) { @@ -112,7 +112,7 @@ public void testEmpty() { @Test public void testLarge() { // skip if not on 64 bits - if (MemoryLayouts.ADDRESS.byteSize() > 32) { + if (ValueLayout.ADDRESS.byteSize() > 32) { try (ResourceScope scope = ResourceScope.newConfinedScope()) { var s1 = MemorySegment.allocateNative((long) Integer.MAX_VALUE + 10L, 8, scope); var s2 = MemorySegment.allocateNative((long) Integer.MAX_VALUE + 10L, 8, scope); diff --git a/test/jdk/java/foreign/TestNULLAddress.java b/test/jdk/java/foreign/TestNULLAddress.java index 7ea4cbb974faa..14aec5fb75766 100644 --- a/test/jdk/java/foreign/TestNULLAddress.java +++ b/test/jdk/java/foreign/TestNULLAddress.java @@ -34,39 +34,37 @@ import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.FunctionDescriptor; import jdk.incubator.foreign.MemoryAddress; +import jdk.incubator.foreign.NativeSymbol; +import jdk.incubator.foreign.ResourceScope; import org.testng.annotations.Test; import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; -import java.nio.charset.Charset; public class TestNULLAddress { - static final CLinker LINKER = CLinker.getInstance(); + static final CLinker LINKER = CLinker.systemCLinker(); @Test(expectedExceptions = IllegalArgumentException.class) public void testNULLLinking() { LINKER.downcallHandle( - MemoryAddress.NULL, - MethodType.methodType(void.class), + NativeSymbol.ofAddress("nullAddress", MemoryAddress.NULL, ResourceScope.globalScope()), FunctionDescriptor.ofVoid()); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNULLVirtual() throws Throwable { MethodHandle mh = LINKER.downcallHandle( - MethodType.methodType(void.class), FunctionDescriptor.ofVoid()); - mh.invokeExact((Addressable) MemoryAddress.NULL); + mh.invokeExact(NativeSymbol.ofAddress("null", MemoryAddress.NULL, ResourceScope.globalScope())); } @Test(expectedExceptions = IllegalArgumentException.class) - public void testNULLtoJavaString() { - CLinker.toJavaString(MemoryAddress.NULL); + public void testNULLgetString() { + MemoryAddress.NULL.getUtf8String(0); } @Test(expectedExceptions = IllegalArgumentException.class) - public void testNULLfreeMemory() { - CLinker.freeMemory(MemoryAddress.NULL); + public void testNULLsetString() { + MemoryAddress.NULL.setUtf8String(0, "hello"); } } diff --git a/test/jdk/java/foreign/TestNative.java b/test/jdk/java/foreign/TestNative.java index 443c13b11d506..0b6a6cdfb8d37 100644 --- a/test/jdk/java/foreign/TestNative.java +++ b/test/jdk/java/foreign/TestNative.java @@ -29,15 +29,13 @@ * @run testng/othervm --enable-native-access=ALL-UNNAMED TestNative */ -import jdk.incubator.foreign.CLinker; -import jdk.incubator.foreign.MemoryAccess; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout.PathElement; -import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.SequenceLayout; +import jdk.incubator.foreign.ValueLayout; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -56,45 +54,46 @@ import java.util.function.Consumer; import java.util.function.Function; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; import static org.testng.Assert.*; -public class TestNative { +public class TestNative extends NativeTestHelper { static SequenceLayout bytes = MemoryLayout.sequenceLayout(100, - MemoryLayouts.JAVA_BYTE.withOrder(ByteOrder.nativeOrder()) + ValueLayout.JAVA_BYTE.withOrder(ByteOrder.nativeOrder()) ); static SequenceLayout chars = MemoryLayout.sequenceLayout(100, - MemoryLayouts.JAVA_CHAR.withOrder(ByteOrder.nativeOrder()) + ValueLayout.JAVA_CHAR.withOrder(ByteOrder.nativeOrder()) ); static SequenceLayout shorts = MemoryLayout.sequenceLayout(100, - MemoryLayouts.JAVA_SHORT.withOrder(ByteOrder.nativeOrder()) + ValueLayout.JAVA_SHORT.withOrder(ByteOrder.nativeOrder()) ); static SequenceLayout ints = MemoryLayout.sequenceLayout(100, - MemoryLayouts.JAVA_INT.withOrder(ByteOrder.nativeOrder()) + JAVA_INT.withOrder(ByteOrder.nativeOrder()) ); static SequenceLayout floats = MemoryLayout.sequenceLayout(100, - MemoryLayouts.JAVA_FLOAT.withOrder(ByteOrder.nativeOrder()) + ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.nativeOrder()) ); static SequenceLayout longs = MemoryLayout.sequenceLayout(100, - MemoryLayouts.JAVA_LONG.withOrder(ByteOrder.nativeOrder()) + ValueLayout.JAVA_LONG.withOrder(ByteOrder.nativeOrder()) ); static SequenceLayout doubles = MemoryLayout.sequenceLayout(100, - MemoryLayouts.JAVA_DOUBLE.withOrder(ByteOrder.nativeOrder()) + ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.nativeOrder()) ); - static VarHandle byteHandle = bytes.varHandle(byte.class, PathElement.sequenceElement()); - static VarHandle charHandle = chars.varHandle(char.class, PathElement.sequenceElement()); - static VarHandle shortHandle = shorts.varHandle(short.class, PathElement.sequenceElement()); - static VarHandle intHandle = ints.varHandle(int.class, PathElement.sequenceElement()); - static VarHandle floatHandle = floats.varHandle(float.class, PathElement.sequenceElement()); - static VarHandle longHandle = doubles.varHandle(long.class, PathElement.sequenceElement()); - static VarHandle doubleHandle = longs.varHandle(double.class, PathElement.sequenceElement()); + static VarHandle byteHandle = bytes.varHandle(PathElement.sequenceElement()); + static VarHandle charHandle = chars.varHandle(PathElement.sequenceElement()); + static VarHandle shortHandle = shorts.varHandle(PathElement.sequenceElement()); + static VarHandle intHandle = ints.varHandle(PathElement.sequenceElement()); + static VarHandle floatHandle = floats.varHandle(PathElement.sequenceElement()); + static VarHandle longHandle = longs.varHandle(PathElement.sequenceElement()); + static VarHandle doubleHandle = doubles.varHandle(PathElement.sequenceElement()); static void initBytes(MemorySegment base, SequenceLayout seq, BiConsumer handleSetter) { for (long i = 0; i < seq.elementCount().getAsLong() ; i++) { @@ -144,14 +143,6 @@ static void checkBytes(MemorySegment base, SequenceLayout lay public static native long getCapacity(Buffer buffer); - public static MemoryAddress allocate(int size) { - return CLinker.allocateMemory(size); - } - - public static void free(MemoryAddress addr) { - CLinker.freeMemory(addr); - } - @Test(dataProvider="nativeAccessOps") public void testNativeAccess(Consumer checker, Consumer initializer, SequenceLayout seq) { try (ResourceScope scope = ResourceScope.newConfinedScope()) { @@ -176,25 +167,21 @@ public void testNativeCapacity(Function bufferFunction, int @Test public void testDefaultAccessModes() { - MemoryAddress addr = allocate(12); + MemoryAddress addr = allocateMemory(12); try (ResourceScope scope = ResourceScope.newConfinedScope()) { - MemorySegment mallocSegment = addr.asSegment(12, () -> free(addr), scope); + scope.addCloseAction(() -> freeMemory(addr)); + MemorySegment mallocSegment = MemorySegment.ofAddress(addr, 12, scope); assertFalse(mallocSegment.isReadOnly()); } } - @Test - public void testDefaultAccessModesEverthing() { - MemorySegment everything = MemorySegment.globalNativeSegment(); - assertFalse(everything.isReadOnly()); - } - @Test public void testMallocSegment() { - MemoryAddress addr = allocate(12); + MemoryAddress addr = allocateMemory(12); MemorySegment mallocSegment = null; try (ResourceScope scope = ResourceScope.newConfinedScope()) { - mallocSegment = addr.asSegment(12, () -> free(addr), scope); + scope.addCloseAction(() -> freeMemory(addr)); + mallocSegment = MemorySegment.ofAddress(addr, 12, scope); assertEquals(mallocSegment.byteSize(), 12); //free here } @@ -202,19 +189,18 @@ public void testMallocSegment() { } @Test - public void testEverythingSegment() { - MemoryAddress addr = allocate(4); - MemorySegment everything = MemorySegment.globalNativeSegment(); - MemoryAccess.setIntAtOffset(everything, addr.toRawLongValue(), 42); - assertEquals(MemoryAccess.getIntAtOffset(everything, addr.toRawLongValue()), 42); - free(addr); + public void testAddressAccess() { + MemoryAddress addr = allocateMemory(4); + addr.set(JAVA_INT, 0, 42); + assertEquals(addr.get(JAVA_INT, 0), 42); + freeMemory(addr); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadResize() { try (ResourceScope scope = ResourceScope.newConfinedScope()) { MemorySegment segment = MemorySegment.allocateNative(4, 1, scope); - segment.address().asSegment(0, ResourceScope.globalScope()); + MemorySegment.ofAddress(segment.address(), 0, ResourceScope.globalScope()); } } diff --git a/test/jdk/java/foreign/TestNoForeignUnsafeOverride.java b/test/jdk/java/foreign/TestNoForeignUnsafeOverride.java index ac47ea3113f05..d5aa76dc5413a 100644 --- a/test/jdk/java/foreign/TestNoForeignUnsafeOverride.java +++ b/test/jdk/java/foreign/TestNoForeignUnsafeOverride.java @@ -28,7 +28,7 @@ public class PanamaMain { public static void main(String[] args) { System.out.println("Trying to get CLinker"); - CLinker.getInstance(); + CLinker.systemCLinker(); System.out.println("Got CLinker"); } } diff --git a/test/jdk/java/foreign/TestNulls.java b/test/jdk/java/foreign/TestNulls.java index ccb27a61d35d5..7f57108b5d2a6 100644 --- a/test/jdk/java/foreign/TestNulls.java +++ b/test/jdk/java/foreign/TestNulls.java @@ -60,6 +60,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_LONG; import static org.testng.Assert.*; import static org.testng.Assert.fail; @@ -82,27 +84,43 @@ public class TestNulls { MemoryLayout.PathElement.class, SequenceLayout.class, ValueLayout.class, + ValueLayout.OfBoolean.class, + ValueLayout.OfByte.class, + ValueLayout.OfChar.class, + ValueLayout.OfShort.class, + ValueLayout.OfInt.class, + ValueLayout.OfFloat.class, + ValueLayout.OfLong.class, + ValueLayout.OfDouble.class, + ValueLayout.OfAddress.class, GroupLayout.class, Addressable.class, SymbolLookup.class, - MemoryAccess.class, - MemoryLayouts.class, MemoryHandles.class, CLinker.class, - CLinker.VaList.class, - CLinker.VaList.Builder.class, + VaList.class, + VaList.Builder.class, FunctionDescriptor.class, SegmentAllocator.class, - ResourceScope.class + ResourceScope.class, + NativeSymbol.class }; static final Set EXCLUDE_LIST = Set.of( + "jdk.incubator.foreign.ResourceScope/newConfinedScope(java.lang.ref.Cleaner)/0/0", + "jdk.incubator.foreign.ResourceScope/newSharedScope(java.lang.ref.Cleaner)/0/0", "jdk.incubator.foreign.MemoryLayout/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", - "jdk.incubator.foreign.MemoryAddress/asSegment(long,java.lang.Runnable,java.lang.Object)/1/0", - "jdk.incubator.foreign.MemoryAddress/asSegment(long,java.lang.Runnable,java.lang.Object)/2/0", - "jdk.incubator.foreign.MemoryAddress/asSegment(long,java.lang.Runnable,jdk.incubator.foreign.ResourceScope)/1/0", "jdk.incubator.foreign.SequenceLayout/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", "jdk.incubator.foreign.ValueLayout/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", + "jdk.incubator.foreign.ValueLayout$OfAddress/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", + "jdk.incubator.foreign.ValueLayout$OfBoolean/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", + "jdk.incubator.foreign.ValueLayout$OfByte/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", + "jdk.incubator.foreign.ValueLayout$OfChar/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", + "jdk.incubator.foreign.ValueLayout$OfShort/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", + "jdk.incubator.foreign.ValueLayout$OfInt/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", + "jdk.incubator.foreign.ValueLayout$OfFloat/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", + "jdk.incubator.foreign.ValueLayout$OfLong/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", + "jdk.incubator.foreign.ValueLayout$OfDouble/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", "jdk.incubator.foreign.GroupLayout/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", "jdk.incubator.foreign.MemoryHandles/insertCoordinates(java.lang.invoke.VarHandle,int,java.lang.Object[])/2/1", "jdk.incubator.foreign.FunctionDescriptor/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0" @@ -139,7 +157,7 @@ static void addDefaultMapping(Class carrier, Z value) { addDefaultMapping(Class.class, String.class); addDefaultMapping(Runnable.class, () -> {}); addDefaultMapping(Object.class, new Object()); - addDefaultMapping(VarHandle.class, MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder())); + addDefaultMapping(VarHandle.class, MemoryHandles.varHandle(JAVA_INT)); addDefaultMapping(MethodHandle.class, MethodHandles.identity(int.class)); addDefaultMapping(List.class, List.of()); addDefaultMapping(Charset.class, Charset.defaultCharset()); @@ -147,32 +165,41 @@ static void addDefaultMapping(Class carrier, Z value) { addDefaultMapping(MethodType.class, MethodType.methodType(void.class)); addDefaultMapping(MemoryAddress.class, MemoryAddress.ofLong(1)); addDefaultMapping(Addressable.class, MemoryAddress.ofLong(1)); - addDefaultMapping(MemoryLayout.class, MemoryLayouts.JAVA_INT); - addDefaultMapping(ValueLayout.class, MemoryLayouts.JAVA_INT); - addDefaultMapping(GroupLayout.class, MemoryLayout.structLayout(MemoryLayouts.JAVA_INT)); - addDefaultMapping(SequenceLayout.class, MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT)); + addDefaultMapping(MemoryLayout.class, ValueLayout.JAVA_INT); + addDefaultMapping(ValueLayout.class, ValueLayout.JAVA_INT); + addDefaultMapping(ValueLayout.OfAddress.class, ValueLayout.ADDRESS); + addDefaultMapping(ValueLayout.OfByte.class, ValueLayout.JAVA_BYTE); + addDefaultMapping(ValueLayout.OfBoolean.class, ValueLayout.JAVA_BOOLEAN); + addDefaultMapping(ValueLayout.OfChar.class, ValueLayout.JAVA_CHAR); + addDefaultMapping(ValueLayout.OfShort.class, ValueLayout.JAVA_SHORT); + addDefaultMapping(ValueLayout.OfInt.class, ValueLayout.JAVA_INT); + addDefaultMapping(ValueLayout.OfFloat.class, ValueLayout.JAVA_FLOAT); + addDefaultMapping(ValueLayout.OfLong.class, JAVA_LONG); + addDefaultMapping(ValueLayout.OfDouble.class, ValueLayout.JAVA_DOUBLE); + addDefaultMapping(GroupLayout.class, MemoryLayout.structLayout(ValueLayout.JAVA_INT)); + addDefaultMapping(SequenceLayout.class, MemoryLayout.sequenceLayout(ValueLayout.JAVA_INT)); addDefaultMapping(MemorySegment.class, MemorySegment.ofArray(new byte[10])); addDefaultMapping(FunctionDescriptor.class, FunctionDescriptor.ofVoid()); - addDefaultMapping(CLinker.class, CLinker.getInstance()); - addDefaultMapping(CLinker.VaList.class, VaListHelper.vaList); - addDefaultMapping(CLinker.VaList.Builder.class, VaListHelper.vaListBuilder); - addDefaultMapping(ResourceScope.class, ResourceScope.newImplicitScope()); - addDefaultMapping(SegmentAllocator.class, (size, align) -> null); + addDefaultMapping(CLinker.class, CLinker.systemCLinker()); + addDefaultMapping(VaList.class, VaListHelper.vaList); + addDefaultMapping(VaList.Builder.class, VaListHelper.vaListBuilder); + addDefaultMapping(ResourceScope.class, ResourceScope.newSharedScope()); + addDefaultMapping(SegmentAllocator.class, SegmentAllocator.prefixAllocator(MemorySegment.ofArray(new byte[10]))); addDefaultMapping(Supplier.class, () -> null); - addDefaultMapping(ResourceScope.Handle.class, ResourceScope.globalScope().acquire()); addDefaultMapping(ClassLoader.class, TestNulls.class.getClassLoader()); - addDefaultMapping(SymbolLookup.class, CLinker.systemLookup()); + addDefaultMapping(SymbolLookup.class, CLinker.systemCLinker()); + addDefaultMapping(NativeSymbol.class, NativeSymbol.ofAddress("dummy", MemoryAddress.ofLong(1), ResourceScope.globalScope())); } static class VaListHelper { - static final CLinker.VaList vaList; - static final CLinker.VaList.Builder vaListBuilder; + static final VaList vaList; + static final VaList.Builder vaListBuilder; static { - AtomicReference builderRef = new AtomicReference<>(); - vaList = CLinker.VaList.make(b -> { + AtomicReference builderRef = new AtomicReference<>(); + vaList = VaList.make(b -> { builderRef.set(b); - b.vargFromLong(CLinker.C_LONG_LONG, 42L); + b.addVarg(JAVA_LONG, 42L); }, ResourceScope.newImplicitScope()); vaListBuilder = builderRef.get(); } diff --git a/test/jdk/java/foreign/TestReshape.java b/test/jdk/java/foreign/TestReshape.java index dc53e83ee0f09..4fcfb5eb2103e 100644 --- a/test/jdk/java/foreign/TestReshape.java +++ b/test/jdk/java/foreign/TestReshape.java @@ -27,7 +27,6 @@ */ import jdk.incubator.foreign.MemoryLayout; -import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.SequenceLayout; import java.util.ArrayList; @@ -35,6 +34,7 @@ import java.util.List; import java.util.stream.LongStream; +import jdk.incubator.foreign.ValueLayout; import org.testng.annotations.*; import static org.testng.Assert.*; @@ -54,43 +54,43 @@ public void testReshape(MemoryLayout layout, long[] expectedShape) { @Test(expectedExceptions = IllegalArgumentException.class) public void testInvalidReshape() { - SequenceLayout seq = MemoryLayout.sequenceLayout(4, MemoryLayouts.JAVA_INT); + SequenceLayout seq = MemoryLayout.sequenceLayout(4, ValueLayout.JAVA_INT); seq.reshape(3, 2); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadReshapeInference() { - SequenceLayout seq = MemoryLayout.sequenceLayout(4, MemoryLayouts.JAVA_INT); + SequenceLayout seq = MemoryLayout.sequenceLayout(4, ValueLayout.JAVA_INT); seq.reshape(-1, -1); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadReshapeParameterZero() { - SequenceLayout seq = MemoryLayout.sequenceLayout(4, MemoryLayouts.JAVA_INT); + SequenceLayout seq = MemoryLayout.sequenceLayout(4, ValueLayout.JAVA_INT); seq.reshape(0, 4); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadReshapeParameterNegative() { - SequenceLayout seq = MemoryLayout.sequenceLayout(4, MemoryLayouts.JAVA_INT); + SequenceLayout seq = MemoryLayout.sequenceLayout(4, ValueLayout.JAVA_INT); seq.reshape(-2, 2); } @Test(expectedExceptions = UnsupportedOperationException.class) public void testReshapeOnUnboundSequence() { - SequenceLayout seq = MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT); + SequenceLayout seq = MemoryLayout.sequenceLayout(ValueLayout.JAVA_INT); seq.reshape(3, 2); } @Test(expectedExceptions = UnsupportedOperationException.class) public void testFlattenOnUnboundSequence() { - SequenceLayout seq = MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT); + SequenceLayout seq = MemoryLayout.sequenceLayout(ValueLayout.JAVA_INT); seq.flatten(); } @Test(expectedExceptions = UnsupportedOperationException.class) public void testFlattenOnUnboundNestedSequence() { - SequenceLayout seq = MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT)); + SequenceLayout seq = MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(ValueLayout.JAVA_INT)); seq.flatten(); } @@ -125,61 +125,61 @@ public Iterator iterator() { } static MemoryLayout POINT = MemoryLayout.structLayout( - MemoryLayouts.JAVA_INT, - MemoryLayouts.JAVA_INT + ValueLayout.JAVA_INT, + ValueLayout.JAVA_INT ); @DataProvider(name = "shapes") Object[][] shapes() { return new Object[][] { - { MemoryLayouts.JAVA_BYTE, new long[] { 256 } }, - { MemoryLayouts.JAVA_BYTE, new long[] { 16, 16 } }, - { MemoryLayouts.JAVA_BYTE, new long[] { 4, 4, 4, 4 } }, - { MemoryLayouts.JAVA_BYTE, new long[] { 2, 8, 16 } }, - { MemoryLayouts.JAVA_BYTE, new long[] { 16, 8, 2 } }, - { MemoryLayouts.JAVA_BYTE, new long[] { 8, 16, 2 } }, - - { MemoryLayouts.JAVA_SHORT, new long[] { 256 } }, - { MemoryLayouts.JAVA_SHORT, new long[] { 16, 16 } }, - { MemoryLayouts.JAVA_SHORT, new long[] { 4, 4, 4, 4 } }, - { MemoryLayouts.JAVA_SHORT, new long[] { 2, 8, 16 } }, - { MemoryLayouts.JAVA_SHORT, new long[] { 16, 8, 2 } }, - { MemoryLayouts.JAVA_SHORT, new long[] { 8, 16, 2 } }, - - { MemoryLayouts.JAVA_CHAR, new long[] { 256 } }, - { MemoryLayouts.JAVA_CHAR, new long[] { 16, 16 } }, - { MemoryLayouts.JAVA_CHAR, new long[] { 4, 4, 4, 4 } }, - { MemoryLayouts.JAVA_CHAR, new long[] { 2, 8, 16 } }, - { MemoryLayouts.JAVA_CHAR, new long[] { 16, 8, 2 } }, - { MemoryLayouts.JAVA_CHAR, new long[] { 8, 16, 2 } }, - - { MemoryLayouts.JAVA_INT, new long[] { 256 } }, - { MemoryLayouts.JAVA_INT, new long[] { 16, 16 } }, - { MemoryLayouts.JAVA_INT, new long[] { 4, 4, 4, 4 } }, - { MemoryLayouts.JAVA_INT, new long[] { 2, 8, 16 } }, - { MemoryLayouts.JAVA_INT, new long[] { 16, 8, 2 } }, - { MemoryLayouts.JAVA_INT, new long[] { 8, 16, 2 } }, - - { MemoryLayouts.JAVA_LONG, new long[] { 256 } }, - { MemoryLayouts.JAVA_LONG, new long[] { 16, 16 } }, - { MemoryLayouts.JAVA_LONG, new long[] { 4, 4, 4, 4 } }, - { MemoryLayouts.JAVA_LONG, new long[] { 2, 8, 16 } }, - { MemoryLayouts.JAVA_LONG, new long[] { 16, 8, 2 } }, - { MemoryLayouts.JAVA_LONG, new long[] { 8, 16, 2 } }, - - { MemoryLayouts.JAVA_FLOAT, new long[] { 256 } }, - { MemoryLayouts.JAVA_FLOAT, new long[] { 16, 16 } }, - { MemoryLayouts.JAVA_FLOAT, new long[] { 4, 4, 4, 4 } }, - { MemoryLayouts.JAVA_FLOAT, new long[] { 2, 8, 16 } }, - { MemoryLayouts.JAVA_FLOAT, new long[] { 16, 8, 2 } }, - { MemoryLayouts.JAVA_FLOAT, new long[] { 8, 16, 2 } }, - - { MemoryLayouts.JAVA_DOUBLE, new long[] { 256 } }, - { MemoryLayouts.JAVA_DOUBLE, new long[] { 16, 16 } }, - { MemoryLayouts.JAVA_DOUBLE, new long[] { 4, 4, 4, 4 } }, - { MemoryLayouts.JAVA_DOUBLE, new long[] { 2, 8, 16 } }, - { MemoryLayouts.JAVA_DOUBLE, new long[] { 16, 8, 2 } }, - { MemoryLayouts.JAVA_DOUBLE, new long[] { 8, 16, 2 } }, + { ValueLayout.JAVA_BYTE, new long[] { 256 } }, + { ValueLayout.JAVA_BYTE, new long[] { 16, 16 } }, + { ValueLayout.JAVA_BYTE, new long[] { 4, 4, 4, 4 } }, + { ValueLayout.JAVA_BYTE, new long[] { 2, 8, 16 } }, + { ValueLayout.JAVA_BYTE, new long[] { 16, 8, 2 } }, + { ValueLayout.JAVA_BYTE, new long[] { 8, 16, 2 } }, + + { ValueLayout.JAVA_SHORT, new long[] { 256 } }, + { ValueLayout.JAVA_SHORT, new long[] { 16, 16 } }, + { ValueLayout.JAVA_SHORT, new long[] { 4, 4, 4, 4 } }, + { ValueLayout.JAVA_SHORT, new long[] { 2, 8, 16 } }, + { ValueLayout.JAVA_SHORT, new long[] { 16, 8, 2 } }, + { ValueLayout.JAVA_SHORT, new long[] { 8, 16, 2 } }, + + { ValueLayout.JAVA_CHAR, new long[] { 256 } }, + { ValueLayout.JAVA_CHAR, new long[] { 16, 16 } }, + { ValueLayout.JAVA_CHAR, new long[] { 4, 4, 4, 4 } }, + { ValueLayout.JAVA_CHAR, new long[] { 2, 8, 16 } }, + { ValueLayout.JAVA_CHAR, new long[] { 16, 8, 2 } }, + { ValueLayout.JAVA_CHAR, new long[] { 8, 16, 2 } }, + + { ValueLayout.JAVA_INT, new long[] { 256 } }, + { ValueLayout.JAVA_INT, new long[] { 16, 16 } }, + { ValueLayout.JAVA_INT, new long[] { 4, 4, 4, 4 } }, + { ValueLayout.JAVA_INT, new long[] { 2, 8, 16 } }, + { ValueLayout.JAVA_INT, new long[] { 16, 8, 2 } }, + { ValueLayout.JAVA_INT, new long[] { 8, 16, 2 } }, + + { ValueLayout.JAVA_LONG, new long[] { 256 } }, + { ValueLayout.JAVA_LONG, new long[] { 16, 16 } }, + { ValueLayout.JAVA_LONG, new long[] { 4, 4, 4, 4 } }, + { ValueLayout.JAVA_LONG, new long[] { 2, 8, 16 } }, + { ValueLayout.JAVA_LONG, new long[] { 16, 8, 2 } }, + { ValueLayout.JAVA_LONG, new long[] { 8, 16, 2 } }, + + { ValueLayout.JAVA_FLOAT, new long[] { 256 } }, + { ValueLayout.JAVA_FLOAT, new long[] { 16, 16 } }, + { ValueLayout.JAVA_FLOAT, new long[] { 4, 4, 4, 4 } }, + { ValueLayout.JAVA_FLOAT, new long[] { 2, 8, 16 } }, + { ValueLayout.JAVA_FLOAT, new long[] { 16, 8, 2 } }, + { ValueLayout.JAVA_FLOAT, new long[] { 8, 16, 2 } }, + + { ValueLayout.JAVA_DOUBLE, new long[] { 256 } }, + { ValueLayout.JAVA_DOUBLE, new long[] { 16, 16 } }, + { ValueLayout.JAVA_DOUBLE, new long[] { 4, 4, 4, 4 } }, + { ValueLayout.JAVA_DOUBLE, new long[] { 2, 8, 16 } }, + { ValueLayout.JAVA_DOUBLE, new long[] { 16, 8, 2 } }, + { ValueLayout.JAVA_DOUBLE, new long[] { 8, 16, 2 } }, { POINT, new long[] { 256 } }, { POINT, new long[] { 16, 16 } }, diff --git a/test/jdk/java/foreign/TestResourceScope.java b/test/jdk/java/foreign/TestResourceScope.java index bb126abbe8a6b..77c436bca8991 100644 --- a/test/jdk/java/foreign/TestResourceScope.java +++ b/test/jdk/java/foreign/TestResourceScope.java @@ -39,6 +39,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; @@ -157,15 +158,14 @@ public void testSharedMultiThread(Supplier cleanerSupplier) { } } - @Test(dataProvider = "cleaners") - public void testLockSingleThread(Supplier cleanerSupplier) { - Cleaner cleaner = cleanerSupplier.get(); - ResourceScope scope = cleaner != null ? - ResourceScope.newConfinedScope(cleaner) : - ResourceScope.newConfinedScope(); - List handles = new ArrayList<>(); + @Test + public void testLockSingleThread() { + ResourceScope scope = ResourceScope.newConfinedScope(); + List handles = new ArrayList<>(); for (int i = 0 ; i < N_THREADS ; i++) { - handles.add(scope.acquire()); + ResourceScope handle = ResourceScope.newConfinedScope(); + handle.keepAlive(scope); + handles.add(handle); } while (true) { @@ -175,31 +175,24 @@ public void testLockSingleThread(Supplier cleanerSupplier) { break; } catch (IllegalStateException ex) { assertTrue(handles.size() > 0); - ResourceScope.Handle handle = handles.remove(0); - scope.release(handle); - scope.release(handle); // make sure it's idempotent - scope.release(handle); // make sure it's idempotent + ResourceScope handle = handles.remove(0); + handle.close(); } } } - @Test(dataProvider = "cleaners") - public void testLockSharedMultiThread(Supplier cleanerSupplier) { - Cleaner cleaner = cleanerSupplier.get(); - ResourceScope scope = cleaner != null ? - ResourceScope.newSharedScope(cleaner) : - ResourceScope.newSharedScope(); + @Test + public void testLockSharedMultiThread() { + ResourceScope scope = ResourceScope.newSharedScope(); AtomicInteger lockCount = new AtomicInteger(); for (int i = 0 ; i < N_THREADS ; i++) { new Thread(() -> { - try { - ResourceScope.Handle handle = scope.acquire(); // this can throw if segment has been closed + try (ResourceScope handle = ResourceScope.newConfinedScope()) { + handle.keepAlive(scope); lockCount.incrementAndGet(); waitSomeTime(); lockCount.decrementAndGet(); - scope.release(handle); // cannot throw (acquired segments cannot be closed) - scope.release(handle); // cannot throw (idempotent) - scope.release(handle); // cannot throw (idempotent) + handle.close(); } catch (IllegalStateException ex) { // might be already closed - do nothing } @@ -230,13 +223,12 @@ public void testCloseEmptySharedScope() { @Test public void testCloseConfinedLock() { ResourceScope scope = ResourceScope.newConfinedScope(); - ResourceScope.Handle handle = scope.acquire(); + ResourceScope handle = ResourceScope.newConfinedScope(); + handle.keepAlive(scope); AtomicReference failure = new AtomicReference<>(); Thread t = new Thread(() -> { try { - scope.release(handle); - scope.release(handle); // make sure it's idempotent - scope.release(handle); // make sure it's idempotent + handle.close(); } catch (Throwable ex) { failure.set(ex); } @@ -255,24 +247,85 @@ public void testCloseConfinedLock() { public void testScopeHandles(Supplier scopeFactory) { ResourceScope scope = scopeFactory.get(); acquireRecursive(scope, 5); - if (!scope.isImplicit()) { + if (scope != ResourceScope.globalScope()) { scope.close(); } } + @Test(dataProvider = "scopes", expectedExceptions = IllegalArgumentException.class) + public void testAcquireSelf(Supplier scopeSupplier) { + ResourceScope scope = scopeSupplier.get(); + scope.keepAlive(scope); + } + private void acquireRecursive(ResourceScope scope, int acquireCount) { - ResourceScope.Handle handle = scope.acquire(); - assertEquals(handle.scope(), scope); - if (acquireCount > 0) { - // recursive acquire - acquireRecursive(scope, acquireCount - 1); + try (ResourceScope handle = ResourceScope.newConfinedScope()) { + handle.keepAlive(scope); + if (acquireCount > 0) { + // recursive acquire + acquireRecursive(scope, acquireCount - 1); + } + if (scope != ResourceScope.globalScope()) { + assertThrows(IllegalStateException.class, scope::close); + } } - if (!scope.isImplicit()) { - assertThrows(IllegalStateException.class, scope::close); + } + + @Test + public void testConfinedScopeWithImplicitDependency() { + ResourceScope root = ResourceScope.newConfinedScope(); + // Create many implicit scopes which depend on 'root', and let them become unreachable. + for (int i = 0; i < N_THREADS; i++) { + ResourceScope.newConfinedScope(Cleaner.create()).keepAlive(root); } - scope.release(handle); - scope.release(handle); // make sure it's idempotent - scope.release(handle); // make sure it's idempotent + // Now let's keep trying to close 'root' until we succeed. This is trickier than it seems: cleanup action + // might be called from another thread (the Cleaner thread), so that the confined scope lock count is updated racily. + // If that happens, the loop below never terminates. + while (true) { + try { + root.close(); + break; // success! + } catch (IllegalStateException ex) { + kickGC(); + for (int i = 0 ; i < N_THREADS ; i++) { // add more races from current thread + try (ResourceScope scope = ResourceScope.newConfinedScope()) { + scope.keepAlive(root); + // dummy + } + } + // try again + } + } + } + + @Test + public void testConfinedScopeWithSharedDependency() { + ResourceScope root = ResourceScope.newConfinedScope(); + List threads = new ArrayList<>(); + // Create many implicit scopes which depend on 'root', and let them become unreachable. + for (int i = 0; i < N_THREADS; i++) { + ResourceScope scope = ResourceScope.newSharedScope(); // create scope inside same thread! + scope.keepAlive(root); + Thread t = new Thread(scope::close); // close from another thread! + threads.add(t); + t.start(); + } + for (int i = 0 ; i < N_THREADS ; i++) { // add more races from current thread + try (ResourceScope scope = ResourceScope.newConfinedScope()) { + scope.keepAlive(root); + // dummy + } + } + threads.forEach(t -> { + try { + t.join(); + } catch (InterruptedException ex) { + // ok + } + }); + // Now let's close 'root'. This is trickier than it seems: releases of the confined scope happen in different + // threads, so that the confined scope lock count is updated racily. If that happens, the following close will blow up. + root.close(); } private void waitSomeTime() { diff --git a/test/jdk/java/foreign/TestRestricted.java b/test/jdk/java/foreign/TestRestricted.java index 68d23b6eb237e..8667ffe1f4710 100644 --- a/test/jdk/java/foreign/TestRestricted.java +++ b/test/jdk/java/foreign/TestRestricted.java @@ -21,6 +21,7 @@ * questions. */ +import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; @@ -38,37 +39,37 @@ public class TestRestricted { @Test(expectedExceptions = InvocationTargetException.class) public void testReflection() throws Throwable { - Method method = MemorySegment.class.getDeclaredMethod("globalNativeSegment"); + Method method = CLinker.class.getDeclaredMethod("systemCLinker"); method.invoke(null); } @Test(expectedExceptions = IllegalCallerException.class) public void testInvoke() throws Throwable { - var mh = MethodHandles.lookup().findStatic(MemorySegment.class, - "globalNativeSegment", MethodType.methodType(MemorySegment.class)); - var seg = (MemorySegment)mh.invokeExact(); + var mh = MethodHandles.lookup().findStatic(CLinker.class, + "systemCLinker", MethodType.methodType(CLinker.class)); + var seg = (CLinker)mh.invokeExact(); } @Test(expectedExceptions = IllegalCallerException.class) public void testDirectAccess() throws Throwable { - MemorySegment.globalNativeSegment(); + CLinker.systemCLinker(); } @Test(expectedExceptions = InvocationTargetException.class) public void testReflection2() throws Throwable { - Method method = MemoryAddress.class.getDeclaredMethod("asSegment", long.class, ResourceScope.class); - method.invoke(MemoryAddress.NULL, 4000L, ResourceScope.globalScope()); + Method method = MemorySegment.class.getDeclaredMethod("ofAddress", MemoryAddress.class, long.class, ResourceScope.class); + method.invoke(null, MemoryAddress.NULL, 4000L, ResourceScope.globalScope()); } @Test(expectedExceptions = IllegalCallerException.class) public void testInvoke2() throws Throwable { - var mh = MethodHandles.lookup().findVirtual(MemoryAddress.class, "asSegment", - MethodType.methodType(MemorySegment.class, long.class, ResourceScope.class)); + var mh = MethodHandles.lookup().findStatic(MemorySegment.class, "ofAddress", + MethodType.methodType(MemorySegment.class, MemoryAddress.class, long.class, ResourceScope.class)); var seg = (MemorySegment)mh.invokeExact(MemoryAddress.NULL, 4000L, ResourceScope.globalScope()); } @Test(expectedExceptions = IllegalCallerException.class) public void testDirectAccess2() throws Throwable { - MemoryAddress.NULL.asSegment(4000L, ResourceScope.globalScope()); + MemorySegment.ofAddress(MemoryAddress.NULL, 4000, ResourceScope.globalScope()); } } diff --git a/test/jdk/java/foreign/TestScopedOperations.java b/test/jdk/java/foreign/TestScopedOperations.java index 9254a9e5b3a79..7384693481c20 100644 --- a/test/jdk/java/foreign/TestScopedOperations.java +++ b/test/jdk/java/foreign/TestScopedOperations.java @@ -27,13 +27,14 @@ * @run testng/othervm --enable-native-access=ALL-UNNAMED TestScopedOperations */ -import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; -import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.NativeSymbol; import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.SegmentAllocator; +import jdk.incubator.foreign.VaList; +import jdk.incubator.foreign.ValueLayout; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -47,6 +48,9 @@ import java.util.function.Consumer; import java.util.function.Function; +import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_LONG; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; @@ -67,11 +71,12 @@ public class TestScopedOperations { } @Test(dataProvider = "scopedOperations") - public void testOpAfterClose(String name, ScopedOperation scopedOperation) { + public void testOpAfterClose(String name, ScopedOperation scopedOperation) { ResourceScope scope = ResourceScope.newConfinedScope(); + Z obj = scopedOperation.apply(scope); scope.close(); try { - scopedOperation.accept(scope); + scopedOperation.accept(obj); fail(); } catch (IllegalStateException ex) { assertTrue(ex.getMessage().contains("closed")); @@ -79,12 +84,13 @@ public void testOpAfterClose(String name, ScopedOperation scopedOperation) { } @Test(dataProvider = "scopedOperations") - public void testOpOutsideConfinement(String name, ScopedOperation scopedOperation) { + public void testOpOutsideConfinement(String name, ScopedOperation scopedOperation) { try (ResourceScope scope = ResourceScope.newConfinedScope()) { + Z obj = scopedOperation.apply(scope); AtomicReference failed = new AtomicReference<>(); Thread t = new Thread(() -> { try { - scopedOperation.accept(scope); + scopedOperation.accept(obj); } catch (Throwable ex) { failed.set(ex); } @@ -106,9 +112,10 @@ public void testOpOutsideConfinement(String name, ScopedOperation scopedOperatio ScopedOperation.ofScope(scope -> scope.addCloseAction(() -> { }), "ResourceScope::addOnClose"); ScopedOperation.ofScope(scope -> { - ResourceScope.Handle handle = scope.acquire(); - scope.release(handle); - }, "ResourceScope::lock"); + ResourceScope scope2 = ResourceScope.newConfinedScope(); + scope2.keepAlive(scope); + scope2.close(); + }, "ResourceScope::keepAlive"); ScopedOperation.ofScope(scope -> MemorySegment.allocateNative(100, scope), "MemorySegment::allocateNative"); ScopedOperation.ofScope(scope -> { try { @@ -117,54 +124,46 @@ public void testOpOutsideConfinement(String name, ScopedOperation scopedOperatio fail(); } }, "MemorySegment::mapFromFile"); - ScopedOperation.ofScope(scope -> CLinker.VaList.make(b -> {}, scope), "VaList::make"); - ScopedOperation.ofScope(scope -> CLinker.VaList.ofAddress(MemoryAddress.ofLong(42), scope), "VaList::make"); - ScopedOperation.ofScope(scope -> CLinker.toCString("Hello", scope), "CLinker::toCString"); - ScopedOperation.ofScope(SegmentAllocator::arenaAllocator, "SegmentAllocator::arenaAllocator"); + ScopedOperation.ofScope(scope -> VaList.make(b -> b.addVarg(JAVA_INT, 42), scope), "VaList::make"); + ScopedOperation.ofScope(scope -> VaList.ofAddress(MemoryAddress.ofLong(42), scope), "VaList::make"); + ScopedOperation.ofScope(SegmentAllocator::newNativeArena, "SegmentAllocator::arenaAllocator"); // segment operations - ScopedOperation.ofSegment(MemorySegment::toByteArray, "MemorySegment::toByteArray"); - ScopedOperation.ofSegment(MemorySegment::toCharArray, "MemorySegment::toCharArray"); - ScopedOperation.ofSegment(MemorySegment::toShortArray, "MemorySegment::toShortArray"); - ScopedOperation.ofSegment(MemorySegment::toIntArray, "MemorySegment::toIntArray"); - ScopedOperation.ofSegment(MemorySegment::toFloatArray, "MemorySegment::toFloatArray"); - ScopedOperation.ofSegment(MemorySegment::toLongArray, "MemorySegment::toLongArray"); - ScopedOperation.ofSegment(MemorySegment::toDoubleArray, "MemorySegment::toDoubleArray"); + ScopedOperation.ofSegment(s -> s.toArray(JAVA_BYTE), "MemorySegment::toArray(BYTE)"); ScopedOperation.ofSegment(MemorySegment::address, "MemorySegment::address"); - ScopedOperation.ofSegment(s -> MemoryLayout.sequenceLayout(s.byteSize(), MemoryLayouts.JAVA_BYTE), "MemorySegment::spliterator"); ScopedOperation.ofSegment(s -> s.copyFrom(s), "MemorySegment::copyFrom"); ScopedOperation.ofSegment(s -> s.mismatch(s), "MemorySegment::mismatch"); ScopedOperation.ofSegment(s -> s.fill((byte) 0), "MemorySegment::fill"); - // address operations - ScopedOperation.ofAddress(a -> a.toRawLongValue(), "MemoryAddress::toRawLongValue"); - ScopedOperation.ofAddress(a -> a.asSegment(100, ResourceScope.globalScope()), "MemoryAddress::asSegment"); // valist operations - ScopedOperation.ofVaList(CLinker.VaList::address, "VaList::address"); - ScopedOperation.ofVaList(CLinker.VaList::copy, "VaList::copy"); - ScopedOperation.ofVaList(list -> list.vargAsAddress(MemoryLayouts.ADDRESS), "VaList::vargAsAddress"); - ScopedOperation.ofVaList(list -> list.vargAsInt(MemoryLayouts.JAVA_INT), "VaList::vargAsInt"); - ScopedOperation.ofVaList(list -> list.vargAsLong(MemoryLayouts.JAVA_LONG), "VaList::vargAsLong"); - ScopedOperation.ofVaList(list -> list.vargAsDouble(MemoryLayouts.JAVA_DOUBLE), "VaList::vargAsDouble"); - ScopedOperation.ofVaList(CLinker.VaList::skip, "VaList::skip"); - ScopedOperation.ofVaList(list -> list.vargAsSegment(MemoryLayout.structLayout(MemoryLayouts.JAVA_INT), ResourceScope.newImplicitScope()), "VaList::vargAsSegment/1"); + ScopedOperation.ofVaList(VaList::address, "VaList::address"); + ScopedOperation.ofVaList(VaList::copy, "VaList::copy"); + ScopedOperation.ofVaList(list -> list.nextVarg(ValueLayout.ADDRESS), "VaList::nextVarg/address"); + ScopedOperation.ofVaList(list -> list.nextVarg(ValueLayout.JAVA_INT), "VaList::nextVarg/int"); + ScopedOperation.ofVaList(list -> list.nextVarg(ValueLayout.JAVA_LONG), "VaList::nextVarg/long"); + ScopedOperation.ofVaList(list -> list.nextVarg(ValueLayout.JAVA_DOUBLE), "VaList::nextVarg/double"); + ScopedOperation.ofVaList(VaList::skip, "VaList::skip"); + ScopedOperation.ofVaList(list -> list.nextVarg(MemoryLayout.structLayout(ValueLayout.JAVA_INT), + SegmentAllocator.prefixAllocator(MemorySegment.ofArray(new byte[4]))), "VaList::nextVargs/segment"); // allocator operations ScopedOperation.ofAllocator(a -> a.allocate(1), "NativeAllocator::allocate/size"); ScopedOperation.ofAllocator(a -> a.allocate(1, 1), "NativeAllocator::allocate/size/align"); - ScopedOperation.ofAllocator(a -> a.allocate(MemoryLayouts.JAVA_BYTE), "NativeAllocator::allocate/layout"); - ScopedOperation.ofAllocator(a -> a.allocate(MemoryLayouts.JAVA_BYTE, (byte) 0), "NativeAllocator::allocate/byte"); - ScopedOperation.ofAllocator(a -> a.allocate(MemoryLayouts.JAVA_CHAR, (char) 0), "NativeAllocator::allocate/char"); - ScopedOperation.ofAllocator(a -> a.allocate(MemoryLayouts.JAVA_SHORT, (short) 0), "NativeAllocator::allocate/short"); - ScopedOperation.ofAllocator(a -> a.allocate(MemoryLayouts.JAVA_INT, 0), "NativeAllocator::allocate/int"); - ScopedOperation.ofAllocator(a -> a.allocate(MemoryLayouts.JAVA_FLOAT, 0f), "NativeAllocator::allocate/float"); - ScopedOperation.ofAllocator(a -> a.allocate(MemoryLayouts.JAVA_LONG, 0L), "NativeAllocator::allocate/long"); - ScopedOperation.ofAllocator(a -> a.allocate(MemoryLayouts.JAVA_DOUBLE, 0d), "NativeAllocator::allocate/double"); - ScopedOperation.ofAllocator(a -> a.allocateArray(MemoryLayouts.JAVA_BYTE, 1L), "NativeAllocator::allocateArray/size"); - ScopedOperation.ofAllocator(a -> a.allocateArray(MemoryLayouts.JAVA_BYTE, new byte[]{0}), "NativeAllocator::allocateArray/byte"); - ScopedOperation.ofAllocator(a -> a.allocateArray(MemoryLayouts.JAVA_CHAR, new char[]{0}), "NativeAllocator::allocateArray/char"); - ScopedOperation.ofAllocator(a -> a.allocateArray(MemoryLayouts.JAVA_SHORT, new short[]{0}), "NativeAllocator::allocateArray/short"); - ScopedOperation.ofAllocator(a -> a.allocateArray(MemoryLayouts.JAVA_INT, new int[]{0}), "NativeAllocator::allocateArray/int"); - ScopedOperation.ofAllocator(a -> a.allocateArray(MemoryLayouts.JAVA_FLOAT, new float[]{0}), "NativeAllocator::allocateArray/float"); - ScopedOperation.ofAllocator(a -> a.allocateArray(MemoryLayouts.JAVA_LONG, new long[]{0}), "NativeAllocator::allocateArray/long"); - ScopedOperation.ofAllocator(a -> a.allocateArray(MemoryLayouts.JAVA_DOUBLE, new double[]{0}), "NativeAllocator::allocateArray/double"); + ScopedOperation.ofAllocator(a -> a.allocate(JAVA_BYTE), "NativeAllocator::allocate/layout"); + ScopedOperation.ofAllocator(a -> a.allocate(JAVA_BYTE, (byte) 0), "NativeAllocator::allocate/byte"); + ScopedOperation.ofAllocator(a -> a.allocate(ValueLayout.JAVA_CHAR, (char) 0), "NativeAllocator::allocate/char"); + ScopedOperation.ofAllocator(a -> a.allocate(ValueLayout.JAVA_SHORT, (short) 0), "NativeAllocator::allocate/short"); + ScopedOperation.ofAllocator(a -> a.allocate(ValueLayout.JAVA_INT, 0), "NativeAllocator::allocate/int"); + ScopedOperation.ofAllocator(a -> a.allocate(ValueLayout.JAVA_FLOAT, 0f), "NativeAllocator::allocate/float"); + ScopedOperation.ofAllocator(a -> a.allocate(ValueLayout.JAVA_LONG, 0L), "NativeAllocator::allocate/long"); + ScopedOperation.ofAllocator(a -> a.allocate(ValueLayout.JAVA_DOUBLE, 0d), "NativeAllocator::allocate/double"); + ScopedOperation.ofAllocator(a -> a.allocateArray(JAVA_BYTE, 1L), "NativeAllocator::allocateArray/size"); + ScopedOperation.ofAllocator(a -> a.allocateArray(JAVA_BYTE, new byte[]{0}), "NativeAllocator::allocateArray/byte"); + ScopedOperation.ofAllocator(a -> a.allocateArray(ValueLayout.JAVA_CHAR, new char[]{0}), "NativeAllocator::allocateArray/char"); + ScopedOperation.ofAllocator(a -> a.allocateArray(ValueLayout.JAVA_SHORT, new short[]{0}), "NativeAllocator::allocateArray/short"); + ScopedOperation.ofAllocator(a -> a.allocateArray(ValueLayout.JAVA_INT, new int[]{0}), "NativeAllocator::allocateArray/int"); + ScopedOperation.ofAllocator(a -> a.allocateArray(ValueLayout.JAVA_FLOAT, new float[]{0}), "NativeAllocator::allocateArray/float"); + ScopedOperation.ofAllocator(a -> a.allocateArray(ValueLayout.JAVA_LONG, new long[]{0}), "NativeAllocator::allocateArray/long"); + ScopedOperation.ofAllocator(a -> a.allocateArray(ValueLayout.JAVA_DOUBLE, new double[]{0}), "NativeAllocator::allocateArray/double"); + // native symbol + ScopedOperation.of(scope -> NativeSymbol.ofAddress("", MemoryAddress.NULL, scope), NativeSymbol::address, "NativeSymbol::address"); }; @DataProvider(name = "scopedOperations") @@ -172,56 +171,56 @@ static Object[][] scopedOperations() { return scopedOperations.stream().map(op -> new Object[] { op.name, op }).toArray(Object[][]::new); } - static class ScopedOperation implements Consumer { + static class ScopedOperation implements Consumer, Function { - final Consumer scopeConsumer; + final Function factory; + final Consumer operation; final String name; - private ScopedOperation(Consumer scopeConsumer, String name) { - this.scopeConsumer = scopeConsumer; + private ScopedOperation(Function factory, Consumer operation, String name) { + this.factory = factory; + this.operation = operation; this.name = name; } @Override - public void accept(ResourceScope scope) { - scopeConsumer.accept(scope); + public void accept(X obj) { + operation.accept(obj); } - static void ofScope(Consumer scopeConsumer, String name) { - scopedOperations.add(new ScopedOperation(scopeConsumer::accept, name)); + @Override + public X apply(ResourceScope scope) { + return factory.apply(scope); } - static void ofVaList(Consumer vaListConsumer, String name) { - scopedOperations.add(new ScopedOperation(scope -> { - CLinker.VaList vaList = CLinker.VaList.make((builder) -> {}, scope); - vaListConsumer.accept(vaList); - }, name)); + static void of(Function factory, Consumer consumer, String name) { + scopedOperations.add(new ScopedOperation<>(factory, consumer, name)); } - static void ofSegment(Consumer segmentConsumer, String name) { - for (SegmentFactory segmentFactory : SegmentFactory.values()) { - scopedOperations.add(new ScopedOperation(scope -> { - MemorySegment segment = segmentFactory.segmentFactory.apply(scope); - segmentConsumer.accept(segment); - }, segmentFactory.name() + "/" + name)); - } + static void ofScope(Consumer scopeConsumer, String name) { + scopedOperations.add(new ScopedOperation<>(Function.identity(), scopeConsumer, name)); + } + + static void ofVaList(Consumer vaListConsumer, String name) { + scopedOperations.add(new ScopedOperation<>(scope -> VaList.make(builder -> builder.addVarg(JAVA_LONG, 42), scope), + vaListConsumer, name)); } - static void ofAddress(Consumer addressConsumer, String name) { + static void ofSegment(Consumer segmentConsumer, String name) { for (SegmentFactory segmentFactory : SegmentFactory.values()) { - scopedOperations.add(new ScopedOperation(scope -> { - MemoryAddress segment = segmentFactory.segmentFactory.apply(scope).address(); - addressConsumer.accept(segment); - }, segmentFactory.name() + "/" + name)); + scopedOperations.add(new ScopedOperation<>( + segmentFactory.segmentFactory, + segmentConsumer, + segmentFactory.name() + "/" + name)); } } static void ofAllocator(Consumer allocatorConsumer, String name) { for (AllocatorFactory allocatorFactory : AllocatorFactory.values()) { - scopedOperations.add(new ScopedOperation(scope -> { - SegmentAllocator allocator = allocatorFactory.allocatorFactory.apply(scope); - allocatorConsumer.accept(allocator); - }, allocatorFactory.name() + "/" + name)); + scopedOperations.add(new ScopedOperation<>( + allocatorFactory.allocatorFactory, + allocatorConsumer, + allocatorFactory.name() + "/" + name)); } } @@ -235,7 +234,7 @@ enum SegmentFactory { throw new AssertionError(ex); } }), - UNSAFE(scope -> MemoryAddress.NULL.asSegment(10, scope)); + UNSAFE(scope -> MemorySegment.ofAddress(MemoryAddress.NULL, 10, scope)); static { try { @@ -255,13 +254,8 @@ enum SegmentFactory { } enum AllocatorFactory { - ARENA_BOUNDED(scope -> SegmentAllocator.arenaAllocator(1000, scope)), - ARENA_UNBOUNDED(SegmentAllocator::arenaAllocator), - FROM_SEGMENT(scope -> { - MemorySegment segment = MemorySegment.allocateNative(10, scope); - return SegmentAllocator.ofSegment(segment); - }), - FROM_SCOPE(SegmentAllocator::ofScope); + ARENA_BOUNDED(scope -> SegmentAllocator.newNativeArena(1000, scope)), + ARENA_UNBOUNDED(SegmentAllocator::newNativeArena); final Function allocatorFactory; diff --git a/test/jdk/java/foreign/TestSegmentAllocators.java b/test/jdk/java/foreign/TestSegmentAllocators.java index 3d2538ad897a2..aa269191a2331 100644 --- a/test/jdk/java/foreign/TestSegmentAllocators.java +++ b/test/jdk/java/foreign/TestSegmentAllocators.java @@ -34,6 +34,7 @@ import java.lang.invoke.VarHandle; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.nio.CharBuffer; import java.nio.DoubleBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; @@ -51,17 +52,19 @@ public class TestSegmentAllocators { final static int ELEMS = 128; - final static Class ADDRESS_CARRIER = MemoryLayouts.ADDRESS.bitSize() == 64 ? long.class : int.class; + final static Class ADDRESS_CARRIER = ValueLayout.ADDRESS.bitSize() == 64 ? long.class : int.class; @Test(dataProvider = "nativeScopes") - public void testAllocation(Z value, AllocationFactory allocationFactory, ValueLayout layout, AllocationFunction allocationFunction, Function handleFactory) { - ValueLayout[] layouts = { + @SuppressWarnings("unchecked") + public void testAllocation(Z value, AllocationFactory allocationFactory, L layout, AllocationFunction allocationFunction, Function handleFactory) { + layout = (L)layout.withBitAlignment(layout.bitSize()); + L[] layouts = (L[])new ValueLayout[] { layout, layout.withBitAlignment(layout.bitAlignment() * 2), layout.withBitAlignment(layout.bitAlignment() * 4), layout.withBitAlignment(layout.bitAlignment() * 8) }; - for (ValueLayout alignedLayout : layouts) { + for (L alignedLayout : layouts) { List addressList = new ArrayList<>(); int elems = ELEMS / ((int)alignedLayout.byteAlignment() / (int)layout.byteAlignment()); ResourceScope[] scopes = { @@ -80,16 +83,18 @@ public void testAllocation(Z value, AllocationFactory allocationFactory, Val } boolean isBound = allocationFactory.isBound(); try { - allocationFunction.allocate(allocator, alignedLayout, value); //too much, should fail if bound + allocationFunction.allocate(allocator, alignedLayout, value); assertFalse(isBound); } catch (OutOfMemoryError ex) { //failure is expected if bound assertTrue(isBound); } } - // addresses should be invalid now - for (MemorySegment address : addressList) { - assertFalse(address.scope().isAlive()); + if (allocationFactory != AllocationFactory.IMPLICIT_ALLOCATOR) { + // addresses should be invalid now + for (MemorySegment address : addressList) { + assertFalse(address.scope().isAlive()); + } } } } @@ -100,7 +105,7 @@ public void testAllocation(Z value, AllocationFactory allocationFactory, Val @Test public void testBigAllocationInUnboundedScope() { try (ResourceScope scope = ResourceScope.newConfinedScope()) { - SegmentAllocator allocator = SegmentAllocator.arenaAllocator(scope); + SegmentAllocator allocator = SegmentAllocator.newNativeArena(scope); for (int i = 8 ; i < SIZE_256M ; i *= 8) { MemorySegment address = allocator.allocate(i, i); //check size @@ -111,24 +116,30 @@ public void testBigAllocationInUnboundedScope() { } } - @Test(expectedExceptions = OutOfMemoryError.class) + @Test public void testTooBigForBoundedArena() { try (ResourceScope scope = ResourceScope.newConfinedScope()) { - SegmentAllocator allocator = SegmentAllocator.arenaAllocator(10, scope); - allocator.allocate(12); + SegmentAllocator allocator = SegmentAllocator.newNativeArena(10, scope); + assertThrows(OutOfMemoryError.class, () -> allocator.allocate(12)); + allocator.allocate(5); // ok } } @Test public void testBiggerThanBlockForBoundedArena() { try (ResourceScope scope = ResourceScope.newConfinedScope()) { - SegmentAllocator allocator = SegmentAllocator.arenaAllocator(4 * 1024 * 2, scope); + SegmentAllocator allocator = SegmentAllocator.newNativeArena(4 * 1024 * 2, scope); allocator.allocate(4 * 1024 + 1); // should be ok } } + @Test(expectedExceptions = IllegalArgumentException.class) + public void testBadUnboundedArenaSize() { + SegmentAllocator.newNativeArena( -1, ResourceScope.globalScope()); + } + @Test(dataProvider = "arrayScopes") - public void testArray(AllocationFactory allocationFactory, ValueLayout layout, AllocationFunction allocationFunction, ToArrayHelper arrayHelper) { + public void testArray(AllocationFactory allocationFactory, ValueLayout layout, AllocationFunction allocationFunction, ToArrayHelper arrayHelper) { Z arr = arrayHelper.array(); ResourceScope[] scopes = { ResourceScope.newConfinedScope(), @@ -146,221 +157,142 @@ public void testArray(AllocationFactory allocationFactory, ValueLayout layou @DataProvider(name = "nativeScopes") static Object[][] nativeScopes() { - return new Object[][] { - { (byte)42, AllocationFactory.BOUNDED, MemoryLayouts.BITS_8_BE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(byte.class) }, - { (short)42, AllocationFactory.BOUNDED, MemoryLayouts.BITS_16_BE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(short.class) }, - { (char)42, AllocationFactory.BOUNDED, MemoryLayouts.BITS_16_BE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(char.class) }, - { 42, AllocationFactory.BOUNDED, - MemoryLayouts.BITS_32_BE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(int.class) }, - { 42f, AllocationFactory.BOUNDED, MemoryLayouts.BITS_32_BE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(float.class) }, - { 42L, AllocationFactory.BOUNDED, MemoryLayouts.BITS_64_BE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(long.class) }, - { 42d, AllocationFactory.BOUNDED, MemoryLayouts.BITS_64_BE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(double.class) }, - { MemoryAddress.ofLong(42), AllocationFactory.BOUNDED, MemoryLayouts.ADDRESS.withOrder(ByteOrder.BIG_ENDIAN), - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> MemoryHandles.asAddressVarHandle(l.varHandle(ADDRESS_CARRIER)) }, - - { (byte)42, AllocationFactory.BOUNDED, MemoryLayouts.BITS_8_LE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(byte.class) }, - { (short)42, AllocationFactory.BOUNDED, MemoryLayouts.BITS_16_LE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(short.class) }, - { (char)42, AllocationFactory.BOUNDED, MemoryLayouts.BITS_16_LE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(char.class) }, - { 42, AllocationFactory.BOUNDED, - MemoryLayouts.BITS_32_LE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(int.class) }, - { 42f, AllocationFactory.BOUNDED, MemoryLayouts.BITS_32_LE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(float.class) }, - { 42L, AllocationFactory.BOUNDED, MemoryLayouts.BITS_64_LE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(long.class) }, - { 42d, AllocationFactory.BOUNDED, MemoryLayouts.BITS_64_LE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(double.class) }, - { MemoryAddress.ofLong(42), AllocationFactory.BOUNDED, MemoryLayouts.ADDRESS.withOrder(ByteOrder.LITTLE_ENDIAN), - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> MemoryHandles.asAddressVarHandle(l.varHandle(ADDRESS_CARRIER)) }, - - { (byte)42, AllocationFactory.UNBOUNDED, MemoryLayouts.BITS_8_BE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(byte.class) }, - { (short)42, AllocationFactory.UNBOUNDED, MemoryLayouts.BITS_16_BE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(short.class) }, - { (char)42, AllocationFactory.UNBOUNDED, MemoryLayouts.BITS_16_BE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(char.class) }, - { 42, AllocationFactory.UNBOUNDED, - MemoryLayouts.BITS_32_BE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(int.class) }, - { 42f, AllocationFactory.UNBOUNDED, MemoryLayouts.BITS_32_BE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(float.class) }, - { 42L, AllocationFactory.UNBOUNDED, MemoryLayouts.BITS_64_BE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(long.class) }, - { 42d, AllocationFactory.UNBOUNDED, MemoryLayouts.BITS_64_BE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(double.class) }, - { MemoryAddress.ofLong(42), AllocationFactory.UNBOUNDED, MemoryLayouts.ADDRESS.withOrder(ByteOrder.BIG_ENDIAN), - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> MemoryHandles.asAddressVarHandle(l.varHandle(ADDRESS_CARRIER)) }, - - { (byte)42, AllocationFactory.UNBOUNDED, MemoryLayouts.BITS_8_LE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(byte.class) }, - { (short)42, AllocationFactory.UNBOUNDED, MemoryLayouts.BITS_16_LE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(short.class) }, - { (char)42, AllocationFactory.UNBOUNDED, MemoryLayouts.BITS_16_LE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(char.class) }, - { 42, AllocationFactory.UNBOUNDED, - MemoryLayouts.BITS_32_LE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(int.class) }, - { 42f, AllocationFactory.UNBOUNDED, MemoryLayouts.BITS_32_LE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(float.class) }, - { 42L, AllocationFactory.UNBOUNDED, MemoryLayouts.BITS_64_LE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(long.class) }, - { 42d, AllocationFactory.UNBOUNDED, MemoryLayouts.BITS_64_LE, - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> l.varHandle(double.class) }, - { MemoryAddress.ofLong(42), AllocationFactory.UNBOUNDED, MemoryLayouts.ADDRESS.withOrder(ByteOrder.LITTLE_ENDIAN), - (AllocationFunction) SegmentAllocator::allocate, - (Function)l -> MemoryHandles.asAddressVarHandle(l.varHandle(ADDRESS_CARRIER)) }, - }; + List nativeScopes = new ArrayList<>(); + for (AllocationFactory factory : AllocationFactory.values()) { + nativeScopes.add(new Object[] { (byte)42, factory, ValueLayout.JAVA_BYTE, + (AllocationFunction.OfByte) SegmentAllocator::allocate, + (Function)l -> l.varHandle() }); + nativeScopes.add(new Object[] { (short)42, factory, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), + (AllocationFunction.OfShort) SegmentAllocator::allocate, + (Function)l -> l.varHandle() }); + nativeScopes.add(new Object[] { (char)42, factory, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), + (AllocationFunction.OfChar) SegmentAllocator::allocate, + (Function)l -> l.varHandle() }); + nativeScopes.add(new Object[] { 42, factory, + ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), + (AllocationFunction.OfInt) SegmentAllocator::allocate, + (Function)l -> l.varHandle() }); + nativeScopes.add(new Object[] { 42f, factory, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), + (AllocationFunction.OfFloat) SegmentAllocator::allocate, + (Function)l -> l.varHandle() }); + nativeScopes.add(new Object[] { 42L, factory, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), + (AllocationFunction.OfLong) SegmentAllocator::allocate, + (Function)l -> l.varHandle() }); + nativeScopes.add(new Object[] { 42d, factory, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), + (AllocationFunction.OfDouble) SegmentAllocator::allocate, + (Function)l -> l.varHandle() }); + nativeScopes.add(new Object[] { MemoryAddress.ofLong(42), factory, ValueLayout.ADDRESS.withOrder(ByteOrder.BIG_ENDIAN), + (AllocationFunction.OfAddress) SegmentAllocator::allocate, + (Function)l -> l.varHandle() }); + + nativeScopes.add(new Object[] { (short)42, factory, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), + (AllocationFunction.OfShort) SegmentAllocator::allocate, + (Function)l -> l.varHandle() }); + nativeScopes.add(new Object[] { (char)42, factory, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), + (AllocationFunction.OfChar) SegmentAllocator::allocate, + (Function)l -> l.varHandle() }); + nativeScopes.add(new Object[] { 42, factory, + ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), + (AllocationFunction.OfInt) SegmentAllocator::allocate, + (Function)l -> l.varHandle() }); + nativeScopes.add(new Object[] { 42f, factory, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN), + (AllocationFunction.OfFloat) SegmentAllocator::allocate, + (Function)l -> l.varHandle() }); + nativeScopes.add(new Object[] { 42L, factory, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN), + (AllocationFunction.OfLong) SegmentAllocator::allocate, + (Function)l -> l.varHandle() }); + nativeScopes.add(new Object[] { 42d, factory, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN), + (AllocationFunction.OfDouble) SegmentAllocator::allocate, + (Function)l -> l.varHandle() }); + nativeScopes.add(new Object[] { MemoryAddress.ofLong(42), factory, ValueLayout.ADDRESS.withOrder(ByteOrder.BIG_ENDIAN), + (AllocationFunction.OfAddress) SegmentAllocator::allocate, + (Function)l -> l.varHandle() }); + } + return nativeScopes.toArray(Object[][]::new); } @DataProvider(name = "arrayScopes") static Object[][] arrayScopes() { - return new Object[][] { - { AllocationFactory.BOUNDED, MemoryLayouts.BITS_8_LE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toByteArray }, - { AllocationFactory.BOUNDED, MemoryLayouts.BITS_16_LE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toShortArray }, - { AllocationFactory.BOUNDED, - MemoryLayouts.BITS_32_LE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toIntArray }, - { AllocationFactory.BOUNDED, MemoryLayouts.BITS_32_LE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toFloatArray }, - { AllocationFactory.BOUNDED, MemoryLayouts.BITS_64_LE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toLongArray }, - { AllocationFactory.BOUNDED, MemoryLayouts.BITS_64_LE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toDoubleArray }, - { AllocationFactory.BOUNDED, MemoryLayouts.ADDRESS.withOrder(ByteOrder.LITTLE_ENDIAN), - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toAddressArray }, - - - { AllocationFactory.BOUNDED, MemoryLayouts.BITS_8_BE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toByteArray }, - { AllocationFactory.BOUNDED, MemoryLayouts.BITS_16_BE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toShortArray }, - { AllocationFactory.BOUNDED, - MemoryLayouts.BITS_32_BE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toIntArray }, - { AllocationFactory.BOUNDED, MemoryLayouts.BITS_32_BE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toFloatArray }, - { AllocationFactory.BOUNDED, MemoryLayouts.BITS_64_BE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toLongArray }, - { AllocationFactory.BOUNDED, MemoryLayouts.BITS_64_BE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toDoubleArray }, - { AllocationFactory.BOUNDED, MemoryLayouts.ADDRESS.withOrder(ByteOrder.BIG_ENDIAN), - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toAddressArray }, - - { AllocationFactory.UNBOUNDED, MemoryLayouts.BITS_8_LE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toByteArray }, - { AllocationFactory.UNBOUNDED, MemoryLayouts.BITS_16_LE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toShortArray }, - { AllocationFactory.UNBOUNDED, - MemoryLayouts.BITS_32_LE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toIntArray }, - { AllocationFactory.UNBOUNDED, MemoryLayouts.BITS_32_LE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toFloatArray }, - { AllocationFactory.UNBOUNDED, MemoryLayouts.BITS_64_LE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toLongArray }, - { AllocationFactory.UNBOUNDED, MemoryLayouts.BITS_64_LE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toDoubleArray }, - { AllocationFactory.UNBOUNDED, MemoryLayouts.ADDRESS.withOrder(ByteOrder.LITTLE_ENDIAN), - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toAddressArray }, - - - { AllocationFactory.UNBOUNDED, MemoryLayouts.BITS_8_BE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toByteArray }, - { AllocationFactory.UNBOUNDED, MemoryLayouts.BITS_16_BE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toShortArray }, - { AllocationFactory.UNBOUNDED, - MemoryLayouts.BITS_32_BE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toIntArray }, - { AllocationFactory.UNBOUNDED, MemoryLayouts.BITS_32_BE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toFloatArray }, - { AllocationFactory.UNBOUNDED, MemoryLayouts.BITS_64_BE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toLongArray }, - { AllocationFactory.UNBOUNDED, MemoryLayouts.BITS_64_BE, - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toDoubleArray }, - { AllocationFactory.UNBOUNDED, MemoryLayouts.ADDRESS.withOrder(ByteOrder.BIG_ENDIAN), - (AllocationFunction) SegmentAllocator::allocateArray, - ToArrayHelper.toAddressArray }, + List arrayScopes = new ArrayList<>(); + for (AllocationFactory factory : AllocationFactory.values()) { + arrayScopes.add(new Object[] { factory, ValueLayout.JAVA_BYTE, + (AllocationFunction.OfByteArray) SegmentAllocator::allocateArray, + ToArrayHelper.toByteArray }); + arrayScopes.add(new Object[] { factory, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), + (AllocationFunction.OfCharArray) SegmentAllocator::allocateArray, + ToArrayHelper.toCharArray }); + arrayScopes.add(new Object[] { factory, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), + (AllocationFunction.OfShortArray) SegmentAllocator::allocateArray, + ToArrayHelper.toShortArray }); + arrayScopes.add(new Object[] { factory, + ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), + (AllocationFunction.OfIntArray) SegmentAllocator::allocateArray, + ToArrayHelper.toIntArray }); + arrayScopes.add(new Object[] { factory, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN), + (AllocationFunction.OfFloatArray) SegmentAllocator::allocateArray, + ToArrayHelper.toFloatArray }); + arrayScopes.add(new Object[] { factory, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN), + (AllocationFunction.OfLongArray) SegmentAllocator::allocateArray, + ToArrayHelper.toLongArray }); + arrayScopes.add(new Object[] { factory, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN), + (AllocationFunction.OfDoubleArray) SegmentAllocator::allocateArray, + ToArrayHelper.toDoubleArray }); + + arrayScopes.add(new Object[] { factory, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), + (AllocationFunction.OfCharArray) SegmentAllocator::allocateArray, + ToArrayHelper.toCharArray }); + arrayScopes.add(new Object[] { factory, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), + (AllocationFunction.OfShortArray) SegmentAllocator::allocateArray, + ToArrayHelper.toShortArray }); + arrayScopes.add(new Object[] { factory, + ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), + (AllocationFunction.OfIntArray) SegmentAllocator::allocateArray, + ToArrayHelper.toIntArray }); + arrayScopes.add(new Object[] { factory, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), + (AllocationFunction.OfFloatArray) SegmentAllocator::allocateArray, + ToArrayHelper.toFloatArray }); + arrayScopes.add(new Object[] { factory, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), + (AllocationFunction.OfLongArray) SegmentAllocator::allocateArray, + ToArrayHelper.toLongArray }); + arrayScopes.add(new Object[] { factory, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), + (AllocationFunction.OfDoubleArray) SegmentAllocator::allocateArray, + ToArrayHelper.toDoubleArray }); }; + return arrayScopes.toArray(Object[][]::new); } - interface AllocationFunction { - MemorySegment allocate(SegmentAllocator allocator, ValueLayout layout, X value); + interface AllocationFunction { + MemorySegment allocate(SegmentAllocator allocator, L layout, X value); + + interface OfByte extends AllocationFunction { } + interface OfBoolean extends AllocationFunction { } + interface OfChar extends AllocationFunction { } + interface OfShort extends AllocationFunction { } + interface OfInt extends AllocationFunction { } + interface OfFloat extends AllocationFunction { } + interface OfLong extends AllocationFunction { } + interface OfDouble extends AllocationFunction { } + interface OfAddress extends AllocationFunction { } + + interface OfByteArray extends AllocationFunction { } + interface OfCharArray extends AllocationFunction { } + interface OfShortArray extends AllocationFunction { } + interface OfIntArray extends AllocationFunction { } + interface OfFloatArray extends AllocationFunction { } + interface OfLongArray extends AllocationFunction { } + interface OfDoubleArray extends AllocationFunction { } } - static class AllocationFactory { + enum AllocationFactory { + ARENA_BOUNDED(true, SegmentAllocator::newNativeArena), + ARENA_UNBOUNDED(false, (size, scope) -> SegmentAllocator.newNativeArena(scope)), + NATIVE_ALLOCATOR(false, (size, scope) -> SegmentAllocator.nativeAllocator(scope)), + IMPLICIT_ALLOCATOR(false, (size, scope) -> SegmentAllocator.implicitAllocator()); + private final boolean isBound; private final BiFunction factory; - private AllocationFactory(boolean isBound, BiFunction factory) { + AllocationFactory(boolean isBound, BiFunction factory) { this.isBound = isBound; this.factory = factory; } @@ -372,9 +304,6 @@ SegmentAllocator allocator(long size, ResourceScope scope) { public boolean isBound() { return isBound; } - - static AllocationFactory BOUNDED = new AllocationFactory(true, SegmentAllocator::arenaAllocator); - static AllocationFactory UNBOUNDED = new AllocationFactory(false, (size, scope) -> SegmentAllocator.arenaAllocator(scope)); } interface ToArrayHelper { @@ -396,6 +325,21 @@ public byte[] toArray(MemorySegment segment, ValueLayout layout) { } }; + ToArrayHelper toCharArray = new ToArrayHelper<>() { + @Override + public char[] array() { + return new char[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + } + + @Override + public char[] toArray(MemorySegment segment, ValueLayout layout) { + CharBuffer buffer = segment.asByteBuffer().order(layout.order()).asCharBuffer(); + char[] found = new char[buffer.limit()]; + buffer.get(found); + return found; + } + }; + ToArrayHelper toShortArray = new ToArrayHelper<>() { @Override public short[] array() { @@ -474,7 +418,7 @@ public double[] toArray(MemorySegment segment, ValueLayout layout) { ToArrayHelper toAddressArray = new ToArrayHelper<>() { @Override public MemoryAddress[] array() { - return switch ((int)MemoryLayouts.ADDRESS.byteSize()) { + return switch ((int) ValueLayout.ADDRESS.byteSize()) { case 4 -> wrap(toIntArray.array()); case 8 -> wrap(toLongArray.array()); default -> throw new IllegalStateException("Cannot get here"); diff --git a/test/jdk/java/foreign/TestSegmentCopy.java b/test/jdk/java/foreign/TestSegmentCopy.java new file mode 100644 index 0000000000000..de2dba0848341 --- /dev/null +++ b/test/jdk/java/foreign/TestSegmentCopy.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/* + * @test + * @run testng TestSegmentCopy + */ + +import jdk.incubator.foreign.MemoryHandles; +import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.ResourceScope; +import jdk.incubator.foreign.ValueLayout; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.List; +import java.util.function.IntFunction; + +import static org.testng.Assert.*; + +public class TestSegmentCopy { + + @Test(dataProvider = "slices") + public void testByteCopy(SegmentSlice s1, SegmentSlice s2) { + int size = Math.min(s1.byteSize(), s2.byteSize()); + //prepare source and target segments + for (int i = 0 ; i < size ; i++) { + Type.BYTE.set(s2, i, 0); + } + for (int i = 0 ; i < size ; i++) { + Type.BYTE.set(s1, i, i); + } + //perform copy + MemorySegment.copy(s1.segment, 0, s2.segment, 0, size); + //check that copy actually worked + for (int i = 0 ; i < size ; i++) { + Type.BYTE.check(s2, i, i); + } + } + + @Test(dataProvider = "slices") + public void testElementCopy(SegmentSlice s1, SegmentSlice s2) { + if (s1.type.carrier != s2.type.carrier) return; + int size = Math.min(s1.elementSize(), s2.elementSize()); + //prepare source and target segments + for (int i = 0 ; i < size ; i++) { + s2.set(i, 0); + } + for (int i = 0 ; i < size ; i++) { + s1.set(i, i); + } + //perform copy + MemorySegment.copy(s1.segment, s1.type.layout, 0, s2.segment, s2.type.layout, 0, size); + //check that copy actually worked + for (int i = 0; i < size; i++) { + s2.check(i, i); + } + } + + interface Getter { + X get(MemorySegment segment, ValueLayout layout, long index); + } + + interface Setter { + void set(MemorySegment segment, ValueLayout layout, long index, X val); + } + + enum Type { + // Byte + BYTE(byte.class, ValueLayout.JAVA_BYTE, i -> (byte)i), + //LE + SHORT_LE(short.class, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), i -> (short)i), + CHAR_LE(char.class, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), i -> (char)i), + INT_LE(int.class, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), i -> i), + FLOAT_LE(float.class, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN), i -> (float)i), + LONG_LE(long.class, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN), i -> (long)i), + DOUBLE_LE(double.class, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN), i -> (double)i), + //BE + SHORT_BE(short.class, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), i -> (short)i), + CHAR_BE(char.class, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), i -> (char)i), + INT_BE(int.class, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), i -> i), + FLOAT_BE(float.class, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), i -> (float)i), + LONG_BE(long.class, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), i -> (long)i), + DOUBLE_BE(double.class, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), i -> (double)i); + + final ValueLayout layout; + final IntFunction valueConverter; + final Class carrier; + + @SuppressWarnings("unchecked") + Type(Class carrier, ValueLayout layout, IntFunction valueConverter) { + this.carrier = carrier; + this.layout = layout; + this.valueConverter = (IntFunction)valueConverter; + } + + int size() { + return (int)layout.byteSize(); + } + + VarHandle handle() { + return MemoryHandles.varHandle(layout); + } + + void set(SegmentSlice slice, int index, int val) { + handle().set(slice.segment, index * size(), valueConverter.apply(val)); + } + + void check(SegmentSlice slice, int index, int val) { + assertEquals(handle().get(slice.segment, index * size()), valueConverter.apply(val)); + } + } + + static class SegmentSlice { + + enum Kind { + NATIVE(i -> MemorySegment.allocateNative(i, ResourceScope.newImplicitScope())), + ARRAY(i -> MemorySegment.ofArray(new byte[i])); + + final IntFunction segmentFactory; + + Kind(IntFunction segmentFactory) { + this.segmentFactory = segmentFactory; + } + + MemorySegment makeSegment(int elems) { + return segmentFactory.apply(elems); + } + } + + final Kind kind; + final Type type; + final int first; + final int last; + final MemorySegment segment; + + public SegmentSlice(Kind kind, Type type, int first, int last, MemorySegment segment) { + this.kind = kind; + this.type = type; + this.first = first; + this.last = last; + this.segment = segment; + } + + void set(int index, int val) { + type.set(this, index, val); + } + + void check(int index, int val) { + type.check(this, index, val); + } + + int byteSize() { + return last - first + 1; + } + + int elementSize() { + return byteSize() / type.size(); + } + + @Override + public String toString() { + return String.format("SegmentSlice{%s, %d, %d}", type, first, last); + } + } + + @DataProvider(name = "slices") + static Object[][] elementSlices() { + List slices = new ArrayList<>(); + for (SegmentSlice.Kind kind : SegmentSlice.Kind.values()) { + MemorySegment segment = kind.makeSegment(16); + //compute all slices + for (Type type : Type.values()) { + for (int index = 0; index < 16; index += type.size()) { + MemorySegment first = segment.asSlice(0, index); + slices.add(new SegmentSlice(kind, type, 0, index - 1, first)); + MemorySegment second = segment.asSlice(index); + slices.add(new SegmentSlice(kind, type, index, 15, second)); + } + } + } + Object[][] sliceArray = new Object[slices.size() * slices.size()][]; + for (int i = 0 ; i < slices.size() ; i++) { + for (int j = 0 ; j < slices.size() ; j++) { + sliceArray[i * slices.size() + j] = new Object[] { slices.get(i), slices.get(j) }; + } + } + return sliceArray; + } +} diff --git a/test/jdk/java/foreign/TestRebase.java b/test/jdk/java/foreign/TestSegmentOffset.java similarity index 68% rename from test/jdk/java/foreign/TestRebase.java rename to test/jdk/java/foreign/TestSegmentOffset.java index 0396523c8a558..b9149f2471f43 100644 --- a/test/jdk/java/foreign/TestRebase.java +++ b/test/jdk/java/foreign/TestSegmentOffset.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -16,60 +16,58 @@ * 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 + * 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 - * @run testng TestRebase + * @run testng TestSegmentOffset */ -import jdk.incubator.foreign.MemoryAccess; -import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; - import java.util.ArrayList; import java.util.List; import java.util.function.IntFunction; - +import static java.lang.System.out; +import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE; import static org.testng.Assert.*; -public class TestRebase { +public class TestSegmentOffset { @Test(dataProvider = "slices") - public void testRebase(SegmentSlice s1, SegmentSlice s2) { + public void testOffset(SegmentSlice s1, SegmentSlice s2) { if (s1.contains(s2)) { - //check that an address and its rebased counterpart point to same element - MemoryAddress base = s2.segment.address(); - long offset = base.segmentOffset(s1.segment); + // check that a segment and its overlapping segment point to same elements + long offset = s1.segment.segmentOffset(s2.segment); for (int i = 0; i < s2.size(); i++) { - int expected = MemoryAccess.getByteAtOffset(s2.segment, i); - int found = (int)MemoryAccess.getByteAtOffset(s1.segment, i + offset); + out.format("testOffset s1:%s, s2:%s, offset:%d, i:%s\n", s1, s2, offset, i); + byte expected = s2.segment.get(JAVA_BYTE, i); + byte found = s1.segment.get(JAVA_BYTE, i + offset); assertEquals(found, expected); } } else if (s1.kind != s2.kind) { - // check that rebase s1 to s2 fails + // check that offset from s1 to s2 fails try { - s1.segment.address().segmentOffset(s2.segment); - fail("Rebase unexpectedly passed!"); - } catch (IllegalArgumentException ex) { - assertTrue(true); + long offset = s1.segment.segmentOffset(s2.segment); + out.format("testOffset s1:%s, s2:%s, offset:%d\n", s1, s2, offset); + fail("offset unexpectedly passed!"); + } catch (UnsupportedOperationException ex) { + assertTrue(ex.getMessage().contains("Cannot compute offset from native to heap (or vice versa).")); } } else if (!s2.contains(s1)) { - //disjoint segments - check that rebased address is out of bounds - MemoryAddress base = s2.segment.address(); - long offset = base.segmentOffset(s1.segment); + // disjoint segments - check that offset is out of bounds + long offset = s1.segment.segmentOffset(s2.segment); for (int i = 0; i < s2.size(); i++) { - MemoryAccess.getByteAtOffset(s2.segment, i); + out.format("testOffset s1:%s, s2:%s, offset:%d, i:%s\n", s1, s2, offset, i); + s2.segment.get(JAVA_BYTE, i); try { - MemoryAccess.getByteAtOffset(s1.segment, i + offset); - fail("Rebased address on a disjoint segment is not out of bounds!"); + s1.segment.get(JAVA_BYTE, i + offset); + fail("Offset on a disjoint segment is not out of bounds!"); } catch (IndexOutOfBoundsException ex) { assertTrue(true); } @@ -80,7 +78,7 @@ public void testRebase(SegmentSlice s1, SegmentSlice s2) { static class SegmentSlice { enum Kind { - NATIVE(i -> MemorySegment.allocateNative(i, ResourceScope.newImplicitScope())), + NATIVE(i -> MemorySegment.allocateNative(i, ResourceScope.newConfinedScope())), ARRAY(i -> MemorySegment.ofArray(new byte[i])); final IntFunction segmentFactory; @@ -122,12 +120,12 @@ static Object[][] slices() { int[] sizes = { 16, 8, 4, 2, 1 }; List slices = new ArrayList<>(); for (SegmentSlice.Kind kind : SegmentSlice.Kind.values()) { - //init root segment + // init root segment MemorySegment segment = kind.makeSegment(16); for (int i = 0 ; i < 16 ; i++) { - MemoryAccess.setByteAtOffset(segment, i, (byte)i); + segment.set(JAVA_BYTE, i, (byte)i); } - //compute all slices + // compute all slices for (int size : sizes) { for (int index = 0 ; index < 16 ; index += size) { MemorySegment slice = segment.asSlice(index, size); diff --git a/test/jdk/java/foreign/TestSegmentOverlap.java b/test/jdk/java/foreign/TestSegmentOverlap.java new file mode 100644 index 0000000000000..fb55e92dedb90 --- /dev/null +++ b/test/jdk/java/foreign/TestSegmentOverlap.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @run testng/othervm TestSegmentOverlap + */ + +import java.io.File; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.List; +import java.util.function.Supplier; +import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.ResourceScope; +import org.testng.annotations.Test; +import org.testng.annotations.DataProvider; +import static java.lang.System.out; +import static org.testng.Assert.*; + +public class TestSegmentOverlap { + + static Path tempPath; + + static { + try { + File file = File.createTempFile("buffer", "txt"); + file.deleteOnExit(); + tempPath = file.toPath(); + Files.write(file.toPath(), new byte[16], StandardOpenOption.WRITE); + + } catch (IOException ex) { + throw new ExceptionInInitializerError(ex); + } + } + + @DataProvider(name = "segmentFactories") + public Object[][] segmentFactories() { + List> l = List.of( + () -> MemorySegment.allocateNative(16, ResourceScope.newConfinedScope()), + () -> { + try { + return MemorySegment.mapFile(tempPath, 0L, 16, FileChannel.MapMode.READ_WRITE, ResourceScope.newConfinedScope()); + } catch (IOException e) { + throw new RuntimeException(e); + } + }, + () -> MemorySegment.ofArray(new byte[] { 0x00, 0x01, 0x02, 0x03 } ), + () -> MemorySegment.ofArray(new char[] {'a', 'b', 'c', 'd' } ), + () -> MemorySegment.ofArray(new double[] { 1d, 2d, 3d, 4d} ), + () -> MemorySegment.ofArray(new float[] { 1.0f, 2.0f, 3.0f, 4.0f } ), + () -> MemorySegment.ofArray(new int[] { 1, 2, 3, 4 }), + () -> MemorySegment.ofArray(new long[] { 1L, 2L, 3L, 4L } ), + () -> MemorySegment.ofArray(new short[] { 1, 2, 3, 4 } ) + ); + return l.stream().map(s -> new Object[] { s }).toArray(Object[][]::new); + } + + @Test(dataProvider="segmentFactories") + public void testBasic(Supplier segmentSupplier) { + var s1 = segmentSupplier.get(); + var s2 = segmentSupplier.get(); + var sOther = s1.isNative() ? OtherSegmentFactory.HEAP.factory.get() + : OtherSegmentFactory.NATIVE.factory.get(); + out.format("testBasic s1:%s, s2:%s, sOther:%s\n", s1, s2, sOther); + assertNull(s1.asOverlappingSlice(s2)); + assertNull(s2.asOverlappingSlice(s1)); + assertNull(s1.asOverlappingSlice(sOther)); + } + + @Test(dataProvider="segmentFactories") + public void testIdentical(Supplier segmentSupplier) { + var s1 = segmentSupplier.get(); + var s2 = s1.asReadOnly(); + out.format("testIdentical s1:%s, s2:%s\n", s1, s2); + assertEquals(s1.asOverlappingSlice(s2).byteSize(), s1.byteSize()); + assertEquals(s1.asOverlappingSlice(s2).scope(), s1.scope()); + + assertEquals(s2.asOverlappingSlice(s1).byteSize(), s2.byteSize()); + assertEquals(s2.asOverlappingSlice(s1).scope(), s2.scope()); + + if (s1.isNative()) { + assertEquals(s1.asOverlappingSlice(s2).address(), s1.address()); + assertEquals(s2.asOverlappingSlice(s1).address(), s2.address()); + } + } + + @Test(dataProvider="segmentFactories") + public void testSlices(Supplier segmentSupplier) { + MemorySegment s1 = segmentSupplier.get(); + MemorySegment s2 = segmentSupplier.get(); + for (int offset = 0 ; offset < 4 ; offset++) { + MemorySegment slice = s1.asSlice(offset); + out.format("testSlices s1:%s, s2:%s, slice:%s, offset:%d\n", s1, s2, slice, offset); + assertEquals(s1.asOverlappingSlice(slice).byteSize(), s1.byteSize() - offset); + assertEquals(s1.asOverlappingSlice(slice).scope(), s1.scope()); + + assertEquals(slice.asOverlappingSlice(s1).byteSize(), slice.byteSize()); + assertEquals(slice.asOverlappingSlice(s1).scope(), slice.scope()); + + if (s1.isNative()) { + assertEquals(s1.asOverlappingSlice(slice).address(), s1.address().addOffset(offset)); + assertEquals(slice.asOverlappingSlice(s1).address(), slice.address()); + } + assertNull(s2.asOverlappingSlice(slice)); + } + } + + enum OtherSegmentFactory { + NATIVE(() -> MemorySegment.allocateNative(16, ResourceScope.newConfinedScope())), + HEAP(() -> MemorySegment.ofArray(new byte[]{16})); + + final Supplier factory; + + OtherSegmentFactory(Supplier segmentFactory) { + this.factory = segmentFactory; + } + } +} diff --git a/test/jdk/java/foreign/TestSegments.java b/test/jdk/java/foreign/TestSegments.java index beadf1618ac07..fdf373448230a 100644 --- a/test/jdk/java/foreign/TestSegments.java +++ b/test/jdk/java/foreign/TestSegments.java @@ -27,23 +27,21 @@ * @run testng/othervm -Xmx4G -XX:MaxDirectMemorySize=1M TestSegments */ -import jdk.incubator.foreign.MemoryAccess; import jdk.incubator.foreign.MemoryLayout; -import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; +import jdk.incubator.foreign.ValueLayout; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.lang.invoke.VarHandle; import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import java.util.function.IntFunction; -import java.util.function.LongFunction; import java.util.function.Supplier; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; import static org.testng.Assert.*; public class TestSegments { @@ -53,11 +51,6 @@ public void testBadAllocateAlign(long size, long align) { MemorySegment.allocateNative(size, align, ResourceScope.newImplicitScope()); } - @Test(dataProvider = "badLayouts", expectedExceptions = UnsupportedOperationException.class) - public void testBadAllocateLayout(MemoryLayout layout) { - MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope()); - } - @Test(expectedExceptions = { OutOfMemoryError.class, IllegalArgumentException.class }) public void testAllocateTooBig() { @@ -71,8 +64,8 @@ public void testNativeAllocationTooBig() { @Test public void testNativeSegmentIsZeroed() { - VarHandle byteHandle = MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_BYTE) - .varHandle(byte.class, MemoryLayout.PathElement.sequenceElement()); + VarHandle byteHandle = MemoryLayout.sequenceLayout(ValueLayout.JAVA_BYTE) + .varHandle(MemoryLayout.PathElement.sequenceElement()); try (ResourceScope scope = ResourceScope.newConfinedScope()) { MemorySegment segment = MemorySegment.allocateNative(1000, 1, scope); for (long i = 0 ; i < segment.byteSize() ; i++) { @@ -83,8 +76,8 @@ public void testNativeSegmentIsZeroed() { @Test public void testSlices() { - VarHandle byteHandle = MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_BYTE) - .varHandle(byte.class, MemoryLayout.PathElement.sequenceElement()); + VarHandle byteHandle = MemoryLayout.sequenceLayout(ValueLayout.JAVA_BYTE) + .varHandle(MemoryLayout.PathElement.sequenceElement()); try (ResourceScope scope = ResourceScope.newConfinedScope()) { MemorySegment segment = MemorySegment.allocateNative(10, 1, scope); //init @@ -107,14 +100,14 @@ public void testSlices() { public void testSmallSegmentMax() { long offset = (long)Integer.MAX_VALUE + (long)Integer.MAX_VALUE + 2L + 6L; // overflows to 6 when casted to int MemorySegment memorySegment = MemorySegment.allocateNative(10, ResourceScope.newImplicitScope()); - MemoryAccess.getIntAtOffset(memorySegment, offset); + memorySegment.get(JAVA_INT, offset); } @Test(expectedExceptions = IndexOutOfBoundsException.class) public void testSmallSegmentMin() { long offset = ((long)Integer.MIN_VALUE * 2L) + 6L; // underflows to 6 when casted to int MemorySegment memorySegment = MemorySegment.allocateNative(10, ResourceScope.newImplicitScope()); - MemoryAccess.getIntAtOffset(memorySegment, offset); + memorySegment.get(JAVA_INT, offset); } @Test(dataProvider = "segmentFactories") @@ -125,7 +118,7 @@ public void testAccessModesOfFactories(Supplier memorySegmentSupp } static void tryClose(MemorySegment segment) { - if (!segment.scope().isImplicit()) { + if (segment.scope() != ResourceScope.globalScope()) { segment.scope().close(); } } @@ -142,10 +135,10 @@ public Object[][] segmentFactories() { () -> MemorySegment.ofArray(new short[] { 1, 2, 3, 4 } ), () -> MemorySegment.allocateNative(4, ResourceScope.newImplicitScope()), () -> MemorySegment.allocateNative(4, 8, ResourceScope.newImplicitScope()), - () -> MemorySegment.allocateNative(MemoryLayout.valueLayout(32, ByteOrder.nativeOrder()), ResourceScope.newImplicitScope()), - () -> MemorySegment.allocateNative(4, ResourceScope.newConfinedScope()), - () -> MemorySegment.allocateNative(4, 8, ResourceScope.newConfinedScope()), - () -> MemorySegment.allocateNative(MemoryLayout.valueLayout(32, ByteOrder.nativeOrder()), ResourceScope.newConfinedScope()) + () -> MemorySegment.allocateNative(JAVA_INT, ResourceScope.newImplicitScope()), + () -> MemorySegment.allocateNative(4, ResourceScope.newImplicitScope()), + () -> MemorySegment.allocateNative(4, 8, ResourceScope.newImplicitScope()), + () -> MemorySegment.allocateNative(JAVA_INT, ResourceScope.newImplicitScope()) ); return l.stream().map(s -> new Object[] { s }).toArray(Object[][]::new); @@ -153,8 +146,8 @@ public Object[][] segmentFactories() { @Test(dataProvider = "segmentFactories") public void testFill(Supplier memorySegmentSupplier) { - VarHandle byteHandle = MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_BYTE) - .varHandle(byte.class, MemoryLayout.PathElement.sequenceElement()); + VarHandle byteHandle = MemoryLayout.sequenceLayout(ValueLayout.JAVA_BYTE) + .varHandle(MemoryLayout.PathElement.sequenceElement()); for (byte value : new byte[] {(byte) 0xFF, (byte) 0x00, (byte) 0x45}) { MemorySegment segment = memorySegmentSupplier.get(); @@ -193,15 +186,13 @@ public void testFillClosed(Supplier memorySegmentSupplier) { } @Test(dataProvider = "segmentFactories") - public void testNativeSegments(Supplier memorySegmentSupplier) throws Exception { + public void testNativeSegments(Supplier memorySegmentSupplier) { MemorySegment segment = memorySegmentSupplier.get(); try { - segment.address().toRawLongValue(); + segment.address(); assertTrue(segment.isNative()); - assertTrue(segment.address().isNative()); } catch (UnsupportedOperationException exception) { assertFalse(segment.isNative()); - assertFalse(segment.address().isNative()); } tryClose(segment); } @@ -262,33 +253,6 @@ public Object[][] sizesAndAlignments() { }; } - @DataProvider(name = "badLayouts") - public Object[][] layouts() { - SizedLayoutFactory[] layoutFactories = SizedLayoutFactory.values(); - Object[][] values = new Object[layoutFactories.length * 2][2]; - for (int i = 0; i < layoutFactories.length ; i++) { - values[i * 2] = new Object[] { MemoryLayout.structLayout(layoutFactories[i].make(7), MemoryLayout.paddingLayout(9)) }; // good size, bad align - values[(i * 2) + 1] = new Object[] { layoutFactories[i].make(15).withBitAlignment(16) }; // bad size, good align - } - return values; - } - - enum SizedLayoutFactory { - VALUE_BE(size -> MemoryLayout.valueLayout(size, ByteOrder.BIG_ENDIAN)), - VALUE_LE(size -> MemoryLayout.valueLayout(size, ByteOrder.LITTLE_ENDIAN)), - PADDING(MemoryLayout::paddingLayout); - - private final LongFunction factory; - - SizedLayoutFactory(LongFunction factory) { - this.factory = factory; - } - - MemoryLayout make(long size) { - return factory.apply(size); - } - } - @DataProvider(name = "heapFactories") public Object[][] heapFactories() { return new Object[][] { diff --git a/test/jdk/java/foreign/TestSharedAccess.java b/test/jdk/java/foreign/TestSharedAccess.java index b5b5f717b46e5..346d92acef7ca 100644 --- a/test/jdk/java/foreign/TestSharedAccess.java +++ b/test/jdk/java/foreign/TestSharedAccess.java @@ -43,11 +43,11 @@ public class TestSharedAccess { - static final VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); + static final VarHandle intHandle = ValueLayout.JAVA_INT.varHandle(); @Test public void testShared() throws Throwable { - SequenceLayout layout = MemoryLayout.sequenceLayout(1024, MemoryLayouts.JAVA_INT); + SequenceLayout layout = MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_INT); try (ResourceScope scope = ResourceScope.newSharedScope()) { MemorySegment s = MemorySegment.allocateNative(layout, scope); for (int i = 0 ; i < layout.elementCount().getAsLong() ; i++) { @@ -98,7 +98,7 @@ public void testSharedUnsafe() throws Throwable { setInt(s, 42); assertEquals(getInt(s), 42); List threads = new ArrayList<>(); - MemorySegment sharedSegment = s.address().asSegment(s.byteSize(), scope); + MemorySegment sharedSegment = MemorySegment.ofAddress(s.address(), s.byteSize(), scope); for (int i = 0 ; i < 1000 ; i++) { threads.add(new Thread(() -> { assertEquals(getInt(sharedSegment), 42); @@ -121,7 +121,7 @@ public void testOutsideConfinementThread() throws Throwable { CountDownLatch b = new CountDownLatch(1); CompletableFuture r; try (ResourceScope scope = ResourceScope.newConfinedScope()) { - MemorySegment s1 = MemorySegment.allocateNative(MemoryLayout.sequenceLayout(2, MemoryLayouts.JAVA_INT), scope); + MemorySegment s1 = MemorySegment.allocateNative(MemoryLayout.sequenceLayout(2, ValueLayout.JAVA_INT), scope); r = CompletableFuture.runAsync(() -> { try { ByteBuffer bb = s1.asByteBuffer(); diff --git a/test/jdk/java/foreign/TestSlices.java b/test/jdk/java/foreign/TestSlices.java index da9ad9135b1ca..0cdc392cd93a9 100644 --- a/test/jdk/java/foreign/TestSlices.java +++ b/test/jdk/java/foreign/TestSlices.java @@ -23,12 +23,12 @@ */ import jdk.incubator.foreign.MemoryLayout; -import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; import java.lang.invoke.VarHandle; import jdk.incubator.foreign.ResourceScope; +import jdk.incubator.foreign.ValueLayout; import org.testng.annotations.*; import static org.testng.Assert.*; @@ -39,9 +39,9 @@ public class TestSlices { static MemoryLayout LAYOUT = MemoryLayout.sequenceLayout(2, - MemoryLayout.sequenceLayout(5, MemoryLayouts.JAVA_INT)); + MemoryLayout.sequenceLayout(5, ValueLayout.JAVA_INT)); - static VarHandle VH_ALL = LAYOUT.varHandle(int.class, + static VarHandle VH_ALL = LAYOUT.varHandle( MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement()); @Test(dataProvider = "slices") @@ -76,16 +76,16 @@ static Object[][] slices() { // x { VH_ALL, 2, 5, new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } }, // x[0::2] - { LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(), + { LAYOUT.varHandle(MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement(0, 2)), 2, 3, new int[] { 1, 3, 5, 6, 8, 10 } }, // x[1::2] - { LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(), + { LAYOUT.varHandle(MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement(1, 2)), 2, 2, new int[] { 2, 4, 7, 9 } }, // x[4::-2] - { LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(), + { LAYOUT.varHandle(MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement(4, -2)), 2, 3, new int[] { 5, 3, 1, 10, 8, 6 } }, // x[3::-2] - { LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(), + { LAYOUT.varHandle(MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement(3, -2)), 2, 2, new int[] { 4, 2, 9, 7 } }, }; } diff --git a/test/jdk/java/foreign/TestSpliterator.java b/test/jdk/java/foreign/TestSpliterator.java index d8c3fe24e0a9a..17f4ef3925904 100644 --- a/test/jdk/java/foreign/TestSpliterator.java +++ b/test/jdk/java/foreign/TestSpliterator.java @@ -27,7 +27,6 @@ */ import jdk.incubator.foreign.MemoryLayout; -import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.SequenceLayout; @@ -40,22 +39,22 @@ import java.util.concurrent.RecursiveTask; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.LongStream; -import java.util.stream.StreamSupport; +import jdk.incubator.foreign.ValueLayout; import org.testng.annotations.*; import static org.testng.Assert.*; public class TestSpliterator { - static final VarHandle INT_HANDLE = MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT) - .varHandle(int.class, MemoryLayout.PathElement.sequenceElement()); + static final VarHandle INT_HANDLE = MemoryLayout.sequenceLayout(ValueLayout.JAVA_INT) + .varHandle(MemoryLayout.PathElement.sequenceElement()); final static int CARRIER_SIZE = 4; @Test(dataProvider = "splits") public void testSum(int size, int threshold) { - SequenceLayout layout = MemoryLayout.sequenceLayout(size, MemoryLayouts.JAVA_INT); + SequenceLayout layout = MemoryLayout.sequenceLayout(size, ValueLayout.JAVA_INT); //setup try (ResourceScope scope = ResourceScope.newSharedScope()) { @@ -82,7 +81,7 @@ public void testSum(int size, int threshold) { @Test public void testSumSameThread() { - SequenceLayout layout = MemoryLayout.sequenceLayout(1024, MemoryLayouts.JAVA_INT); + SequenceLayout layout = MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_INT); //setup MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope()); @@ -100,32 +99,32 @@ public void testSumSameThread() { @Test(expectedExceptions = IllegalArgumentException.class) public void testBadSpliteratorElementSizeTooBig() { - MemorySegment.ofArray(new byte[2]).spliterator(MemoryLayouts.JAVA_INT); + MemorySegment.ofArray(new byte[2]).spliterator(ValueLayout.JAVA_INT); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadStreamElementSizeTooBig() { - MemorySegment.ofArray(new byte[2]).elements(MemoryLayouts.JAVA_INT); + MemorySegment.ofArray(new byte[2]).elements(ValueLayout.JAVA_INT); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadSpliteratorElementSizeNotMultiple() { - MemorySegment.ofArray(new byte[7]).spliterator(MemoryLayouts.JAVA_INT); + MemorySegment.ofArray(new byte[7]).spliterator(ValueLayout.JAVA_INT); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadStreamElementSizeNotMultiple() { - MemorySegment.ofArray(new byte[7]).elements(MemoryLayouts.JAVA_INT); + MemorySegment.ofArray(new byte[7]).elements(ValueLayout.JAVA_INT); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadSpliteratorElementSizeZero() { - MemorySegment.ofArray(new byte[7]).spliterator(MemoryLayout.sequenceLayout(0, MemoryLayouts.JAVA_INT)); + MemorySegment.ofArray(new byte[7]).spliterator(MemoryLayout.sequenceLayout(0, ValueLayout.JAVA_INT)); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadStreamElementSizeZero() { - MemorySegment.ofArray(new byte[7]).elements(MemoryLayout.sequenceLayout(0, MemoryLayouts.JAVA_INT)); + MemorySegment.ofArray(new byte[7]).elements(MemoryLayout.sequenceLayout(0, ValueLayout.JAVA_INT)); } static long sumSingle(long acc, MemorySegment segment) { diff --git a/test/jdk/java/foreign/TestStringEncoding.java b/test/jdk/java/foreign/TestStringEncoding.java index 90fae756ada7f..baa14db9d9bb4 100644 --- a/test/jdk/java/foreign/TestStringEncoding.java +++ b/test/jdk/java/foreign/TestStringEncoding.java @@ -22,10 +22,10 @@ * */ -import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; +import jdk.incubator.foreign.SegmentAllocator; import org.testng.annotations.*; import static org.testng.Assert.*; @@ -40,11 +40,12 @@ public class TestStringEncoding { @Test(dataProvider = "strings") public void testStrings(String testString, int expectedByteLength) { try (ResourceScope scope = ResourceScope.newConfinedScope()) { - MemorySegment text = CLinker.toCString(testString, scope); + SegmentAllocator allocator = SegmentAllocator.newNativeArena(expectedByteLength, scope); + MemorySegment text = allocator.allocateUtf8String(testString); assertEquals(text.byteSize(), expectedByteLength); - String roundTrip = CLinker.toJavaString(text); + String roundTrip = text.getUtf8String(0); assertEquals(roundTrip, testString); } } diff --git a/test/jdk/java/foreign/TestSymbolLookup.java b/test/jdk/java/foreign/TestSymbolLookup.java index 1cc537d5261e6..fa76aa89ce3f7 100644 --- a/test/jdk/java/foreign/TestSymbolLookup.java +++ b/test/jdk/java/foreign/TestSymbolLookup.java @@ -29,12 +29,12 @@ */ import jdk.incubator.foreign.SymbolLookup; -import jdk.incubator.foreign.MemoryAccess; -import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; +import jdk.incubator.foreign.ValueLayout; import org.testng.annotations.Test; +import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE; import static org.testng.Assert.*; // FYI this test is run on 64-bit platforms only for now, @@ -61,7 +61,7 @@ public void testInvalidSymbolLookup() { @Test public void testVariableSymbolLookup() { - MemorySegment segment = LOOKUP.lookup("c").get().asSegment(MemoryLayouts.JAVA_INT.byteSize(), ResourceScope.globalScope()); - assertEquals(MemoryAccess.getInt(segment), 42); + MemorySegment segment = MemorySegment.ofAddress(LOOKUP.lookup("c").get().address(), ValueLayout.JAVA_INT.byteSize(), ResourceScope.globalScope()); + assertEquals(segment.get(JAVA_BYTE, 0), 42); } } diff --git a/test/jdk/java/foreign/TestTypeAccess.java b/test/jdk/java/foreign/TestTypeAccess.java index 9683f58987f4a..dc3ee8434bdc9 100644 --- a/test/jdk/java/foreign/TestTypeAccess.java +++ b/test/jdk/java/foreign/TestTypeAccess.java @@ -27,10 +27,9 @@ * @run testng TestTypeAccess */ -import jdk.incubator.foreign.MemoryHandles; import jdk.incubator.foreign.MemorySegment; -import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.ResourceScope; +import jdk.incubator.foreign.ValueLayout; import org.testng.annotations.*; import java.lang.invoke.VarHandle; @@ -38,24 +37,17 @@ public class TestTypeAccess { - static final VarHandle INT_HANDLE = MemoryLayouts.JAVA_INT.varHandle(int.class); - - static final VarHandle ADDR_HANDLE = MemoryHandles.asAddressVarHandle(INT_HANDLE); + static final VarHandle INT_HANDLE = ValueLayout.JAVA_INT.varHandle(); + static final VarHandle ADDR_HANDLE = ValueLayout.ADDRESS.varHandle(); @Test(expectedExceptions=ClassCastException.class) public void testMemoryAddressCoordinateAsString() { - try (ResourceScope scope = ResourceScope.newConfinedScope()) { - MemorySegment s = MemorySegment.allocateNative(8, 8, scope); - int v = (int)INT_HANDLE.get("string"); - } + int v = (int)INT_HANDLE.get("string"); } @Test(expectedExceptions=WrongMethodTypeException.class) public void testMemoryCoordinatePrimitive() { - try (ResourceScope scope = ResourceScope.newConfinedScope()) { - MemorySegment s = MemorySegment.allocateNative(8, 8, scope); - int v = (int)INT_HANDLE.get(1); - } + int v = (int)INT_HANDLE.get(1); } @Test(expectedExceptions=ClassCastException.class) diff --git a/test/jdk/java/foreign/TestUnsupportedPlatform.java b/test/jdk/java/foreign/TestUnsupportedPlatform.java index 478cac72e33b8..eb1b5cf53a415 100644 --- a/test/jdk/java/foreign/TestUnsupportedPlatform.java +++ b/test/jdk/java/foreign/TestUnsupportedPlatform.java @@ -39,7 +39,7 @@ public class TestUnsupportedPlatform { @Test(expectedExceptions = ExceptionInInitializerError.class) public void testNoInitialization() { - CLinker.getInstance(); // trigger initialization + CLinker.systemCLinker(); // trigger initialization } } diff --git a/test/jdk/java/foreign/TestUpcall.java b/test/jdk/java/foreign/TestUpcall.java index 2b679e8539647..9ee7a381cae0b 100644 --- a/test/jdk/java/foreign/TestUpcall.java +++ b/test/jdk/java/foreign/TestUpcall.java @@ -23,24 +23,29 @@ */ /* - * @test + * @test id=async * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" * @modules jdk.incubator.foreign/jdk.internal.foreign * @build NativeTestHelper CallGeneratorHelper TestUpcall * * @run testng/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies * --enable-native-access=ALL-UNNAMED -Dgenerator.sample.factor=17 + * -DUPCALL_TEST_TYPE=ASYNC * TestUpcall */ +import jdk.incubator.foreign.Addressable; import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.FunctionDescriptor; +import jdk.incubator.foreign.NativeSymbol; +import jdk.incubator.foreign.SegmentAllocator; import jdk.incubator.foreign.SymbolLookup; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; +import org.testng.SkipException; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -48,22 +53,32 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.stream.Collectors; import static java.lang.invoke.MethodHandles.insertArguments; -import static jdk.incubator.foreign.CLinker.C_POINTER; import static org.testng.Assert.assertEquals; public class TestUpcall extends CallGeneratorHelper { + private enum TestType { + SCOPE, + NO_SCOPE, + ASYNC + } + + private static final TestType UPCALL_TEST_TYPE = TestType.valueOf(System.getProperty("UPCALL_TEST_TYPE")); + static { System.loadLibrary("TestUpcall"); + System.loadLibrary("AsyncInvokers"); } - static CLinker abi = CLinker.getInstance(); + static CLinker abi = CLinker.systemCLinker(); static final SymbolLookup LOOKUP = SymbolLookup.loaderLookup(); @@ -80,22 +95,29 @@ public class TestUpcall extends CallGeneratorHelper { } } - static MemoryAddress dummyStub; + static NativeSymbol dummyStub; @BeforeClass void setup() { dummyStub = abi.upcallStub(DUMMY, FunctionDescriptor.ofVoid(), ResourceScope.newImplicitScope()); } + private static void checkSelected(TestType type) { + if (UPCALL_TEST_TYPE != type) + return;//throw new SkipException("Skipping tests that were not selected"); + } + @Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class) public void testUpcalls(int count, String fName, Ret ret, List paramTypes, List fields) throws Throwable { + checkSelected(TestType.SCOPE); + List> returnChecks = new ArrayList<>(); List> argChecks = new ArrayList<>(); - MemoryAddress addr = LOOKUP.lookup(fName).get(); - MethodType mtype = methodType(ret, paramTypes, fields); - try (NativeScope scope = new NativeScope()) { - MethodHandle mh = abi.downcallHandle(addr, scope, mtype, function(ret, paramTypes, fields)); - Object[] args = makeArgs(scope.scope(), ret, paramTypes, fields, returnChecks, argChecks); + NativeSymbol addr = LOOKUP.lookup(fName).get(); + try (ResourceScope scope = ResourceScope.newConfinedScope()) { + SegmentAllocator allocator = SegmentAllocator.newNativeArena(scope); + MethodHandle mh = downcallHandle(abi, addr, allocator, function(ret, paramTypes, fields)); + Object[] args = makeArgs(scope, ret, paramTypes, fields, returnChecks, argChecks); Object[] callArgs = args; Object res = mh.invokeWithArguments(callArgs); argChecks.forEach(c -> c.accept(args)); @@ -106,29 +128,57 @@ public void testUpcalls(int count, String fName, Ret ret, List paramT } @Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class) - public void testUpcallsNoScope(int count, String fName, Ret ret, List paramTypes, List fields) throws Throwable { + public void testUpcallsAsync(int count, String fName, Ret ret, List paramTypes, List fields) throws Throwable { + checkSelected(TestType.ASYNC); List> returnChecks = new ArrayList<>(); List> argChecks = new ArrayList<>(); - MemoryAddress addr = LOOKUP.lookup(fName).get(); - MethodType mtype = methodType(ret, paramTypes, fields); - MethodHandle mh = abi.downcallHandle(addr, IMPLICIT_ALLOCATOR, mtype, function(ret, paramTypes, fields)); - Object[] args = makeArgs(ResourceScope.newImplicitScope(), ret, paramTypes, fields, returnChecks, argChecks); - Object[] callArgs = args; - Object res = mh.invokeWithArguments(callArgs); - argChecks.forEach(c -> c.accept(args)); - if (ret == Ret.NON_VOID) { - returnChecks.forEach(c -> c.accept(res)); + NativeSymbol addr = LOOKUP.lookup(fName).get(); + try (ResourceScope scope = ResourceScope.newSharedScope()) { + SegmentAllocator allocator = SegmentAllocator.newNativeArena(scope); + FunctionDescriptor descriptor = function(ret, paramTypes, fields); + MethodHandle mh = downcallHandle(abi, addr, allocator, descriptor); + Object[] args = makeArgs(ResourceScope.newImplicitScope(), ret, paramTypes, fields, returnChecks, argChecks); + + mh = mh.asSpreader(Object[].class, args.length); + mh = MethodHandles.insertArguments(mh, 0, (Object) args); + FunctionDescriptor callbackDesc = descriptor.returnLayout() + .map(FunctionDescriptor::of) + .orElse(FunctionDescriptor.ofVoid()); + NativeSymbol callback = abi.upcallStub(mh.asType(CLinker.upcallType(callbackDesc)), callbackDesc, scope); + + MethodHandle invoker = asyncInvoker(ret, ret == Ret.VOID ? null : paramTypes.get(0), fields); + + Object res = invoker.type().returnType() == MemorySegment.class + ? invoker.invoke(allocator, callback) + : invoker.invoke(callback); + argChecks.forEach(c -> c.accept(args)); + if (ret == Ret.NON_VOID) { + returnChecks.forEach(c -> c.accept(res)); + } } } - static MethodType methodType(Ret ret, List params, List fields) { - MethodType mt = ret == Ret.VOID ? - MethodType.methodType(void.class) : MethodType.methodType(paramCarrier(params.get(0).layout(fields))); - for (ParamType p : params) { - mt = mt.appendParameterTypes(paramCarrier(p.layout(fields))); + private static final Map INVOKERS = new HashMap<>(); + + private MethodHandle asyncInvoker(Ret ret, ParamType returnType, List fields) { + if (ret == Ret.VOID) { + String name = "call_async_V"; + return INVOKERS.computeIfAbsent(name, symbol -> + abi.downcallHandle( + LOOKUP.lookup(symbol).orElseThrow(), + FunctionDescriptor.ofVoid(C_POINTER))); } - mt = mt.appendParameterTypes(MemoryAddress.class); //the callback - return mt; + + String name = "call_async_" + returnType.name().charAt(0) + + (returnType == ParamType.STRUCT ? "_" + sigCode(fields) : ""); + + return INVOKERS.computeIfAbsent(name, symbol -> { + NativeSymbol invokerSymbol = LOOKUP.lookup(symbol).orElseThrow(); + MemoryLayout returnLayout = returnType.layout(fields); + FunctionDescriptor desc = FunctionDescriptor.of(returnLayout, C_POINTER); + + return abi.downcallHandle(invokerSymbol, desc); + }); } static FunctionDescriptor function(Ret ret, List params, List fields) { @@ -150,9 +200,9 @@ static Object[] makeArgs(ResourceScope scope, Ret ret, List params, L } @SuppressWarnings("unchecked") - static MemoryAddress makeCallback(ResourceScope scope, Ret ret, List params, List fields, List> checks, List> argChecks) { + static NativeSymbol makeCallback(ResourceScope scope, Ret ret, List params, List fields, List> checks, List> argChecks) { if (params.isEmpty()) { - return dummyStub.address(); + return dummyStub; } AtomicReference box = new AtomicReference<>(); @@ -162,7 +212,7 @@ static MemoryAddress makeCallback(ResourceScope scope, Ret ret, List for (int i = 0; i < params.size(); i++) { ParamType pt = params.get(i); MemoryLayout layout = pt.layout(fields); - Class carrier = paramCarrier(layout); + Class carrier = carrier(layout, false); mh = mh.asType(mh.type().changeParameterType(i, carrier)); final int finalI = i; @@ -175,7 +225,7 @@ static MemoryAddress makeCallback(ResourceScope scope, Ret ret, List ParamType firstParam = params.get(0); MemoryLayout firstlayout = firstParam.layout(fields); - Class firstCarrier = paramCarrier(firstlayout); + Class firstCarrier = carrier(firstlayout, true); if (firstCarrier == MemorySegment.class) { checks.add(o -> assertStructEquals((MemorySegment) box.get()[0], (MemorySegment) o, firstlayout)); diff --git a/test/jdk/java/foreign/TestUpcallException.java b/test/jdk/java/foreign/TestUpcallException.java index d6630a74d77ce..900b9c58513c3 100644 --- a/test/jdk/java/foreign/TestUpcallException.java +++ b/test/jdk/java/foreign/TestUpcallException.java @@ -33,9 +33,6 @@ * TestUpcallException */ -import jdk.incubator.foreign.CLinker; -import jdk.incubator.foreign.FunctionDescriptor; -import jdk.incubator.foreign.ResourceScope; import jdk.test.lib.Utils; import org.testng.annotations.Test; @@ -54,17 +51,17 @@ public class TestUpcallException { @Test public void testExceptionInterpreted() throws InterruptedException, IOException { - boolean useSpec = false; - run(useSpec); + run(/* useSpec = */ false, /* isVoid = */ true); + run(/* useSpec = */ false, /* isVoid = */ false); } @Test public void testExceptionSpecialized() throws IOException, InterruptedException { - boolean useSpec = true; - run(useSpec); + run(/* useSpec = */ true, /* isVoid = */ true); + run(/* useSpec = */ true, /* isVoid = */ false); } - private void run(boolean useSpec) throws IOException, InterruptedException { + private void run(boolean useSpec, boolean isVoid) throws IOException, InterruptedException { Process process = new ProcessBuilder() .command( Paths.get(Utils.TEST_JDK) @@ -77,7 +74,8 @@ private void run(boolean useSpec) throws IOException, InterruptedException { "-Djava.library.path=" + System.getProperty("java.library.path"), "-Djdk.internal.foreign.ProgrammableUpcallHandler.USE_SPEC=" + useSpec, "-cp", Utils.TEST_CLASS_PATH, - "ThrowingUpcall") + "ThrowingUpcall", + isVoid ? "void" : "non-void") .start(); int result = process.waitFor(); diff --git a/test/jdk/java/foreign/TestUpcallHighArity.java b/test/jdk/java/foreign/TestUpcallHighArity.java index dfe819c97e22f..b025de9ed4ad4 100644 --- a/test/jdk/java/foreign/TestUpcallHighArity.java +++ b/test/jdk/java/foreign/TestUpcallHighArity.java @@ -33,8 +33,10 @@ * TestUpcallHighArity */ +import jdk.incubator.foreign.Addressable; import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.FunctionDescriptor; +import jdk.incubator.foreign.NativeSymbol; import jdk.incubator.foreign.SymbolLookup; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; @@ -49,13 +51,12 @@ import java.util.List; import java.util.concurrent.atomic.AtomicReference; -import static jdk.incubator.foreign.CLinker.*; import static org.testng.Assert.assertEquals; public class TestUpcallHighArity extends CallGeneratorHelper { static final MethodHandle MH_do_upcall; static final MethodHandle MH_passAndSave; - static final CLinker LINKER = CLinker.getInstance(); + static final CLinker LINKER = CLinker.systemCLinker(); // struct S_PDI { void* p0; double p1; int p2; }; static final MemoryLayout S_PDI_LAYOUT = MemoryLayout.structLayout( @@ -70,12 +71,7 @@ public class TestUpcallHighArity extends CallGeneratorHelper { SymbolLookup lookup = SymbolLookup.loaderLookup(); MH_do_upcall = LINKER.downcallHandle( lookup.lookup("do_upcall").get(), - MethodType.methodType(void.class, MemoryAddress.class, - MemorySegment.class, int.class, double.class, MemoryAddress.class, - MemorySegment.class, int.class, double.class, MemoryAddress.class, - MemorySegment.class, int.class, double.class, MemoryAddress.class, - MemorySegment.class, int.class, double.class, MemoryAddress.class), - FunctionDescriptor.ofVoid(C_POINTER, + FunctionDescriptor.ofVoid(C_POINTER, S_PDI_LAYOUT, C_INT, C_DOUBLE, C_POINTER, S_PDI_LAYOUT, C_INT, C_DOUBLE, C_POINTER, S_PDI_LAYOUT, C_INT, C_DOUBLE, C_POINTER, @@ -108,9 +104,9 @@ public void testUpcall(MethodHandle downcall, MethodType upcallType, .asCollector(Object[].class, upcallType.parameterCount()) .asType(upcallType); try (ResourceScope scope = ResourceScope.newConfinedScope()) { - MemoryAddress upcallStub = LINKER.upcallStub(target, upcallDescriptor, scope); + NativeSymbol upcallStub = LINKER.upcallStub(target, upcallDescriptor, scope); Object[] args = new Object[upcallType.parameterCount() + 1]; - args[0] = upcallStub.address(); + args[0] = upcallStub; List argLayouts = upcallDescriptor.argumentLayouts(); for (int i = 1; i < args.length; i++) { args[i] = makeArg(argLayouts.get(i - 1), null, false); @@ -123,7 +119,7 @@ public void testUpcall(MethodHandle downcall, MethodType upcallType, if (upcallType.parameterType(i) == MemorySegment.class) { assertStructEquals((MemorySegment) capturedArgsArr[i], (MemorySegment) args[i + 1], argLayouts.get(i)); } else { - assertEquals(capturedArgsArr[i], args[i + 1]); + assertEquals(capturedArgsArr[i], args[i + 1], "For index " + i); } } } diff --git a/test/jdk/java/foreign/TestUpcallStructScope.java b/test/jdk/java/foreign/TestUpcallStructScope.java index 72bb1eafb9f09..045d6191a9d50 100644 --- a/test/jdk/java/foreign/TestUpcallStructScope.java +++ b/test/jdk/java/foreign/TestUpcallStructScope.java @@ -37,8 +37,10 @@ * TestUpcallStructScope */ +import jdk.incubator.foreign.Addressable; import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.FunctionDescriptor; +import jdk.incubator.foreign.NativeSymbol; import jdk.incubator.foreign.SymbolLookup; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; @@ -52,14 +54,11 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; -import static jdk.incubator.foreign.CLinker.C_DOUBLE; -import static jdk.incubator.foreign.CLinker.C_INT; -import static jdk.incubator.foreign.CLinker.C_POINTER; import static org.testng.Assert.assertFalse; -public class TestUpcallStructScope { +public class TestUpcallStructScope extends NativeTestHelper { static final MethodHandle MH_do_upcall; - static final CLinker LINKER = CLinker.getInstance(); + static final CLinker LINKER = CLinker.systemCLinker(); static final MethodHandle MH_Consumer_accept; // struct S_PDI { void* p0; double p1; int p2; }; @@ -74,8 +73,7 @@ public class TestUpcallStructScope { SymbolLookup lookup = SymbolLookup.loaderLookup(); MH_do_upcall = LINKER.downcallHandle( lookup.lookup("do_upcall").get(), - MethodType.methodType(void.class, MemoryAddress.class, MemorySegment.class), - FunctionDescriptor.ofVoid(C_POINTER, S_PDI_LAYOUT) + FunctionDescriptor.ofVoid(C_POINTER, S_PDI_LAYOUT) ); try { @@ -96,9 +94,9 @@ public void testUpcall() throws Throwable { MethodHandle target = methodHandle(capturedSegment::set); FunctionDescriptor upcallDesc = FunctionDescriptor.ofVoid(S_PDI_LAYOUT); try (ResourceScope scope = ResourceScope.newConfinedScope()) { - MemoryAddress upcallStub = LINKER.upcallStub(target, upcallDesc, scope); + NativeSymbol upcallStub = LINKER.upcallStub(target, upcallDesc, scope); MemorySegment argSegment = MemorySegment.allocateNative(S_PDI_LAYOUT, scope); - MH_do_upcall.invokeExact(upcallStub.address(), argSegment); + MH_do_upcall.invoke(upcallStub, argSegment); } MemorySegment captured = capturedSegment.get(); diff --git a/test/jdk/java/foreign/TestVarArgs.java b/test/jdk/java/foreign/TestVarArgs.java index 40ccea4e6845c..0efc22d751460 100644 --- a/test/jdk/java/foreign/TestVarArgs.java +++ b/test/jdk/java/foreign/TestVarArgs.java @@ -33,6 +33,7 @@ import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.NativeSymbol; import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.SymbolLookup; import jdk.incubator.foreign.ValueLayout; @@ -45,27 +46,26 @@ import java.util.ArrayList; import java.util.List; -import static jdk.incubator.foreign.CLinker.*; import static jdk.incubator.foreign.MemoryLayout.PathElement.*; import static org.testng.Assert.assertEquals; -public class TestVarArgs { +public class TestVarArgs extends NativeTestHelper { static final MemoryLayout ML_CallInfo = MemoryLayout.structLayout( C_POINTER.withName("writeback"), // writeback C_POINTER.withName("argIDs")); // arg ids - static final VarHandle VH_CallInfo_writeback = ML_CallInfo.varHandle(long.class, groupElement("writeback")); - static final VarHandle VH_CallInfo_argIDs = ML_CallInfo.varHandle(long.class, groupElement("argIDs")); + static final VarHandle VH_CallInfo_writeback = ML_CallInfo.varHandle(groupElement("writeback")); + static final VarHandle VH_CallInfo_argIDs = ML_CallInfo.varHandle(groupElement("argIDs")); - static final VarHandle VH_IntArray = MemoryLayout.sequenceLayout(C_INT).varHandle(int.class, sequenceElement()); + static final VarHandle VH_IntArray = MemoryLayout.sequenceLayout(C_INT).varHandle(sequenceElement()); - static final CLinker abi = CLinker.getInstance(); + static final CLinker abi = CLinker.systemCLinker(); static { System.loadLibrary("VarArgs"); } - static final MemoryAddress VARARGS_ADDR = + static final NativeSymbol VARARGS_ADDR = SymbolLookup.loaderLookup() .lookup("varargs").get(); @@ -80,8 +80,8 @@ public void testVarArgs(List args) throws Throwable { MemoryAddress callInfoPtr = callInfo.address(); - VH_CallInfo_writeback.set(callInfo, writeBack.address().toRawLongValue()); - VH_CallInfo_argIDs.set(callInfo, argIDs.address().toRawLongValue()); + VH_CallInfo_writeback.set(callInfo, writeBack.address()); + VH_CallInfo_argIDs.set(callInfo, argIDs.address()); for (int i = 0; i < args.size(); i++) { VH_IntArray.set(argIDs, (long) i, args.get(i).id.ordinal()); @@ -90,9 +90,9 @@ public void testVarArgs(List args) throws Throwable { List argLayouts = new ArrayList<>(); argLayouts.add(C_POINTER); // call info argLayouts.add(C_INT); // size - args.forEach(a -> argLayouts.add(asVarArg(a.layout))); - FunctionDescriptor desc = FunctionDescriptor.ofVoid(argLayouts.toArray(MemoryLayout[]::new)); + FunctionDescriptor desc = FunctionDescriptor.ofVoid(argLayouts.stream().toArray(MemoryLayout[]::new)) + .asVariadic(args.stream().map(a -> a.layout).toArray(MemoryLayout[]::new)); List> carriers = new ArrayList<>(); carriers.add(MemoryAddress.class); // call info @@ -101,7 +101,7 @@ public void testVarArgs(List args) throws Throwable { MethodType mt = MethodType.methodType(void.class, carriers); - MethodHandle downcallHandle = abi.downcallHandle(VARARGS_ADDR, mt, desc); + MethodHandle downcallHandle = abi.downcallHandle(VARARGS_ADDR, desc); List argValues = new ArrayList<>(); argValues.add(callInfoPtr); // call info @@ -140,7 +140,7 @@ private VarArg(NativeType id, ValueLayout layout, Class carrier, Object value this.value = value; this.layout = layout; this.carrier = carrier; - this.vh = layout.varHandle(carrier); + this.vh = layout.varHandle(); } static VarArg intArg(int value) { diff --git a/test/jdk/java/foreign/TestVarHandleCombinators.java b/test/jdk/java/foreign/TestVarHandleCombinators.java index 2090c31fdd9ad..e9beaebafa84c 100644 --- a/test/jdk/java/foreign/TestVarHandleCombinators.java +++ b/test/jdk/java/foreign/TestVarHandleCombinators.java @@ -29,6 +29,7 @@ import jdk.incubator.foreign.MemoryHandles; import jdk.incubator.foreign.ResourceScope; +import jdk.incubator.foreign.ValueLayout; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -43,7 +44,7 @@ public class TestVarHandleCombinators { @Test public void testElementAccess() { - VarHandle vh = MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder()); + VarHandle vh = MemoryHandles.varHandle(ValueLayout.JAVA_BYTE); byte[] arr = { 0, 0, -1, 0 }; MemorySegment segment = MemorySegment.ofArray(arr); @@ -52,7 +53,7 @@ public void testElementAccess() { @Test(expectedExceptions = IllegalStateException.class) public void testUnalignedElement() { - VarHandle vh = MemoryHandles.varHandle(byte.class, 4, ByteOrder.nativeOrder()); + VarHandle vh = MemoryHandles.varHandle(ValueLayout.JAVA_BYTE.withBitAlignment(32)); MemorySegment segment = MemorySegment.ofArray(new byte[4]); vh.get(segment, 2L); //should throw //FIXME: the VH only checks the alignment of the segment, which is fine if the VH is derived from layouts, @@ -60,19 +61,9 @@ public void testUnalignedElement() { //FIXME: at least until the VM is fixed } - @Test(expectedExceptions = IllegalArgumentException.class) - public void testAlignNotPowerOf2() { - VarHandle vh = MemoryHandles.varHandle(byte.class, 3, ByteOrder.nativeOrder()); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void testAlignNegative() { - VarHandle vh = MemoryHandles.varHandle(byte.class, -1, ByteOrder.nativeOrder()); - } - @Test public void testAlign() { - VarHandle vh = MemoryHandles.varHandle(byte.class, 2, ByteOrder.nativeOrder()); + VarHandle vh = MemoryHandles.varHandle(ValueLayout.JAVA_BYTE.withBitAlignment(16)); MemorySegment segment = MemorySegment.allocateNative(1, 2, ResourceScope.newImplicitScope()); vh.set(segment, 0L, (byte) 10); // fine, memory region is aligned @@ -81,7 +72,7 @@ public void testAlign() { @Test public void testByteOrderLE() { - VarHandle vh = MemoryHandles.varHandle(short.class, 2, ByteOrder.LITTLE_ENDIAN); + VarHandle vh = MemoryHandles.varHandle(ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN)); byte[] arr = new byte[2]; MemorySegment segment = MemorySegment.ofArray(arr); vh.set(segment, 0L, (short) 0xFF); @@ -91,7 +82,7 @@ public void testByteOrderLE() { @Test public void testByteOrderBE() { - VarHandle vh = MemoryHandles.varHandle(short.class, 2, ByteOrder.BIG_ENDIAN); + VarHandle vh = MemoryHandles.varHandle(ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN)); byte[] arr = new byte[2]; MemorySegment segment = MemorySegment.ofArray(arr); vh.set(segment, 0L, (short) 0xFF); @@ -106,7 +97,7 @@ public void testNestedSequenceAccess() { //[10 : [5 : [x32 i32]]] - VarHandle vh = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder()); + VarHandle vh = MemoryHandles.varHandle(ValueLayout.JAVA_INT.withBitAlignment(32)); int count = 0; try (ResourceScope scope = ResourceScope.newConfinedScope()) { MemorySegment segment = MemorySegment.allocateNative(inner_size * outer_size * 8, 4, scope); @@ -121,21 +112,4 @@ public void testNestedSequenceAccess() { } } } - - @Test(dataProvider = "badCarriers", expectedExceptions = IllegalArgumentException.class) - public void testBadCarrier(Class carrier) { - MemoryHandles.varHandle(carrier, ByteOrder.nativeOrder()); - } - - @DataProvider(name = "badCarriers") - public Object[][] createBadCarriers() { - return new Object[][] { - { void.class }, - { boolean.class }, - { Object.class }, - { int[].class }, - { MemorySegment.class } - }; - } - } diff --git a/test/jdk/java/foreign/ThrowingUpcall.java b/test/jdk/java/foreign/ThrowingUpcall.java index 405a89fc2f4c6..9efbf68565c35 100644 --- a/test/jdk/java/foreign/ThrowingUpcall.java +++ b/test/jdk/java/foreign/ThrowingUpcall.java @@ -23,29 +23,30 @@ import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.FunctionDescriptor; -import jdk.incubator.foreign.MemoryAddress; +import jdk.incubator.foreign.NativeSymbol; import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.SymbolLookup; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; -import java.security.Permission; -import static jdk.incubator.foreign.CLinker.C_POINTER; +public class ThrowingUpcall extends NativeTestHelper { -public class ThrowingUpcall { - - private static final MethodHandle downcall; + private static final MethodHandle downcallVoid; + private static final MethodHandle downcallNonVoid; public static final MethodHandle MH_throwException; static { System.loadLibrary("TestUpcall"); SymbolLookup lookup = SymbolLookup.loaderLookup(); - downcall = CLinker.getInstance().downcallHandle( + downcallVoid = CLinker.systemCLinker().downcallHandle( lookup.lookup("f0_V__").orElseThrow(), - MethodType.methodType(void.class, MemoryAddress.class), - FunctionDescriptor.ofVoid(C_POINTER) + FunctionDescriptor.ofVoid(C_POINTER) + ); + downcallNonVoid = CLinker.systemCLinker().downcallHandle( + lookup.lookup("f10_I_I_").orElseThrow(), + FunctionDescriptor.of(C_INT, C_INT, C_POINTER) ); try { @@ -61,18 +62,35 @@ public static void throwException() throws Throwable { } public static void main(String[] args) throws Throwable { - test(); + if (args[0].equals("void")) { + testVoid(); + } else { + testNonVoid(); + } } - public static void test() throws Throwable { + public static void testVoid() throws Throwable { MethodHandle handle = MH_throwException; MethodHandle invoker = MethodHandles.exactInvoker(MethodType.methodType(void.class)); handle = MethodHandles.insertArguments(invoker, 0, handle); try (ResourceScope scope = ResourceScope.newConfinedScope()) { - MemoryAddress stub = CLinker.getInstance().upcallStub(handle, FunctionDescriptor.ofVoid(), scope); + NativeSymbol stub = CLinker.systemCLinker().upcallStub(handle, FunctionDescriptor.ofVoid(), scope); + + downcallVoid.invoke(stub); // should call Shutdown.exit(1); + } + } + + public static void testNonVoid() throws Throwable { + MethodHandle handle = MethodHandles.identity(int.class); + handle = MethodHandles.collectArguments(handle, 0, MH_throwException); + MethodHandle invoker = MethodHandles.exactInvoker(MethodType.methodType(int.class, int.class)); + handle = MethodHandles.insertArguments(invoker, 0, handle); + + try (ResourceScope scope = ResourceScope.newConfinedScope()) { + NativeSymbol stub = CLinker.systemCLinker().upcallStub(handle, FunctionDescriptor.of(C_INT, C_INT), scope); - downcall.invokeExact(stub); // should call Shutdown.exit(1); + downcallNonVoid.invoke(42, stub); // should call Shutdown.exit(1); } } diff --git a/test/jdk/java/foreign/callarranger/TestAarch64CallArranger.java b/test/jdk/java/foreign/callarranger/TestAarch64CallArranger.java index eef17a4aad18d..382b303f89b89 100644 --- a/test/jdk/java/foreign/callarranger/TestAarch64CallArranger.java +++ b/test/jdk/java/foreign/callarranger/TestAarch64CallArranger.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,7 +56,7 @@ public class TestAarch64CallArranger extends CallArrangerTestBase { public void testEmpty() { MethodType mt = MethodType.methodType(void.class); FunctionDescriptor fd = FunctionDescriptor.ofVoid(); - CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); + CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); assertFalse(bindings.isInMemoryReturn); CallingSequence callingSequence = bindings.callingSequence; @@ -78,7 +78,7 @@ public void testInteger() { C_INT, C_INT, C_INT, C_INT, C_INT, C_INT, C_INT, C_INT, C_INT, C_INT); - CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); + CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); assertFalse(bindings.isInMemoryReturn); CallingSequence callingSequence = bindings.callingSequence; @@ -103,11 +103,11 @@ public void testInteger() { @Test public void testTwoIntTwoFloat() { - MethodType mt = MethodType.methodType(void.class, + MethodType mt = MethodType.methodType(void.class, int.class, int.class, float.class, float.class); FunctionDescriptor fd = FunctionDescriptor.ofVoid( C_INT, C_INT, C_FLOAT, C_FLOAT); - CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); + CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); assertFalse(bindings.isInMemoryReturn); CallingSequence callingSequence = bindings.callingSequence; @@ -128,7 +128,7 @@ public void testTwoIntTwoFloat() { public void testStruct(MemoryLayout struct, Binding[] expectedBindings) { MethodType mt = MethodType.methodType(void.class, MemorySegment.class); FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct); - CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); + CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); assertFalse(bindings.isInMemoryReturn); CallingSequence callingSequence = bindings.callingSequence; @@ -157,8 +157,7 @@ public static Object[][] structs() { // struct s { int32_t a, b; double c; int32_t d }; { struct2, new Binding[] { copy(struct2), - baseAddress(), - unboxAddress(), + unboxAddress(MemorySegment.class), vmStore(r0, long.class) }}, // struct s { int32_t a[2]; float b[2] }; @@ -188,7 +187,7 @@ public void testMultipleStructs() { MethodType mt = MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class, int.class); FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct1, struct2, C_INT); - CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); + CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); assertFalse(bindings.isInMemoryReturn); CallingSequence callingSequence = bindings.callingSequence; @@ -198,14 +197,12 @@ public void testMultipleStructs() { checkArgumentBindings(callingSequence, new Binding[][]{ { copy(struct1), - baseAddress(), - unboxAddress(), + unboxAddress(MemorySegment.class), vmStore(r0, long.class) }, { copy(struct2), - baseAddress(), - unboxAddress(), + unboxAddress(MemorySegment.class), vmStore(r1, long.class) }, { vmStore(r2, int.class) } @@ -220,7 +217,7 @@ public void testReturnStruct1() { MethodType mt = MethodType.methodType(MemorySegment.class); FunctionDescriptor fd = FunctionDescriptor.of(struct); - CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); + CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); assertTrue(bindings.isInMemoryReturn); CallingSequence callingSequence = bindings.callingSequence; @@ -243,7 +240,7 @@ public void testReturnStruct2() { MethodType mt = MethodType.methodType(MemorySegment.class); FunctionDescriptor fd = FunctionDescriptor.of(struct); - CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); + CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); assertFalse(bindings.isInMemoryReturn); CallingSequence callingSequence = bindings.callingSequence; @@ -269,7 +266,7 @@ public void testStructHFA1() { MethodType mt = MethodType.methodType(MemorySegment.class, float.class, int.class, MemorySegment.class); FunctionDescriptor fd = FunctionDescriptor.of(hfa, C_FLOAT, C_INT, hfa); - CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); + CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); assertFalse(bindings.isInMemoryReturn); CallingSequence callingSequence = bindings.callingSequence; @@ -305,7 +302,7 @@ public void testStructHFA3() { MethodType mt = MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class, MemorySegment.class); FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct, struct, struct); - CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); + CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); assertFalse(bindings.isInMemoryReturn); CallingSequence callingSequence = bindings.callingSequence; @@ -358,7 +355,7 @@ public void testStructStackSpill() { int.class, int.class, int.class, int.class, MemorySegment.class, int.class); FunctionDescriptor fd = FunctionDescriptor.ofVoid( struct, struct, C_INT, C_INT, C_INT, C_INT, C_INT, C_INT, struct, C_INT); - CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); + CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); assertFalse(bindings.isInMemoryReturn); CallingSequence callingSequence = bindings.callingSequence; @@ -366,18 +363,60 @@ public void testStructStackSpill() { assertEquals(callingSequence.functionDesc(), fd); checkArgumentBindings(callingSequence, new Binding[][]{ - { copy(struct), baseAddress(), unboxAddress(), vmStore(r0, long.class) }, - { copy(struct), baseAddress(), unboxAddress(), vmStore(r1, long.class) }, + { copy(struct), unboxAddress(MemorySegment.class), vmStore(r0, long.class) }, + { copy(struct), unboxAddress(MemorySegment.class), vmStore(r1, long.class) }, { vmStore(r2, int.class) }, { vmStore(r3, int.class) }, { vmStore(r4, int.class) }, { vmStore(r5, int.class) }, { vmStore(r6, int.class) }, { vmStore(r7, int.class) }, - { copy(struct), baseAddress(), unboxAddress(), vmStore(stackStorage(0), long.class) }, + { copy(struct), unboxAddress(MemorySegment.class), vmStore(stackStorage(0), long.class) }, { vmStore(stackStorage(1), int.class) }, }); checkReturnBindings(callingSequence, new Binding[]{}); } + + @Test + public void testVarArgsInRegs() { + MethodType mt = MethodType.methodType(void.class, int.class, int.class, float.class); + FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_INT).asVariadic(C_INT, C_FLOAT); + CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); + + assertFalse(bindings.isInMemoryReturn); + CallingSequence callingSequence = bindings.callingSequence; + assertEquals(callingSequence.methodType(), mt); + assertEquals(callingSequence.functionDesc(), fd); + + // This is identical to the non-variadic calling sequence + checkArgumentBindings(callingSequence, new Binding[][]{ + { vmStore(r0, int.class) }, + { vmStore(r1, int.class) }, + { vmStore(v0, float.class) }, + }); + + checkReturnBindings(callingSequence, new Binding[]{}); + } + + @Test + public void testVarArgsOnStack() { + MethodType mt = MethodType.methodType(void.class, int.class, int.class, float.class); + FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_INT).asVariadic(C_INT, C_FLOAT); + CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false); + + assertFalse(bindings.isInMemoryReturn); + CallingSequence callingSequence = bindings.callingSequence; + assertEquals(callingSequence.methodType(), mt); + assertEquals(callingSequence.functionDesc(), fd); + + // The two variadic arguments should be allocated on the stack + checkArgumentBindings(callingSequence, new Binding[][]{ + { vmStore(r0, int.class) }, + { vmStore(stackStorage(0), int.class) }, + { vmStore(stackStorage(1), float.class) }, + }); + + checkReturnBindings(callingSequence, new Binding[]{}); + } } diff --git a/test/jdk/java/foreign/callarranger/TestWindowsCallArranger.java b/test/jdk/java/foreign/callarranger/TestWindowsCallArranger.java index cb85c6f55e8a5..98ad5f4befbcc 100644 --- a/test/jdk/java/foreign/callarranger/TestWindowsCallArranger.java +++ b/test/jdk/java/foreign/callarranger/TestWindowsCallArranger.java @@ -156,8 +156,7 @@ public void testAbiExample() { { vmStore(rdx, int.class) }, { copy(structLayout), - baseAddress(), - unboxAddress(), + unboxAddress(MemorySegment.class), vmStore(r8, long.class) }, { vmStore(r9, int.class) }, @@ -178,7 +177,7 @@ public void testAbiExampleVarargs() { MethodType mt = MethodType.methodType(void.class, int.class, double.class, int.class, double.class, double.class); FunctionDescriptor fd = FunctionDescriptor.ofVoid( - C_INT, C_DOUBLE, asVarArg(C_INT), asVarArg(C_DOUBLE), asVarArg(C_DOUBLE)); + C_INT, C_DOUBLE).asVariadic(C_INT, C_DOUBLE, C_DOUBLE); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); assertFalse(bindings.isInMemoryReturn); @@ -251,8 +250,7 @@ public void testStructReference() { checkArgumentBindings(callingSequence, new Binding[][]{ { copy(struct), - baseAddress(), - unboxAddress(), + unboxAddress(MemorySegment.class), vmStore(rcx, long.class) } }); @@ -350,19 +348,19 @@ public void testStackStruct() { assertEquals(callingSequence.functionDesc(), fd); checkArgumentBindings(callingSequence, new Binding[][]{ - { copy(struct), baseAddress(), unboxAddress(), vmStore(rcx, long.class) }, + { copy(struct), unboxAddress(MemorySegment.class), vmStore(rcx, long.class) }, { vmStore(rdx, int.class) }, { vmStore(xmm2, double.class) }, { unboxAddress(), vmStore(r9, long.class) }, - { copy(struct), baseAddress(), unboxAddress(), vmStore(stackStorage(0), long.class) }, + { copy(struct), unboxAddress(MemorySegment.class), vmStore(stackStorage(0), long.class) }, { vmStore(stackStorage(1), int.class) }, { vmStore(stackStorage(2), double.class) }, { unboxAddress(), vmStore(stackStorage(3), long.class) }, - { copy(struct), baseAddress(), unboxAddress(), vmStore(stackStorage(4), long.class) }, + { copy(struct), unboxAddress(MemorySegment.class), vmStore(stackStorage(4), long.class) }, { vmStore(stackStorage(5), int.class) }, { vmStore(stackStorage(6), double.class) }, { unboxAddress(), vmStore(stackStorage(7), long.class) }, - { copy(struct), baseAddress(), unboxAddress(), vmStore(stackStorage(8), long.class) }, + { copy(struct), unboxAddress(MemorySegment.class), vmStore(stackStorage(8), long.class) }, { vmStore(stackStorage(9), int.class) }, { vmStore(stackStorage(10), double.class) }, { unboxAddress(), vmStore(stackStorage(11), long.class) }, diff --git a/test/jdk/java/foreign/channels/AbstractChannelsTest.java b/test/jdk/java/foreign/channels/AbstractChannelsTest.java index 81c6f0bf1932c..be0fb0afe8b2e 100644 --- a/test/jdk/java/foreign/channels/AbstractChannelsTest.java +++ b/test/jdk/java/foreign/channels/AbstractChannelsTest.java @@ -22,18 +22,19 @@ */ import java.io.IOException; -import java.lang.ref.Cleaner; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Random; import java.util.concurrent.ExecutionException; import java.util.function.Supplier; import java.util.stream.Stream; -import jdk.incubator.foreign.MemoryAccess; + import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; import jdk.test.lib.RandomFactory; import org.testng.annotations.*; + +import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE; import static org.testng.Assert.*; /** @@ -51,7 +52,7 @@ interface ThrowingConsumer { } static ResourceScope closeableScopeOrNull(ResourceScope scope) { - if (scope.isImplicit()) + if (scope == ResourceScope.globalScope()) return null; return scope; } @@ -75,7 +76,7 @@ static ByteBuffer[] clear(ByteBuffer[] buffers) { static ByteBuffer segmentBufferOfSize(ResourceScope scope, int size) { var segment = MemorySegment.allocateNative(size, 1, scope); for (int i = 0; i < size; i++) { - MemoryAccess.setByteAtOffset(segment, i, ((byte)RANDOM.nextInt())); + segment.set(JAVA_BYTE, i, ((byte)RANDOM.nextInt())); } return segment.asByteBuffer(); } @@ -129,7 +130,6 @@ static void assertCauses(Throwable ex, Class... exceptions) public static Object[][] confinedScopes() { return new Object[][] { { ScopeSupplier.NEW_CONFINED }, - { ScopeSupplier.NEW_CONFINED_EXPLICIT }, }; } @@ -137,7 +137,6 @@ public static Object[][] confinedScopes() { public static Object[][] sharedScopes() { return new Object[][] { { ScopeSupplier.NEW_SHARED }, - { ScopeSupplier.NEW_SHARED_EXPLICIT }, }; } @@ -151,7 +150,6 @@ public static Object[][] closeableScopes() { @DataProvider(name = "implicitScopes") public static Object[][] implicitScopes() { return new Object[][] { - { ScopeSupplier.NEW_IMPLICIT }, { ScopeSupplier.GLOBAL }, }; } @@ -174,26 +172,20 @@ public static Object[][] allScopes() { public static Object[][] sharedScopesAndTimeouts() { return new Object[][] { { ScopeSupplier.NEW_SHARED , 0 }, - { ScopeSupplier.NEW_SHARED_EXPLICIT , 0 }, { ScopeSupplier.NEW_SHARED , 30 }, - { ScopeSupplier.NEW_SHARED_EXPLICIT , 30 }, }; } static class ScopeSupplier implements Supplier { static final Supplier NEW_CONFINED = - new ScopeSupplier(() -> ResourceScope.newConfinedScope(), "newConfinedScope()"); - static final Supplier NEW_CONFINED_EXPLICIT = - new ScopeSupplier(() -> ResourceScope.newConfinedScope(Cleaner.create()), "newConfinedScope(Cleaner)"); + new ScopeSupplier(ResourceScope::newConfinedScope, "newConfinedScope()"); static final Supplier NEW_SHARED = - new ScopeSupplier(() -> ResourceScope.newSharedScope(), "newSharedScope()"); - static final Supplier NEW_SHARED_EXPLICIT = - new ScopeSupplier(() -> ResourceScope.newSharedScope(Cleaner.create()), "newSharedScope(Cleaner)"); + new ScopeSupplier(ResourceScope::newSharedScope, "newSharedScope()"); static final Supplier NEW_IMPLICIT = - new ScopeSupplier(() -> ResourceScope.newImplicitScope(), "newImplicitScope()"); + new ScopeSupplier(ResourceScope::newImplicitScope, "newImplicitScope()"); static final Supplier GLOBAL = - new ScopeSupplier(() -> ResourceScope.globalScope(), "globalScope()"); + new ScopeSupplier(ResourceScope::globalScope, "globalScope()"); private final Supplier supplier; private final String str; diff --git a/test/jdk/java/foreign/channels/TestAsyncSocketChannels.java b/test/jdk/java/foreign/channels/TestAsyncSocketChannels.java index 1189600ddfe6d..f3d4a2cee4666 100644 --- a/test/jdk/java/foreign/channels/TestAsyncSocketChannels.java +++ b/test/jdk/java/foreign/channels/TestAsyncSocketChannels.java @@ -48,12 +48,13 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Supplier; -import jdk.incubator.foreign.MemoryAccess; + import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; import org.testng.annotations.*; import static java.lang.System.out; import static java.util.concurrent.TimeUnit.SECONDS; +import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE; import static org.testng.Assert.*; /** @@ -162,7 +163,7 @@ public void testBasicIOWithSupportedScope(Supplier scopeSupplier) MemorySegment segment1 = MemorySegment.allocateNative(10, 1, scope); MemorySegment segment2 = MemorySegment.allocateNative(10, 1, scope); for (int i = 0; i < 10; i++) { - MemoryAccess.setByteAtOffset(segment1, i, (byte) i); + segment1.set(JAVA_BYTE, i, (byte) i); } { // Future variants ByteBuffer bb1 = segment1.asByteBuffer(); @@ -221,7 +222,7 @@ public void testCloseWithOutstandingRead(Supplier scopeSupplier, ioOp.accept(handler); assertFalse(handler.isDone()); assertTrue(scope.isAlive()); - assertMessage(expectThrows(ISE, () -> scope.close()), "Scope is acquired by"); + assertMessage(expectThrows(ISE, () -> scope.close()), "Scope is kept alive by"); // write to allow the blocking read complete, which will // in turn unlock the scope and allow it to be closed. @@ -270,7 +271,7 @@ public void completed(Long result, Void att) { // give time for socket buffer to fill up. awaitNoFurtherWrites(bytesWritten); - assertMessage(expectThrows(ISE, () -> scope.close()), "Scope is acquired by"); + assertMessage(expectThrows(ISE, () -> scope.close()), "Scope is kept alive by"); assertTrue(scope.isAlive()); // signal handler to stop further writing diff --git a/test/jdk/java/foreign/channels/TestSocketChannels.java b/test/jdk/java/foreign/channels/TestSocketChannels.java index 3ca3e2dc11aa7..7cbb624fd8752 100644 --- a/test/jdk/java/foreign/channels/TestSocketChannels.java +++ b/test/jdk/java/foreign/channels/TestSocketChannels.java @@ -40,10 +40,12 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; import java.util.stream.Stream; -import jdk.incubator.foreign.MemoryAccess; + import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; import org.testng.annotations.*; + +import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE; import static org.testng.Assert.*; /** @@ -101,7 +103,7 @@ public void testBasicIO(Supplier scopeSupplier) MemorySegment segment1 = MemorySegment.allocateNative(10, 1, scope); MemorySegment segment2 = MemorySegment.allocateNative(10, 1, scope); for (int i = 0; i < 10; i++) { - MemoryAccess.setByteAtOffset(segment1, i, (byte) i); + segment1.set(JAVA_BYTE, i, (byte) i); } ByteBuffer bb1 = segment1.asByteBuffer(); ByteBuffer bb2 = segment2.asByteBuffer(); @@ -119,7 +121,7 @@ public void testBasicHeapIOWithGlobalScope() throws Exception { var segment1 = MemorySegment.ofArray(new byte[10]); var segment2 = MemorySegment.ofArray(new byte[10]); for (int i = 0; i < 10; i++) { - MemoryAccess.setByteAtOffset(segment1, i, (byte) i); + segment1.set(JAVA_BYTE, i, (byte) i); } ByteBuffer bb1 = segment1.asByteBuffer(); ByteBuffer bb2 = segment2.asByteBuffer(); diff --git a/test/jdk/java/foreign/enablenativeaccess/org/openjdk/foreigntest/PanamaMainUnnamedModule.java b/test/jdk/java/foreign/enablenativeaccess/org/openjdk/foreigntest/PanamaMainUnnamedModule.java index bf32954eee3ae..789d70b139782 100644 --- a/test/jdk/java/foreign/enablenativeaccess/org/openjdk/foreigntest/PanamaMainUnnamedModule.java +++ b/test/jdk/java/foreign/enablenativeaccess/org/openjdk/foreigntest/PanamaMainUnnamedModule.java @@ -32,26 +32,26 @@ public class PanamaMainUnnamedModule { @Test public void testReflection() throws Throwable { - Method method = CLinker.class.getDeclaredMethod("getInstance"); + Method method = CLinker.class.getDeclaredMethod("systemCLinker"); method.invoke(null); } @Test public void testSetAccessible() throws Throwable { - Method method = CLinker.class.getDeclaredMethod("getInstance"); + Method method = CLinker.class.getDeclaredMethod("systemCLinker"); method.setAccessible(true); method.invoke(null); } @Test public void testInvoke() throws Throwable { - var mh = MethodHandles.lookup().findStatic(CLinker.class, "getInstance", + var mh = MethodHandles.lookup().findStatic(CLinker.class, "systemCLinker", MethodType.methodType(CLinker.class)); var linker = (CLinker)mh.invokeExact(); } @Test public void testDirectAccess() throws Throwable { - CLinker.getInstance(); + CLinker.systemCLinker(); } } diff --git a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMain.java b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMain.java index b4416f87808f0..82c1e50085399 100644 --- a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMain.java +++ b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMain.java @@ -28,7 +28,7 @@ public class PanamaMain { public static void main(String[] args) { System.out.println("Trying to get CLinker"); - CLinker.getInstance(); + CLinker.systemCLinker(); System.out.println("Got CLinker"); } } diff --git a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainInvoke.java b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainInvoke.java index 6a5a2bd4a9230..46bf5a2c8d2ea 100644 --- a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainInvoke.java +++ b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainInvoke.java @@ -28,7 +28,7 @@ public class PanamaMainInvoke { public static void main(String[] args) throws Throwable { - var mh = MethodHandles.lookup().findStatic(CLinker.class, "getInstance", + var mh = MethodHandles.lookup().findStatic(CLinker.class, "systemCLinker", MethodType.methodType(CLinker.class)); var linker = (CLinker)mh.invokeExact(); } diff --git a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainReflection.java b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainReflection.java index 0ddd28b97ee1e..8206aab123bae 100644 --- a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainReflection.java +++ b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainReflection.java @@ -28,7 +28,7 @@ public class PanamaMainReflection { public static void main(String[] args) throws Throwable { - Method method = CLinker.class.getDeclaredMethod("getInstance"); + Method method = CLinker.class.getDeclaredMethod("systemCLinker"); method.invoke(null); } } diff --git a/test/jdk/java/foreign/handles/invoker_module/handle/invoker/MethodHandleInvoker.java b/test/jdk/java/foreign/handles/invoker_module/handle/invoker/MethodHandleInvoker.java index bad4d441a48de..c9ffe911be6d8 100644 --- a/test/jdk/java/foreign/handles/invoker_module/handle/invoker/MethodHandleInvoker.java +++ b/test/jdk/java/foreign/handles/invoker_module/handle/invoker/MethodHandleInvoker.java @@ -28,10 +28,11 @@ import jdk.incubator.foreign.FunctionDescriptor; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; -import jdk.incubator.foreign.MemoryLayouts; +import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.SegmentAllocator; import jdk.incubator.foreign.SymbolLookup; +import jdk.incubator.foreign.ValueLayout; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -52,7 +53,7 @@ public void call(MethodHandle methodHandle) throws Throwable { throw new AssertionError("Caller module is not lookup_module!"); } } catch (Throwable ex) { - throw new AssertionError("Call to restricted method did not fail as expected!"); + throw new AssertionError("Call to restricted method did not fail as expected!", ex); } } @@ -63,8 +64,7 @@ static void addDefaultMapping(Class carrier, Z value) { } static { - addDefaultMapping(CLinker.class, CLinker.getInstance()); - addDefaultMapping(long.class, 0L); + addDefaultMapping(CLinker.class, CLinker.systemCLinker()); addDefaultMapping(Path.class, Path.of("nonExistent")); addDefaultMapping(String.class, "Hello!"); addDefaultMapping(Runnable.class, () -> {}); @@ -73,11 +73,28 @@ static void addDefaultMapping(Class carrier, Z value) { addDefaultMapping(MethodType.class, MethodType.methodType(void.class)); addDefaultMapping(MemoryAddress.class, MemoryAddress.NULL); addDefaultMapping(Addressable.class, MemoryAddress.NULL); - addDefaultMapping(MemoryLayout.class, MemoryLayouts.JAVA_INT); + addDefaultMapping(MemoryLayout.class, ValueLayout.JAVA_INT); addDefaultMapping(FunctionDescriptor.class, FunctionDescriptor.ofVoid()); addDefaultMapping(SymbolLookup.class, SymbolLookup.loaderLookup()); addDefaultMapping(ResourceScope.class, ResourceScope.newImplicitScope()); - addDefaultMapping(SegmentAllocator.class, (size, align) -> null); + addDefaultMapping(SegmentAllocator.class, SegmentAllocator.prefixAllocator(MemorySegment.ofArray(new byte[10]))); + addDefaultMapping(ValueLayout.OfByte.class, ValueLayout.JAVA_BYTE); + addDefaultMapping(ValueLayout.OfBoolean.class, ValueLayout.JAVA_BOOLEAN); + addDefaultMapping(ValueLayout.OfChar.class, ValueLayout.JAVA_CHAR); + addDefaultMapping(ValueLayout.OfShort.class, ValueLayout.JAVA_SHORT); + addDefaultMapping(ValueLayout.OfInt.class, ValueLayout.JAVA_INT); + addDefaultMapping(ValueLayout.OfFloat.class, ValueLayout.JAVA_FLOAT); + addDefaultMapping(ValueLayout.OfLong.class, ValueLayout.JAVA_LONG); + addDefaultMapping(ValueLayout.OfDouble.class, ValueLayout.JAVA_DOUBLE); + addDefaultMapping(ValueLayout.OfAddress.class, ValueLayout.ADDRESS); + addDefaultMapping(byte.class, (byte)0); + addDefaultMapping(boolean.class, true); + addDefaultMapping(char.class, (char)0); + addDefaultMapping(short.class, (short)0); + addDefaultMapping(int.class, 0); + addDefaultMapping(float.class, 0f); + addDefaultMapping(long.class, 0L); + addDefaultMapping(double.class, 0d); } static Object[] makeArgs(MethodType type) { diff --git a/test/jdk/java/foreign/handles/lookup_module/handle/lookup/MethodHandleLookup.java b/test/jdk/java/foreign/handles/lookup_module/handle/lookup/MethodHandleLookup.java index 20f092057c4e2..8755d40e6b173 100644 --- a/test/jdk/java/foreign/handles/lookup_module/handle/lookup/MethodHandleLookup.java +++ b/test/jdk/java/foreign/handles/lookup_module/handle/lookup/MethodHandleLookup.java @@ -23,24 +23,20 @@ package handle.lookup; -import jdk.incubator.foreign.Addressable; import jdk.incubator.foreign.CLinker; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; -import java.nio.charset.Charset; -import java.nio.file.Path; -import java.util.Optional; -import jdk.incubator.foreign.FunctionDescriptor; +import jdk.incubator.foreign.Addressable; import jdk.incubator.foreign.SymbolLookup; import jdk.incubator.foreign.MemoryAddress; -import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; -import jdk.incubator.foreign.SegmentAllocator; +import jdk.incubator.foreign.VaList; +import jdk.incubator.foreign.ValueLayout; import org.testng.annotations.*; public class MethodHandleLookup { @@ -54,38 +50,77 @@ public void testRestrictedHandles(MethodHandle handle, String testName) throws T static Object[][] restrictedMethods() { try { return new Object[][]{ - { MethodHandles.lookup().findStatic(CLinker.class, "getInstance", - MethodType.methodType(CLinker.class)), "CLinker::getInstance" }, - { MethodHandles.lookup().findStatic(CLinker.class, "toJavaString", - MethodType.methodType(String.class, MemoryAddress.class)), - "CLinker::toJavaString" }, - { MethodHandles.lookup().findStatic(CLinker.class, "allocateMemory", - MethodType.methodType(MemoryAddress.class, long.class)), - "CLinker::allocateMemory" }, - { MethodHandles.lookup().findStatic(CLinker.class, "freeMemory", - MethodType.methodType(void.class, MemoryAddress.class)), - "CLinker::freeMemory" }, - { MethodHandles.lookup().findStatic(CLinker.VaList.class, "ofAddress", - MethodType.methodType(CLinker.VaList.class, MemoryAddress.class)), + { MethodHandles.lookup().findStatic(CLinker.class, "systemCLinker", + MethodType.methodType(CLinker.class)), "ForeignLinker::systemCLinker" }, + { MethodHandles.lookup().findStatic(VaList.class, "ofAddress", + MethodType.methodType(VaList.class, MemoryAddress.class, ResourceScope.class)), "VaList::ofAddress/1" }, - { MethodHandles.lookup().findStatic(CLinker.VaList.class, "ofAddress", - MethodType.methodType(CLinker.VaList.class, MemoryAddress.class, ResourceScope.class)), - "VaList::ofAddress/2" }, - { MethodHandles.lookup().findStatic(CLinker.class, "systemLookup", - MethodType.methodType(SymbolLookup.class)), - "CLinker::systemLookup" }, { MethodHandles.lookup().findStatic(SymbolLookup.class, "loaderLookup", MethodType.methodType(SymbolLookup.class)), "SymbolLookup::loaderLookup" }, - { MethodHandles.lookup().findVirtual(MemoryAddress.class, "asSegment", - MethodType.methodType(MemorySegment.class, long.class, ResourceScope.class)), - "MemoryAddress::asSegment/1" }, - { MethodHandles.lookup().findVirtual(MemoryAddress.class, "asSegment", - MethodType.methodType(MemorySegment.class, long.class, Runnable.class, ResourceScope.class)), - "MemoryAddress::asSegment/2" }, - { MethodHandles.lookup().findStatic(MemorySegment.class, "globalNativeSegment", - MethodType.methodType(MemorySegment.class)), - "MemoryAddress::globalNativeSegment" } + { MethodHandles.lookup().findStatic(MemorySegment.class, "ofAddress", + MethodType.methodType(MemorySegment.class, MemoryAddress.class, long.class, ResourceScope.class)), + "MemorySegment::ofAddress" }, + { MethodHandles.lookup().findVirtual(MemoryAddress.class, "getUtf8String", + MethodType.methodType(String.class, long.class)), + "MemoryAddress::getUtf8String" }, + { MethodHandles.lookup().findVirtual(MemoryAddress.class, "setUtf8String", + MethodType.methodType(void.class, long.class, String.class)), + "MemoryAddress::setUtf8String" }, + { MethodHandles.lookup().findVirtual(MemoryAddress.class, "get", + MethodType.methodType(byte.class, ValueLayout.OfByte.class, long.class)), + "MemoryAddress::get/byte" }, + { MethodHandles.lookup().findVirtual(MemoryAddress.class, "get", + MethodType.methodType(boolean.class, ValueLayout.OfBoolean.class, long.class)), + "MemoryAddress::get/boolean" }, + { MethodHandles.lookup().findVirtual(MemoryAddress.class, "get", + MethodType.methodType(char.class, ValueLayout.OfChar.class, long.class)), + "MemoryAddress::get/char" }, + { MethodHandles.lookup().findVirtual(MemoryAddress.class, "get", + MethodType.methodType(short.class, ValueLayout.OfShort.class, long.class)), + "MemoryAddress::get/short" }, + { MethodHandles.lookup().findVirtual(MemoryAddress.class, "get", + MethodType.methodType(int.class, ValueLayout.OfInt.class, long.class)), + "MemoryAddress::get/int" }, + { MethodHandles.lookup().findVirtual(MemoryAddress.class, "get", + MethodType.methodType(float.class, ValueLayout.OfFloat.class, long.class)), + "MemoryAddress::get/float" }, + { MethodHandles.lookup().findVirtual(MemoryAddress.class, "get", + MethodType.methodType(long.class, ValueLayout.OfLong.class, long.class)), + "MemoryAddress::get/long" }, + { MethodHandles.lookup().findVirtual(MemoryAddress.class, "get", + MethodType.methodType(double.class, ValueLayout.OfDouble.class, long.class)), + "MemoryAddress::get/double" }, + { MethodHandles.lookup().findVirtual(MemoryAddress.class, "get", + MethodType.methodType(MemoryAddress.class, ValueLayout.OfAddress.class, long.class)), + "MemoryAddress::get/address" }, + { MethodHandles.lookup().findVirtual(MemoryAddress.class, "set", + MethodType.methodType(void.class, ValueLayout.OfByte.class, long.class, byte.class)), + "MemoryAddress::set/byte" }, + { MethodHandles.lookup().findVirtual(MemoryAddress.class, "set", + MethodType.methodType(void.class, ValueLayout.OfBoolean.class, long.class, boolean.class)), + "MemoryAddress::set/boolean" }, + { MethodHandles.lookup().findVirtual(MemoryAddress.class, "set", + MethodType.methodType(void.class, ValueLayout.OfChar.class, long.class, char.class)), + "MemoryAddress::set/char" }, + { MethodHandles.lookup().findVirtual(MemoryAddress.class, "set", + MethodType.methodType(void.class, ValueLayout.OfShort.class, long.class, short.class)), + "MemoryAddress::set/short" }, + { MethodHandles.lookup().findVirtual(MemoryAddress.class, "set", + MethodType.methodType(void.class, ValueLayout.OfInt.class, long.class, int.class)), + "MemoryAddress::set/int" }, + { MethodHandles.lookup().findVirtual(MemoryAddress.class, "set", + MethodType.methodType(void.class, ValueLayout.OfFloat.class, long.class, float.class)), + "MemoryAddress::set/float" }, + { MethodHandles.lookup().findVirtual(MemoryAddress.class, "set", + MethodType.methodType(void.class, ValueLayout.OfLong.class, long.class, long.class)), + "MemoryAddress::set/long" }, + { MethodHandles.lookup().findVirtual(MemoryAddress.class, "set", + MethodType.methodType(void.class, ValueLayout.OfDouble.class, long.class, double.class)), + "MemoryAddress::set/double" }, + { MethodHandles.lookup().findVirtual(MemoryAddress.class, "set", + MethodType.methodType(void.class, ValueLayout.OfAddress.class, long.class, Addressable.class)), + "MemoryAddress::set/address" }, }; } catch (Throwable ex) { throw new ExceptionInInitializerError((ex)); diff --git a/test/jdk/java/foreign/libAsyncInvokers.cpp b/test/jdk/java/foreign/libAsyncInvokers.cpp new file mode 100644 index 0000000000000..775e2c3c81439 --- /dev/null +++ b/test/jdk/java/foreign/libAsyncInvokers.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include + +#include "libTestUpcall.h" +#ifdef __clang__ +#pragma clang optimize off +#elif defined __GNUC__ +#pragma GCC optimize ("O0") +#elif defined _MSC_BUILD +#pragma optimize( "", off ) +#endif + +template +void launch_v(CB cb) { + std::thread thrd(cb); + thrd.join(); +} + +template +void start(O& out, CB cb) { + out = cb(); +} + +template +O launch(CB cb) { + O result; + std::thread thrd(&start, std::ref(result), cb); + thrd.join(); + return result; +} + +extern "C" { +EXPORT void call_async_V(void (*cb)(void)) { launch_v(cb); } + +EXPORT int call_async_I(int (*cb)(void)) { return launch(cb); } +EXPORT float call_async_F(float (*cb)(void)) { return launch(cb); } +EXPORT double call_async_D(double (*cb)(void)) { return launch(cb); } +EXPORT void* call_async_P(void* (*cb)(void)) { return launch(cb); } + +EXPORT struct S_I call_async_S_I(struct S_I (*cb)(void)) { return launch(cb); } +EXPORT struct S_F call_async_S_F(struct S_F (*cb)(void)) { return launch(cb); } +EXPORT struct S_D call_async_S_D(struct S_D (*cb)(void)) { return launch(cb); } +EXPORT struct S_P call_async_S_P(struct S_P (*cb)(void)) { return launch(cb); } +EXPORT struct S_II call_async_S_II(struct S_II (*cb)(void)) { return launch(cb); } +EXPORT struct S_IF call_async_S_IF(struct S_IF (*cb)(void)) { return launch(cb); } +EXPORT struct S_ID call_async_S_ID(struct S_ID (*cb)(void)) { return launch(cb); } +EXPORT struct S_IP call_async_S_IP(struct S_IP (*cb)(void)) { return launch(cb); } +EXPORT struct S_FI call_async_S_FI(struct S_FI (*cb)(void)) { return launch(cb); } +EXPORT struct S_FF call_async_S_FF(struct S_FF (*cb)(void)) { return launch(cb); } +EXPORT struct S_FD call_async_S_FD(struct S_FD (*cb)(void)) { return launch(cb); } +EXPORT struct S_FP call_async_S_FP(struct S_FP (*cb)(void)) { return launch(cb); } +EXPORT struct S_DI call_async_S_DI(struct S_DI (*cb)(void)) { return launch(cb); } +EXPORT struct S_DF call_async_S_DF(struct S_DF (*cb)(void)) { return launch(cb); } +EXPORT struct S_DD call_async_S_DD(struct S_DD (*cb)(void)) { return launch(cb); } +EXPORT struct S_DP call_async_S_DP(struct S_DP (*cb)(void)) { return launch(cb); } +EXPORT struct S_PI call_async_S_PI(struct S_PI (*cb)(void)) { return launch(cb); } +EXPORT struct S_PF call_async_S_PF(struct S_PF (*cb)(void)) { return launch(cb); } +EXPORT struct S_PD call_async_S_PD(struct S_PD (*cb)(void)) { return launch(cb); } +EXPORT struct S_PP call_async_S_PP(struct S_PP (*cb)(void)) { return launch(cb); } +EXPORT struct S_III call_async_S_III(struct S_III (*cb)(void)) { return launch(cb); } +EXPORT struct S_IIF call_async_S_IIF(struct S_IIF (*cb)(void)) { return launch(cb); } +EXPORT struct S_IID call_async_S_IID(struct S_IID (*cb)(void)) { return launch(cb); } +EXPORT struct S_IIP call_async_S_IIP(struct S_IIP (*cb)(void)) { return launch(cb); } +EXPORT struct S_IFI call_async_S_IFI(struct S_IFI (*cb)(void)) { return launch(cb); } +EXPORT struct S_IFF call_async_S_IFF(struct S_IFF (*cb)(void)) { return launch(cb); } +EXPORT struct S_IFD call_async_S_IFD(struct S_IFD (*cb)(void)) { return launch(cb); } +EXPORT struct S_IFP call_async_S_IFP(struct S_IFP (*cb)(void)) { return launch(cb); } +EXPORT struct S_IDI call_async_S_IDI(struct S_IDI (*cb)(void)) { return launch(cb); } +EXPORT struct S_IDF call_async_S_IDF(struct S_IDF (*cb)(void)) { return launch(cb); } +EXPORT struct S_IDD call_async_S_IDD(struct S_IDD (*cb)(void)) { return launch(cb); } +EXPORT struct S_IDP call_async_S_IDP(struct S_IDP (*cb)(void)) { return launch(cb); } +EXPORT struct S_IPI call_async_S_IPI(struct S_IPI (*cb)(void)) { return launch(cb); } +EXPORT struct S_IPF call_async_S_IPF(struct S_IPF (*cb)(void)) { return launch(cb); } +EXPORT struct S_IPD call_async_S_IPD(struct S_IPD (*cb)(void)) { return launch(cb); } +EXPORT struct S_IPP call_async_S_IPP(struct S_IPP (*cb)(void)) { return launch(cb); } +EXPORT struct S_FII call_async_S_FII(struct S_FII (*cb)(void)) { return launch(cb); } +EXPORT struct S_FIF call_async_S_FIF(struct S_FIF (*cb)(void)) { return launch(cb); } +EXPORT struct S_FID call_async_S_FID(struct S_FID (*cb)(void)) { return launch(cb); } +EXPORT struct S_FIP call_async_S_FIP(struct S_FIP (*cb)(void)) { return launch(cb); } +EXPORT struct S_FFI call_async_S_FFI(struct S_FFI (*cb)(void)) { return launch(cb); } +EXPORT struct S_FFF call_async_S_FFF(struct S_FFF (*cb)(void)) { return launch(cb); } +EXPORT struct S_FFD call_async_S_FFD(struct S_FFD (*cb)(void)) { return launch(cb); } +EXPORT struct S_FFP call_async_S_FFP(struct S_FFP (*cb)(void)) { return launch(cb); } +EXPORT struct S_FDI call_async_S_FDI(struct S_FDI (*cb)(void)) { return launch(cb); } +EXPORT struct S_FDF call_async_S_FDF(struct S_FDF (*cb)(void)) { return launch(cb); } +EXPORT struct S_FDD call_async_S_FDD(struct S_FDD (*cb)(void)) { return launch(cb); } +EXPORT struct S_FDP call_async_S_FDP(struct S_FDP (*cb)(void)) { return launch(cb); } +EXPORT struct S_FPI call_async_S_FPI(struct S_FPI (*cb)(void)) { return launch(cb); } +EXPORT struct S_FPF call_async_S_FPF(struct S_FPF (*cb)(void)) { return launch(cb); } +EXPORT struct S_FPD call_async_S_FPD(struct S_FPD (*cb)(void)) { return launch(cb); } +EXPORT struct S_FPP call_async_S_FPP(struct S_FPP (*cb)(void)) { return launch(cb); } +EXPORT struct S_DII call_async_S_DII(struct S_DII (*cb)(void)) { return launch(cb); } +EXPORT struct S_DIF call_async_S_DIF(struct S_DIF (*cb)(void)) { return launch(cb); } +EXPORT struct S_DID call_async_S_DID(struct S_DID (*cb)(void)) { return launch(cb); } +EXPORT struct S_DIP call_async_S_DIP(struct S_DIP (*cb)(void)) { return launch(cb); } +EXPORT struct S_DFI call_async_S_DFI(struct S_DFI (*cb)(void)) { return launch(cb); } +EXPORT struct S_DFF call_async_S_DFF(struct S_DFF (*cb)(void)) { return launch(cb); } +EXPORT struct S_DFD call_async_S_DFD(struct S_DFD (*cb)(void)) { return launch(cb); } +EXPORT struct S_DFP call_async_S_DFP(struct S_DFP (*cb)(void)) { return launch(cb); } +EXPORT struct S_DDI call_async_S_DDI(struct S_DDI (*cb)(void)) { return launch(cb); } +EXPORT struct S_DDF call_async_S_DDF(struct S_DDF (*cb)(void)) { return launch(cb); } +EXPORT struct S_DDD call_async_S_DDD(struct S_DDD (*cb)(void)) { return launch(cb); } +EXPORT struct S_DDP call_async_S_DDP(struct S_DDP (*cb)(void)) { return launch(cb); } +EXPORT struct S_DPI call_async_S_DPI(struct S_DPI (*cb)(void)) { return launch(cb); } +EXPORT struct S_DPF call_async_S_DPF(struct S_DPF (*cb)(void)) { return launch(cb); } +EXPORT struct S_DPD call_async_S_DPD(struct S_DPD (*cb)(void)) { return launch(cb); } +EXPORT struct S_DPP call_async_S_DPP(struct S_DPP (*cb)(void)) { return launch(cb); } +EXPORT struct S_PII call_async_S_PII(struct S_PII (*cb)(void)) { return launch(cb); } +EXPORT struct S_PIF call_async_S_PIF(struct S_PIF (*cb)(void)) { return launch(cb); } +EXPORT struct S_PID call_async_S_PID(struct S_PID (*cb)(void)) { return launch(cb); } +EXPORT struct S_PIP call_async_S_PIP(struct S_PIP (*cb)(void)) { return launch(cb); } +EXPORT struct S_PFI call_async_S_PFI(struct S_PFI (*cb)(void)) { return launch(cb); } +EXPORT struct S_PFF call_async_S_PFF(struct S_PFF (*cb)(void)) { return launch(cb); } +EXPORT struct S_PFD call_async_S_PFD(struct S_PFD (*cb)(void)) { return launch(cb); } +EXPORT struct S_PFP call_async_S_PFP(struct S_PFP (*cb)(void)) { return launch(cb); } +EXPORT struct S_PDI call_async_S_PDI(struct S_PDI (*cb)(void)) { return launch(cb); } +EXPORT struct S_PDF call_async_S_PDF(struct S_PDF (*cb)(void)) { return launch(cb); } +EXPORT struct S_PDD call_async_S_PDD(struct S_PDD (*cb)(void)) { return launch(cb); } +EXPORT struct S_PDP call_async_S_PDP(struct S_PDP (*cb)(void)) { return launch(cb); } +EXPORT struct S_PPI call_async_S_PPI(struct S_PPI (*cb)(void)) { return launch(cb); } +EXPORT struct S_PPF call_async_S_PPF(struct S_PPF (*cb)(void)) { return launch(cb); } +EXPORT struct S_PPD call_async_S_PPD(struct S_PPD (*cb)(void)) { return launch(cb); } +EXPORT struct S_PPP call_async_S_PPP(struct S_PPP (*cb)(void)) { return launch(cb); } +} diff --git a/test/jdk/java/foreign/libIntrinsics.c b/test/jdk/java/foreign/libIntrinsics.c index 5b6923cbbd780..89d0a09c7b608 100644 --- a/test/jdk/java/foreign/libIntrinsics.c +++ b/test/jdk/java/foreign/libIntrinsics.c @@ -22,6 +22,8 @@ * */ +#include + #ifdef _WIN64 #define EXPORT __declspec(dllexport) #else @@ -31,6 +33,10 @@ EXPORT void empty() { } +EXPORT bool identity_bool(bool x) { + return x; +} + EXPORT char identity_char(char x) { return x; } diff --git a/test/jdk/java/foreign/libSafeAccess.c b/test/jdk/java/foreign/libSafeAccess.c index a5bcbe0e892df..3a640329aa126 100644 --- a/test/jdk/java/foreign/libSafeAccess.c +++ b/test/jdk/java/foreign/libSafeAccess.c @@ -36,3 +36,9 @@ struct Point { EXPORT void struct_func(struct Point p) { } EXPORT void addr_func(struct Point* p) { } + +EXPORT void addr_func_6(struct Point* p1, struct Point* p2, struct Point* p3, struct Point* p4, struct Point* p5, struct Point* p6) { } + +EXPORT void addr_func_cb(void* p, void (*callback)()) { + callback(); +} diff --git a/test/jdk/java/foreign/loaderLookup/TestLoaderLookup.java b/test/jdk/java/foreign/loaderLookup/TestLoaderLookup.java new file mode 100644 index 0000000000000..3d83ba044edef --- /dev/null +++ b/test/jdk/java/foreign/loaderLookup/TestLoaderLookup.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" + * @compile --add-modules jdk.incubator.foreign lookup/Lookup.java + * @compile --add-modules jdk.incubator.foreign invoker/Invoker.java + * @run main/othervm --enable-native-access=ALL-UNNAMED TestLoaderLookup + */ + +import java.lang.reflect.*; +import jdk.incubator.foreign.*; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Paths; + +public class TestLoaderLookup { + public static void main(String[] args) throws ReflectiveOperationException { + ClassLoader loader1 = newClassLoader("lookup"); + Class lookup = loader1.loadClass("lookup.Lookup"); + Method fooSymbol = lookup.getDeclaredMethod("fooSymbol"); + NativeSymbol foo = (NativeSymbol)fooSymbol.invoke(null); + + ClassLoader loader2 = newClassLoader("invoker"); + Class invoker = loader2.loadClass("invoker.Invoker"); + Method invoke = invoker.getDeclaredMethod("invoke", NativeSymbol.class); + invoke.invoke(null, foo); + + loader1 = null; + lookup = null; + fooSymbol = null; + // Make sure that the loader is kept reachable + for (int i = 0 ; i < 1000 ; i++) { + invoke.invoke(null, foo); // might crash if loader1 is GC'ed + System.gc(); + } + } + + public static ClassLoader newClassLoader(String path) { + try { + return new URLClassLoader(new URL[] { + Paths.get(System.getProperty("test.classes", path)).toUri().toURL(), + }, null); + } catch (MalformedURLException e){ + throw new RuntimeException("Unexpected URL conversion failure", e); + } + } +} diff --git a/test/jdk/java/foreign/loaderLookup/invoker/Invoker.java b/test/jdk/java/foreign/loaderLookup/invoker/Invoker.java new file mode 100644 index 0000000000000..fab39a2549813 --- /dev/null +++ b/test/jdk/java/foreign/loaderLookup/invoker/Invoker.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package invoker; + +import jdk.incubator.foreign.*; + +public class Invoker { + public static void invoke(NativeSymbol symbol) throws Throwable { + var linker = CLinker.systemCLinker(); + var handle = linker.downcallHandle(symbol, FunctionDescriptor.ofVoid()); + handle.invokeExact(); + } +} \ No newline at end of file diff --git a/test/jdk/java/foreign/loaderLookup/lookup/Lookup.java b/test/jdk/java/foreign/loaderLookup/lookup/Lookup.java new file mode 100644 index 0000000000000..087a91dcdc2b8 --- /dev/null +++ b/test/jdk/java/foreign/loaderLookup/lookup/Lookup.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package lookup; + +import jdk.incubator.foreign.*; + +public class Lookup { + static { + System.loadLibrary("Foo"); + } + + static SymbolLookup lookup = SymbolLookup.loaderLookup(); + + public static NativeSymbol fooSymbol() { + return lookup.lookup("foo").get(); + } +} \ No newline at end of file diff --git a/src/utils/IdealGraphVisualizer/BatikSVGProxy/src/main/java/com/sun/hotspot/igv/svg/package-info.java b/test/jdk/java/foreign/loaderLookup/lookup/libFoo.c similarity index 76% rename from src/utils/IdealGraphVisualizer/BatikSVGProxy/src/main/java/com/sun/hotspot/igv/svg/package-info.java rename to test/jdk/java/foreign/loaderLookup/lookup/libFoo.c index 272fc48e96f03..68525215b9345 100644 --- a/src/utils/IdealGraphVisualizer/BatikSVGProxy/src/main/java/com/sun/hotspot/igv/svg/package-info.java +++ b/test/jdk/java/foreign/loaderLookup/lookup/libFoo.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -19,11 +19,16 @@ * 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. - * - */ -/** - * This package is used to proxy the SVG export functionality of the BatikSVG library. Reflection is used such that the - * library is optional and need not be present at build time. */ -package com.sun.hotspot.igv.svg; +#include + +#ifdef _WIN64 +#define EXPORT __declspec(dllexport) +#else +#define EXPORT +#endif + +EXPORT void foo(void) { + // do nothing +} diff --git a/test/jdk/java/foreign/malloc/TestMixedMallocFree.java b/test/jdk/java/foreign/malloc/TestMixedMallocFree.java index 41c7616c5f97f..1603fd83ee392 100644 --- a/test/jdk/java/foreign/malloc/TestMixedMallocFree.java +++ b/test/jdk/java/foreign/malloc/TestMixedMallocFree.java @@ -23,13 +23,13 @@ /* * @test + * @library ../ * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" * @run testng/othervm --enable-native-access=ALL-UNNAMED TestMixedMallocFree */ import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.FunctionDescriptor; -import jdk.incubator.foreign.MemoryAccess; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; @@ -37,12 +37,11 @@ import org.testng.annotations.Test; import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; -import static jdk.incubator.foreign.CLinker.*; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; import static org.testng.Assert.assertEquals; -public class TestMixedMallocFree { +public class TestMixedMallocFree extends NativeTestHelper { static final MethodHandle MH_my_malloc; @@ -50,21 +49,20 @@ public class TestMixedMallocFree { System.loadLibrary("Malloc"); SymbolLookup MALLOC = SymbolLookup.loaderLookup(); - MH_my_malloc = CLinker.getInstance().downcallHandle( + MH_my_malloc = CLinker.systemCLinker().downcallHandle( MALLOC.lookup("my_malloc").orElseThrow(), - MethodType.methodType(MemoryAddress.class, long.class), - FunctionDescriptor.of(C_POINTER, C_LONG_LONG)); + FunctionDescriptor.of(C_POINTER, C_LONG_LONG)); } @Test public void testMalloc() throws Throwable { MemoryAddress ma = (MemoryAddress) MH_my_malloc.invokeExact(4L); - MemorySegment seg = ma.asSegment(4L, ResourceScope.newImplicitScope()); - MemoryAccess.setInt(seg, 42); - assertEquals(MemoryAccess.getInt(seg), 42); + MemorySegment seg = MemorySegment.ofAddress(ma, 4L, ResourceScope.newImplicitScope()); + seg.set(JAVA_INT, 0, 42); + assertEquals(seg.get(JAVA_INT, 0), 42); // Test if this free crashes the VM, which might be the case if we load the wrong default library // and end up mixing two allocators together. - CLinker.freeMemory(ma); + freeMemory(ma); } } diff --git a/test/jdk/java/foreign/stackwalk/TestAsyncStackWalk.java b/test/jdk/java/foreign/stackwalk/TestAsyncStackWalk.java index b56231230fdf4..9c9de5e030539 100644 --- a/test/jdk/java/foreign/stackwalk/TestAsyncStackWalk.java +++ b/test/jdk/java/foreign/stackwalk/TestAsyncStackWalk.java @@ -25,6 +25,7 @@ * @test id=default_gc * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" * @library /test/lib + * @library ../ * @build sun.hotspot.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox * @@ -52,6 +53,7 @@ * @requires (((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64") * @requires vm.gc.Z * @library /test/lib + * @library ../ * @build sun.hotspot.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox * @@ -80,6 +82,7 @@ * @requires (((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64") * @requires vm.gc.Shenandoah * @library /test/lib + * @library ../ * @build sun.hotspot.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox * @@ -106,6 +109,7 @@ import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.FunctionDescriptor; +import jdk.incubator.foreign.NativeSymbol; import jdk.incubator.foreign.SymbolLookup; import jdk.incubator.foreign.MemoryAddress; @@ -116,13 +120,12 @@ import sun.hotspot.WhiteBox; import static java.lang.invoke.MethodHandles.lookup; -import static jdk.incubator.foreign.CLinker.C_POINTER; import static jdk.test.lib.Asserts.assertTrue; -public class TestAsyncStackWalk { +public class TestAsyncStackWalk extends NativeTestHelper { static final WhiteBox WB = WhiteBox.getWhiteBox(); - static final CLinker linker = CLinker.getInstance(); + static final CLinker linker = CLinker.systemCLinker(); static final MethodHandle MH_asyncStackWalk; static final MethodHandle MH_m; @@ -133,7 +136,6 @@ public class TestAsyncStackWalk { SymbolLookup lookup = SymbolLookup.loaderLookup(); MH_asyncStackWalk = linker.downcallHandle( lookup.lookup("asyncStackWalk").get(), - MethodType.methodType(void.class, MemoryAddress.class), FunctionDescriptor.ofVoid(C_POINTER)); MH_m = lookup().findStatic(TestAsyncStackWalk.class, "m", MethodType.methodType(void.class)); } catch (ReflectiveOperationException e) { @@ -146,7 +148,7 @@ public class TestAsyncStackWalk { public static void main(String[] args) throws Throwable { try (ResourceScope scope = ResourceScope.newConfinedScope()) { - MemoryAddress stub = linker.upcallStub(MH_m, FunctionDescriptor.ofVoid(), scope); + NativeSymbol stub = linker.upcallStub(MH_m, FunctionDescriptor.ofVoid(), scope); MemoryAddress stubAddress = stub.address(); invocations = 0; didStackWalk = false; @@ -156,7 +158,7 @@ public static void main(String[] args) throws Throwable { } static void payload(MemoryAddress cb) throws Throwable { - MH_asyncStackWalk.invokeExact(cb); + MH_asyncStackWalk.invoke(cb); } static void m() { diff --git a/test/jdk/java/foreign/stackwalk/TestStackWalk.java b/test/jdk/java/foreign/stackwalk/TestStackWalk.java index 5d220cce25c72..07cc0e83df66b 100644 --- a/test/jdk/java/foreign/stackwalk/TestStackWalk.java +++ b/test/jdk/java/foreign/stackwalk/TestStackWalk.java @@ -25,6 +25,7 @@ * @test id=default_gc * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" * @library /test/lib + * @library ../ * @build sun.hotspot.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox * @@ -52,6 +53,7 @@ * @requires (((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64") * @requires vm.gc.Z * @library /test/lib + * @library ../ * @build sun.hotspot.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox * @@ -80,6 +82,7 @@ * @requires (((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64") * @requires vm.gc.Shenandoah * @library /test/lib + * @library ../ * @build sun.hotspot.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox * @@ -106,6 +109,7 @@ import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.FunctionDescriptor; +import jdk.incubator.foreign.NativeSymbol; import jdk.incubator.foreign.SymbolLookup; import jdk.incubator.foreign.MemoryAddress; @@ -117,12 +121,11 @@ import sun.hotspot.WhiteBox; import static java.lang.invoke.MethodHandles.lookup; -import static jdk.incubator.foreign.CLinker.C_POINTER; -public class TestStackWalk { +public class TestStackWalk extends NativeTestHelper { static final WhiteBox WB = WhiteBox.getWhiteBox(); - static final CLinker linker = CLinker.getInstance(); + static final CLinker linker = CLinker.systemCLinker(); static final MethodHandle MH_foo; static final MethodHandle MH_m; @@ -133,7 +136,6 @@ public class TestStackWalk { SymbolLookup lookup = SymbolLookup.loaderLookup(); MH_foo = linker.downcallHandle( lookup.lookup("foo").get(), - MethodType.methodType(void.class, MemoryAddress.class), FunctionDescriptor.ofVoid(C_POINTER)); MH_m = lookup().findStatic(TestStackWalk.class, "m", MethodType.methodType(void.class)); } catch (ReflectiveOperationException e) { @@ -145,7 +147,7 @@ public class TestStackWalk { public static void main(String[] args) throws Throwable { try (ResourceScope scope = ResourceScope.newConfinedScope()) { - MemoryAddress stub = linker.upcallStub(MH_m, FunctionDescriptor.ofVoid(), scope); + NativeSymbol stub = linker.upcallStub(MH_m, FunctionDescriptor.ofVoid(), scope); MemoryAddress stubAddress = stub.address(); armed = false; for (int i = 0; i < 20_000; i++) { @@ -158,7 +160,7 @@ public static void main(String[] args) throws Throwable { } static void payload(MemoryAddress cb) throws Throwable { - MH_foo.invokeExact(cb); + MH_foo.invoke(cb); Reference.reachabilityFence(cb); // keep oop alive across call } diff --git a/test/jdk/java/foreign/valist/VaListTest.java b/test/jdk/java/foreign/valist/VaListTest.java index b2aac7d6dc8f6..9b48d361ed8f1 100644 --- a/test/jdk/java/foreign/valist/VaListTest.java +++ b/test/jdk/java/foreign/valist/VaListTest.java @@ -36,7 +36,7 @@ */ import jdk.incubator.foreign.*; -import jdk.incubator.foreign.CLinker.VaList; +import jdk.incubator.foreign.VaList; import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker; import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker; import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker; @@ -55,59 +55,65 @@ import java.util.stream.DoubleStream; import java.util.stream.IntStream; -import static jdk.incubator.foreign.CLinker.C_DOUBLE; -import static jdk.incubator.foreign.CLinker.C_FLOAT; -import static jdk.incubator.foreign.CLinker.C_INT; -import static jdk.incubator.foreign.CLinker.C_LONG; -import static jdk.incubator.foreign.CLinker.C_LONG_LONG; -import static jdk.incubator.foreign.CLinker.C_POINTER; -import static jdk.incubator.foreign.CLinker.C_VA_LIST; import static jdk.incubator.foreign.MemoryLayout.PathElement.groupElement; -import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_DOUBLE; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_LONG; import static jdk.internal.foreign.PlatformLayouts.*; import static org.testng.Assert.*; public class VaListTest extends NativeTestHelper { - private static final CLinker abi = CLinker.getInstance(); + private static final CLinker abi = CLinker.systemCLinker(); static { System.loadLibrary("VaList"); } static final SymbolLookup LOOKUP = SymbolLookup.loaderLookup(); - private static final MethodHandle MH_sumInts = link("sumInts", - MethodType.methodType(int.class, int.class, VaList.class), - FunctionDescriptor.of(C_INT, C_INT, C_VA_LIST)); - private static final MethodHandle MH_sumDoubles = link("sumDoubles", - MethodType.methodType(double.class, int.class, VaList.class), - FunctionDescriptor.of(C_DOUBLE, C_INT, C_VA_LIST)); - private static final MethodHandle MH_getInt = link("getInt", - MethodType.methodType(int.class, VaList.class), - FunctionDescriptor.of(C_INT, C_VA_LIST)); - private static final MethodHandle MH_sumStruct = link("sumStruct", - MethodType.methodType(int.class, VaList.class), - FunctionDescriptor.of(C_INT, C_VA_LIST)); - private static final MethodHandle MH_sumBigStruct = link("sumBigStruct", - MethodType.methodType(long.class, VaList.class), - FunctionDescriptor.of(C_LONG_LONG, C_VA_LIST)); - private static final MethodHandle MH_sumHugeStruct = link("sumHugeStruct", - MethodType.methodType(long.class, VaList.class), - FunctionDescriptor.of(C_LONG_LONG, C_VA_LIST)); - private static final MethodHandle MH_sumFloatStruct = link("sumFloatStruct", - MethodType.methodType(float.class, VaList.class), - FunctionDescriptor.of(C_FLOAT, C_VA_LIST)); - private static final MethodHandle MH_sumStack = link("sumStack", - MethodType.methodType(void.class, MemoryAddress.class, MemoryAddress.class, VaList.class), - FunctionDescriptor.ofVoid(C_POINTER, C_POINTER, C_VA_LIST)); - - private static MethodHandle link(String symbol, MethodType mt, FunctionDescriptor fd) { - return abi.downcallHandle(LOOKUP.lookup(symbol).get(), mt, fd); + private static final MethodHandle ADDRESS_TO_VALIST; + + static { + try { + ADDRESS_TO_VALIST = MethodHandles.lookup().findStatic(VaList.class, "ofAddress", MethodType.methodType(VaList.class, MemoryAddress.class, ResourceScope.class)); + } catch (Throwable ex) { + throw new ExceptionInInitializerError(ex); + } + } + + + private static final MethodHandle MH_sumInts = linkVaList("sumInts", + FunctionDescriptor.of(C_INT, C_INT, C_POINTER)); + private static final MethodHandle MH_sumDoubles = linkVaList("sumDoubles", + FunctionDescriptor.of(C_DOUBLE, C_INT, C_POINTER)); + private static final MethodHandle MH_getInt = linkVaList("getInt", + FunctionDescriptor.of(C_INT, C_POINTER)); + private static final MethodHandle MH_sumStruct = linkVaList("sumStruct", + FunctionDescriptor.of(C_INT, C_POINTER)); + private static final MethodHandle MH_sumBigStruct = linkVaList("sumBigStruct", + FunctionDescriptor.of(C_LONG_LONG, C_POINTER)); + private static final MethodHandle MH_sumHugeStruct = linkVaList("sumHugeStruct", + FunctionDescriptor.of(C_LONG_LONG, C_POINTER)); + private static final MethodHandle MH_sumFloatStruct = linkVaList("sumFloatStruct", + FunctionDescriptor.of(C_FLOAT, C_POINTER)); + private static final MethodHandle MH_sumStack = linkVaList("sumStack", + FunctionDescriptor.ofVoid(C_POINTER, C_POINTER, C_POINTER)); + + private static MethodHandle link(String symbol, FunctionDescriptor fd) { + return linkInternal(symbol, fd); + } + + private static MethodHandle linkVaList(String symbol, FunctionDescriptor fd) { + return linkInternal(symbol, fd); + } + + + private static MethodHandle linkInternal(String symbol, FunctionDescriptor fd) { + return abi.downcallHandle(LOOKUP.lookup(symbol).get(), fd); } private static MethodHandle linkVaListCB(String symbol) { return link(symbol, - MethodType.methodType(void.class, MemoryAddress.class), FunctionDescriptor.ofVoid(C_POINTER)); } @@ -123,22 +129,22 @@ private static MethodHandle linkVaListCB(String symbol) { private static final Function, VaList> platformVaListFactory = (builder) -> VaList.make(builder, ResourceScope.newConfinedScope()); - private static final BiFunction, NativeScope, VaList> winVaListScopedFactory - = (builder, scope) -> Windowsx64Linker.newVaList(builder, scope.scope()); - private static final BiFunction, NativeScope, VaList> sysvVaListScopedFactory - = (builder, scope) -> SysVx64Linker.newVaList(builder, scope.scope()); - private static final BiFunction, NativeScope, VaList> linuxAArch64VaListScopedFactory - = (builder, scope) -> LinuxAArch64Linker.newVaList(builder, scope.scope()); - private static final BiFunction, NativeScope, VaList> macAArch64VaListScopedFactory - = (builder, scope) -> MacOsAArch64Linker.newVaList(builder, scope.scope()); - private static final BiFunction, NativeScope, VaList> platformVaListScopedFactory - = (builder, scope) -> VaList.make(builder, scope.scope()); + private static final BiFunction, ResourceScope, VaList> winVaListScopedFactory + = (builder, scope) -> Windowsx64Linker.newVaList(builder, scope); + private static final BiFunction, ResourceScope, VaList> sysvVaListScopedFactory + = (builder, scope) -> SysVx64Linker.newVaList(builder, scope); + private static final BiFunction, ResourceScope, VaList> linuxAArch64VaListScopedFactory + = (builder, scope) -> LinuxAArch64Linker.newVaList(builder, scope); + private static final BiFunction, ResourceScope, VaList> macAArch64VaListScopedFactory + = (builder, scope) -> MacOsAArch64Linker.newVaList(builder, scope); + private static final BiFunction, ResourceScope, VaList> platformVaListScopedFactory + = (builder, scope) -> VaList.make(builder, scope); @DataProvider @SuppressWarnings("unchecked") public static Object[][] sumInts() { - Function> sumIntsJavaFact = layout -> - (num, list) -> IntStream.generate(() -> list.vargAsInt(layout)).limit(num).sum(); + Function> sumIntsJavaFact = layout -> + (num, list) -> IntStream.generate(() -> list.nextVarg(layout)).limit(num).sum(); BiFunction sumIntsNative = MethodHandleProxies.asInterfaceInstance(BiFunction.class, MH_sumInts); return new Object[][]{ @@ -153,11 +159,11 @@ public static Object[][] sumInts() { @Test(dataProvider = "sumInts") public void testIntSum(Function, VaList> vaListFactory, BiFunction sumInts, - ValueLayout intLayout) { + ValueLayout.OfInt intLayout) { VaList vaList = vaListFactory.apply(b -> - b.vargFromInt(intLayout, 10) - .vargFromInt(intLayout, 15) - .vargFromInt(intLayout, 20)); + b.addVarg(intLayout, 10) + .addVarg(intLayout, 15) + .addVarg(intLayout, 20)); int x = sumInts.apply(3, vaList); assertEquals(x, 45); vaList.scope().close(); @@ -166,8 +172,8 @@ public void testIntSum(Function, VaList> vaListFactory, @DataProvider @SuppressWarnings("unchecked") public static Object[][] sumDoubles() { - Function> sumDoublesJavaFact = layout -> - (num, list) -> DoubleStream.generate(() -> list.vargAsDouble(layout)).limit(num).sum(); + Function> sumDoublesJavaFact = layout -> + (num, list) -> DoubleStream.generate(() -> list.nextVarg(layout)).limit(num).sum(); BiFunction sumDoublesNative = MethodHandleProxies.asInterfaceInstance(BiFunction.class, MH_sumDoubles); return new Object[][]{ @@ -182,11 +188,11 @@ public static Object[][] sumDoubles() { @Test(dataProvider = "sumDoubles") public void testDoubleSum(Function, VaList> vaListFactory, BiFunction sumDoubles, - ValueLayout doubleLayout) { + ValueLayout.OfDouble doubleLayout) { VaList vaList = vaListFactory.apply(b -> - b.vargFromDouble(doubleLayout, 3.0D) - .vargFromDouble(doubleLayout, 4.0D) - .vargFromDouble(doubleLayout, 5.0D)); + b.addVarg(doubleLayout, 3.0D) + .addVarg(doubleLayout, 4.0D) + .addVarg(doubleLayout, 5.0D)); double x = sumDoubles.apply(3, vaList); assertEquals(x, 12.0D); vaList.scope().close(); @@ -195,10 +201,10 @@ public void testDoubleSum(Function, VaList> vaListFacto @DataProvider @SuppressWarnings("unchecked") public static Object[][] pointers() { - Function> getIntJavaFact = layout -> + Function> getIntJavaFact = layout -> list -> { - MemoryAddress ma = list.vargAsAddress(layout); - return MemoryAccess.getIntAtOffset(MemorySegment.globalNativeSegment(), ma.toRawLongValue()); + MemoryAddress ma = list.nextVarg(layout); + return ma.get(JAVA_INT, 0); }; Function getIntNative = MethodHandleProxies.asInterfaceInstance(Function.class, MH_getInt); return new Object[][]{ @@ -213,11 +219,11 @@ public static Object[][] pointers() { @Test(dataProvider = "pointers") public void testVaListMemoryAddress(Function, VaList> vaListFactory, Function getFromPointer, - ValueLayout pointerLayout) { + ValueLayout.OfAddress pointerLayout) { try (ResourceScope scope = ResourceScope.newConfinedScope()) { MemorySegment msInt = MemorySegment.allocateNative(JAVA_INT, scope); - MemoryAccess.setInt(msInt, 10); - VaList vaList = vaListFactory.apply(b -> b.vargFromAddress(pointerLayout, msInt.address())); + msInt.set(JAVA_INT, 0, 10); + VaList vaList = vaListFactory.apply(b -> b.addVarg(pointerLayout, msInt.address())); int x = getFromPointer.apply(vaList); assertEquals(x, 10); vaList.scope().close(); @@ -231,28 +237,29 @@ interface TriFunction { @DataProvider @SuppressWarnings("unchecked") public static Object[][] structs() { - TriFunction> sumStructJavaFact + TriFunction> sumStructJavaFact = (pointLayout, VH_Point_x, VH_Point_y) -> list -> { - MemorySegment struct = list.vargAsSegment(pointLayout, ResourceScope.newImplicitScope()); + MemorySegment struct = MemorySegment.allocateNative(pointLayout, ResourceScope.newImplicitScope()); + list.nextVarg(pointLayout, SegmentAllocator.prefixAllocator(struct)); int x = (int) VH_Point_x.get(struct); int y = (int) VH_Point_y.get(struct); return x + y; }; - TriFunction> sumStructNativeFact + TriFunction> sumStructNativeFact = (pointLayout, VH_Point_x, VH_Point_y) -> MethodHandleProxies.asInterfaceInstance(Function.class, MH_sumStruct); TriFunction, VaList>, MemoryLayout, - TriFunction>, Object[]> argsFact + TriFunction>, Object[]> argsFact = (vaListFact, intLayout, sumStructFact) -> { GroupLayout pointLayout = MemoryLayout.structLayout( intLayout.withName("x"), intLayout.withName("y") ); - VarHandle VH_Point_x = pointLayout.varHandle(int.class, groupElement("x")); - VarHandle VH_Point_y = pointLayout.varHandle(int.class, groupElement("y")); + VarHandle VH_Point_x = pointLayout.varHandle(groupElement("x")); + VarHandle VH_Point_y = pointLayout.varHandle(groupElement("y")); return new Object[] { vaListFact, sumStructFact.apply(pointLayout, VH_Point_x, VH_Point_y), pointLayout, VH_Point_x, VH_Point_y }; }; @@ -274,7 +281,7 @@ public void testStruct(Function, VaList> vaListFactory, VH_Point_x.set(struct, 5); VH_Point_y.set(struct, 10); - VaList vaList = vaListFactory.apply(b -> b.vargFromSegment(Point_LAYOUT, struct)); + VaList vaList = vaListFactory.apply(b -> b.addVarg(Point_LAYOUT, struct)); int sum = sumStruct.apply(vaList); assertEquals(sum, 15); vaList.scope().close(); @@ -284,28 +291,29 @@ public void testStruct(Function, VaList> vaListFactory, @DataProvider @SuppressWarnings("unchecked") public static Object[][] bigStructs() { - TriFunction> sumStructJavaFact + TriFunction> sumStructJavaFact = (BigPoint_LAYOUT, VH_BigPoint_x, VH_BigPoint_y) -> list -> { - MemorySegment struct = list.vargAsSegment(BigPoint_LAYOUT, ResourceScope.newImplicitScope()); + MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, ResourceScope.newImplicitScope()); + list.nextVarg(BigPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); long x = (long) VH_BigPoint_x.get(struct); long y = (long) VH_BigPoint_y.get(struct); return x + y; }; - TriFunction> sumStructNativeFact + TriFunction> sumStructNativeFact = (pointLayout, VH_BigPoint_x, VH_BigPoint_y) -> MethodHandleProxies.asInterfaceInstance(Function.class, MH_sumBigStruct); TriFunction, VaList>, MemoryLayout, - TriFunction>, Object[]> argsFact + TriFunction>, Object[]> argsFact = (vaListFact, longLongLayout, sumBigStructFact) -> { GroupLayout BigPoint_LAYOUT = MemoryLayout.structLayout( longLongLayout.withName("x"), longLongLayout.withName("y") ); - VarHandle VH_BigPoint_x = BigPoint_LAYOUT.varHandle(long.class, groupElement("x")); - VarHandle VH_BigPoint_y = BigPoint_LAYOUT.varHandle(long.class, groupElement("y")); + VarHandle VH_BigPoint_x = BigPoint_LAYOUT.varHandle(groupElement("x")); + VarHandle VH_BigPoint_y = BigPoint_LAYOUT.varHandle(groupElement("y")); return new Object[] { vaListFact, sumBigStructFact.apply(BigPoint_LAYOUT, VH_BigPoint_x, VH_BigPoint_y), BigPoint_LAYOUT, VH_BigPoint_x, VH_BigPoint_y }; }; @@ -327,7 +335,7 @@ public void testBigStruct(Function, VaList> vaListFacto VH_BigPoint_x.set(struct, 5); VH_BigPoint_y.set(struct, 10); - VaList vaList = vaListFactory.apply(b -> b.vargFromSegment(BigPoint_LAYOUT, struct)); + VaList vaList = vaListFactory.apply(b -> b.addVarg(BigPoint_LAYOUT, struct)); long sum = sumBigStruct.apply(vaList); assertEquals(sum, 15); vaList.scope().close(); @@ -337,28 +345,29 @@ public void testBigStruct(Function, VaList> vaListFacto @DataProvider @SuppressWarnings("unchecked") public static Object[][] floatStructs() { - TriFunction> sumStructJavaFact + TriFunction> sumStructJavaFact = (FloatPoint_LAYOUT, VH_FloatPoint_x, VH_FloatPoint_y) -> list -> { - MemorySegment struct = list.vargAsSegment(FloatPoint_LAYOUT, ResourceScope.newImplicitScope()); + MemorySegment struct = MemorySegment.allocateNative(FloatPoint_LAYOUT, ResourceScope.newImplicitScope()); + list.nextVarg(FloatPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); float x = (float) VH_FloatPoint_x.get(struct); float y = (float) VH_FloatPoint_y.get(struct); return x + y; }; - TriFunction> sumStructNativeFact + TriFunction> sumStructNativeFact = (pointLayout, VH_FloatPoint_x, VH_FloatPoint_y) -> MethodHandleProxies.asInterfaceInstance(Function.class, MH_sumFloatStruct); TriFunction, VaList>, MemoryLayout, - TriFunction>, Object[]> argsFact + TriFunction>, Object[]> argsFact = (vaListFact, floatLayout, sumFloatStructFact) -> { GroupLayout FloatPoint_LAYOUT = MemoryLayout.structLayout( floatLayout.withName("x"), floatLayout.withName("y") ); - VarHandle VH_FloatPoint_x = FloatPoint_LAYOUT.varHandle(float.class, groupElement("x")); - VarHandle VH_FloatPoint_y = FloatPoint_LAYOUT.varHandle(float.class, groupElement("y")); + VarHandle VH_FloatPoint_x = FloatPoint_LAYOUT.varHandle(groupElement("x")); + VarHandle VH_FloatPoint_y = FloatPoint_LAYOUT.varHandle(groupElement("y")); return new Object[] { vaListFact, sumFloatStructFact.apply(FloatPoint_LAYOUT, VH_FloatPoint_x, VH_FloatPoint_y), FloatPoint_LAYOUT, VH_FloatPoint_x, VH_FloatPoint_y }; }; @@ -381,7 +390,7 @@ public void testFloatStruct(Function, VaList> vaListFac VH_FloatPoint_x.set(struct, 1.234f); VH_FloatPoint_y.set(struct, 3.142f); - VaList vaList = vaListFactory.apply(b -> b.vargFromSegment(FloatPoint_LAYOUT, struct)); + VaList vaList = vaListFactory.apply(b -> b.addVarg(FloatPoint_LAYOUT, struct)); float sum = sumFloatStruct.apply(vaList); assertEquals(sum, 4.376f, 0.00001f); vaList.scope().close(); @@ -395,31 +404,32 @@ interface QuadFunc { @DataProvider @SuppressWarnings("unchecked") public static Object[][] hugeStructs() { - QuadFunc> sumStructJavaFact + QuadFunc> sumStructJavaFact = (HugePoint_LAYOUT, VH_HugePoint_x, VH_HugePoint_y, VH_HugePoint_z) -> list -> { - MemorySegment struct = list.vargAsSegment(HugePoint_LAYOUT, ResourceScope.newImplicitScope()); + MemorySegment struct = MemorySegment.allocateNative(HugePoint_LAYOUT, ResourceScope.newImplicitScope()); + list.nextVarg(HugePoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); long x = (long) VH_HugePoint_x.get(struct); long y = (long) VH_HugePoint_y.get(struct); long z = (long) VH_HugePoint_z.get(struct); return x + y + z; }; - QuadFunc> sumStructNativeFact + QuadFunc> sumStructNativeFact = (pointLayout, VH_HugePoint_x, VH_HugePoint_y, VH_HugePoint_z) -> MethodHandleProxies.asInterfaceInstance(Function.class, MH_sumHugeStruct); TriFunction, VaList>, MemoryLayout, - QuadFunc>, Object[]> argsFact + QuadFunc>, Object[]> argsFact = (vaListFact, longLongLayout, sumBigStructFact) -> { GroupLayout HugePoint_LAYOUT = MemoryLayout.structLayout( longLongLayout.withName("x"), longLongLayout.withName("y"), longLongLayout.withName("z") ); - VarHandle VH_HugePoint_x = HugePoint_LAYOUT.varHandle(long.class, groupElement("x")); - VarHandle VH_HugePoint_y = HugePoint_LAYOUT.varHandle(long.class, groupElement("y")); - VarHandle VH_HugePoint_z = HugePoint_LAYOUT.varHandle(long.class, groupElement("z")); + VarHandle VH_HugePoint_x = HugePoint_LAYOUT.varHandle(groupElement("x")); + VarHandle VH_HugePoint_y = HugePoint_LAYOUT.varHandle(groupElement("y")); + VarHandle VH_HugePoint_z = HugePoint_LAYOUT.varHandle(groupElement("z")); return new Object[] { vaListFact, sumBigStructFact.apply(HugePoint_LAYOUT, VH_HugePoint_x, VH_HugePoint_y, VH_HugePoint_z), HugePoint_LAYOUT, VH_HugePoint_x, VH_HugePoint_y, VH_HugePoint_z }; @@ -446,7 +456,7 @@ public void testHugeStruct(Function, VaList> vaListFact VH_HugePoint_y.set(struct, 2); VH_HugePoint_z.set(struct, 3); - VaList vaList = vaListFactory.apply(b -> b.vargFromSegment(HugePoint_LAYOUT, struct)); + VaList vaList = vaListFactory.apply(b -> b.addVarg(HugePoint_LAYOUT, struct)); long sum = sumHugeStruct.apply(vaList); assertEquals(sum, 6); vaList.scope().close(); @@ -459,22 +469,22 @@ public interface SumStackFunc { @DataProvider public static Object[][] sumStack() { - BiFunction sumStackJavaFact = (longLayout, doubleLayout) -> + BiFunction sumStackJavaFact = (longLayout, doubleLayout) -> (longSum, doubleSum, list) -> { long lSum = 0L; for (int i = 0; i < 16; i++) { - lSum += list.vargAsLong(longLayout); + lSum += list.nextVarg(longLayout); } - MemoryAccess.setLong(longSum, lSum); + longSum.set(JAVA_LONG, 0, lSum); double dSum = 0D; for (int i = 0; i < 16; i++) { - dSum += list.vargAsDouble(doubleLayout); + dSum += list.nextVarg(doubleLayout); } - MemoryAccess.setDouble(doubleSum, dSum); + doubleSum.set(JAVA_DOUBLE, 0, dSum); }; SumStackFunc sumStackNative = (longSum, doubleSum, list) -> { try { - MH_sumStack.invokeExact(longSum.address(), doubleSum.address(), list); + MH_sumStack.invoke(longSum, doubleSum, list); } catch (Throwable ex) { throw new AssertionError(ex); } @@ -491,20 +501,20 @@ public static Object[][] sumStack() { @Test(dataProvider = "sumStack") public void testStack(Function, VaList> vaListFactory, SumStackFunc sumStack, - ValueLayout longLayout, - ValueLayout doubleLayout) { + ValueLayout.OfLong longLayout, + ValueLayout.OfDouble doubleLayout) { try (ResourceScope scope = ResourceScope.newConfinedScope()) { MemorySegment longSum = MemorySegment.allocateNative(longLayout, scope); MemorySegment doubleSum = MemorySegment.allocateNative(doubleLayout, scope); - MemoryAccess.setLong(longSum, 0L); - MemoryAccess.setDouble(doubleSum, 0D); + longSum.set(JAVA_LONG, 0, 0L); + doubleSum.set(JAVA_DOUBLE, 0, 0D); VaList list = vaListFactory.apply(b -> { for (long l = 1; l <= 16L; l++) { - b.vargFromLong(longLayout, l); + b.addVarg(longLayout, l); } for (double d = 1; d <= 16D; d++) { - b.vargFromDouble(doubleLayout, d); + b.addVarg(doubleLayout, d); } }); @@ -514,8 +524,8 @@ public void testStack(Function, VaList> vaListFactory, list.scope().close(); } - long lSum = MemoryAccess.getLong(longSum); - double dSum = MemoryAccess.getDouble(doubleSum); + long lSum = longSum.get(JAVA_LONG, 0); + double dSum = doubleSum.get(JAVA_DOUBLE, 0); assertEquals(lSum, 136L); assertEquals(dSum, 136D); @@ -524,10 +534,10 @@ public void testStack(Function, VaList> vaListFactory, @Test(dataProvider = "upcalls") public void testUpcall(MethodHandle target, MethodHandle callback) throws Throwable { - FunctionDescriptor desc = FunctionDescriptor.ofVoid(C_VA_LIST); + FunctionDescriptor desc = FunctionDescriptor.ofVoid(C_POINTER); try (ResourceScope scope = ResourceScope.newConfinedScope()) { - MemoryAddress stub = abi.upcallStub(callback, desc, scope); - target.invokeExact(stub.address()); + NativeSymbol stub = abi.upcallStub(callback, desc, scope); + target.invoke(stub); } } @@ -555,8 +565,8 @@ public void testEmptyNotCloseable(VaList emptyList) { @DataProvider @SuppressWarnings("unchecked") public static Object[][] sumIntsScoped() { - Function> sumIntsJavaFact = layout -> - (num, list) -> IntStream.generate(() -> list.vargAsInt(layout)).limit(num).sum(); + Function> sumIntsJavaFact = layout -> + (num, list) -> IntStream.generate(() -> list.nextVarg(layout)).limit(num).sum(); BiFunction sumIntsNative = MethodHandleProxies.asInterfaceInstance(BiFunction.class, MH_sumInts); return new Object[][]{ @@ -569,14 +579,13 @@ public static Object[][] sumIntsScoped() { } @Test(dataProvider = "sumIntsScoped") - public void testScopedVaList(BiFunction, NativeScope, VaList> vaListFactory, + public void testScopedVaList(BiFunction, ResourceScope, VaList> vaListFactory, BiFunction sumInts, - ValueLayout intLayout) { + ValueLayout.OfInt intLayout) { VaList listLeaked; - try (NativeScope scope = new NativeScope()) { - VaList list = vaListFactory.apply(b -> b.vargFromInt(intLayout, 4) - .vargFromInt(intLayout, 8), - scope); + try (ResourceScope scope = ResourceScope.newConfinedScope()) { + VaList list = vaListFactory.apply(b -> b.addVarg(intLayout, 4) + .addVarg(intLayout, 8), scope); int x = sumInts.apply(2, list); assertEquals(x, 12); listLeaked = list; @@ -589,13 +598,14 @@ public void testScopeMSRead(Function, VaList> vaListFac Function sumStruct, // ignored GroupLayout Point_LAYOUT, VarHandle VH_Point_x, VarHandle VH_Point_y) { MemorySegment pointOut; - try (NativeScope scope = new NativeScope()) { + try (ResourceScope scope = ResourceScope.newConfinedScope()) { try (ResourceScope innerScope = ResourceScope.newConfinedScope()) { MemorySegment pointIn = MemorySegment.allocateNative(Point_LAYOUT, innerScope); VH_Point_x.set(pointIn, 3); VH_Point_y.set(pointIn, 6); - VaList list = vaListFactory.apply(b -> b.vargFromSegment(Point_LAYOUT, pointIn)); - pointOut = list.vargAsSegment(Point_LAYOUT, scope); + VaList list = vaListFactory.apply(b -> b.addVarg(Point_LAYOUT, pointIn)); + pointOut = MemorySegment.allocateNative(Point_LAYOUT, scope); + list.nextVarg(Point_LAYOUT, SegmentAllocator.prefixAllocator(pointOut)); assertEquals((int) VH_Point_x.get(pointOut), 3); assertEquals((int) VH_Point_y.get(pointOut), 6); list.scope().close(); @@ -617,12 +627,12 @@ public Object[][] copy() { } @Test(dataProvider = "copy") - public void testCopy(Function, VaList> vaListFactory, ValueLayout intLayout) { - VaList list = vaListFactory.apply(b -> b.vargFromInt(intLayout, 4) - .vargFromInt(intLayout, 8)); + public void testCopy(Function, VaList> vaListFactory, ValueLayout.OfInt intLayout) { + VaList list = vaListFactory.apply(b -> b.addVarg(intLayout, 4) + .addVarg(intLayout, 8)); VaList copy = list.copy(); - assertEquals(copy.vargAsInt(intLayout), 4); - assertEquals(copy.vargAsInt(intLayout), 8); + assertEquals(copy.nextVarg(intLayout), 4); + assertEquals(copy.nextVarg(intLayout), 8); // try { // this logic only works on Windows! // int x = copy.vargAsInt(intLayout); @@ -631,21 +641,21 @@ public void testCopy(Function, VaList> vaListFactory, V // // ok - we exhausted the list // } - assertEquals(list.vargAsInt(intLayout), 4); - assertEquals(list.vargAsInt(intLayout), 8); + assertEquals(list.nextVarg(intLayout), 4); + assertEquals(list.nextVarg(intLayout), 8); list.scope().close(); } @Test(dataProvider = "copy", expectedExceptions = IllegalStateException.class) public void testCopyUnusableAfterOriginalClosed(Function, VaList> vaListFactory, - ValueLayout intLayout) { - VaList list = vaListFactory.apply(b -> b.vargFromInt(intLayout, 4) - .vargFromInt(intLayout, 8)); + ValueLayout.OfInt intLayout) { + VaList list = vaListFactory.apply(b -> b.addVarg(intLayout, 4) + .addVarg(intLayout, 8)); VaList copy = list.copy(); list.scope().close(); - copy.vargAsInt(intLayout); // should throw + copy.nextVarg(intLayout); // should throw } @DataProvider @@ -654,38 +664,40 @@ public static Object[][] upcalls() { C_LONG_LONG.withName("x"), C_LONG_LONG.withName("y") ); - VarHandle VH_BigPoint_x = BigPoint_LAYOUT.varHandle(long.class, groupElement("x")); - VarHandle VH_BigPoint_y = BigPoint_LAYOUT.varHandle(long.class, groupElement("y")); + VarHandle VH_BigPoint_x = BigPoint_LAYOUT.varHandle(groupElement("x")); + VarHandle VH_BigPoint_y = BigPoint_LAYOUT.varHandle(groupElement("y")); GroupLayout Point_LAYOUT = MemoryLayout.structLayout( C_INT.withName("x"), C_INT.withName("y") ); - VarHandle VH_Point_x = Point_LAYOUT.varHandle(int.class, groupElement("x")); - VarHandle VH_Point_y = Point_LAYOUT.varHandle(int.class, groupElement("y")); + VarHandle VH_Point_x = Point_LAYOUT.varHandle(groupElement("x")); + VarHandle VH_Point_y = Point_LAYOUT.varHandle(groupElement("y")); GroupLayout FloatPoint_LAYOUT = MemoryLayout.structLayout( C_FLOAT.withName("x"), C_FLOAT.withName("y") ); - VarHandle VH_FloatPoint_x = FloatPoint_LAYOUT.varHandle(float.class, groupElement("x")); - VarHandle VH_FloatPoint_y = FloatPoint_LAYOUT.varHandle(float.class, groupElement("y")); + VarHandle VH_FloatPoint_x = FloatPoint_LAYOUT.varHandle(groupElement("x")); + VarHandle VH_FloatPoint_y = FloatPoint_LAYOUT.varHandle(groupElement("y")); GroupLayout HugePoint_LAYOUT = MemoryLayout.structLayout( C_LONG_LONG.withName("x"), C_LONG_LONG.withName("y"), C_LONG_LONG.withName("z") ); - VarHandle VH_HugePoint_x = HugePoint_LAYOUT.varHandle(long.class, groupElement("x")); - VarHandle VH_HugePoint_y = HugePoint_LAYOUT.varHandle(long.class, groupElement("y")); - VarHandle VH_HugePoint_z = HugePoint_LAYOUT.varHandle(long.class, groupElement("z")); + VarHandle VH_HugePoint_x = HugePoint_LAYOUT.varHandle(groupElement("x")); + VarHandle VH_HugePoint_y = HugePoint_LAYOUT.varHandle(groupElement("y")); + VarHandle VH_HugePoint_z = HugePoint_LAYOUT.varHandle(groupElement("z")); return new Object[][]{ { linkVaListCB("upcallBigStruct"), VaListConsumer.mh(vaList -> { - MemorySegment struct = vaList.vargAsSegment(BigPoint_LAYOUT, ResourceScope.newImplicitScope()); + MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, ResourceScope.newImplicitScope()); + vaList.nextVarg(BigPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); assertEquals((long) VH_BigPoint_x.get(struct), 8); assertEquals((long) VH_BigPoint_y.get(struct), 16); })}, { linkVaListCB("upcallBigStruct"), VaListConsumer.mh(vaList -> { VaList copy = vaList.copy(); - MemorySegment struct = vaList.vargAsSegment(BigPoint_LAYOUT, ResourceScope.newImplicitScope()); + MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, ResourceScope.newImplicitScope()); + vaList.nextVarg(BigPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); assertEquals((long) VH_BigPoint_x.get(struct), 8); assertEquals((long) VH_BigPoint_y.get(struct), 16); @@ -693,84 +705,91 @@ public static Object[][] upcalls() { VH_BigPoint_y.set(struct, 0); // should be independent - struct = copy.vargAsSegment(BigPoint_LAYOUT, ResourceScope.newImplicitScope()); + copy.nextVarg(BigPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); assertEquals((long) VH_BigPoint_x.get(struct), 8); assertEquals((long) VH_BigPoint_y.get(struct), 16); })}, { linkVaListCB("upcallBigStructPlusScalar"), VaListConsumer.mh(vaList -> { - MemorySegment struct = vaList.vargAsSegment(BigPoint_LAYOUT, ResourceScope.newImplicitScope()); + MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, ResourceScope.newImplicitScope()); + vaList.nextVarg(BigPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); assertEquals((long) VH_BigPoint_x.get(struct), 8); assertEquals((long) VH_BigPoint_y.get(struct), 16); - assertEquals(vaList.vargAsLong(C_LONG_LONG), 42); + assertEquals(vaList.nextVarg(C_LONG_LONG), 42); })}, { linkVaListCB("upcallBigStructPlusScalar"), VaListConsumer.mh(vaList -> { vaList.skip(BigPoint_LAYOUT); - assertEquals(vaList.vargAsLong(C_LONG_LONG), 42); + assertEquals(vaList.nextVarg(C_LONG_LONG), 42); })}, { linkVaListCB("upcallStruct"), VaListConsumer.mh(vaList -> { - MemorySegment struct = vaList.vargAsSegment(Point_LAYOUT, ResourceScope.newImplicitScope()); + MemorySegment struct = MemorySegment.allocateNative(Point_LAYOUT, ResourceScope.newImplicitScope()); + vaList.nextVarg(Point_LAYOUT, SegmentAllocator.prefixAllocator(struct)); assertEquals((int) VH_Point_x.get(struct), 5); assertEquals((int) VH_Point_y.get(struct), 10); })}, { linkVaListCB("upcallHugeStruct"), VaListConsumer.mh(vaList -> { - MemorySegment struct = vaList.vargAsSegment(HugePoint_LAYOUT, ResourceScope.newImplicitScope()); + MemorySegment struct = MemorySegment.allocateNative(HugePoint_LAYOUT, ResourceScope.newImplicitScope()); + vaList.nextVarg(HugePoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); assertEquals((long) VH_HugePoint_x.get(struct), 1); assertEquals((long) VH_HugePoint_y.get(struct), 2); assertEquals((long) VH_HugePoint_z.get(struct), 3); })}, { linkVaListCB("upcallFloatStruct"), VaListConsumer.mh(vaList -> { - MemorySegment struct = vaList.vargAsSegment(FloatPoint_LAYOUT, ResourceScope.newImplicitScope()); + MemorySegment struct = MemorySegment.allocateNative(FloatPoint_LAYOUT, ResourceScope.newImplicitScope()); + vaList.nextVarg(FloatPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); assertEquals((float) VH_FloatPoint_x.get(struct), 1.0f); assertEquals((float) VH_FloatPoint_y.get(struct), 2.0f); })}, { linkVaListCB("upcallMemoryAddress"), VaListConsumer.mh(vaList -> { - MemoryAddress intPtr = vaList.vargAsAddress(C_POINTER); - MemorySegment ms = intPtr.asSegment(C_INT.byteSize(), ResourceScope.globalScope()); - int x = MemoryAccess.getInt(ms); + MemoryAddress intPtr = vaList.nextVarg(C_POINTER); + MemorySegment ms = MemorySegment.ofAddress(intPtr, C_INT.byteSize(), ResourceScope.globalScope()); + int x = ms.get(JAVA_INT, 0); assertEquals(x, 10); })}, { linkVaListCB("upcallDoubles"), VaListConsumer.mh(vaList -> { - assertEquals(vaList.vargAsDouble(C_DOUBLE), 3.0); - assertEquals(vaList.vargAsDouble(C_DOUBLE), 4.0); - assertEquals(vaList.vargAsDouble(C_DOUBLE), 5.0); + assertEquals(vaList.nextVarg(C_DOUBLE), 3.0); + assertEquals(vaList.nextVarg(C_DOUBLE), 4.0); + assertEquals(vaList.nextVarg(C_DOUBLE), 5.0); })}, { linkVaListCB("upcallInts"), VaListConsumer.mh(vaList -> { - assertEquals(vaList.vargAsInt(C_INT), 10); - assertEquals(vaList.vargAsInt(C_INT), 15); - assertEquals(vaList.vargAsInt(C_INT), 20); + assertEquals(vaList.nextVarg(C_INT), 10); + assertEquals(vaList.nextVarg(C_INT), 15); + assertEquals(vaList.nextVarg(C_INT), 20); })}, { linkVaListCB("upcallStack"), VaListConsumer.mh(vaList -> { // skip all registers for (long l = 1; l <= 16; l++) { - assertEquals(vaList.vargAsLong(C_LONG_LONG), l); + assertEquals(vaList.nextVarg(C_LONG_LONG), l); } for (double d = 1; d <= 16; d++) { - assertEquals(vaList.vargAsDouble(C_DOUBLE), d); + assertEquals(vaList.nextVarg(C_DOUBLE), d); } // test some arbitrary values on the stack - assertEquals((byte) vaList.vargAsInt(C_INT), (byte) 1); - assertEquals((char) vaList.vargAsInt(C_INT), 'a'); - assertEquals((short) vaList.vargAsInt(C_INT), (short) 3); - assertEquals(vaList.vargAsInt(C_INT), 4); - assertEquals(vaList.vargAsLong(C_LONG_LONG), 5L); - assertEquals((float) vaList.vargAsDouble(C_DOUBLE), 6.0F); - assertEquals(vaList.vargAsDouble(C_DOUBLE), 7.0D); - assertEquals((byte) vaList.vargAsInt(C_INT), (byte) 8); - assertEquals((char) vaList.vargAsInt(C_INT), 'b'); - assertEquals((short) vaList.vargAsInt(C_INT), (short) 10); - assertEquals(vaList.vargAsInt(C_INT), 11); - assertEquals(vaList.vargAsLong(C_LONG_LONG), 12L); - assertEquals((float) vaList.vargAsDouble(C_DOUBLE), 13.0F); - assertEquals(vaList.vargAsDouble(C_DOUBLE), 14.0D); - - MemorySegment point = vaList.vargAsSegment(Point_LAYOUT, ResourceScope.newImplicitScope()); + assertEquals((byte) vaList.nextVarg(C_INT), (byte) 1); + assertEquals((char) vaList.nextVarg(C_INT), 'a'); + assertEquals((short) vaList.nextVarg(C_INT), (short) 3); + assertEquals(vaList.nextVarg(C_INT), 4); + assertEquals(vaList.nextVarg(C_LONG_LONG), 5L); + assertEquals((float) vaList.nextVarg(C_DOUBLE), 6.0F); + assertEquals(vaList.nextVarg(C_DOUBLE), 7.0D); + assertEquals((byte) vaList.nextVarg(C_INT), (byte) 8); + assertEquals((char) vaList.nextVarg(C_INT), 'b'); + assertEquals((short) vaList.nextVarg(C_INT), (short) 10); + assertEquals(vaList.nextVarg(C_INT), 11); + assertEquals(vaList.nextVarg(C_LONG_LONG), 12L); + assertEquals((float) vaList.nextVarg(C_DOUBLE), 13.0F); + assertEquals(vaList.nextVarg(C_DOUBLE), 14.0D); + + MemorySegment buffer = MemorySegment.allocateNative(BigPoint_LAYOUT, ResourceScope.newImplicitScope()); + SegmentAllocator bufferAllocator = SegmentAllocator.prefixAllocator(buffer); + + MemorySegment point = vaList.nextVarg(Point_LAYOUT, bufferAllocator); assertEquals((int) VH_Point_x.get(point), 5); assertEquals((int) VH_Point_y.get(point), 10); VaList copy = vaList.copy(); - MemorySegment bigPoint = vaList.vargAsSegment(BigPoint_LAYOUT, ResourceScope.newImplicitScope()); + MemorySegment bigPoint = vaList.nextVarg(BigPoint_LAYOUT, bufferAllocator); assertEquals((long) VH_BigPoint_x.get(bigPoint), 15); assertEquals((long) VH_BigPoint_y.get(bigPoint), 20); @@ -778,20 +797,20 @@ public static Object[][] upcalls() { VH_BigPoint_y.set(bigPoint, 0); // should be independent - MemorySegment struct = copy.vargAsSegment(BigPoint_LAYOUT, ResourceScope.newImplicitScope()); + MemorySegment struct = copy.nextVarg(BigPoint_LAYOUT, bufferAllocator); assertEquals((long) VH_BigPoint_x.get(struct), 15); assertEquals((long) VH_BigPoint_y.get(struct), 20); })}, // test skip { linkVaListCB("upcallStack"), VaListConsumer.mh(vaList -> { vaList.skip(C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG); - assertEquals(vaList.vargAsLong(C_LONG_LONG), 5L); + assertEquals(vaList.nextVarg(C_LONG_LONG), 5L); vaList.skip(C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG); - assertEquals(vaList.vargAsLong(C_LONG_LONG), 10L); + assertEquals(vaList.nextVarg(C_LONG_LONG), 10L); vaList.skip(C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG); - assertEquals(vaList.vargAsDouble(C_DOUBLE), 1.0D); + assertEquals(vaList.nextVarg(C_DOUBLE), 1.0D); vaList.skip(C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE); - assertEquals(vaList.vargAsDouble(C_DOUBLE), 6.0D); + assertEquals(vaList.nextVarg(C_DOUBLE), 6.0D); })}, }; } @@ -801,8 +820,10 @@ interface VaListConsumer { static MethodHandle mh(VaListConsumer instance) { try { - return MethodHandles.lookup().findVirtual(VaListConsumer.class, "accept", + MethodHandle handle = MethodHandles.lookup().findVirtual(VaListConsumer.class, "accept", MethodType.methodType(void.class, VaList.class)).bindTo(instance); + return MethodHandles.filterArguments(handle, 0, + MethodHandles.insertArguments(ADDRESS_TO_VALIST, 1, ResourceScope.newConfinedScope())); } catch (ReflectiveOperationException e) { throw new InternalError(e); } diff --git a/test/jdk/java/foreign/virtual/TestVirtualCalls.java b/test/jdk/java/foreign/virtual/TestVirtualCalls.java index 39f80bdce1d0f..70eecd151ceb4 100644 --- a/test/jdk/java/foreign/virtual/TestVirtualCalls.java +++ b/test/jdk/java/foreign/virtual/TestVirtualCalls.java @@ -24,6 +24,7 @@ /* * @test * @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" + * @library ../ * @run testng/othervm * --enable-native-access=ALL-UNNAMED * TestVirtualCalls @@ -34,28 +35,26 @@ import jdk.incubator.foreign.FunctionDescriptor; import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; +import jdk.incubator.foreign.NativeSymbol; import jdk.incubator.foreign.SymbolLookup; import jdk.incubator.foreign.MemoryAddress; import org.testng.annotations.*; -import static jdk.incubator.foreign.CLinker.*; import static org.testng.Assert.assertEquals; -public class TestVirtualCalls { +public class TestVirtualCalls extends NativeTestHelper { - static final CLinker abi = CLinker.getInstance(); + static final CLinker abi = CLinker.systemCLinker(); static final MethodHandle func; - static final MemoryAddress funcA; - static final MemoryAddress funcB; - static final MemoryAddress funcC; + static final NativeSymbol funcA; + static final NativeSymbol funcB; + static final NativeSymbol funcC; static { func = abi.downcallHandle( - MethodType.methodType(int.class), - FunctionDescriptor.of(C_INT)); + FunctionDescriptor.of(C_INT)); System.loadLibrary("Virtual"); SymbolLookup lookup = SymbolLookup.loaderLookup(); @@ -66,14 +65,14 @@ public class TestVirtualCalls { @Test public void testVirtualCalls() throws Throwable { - assertEquals((int) func.invokeExact((Addressable) funcA), 1); - assertEquals((int) func.invokeExact((Addressable) funcB), 2); - assertEquals((int) func.invokeExact((Addressable) funcC), 3); + assertEquals((int) func.invokeExact(funcA), 1); + assertEquals((int) func.invokeExact(funcB), 2); + assertEquals((int) func.invokeExact(funcC), 3); } @Test(expectedExceptions = NullPointerException.class) public void testNullTarget() throws Throwable { - int x = (int) func.invokeExact((Addressable) null); + int x = (int) func.invokeExact((NativeSymbol) null); } } diff --git a/test/jdk/java/io/Serializable/GetField/ReadFieldsCNF.java b/test/jdk/java/io/Serializable/GetField/ReadFieldsCNF.java new file mode 100644 index 0000000000000..7b91222d737e7 --- /dev/null +++ b/test/jdk/java/io/Serializable/GetField/ReadFieldsCNF.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8273660 + * @summary Verify that ObjectInputStream ReadFields correctly reports ClassNotFoundException + * while getting the field value. The test uses Vector that calls ReadFields from its readObject. + * @library /test/lib + * @run testng ReadFieldsCNF + * @run testng/othervm -Djdk.serialGetFieldCnfeReturnsNull=true ReadFieldsCNF + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.io.StreamCorruptedException; +import java.nio.charset.StandardCharsets; +import java.util.Vector; + +import org.testng.annotations.Test; +import org.testng.Assert; + +import jdk.test.lib.hexdump.HexPrinter; +import jdk.test.lib.hexdump.ObjectStreamPrinter; + +public class ReadFieldsCNF { + + private static final boolean GETFIELD_CNFE_RETURNS_NULL = + Boolean.getBoolean("jdk.serialGetFieldCnfeReturnsNull"); + + + /** + * Test a Vector holding a reference to a class instance that will not be found. + * @throws IOException If any other exception occurs + */ + @Test + private static void testVectorWithRole() throws IOException { + System.out.println("Property GETFIELD_CNFE_RETURNS_NULL: " + GETFIELD_CNFE_RETURNS_NULL); + + Role role = new Role(); + Vector vector = new Vector<>(); + vector.add(role); + + // Modify the byte stream to change the classname to be deserialized to + // XeadFieldsCNF$Role. + byte[] bytes = writeObject(vector); + + // Locate the name of the class to be deserialize + String s = new String(bytes, StandardCharsets.ISO_8859_1); // Map bytes to chars + int off = s.indexOf(Role.class.getName()); + System.out.printf("Role offset: %d (0x%x) : %s%n", off, off, Role.class.getName()); + if (off < 0) { + HexPrinter.simple().formatter(ObjectStreamPrinter.formatter()).format(bytes); + Assert.fail("classname not found"); + } + + bytes[off] = (byte) 'X'; // replace R with X -> Class not found + + // Deserialize the Vector expecting a ClassNotFoundException + ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes)); + try { + Object obj = in.readObject(); + System.out.println("Read: " + obj); + Assert.fail("Should not reach here, an exception should always occur"); + } catch (ClassNotFoundException cnfe) { + // Expected ClassNotFoundException + String expected = "XeadFieldsCNF$Role"; + Assert.assertEquals(expected, cnfe.getMessage(), "Wrong classname"); + if (GETFIELD_CNFE_RETURNS_NULL) { + Assert.fail("Expected IOException got ClassNotFoundException", cnfe); + } + System.out.println("Normal: OIS.readObject: " + cnfe); + } catch (StreamCorruptedException ioe) { + if (!GETFIELD_CNFE_RETURNS_NULL) { + Assert.fail("Expected ClassNotFoundException got StreamCorruptedException ", ioe); + } + System.out.println("Normal: " + ioe); + } + // Other exceptions cause the test to fail + } + + /** + * For an object holding a reference to a class that will not be found. + * @throws IOException If any other exception occurs + */ + @Test + private static void testHolderWithRole() throws IOException { + System.out.println("Property GETFIELD_CNFE_RETURNS_NULL: " + GETFIELD_CNFE_RETURNS_NULL); + Role role = new Role(); + Holder holder = new Holder(role); + + // Modify the byte stream to change the classname to be deserialized to + // XeadFieldsCNF$Role. + byte[] bytes = writeObject(holder); + + String s = new String(bytes, StandardCharsets.ISO_8859_1); // Map bytes to chars + int off = s.indexOf(Role.class.getName(), 0); + off = s.indexOf(Role.class.getName(), off + 1); // 2nd occurrence of classname + System.out.printf("Role offset: %d (0x%x)%n", off, off); + if (off < 0) { + HexPrinter.simple().formatter(ObjectStreamPrinter.formatter()).format(bytes); + Assert.fail("classname found at index: " + off + " (0x" + Integer.toHexString(off) + ")"); + } + + bytes[off] = (byte) 'X'; // replace R with X -> Class not found + + // Deserialize the Vector expecting a ClassNotFoundException + ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes)); + try { + Holder obj = (Holder)in.readObject(); + System.out.println("Read: " + obj); + Assert.fail("Should not reach here, an exception should always occur"); + } catch (ClassNotFoundException cnfe) { + // Expected ClassNotFoundException + String expected = "XeadFieldsCNF$Role"; + Assert.assertEquals(expected, cnfe.getMessage(), "Wrong classname"); + System.out.println("Normal: OIS.readObject: " + cnfe); + } catch (StreamCorruptedException ioe) { + if (!GETFIELD_CNFE_RETURNS_NULL) { + Assert.fail("Expected ClassNotFoundException got StreamCorruptedException ", ioe); + } + System.out.println("Normal: " + ioe); + } + // Other exceptions cause the test to fail + } + + private static byte[] writeObject(Object o) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ObjectOutputStream os = new ObjectOutputStream(baos)) { + os.writeObject(o); + } + return baos.toByteArray(); + } + + static class Role implements Serializable { + private static final long serialVersionUID = 0L; + + Role() {} + } + + static class Holder implements Serializable { + private static final long serialVersionUID = 1L; + + Role role; + + Holder(Role role) { + this.role = role; + } + + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + ObjectInputStream.GetField fields = ois.readFields(); + try { + Object repl = new Object(); + role = (Role)fields.get("role", repl); + System.out.println("Holder.readObject Role: " + role); + } catch (Exception ex) { + // Catch CNFE and ignore it; check elsewhere that CNFE is thrown from OIS.readObject + System.out.println("Normal: exception in Holder.readObject, ignoring: " + ex); + } + } + + public String toString() { + return "role: " + role; + } + } +} diff --git a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestExact.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestExact.java index 6c14c885a2410..bd1d1b58b6f3f 100644 --- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestExact.java +++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestExact.java @@ -47,6 +47,7 @@ */ import jdk.incubator.foreign.MemoryHandles; +import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; import jdk.internal.access.foreign.MemorySegmentProxy; @@ -170,7 +171,7 @@ public void testExactBufferSet(Class arrayClass, Object testValue, SetBufferX @Test(dataProvider = "dataSetMemorySegment") public void testExactSegmentSet(Class carrier, Object testValue, SetSegmentX setter) { - VarHandle vh = MemoryHandles.varHandle(carrier, ByteOrder.nativeOrder()); + VarHandle vh = MemoryHandles.varHandle(MemoryLayout.valueLayout(carrier, ByteOrder.nativeOrder())); try (ResourceScope scope = ResourceScope.newConfinedScope()) { MemorySegment seg = MemorySegment.allocateNative(8, scope); doTest(vh, diff --git a/test/jdk/java/lang/reflect/Field/NegativeTest.java b/test/jdk/java/lang/reflect/Field/NegativeTest.java new file mode 100644 index 0000000000000..c8a08beadb455 --- /dev/null +++ b/test/jdk/java/lang/reflect/Field/NegativeTest.java @@ -0,0 +1,582 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8277451 + * @run testng/othervm -Djdk.reflect.useDirectMethodHandle=true NegativeTest + * @run testng/othervm -Djdk.reflect.useDirectMethodHandle=false NegativeTest + * @summary Test exception thrown due to bad receiver and bad value on + * Field with and without setAccessible(true) + */ + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +public class NegativeTest { + static class Fields { + public static int si; + public static char sc; + public static byte sb; + public static short ss; + public static long sl; + public static double sd; + public static float sf; + public static boolean sz; + public static String so; + + public static final int sfi = 10; + public static final char sfc = 'a'; + public static final byte sfb = 1; + public static final short sfs = 2; + public static final long sfl = 1000L; + public static final double sfd = 1.0; + public static final float sff = 2.0f; + public static final boolean sfz = true; + public static final String sfo = "abc"; + + public int i; + public char c; + public byte b; + public short s; + public long l; + public double d; + public float f; + public boolean z; + public String o; + + public final int fi = 10; + public final char fc = 'a'; + public final byte fb = 1; + public final short fs = 2; + public final long fl = 1000L; + public final double fd = 1.0; + public final float ff = 2.0f; + public final boolean fz = true; + public final String fo = "abc"; + } + + static final Field i_field = field("i", false); + static final Field c_field = field("c", false); + static final Field b_field = field("b", false); + static final Field s_field = field("s", false); + static final Field l_field = field("l", false); + static final Field d_field = field("d", false); + static final Field f_field = field("f", false); + static final Field z_field = field("z", false); + static final Field o_field = field("o", false); + static final Field fi_field = field("fi", false); + static final Field fc_field = field("fc", false); + static final Field fb_field = field("fb", false); + static final Field fs_field = field("fs", false); + static final Field fl_field = field("fl", false); + static final Field fd_field = field("fd", false); + static final Field ff_field = field("ff", false); + static final Field fz_field = field("fz", false); + static final Field fo_field = field("fo", false); + + static final Field override_i_field = field("i", true); + static final Field override_c_field = field("c", true); + static final Field override_b_field = field("b", true); + static final Field override_s_field = field("s", true); + static final Field override_l_field = field("l", true); + static final Field override_d_field = field("d", true); + static final Field override_f_field = field("f", true); + static final Field override_z_field = field("z", true); + static final Field override_o_field = field("o", true); + static final Field override_fi_field = field("fi", true); + static final Field override_fc_field = field("fc", true); + static final Field override_fb_field = field("fb", true); + static final Field override_fs_field = field("fs", true); + static final Field override_fl_field = field("fl", true); + static final Field override_fd_field = field("fd", true); + static final Field override_ff_field = field("ff", true); + static final Field override_fz_field = field("fz", true); + static final Field override_fo_field = field("fo", true); + + static final Field si_field = field("si", false); + static final Field sc_field = field("sc", false); + static final Field sb_field = field("sb", false); + static final Field ss_field = field("ss", false); + static final Field sl_field = field("sl", false); + static final Field sd_field = field("sd", false); + static final Field sf_field = field("sf", false); + static final Field sz_field = field("sz", false); + static final Field so_field = field("so", false); + static final Field sfi_field = field("sfi", false); + static final Field sfc_field = field("sfc", false); + static final Field sfb_field = field("sfb", false); + static final Field sfs_field = field("sfs", false); + static final Field sfl_field = field("sfl", false); + static final Field sfd_field = field("sfd", false); + static final Field sff_field = field("sff", false); + static final Field sfz_field = field("sfz", false); + static final Field sfo_field = field("sfo", false); + + static final Field override_si_field = field("si", true); + static final Field override_sc_field = field("sc", true); + static final Field override_sb_field = field("sb", true); + static final Field override_ss_field = field("ss", true); + static final Field override_sl_field = field("sl", true); + static final Field override_sd_field = field("sd", true); + static final Field override_sf_field = field("sf", true); + static final Field override_sz_field = field("sz", true); + static final Field override_so_field = field("so", true); + static final Field override_sfi_field = field("sfi", true); + static final Field override_sfc_field = field("sfc", true); + static final Field override_sfb_field = field("sfb", true); + static final Field override_sfs_field = field("sfs", true); + static final Field override_sfl_field = field("sfl", true); + static final Field override_sfd_field = field("sfd", true); + static final Field override_sff_field = field("sff", true); + static final Field override_sfz_field = field("sfz", true); + static final Field override_sfo_field = field("sfo", true); + + private static Field field(String name, boolean suppressAccessCheck) { + try { + Field f = Fields.class.getDeclaredField(name); + if (suppressAccessCheck) { + f.setAccessible(true); + } + return f; + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @DataProvider(name = "instanceFields") + private Object[][] instanceFields() { + return new Object[][]{ + new Object[]{i_field}, + new Object[]{c_field}, + new Object[]{b_field}, + new Object[]{s_field}, + new Object[]{l_field}, + new Object[]{d_field}, + new Object[]{f_field}, + new Object[]{z_field}, + new Object[]{o_field}, + new Object[]{override_i_field}, + new Object[]{override_c_field}, + new Object[]{override_b_field}, + new Object[]{override_s_field}, + new Object[]{override_l_field}, + new Object[]{override_d_field}, + new Object[]{override_f_field}, + new Object[]{override_z_field}, + new Object[]{override_o_field}, + // final instance fields + new Object[]{fi_field}, + new Object[]{fc_field}, + new Object[]{fb_field}, + new Object[]{fs_field}, + new Object[]{fl_field}, + new Object[]{fd_field}, + new Object[]{ff_field}, + new Object[]{fz_field}, + new Object[]{fo_field}, + new Object[]{override_fi_field}, + new Object[]{override_fc_field}, + new Object[]{override_fb_field}, + new Object[]{override_fs_field}, + new Object[]{override_fl_field}, + new Object[]{override_fd_field}, + new Object[]{override_ff_field}, + new Object[]{override_fz_field}, + new Object[]{override_fo_field}, + }; + } + private static Fields INSTANCE = new Fields(); + + /* + * Test Field::get on a good receiver, a bad receiver and null. + * + * IllegalArgumentException is thrown if the receiver is of + * a bad type. NullPointerException is thrown if the receiver is null. + */ + @Test(dataProvider = "instanceFields") + public void testReceiver(Field f) throws ReflectiveOperationException { + f.get(INSTANCE); // good receiver + + testBadReceiver(f); + testNullReceiver(f); + } + + /* + * IllegalArgumentException should be thrown for bad receiver type + */ + private void testBadReceiver(Field f) throws ReflectiveOperationException { + assertFalse(Modifier.isStatic(f.getModifiers())); // instance field + Object badObj = new NegativeTest(); + try { + f.get(badObj); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + Class fType = f.getType(); + if (fType.isPrimitive()) { + try { + switch (fType.descriptorString()) { + case "B" -> f.getByte(badObj); + case "C" -> f.getChar(badObj); + case "D" -> f.getDouble(badObj); + case "F" -> f.getFloat(badObj); + case "I" -> f.getInt(badObj); + case "J" -> f.getLong(badObj); + case "S" -> f.getShort(badObj); + case "Z" -> f.getBoolean(badObj); + } + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + } + } + + /* + * NullPointerException should be thrown for null receiver + */ + private void testNullReceiver(Field f) throws ReflectiveOperationException { + assertFalse(Modifier.isStatic(f.getModifiers())); // instance field + try { + f.get(null); + fail("expected NullPointerException"); + } catch (NullPointerException e) { + // expected + } + + Class fType = f.getType(); + if (fType.isPrimitive()) { + try { + switch (fType.descriptorString()) { + case "B" -> f.getByte(null); + case "C" -> f.getChar(null); + case "D" -> f.getDouble(null); + case "F" -> f.getFloat(null); + case "I" -> f.getInt(null); + case "J" -> f.getLong(null); + case "S" -> f.getShort(null); + case "Z" -> f.getBoolean(null); + } + fail("expected NullPointerException"); + } catch (NullPointerException e) { + // expected + } + } + } + + @DataProvider(name = "writeableFields") + private Object[][] writeableFields() { + Fields obj = new Fields(); + return new Object[][]{ + // instance fields with and without setAccessible(true) + new Object[]{i_field, obj, Integer.valueOf(10)}, + new Object[]{c_field, obj, Character.valueOf('c')}, + new Object[]{b_field, obj, Byte.valueOf((byte)1)}, + new Object[]{s_field, obj, Short.valueOf((short)2)}, + new Object[]{l_field, obj, Long.valueOf(1000)}, + new Object[]{d_field, obj, Double.valueOf(1.2)}, + new Object[]{f_field, obj, Float.valueOf(2.5f)}, + new Object[]{z_field, obj, Boolean.valueOf(true)}, + new Object[]{o_field, obj, "good-value"}, + new Object[]{override_i_field, obj, Integer.valueOf(10)}, + new Object[]{override_c_field, obj, Character.valueOf('c')}, + new Object[]{override_b_field, obj, Byte.valueOf((byte)1)}, + new Object[]{override_s_field, obj, Short.valueOf((short)2)}, + new Object[]{override_l_field, obj, Long.valueOf(1000)}, + new Object[]{override_d_field, obj, Double.valueOf(1.2)}, + new Object[]{override_f_field, obj, Float.valueOf(2.5f)}, + new Object[]{override_z_field, obj, Boolean.valueOf(true)}, + new Object[]{override_o_field, obj, "good-value"}, + // instance final fields with setAccessible(true) + new Object[]{override_fi_field, obj, Integer.valueOf(10)}, + new Object[]{override_fc_field, obj, Character.valueOf('c')}, + new Object[]{override_fb_field, obj, Byte.valueOf((byte)1)}, + new Object[]{override_fs_field, obj, Short.valueOf((short)2)}, + new Object[]{override_fl_field, obj, Long.valueOf(1000)}, + new Object[]{override_fd_field, obj, Double.valueOf(1.2)}, + new Object[]{override_ff_field, obj, Float.valueOf(2.5f)}, + new Object[]{override_fz_field, obj, Boolean.valueOf(true)}, + new Object[]{override_fo_field, obj, "good-value"}, + // static fields with and without setAccessible(true) + new Object[]{si_field, null, Integer.valueOf(10)}, + new Object[]{sc_field, null, Character.valueOf('c')}, + new Object[]{sb_field, null, Byte.valueOf((byte)1)}, + new Object[]{ss_field, null, Short.valueOf((short)2)}, + new Object[]{sl_field, null, Long.valueOf(1000)}, + new Object[]{sd_field, null, Double.valueOf(1.2)}, + new Object[]{sf_field, null, Float.valueOf(2.5f)}, + new Object[]{sz_field, null, Boolean.valueOf(true)}, + new Object[]{so_field, null, "good-value"}, + new Object[]{override_si_field, null, Integer.valueOf(10)}, + new Object[]{override_sc_field, null, Character.valueOf('c')}, + new Object[]{override_sb_field, null, Byte.valueOf((byte)1)}, + new Object[]{override_ss_field, null, Short.valueOf((short)2)}, + new Object[]{override_sl_field, null, Long.valueOf(1000)}, + new Object[]{override_sd_field, null, Double.valueOf(1.2)}, + new Object[]{override_sf_field, null, Float.valueOf(2.5f)}, + new Object[]{override_sz_field, null, Boolean.valueOf(true)}, + new Object[]{override_so_field, null, "good-value"}, + }; + } + + /* + * Test Field::set with a good and bad value. + * Test setting to null if the field type is primitive. + * + * IllegalArgumentException is thrown if the value is of a bad type or null. + * NullPointerException is thrown if the receiver of an instance field is null. + * The receiver is checked + */ + @Test(dataProvider = "writeableFields") + public void testSetValue(Field f, Object obj, Object value) throws IllegalAccessException { + f.set(obj, value); + Class fType = f.getType(); + if (fType.isPrimitive()) { + switch (fType.descriptorString()) { + case "B" -> f.setByte(obj, ((Byte) value).byteValue()); + case "C" -> f.setChar(obj, ((Character) value).charValue()); + case "D" -> f.setDouble(obj, ((Double) value).doubleValue()); + case "F" -> f.setFloat(obj, ((Float) value).floatValue()); + case "I" -> f.setInt(obj, ((Integer) value).intValue()); + case "J" -> f.setLong(obj, ((Long) value).longValue()); + case "S" -> f.setShort(obj, ((Short) value).shortValue()); + case "Z" -> f.setBoolean(obj, ((Boolean) value).booleanValue()); + } + + // test null value only if it's primitive type + try { + f.set(obj, null); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + } + + Object badValue = new NegativeTest(); + try { + f.set(obj, badValue); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + } + + @DataProvider(name = "readOnlyFinalFields") + private Object[][] readOnlyFinalFields() { + Object obj = INSTANCE; + return new Object[][]{ + // instance final fields + new Object[]{fi_field, obj, Integer.valueOf(10)}, + new Object[]{fc_field, obj, Character.valueOf('c')}, + new Object[]{fb_field, obj, Byte.valueOf((byte)1)}, + new Object[]{fs_field, obj, Short.valueOf((short)2)}, + new Object[]{fl_field, obj, Long.valueOf(1000)}, + new Object[]{fd_field, obj, Double.valueOf(1.2)}, + new Object[]{ff_field, obj, Float.valueOf(2.5f)}, + new Object[]{fz_field, obj, Boolean.valueOf(true)}, + new Object[]{fo_field, obj, "good-value"}, + // static final fields + new Object[]{sfi_field, null, Integer.valueOf(10)}, + new Object[]{sfc_field, null, Character.valueOf('c')}, + new Object[]{sfb_field, null, Byte.valueOf((byte)1)}, + new Object[]{sfs_field, null, Short.valueOf((short)2)}, + new Object[]{sfl_field, null, Long.valueOf(1000)}, + new Object[]{sfd_field, null, Double.valueOf(1.2)}, + new Object[]{sff_field, null, Float.valueOf(2.5f)}, + new Object[]{sfz_field, null, Boolean.valueOf(true)}, + new Object[]{sfo_field, null, "good-value"}, + new Object[]{override_sfi_field, null, Integer.valueOf(10)}, + new Object[]{override_sfc_field, null, Character.valueOf('c')}, + new Object[]{override_sfb_field, null, Byte.valueOf((byte)1)}, + new Object[]{override_sfs_field, null, Short.valueOf((short)2)}, + new Object[]{override_sfl_field, null, Long.valueOf(1000)}, + new Object[]{override_sfd_field, null, Double.valueOf(1.2)}, + new Object[]{override_sff_field, null, Float.valueOf(2.5f)}, + new Object[]{override_sfz_field, null, Boolean.valueOf(true)}, + new Object[]{override_sfo_field, null, "good-value"}, + }; + } + + /* + * Test Field::set on a read-only final field. + * IllegalAccessException is thrown regardless of whether the value + * is of a bad type or not. + */ + @Test(dataProvider = "readOnlyFinalFields") + public void testSetValueOnFinalField(Field f, Object obj, Object value) { + assertTrue(Modifier.isFinal(f.getModifiers())); + try { + f.set(obj, value); + fail("expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // expected + } + + Class fType = f.getType(); + if (fType.isPrimitive()) { + try { + switch (fType.descriptorString()) { + case "B" -> f.setByte(obj, ((Byte)value).byteValue()); + case "C" -> f.setChar(obj, ((Character)value).charValue()); + case "D" -> f.setDouble(obj, ((Double)value).doubleValue()); + case "F" -> f.setFloat(obj, ((Float)value).floatValue()); + case "I" -> f.setInt(obj, ((Integer)value).intValue()); + case "J" -> f.setLong(obj, ((Long)value).longValue()); + case "S" -> f.setShort(obj, ((Short)value).shortValue()); + case "Z" -> f.setBoolean(obj, ((Boolean)value).booleanValue()); + } + fail("expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // expected + } + + // test null value only if it's primitive type + try { + f.set(obj, null); + fail("expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // expected + } + } + + Object badValue = new NegativeTest(); + try { + f.set(obj, badValue); + fail("expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // expected + } + } + + + + @DataProvider(name = "finalInstanceFields") + private Object[][] finalInstanceFields() { + return new Object[][]{ + new Object[]{fi_field, Integer.valueOf(10)}, + new Object[]{fc_field, Character.valueOf('c')}, + new Object[]{fb_field, Byte.valueOf((byte) 1)}, + new Object[]{fs_field, Short.valueOf((short) 2)}, + new Object[]{fl_field, Long.valueOf(1000)}, + new Object[]{fd_field, Double.valueOf(1.2)}, + new Object[]{ff_field, Float.valueOf(2.5f)}, + new Object[]{fz_field, Boolean.valueOf(true)}, + new Object[]{fo_field, "good-value"}, + }; + } + + /* + * Test Field::set on a final instance field with either a bad receiver + * or null. IllegalArgumentException is thrown if the receiver is of + * a bad type. NullPointerException is thrown if the receiver is null. + * The receiver is checked before the access check is performed and + * also before the value is checked. + */ + @Test(dataProvider = "finalInstanceFields") + public void testReceiverOnFinalField(Field f, Object value) { + assertTrue(Modifier.isFinal(f.getModifiers())); + Object badReceiver = new NegativeTest(); + // set the field with a bad receiver with a good value + try { + f.set(badReceiver, value); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } catch (IllegalAccessException e) { + throw new RuntimeException("Expected IllegalArgumentException but got: " + e.getMessage(), e); + } + + // set the field with a bad receiver with a bad value + Object badValue = new NegativeTest(); + try { + f.set(badReceiver, badValue); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } catch (IllegalAccessException e) { + throw new RuntimeException("Expected IllegalArgumentException but got: " + e.getMessage(), e); + } + + // set the field with a null receiver with a good value + try { + f.set(null, value); + fail("expected NullPointerException"); + } catch (NullPointerException e) { + // expected + } catch (IllegalAccessException e) { + throw new RuntimeException("Expected NullPointerException but got: " + e.getMessage(), e); + } + // set the field with a null receiver with a bad value + try { + f.set(null, badValue); + fail("expected NullPointerException"); + } catch (NullPointerException e) { + // expected + } catch (IllegalAccessException e) { + throw new RuntimeException("Expected NullPointerException but got: " + e.getMessage(), e); + } + + Class fType = f.getType(); + if (fType.isPrimitive()) { + // test bad receiver + try { + switch (fType.descriptorString()) { + case "B" -> f.setByte(badReceiver, ((Byte) value).byteValue()); + case "C" -> f.setChar(badReceiver, ((Character) value).charValue()); + case "D" -> f.setDouble(badReceiver, ((Double) value).doubleValue()); + case "F" -> f.setFloat(badReceiver, ((Float) value).floatValue()); + case "I" -> f.setInt(badReceiver, ((Integer) value).intValue()); + case "J" -> f.setLong(badReceiver, ((Long) value).longValue()); + case "S" -> f.setShort(badReceiver, ((Short) value).shortValue()); + case "Z" -> f.setBoolean(badReceiver, ((Boolean) value).booleanValue()); + } + } catch (IllegalArgumentException e) { + } catch (IllegalAccessException e) { + throw new RuntimeException("Expected IllegalArgumentException but got: " + e.getMessage(), e); + } + // test null receiver + try { + switch (fType.descriptorString()) { + case "B" -> f.setByte(null, ((Byte) value).byteValue()); + case "C" -> f.setChar(null, ((Character) value).charValue()); + case "D" -> f.setDouble(null, ((Double) value).doubleValue()); + case "F" -> f.setFloat(null, ((Float) value).floatValue()); + case "I" -> f.setInt(null, ((Integer) value).intValue()); + case "J" -> f.setLong(null, ((Long) value).longValue()); + case "S" -> f.setShort(null, ((Short) value).shortValue()); + case "Z" -> f.setBoolean(null, ((Boolean) value).booleanValue()); + } + } catch (NullPointerException e) { + // expected + } catch (IllegalAccessException e) { + throw new RuntimeException("Expected NullPointerException but got: " + e.getMessage(), e); + } + } + } +} diff --git a/test/jdk/java/lang/reflect/MethodHandleAccessorsTest.java b/test/jdk/java/lang/reflect/MethodHandleAccessorsTest.java index 3b702edc5f37b..1636bd704451b 100644 --- a/test/jdk/java/lang/reflect/MethodHandleAccessorsTest.java +++ b/test/jdk/java/lang/reflect/MethodHandleAccessorsTest.java @@ -115,6 +115,8 @@ public static final class Public { public static final int STATIC_FINAL = 1; private final int i; private final String s; + private static String name = "name"; + private byte b = 9; public Public() { this.i = 0; @@ -130,6 +132,11 @@ public Public(String s) { this.i = 0; this.s = s; } + public Public(byte b) { + this.b = b; + this.i = 0; + this.s = null; + } public Public(int first, int... rest) { this(varargs_primitive(first, rest)); @@ -162,6 +169,7 @@ public String toString() { return "Public{" + "i=" + i + ", s='" + s + '\'' + + ", b=" + b + '}'; } } @@ -385,11 +393,14 @@ static void doTest(Field f, Object target, Object oldValue, Object newValue, Thr new IllegalArgumentException("argument type mismatch"), new IllegalArgumentException("object is not an instance of declaring class"), }; - private static final Throwable[] cannot_get_final_field = new Throwable[] { - new IllegalArgumentException("Can not get final") + private static final Throwable[] cannot_get_field = new Throwable[] { + new IllegalArgumentException("Can not get") + }; + private static final Throwable[] cannot_set_field = new Throwable[] { + new IllegalArgumentException("Can not set") }; - private static final Throwable[] cannot_set_final_field = new Throwable[] { - new IllegalArgumentException("Can not set final") + private static final Throwable[] mismatched_field_type = new Throwable[] { + new IllegalArgumentException("Can not set") }; private static final Throwable[] wrong_argument_count_no_details = new Throwable[] { new IllegalArgumentException("wrong number of arguments") @@ -592,23 +603,30 @@ public void testLambdaProxyClass() throws Exception { @DataProvider(name = "readAccess") private Object[][] readAccess() { boolean newImpl = Boolean.getBoolean("jdk.reflect.useDirectMethodHandle"); + String wrongInst = new String(); return new Object[][]{ new Object[]{"i", new Public(100), 100, noException}, new Object[]{"s", new Public("test"), "test", noException}, - new Object[]{"s", new Object(), "test", - newImpl ? cannot_get_final_field : cannot_set_final_field}, new Object[]{"s", null, "test", null_target}, + new Object[]{"s", wrongInst, "test", + newImpl ? cannot_get_field : cannot_set_field}, + new Object[]{"b", wrongInst, 0, + newImpl ? cannot_get_field : cannot_set_field}, }; } @DataProvider(name = "writeAccess") private Object[][] writeAccess() { + Object o = new Object(); + byte b = 1; return new Object[][]{ new Object[]{"i", new Public(100), 100, 200, noException}, + new Object[]{"i", new Public(100), 100, Integer.valueOf(10), noException}, new Object[]{"s", new Public("test"), "test", "newValue", noException}, - // ## no exception thrown - // new Object[]{"i", new Public(100), 100, new Object(), cannot_set_final_field}, - new Object[]{"s", new Object(), "test", "dummy", cannot_set_final_field}, new Object[]{"s", null, "test", "dummy", null_target}, + new Object[]{"b", new Public(b), b, null, mismatched_field_type}, + new Object[]{"b", new Public(b), b, Long.valueOf(10), mismatched_field_type}, + new Object[]{"name", null, "name", o, mismatched_field_type}, + new Object[]{"i", new Public(100), 100, o, mismatched_field_type}, }; } diff --git a/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/SegmentTestDataProvider.java b/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/SegmentTestDataProvider.java index 772e112b23d87..7a4d16dff13ef 100644 --- a/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/SegmentTestDataProvider.java +++ b/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/SegmentTestDataProvider.java @@ -23,9 +23,7 @@ package org.openjdk.tests.java.util.stream; -import jdk.incubator.foreign.MemoryAccess; import jdk.incubator.foreign.MemoryLayout; -import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; import java.lang.invoke.VarHandle; @@ -36,12 +34,13 @@ import java.util.function.Function; import java.util.stream.Collectors; +import jdk.incubator.foreign.ValueLayout; import org.testng.annotations.DataProvider; public class SegmentTestDataProvider { static boolean compareSegmentsByte(Collection segments1, Collection segments2, boolean isOrdered) { - Function mapper = MemoryAccess::getByte; + Function mapper = s -> s.get(ValueLayout.JAVA_BYTE, 0); List list1 = segments1.stream() .map(mapper) .collect(Collectors.toList()); @@ -52,7 +51,7 @@ static boolean compareSegmentsByte(Collection segments1, Collecti } static boolean compareSegmentsChar(Collection segments1, Collection segments2, boolean isOrdered) { - Function mapper = MemoryAccess::getChar; + Function mapper = s -> s.get(ValueLayout.JAVA_CHAR, 0); List list1 = segments1.stream() .map(mapper) .collect(Collectors.toList()); @@ -63,7 +62,7 @@ static boolean compareSegmentsChar(Collection segments1, Collecti } static boolean compareSegmentsShort(Collection segments1, Collection segments2, boolean isOrdered) { - Function mapper = MemoryAccess::getShort; + Function mapper = s -> s.get(ValueLayout.JAVA_SHORT, 0); List list1 = segments1.stream() .map(mapper) .collect(Collectors.toList()); @@ -74,7 +73,7 @@ static boolean compareSegmentsShort(Collection segments1, Collect } static boolean compareSegmentsInt(Collection segments1, Collection segments2, boolean isOrdered) { - Function mapper = MemoryAccess::getInt; + Function mapper = s -> s.get(ValueLayout.JAVA_INT, 0); List list1 = segments1.stream() .map(mapper) .collect(Collectors.toList()); @@ -85,7 +84,7 @@ static boolean compareSegmentsInt(Collection segments1, Collectio } static boolean compareSegmentsLong(Collection segments1, Collection segments2, boolean isOrdered) { - Function mapper = MemoryAccess::getLong; + Function mapper = s-> s.get(ValueLayout.JAVA_LONG, 0); List list1 = segments1.stream() .map(mapper) .collect(Collectors.toList()); @@ -96,7 +95,7 @@ static boolean compareSegmentsLong(Collection segments1, Collecti } static boolean compareSegmentsFloat(Collection segments1, Collection segments2, boolean isOrdered) { - Function mapper = MemoryAccess::getFloat; + Function mapper = s -> s.get(ValueLayout.JAVA_FLOAT, 0); List list1 = segments1.stream() .map(mapper) .collect(Collectors.toList()); @@ -115,7 +114,7 @@ static Consumer segmentCopier(Consumer input) { } static boolean compareSegmentsDouble(Collection segments1, Collection segments2, boolean isOrdered) { - Function mapper = MemoryAccess::getDouble; + Function mapper = s -> s.get(ValueLayout.JAVA_DOUBLE, 0); List list1 = segments1.stream() .map(mapper) .collect(Collectors.toList()); @@ -127,18 +126,18 @@ static boolean compareSegmentsDouble(Collection segments1, Collec static void initSegment(MemorySegment segment) { for (int i = 0 ; i < segment.byteSize() ; i++) { - MemoryAccess.setByte(segment, (byte)i); + segment.set(ValueLayout.JAVA_BYTE, 0, (byte)i); } } static Object[][] spliteratorTestData = { - { "bytes", MemoryLayout.sequenceLayout(1024, MemoryLayouts.JAVA_BYTE), (SpliteratorTestHelper.ContentAsserter)SegmentTestDataProvider::compareSegmentsByte }, - { "chars", MemoryLayout.sequenceLayout(1024, MemoryLayouts.JAVA_CHAR), (SpliteratorTestHelper.ContentAsserter)SegmentTestDataProvider::compareSegmentsChar }, - { "shorts", MemoryLayout.sequenceLayout(1024, MemoryLayouts.JAVA_SHORT), (SpliteratorTestHelper.ContentAsserter)SegmentTestDataProvider::compareSegmentsShort }, - { "ints", MemoryLayout.sequenceLayout(1024, MemoryLayouts.JAVA_INT), (SpliteratorTestHelper.ContentAsserter)SegmentTestDataProvider::compareSegmentsInt }, - { "longs", MemoryLayout.sequenceLayout(1024, MemoryLayouts.JAVA_LONG), (SpliteratorTestHelper.ContentAsserter)SegmentTestDataProvider::compareSegmentsLong }, - { "floats", MemoryLayout.sequenceLayout(1024, MemoryLayouts.JAVA_FLOAT), (SpliteratorTestHelper.ContentAsserter)SegmentTestDataProvider::compareSegmentsFloat }, - { "doubles", MemoryLayout.sequenceLayout(1024, MemoryLayouts.JAVA_DOUBLE), (SpliteratorTestHelper.ContentAsserter)SegmentTestDataProvider::compareSegmentsDouble }, + { "bytes", MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_BYTE), (SpliteratorTestHelper.ContentAsserter)SegmentTestDataProvider::compareSegmentsByte }, + { "chars", MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_CHAR), (SpliteratorTestHelper.ContentAsserter)SegmentTestDataProvider::compareSegmentsChar }, + { "shorts", MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_SHORT), (SpliteratorTestHelper.ContentAsserter)SegmentTestDataProvider::compareSegmentsShort }, + { "ints", MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_INT), (SpliteratorTestHelper.ContentAsserter)SegmentTestDataProvider::compareSegmentsInt }, + { "longs", MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_LONG), (SpliteratorTestHelper.ContentAsserter)SegmentTestDataProvider::compareSegmentsLong }, + { "floats", MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_FLOAT), (SpliteratorTestHelper.ContentAsserter)SegmentTestDataProvider::compareSegmentsFloat }, + { "doubles", MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_DOUBLE), (SpliteratorTestHelper.ContentAsserter)SegmentTestDataProvider::compareSegmentsDouble }, }; // returns an array of (String name, Supplier>, ContentAsserter) diff --git a/test/jdk/java/util/zip/ZipOutputStream/EmptyComment.java b/test/jdk/java/util/zip/ZipOutputStream/EmptyComment.java new file mode 100644 index 0000000000000..0f52844f3f6b0 --- /dev/null +++ b/test/jdk/java/util/zip/ZipOutputStream/EmptyComment.java @@ -0,0 +1,111 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.ByteArrayOutputStream; +import java.util.function.Consumer; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertThrows; + +/** + * @test + * @bug 8277087 + * @summary Verifies various use cases when the zip comment should be empty + * @run testng EmptyComment + */ +public final class EmptyComment { + + @DataProvider() + Object[][] longLengths() { + return new Object[][]{{0xFFFF + 1}, {0xFFFF + 2}, {0xFFFF * 2}}; + } + + /** + * Overflow, the text is too long to be stored as a comment. + */ + @Test(dataProvider = "longLengths") + void testOverflow(int length) throws Exception { + test(zos -> assertThrows(IllegalArgumentException.class, () -> { + zos.setComment("X".repeat(length)); + })); + } + + /** + * Simple cases where the comment is set to the empty text. + */ + @Test + void testSimpleCases() throws Exception { + test(zos -> {/* do nothing */}); + test(zos -> zos.setComment(null)); + test(zos -> zos.setComment("")); + test(zos -> { + zos.setComment(""); + zos.setComment(null); + }); + test(zos -> { + zos.setComment(null); + zos.setComment(""); + }); + test(zos -> { + zos.setComment("Comment"); + zos.setComment(null); + }); + test(zos -> { + zos.setComment("Comment"); + zos.setComment(""); + }); + } + + private static void test(Consumer test) throws Exception { + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ZipOutputStream zos = new ZipOutputStream(baos)) { + + test.accept(zos); + + zos.putNextEntry(new ZipEntry("x")); + zos.finish(); + + byte[] data = baos.toByteArray(); + + if (data.length > 0xFFFF) { // just in case + throw new RuntimeException("data is too big: " + data.length); + } + int pk = data.length - ZipFile.ENDHDR; + if (data[pk] != 'P' || data[pk + 1] != 'K') { + throw new RuntimeException("PK is not found"); + } + // Since the comment is empty this will be two last bytes + int pos = data.length - ZipFile.ENDHDR + ZipFile.ENDCOM; + + int len = (data[pos] & 0xFF) + ((data[pos + 1] & 0xFF) << 8); + if (len != 0) { + throw new RuntimeException("zip comment is not empty: " + len); + } + } + } +} diff --git a/test/jdk/tools/jar/ContentOrder.java b/test/jdk/tools/jar/ContentOrder.java new file mode 100644 index 0000000000000..dc5315efde1be --- /dev/null +++ b/test/jdk/tools/jar/ContentOrder.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8276764 + * @summary test that the jar content ordering is sorted + * @library /test/lib + * @modules jdk.jartool + * @build jdk.test.lib.Platform + * jdk.test.lib.util.FileUtils + * @run testng ContentOrder + */ + +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.io.UncheckedIOException; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.spi.ToolProvider; +import java.util.stream.Stream; +import java.util.zip.ZipException; + +import jdk.test.lib.util.FileUtils; + +public class ContentOrder { + private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar") + .orElseThrow(() -> + new RuntimeException("jar tool not found") + ); + + private final String nl = System.lineSeparator(); + private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + private final PrintStream out = new PrintStream(baos); + private Runnable onCompletion; + + @BeforeMethod + public void reset() { + onCompletion = null; + } + + @AfterMethod + public void run() { + if (onCompletion != null) { + onCompletion.run(); + } + } + + // Test that the jar content ordering when processing a single directory is sorted + @Test + public void testSingleDir() throws IOException { + mkdir("testjar/Ctest1", "testjar/Btest2/subdir1", "testjar/Atest3"); + touch("testjar/Ctest1/testfile1", "testjar/Ctest1/testfile2", "testjar/Ctest1/testfile3"); + touch("testjar/Btest2/subdir1/testfileC", "testjar/Btest2/subdir1/testfileB", "testjar/Btest2/subdir1/testfileA"); + touch("testjar/Atest3/fileZ", "testjar/Atest3/fileY", "testjar/Atest3/fileX"); + + onCompletion = () -> rm("test.jar", "testjar"); + + jar("cf test.jar testjar"); + jar("tf test.jar"); + System.out.println(new String(baos.toByteArray())); + String output = "META-INF/" + nl + + "META-INF/MANIFEST.MF" + nl + + "testjar/" + nl + + "testjar/Atest3/" + nl + + "testjar/Atest3/fileX" + nl + + "testjar/Atest3/fileY" + nl + + "testjar/Atest3/fileZ" + nl + + "testjar/Btest2/" + nl + + "testjar/Btest2/subdir1/" + nl + + "testjar/Btest2/subdir1/testfileA" + nl + + "testjar/Btest2/subdir1/testfileB" + nl + + "testjar/Btest2/subdir1/testfileC" + nl + + "testjar/Ctest1/" + nl + + "testjar/Ctest1/testfile1" + nl + + "testjar/Ctest1/testfile2" + nl + + "testjar/Ctest1/testfile3" + nl; + Assert.assertEquals(baos.toByteArray(), output.getBytes()); + } + + // Test that when specifying multiple directories or releases that the sort + // ordering is done on each directory and release, reserving the order of + // the directories/releases specified on the command line + @Test + public void testMultiDirWithReleases() throws IOException { + mkdir("testjar/foo/classes", + "testjar/foo11/classes/Zclasses", + "testjar/foo11/classes/Yclasses", + "testjar/foo17/classes/Bclasses", + "testjar/foo17/classes/Aclasses"); + touch("testjar/foo/classes/testfile1", "testjar/foo/classes/testfile2"); + touch("testjar/foo11/classes/Zclasses/testfile1", "testjar/foo11/classes/Zclasses/testfile2"); + touch("testjar/foo11/classes/Yclasses/testfileA", "testjar/foo11/classes/Yclasses/testfileB"); + touch("testjar/foo17/classes/Bclasses/testfile1", "testjar/foo17/classes/Bclasses/testfile2"); + touch("testjar/foo17/classes/Aclasses/testfileA", "testjar/foo17/classes/Aclasses/testfileB"); + + onCompletion = () -> rm("test.jar", "testjar"); + + jar("cf test.jar -C testjar/foo classes " + + "--release 17 -C testjar/foo17 classes/Bclasses -C testjar/foo17 classes/Aclasses " + + "--release 11 -C testjar/foo11 classes/Zclasses -C testjar/foo11 classes/Yclasses"); + jar("tf test.jar"); + System.out.println(new String(baos.toByteArray())); + String output = "META-INF/" + nl + + "META-INF/MANIFEST.MF" + nl + + "classes/" + nl + + "classes/testfile1" + nl + + "classes/testfile2" + nl + + "META-INF/versions/17/classes/Bclasses/" + nl + + "META-INF/versions/17/classes/Bclasses/testfile1" + nl + + "META-INF/versions/17/classes/Bclasses/testfile2" + nl + + "META-INF/versions/17/classes/Aclasses/" + nl + + "META-INF/versions/17/classes/Aclasses/testfileA" + nl + + "META-INF/versions/17/classes/Aclasses/testfileB" + nl + + "META-INF/versions/11/classes/Zclasses/" + nl + + "META-INF/versions/11/classes/Zclasses/testfile1" + nl + + "META-INF/versions/11/classes/Zclasses/testfile2" + nl + + "META-INF/versions/11/classes/Yclasses/" + nl + + "META-INF/versions/11/classes/Yclasses/testfileA" + nl + + "META-INF/versions/11/classes/Yclasses/testfileB" + nl; + Assert.assertEquals(baos.toByteArray(), output.getBytes()); + } + + private Stream mkpath(String... args) { + return Arrays.stream(args).map(d -> Paths.get(".", d.split("/"))); + } + + private void mkdir(String... dirs) { + System.out.println("mkdir -p " + Arrays.toString(dirs)); + Arrays.stream(dirs).forEach(p -> { + try { + Files.createDirectories((new File(p)).toPath()); + } catch (IOException x) { + throw new UncheckedIOException(x); + } + }); + } + + private void touch(String... files) { + System.out.println("touch " + Arrays.toString(files)); + Arrays.stream(files).forEach(p -> { + try { + Files.createFile((new File(p)).toPath()); + } catch (IOException x) { + throw new UncheckedIOException(x); + } + }); + } + + private void rm(String... files) { + System.out.println("rm -rf " + Arrays.toString(files)); + Arrays.stream(files).forEach(p -> { + try { + Path path = (new File(p)).toPath(); + if (Files.isDirectory(path)) { + FileUtils.deleteFileTreeWithRetry(path); + } else { + FileUtils.deleteFileIfExistsWithRetry(path); + } + } catch (IOException x) { + throw new UncheckedIOException(x); + } + }); + } + + private void jar(String cmdline) throws IOException { + System.out.println("jar " + cmdline); + baos.reset(); + + // the run method catches IOExceptions, we need to expose them + ByteArrayOutputStream baes = new ByteArrayOutputStream(); + PrintStream err = new PrintStream(baes); + PrintStream saveErr = System.err; + System.setErr(err); + int rc = JAR_TOOL.run(out, err, cmdline.split(" +")); + System.setErr(saveErr); + if (rc != 0) { + String s = baes.toString(); + if (s.startsWith("java.util.zip.ZipException: duplicate entry: ")) { + throw new ZipException(s); + } + throw new IOException(s); + } + } +} diff --git a/test/jdk/tools/jar/modularJar/Basic.java b/test/jdk/tools/jar/modularJar/Basic.java index f6f95b6320797..26e9b835a496d 100644 --- a/test/jdk/tools/jar/modularJar/Basic.java +++ b/test/jdk/tools/jar/modularJar/Basic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1093,9 +1093,6 @@ static void javac(Path dest, Path modulePath, Path... sourceFiles) { List commands = new ArrayList<>(); - if (!TOOL_VM_OPTIONS.isEmpty()) { - commands.addAll(Arrays.asList(TOOL_VM_OPTIONS.split("\\s+", -1))); - } commands.add("-d"); commands.add(dest.toString()); if (dest.toString().contains("bar")) { diff --git a/test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java b/test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java new file mode 100644 index 0000000000000..376b261799854 --- /dev/null +++ b/test/jdk/tools/jar/modularJar/JarToolModuleDescriptorReproducibilityTest.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.module.ModuleDescriptor; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; +import java.util.spi.ToolProvider; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; + +/** + * @test + * @bug 8258117 + * @summary Tests that the content generated for module-info.class, using the jar command, is reproducible + * @run testng JarToolModuleDescriptorReproducibilityTest + */ +public class JarToolModuleDescriptorReproducibilityTest { + + private static final String MODULE_NAME = "foo"; + private static final String MODULE_VERSION = "1.2.3"; + private static final String UPDATED_MODULE_VERSION = "1.2.4"; + private static final String MAIN_CLASS = "jdk.test.foo.Foo"; + private static final Path MODULE_CLASSES_DIR = Path.of("8258117-module-classes", MODULE_NAME).toAbsolutePath(); + + private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar") + .orElseThrow(() + -> new RuntimeException("jar tool not found") + ); + private static final ToolProvider JAVAC_TOOL = ToolProvider.findFirst("javac") + .orElseThrow(() + -> new RuntimeException("javac tool not found") + ); + + + @BeforeClass + public static void setup() throws Exception { + compileModuleClasses(); + } + + /** + * Launches a "jar --create" command multiple times with a module-info.class. The module-info.class + * is internally updated by the jar tool to add additional data. Expects that each such generated + * jar has the exact same bytes. + */ + @Test + public void testJarCreate() throws Exception { + List jarFiles = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + Path targetJar = Files.createTempFile(Path.of("."), "8258117-jar-create", ".jar"); + jarFiles.add(targetJar); + if (i > 0) { + // the timestamp that gets embedded in (Zip/Jar)Entry gets narrowed + // down to SECONDS unit. So we make sure that there's at least a second + // gap between the jar file creations, to be sure that the jar file + // was indeed generated at "different times" + Thread.sleep(1000); + } + // create a modular jar + runJarCommand("--create", + "--file=" + targetJar, + "--main-class=" + MAIN_CLASS, + "--module-version=" + MODULE_VERSION, + "--no-manifest", + "-C", MODULE_CLASSES_DIR.toString(), "."); + // verify the module descriptor in the jar + assertExpectedModuleInfo(targetJar, MODULE_VERSION); + } + assertAllFileContentsAreSame(jarFiles); + } + + /** + * Launches a "jar --update" process multiple times to update the module-info.class + * descriptor with the same content and then expects that the modular jar created by + * each of these processes has the exact same bytes. + */ + @Test + public void testJarUpdate() throws Exception { + List jarFiles = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + Path targetJar = Files.createTempFile(Path.of("."), "8258117-jar-update", ".jar"); + jarFiles.add(targetJar); + if (i > 0) { + // the timestamp that gets embedded in (Zip/Jar)Entry gets narrowed + // down to SECONDS unit. So we make sure that there's at least a second + // gap between the jar file creations, to be sure that the jar file + // was indeed generated at "different times" + Thread.sleep(1000); + } + // first create the modular jar + runJarCommand("--create", + "--file=" + targetJar, + "--module-version=" + MODULE_VERSION, + "--no-manifest", + "-C", MODULE_CLASSES_DIR.toString(), "."); + assertExpectedModuleInfo(targetJar, MODULE_VERSION); + // now update the same modular jar + runJarCommand("--update", + "--file=" + targetJar, + "--module-version=" + UPDATED_MODULE_VERSION, + "--no-manifest", + "-C", MODULE_CLASSES_DIR.toString(), "module-info.class"); + // verify the module descriptor in the jar + assertExpectedModuleInfo(targetJar, UPDATED_MODULE_VERSION); + } + assertAllFileContentsAreSame(jarFiles); + } + + // compiles using javac tool the classes used in the test module + private static void compileModuleClasses() throws Exception { + Path sourcePath = Path.of(System.getProperty("test.src", "."), + "src", MODULE_NAME); + List sourceFiles = new ArrayList<>(); + Files.walkFileTree(sourcePath, new SimpleFileVisitor<>() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + if (file.toString().endsWith(".java")) { + sourceFiles.add(file.toString()); + } + return FileVisitResult.CONTINUE; + } + }); + Path classesDir = Files.createDirectories(MODULE_CLASSES_DIR); + List javacArgs = new ArrayList<>(); + javacArgs.add("-d"); + javacArgs.add(classesDir.toString()); + sourceFiles.forEach((f) -> javacArgs.add(f)); + System.out.println("Launching javac command with args: " + javacArgs); + StringWriter sw = new StringWriter(); + try (PrintWriter pw = new PrintWriter(sw)) { + int exitCode = JAVAC_TOOL.run(pw, pw, javacArgs.toArray(new String[0])); + assertEquals(exitCode, 0, "Module compilation failed: " + sw.toString()); + } + System.out.println("Module classes successfully compiled to directory " + classesDir); + } + + // runs the "jar" command passing it the "jarArgs" and verifying that the command + // execution didn't fail + private static void runJarCommand(String... jarArgs) { + StringWriter sw = new StringWriter(); + System.out.println("Launching jar command with args: " + Arrays.toString(jarArgs)); + try (PrintWriter pw = new PrintWriter(sw)) { + int exitCode = JAR_TOOL.run(pw, pw, jarArgs); + assertEquals(exitCode, 0, "jar command execution failed: " + sw.toString()); + } + } + + // verifies the byte equality of the contents in each of the files + private static void assertAllFileContentsAreSame(List files) throws Exception { + Path firstFile = files.get(0); + for (int i = 1; i < files.size(); i++) { + assertEquals(Files.mismatch(firstFile, files.get(i)), -1, + "Content in file " + files.get(i) + " isn't the same as in file " + firstFile); + } + } + + // verifies that a module-info.class is present in the jar and the module name and version are the expected + // ones + private static void assertExpectedModuleInfo(Path jar, String expectedModuleVersion) throws Exception { + try (JarInputStream jaris = new JarInputStream(Files.newInputStream(jar))) { + JarEntry moduleInfoEntry = null; + JarEntry entry = null; + while ((entry = jaris.getNextJarEntry()) != null) { + if (entry.getName().equals("module-info.class")) { + moduleInfoEntry = entry; + break; + } + } + assertNotNull(moduleInfoEntry, "module-info.class is missing from jar " + jar); + + ModuleDescriptor md = ModuleDescriptor.read(jaris); + assertEquals(md.name(), MODULE_NAME, "Unexpected module name"); + assertFalse(md.rawVersion().isEmpty(), "Module version missing from descriptor"); + + String actualVersion = md.rawVersion().get(); + assertEquals(actualVersion, expectedModuleVersion, "Unexpected module version"); + + System.out.println(moduleInfoEntry.getName() + " has a timestamp of " + + moduleInfoEntry.getTime() + " for version " + actualVersion); + } + } +} + diff --git a/test/jdk/tools/jar/multiRelease/MRTestBase.java b/test/jdk/tools/jar/multiRelease/MRTestBase.java index 8447b20b368be..abd5782fdcde2 100644 --- a/test/jdk/tools/jar/multiRelease/MRTestBase.java +++ b/test/jdk/tools/jar/multiRelease/MRTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -108,7 +108,6 @@ void javac(Path dest, Path... sourceFiles) throws Throwable { if (!opts.isEmpty()) { commands.addAll(Arrays.asList(opts.split(" +"))); } - commands.addAll(Utils.getForwardVmOptions()); commands.add("-d"); commands.add(dest.toString()); Stream.of(sourceFiles) diff --git a/test/jdk/tools/jmod/JmodTest.java b/test/jdk/tools/jmod/JmodTest.java index c32284faee652..bfec68b748beb 100644 --- a/test/jdk/tools/jmod/JmodTest.java +++ b/test/jdk/tools/jmod/JmodTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8142968 8166568 8166286 8170618 8168149 8240910 + * @bug 8142968 8166568 8166286 8170618 8168149 8240910 8276764 * @summary Basic test for jmod * @library /test/lib * @modules jdk.compiler @@ -197,6 +197,17 @@ public void testList() throws IOException { assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/Foo.class"); assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/internal/Message.class"); assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/resources/foo.properties"); + + // JDK-8276764: Ensure the order is sorted for reproducible jmod content + // module-info, followed by + int mod_info_i = r.output.indexOf(CLASSES_PREFIX + "module-info.class"); + int foo_cls_i = r.output.indexOf(CLASSES_PREFIX + "jdk/test/foo/Foo.class"); + int msg_i = r.output.indexOf(CLASSES_PREFIX + "jdk/test/foo/internal/Message.class"); + int res_i = r.output.indexOf(CLASSES_PREFIX + "jdk/test/foo/resources/foo.properties"); + System.out.println("jmod classes sort order check:\n"+r.output); + assertTrue(mod_info_i < foo_cls_i); + assertTrue(foo_cls_i < msg_i); + assertTrue(msg_i < res_i); }); } diff --git a/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java b/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java index cd30b24f350a3..a5b2ece2fc084 100644 --- a/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java @@ -24,6 +24,7 @@ import java.nio.file.Path; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.AdditionalLauncher; /** * Tests generation of app image with --mac-sign and related arguments. Test will @@ -65,13 +66,21 @@ public static void test() throws Exception { cmd.addArguments("--mac-sign", "--mac-signing-key-user-name", SigningBase.DEV_NAME, "--mac-signing-keychain", SigningBase.KEYCHAIN); + + AdditionalLauncher testAL = new AdditionalLauncher("testAL"); + testAL.applyTo(cmd); + cmd.executeAndAssertHelloAppImageCreated(); Path launcherPath = cmd.appLauncherPath(); SigningBase.verifyCodesign(launcherPath, true); + Path testALPath = launcherPath.getParent().resolve("testAL"); + SigningBase.verifyCodesign(testALPath, true); + Path appImage = cmd.outputBundle(); SigningBase.verifyCodesign(appImage, true); SigningBase.verifySpctl(appImage, "exec"); } + } diff --git a/test/langtools/jdk/javadoc/doclet/testSnippetTag/SnippetTester.java b/test/langtools/jdk/javadoc/doclet/testSnippetTag/SnippetTester.java index a220a4a0db95c..ebe13149bb258 100644 --- a/test/langtools/jdk/javadoc/doclet/testSnippetTag/SnippetTester.java +++ b/test/langtools/jdk/javadoc/doclet/testSnippetTag/SnippetTester.java @@ -101,6 +101,12 @@ protected String getSnippetHtmlRepresentation(String pathToHtmlFile, return getSnippetHtmlRepresentation(pathToHtmlFile, content, Optional.empty(), Optional.empty()); } + protected String getSnippetHtmlRepresentation(String pathToHtmlFile, + String content, + Optional lang) { + return getSnippetHtmlRepresentation(pathToHtmlFile, content, lang, Optional.empty()); + } + protected String getSnippetHtmlRepresentation(String pathToHtmlFile, String content, Optional lang, diff --git a/test/langtools/jdk/javadoc/doclet/testSnippetTag/TestLangProperties.java b/test/langtools/jdk/javadoc/doclet/testSnippetTag/TestLangProperties.java new file mode 100644 index 0000000000000..54b0c9c8da360 --- /dev/null +++ b/test/langtools/jdk/javadoc/doclet/testSnippetTag/TestLangProperties.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8266666 + * @summary Implementation for snippets + * @library /tools/lib ../../lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * jdk.javadoc/jdk.javadoc.internal.tool + * @build javadoc.tester.* toolbox.ToolBox toolbox.ModuleBuilder builder.ClassBuilder + * @run main TestLangProperties + */ + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class TestLangProperties extends SnippetTester { + + public static void main(String... args) throws Exception { + new TestLangProperties().runTests(m -> new Object[]{Paths.get(m.getName())}); + } + + @Test + public void testPositiveOuterMarkup(Path base) throws Exception { + var testCases = new ArrayList(); + for (String whitespace1 : List.of("", " ", "\t")) + for (String commentIndicator1 : List.of("#", "!")) + for (String whitespace2 : List.of("", " ", "\t")) { + String markup = whitespace1 + commentIndicator1 + + whitespace2 + "@highlight :"; + var t = new TestSnippetMarkup.TestCase( + """ + %s + coffee=espresso + tea=black + """.formatted(markup), + """ + + coffee=espresso + tea=black + """); + testCases.add(t); + } + testPositive(base, testCases); + } + + @Test + public void testPositiveInnerMarkup(Path base) throws Exception { + var testCases = new ArrayList(); + for (String whitespace1 : List.of("", " ", "\t")) + for (String commentIndicator1 : List.of("#", "!")) + for (String whitespace2 : List.of("", " ", "\t")) + for (String unrelatedComment : List.of("a comment")) + for (String whitespace3 : List.of("", " ")) + for (String commentIndicator2 : List.of("#", "!")) { + String payload = whitespace1 + commentIndicator1 + whitespace2 + unrelatedComment; + String markup = payload + whitespace3 + commentIndicator2 + "@highlight :"; + var t = new TestSnippetMarkup.TestCase( + """ + %s + coffee=espresso + tea=black + """.formatted(markup), + """ + %s + coffee=espresso + tea=black + """.formatted(payload)); + testCases.add(t); + } + testPositive(base, testCases); + } + + @Test + public void testPositiveIneffectiveOuterMarkup(Path base) throws Exception { + var testCases = new ArrayList(); + for (String whitespace1 : List.of("", " ", "\t")) + for (String commentIndicator1 : List.of("#", "!")) + for (String whitespace2 : List.of("", " ", "\t")) { + String ineffectiveMarkup = whitespace1 + + commentIndicator1 + whitespace2 + + "@highlight :"; + var t = new TestSnippetMarkup.TestCase( + """ + coffee=espresso%s + tea=black + """.formatted(ineffectiveMarkup), + """ + coffee=espresso%s + tea=black + """.formatted(ineffectiveMarkup)); + testCases.add(t); + } + testPositive(base, testCases); + } + + @Test + public void testPositiveIneffectiveInnerMarkup(Path base) throws Exception { + var testCases = new ArrayList(); + for (String whitespace1 : List.of("", " ", "\t")) + for (String commentIndicator1 : List.of("#", "!")) + for (String whitespace2 : List.of("", " ", "\t")) + for (String unrelatedComment : List.of("a comment")) + for (String whitespace3 : List.of("", " ")) + for (String commentIndicator2 : List.of("#", "!")) { + String ineffectiveMarkup = whitespace1 + + commentIndicator1 + whitespace2 + + unrelatedComment + whitespace3 + + commentIndicator2 + "@highlight :"; + var t = new TestSnippetMarkup.TestCase( + """ + coffee=espresso%s + tea=black + """.formatted(ineffectiveMarkup), + """ + coffee=espresso%s + tea=black + """.formatted(ineffectiveMarkup)); + testCases.add(t); + } + testPositive(base, testCases); + } + + private void testPositive(Path base, List testCases) + throws IOException { + StringBuilder methods = new StringBuilder(); + forEachNumbered(testCases, (i, n) -> { + String r = i.region().isBlank() ? "" : "region=" + i.region(); + var methodDef = """ + + /** + {@snippet lang="properties" %s: + %s}*/ + public void case%s() {} + """.formatted(r, i.input(), n); + methods.append(methodDef); + }); + var classDef = """ + public class A { + %s + } + """.formatted(methods.toString()); + Path src = Files.createDirectories(base.resolve("src")); + tb.writeJavaFiles(src, classDef); + javadoc("-d", base.resolve("out").toString(), + "-sourcepath", src.toString(), + src.resolve("A.java").toString()); + checkExit(Exit.OK); + checkNoCrashes(); + forEachNumbered(testCases, (t, index) -> { + String html = """ + case%s() +
    + %s +
    """.formatted(index, getSnippetHtmlRepresentation("A.html", + t.expectedOutput(), Optional.of("properties"))); + checkOutput("A.html", true, html); + }); + } +} diff --git a/test/langtools/jdk/jshell/ToolEnableNativeAccessTest.java b/test/langtools/jdk/jshell/ToolEnableNativeAccessTest.java new file mode 100644 index 0000000000000..212301c0fd83e --- /dev/null +++ b/test/langtools/jdk/jshell/ToolEnableNativeAccessTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8268725 + * @summary Tests for the --enable-native-access option + * @modules jdk.jshell + * @run testng ToolEnableNativeAccessTest + */ + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertTrue; + +public class ToolEnableNativeAccessTest extends ReplToolTesting { + + @Test + public void testOptionDebug() { + test( + (a) -> assertCommand(a, "/debug b", + "RemoteVM Options: []\n" + + "Compiler options: []"), + (a) -> assertCommand(a, "/env --enable-native-access", + "| Setting new options and restoring state."), + (a) -> assertCommandCheckOutput(a, "/debug b", s -> { + assertTrue(s.contains("RemoteVM Options: [--enable-native-access, ALL-UNNAMED]")); + assertTrue(s.contains("Compiler options: []")); + }) + ); + } + + @Test + public void testCommandLineFlag() { + test(new String[] {"--enable-native-access"}, + (a) -> assertCommandCheckOutput(a, "/debug b", s -> { + assertTrue(s.contains("RemoteVM Options: [--enable-native-access, ALL-UNNAMED]")); + assertTrue(s.contains("Compiler options: []")); + }) + ); + } + +} diff --git a/test/langtools/tools/javac/6521805/T6521805d.java b/test/langtools/tools/javac/6521805/T6521805d.java index 2cdce03c123e4..15535509d4478 100644 --- a/test/langtools/tools/javac/6521805/T6521805d.java +++ b/test/langtools/tools/javac/6521805/T6521805d.java @@ -7,6 +7,8 @@ * @compile/fail/ref=T6521805d.out T6521805d.java -XDrawDiagnostics */ +import java.util.Objects; + class T6521805 { static class Inner extends T6521805.Outer { @@ -22,6 +24,11 @@ public void foo() { } } - class Outer {} + class Outer { + { + // access enclosing instance so this$0 field is generated + Objects.requireNonNull(T6521805.this); + } + } } diff --git a/test/langtools/tools/javac/6521805/T6521805d.out b/test/langtools/tools/javac/6521805/T6521805d.out index 1cf1b8e229e6c..92562a1b53596 100644 --- a/test/langtools/tools/javac/6521805/T6521805d.out +++ b/test/langtools/tools/javac/6521805/T6521805d.out @@ -1,2 +1,2 @@ -T6521805d.java:18:18: compiler.err.cannot.generate.class: T6521805.Inner, (compiler.misc.synthetic.name.conflict: this$0, T6521805.Inner) +T6521805d.java:20:18: compiler.err.cannot.generate.class: T6521805.Inner, (compiler.misc.synthetic.name.conflict: this$0, T6521805.Inner) 1 error diff --git a/test/langtools/tools/javac/6521805/p/Outer.java b/test/langtools/tools/javac/6521805/p/Outer.java index 8a92158426b97..acbbfa0f91f39 100644 --- a/test/langtools/tools/javac/6521805/p/Outer.java +++ b/test/langtools/tools/javac/6521805/p/Outer.java @@ -2,6 +2,13 @@ package p; +import java.util.Objects; + class Outer { - class Super {} + class Super { + { + // access enclosing instance so this$0 field is generated + Objects.requireNonNull(Outer.this); + } + } } diff --git a/test/langtools/tools/javac/ClassFileModifiers/MemberModifiers.out b/test/langtools/tools/javac/ClassFileModifiers/MemberModifiers.out index 35b1bc29ca9b3..6d26076a57f21 100644 --- a/test/langtools/tools/javac/ClassFileModifiers/MemberModifiers.out +++ b/test/langtools/tools/javac/ClassFileModifiers/MemberModifiers.out @@ -1,8 +1,6 @@ CLASSFILE MemberModifiers.c --- SUPER -FIELD this$0 ---- FINAL METHOD --- @@ -20,8 +18,6 @@ METHOD m CLASSFILE MemberModifiersAux.Foo.c --- SUPER -FIELD this$1 ---- FINAL METHOD --- @@ -29,8 +25,6 @@ CLASSFILE MemberModifiersAux.Foo --- FINAL SUPER FIELD f --- -FIELD this$0 ---- FINAL METHOD --- METHOD m diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/AnnotatedExtendsTest.java b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/AnnotatedExtendsTest.java index 5705219aded0d..3c785debf42cd 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/AnnotatedExtendsTest.java +++ b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/AnnotatedExtendsTest.java @@ -59,7 +59,7 @@ public static strictfp void main(String args[]) throws Exception { .classes(classPath.toString()) .run() .getOutput(Task.OutputKind.DIRECT); - if (!javapOut.contains("0: #22(): CLASS_EXTENDS, type_index=65535")) + if (!javapOut.contains("0: #20(): CLASS_EXTENDS, type_index=65535")) throw new AssertionError("Expected output missing: " + javapOut); } -} \ No newline at end of file +} diff --git a/test/langtools/tools/javac/classfiles/attributes/Synthetic/AccessToPrivateInnerClassConstructorsTest.java b/test/langtools/tools/javac/classfiles/attributes/Synthetic/AccessToPrivateInnerClassConstructorsTest.java index 5fba9044c19d6..d7152de66ba01 100644 --- a/test/langtools/tools/javac/classfiles/attributes/Synthetic/AccessToPrivateInnerClassConstructorsTest.java +++ b/test/langtools/tools/javac/classfiles/attributes/Synthetic/AccessToPrivateInnerClassConstructorsTest.java @@ -44,14 +44,11 @@ "(AccessToPrivateInnerClassConstructorsTest)", "(AccessToPrivateInnerClassConstructorsTest, " + "AccessToPrivateInnerClassConstructorsTest$1)"}, - expectedNumberOfSyntheticFields = 1, expectedNumberOfSyntheticMethods = 0) @ExpectedClass(className = "AccessToPrivateInnerClassConstructorsTest$1Local", - expectedMethods = {"(AccessToPrivateInnerClassConstructorsTest)"}, - expectedNumberOfSyntheticFields = 1) + expectedMethods = {"(AccessToPrivateInnerClassConstructorsTest)"}) @ExpectedClass(className = "AccessToPrivateInnerClassConstructorsTest$2Local", - expectedMethods = {"(AccessToPrivateInnerClassConstructorsTest)"}, - expectedNumberOfSyntheticFields = 1) + expectedMethods = {"(AccessToPrivateInnerClassConstructorsTest)"}) public class AccessToPrivateInnerClassConstructorsTest { public static void main(String... args) { diff --git a/test/langtools/tools/javac/classfiles/attributes/Synthetic/AccessToPrivateInnerClassMembersTest.java b/test/langtools/tools/javac/classfiles/attributes/Synthetic/AccessToPrivateInnerClassMembersTest.java index 79fdf5d7e61dc..171a7a5627b03 100644 --- a/test/langtools/tools/javac/classfiles/attributes/Synthetic/AccessToPrivateInnerClassMembersTest.java +++ b/test/langtools/tools/javac/classfiles/attributes/Synthetic/AccessToPrivateInnerClassMembersTest.java @@ -43,15 +43,13 @@ * 3. access method for private method function(). * 4. getter/setter for private field staticVar. * 5. access method for private method staticFunction(). - * 6. field this in Inner1. - * 7. constructor for Inner*. + * 6. constructor for Inner*. */ @ExpectedClass(className = "AccessToPrivateInnerClassMembersTest", expectedMethods = {"()", "()"}) @ExpectedClass(className = "AccessToPrivateInnerClassMembersTest$Inner1", expectedMethods = {"(AccessToPrivateInnerClassMembersTest)", "function()"}, - expectedFields = "var", - expectedNumberOfSyntheticFields = 1) + expectedFields = "var") @ExpectedClass(className = "AccessToPrivateInnerClassMembersTest$Inner2", expectedMethods = {"function()", "staticFunction()", "()"}, expectedFields = {"staticVar", "var"}) diff --git a/test/langtools/tools/javac/classfiles/attributes/Synthetic/AccessToPrivateSiblingsTest.java b/test/langtools/tools/javac/classfiles/attributes/Synthetic/AccessToPrivateSiblingsTest.java index 47269cf877624..c0e039f83443e 100644 --- a/test/langtools/tools/javac/classfiles/attributes/Synthetic/AccessToPrivateSiblingsTest.java +++ b/test/langtools/tools/javac/classfiles/attributes/Synthetic/AccessToPrivateSiblingsTest.java @@ -43,14 +43,12 @@ * 3. access method for private method function(). * 4. getter/setter for private field staticVar. * 5. access method for private method staticFunction(). - * 6. field this in Inner1. - * 7. constructor for Inner*. + * 6. constructor for Inner*. */ @ExpectedClass(className = "AccessToPrivateSiblingsTest", expectedMethods = "()") @ExpectedClass(className = "AccessToPrivateSiblingsTest$Inner1", expectedMethods = {"function()", "(AccessToPrivateSiblingsTest)"}, - expectedFields = "var", - expectedNumberOfSyntheticFields = 1) + expectedFields = "var") @ExpectedClass(className = "AccessToPrivateSiblingsTest$Inner2", expectedMethods = "(AccessToPrivateSiblingsTest)", expectedNumberOfSyntheticFields = 1) diff --git a/test/langtools/tools/javac/classfiles/attributes/Synthetic/BridgeMethodsForLambdaTest.java b/test/langtools/tools/javac/classfiles/attributes/Synthetic/BridgeMethodsForLambdaTest.java index 90d0659d2b3a0..51d9f324e06cb 100644 --- a/test/langtools/tools/javac/classfiles/attributes/Synthetic/BridgeMethodsForLambdaTest.java +++ b/test/langtools/tools/javac/classfiles/attributes/Synthetic/BridgeMethodsForLambdaTest.java @@ -55,19 +55,16 @@ @ExpectedClass(className = "BridgeMethodsForLambdaTest$Inner1", expectedMethods = {"(BridgeMethodsForLambdaTest)", "function()", "run()"}, expectedFields = "lambda1", - expectedNumberOfSyntheticMethods = 1, - expectedNumberOfSyntheticFields = 1) + expectedNumberOfSyntheticMethods = 1) @ExpectedClass(className = "BridgeMethodsForLambdaTest$Inner2", expectedMethods = {"()", "staticFunction()"}, expectedFields = "lambda1", expectedNumberOfSyntheticMethods = 1) @ExpectedClass(className = "BridgeMethodsForLambdaTest$Inner3", - expectedMethods = {"(BridgeMethodsForLambdaTest)", "function()"}, - expectedNumberOfSyntheticFields = 1) + expectedMethods = {"(BridgeMethodsForLambdaTest)", "function()"}) @ExpectedClass(className = "BridgeMethodsForLambdaTest$Inner4", expectedMethods = {"(BridgeMethodsForLambdaTest)", "function()"}, - expectedNumberOfSyntheticMethods = 1, - expectedNumberOfSyntheticFields = 1) + expectedNumberOfSyntheticMethods = 1) public class BridgeMethodsForLambdaTest { private class Inner1 implements Runnable { diff --git a/test/langtools/tools/javac/classfiles/attributes/Synthetic/ThisFieldTest.java b/test/langtools/tools/javac/classfiles/attributes/Synthetic/ThisFieldTest.java index 605fb0cc6ded2..3ca6e65ef9004 100644 --- a/test/langtools/tools/javac/classfiles/attributes/Synthetic/ThisFieldTest.java +++ b/test/langtools/tools/javac/classfiles/attributes/Synthetic/ThisFieldTest.java @@ -34,6 +34,8 @@ * @run main SyntheticTestDriver ThisFieldTest */ +import java.util.Objects; + /** * Synthetic members: * 1. fields this$0 for local and anonymous classes. @@ -49,9 +51,17 @@ public class ThisFieldTest { { class Local { + { + // access enclosing instance so this$0 field is generated + Objects.requireNonNull(ThisFieldTest.this); + } } new Local() { + { + // access enclosing instance so this$0 field is generated + Objects.requireNonNull(ThisFieldTest.this); + } }; } } diff --git a/test/langtools/tools/javac/diags/examples/ErrSyntheticNameConflict.java b/test/langtools/tools/javac/diags/examples/ErrSyntheticNameConflict.java index 45d523dc08a67..d4b3ead499bbf 100644 --- a/test/langtools/tools/javac/diags/examples/ErrSyntheticNameConflict.java +++ b/test/langtools/tools/javac/diags/examples/ErrSyntheticNameConflict.java @@ -24,11 +24,18 @@ // key: compiler.err.cannot.generate.class // key: compiler.misc.synthetic.name.conflict +import java.util.Objects; + class ErrSyntheticNameConflict { static class Outer { ErrSyntheticNameConflict this$0 = null; } - public class Inner extends Outer { } + public class Inner extends Outer { + { + // access enclosing instance so this$0 field is generated + Objects.requireNonNull(ErrSyntheticNameConflict.this); + } + } } diff --git a/test/langtools/tools/javac/launcher/SourceLauncherTest.java b/test/langtools/tools/javac/launcher/SourceLauncherTest.java index 7507a6d9c276b..1b8fc23e67979 100644 --- a/test/langtools/tools/javac/launcher/SourceLauncherTest.java +++ b/test/langtools/tools/javac/launcher/SourceLauncherTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8192920 8204588 8246774 8248843 8268869 + * @bug 8192920 8204588 8246774 8248843 8268869 8235876 * @summary Test source launcher * @library /tools/lib * @modules jdk.compiler/com.sun.tools.javac.api @@ -630,6 +630,20 @@ public void testNoRecompileWithSuggestions(Path base) throws IOException { } } + @Test + public void testNoOptionsWarnings(Path base) throws IOException { + tb.writeJavaFiles(base, "public class Main { public static void main(String... args) {}}"); + String log = new JavaTask(tb) + .vmOptions("--source", "7") + .className(base.resolve("Main.java").toString()) + .run(Task.Expect.SUCCESS) + .getOutput(Task.OutputKind.STDERR); + + if (log.contains("warning: [options]")) { + error("Unexpected options warning in error output: " + log); + } + } + void testError(Path file, String expectStdErr, String expectFault) throws IOException { Result r = run(file, Collections.emptyList(), List.of("1", "2", "3")); checkEmpty("stdout", r.stdOut); diff --git a/test/langtools/tools/javac/optimizeOuterThis/DontOptimizeOuterThis.java b/test/langtools/tools/javac/optimizeOuterThis/DontOptimizeOuterThis.java new file mode 100644 index 0000000000000..dfba801d256b7 --- /dev/null +++ b/test/langtools/tools/javac/optimizeOuterThis/DontOptimizeOuterThis.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021, Google LLC. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Optional; + +/** + * @test + * @bug 8271623 + * + * @compile --release 17 DontOptimizeOuterThis.java InnerClasses.java + * @run main DontOptimizeOuterThis + */ +public class DontOptimizeOuterThis extends InnerClasses { + + public static void main(String[] args) { + new DontOptimizeOuterThis().test(); + } + + public void test() { + checkInner(localCapturesParameter(0), true); + checkInner(localCapturesLocal(), true); + checkInner(localCapturesEnclosing(), true); + + checkInner(anonCapturesParameter(0), true); + checkInner(anonCapturesLocal(), true); + checkInner(anonCapturesEnclosing(), true); + + checkInner(StaticMemberClass.class, false); // static + checkInner(NonStaticMemberClass.class, true); + checkInner(NonStaticMemberClassCapturesEnclosing.class, true); + + checkInner(N0.class, false); // static + checkInner(N0.N1.class, true); + checkInner(N0.N1.N2.class, true); + checkInner(N0.N1.N2.N3.class, true); + checkInner(N0.N1.N2.N3.N4.class, true); + checkInner(N0.N1.N2.N3.N4.N5.class, true); + + checkInner(SerializableCapture.class, true); + checkInner(SerializableWithSerialVersionUID.class, true); + checkInner(SerializableWithInvalidSerialVersionUIDType.class, true); + checkInner(SerializableWithInvalidSerialVersionUIDNonFinal.class, true); + checkInner(SerializableWithInvalidSerialVersionUIDNonStatic.class, true); + } + + private static void checkInner(Class clazz, boolean expectOuterThis) { + Optional outerThis = Arrays.stream(clazz.getDeclaredFields()) + .filter(f -> f.getName().startsWith("this$")).findFirst(); + if (expectOuterThis) { + if (outerThis.isEmpty()) { + throw new AssertionError( + String.format( + "expected %s to have an enclosing instance", clazz.getName())); + } + } else { + if (outerThis.isPresent()) { + throw new AssertionError( + String.format("%s had an unexpected enclosing instance", clazz.getName())); + } + } + } +} diff --git a/test/langtools/tools/javac/optimizeOuterThis/InnerClasses.java b/test/langtools/tools/javac/optimizeOuterThis/InnerClasses.java new file mode 100644 index 0000000000000..2f4ea1ed61511 --- /dev/null +++ b/test/langtools/tools/javac/optimizeOuterThis/InnerClasses.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2021, Google LLC. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.Serializable; + +public class InnerClasses { + + public Class localCapturesParameter(final int x) { + class Local { + public void f() { + System.err.println(x); + } + } + return Local.class; + } + + public Class localCapturesLocal() { + final int x = 0; + class Local { + public void f() { + System.err.println(x); + } + } + return Local.class; + } + + public Class localCapturesEnclosing() { + class Local { + public void f() { + System.err.println(InnerClasses.this); + } + } + return Local.class; + } + + public Class anonCapturesParameter(final int x) { + return new Object() { + public void f() { + System.err.println(x); + } + }.getClass(); + } + + public Class anonCapturesLocal() { + final int x = 0; + return new Object() { + public void f() { + System.err.println(x); + } + }.getClass(); + } + + public Class anonCapturesEnclosing() { + return new Object() { + public void f() { + System.err.println(InnerClasses.this); + } + }.getClass(); + } + + public static class StaticMemberClass {} + + public class NonStaticMemberClass {} + + public class NonStaticMemberClassCapturesEnclosing { + public void f() { + System.err.println(InnerClasses.this); + } + } + + static class N0 { + int x; + + class N1 { + class N2 { + class N3 { + void f() { + System.err.println(x); + } + + class N4 { + class N5 {} + } + } + } + } + } + + class SerializableCapture implements Serializable { + void f() { + System.err.println(InnerClasses.this); + } + } + + class SerializableWithSerialVersionUID implements Serializable { + private static final long serialVersionUID = 0; + } + + class SerializableWithInvalidSerialVersionUIDType implements Serializable { + private static final int serialVersionUID = 0; + } + + class SerializableWithInvalidSerialVersionUIDNonFinal implements Serializable { + private static long serialVersionUID = 0; + } + + class SerializableWithInvalidSerialVersionUIDNonStatic implements Serializable { + private final long serialVersionUID = 0; + } +} diff --git a/test/langtools/tools/javac/optimizeOuterThis/OptimizeOuterThis.java b/test/langtools/tools/javac/optimizeOuterThis/OptimizeOuterThis.java new file mode 100644 index 0000000000000..1a01d61f94ec8 --- /dev/null +++ b/test/langtools/tools/javac/optimizeOuterThis/OptimizeOuterThis.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2021, Google LLC. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Optional; + +/** + * @test + * @bug 8271623 + * + * @clean * + * @compile OptimizeOuterThis.java InnerClasses.java + * @run main OptimizeOuterThis + * + * @clean * + * @compile -XDoptimizeOuterThis=true --release 17 OptimizeOuterThis.java InnerClasses.java + * @run main OptimizeOuterThis + */ +public class OptimizeOuterThis extends InnerClasses { + + public static void main(String[] args) { + new OptimizeOuterThis().test(); + } + + public void test() { + checkInner(localCapturesParameter(0), false); + checkInner(localCapturesLocal(), false); + checkInner(localCapturesEnclosing(), true); + + checkInner(anonCapturesParameter(0), false); + checkInner(anonCapturesLocal(), false); + checkInner(anonCapturesEnclosing(), true); + + checkInner(StaticMemberClass.class, false); + checkInner(NonStaticMemberClass.class, false); + checkInner(NonStaticMemberClassCapturesEnclosing.class, true); + + checkInner(N0.class, false); + checkInner(N0.N1.class, true); + checkInner(N0.N1.N2.class, true); + checkInner(N0.N1.N2.N3.class, true); + checkInner(N0.N1.N2.N3.N4.class, false); + checkInner(N0.N1.N2.N3.N4.N5.class, false); + + checkInner(SerializableCapture.class, true); + checkInner(SerializableWithSerialVersionUID.class, false); + checkInner(SerializableWithInvalidSerialVersionUIDType.class, true); + checkInner(SerializableWithInvalidSerialVersionUIDNonFinal.class, true); + checkInner(SerializableWithInvalidSerialVersionUIDNonStatic.class, true); + } + + private static void checkInner(Class clazz, boolean expectOuterThis) { + Optional outerThis = Arrays.stream(clazz.getDeclaredFields()) + .filter(f -> f.getName().startsWith("this$")).findFirst(); + if (expectOuterThis) { + if (outerThis.isEmpty()) { + throw new AssertionError( + String.format( + "expected %s to have an enclosing instance", clazz.getName())); + } + } else { + if (outerThis.isPresent()) { + throw new AssertionError( + String.format("%s had an unexpected enclosing instance %s", clazz.getName(), outerThis.get())); + } + } + } +} diff --git a/test/langtools/tools/javac/options/modes/AtFilesTest.java b/test/langtools/tools/javac/options/modes/AtFilesTest.java index 761e20b53021e..5cf5c2acbaf79 100644 --- a/test/langtools/tools/javac/options/modes/AtFilesTest.java +++ b/test/langtools/tools/javac/options/modes/AtFilesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8044859 + * @bug 8044859 8272728 * @summary test support for at-files * @modules jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.file @@ -33,6 +33,7 @@ * @run main AtFilesTest */ +import com.sun.tools.javac.main.Main; import java.io.IOException; public class AtFilesTest extends OptionModesTester { @@ -60,4 +61,16 @@ void testAtFiles() throws IOException { runParse(opts, files) .checkIllegalArgumentException(); } + + @Test + void testAtFilesMustNotContainOptionJ() throws IOException { + writeFile("args", "-J-verbose"); + + String[] opts = { "@args", "-version" }; + String[] files = { }; + + runMain(opts, files) + .checkResult(Main.Result.CMDERR.exitCode) + .checkLog(Log.DIRECT, "-J-verbose"); + } } diff --git a/test/langtools/tools/javac/patterns/Domination.java b/test/langtools/tools/javac/patterns/Domination.java index b5283fe31a7ae..683758c297caf 100644 --- a/test/langtools/tools/javac/patterns/Domination.java +++ b/test/langtools/tools/javac/patterns/Domination.java @@ -68,6 +68,20 @@ int testDominatesStringConstant(String str) { } } + int testDominatesStringConstant2(String str) { + switch (str) { + case (String s && s.isEmpty()): return 1; + case "": return -1; + } + } + + int testDominatesStringConstant3(String str) { + switch (str) { + case (String s && !s.isEmpty()): return 1; + case "": return -1; + } + } + int testDominatesIntegerConstant(Integer i) { switch (i) { case Integer j: return 1; @@ -75,6 +89,20 @@ int testDominatesIntegerConstant(Integer i) { } } + int testDominatesIntegerConstant2(Integer i) { + switch (i) { + case (Integer j && j == 0): return 1; + case 0: return -1; + } + } + + int testDominatesIntegerConstant3(Integer i) { + switch (i) { + case (Integer j && j == 1): return 1; + case 0: return -1; + } + } + int testDominatesEnumConstant() { enum E { A, B; @@ -86,4 +114,26 @@ enum E { } } + int testDominatesEnumConstant2() { + enum E { + A, B; + } + E e = E.A; + switch (e) { + case (E d && d == E.A): return 1; + case A: return -1; + } + } + + int testDominatesEnumConstant3() { + enum E { + A, B; + } + E e = E.A; + switch (e) { + case (E d && d == E.B): return 1; + case A: return -1; + } + } + } diff --git a/test/langtools/tools/javac/patterns/Domination.out b/test/langtools/tools/javac/patterns/Domination.out index 3d1aa46bab0fc..80b95b8527d84 100644 --- a/test/langtools/tools/javac/patterns/Domination.out +++ b/test/langtools/tools/javac/patterns/Domination.out @@ -3,7 +3,13 @@ Domination.java:43:18: compiler.err.pattern.dominated Domination.java:51:18: compiler.err.pattern.dominated Domination.java:67:18: compiler.err.pattern.dominated Domination.java:74:18: compiler.err.pattern.dominated -Domination.java:85:18: compiler.err.pattern.dominated +Domination.java:81:18: compiler.err.pattern.dominated +Domination.java:88:18: compiler.err.pattern.dominated +Domination.java:95:18: compiler.err.pattern.dominated +Domination.java:102:18: compiler.err.pattern.dominated +Domination.java:113:18: compiler.err.pattern.dominated +Domination.java:124:18: compiler.err.pattern.dominated +Domination.java:135:18: compiler.err.pattern.dominated - compiler.note.preview.filename: Domination.java, DEFAULT - compiler.note.preview.recompile -6 errors +12 errors diff --git a/test/langtools/tools/javac/patterns/EnumTypeChanges.java b/test/langtools/tools/javac/patterns/EnumTypeChanges.java index f6a0aafe6e7ef..8d16efd796551 100644 --- a/test/langtools/tools/javac/patterns/EnumTypeChanges.java +++ b/test/langtools/tools/javac/patterns/EnumTypeChanges.java @@ -52,8 +52,8 @@ void doRun(Function c) throws Exception { String statementEnum(EnumTypeChangesEnum e) { switch (e) { case A -> { return "A"; } - case EnumTypeChangesEnum e1 && false -> throw new AssertionError(); case B -> { return "B"; } + case EnumTypeChangesEnum e1 && false -> throw new AssertionError(); default -> { return "D"; } } } @@ -61,8 +61,8 @@ String statementEnum(EnumTypeChangesEnum e) { String expressionEnum(EnumTypeChangesEnum e) { return switch (e) { case A -> "A"; - case EnumTypeChangesEnum e1 && false -> throw new AssertionError(); case B -> "B"; + case EnumTypeChangesEnum e1 && false -> throw new AssertionError(); default -> "D"; }; } diff --git a/test/langtools/tools/javac/patterns/Exhaustiveness.java b/test/langtools/tools/javac/patterns/Exhaustiveness.java index 9728fd83f48b0..36c824deeeed3 100644 --- a/test/langtools/tools/javac/patterns/Exhaustiveness.java +++ b/test/langtools/tools/javac/patterns/Exhaustiveness.java @@ -799,6 +799,94 @@ void test(A arg) { """); } + @Test + public void testOnlyApplicable(Path base) throws Exception { + record TestCase(String cases, String... errors) {} + TestCase[] subCases = new TestCase[] { + new TestCase(""" + case C3 c -> {} + case C5 c -> {} + case C6 c -> {} + """), //OK + new TestCase(""" + case C5 c -> {} + case C6 c -> {} + """, + "Test.java:11:9: compiler.err.not.exhaustive.statement", + "- compiler.note.preview.filename: Test.java, DEFAULT", + "- compiler.note.preview.recompile", + "1 error"), + new TestCase(""" + case C3 c -> {} + case C6 c -> {} + """, + "Test.java:11:9: compiler.err.not.exhaustive.statement", + "- compiler.note.preview.filename: Test.java, DEFAULT", + "- compiler.note.preview.recompile", + "1 error"), + new TestCase(""" + case C3 c -> {} + case C5 c -> {} + """, + "Test.java:11:9: compiler.err.not.exhaustive.statement", + "- compiler.note.preview.filename: Test.java, DEFAULT", + "- compiler.note.preview.recompile", + "1 error"), + new TestCase(""" + case C1 c -> {} + case C3 c -> {} + case C5 c -> {} + case C6 c -> {} + """, + "Test.java:12:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: test.Test.I, test.Test.C1)", + "- compiler.note.preview.filename: Test.java, DEFAULT", + "- compiler.note.preview.recompile", + "1 error"), + new TestCase(""" + case C2 c -> {} + case C3 c -> {} + case C5 c -> {} + case C6 c -> {} + """, + "Test.java:12:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: test.Test.I, test.Test.C2)", + "- compiler.note.preview.filename: Test.java, DEFAULT", + "- compiler.note.preview.recompile", + "1 error"), + new TestCase(""" + case C4 c -> {} + case C3 c -> {} + case C5 c -> {} + case C6 c -> {} + """, + "Test.java:12:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: test.Test.I, test.Test.C4)", + "- compiler.note.preview.filename: Test.java, DEFAULT", + "- compiler.note.preview.recompile", + "1 error"), + }; + for (TestCase tc : subCases) { + doTest(base, + new String[0], + """ + package test; + public class Test { + sealed interface I {} + final class C1 implements I {} + final class C2 implements I {} + final class C3 implements I {} + final class C4 implements I {} + final class C5 implements I {} + final class C6 implements I {} + void t(I i) { + switch (i) { + ${cases} + } + } + } + """.replace("${cases}", tc.cases), + tc.errors); + } + } + private void doTest(Path base, String[] libraryCode, String testCode, String... expectedErrors) throws IOException { Path current = base.resolve("."); Path libClasses = current.resolve("libClasses"); diff --git a/test/langtools/tools/javac/patterns/SwitchErrors.java b/test/langtools/tools/javac/patterns/SwitchErrors.java index ddeca570d3aa1..61f544a52c043 100644 --- a/test/langtools/tools/javac/patterns/SwitchErrors.java +++ b/test/langtools/tools/javac/patterns/SwitchErrors.java @@ -185,7 +185,16 @@ Object guardWithMatchingExpression(Object o1, Object o2) { default -> null; }; } - void test8269146a(Integer i) { + void test8269146a1(Integer i) { + switch (i) { + //error - illegal combination of pattern and constant: + case 1, Integer o && o != null: + break; + default: + break; + } + } + void test8269146a2(Integer i) { switch (i) { //error - illegal combination of pattern and constant: case Integer o && o != null, 1: @@ -210,7 +219,14 @@ void test8269146c(Integer i) { break; } } - void test8269301(Integer i) { + void test8269301a(Integer i) { + switch (i) { + //error - illegal combination of pattern, constant and default + case 1, Integer o && o != null, default: + break; + } + } + void test8269301b(Integer i) { switch (i) { //error - illegal combination of pattern, constant and default case Integer o && o != null, 1, default: diff --git a/test/langtools/tools/javac/patterns/SwitchErrors.out b/test/langtools/tools/javac/patterns/SwitchErrors.out index 9c4a8ff677f4f..4679651c800c6 100644 --- a/test/langtools/tools/javac/patterns/SwitchErrors.out +++ b/test/langtools/tools/javac/patterns/SwitchErrors.out @@ -31,12 +31,15 @@ SwitchErrors.java:160:18: compiler.err.pattern.dominated SwitchErrors.java:172:18: compiler.err.pattern.expected SwitchErrors.java:178:76: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null) SwitchErrors.java:184:71: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null) -SwitchErrors.java:191:42: compiler.err.flows.through.from.pattern -SwitchErrors.java:200:24: compiler.err.flows.through.to.pattern -SwitchErrors.java:209:29: compiler.err.total.pattern.and.default -SwitchErrors.java:216:42: compiler.err.flows.through.from.pattern -SwitchErrors.java:216:45: compiler.err.flows.through.from.pattern -SwitchErrors.java:228:18: compiler.err.duplicate.total.pattern +SwitchErrors.java:191:21: compiler.err.flows.through.to.pattern +SwitchErrors.java:200:42: compiler.err.pattern.dominated +SwitchErrors.java:209:24: compiler.err.flows.through.to.pattern +SwitchErrors.java:218:29: compiler.err.total.pattern.and.default +SwitchErrors.java:225:21: compiler.err.flows.through.to.pattern +SwitchErrors.java:225:45: compiler.err.flows.through.from.pattern +SwitchErrors.java:232:42: compiler.err.pattern.dominated +SwitchErrors.java:232:45: compiler.err.flows.through.from.pattern +SwitchErrors.java:244:18: compiler.err.duplicate.total.pattern SwitchErrors.java:9:9: compiler.err.not.exhaustive.statement SwitchErrors.java:15:9: compiler.err.not.exhaustive.statement SwitchErrors.java:21:9: compiler.err.not.exhaustive.statement @@ -48,7 +51,7 @@ SwitchErrors.java:91:9: compiler.err.not.exhaustive.statement SwitchErrors.java:97:9: compiler.err.not.exhaustive.statement SwitchErrors.java:104:9: compiler.err.not.exhaustive.statement SwitchErrors.java:164:9: compiler.err.not.exhaustive.statement -SwitchErrors.java:221:9: compiler.err.not.exhaustive.statement +SwitchErrors.java:237:9: compiler.err.not.exhaustive.statement - compiler.note.preview.filename: SwitchErrors.java, DEFAULT - compiler.note.preview.recompile -51 errors +54 errors diff --git a/test/langtools/tools/javac/patterns/Switches.java b/test/langtools/tools/javac/patterns/Switches.java index cf9858e7d0a07..714c7973140c6 100644 --- a/test/langtools/tools/javac/patterns/Switches.java +++ b/test/langtools/tools/javac/patterns/Switches.java @@ -265,8 +265,8 @@ String testEnumWithGuards1(E e) { switch (e) { case A: return "a"; case B: return "b"; - case E x && "A".equals(x.name()): return "broken"; case C: return String.valueOf(e); + case E x && "A".equals(x.name()): return "broken"; case null, E x: return String.valueOf(x); } } @@ -275,8 +275,8 @@ String testEnumWithGuardsExpression1(E e) { return switch (e) { case A -> "a"; case B -> "b"; - case E x && "A".equals(x.name()) -> "broken"; case C -> String.valueOf(e); + case E x && "A".equals(x.name()) -> "broken"; case null, E x -> String.valueOf(x); }; } @@ -286,8 +286,7 @@ String testEnumWithGuards2(E e) { case A: return "a"; case B: return "b"; case E x && "C".equals(x.name()): return "C"; - case C: return "broken"; - case null, E x: return String.valueOf(x); + case null, E x: return e == E.C ? "broken" : String.valueOf(x); } } @@ -296,8 +295,7 @@ String testEnumWithGuardsExpression2(E e) { case A -> "a"; case B -> "b"; case E x && "C".equals(x.name()) -> "C"; - case C -> "broken"; - case null, E x -> String.valueOf(x); + case null, E x -> e == E.C ? "broken" : String.valueOf(x); }; } @@ -306,8 +304,7 @@ String testEnumWithGuards3(E e) { case A: return "a"; case B: return "b"; case Object x && "C".equals(x.toString()): return "C"; - case C: return "broken"; - case null, E x: return String.valueOf(x); + case null, E x: return e == E.C ? "broken" : String.valueOf(x); } } @@ -316,8 +313,7 @@ String testEnumWithGuardsExpression3(E e) { case A -> "a"; case B -> "b"; case Object x && "C".equals(x.toString()) -> "C"; - case C -> "broken"; - case null, E x -> String.valueOf(x); + case null, E x -> e == E.C ? "broken" : String.valueOf(x); }; } @@ -326,8 +322,7 @@ String testEnumWithGuards4(E e) { case A: return "a"; case B: return "b"; case Runnable x && "C".equals(x.toString()): return "C"; - case C: return "broken"; - case null, E x: return String.valueOf(x); + case null, E x: return e == E.C ? "broken" : String.valueOf(x); } } @@ -336,8 +331,7 @@ String testEnumWithGuardsExpression4(E e) { case A -> "a"; case B -> "b"; case Runnable x && "C".equals(x.toString()) -> "C"; - case C -> "broken"; - case null, E x -> String.valueOf(x); + case null, E x -> e == E.C ? "broken" : String.valueOf(x); }; } @@ -346,8 +340,7 @@ String testStringWithGuards1(E e) { case "A": return "a"; case Switches.ConstantClassClash: return "b"; case String x && "C".equals(x): return "C"; - case "C": return "broken"; - case null, String x: return String.valueOf(x); + case null, String x: return "C".equals(x) ? "broken" : String.valueOf(x); } } @@ -356,8 +349,7 @@ String testStringWithGuardsExpression1(E e) { case "A" -> "a"; case ConstantClassClash -> "b"; case String x && "C".equals(x) -> "C"; - case "C" -> "broken"; - case null, String x -> String.valueOf(x); + case null, String x -> e == E.C ? "broken" : String.valueOf(x); }; } @@ -366,8 +358,7 @@ String testIntegerWithGuards1(E e) { case 0: return "a"; case 1: return "b"; case Integer x && x.equals(2): return "C"; - case 2: return "broken"; - case null, Integer x: return String.valueOf(x); + case null, Integer x: return Objects.equals(x, 2) ? "broken" : String.valueOf(x); } } @@ -376,8 +367,7 @@ String testIntegerWithGuardsExpression1(E e) { case 0 -> "a"; case 1 -> "b"; case Integer x && x.equals(2) -> "C"; - case 2 -> "broken"; - case null, Integer x -> String.valueOf(x); + case null, Integer x -> Objects.equals(x, 2) ? "broken" : String.valueOf(x); }; } @@ -412,7 +402,6 @@ Integer testFallThrough2Statement(Integer i) { switch (i) { case Integer o && o != null: r = 1; - case -1: r = 1; case null, default: r = 2; } @@ -424,7 +413,6 @@ Integer testFallThrough2Expression(Integer i) { int r = switch (i) { case Integer o && o != null: r = 1; - case -1: r = 1; case null, default: r = 2; yield r; diff --git a/test/langtools/tools/javac/records/BigRecordsToStringTest.java b/test/langtools/tools/javac/records/BigRecordsToStringTest.java new file mode 100644 index 0000000000000..87e269a953248 --- /dev/null +++ b/test/langtools/tools/javac/records/BigRecordsToStringTest.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8261847 + * @summary test the output of the toString method of records with a large number of components + * @run testng BigRecordsToStringTest + */ + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Parameter; +import java.util.List; +import java.util.function.Supplier; + +import org.testng.annotations.*; +import static org.testng.Assert.*; + +@Test +public class BigRecordsToStringTest { + record BigInt( + int i1,int i2,int i3,int i4,int i5,int i6,int i7,int i8,int i9,int i10, + int i11,int i12,int i13,int i14,int i15,int i16,int i17,int i18,int i19,int i20, + int i21,int i22,int i23,int i24,int i25,int i26,int i27,int i28,int i29,int i30, + int i31,int i32,int i33,int i34,int i35,int i36,int i37,int i38,int i39,int i40, + int i41,int i42,int i43,int i44,int i45,int i46,int i47,int i48,int i49,int i50, + int i51,int i52,int i53,int i54,int i55,int i56,int i57,int i58,int i59,int i60, + int i61,int i62,int i63,int i64,int i65,int i66,int i67,int i68,int i69,int i70, + int i71,int i72,int i73,int i74,int i75,int i76,int i77,int i78,int i79,int i80, + int i81,int i82,int i83,int i84,int i85,int i86,int i87,int i88,int i89,int i90, + int i91,int i92,int i93,int i94,int i95,int i96,int i97,int i98,int i99,int i100, + int i101,int i102,int i103,int i104,int i105,int i106,int i107,int i108,int i109,int i110, + int i111,int i112,int i113,int i114,int i115,int i116,int i117,int i118,int i119,int i120, + int i121,int i122,int i123,int i124,int i125,int i126,int i127,int i128,int i129,int i130, + int i131,int i132,int i133,int i134,int i135,int i136,int i137,int i138,int i139,int i140, + int i141,int i142,int i143,int i144,int i145,int i146,int i147,int i148,int i149,int i150, + int i151,int i152,int i153,int i154,int i155,int i156,int i157,int i158,int i159,int i160, + int i161,int i162,int i163,int i164,int i165,int i166,int i167,int i168,int i169,int i170, + int i171,int i172,int i173,int i174,int i175,int i176,int i177,int i178,int i179,int i180, + int i181,int i182,int i183,int i184,int i185,int i186,int i187,int i188,int i189,int i190, + int i191,int i192,int i193,int i194,int i195,int i196,int i197,int i198,int i199, int i200, + int i201,int i202,int i203,int i204,int i205,int i206,int i207,int i208,int i209,int i210, + int i211,int i212,int i213,int i214,int i215,int i216,int i217,int i218,int i219,int i220, + int i221,int i222,int i223,int i224,int i225,int i226,int i227,int i228,int i229,int i230, + int i231,int i232,int i233,int i234,int i235,int i236,int i237,int i238,int i239,int i240, + int i241,int i242,int i243,int i244,int i245,int i246,int i247,int i248,int i249,int i250, + int i251,int i252,int i253,int i254 + ) {} + + BigInt bigInt= new BigInt( + 1,2,3,4,5,6,7,8,9,10, + 11,12,13,14,15,16,17,18,19,20, + 21,22,23,24,25,26,27,28,29,30, + 31,32,33,34,35,36,37,38,39,40, + 41,42,43,44,45,46,47,48,49,50, + 51,52,53,54,55,56,57,58,59,60, + 61,62,63,64,65,66,67,68,69,70, + 71,72,73,74,75,76,77,78,79,80, + 81,82,83,84,85,86,87,88,89,90, + 91,92,93,94,95,96,97,98,99,100, + 101,102,103,104,105,106,107,108,109,110, + 111,112,113,114,115,116,117,118,119,120, + 121,122,123,124,125,126,127,128,129,130, + 131,132,133,134,135,136,137,138,139,140, + 141,142,143,144,145,146,147,148,149,150, + 151,152,153,154,155,156,157,158,159,160, + 161,162,163,164,165,166,167,168,169,170, + 171,172,173,174,175,176,177,178,179,180, + 181,182,183,184,185,186,187,188,189,190, + 191,192,193,194,195,196,197,198,199, 200, + 201,202,203,204,205,206,207,208,209,210, + 211,212,213,214,215,216,217,218,219,220, + 221,222,223,224,225,226,227,228,229,230, + 231,232,233,234,235,236,237,238,239,240, + 241,242,243,244,245,246,247,248,249,250, + 251,252,253,254 + ); + + record BigLong( + long i1,long i2,long i3,long i4,long i5,long i6,long i7,long i8,long i9,long i10, + long i11,long i12,long i13,long i14,long i15,long i16,long i17,long i18,long i19,long i20, + long i21,long i22,long i23,long i24,long i25,long i26,long i27,long i28,long i29,long i30, + long i31,long i32,long i33,long i34,long i35,long i36,long i37,long i38,long i39,long i40, + long i41,long i42,long i43,long i44,long i45,long i46,long i47,long i48,long i49,long i50, + long i51,long i52,long i53,long i54,long i55,long i56,long i57,long i58,long i59,long i60, + long i61,long i62,long i63,long i64,long i65,long i66,long i67,long i68,long i69,long i70, + long i71,long i72,long i73,long i74,long i75,long i76,long i77,long i78,long i79,long i80, + long i81,long i82,long i83,long i84,long i85,long i86,long i87,long i88,long i89,long i90, + long i91,long i92,long i93,long i94,long i95,long i96,long i97,long i98,long i99,long i100, + long i101,long i102,long i103,long i104,long i105,long i106,long i107,long i108,long i109,long i110, + long i111,long i112,long i113,long i114,long i115,long i116,long i117,long i118,long i119,long i120, + long i121,long i122,long i123,long i124,long i125,long i126,long i127 + ) {} + + BigLong bigLong = new BigLong( + 1,2,3,4,5,6,7,8,9,10, + 11,12,13,14,15,16,17,18,19,20, + 21,22,23,24,25,26,27,28,29,30, + 31,32,33,34,35,36,37,38,39,40, + 41,42,43,44,45,46,47,48,49,50, + 51,52,53,54,55,56,57,58,59,60, + 61,62,63,64,65,66,67,68,69,70, + 71,72,73,74,75,76,77,78,79,80, + 81,82,83,84,85,86,87,88,89,90, + 91,92,93,94,95,96,97,98,99,100, + 101,102,103,104,105,106,107,108,109,110, + 111,112,113,114,115,116,117,118,119,120, + 121,122,123,124,125,126,127 + ); + + private static final String BIG_INT_TO_STRING_OUTPUT = + "BigInt[i1=1, i2=2, i3=3, i4=4, i5=5, i6=6, i7=7, i8=8, i9=9, i10=10, i11=11, i12=12, i13=13, i14=14, i15=15, i16=16, " + + "i17=17, i18=18, i19=19, i20=20, i21=21, i22=22, i23=23, i24=24, i25=25, i26=26, i27=27, i28=28, i29=29, i30=30, " + + "i31=31, i32=32, i33=33, i34=34, i35=35, i36=36, i37=37, i38=38, i39=39, i40=40, i41=41, i42=42, i43=43, i44=44, " + + "i45=45, i46=46, i47=47, i48=48, i49=49, i50=50, i51=51, i52=52, i53=53, i54=54, i55=55, i56=56, i57=57, i58=58, " + + "i59=59, i60=60, i61=61, i62=62, i63=63, i64=64, i65=65, i66=66, i67=67, i68=68, i69=69, i70=70, i71=71, i72=72, " + + "i73=73, i74=74, i75=75, i76=76, i77=77, i78=78, i79=79, i80=80, i81=81, i82=82, i83=83, i84=84, i85=85, i86=86, " + + "i87=87, i88=88, i89=89, i90=90, i91=91, i92=92, i93=93, i94=94, i95=95, i96=96, i97=97, i98=98, i99=99, i100=100, " + + "i101=101, i102=102, i103=103, i104=104, i105=105, i106=106, i107=107, i108=108, i109=109, i110=110, i111=111, i112=112, " + + "i113=113, i114=114, i115=115, i116=116, i117=117, i118=118, i119=119, i120=120, i121=121, i122=122, i123=123, i124=124, " + + "i125=125, i126=126, i127=127, i128=128, i129=129, i130=130, i131=131, i132=132, i133=133, i134=134, i135=135, i136=136, " + + "i137=137, i138=138, i139=139, i140=140, i141=141, i142=142, i143=143, i144=144, i145=145, i146=146, i147=147, i148=148, " + + "i149=149, i150=150, i151=151, i152=152, i153=153, i154=154, i155=155, i156=156, i157=157, i158=158, i159=159, i160=160, " + + "i161=161, i162=162, i163=163, i164=164, i165=165, i166=166, i167=167, i168=168, i169=169, i170=170, i171=171, i172=172, " + + "i173=173, i174=174, i175=175, i176=176, i177=177, i178=178, i179=179, i180=180, i181=181, i182=182, i183=183, i184=184, " + + "i185=185, i186=186, i187=187, i188=188, i189=189, i190=190, i191=191, i192=192, i193=193, i194=194, i195=195, i196=196, " + + "i197=197, i198=198, i199=199, i200=200, i201=201, i202=202, i203=203, i204=204, i205=205, i206=206, i207=207, i208=208, " + + "i209=209, i210=210, i211=211, i212=212, i213=213, i214=214, i215=215, i216=216, i217=217, i218=218, i219=219, i220=220, " + + "i221=221, i222=222, i223=223, i224=224, i225=225, i226=226, i227=227, i228=228, i229=229, i230=230, i231=231, i232=232, " + + "i233=233, i234=234, i235=235, i236=236, i237=237, i238=238, i239=239, i240=240, i241=241, i242=242, i243=243, i244=244, " + + "i245=245, i246=246, i247=247, i248=248, i249=249, i250=250, i251=251, i252=252, i253=253, i254=254]"; + + private static final String BIG_LONG_TO_STRING_OUTPUT = + "BigLong[i1=1, i2=2, i3=3, i4=4, i5=5, i6=6, i7=7, i8=8, i9=9, i10=10, i11=11, i12=12, i13=13, i14=14, i15=15, i16=16, i17=17, " + + "i18=18, i19=19, i20=20, i21=21, i22=22, i23=23, i24=24, i25=25, i26=26, i27=27, i28=28, i29=29, i30=30, i31=31, i32=32, i33=33, " + + "i34=34, i35=35, i36=36, i37=37, i38=38, i39=39, i40=40, i41=41, i42=42, i43=43, i44=44, i45=45, i46=46, i47=47, i48=48, i49=49, " + + "i50=50, i51=51, i52=52, i53=53, i54=54, i55=55, i56=56, i57=57, i58=58, i59=59, i60=60, i61=61, i62=62, i63=63, i64=64, i65=65, " + + "i66=66, i67=67, i68=68, i69=69, i70=70, i71=71, i72=72, i73=73, i74=74, i75=75, i76=76, i77=77, i78=78, i79=79, i80=80, i81=81, " + + "i82=82, i83=83, i84=84, i85=85, i86=86, i87=87, i88=88, i89=89, i90=90, i91=91, i92=92, i93=93, i94=94, i95=95, i96=96, i97=97, " + + "i98=98, i99=99, i100=100, i101=101, i102=102, i103=103, i104=104, i105=105, i106=106, i107=107, i108=108, i109=109, i110=110, " + + "i111=111, i112=112, i113=113, i114=114, i115=115, i116=116, i117=117, i118=118, i119=119, i120=120, i121=121, i122=122, i123=123, " + + "i124=124, i125=125, i126=126, i127=127]"; + + public void testToStringOutput() { + assertTrue(bigInt.toString().equals(BIG_INT_TO_STRING_OUTPUT)); + assertTrue(bigLong.toString().equals(BIG_LONG_TO_STRING_OUTPUT)); + } +} diff --git a/test/langtools/tools/javap/AnnoTest.java b/test/langtools/tools/javap/AnnoTest.java index 7d78d163e296d..649d7e7b38afa 100644 --- a/test/langtools/tools/javap/AnnoTest.java +++ b/test/langtools/tools/javap/AnnoTest.java @@ -49,50 +49,50 @@ void run() throws Exception { expect(out, "RuntimeVisibleAnnotations:\n" + - " 0: #21(#22=B#23)\n" + + " 0: #17(#18=B#19)\n" + " AnnoTest$ByteAnno(\n" + " value=(byte) 42\n" + " )\n" + - " 1: #24(#22=S#25)\n" + + " 1: #20(#18=S#21)\n" + " AnnoTest$ShortAnno(\n" + " value=(short) 3\n" + " )"); expect(out, "RuntimeInvisibleAnnotations:\n" + - " 0: #27(#22=[J#28,J#30,J#32,J#34,J#36])\n" + + " 0: #23(#18=[J#24,J#26,J#28,J#30,J#32])\n" + " AnnoTest$ArrayAnno(\n" + " value=[1l,2l,3l,4l,5l]\n" + " )\n" + - " 1: #38(#22=Z#39)\n" + + " 1: #34(#18=Z#35)\n" + " AnnoTest$BooleanAnno(\n" + " value=false\n" + " )\n" + - " 2: #40(#41=c#42)\n" + + " 2: #36(#37=c#38)\n" + " AnnoTest$ClassAnno(\n" + " type=class Ljava/lang/Object;\n" + " )\n" + - " 3: #43(#44=e#45.#46)\n" + + " 3: #39(#40=e#41.#42)\n" + " AnnoTest$EnumAnno(\n" + " kind=Ljavax/lang/model/element/ElementKind;.PACKAGE\n" + " )\n" + - " 4: #47(#22=I#48)\n" + + " 4: #43(#18=I#44)\n" + " AnnoTest$IntAnno(\n" + " value=2\n" + " )\n" + - " 5: #49()\n" + + " 5: #45()\n" + " AnnoTest$IntDefaultAnno\n" + - " 6: #50(#51=s#52)\n" + + " 6: #46(#47=s#48)\n" + " AnnoTest$NameAnno(\n" + " name=\"NAME\"\n" + " )\n" + - " 7: #53(#54=D#55,#57=F#58)\n" + + " 7: #49(#50=D#51,#53=F#54)\n" + " AnnoTest$MultiAnno(\n" + " d=3.14159d\n" + " f=2.71828f\n" + " )\n" + - " 8: #59()\n" + + " 8: #55()\n" + " AnnoTest$SimpleAnno\n" + - " 9: #60(#22=@#47(#22=I#61))\n" + + " 9: #56(#18=@#43(#18=I#57))\n" + " AnnoTest$AnnoAnno(\n" + " value=@AnnoTest$IntAnno(\n" + " value=5\n" + @@ -100,7 +100,7 @@ void run() throws Exception { " )"); expect(out, "RuntimeInvisibleTypeAnnotations:\n" + - " 0: #63(): CLASS_EXTENDS, type_index=0\n" + + " 0: #59(): CLASS_EXTENDS, type_index=0\n" + " AnnoTest$TypeAnno"); if (errors > 0) diff --git a/test/langtools/tools/jdeps/multiVersion/MultiVersionError.java b/test/langtools/tools/jdeps/multiVersion/MultiVersionError.java new file mode 100644 index 0000000000000..62513406b7139 --- /dev/null +++ b/test/langtools/tools/jdeps/multiVersion/MultiVersionError.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8277165 + * @library ../lib + * @build CompilerUtils + * @run testng MultiVersionError + * @summary Tests multiple versions of the same class file + */ + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Set; +import java.util.spi.ToolProvider; + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertTrue; + +public class MultiVersionError { + private static final String TEST_SRC = System.getProperty("test.src"); + private static final Path SRC_DIR = Paths.get(TEST_SRC, "src"); + + private static final Path MODS_DIR = Paths.get("mods"); + + private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar").orElseThrow(); + private static final Set modules = Set.of("m1", "m2"); + + /** + * Compiles classes used by the test + */ + @BeforeTest + public void compileAll() throws Exception { + CompilerUtils.cleanDir(MODS_DIR); + modules.forEach(mn -> + assertTrue(CompilerUtils.compileModule(SRC_DIR, MODS_DIR, mn))); + + // create a modular multi-release m1.jar + Path m1 = MODS_DIR.resolve("m1"); + Path m2 = MODS_DIR.resolve("m2"); + jar("cf", "m1.jar", "-C", m1.toString(), "p/Test.class", + "--release", "9", "-C", m1.toString(), "module-info.class", + "--release", "11", "-C", m1.toString(), "p/internal/P.class"); + jar("cf", "m2.jar", "-C", m2.toString(), "q/Q.class", + "--release", "10", "-C", m2.toString(), "module-info.class"); + + // package private p/internal/P.class in m1 instead + jar("cf", "m3.jar", "-C", m2.toString(), "q/Q.class", + "--release", "12", "-C", m2.toString(), "module-info.class", + "-C", m1.toString(), "p/internal/P.class"); + } + + /* + * multiple module-info.class from different versions should be excluded + * from multiple version check. + */ + @Test + public void noMultiVersionClass() { + // skip parsing p.internal.P to workaround JDK-8277681 + JdepsRunner jdepsRunner = new JdepsRunner("--print-module-deps", "--multi-release", "10", + "--ignore-missing-deps", + "--module-path", "m1.jar", "m2.jar"); + int rc = jdepsRunner.run(true); + assertTrue(rc == 0); + assertTrue(jdepsRunner.outputContains("java.base,m1")); + } + + /* + * Detect multiple versions of p.internal.P class + */ + @Test + public void classInMultiVersions() { + JdepsRunner jdepsRunner = new JdepsRunner("--print-module-deps", "--multi-release", "13", + "--module-path", "m1.jar", "m3.jar"); + int rc = jdepsRunner.run(true); + assertTrue(rc != 0); + assertTrue(jdepsRunner.outputContains("class p.internal.P already associated with version")); + } + + private static void jar(String... options) { + int rc = JAR_TOOL.run(System.out, System.err, options); + assertTrue(rc == 0); + } +} diff --git a/test/langtools/tools/jdeps/multiVersion/src/m1/module-info.java b/test/langtools/tools/jdeps/multiVersion/src/m1/module-info.java new file mode 100644 index 0000000000000..316803466ca7d --- /dev/null +++ b/test/langtools/tools/jdeps/multiVersion/src/m1/module-info.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +module m1 { + requires java.management; + exports p; +} diff --git a/test/langtools/tools/jdeps/multiVersion/src/m1/p/Test.java b/test/langtools/tools/jdeps/multiVersion/src/m1/p/Test.java new file mode 100644 index 0000000000000..e3d9139bbddb6 --- /dev/null +++ b/test/langtools/tools/jdeps/multiVersion/src/m1/p/Test.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package p; + +public class Test { +} diff --git a/test/langtools/tools/jdeps/multiVersion/src/m1/p/internal/P.java b/test/langtools/tools/jdeps/multiVersion/src/m1/p/internal/P.java new file mode 100644 index 0000000000000..3a14badd1d07f --- /dev/null +++ b/test/langtools/tools/jdeps/multiVersion/src/m1/p/internal/P.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package p.internal; + +import java.lang.management.*; + +class P { + private static RuntimeMXBean mxbean = ManagementFactory.getRuntimeMXBean(); +} diff --git a/test/langtools/tools/jdeps/multiVersion/src/m2/module-info.java b/test/langtools/tools/jdeps/multiVersion/src/m2/module-info.java new file mode 100644 index 0000000000000..5404ca478674c --- /dev/null +++ b/test/langtools/tools/jdeps/multiVersion/src/m2/module-info.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +module m2 { + requires m1; + requires java.logging; +} diff --git a/test/langtools/tools/jdeps/multiVersion/src/m2/p/internal/P.java b/test/langtools/tools/jdeps/multiVersion/src/m2/p/internal/P.java new file mode 100644 index 0000000000000..d68265798bda3 --- /dev/null +++ b/test/langtools/tools/jdeps/multiVersion/src/m2/p/internal/P.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package p.internal; + +import java.util.logging.Logger; + +public class P { + private static final Logger LOGGER = Logger.getLogger("p"); +} diff --git a/test/langtools/tools/jdeps/multiVersion/src/m2/q/Q.java b/test/langtools/tools/jdeps/multiVersion/src/m2/q/Q.java new file mode 100644 index 0000000000000..46cad1cc9d137 --- /dev/null +++ b/test/langtools/tools/jdeps/multiVersion/src/m2/q/Q.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package q; + +public class Q { + static p.Test t = new p.Test(); + + public Q() { + Object o = new p.internal.P(); + } +} diff --git a/test/lib/jdk/test/whitebox/WhiteBox.java b/test/lib/jdk/test/whitebox/WhiteBox.java index 4488559c47097..c4f90b975a417 100644 --- a/test/lib/jdk/test/whitebox/WhiteBox.java +++ b/test/lib/jdk/test/whitebox/WhiteBox.java @@ -317,6 +317,36 @@ public int getMethodCompilationLevel(Executable method, boolean isOs Objects.requireNonNull(method); return getMethodCompilationLevel0(method, isOsr); } + public int getMethodDecompileCount(Executable method) { + Objects.requireNonNull(method); + return getMethodDecompileCount0(method); + } + private native int getMethodDecompileCount0(Executable method); + // Get the total trap count of a method. If the trap count for a specific reason + // did overflow, this includes the overflow trap count of the method. + public int getMethodTrapCount(Executable method) { + Objects.requireNonNull(method); + return getMethodTrapCount0(method, null); + } + // Get the trap count of a method for a specific reason. If the trap count for + // that reason did overflow, this includes the overflow trap count of the method. + public int getMethodTrapCount(Executable method, String reason) { + Objects.requireNonNull(method); + return getMethodTrapCount0(method, reason); + } + private native int getMethodTrapCount0(Executable method, String reason); + // Get the total deopt count. + public int getDeoptCount() { + return getDeoptCount0(null, null); + } + // Get the deopt count for a specific reason and a specific action. If either + // one of 'reason' or 'action' is null, the method returns the sum of all + // deoptimizations with the specific 'action' or 'reason' respectively. + // If both arguments are null, the method returns the total deopt count. + public int getDeoptCount(String reason, String action) { + return getDeoptCount0(reason, action); + } + private native int getDeoptCount0(String reason, String action); private native boolean testSetDontInlineMethod0(Executable method, boolean value); public boolean testSetDontInlineMethod(Executable method, boolean value) { Objects.requireNonNull(method); diff --git a/test/lib/sun/hotspot/WhiteBox.java b/test/lib/sun/hotspot/WhiteBox.java index 8db7ec3baafc2..4536e6080937a 100644 --- a/test/lib/sun/hotspot/WhiteBox.java +++ b/test/lib/sun/hotspot/WhiteBox.java @@ -318,6 +318,36 @@ public int getMethodCompilationLevel(Executable method, boolean isOs Objects.requireNonNull(method); return getMethodCompilationLevel0(method, isOsr); } + public int getMethodDecompileCount(Executable method) { + Objects.requireNonNull(method); + return getMethodDecompileCount0(method); + } + private native int getMethodDecompileCount0(Executable method); + // Get the total trap count of a method. If the trap count for a specific reason + // did overflow, this includes the overflow trap count of the method. + public int getMethodTrapCount(Executable method) { + Objects.requireNonNull(method); + return getMethodTrapCount0(method, null); + } + // Get the trap count of a method for a specific reason. If the trap count for + // that reason did overflow, this includes the overflow trap count of the method. + public int getMethodTrapCount(Executable method, String reason) { + Objects.requireNonNull(method); + return getMethodTrapCount0(method, reason); + } + private native int getMethodTrapCount0(Executable method, String reason); + // Get the total deopt count. + public int getDeoptCount() { + return getDeoptCount0(null, null); + } + // Get the deopt count for a specific reason and a specific action. If either + // one of 'reason' or 'action' is null, the method returns the sum of all + // deoptimizations with the specific 'action' or 'reason' respectively. + // If both arguments are null, the method returns the total deopt count. + public int getDeoptCount(String reason, String action) { + return getDeoptCount0(reason, action); + } + private native int getDeoptCount0(String reason, String action); private native boolean testSetDontInlineMethod0(Executable method, boolean value); public boolean testSetDontInlineMethod(Executable method, boolean value) { Objects.requireNonNull(method); diff --git a/test/micro/org/openjdk/bench/java/lang/StringCompareToDifferentLength.java b/test/micro/org/openjdk/bench/java/lang/StringCompareToDifferentLength.java new file mode 100644 index 0000000000000..fb08e32efac5b --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/StringCompareToDifferentLength.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, BELLSOFT. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.arm.benchmarks.intrinsics; + +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.CompilerControl; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import org.openjdk.jmh.infra.Blackhole; + +/** + * This benchmark modified from test/hotspot/jtreg/compiler/intrinsics/string/TestStringCompareToDifferentLength.java + * This benchmark can be used to measure performance of compareTo() in + * (Latin1, Latin1), (Latin1, UTF16), (UTF16, Latin1), and (UTF16, UTF16) + * comparisons. + */ + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Benchmark) +@Measurement(iterations = 3, time = 1000, timeUnit = TimeUnit.MILLISECONDS) +@Warmup(iterations = 3, time = 1000, timeUnit = TimeUnit.MILLISECONDS) +@CompilerControl(CompilerControl.Mode.DONT_INLINE) +public class StringCompareToDifferentLength { + + @State(Scope.Benchmark) + public static class Input { + @Param({"24", "36", "72", "128", "256", "512"}) + public int size; + + @Param({"2"}) + public int delta; + + int count = 100000; + String longLatin1; + String shortLatin1; + String longUTF16FirstChar; + String shortUTF16FirstChar; + String longUTF16LastChar; + String shortUTF16LastChar; + + /** + * Initialize. New array objects and set initial values. + */ + @Setup(Level.Trial) + public void setup() throws Exception { + char[] strsrc = new char[size + delta]; + // generate ASCII string + for (int i = 0; i < size + delta; i++) { + strsrc[i] = (char) ('a' + (i % 26)); + } + + longLatin1 = new String(strsrc); + shortLatin1 = longLatin1.substring(0, size); + longUTF16LastChar = longLatin1.substring(0, longLatin1.length() - 1) + '\ubeef'; + longUTF16FirstChar = '\ubeef' + longLatin1.substring(1, longLatin1.length()); + shortUTF16LastChar = shortLatin1.substring(0, shortLatin1.length() - 1) + '\ubeef'; + shortUTF16FirstChar = longUTF16FirstChar.substring(0, size); + } + } + + private int runCompareTo(String str2, String str1) { + return str1.compareTo(str2); + } + + /** + * latin1-latin1 + */ + @Benchmark + public void compareToLL(Input in, Blackhole blackhole) { + int res = 0; + for (int i = 0; i < in.count; ++i) { + res += runCompareTo(in.longLatin1, in.shortLatin1); + } + blackhole.consume(res); + } + + /** + * UTF16-UTF16 + */ + @Benchmark + public void compareToUU(Input in, Blackhole blackhole) { + int res = 0; + for (int i = 0; i < in.count; ++i) { + res += runCompareTo(in.longUTF16FirstChar, in.shortUTF16FirstChar); + } + blackhole.consume(res); + } + + /** + * latin1-UTF16 + */ + @Benchmark + public void compareToLU(Input in, Blackhole blackhole) { + int res = 0; + for (int i = 0; i < in.count; ++i) { + res += runCompareTo(in.longUTF16LastChar, in.shortLatin1); + } + blackhole.consume(res); + } + + /** + * UTF16-latin1 + */ + @Benchmark + public void compareToUL(Input in, Blackhole blackhole) { + int res = 0; + for (int i = 0; i < in.count; ++i) { + res += runCompareTo(in.longLatin1, in.shortUTF16LastChar); + } + blackhole.consume(res); + } +} + diff --git a/test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWaitProducerConsumer.java b/test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWaitProducerConsumer.java index e111b77ab5171..a4778838a8444 100644 --- a/test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWaitProducerConsumer.java +++ b/test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWaitProducerConsumer.java @@ -193,6 +193,7 @@ public void trial(Blackhole bh) throws Exception { } } threadConsumer.interrupt(); + threadConsumer.join(); if (producedDataCount != maxNum) { throw new RuntimeException("Produced: " + producedDataCount + ". Expected: " + maxNum); diff --git a/test/micro/org/openjdk/bench/java/lang/runtime/ObjectMethods.java b/test/micro/org/openjdk/bench/java/lang/runtime/ObjectMethods.java new file mode 100644 index 0000000000000..c491141e26838 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/runtime/ObjectMethods.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.lang.runtime; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.concurrent.TimeUnit; + +/** + * Benchmark assesses Record.toString which is implemented by ObjectMethods::makeToString + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +public class ObjectMethods { + record R0() {} + record R1(int i) {} + record R10(int i1,int i2,int i3,int i4,int i5,int i6,int i7,int i8,int i9,int i10) {} + record R100(int i1,int i2,int i3,int i4,int i5,int i6,int i7,int i8,int i9,int i10, + int i11,int i12,int i13,int i14,int i15,int i16,int i17,int i18,int i19,int i20, + int i21,int i22,int i23,int i24,int i25,int i26,int i27,int i28,int i29,int i30, + int i31,int i32,int i33,int i34,int i35,int i36,int i37,int i38,int i39,int i40, + int i41,int i42,int i43,int i44,int i45,int i46,int i47,int i48,int i49,int i50, + int i51,int i52,int i53,int i54,int i55,int i56,int i57,int i58,int i59,int i60, + int i61,int i62,int i63,int i64,int i65,int i66,int i67,int i68,int i69,int i70, + int i71,int i72,int i73,int i74,int i75,int i76,int i77,int i78,int i79,int i80, + int i81,int i82,int i83,int i84,int i85,int i86,int i87,int i88,int i89,int i90, + int i91,int i92,int i93,int i94,int i95,int i96,int i97,int i98,int i99,int i100) {} + record R254(int i1,int i2,int i3,int i4,int i5,int i6,int i7,int i8,int i9,int i10, + int i11,int i12,int i13,int i14,int i15,int i16,int i17,int i18,int i19,int i20, + int i21,int i22,int i23,int i24,int i25,int i26,int i27,int i28,int i29,int i30, + int i31,int i32,int i33,int i34,int i35,int i36,int i37,int i38,int i39,int i40, + int i41,int i42,int i43,int i44,int i45,int i46,int i47,int i48,int i49,int i50, + int i51,int i52,int i53,int i54,int i55,int i56,int i57,int i58,int i59,int i60, + int i61,int i62,int i63,int i64,int i65,int i66,int i67,int i68,int i69,int i70, + int i71,int i72,int i73,int i74,int i75,int i76,int i77,int i78,int i79,int i80, + int i81,int i82,int i83,int i84,int i85,int i86,int i87,int i88,int i89,int i90, + int i91,int i92,int i93,int i94,int i95,int i96,int i97,int i98,int i99,int i100, + int i101,int i102,int i103,int i104,int i105,int i106,int i107,int i108,int i109,int i110, + int i111,int i112,int i113,int i114,int i115,int i116,int i117,int i118,int i119,int i120, + int i121,int i122,int i123,int i124,int i125,int i126,int i127,int i128,int i129,int i130, + int i131,int i132,int i133,int i134,int i135,int i136,int i137,int i138,int i139,int i140, + int i141,int i142,int i143,int i144,int i145,int i146,int i147,int i148,int i149,int i150, + int i151,int i152,int i153,int i154,int i155,int i156,int i157,int i158,int i159,int i160, + int i161,int i162,int i163,int i164,int i165,int i166,int i167,int i168,int i169,int i170, + int i171,int i172,int i173,int i174,int i175,int i176,int i177,int i178,int i179,int i180, + int i181,int i182,int i183,int i184,int i185,int i186,int i187,int i188,int i189,int i190, + int i191,int i192,int i193,int i194,int i195,int i196,int i197,int i198,int i199, int i200, + int i201,int i202,int i203,int i204,int i205,int i206,int i207,int i208,int i209,int i210, + int i211,int i212,int i213,int i214,int i215,int i216,int i217,int i218,int i219,int i220, + int i221,int i222,int i223,int i224,int i225,int i226,int i227,int i228,int i229,int i230, + int i231,int i232,int i233,int i234,int i235,int i236,int i237,int i238,int i239,int i240, + int i241,int i242,int i243,int i244,int i245,int i246,int i247,int i248,int i249,int i250, + int i251,int i252,int i253,int i254) {} + + R0 r0; + R1 r1; + R10 r10; + R100 r100; + R254 r254; + + @Setup + public void prepare() { + r0 = new R0(); + r1 = new R1(1); + r10 = new R10(1,2,3,4,5,6,7,8,9,10); + r100 = new R100(1,2,3,4,5,6,7,8,9,10, + 11,12,13,14,15,16,17,18,19,20, + 21,22,23,24,25,26,27,28,29,30, + 31,32,33,34,35,36,37,38,39,40, + 41,42,43,44,45,46,47,48,49,50, + 51,52,53,54,55,56,57,58,59,60, + 61,62,63,64,65,66,67,68,69,70, + 71,72,73,74,75,76,77,78,79,80, + 81,82,83,84,85,86,87,88,89,90, + 91,92,93,94,95,96,97,98,99,100); + r254 = new R254(1,2,3,4,5,6,7,8,9,10, + 11,12,13,14,15,16,17,18,19,20, + 21,22,23,24,25,26,27,28,29,30, + 31,32,33,34,35,36,37,38,39,40, + 41,42,43,44,45,46,47,48,49,50, + 51,52,53,54,55,56,57,58,59,60, + 61,62,63,64,65,66,67,68,69,70, + 71,72,73,74,75,76,77,78,79,80, + 81,82,83,84,85,86,87,88,89,90, + 91,92,93,94,95,96,97,98,99,100, + 101,102,103,104,105,106,107,108,109,110, + 111,112,113,114,115,116,117,118,119,120, + 121,122,123,124,125,126,127,128,129,130, + 131,132,133,134,135,136,137,138,139,140, + 141,142,143,144,145,146,147,148,149,150, + 151,152,153,154,155,156,157,158,159,160, + 161,162,163,164,165,166,167,168,169,170, + 171,172,173,174,175,176,177,178,179,180, + 181,182,183,184,185,186,187,188,189,190, + 191,192,193,194,195,196,197,198,199, 200, + 201,202,203,204,205,206,207,208,209,210, + 211,212,213,214,215,216,217,218,219,220, + 221,222,223,224,225,226,227,228,229,230, + 231,232,233,234,235,236,237,238,239,240, + 241,242,243,244,245,246,247,248,249,250, + 251,252,253,254); + } + + @Benchmark + public String toString0() { + return r0.toString(); + } + + @Benchmark + public String toString1() { + return r1.toString(); + } + + @Benchmark + public String toString10() { + return r10.toString(); + } + + @Benchmark + public String toString100() { + return r100.toString(); + } + + @Benchmark + public String toString254() { + return r254.toString(); + } +} diff --git a/test/micro/org/openjdk/bench/java/util/regex/FindPattern.java b/test/micro/org/openjdk/bench/java/util/regex/FindPattern.java new file mode 100644 index 0000000000000..907a161688934 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/util/regex/FindPattern.java @@ -0,0 +1,71 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.bench.java.util.regex; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +@State(Scope.Benchmark) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Fork(value=1, jvmArgs= {"-showversion", "-XX:+UseSerialGC"}) +@Warmup(iterations = 1, time = 10, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 3, time = 10, timeUnit = TimeUnit.SECONDS) +public class FindPattern { + @Param({"[^A-Za-z0-9]", "[A-Za-z0-9]"}) + static String patternString; + @Param({"abcdefghijklmnop1234567890ABCDEFGHIJKLMNOP", + ",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"}) + static String text; + static Pattern pattern; + + @Setup(Level.Trial) + public void setupTrial() { + pattern = Pattern.compile(patternString); + } + + @Benchmark + @BenchmarkMode(Mode.AverageTime) + public int testFind() { + int counter = 0; + Matcher m = pattern.matcher(text); + while (m.find()) { + counter++; + } + return counter; + } +} diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/BulkMismatchAcquire.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/BulkMismatchAcquire.java index 5d962ee39796e..c9fc3abd33a5c 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/BulkMismatchAcquire.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/BulkMismatchAcquire.java @@ -26,7 +26,6 @@ package org.openjdk.bench.jdk.incubator.foreign; import jdk.incubator.foreign.ResourceScope; -import jdk.incubator.foreign.SegmentAllocator; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -38,15 +37,12 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.annotations.Warmup; -import sun.misc.Unsafe; import jdk.incubator.foreign.MemorySegment; import java.nio.ByteBuffer; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; -import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT; - @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -71,7 +67,7 @@ ResourceScope makeScope() { } } - @Param({"CONFINED", "SHARED", "IMPLICIT"}) + @Param({"CONFINED", "SHARED"}) public ScopeKind scopeKind; // large(ish) segments/buffers with same content, 0, for mismatch, non-multiple-of-8 sized @@ -121,8 +117,7 @@ public void setup() { @TearDown public void tearDown() { - if (!scope.isImplicit()) - scope.close(); + scope.close(); } @Benchmark @@ -134,11 +129,9 @@ public long mismatch_large_segment() { @Benchmark @OutputTimeUnit(TimeUnit.NANOSECONDS) public long mismatch_large_segment_acquire() { - var handle = mismatchSegmentLarge1.scope().acquire(); - try { + try (ResourceScope scope = ResourceScope.newConfinedScope()) { + scope.keepAlive(mismatchSegmentLarge1.scope()); return mismatchSegmentLarge1.mismatch(mismatchSegmentLarge2); - } finally { - mismatchSegmentLarge1.scope().release(handle); } } @@ -157,11 +150,9 @@ public long mismatch_small_segment() { @Benchmark @OutputTimeUnit(TimeUnit.NANOSECONDS) public long mismatch_small_segment_acquire() { - var handle = mismatchSegmentLarge1.scope().acquire(); - try { + try (ResourceScope scope = ResourceScope.newConfinedScope()) { + scope.keepAlive(mismatchSegmentLarge1.scope()); return mismatchSegmentSmall1.mismatch(mismatchSegmentSmall2); - } finally { - mismatchSegmentLarge1.scope().release(handle); } } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/BulkOps.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/BulkOps.java index ac92e9429cae1..25d6cf557b039 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/BulkOps.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/BulkOps.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,19 +27,23 @@ import jdk.incubator.foreign.ResourceScope; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.CompilerControl; import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.annotations.Warmup; import sun.misc.Unsafe; import jdk.incubator.foreign.MemorySegment; import java.nio.ByteBuffer; +import java.nio.IntBuffer; import java.util.concurrent.TimeUnit; -import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -55,26 +59,31 @@ public class BulkOps { static final int CARRIER_SIZE = (int)JAVA_INT.byteSize(); static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE; - static final long unsafe_addr = unsafe.allocateMemory(ALLOC_SIZE); - static final MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE, ResourceScope.newConfinedScope()); + final ResourceScope scope = ResourceScope.newConfinedScope(); - static final int[] bytes = new int[ELEM_SIZE]; - static final MemorySegment bytesSegment = MemorySegment.ofArray(bytes); - static final int UNSAFE_INT_OFFSET = unsafe.arrayBaseOffset(int[].class); + final long unsafe_addr = unsafe.allocateMemory(ALLOC_SIZE); + final MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE, ResourceScope.newConfinedScope()); + final IntBuffer buffer = IntBuffer.allocate(ELEM_SIZE); + + final int[] bytes = new int[ELEM_SIZE]; + final MemorySegment bytesSegment = MemorySegment.ofArray(bytes); + final int UNSAFE_INT_OFFSET = unsafe.arrayBaseOffset(int[].class); // large(ish) segments/buffers with same content, 0, for mismatch, non-multiple-of-8 sized static final int SIZE_WITH_TAIL = (1024 * 1024) + 7; - static final MemorySegment mismatchSegmentLarge1 = MemorySegment.allocateNative(SIZE_WITH_TAIL, ResourceScope.newConfinedScope()); - static final MemorySegment mismatchSegmentLarge2 = MemorySegment.allocateNative(SIZE_WITH_TAIL, ResourceScope.newConfinedScope()); - static final ByteBuffer mismatchBufferLarge1 = ByteBuffer.allocateDirect(SIZE_WITH_TAIL); - static final ByteBuffer mismatchBufferLarge2 = ByteBuffer.allocateDirect(SIZE_WITH_TAIL); + final MemorySegment mismatchSegmentLarge1 = MemorySegment.allocateNative(SIZE_WITH_TAIL, scope); + final MemorySegment mismatchSegmentLarge2 = MemorySegment.allocateNative(SIZE_WITH_TAIL, scope); + final ByteBuffer mismatchBufferLarge1 = ByteBuffer.allocateDirect(SIZE_WITH_TAIL); + final ByteBuffer mismatchBufferLarge2 = ByteBuffer.allocateDirect(SIZE_WITH_TAIL); // mismatch at first byte - static final MemorySegment mismatchSegmentSmall1 = MemorySegment.allocateNative(7, ResourceScope.newConfinedScope()); - static final MemorySegment mismatchSegmentSmall2 = MemorySegment.allocateNative(7, ResourceScope.newConfinedScope()); - static final ByteBuffer mismatchBufferSmall1 = ByteBuffer.allocateDirect(7); - static final ByteBuffer mismatchBufferSmall2 = ByteBuffer.allocateDirect(7); - static { + final MemorySegment mismatchSegmentSmall1 = MemorySegment.allocateNative(7, scope); + final MemorySegment mismatchSegmentSmall2 = MemorySegment.allocateNative(7, scope); + final ByteBuffer mismatchBufferSmall1 = ByteBuffer.allocateDirect(7); + final ByteBuffer mismatchBufferSmall2 = ByteBuffer.allocateDirect(7); + + @Setup + public void setup() { mismatchSegmentSmall1.fill((byte) 0xFF); mismatchBufferSmall1.put((byte) 0xFF).clear(); // verify expected mismatch indices @@ -90,14 +99,17 @@ public class BulkOps { bi = mismatchBufferSmall1.mismatch(mismatchBufferSmall2); if (bi != 0) throw new AssertionError("Unexpected mismatch index:" + bi); - } - static { for (int i = 0 ; i < bytes.length ; i++) { bytes[i] = i; } } + @TearDown + public void tearDown() { + scope.close(); + } + @Benchmark @OutputTimeUnit(TimeUnit.NANOSECONDS) public void unsafe_fill() { @@ -122,6 +134,50 @@ public void segment_copy() { segment.copyFrom(bytesSegment); } + @Benchmark + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public void segment_copy_static() { + MemorySegment.copy(bytes, 0, segment, JAVA_INT, 0, bytes.length); + } + + @Benchmark + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public void segment_copy_static_small() { + MemorySegment.copy(bytes, 0, segment, JAVA_INT, 0, 10); + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public void segment_copy_static_small_dontinline() { + MemorySegment.copy(bytes, 0, segment, JAVA_INT, 0, 10); + } + + @Benchmark + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public void unsafe_copy_small() { + unsafe.copyMemory(bytes, UNSAFE_INT_OFFSET, null, unsafe_addr, 10 * CARRIER_SIZE); + } + + @Benchmark + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public void buffer_copy_small() { + buffer.put(0, bytes, 0, 10); + } + + @Benchmark + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public void buffer_copy() { + buffer.put(0, bytes, 0, bytes.length); + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public void segment_copy_static_dontinline() { + MemorySegment.copy(bytes, 0, segment, JAVA_INT, 0, bytes.length); + } + @Benchmark @OutputTimeUnit(TimeUnit.NANOSECONDS) public long mismatch_large_segment() { diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/CLayouts.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/CLayouts.java new file mode 100644 index 0000000000000..530731e34bfc6 --- /dev/null +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/CLayouts.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.bench.jdk.incubator.foreign; + +import jdk.incubator.foreign.Addressable; +import jdk.incubator.foreign.CLinker; +import jdk.incubator.foreign.FunctionDescriptor; +import jdk.incubator.foreign.MemoryAddress; +import jdk.incubator.foreign.ValueLayout; + +import java.lang.invoke.MethodHandle; + +public class CLayouts { + + // the constants below are useful aliases for C types. The type/carrier association is only valid for 64-bit platforms. + + /** + * The layout for the {@code bool} C type + */ + public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN; + /** + * The layout for the {@code char} C type + */ + public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE; + /** + * The layout for the {@code short} C type + */ + public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT; + /** + * The layout for the {@code int} C type + */ + public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT; + + /** + * The layout for the {@code long long} C type. + */ + public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG; + /** + * The layout for the {@code float} C type + */ + public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT; + /** + * The layout for the {@code double} C type + */ + public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE; + /** + * The {@code T*} native type. + */ + public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS; + + private static CLinker LINKER = CLinker.systemCLinker(); + + private static final MethodHandle FREE = LINKER.downcallHandle( + LINKER.lookup("free").get(), FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)); + + private static final MethodHandle MALLOC = LINKER.downcallHandle( + LINKER.lookup("malloc").get(), FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.JAVA_LONG)); + + public static void freeMemory(Addressable address) { + try { + FREE.invokeExact(address); + } catch (Throwable ex) { + throw new IllegalStateException(ex); + } + } + + public static MemoryAddress allocateMemory(long size) { + try { + return (MemoryAddress)MALLOC.invokeExact(size); + } catch (Throwable ex) { + throw new IllegalStateException(ex); + } + } +} diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/CallOverheadConstant.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/CallOverheadConstant.java index bd627646770b9..8fe3d1b1ea472 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/CallOverheadConstant.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/CallOverheadConstant.java @@ -22,6 +22,7 @@ */ package org.openjdk.bench.jdk.incubator.foreign; +import jdk.incubator.foreign.Addressable; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemorySegment; import org.openjdk.jmh.annotations.Benchmark; @@ -55,11 +56,6 @@ public void panama_blank() throws Throwable { func.invokeExact(); } - @Benchmark - public void panama_blank_trivial() throws Throwable { - func_trivial.invokeExact(); - } - @Benchmark public int jni_identity() throws Throwable { return identity(10); @@ -71,18 +67,73 @@ public int panama_identity() throws Throwable { } @Benchmark - public int panama_identity_trivial() throws Throwable { - return (int) identity_trivial.invokeExact(10); + public MemorySegment panama_identity_struct_confined() throws Throwable { + return (MemorySegment) identity_struct.invokeExact(recycling_allocator, confinedPoint); + } + + @Benchmark + public MemorySegment panama_identity_struct_shared() throws Throwable { + return (MemorySegment) identity_struct.invokeExact(recycling_allocator, sharedPoint); + } + + @Benchmark + public MemorySegment panama_identity_struct_confined_3() throws Throwable { + return (MemorySegment) identity_struct_3.invokeExact(recycling_allocator, confinedPoint, confinedPoint, confinedPoint); + } + + @Benchmark + public MemorySegment panama_identity_struct_shared_3() throws Throwable { + return (MemorySegment) identity_struct_3.invokeExact(recycling_allocator, sharedPoint, sharedPoint, sharedPoint); + } + + @Benchmark + public MemoryAddress panama_identity_memory_address_shared() throws Throwable { + return (MemoryAddress) identity_memory_address.invokeExact((Addressable)sharedPoint.address()); + } + + @Benchmark + public MemoryAddress panama_identity_memory_address_confined() throws Throwable { + return (MemoryAddress) identity_memory_address.invokeExact((Addressable)confinedPoint.address()); + } + + @Benchmark + public MemoryAddress panama_identity_memory_address_shared_3() throws Throwable { + return (MemoryAddress) identity_memory_address_3.invokeExact((Addressable)sharedPoint.address(), (Addressable)sharedPoint.address(), (Addressable)sharedPoint.address()); + } + + @Benchmark + public MemoryAddress panama_identity_memory_address_confined_3() throws Throwable { + return (MemoryAddress) identity_memory_address_3.invokeExact((Addressable)confinedPoint.address(), (Addressable)confinedPoint.address(), (Addressable)confinedPoint.address()); + } + + @Benchmark + public MemoryAddress panama_identity_struct_ref_shared() throws Throwable { + return (MemoryAddress) identity_memory_address.invokeExact((Addressable)sharedPoint); + } + + @Benchmark + public MemoryAddress panama_identity_struct_ref_confined() throws Throwable { + return (MemoryAddress) identity_memory_address.invokeExact((Addressable)confinedPoint); + } + + @Benchmark + public MemoryAddress panama_identity_struct_ref_shared_3() throws Throwable { + return (MemoryAddress) identity_memory_address_3.invokeExact((Addressable)sharedPoint, (Addressable)sharedPoint, (Addressable)sharedPoint); + } + + @Benchmark + public MemoryAddress panama_identity_struct_ref_confined_3() throws Throwable { + return (MemoryAddress) identity_memory_address_3.invokeExact((Addressable)confinedPoint, (Addressable)confinedPoint, (Addressable)confinedPoint); } @Benchmark - public MemorySegment panama_identity_struct() throws Throwable { - return (MemorySegment) identity_struct.invokeExact(recycling_allocator, point); + public MemoryAddress panama_identity_memory_address_null() throws Throwable { + return (MemoryAddress) identity_memory_address.invokeExact((Addressable)MemoryAddress.NULL); } @Benchmark - public MemoryAddress panama_identity_memory_address() throws Throwable { - return (MemoryAddress) identity_memory_address.invokeExact(MemoryAddress.NULL); + public MemoryAddress panama_identity_memory_address_null_non_exact() throws Throwable { + return (MemoryAddress) identity_memory_address.invoke(MemoryAddress.NULL); } @Benchmark diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/CallOverheadHelper.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/CallOverheadHelper.java index 4989f5fa47e5f..259c4b26bdd6a 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/CallOverheadHelper.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/CallOverheadHelper.java @@ -22,12 +22,11 @@ */ package org.openjdk.bench.jdk.incubator.foreign; -import jdk.incubator.foreign.Addressable; import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.FunctionDescriptor; -import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.NativeSymbol; import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.SegmentAllocator; import jdk.incubator.foreign.SymbolLookup; @@ -36,57 +35,58 @@ import java.lang.invoke.MethodType; import static java.lang.invoke.MethodHandles.insertArguments; -import static jdk.incubator.foreign.CLinker.C_DOUBLE; -import static jdk.incubator.foreign.CLinker.C_INT; -import static jdk.incubator.foreign.CLinker.C_LONG_LONG; -import static jdk.incubator.foreign.CLinker.C_POINTER; -public class CallOverheadHelper { +public class CallOverheadHelper extends CLayouts { - static final CLinker abi = CLinker.getInstance(); + static final CLinker abi = CLinker.systemCLinker(); static final MethodHandle func; static final MethodHandle func_v; - static Addressable func_addr; + static NativeSymbol func_addr; static final MethodHandle identity; static final MethodHandle identity_v; - static Addressable identity_addr; + static NativeSymbol identity_addr; static final MethodHandle identity_struct; static final MethodHandle identity_struct_v; - static Addressable identity_struct_addr; + static NativeSymbol identity_struct_addr; + static final MethodHandle identity_struct_3; + static final MethodHandle identity_struct_3_v; + static NativeSymbol identity_struct_3_addr; static final MethodHandle identity_memory_address; static final MethodHandle identity_memory_address_v; - static Addressable identity_memory_address_addr; + static NativeSymbol identity_memory_address_addr; + static final MethodHandle identity_memory_address_3; + static final MethodHandle identity_memory_address_3_v; + static NativeSymbol identity_memory_address_3_addr; static final MethodHandle args1; static final MethodHandle args1_v; - static Addressable args1_addr; + static NativeSymbol args1_addr; static final MethodHandle args2; static final MethodHandle args2_v; - static Addressable args2_addr; + static NativeSymbol args2_addr; static final MethodHandle args3; static final MethodHandle args3_v; - static Addressable args3_addr; + static NativeSymbol args3_addr; static final MethodHandle args4; static final MethodHandle args4_v; - static Addressable args4_addr; + static NativeSymbol args4_addr; static final MethodHandle args5; static final MethodHandle args5_v; - static Addressable args5_addr; + static NativeSymbol args5_addr; static final MethodHandle args10; static final MethodHandle args10_v; - static Addressable args10_addr; - static final MethodHandle func_trivial; - static final MethodHandle func_trivial_v; - static final MethodHandle identity_trivial; - static final MethodHandle identity_trivial_v; + static NativeSymbol args10_addr; static final MemoryLayout POINT_LAYOUT = MemoryLayout.structLayout( - C_LONG_LONG, C_LONG_LONG + C_INT, C_INT ); + static final MemorySegment sharedPoint = MemorySegment.allocateNative(POINT_LAYOUT, ResourceScope.newSharedScope()); + static final MemorySegment confinedPoint = MemorySegment.allocateNative(POINT_LAYOUT, ResourceScope.newConfinedScope()); + static final MemorySegment point = MemorySegment.allocateNative(POINT_LAYOUT, ResourceScope.newImplicitScope()); - static final SegmentAllocator recycling_allocator = SegmentAllocator.ofSegment(MemorySegment.allocateNative(POINT_LAYOUT, ResourceScope.newImplicitScope())); + static final SegmentAllocator recycling_allocator = SegmentAllocator.prefixAllocator(MemorySegment.allocateNative(POINT_LAYOUT, ResourceScope.newImplicitScope())); static { System.loadLibrary("CallOverheadJNI"); @@ -97,66 +97,62 @@ public class CallOverheadHelper { func_addr = lookup.lookup("func").orElseThrow(); MethodType mt = MethodType.methodType(void.class); FunctionDescriptor fd = FunctionDescriptor.ofVoid(); - func_v = abi.downcallHandle(mt, fd); + func_v = abi.downcallHandle(fd); func = insertArguments(func_v, 0, func_addr); - func_trivial_v = abi.downcallHandle(mt, fd.withAttribute(FunctionDescriptor.TRIVIAL_ATTRIBUTE_NAME, true)); - func_trivial = insertArguments(func_trivial_v, 0, func_addr); } { identity_addr = lookup.lookup("identity").orElseThrow(); - MethodType mt = MethodType.methodType(int.class, int.class); FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_INT); - identity_v = abi.downcallHandle(mt, fd); + identity_v = abi.downcallHandle(fd); identity = insertArguments(identity_v, 0, identity_addr); - identity_trivial_v = abi.downcallHandle(mt, fd.withAttribute(FunctionDescriptor.TRIVIAL_ATTRIBUTE_NAME, true)); - identity_trivial = insertArguments(identity_trivial_v, 0, identity_addr); } identity_struct_addr = lookup.lookup("identity_struct").orElseThrow(); identity_struct_v = abi.downcallHandle( - MethodType.methodType(MemorySegment.class, MemorySegment.class), FunctionDescriptor.of(POINT_LAYOUT, POINT_LAYOUT)); identity_struct = insertArguments(identity_struct_v, 0, identity_struct_addr); + identity_struct_3_addr = lookup.lookup("identity_struct_3").orElseThrow(); + identity_struct_3_v = abi.downcallHandle( + FunctionDescriptor.of(POINT_LAYOUT, POINT_LAYOUT, POINT_LAYOUT, POINT_LAYOUT)); + identity_struct_3 = insertArguments(identity_struct_3_v, 0, identity_struct_3_addr); + identity_memory_address_addr = lookup.lookup("identity_memory_address").orElseThrow(); identity_memory_address_v = abi.downcallHandle( - MethodType.methodType(MemoryAddress.class, MemoryAddress.class), FunctionDescriptor.of(C_POINTER, C_POINTER)); identity_memory_address = insertArguments(identity_memory_address_v, 0, identity_memory_address_addr); + identity_memory_address_3_addr = lookup.lookup("identity_memory_address_3").orElseThrow(); + identity_memory_address_3_v = abi.downcallHandle( + FunctionDescriptor.of(C_POINTER, C_POINTER, C_POINTER, C_POINTER)); + identity_memory_address_3 = insertArguments(identity_memory_address_3_v, 0, identity_memory_address_3_addr); + args1_addr = lookup.lookup("args1").orElseThrow(); args1_v = abi.downcallHandle( - MethodType.methodType(void.class, long.class), FunctionDescriptor.ofVoid(C_LONG_LONG)); args1 = insertArguments(args1_v, 0, args1_addr); args2_addr = lookup.lookup("args2").orElseThrow(); args2_v = abi.downcallHandle( - MethodType.methodType(void.class, long.class, double.class), FunctionDescriptor.ofVoid(C_LONG_LONG, C_DOUBLE)); args2 = insertArguments(args2_v, 0, args2_addr); args3_addr = lookup.lookup("args3").orElseThrow(); args3_v = abi.downcallHandle( - MethodType.methodType(void.class, long.class, double.class, long.class), FunctionDescriptor.ofVoid(C_LONG_LONG, C_DOUBLE, C_LONG_LONG)); args3 = insertArguments(args3_v, 0, args3_addr); args4_addr = lookup.lookup("args4").orElseThrow(); args4_v = abi.downcallHandle( - MethodType.methodType(void.class, long.class, double.class, long.class, double.class), FunctionDescriptor.ofVoid(C_LONG_LONG, C_DOUBLE, C_LONG_LONG, C_DOUBLE)); args4 = insertArguments(args4_v, 0, args4_addr); args5_addr = lookup.lookup("args5").orElseThrow(); args5_v = abi.downcallHandle( - MethodType.methodType(void.class, long.class, double.class, long.class, double.class, long.class), FunctionDescriptor.ofVoid(C_LONG_LONG, C_DOUBLE, C_LONG_LONG, C_DOUBLE, C_LONG_LONG)); args5 = insertArguments(args5_v, 0, args5_addr); args10_addr = lookup.lookup("args10").orElseThrow(); args10_v = abi.downcallHandle( - MethodType.methodType(void.class, long.class, double.class, long.class, double.class, long.class, - double.class, long.class, double.class, long.class, double.class), FunctionDescriptor.ofVoid(C_LONG_LONG, C_DOUBLE, C_LONG_LONG, C_DOUBLE, C_LONG_LONG, C_DOUBLE, C_LONG_LONG, C_DOUBLE, C_LONG_LONG, C_DOUBLE)); args10 = insertArguments(args10_v, 0, args10_addr); diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/CallOverheadVirtual.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/CallOverheadVirtual.java index e1e557bad2378..11152bdd04414 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/CallOverheadVirtual.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/CallOverheadVirtual.java @@ -22,6 +22,7 @@ */ package org.openjdk.bench.jdk.incubator.foreign; +import jdk.incubator.foreign.Addressable; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemorySegment; import org.openjdk.jmh.annotations.Benchmark; @@ -56,23 +57,72 @@ public void panama_blank() throws Throwable { } @Benchmark - public void panama_blank_trivial() throws Throwable { - func_trivial_v.invokeExact(func_addr); + public int jni_identity() throws Throwable { + return identity(10); + } + + public MemorySegment panama_identity_struct_confined() throws Throwable { + return (MemorySegment) identity_struct_v.invokeExact(identity_struct_addr, recycling_allocator, confinedPoint); } @Benchmark - public int jni_identity() throws Throwable { - return identity(10); + public MemorySegment panama_identity_struct_shared() throws Throwable { + return (MemorySegment) identity_struct_v.invokeExact(identity_struct_addr, recycling_allocator, sharedPoint); } @Benchmark - public int panama_identity() throws Throwable { - return (int) identity_v.invokeExact(identity_addr, 10); + public MemorySegment panama_identity_struct_confined_3() throws Throwable { + return (MemorySegment) identity_struct_3_v.invokeExact(identity_struct_3_addr, recycling_allocator, confinedPoint, confinedPoint, confinedPoint); } @Benchmark - public int panama_identity_trivial() throws Throwable { - return (int) identity_trivial_v.invokeExact(identity_addr, 10); + public MemorySegment panama_identity_struct_shared_3() throws Throwable { + return (MemorySegment) identity_struct_3_v.invokeExact(identity_struct_3_addr, recycling_allocator, sharedPoint, sharedPoint, sharedPoint); + } + + @Benchmark + public MemoryAddress panama_identity_memory_address_shared() throws Throwable { + return (MemoryAddress) identity_memory_address_v.invokeExact(identity_memory_address_addr, (Addressable)sharedPoint.address()); + } + + @Benchmark + public MemoryAddress panama_identity_memory_address_confined() throws Throwable { + return (MemoryAddress) identity_memory_address_v.invokeExact(identity_memory_address_addr, (Addressable)confinedPoint.address()); + } + + @Benchmark + public MemoryAddress panama_identity_memory_address_shared_3() throws Throwable { + return (MemoryAddress) identity_memory_address_3_v.invokeExact(identity_memory_address_3_addr, (Addressable)sharedPoint.address(), (Addressable)sharedPoint.address(), (Addressable)sharedPoint.address()); + } + + @Benchmark + public MemoryAddress panama_identity_memory_address_confined_3() throws Throwable { + return (MemoryAddress) identity_memory_address_3_v.invokeExact(identity_memory_address_3_addr, (Addressable)confinedPoint.address(), (Addressable)confinedPoint.address(), (Addressable)confinedPoint.address()); + } + + @Benchmark + public MemoryAddress panama_identity_struct_ref_shared() throws Throwable { + return (MemoryAddress) identity_memory_address_v.invokeExact(identity_struct_addr, (Addressable)sharedPoint); + } + + @Benchmark + public MemoryAddress panama_identity_struct_ref_confined() throws Throwable { + return (MemoryAddress) identity_memory_address_v.invokeExact(identity_struct_addr, (Addressable)confinedPoint); + } + + @Benchmark + public MemoryAddress panama_identity_struct_ref_shared_3() throws Throwable { + return (MemoryAddress) identity_memory_address_3_v.invokeExact(identity_struct_3_addr, (Addressable)sharedPoint, (Addressable)sharedPoint, (Addressable)sharedPoint); + } + + @Benchmark + public MemoryAddress panama_identity_struct_ref_confined_3() throws Throwable { + return (MemoryAddress) identity_memory_address_3_v.invokeExact(identity_struct_3_addr, (Addressable)confinedPoint, (Addressable)confinedPoint, (Addressable)confinedPoint); + } + + @Benchmark + public int panama_identity() throws Throwable { + return (int) identity_v.invokeExact(identity_addr, 10); } @Benchmark @@ -81,8 +131,8 @@ public MemorySegment panama_identity_struct() throws Throwable { } @Benchmark - public MemoryAddress panama_identity_memory_address() throws Throwable { - return (MemoryAddress) identity_memory_address_v.invokeExact(identity_memory_address_addr, MemoryAddress.NULL); + public MemoryAddress panama_identity_memory_address_null() throws Throwable { + return (MemoryAddress) identity_memory_address_v.invokeExact(identity_memory_address_addr, (Addressable)MemoryAddress.NULL); } @Benchmark diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/JNICB.h b/test/micro/org/openjdk/bench/jdk/incubator/foreign/JNICB.h new file mode 100644 index 0000000000000..9af5383ead7f4 --- /dev/null +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/JNICB.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include + +typedef struct { + jclass holder; + jmethodID mid; +} *JNICB; diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/JNICB.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/JNICB.java new file mode 100644 index 0000000000000..89ead66025e35 --- /dev/null +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/JNICB.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.bench.jdk.incubator.foreign; + +public class JNICB { + + static { + System.loadLibrary("JNICB"); + } + + public static native long makeCB(String holder, String name, String signature); +} diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverConstant.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverConstant.java index 64cc3697687e0..e1b1b86b61ab7 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverConstant.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverConstant.java @@ -44,7 +44,7 @@ import java.util.concurrent.TimeUnit; import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement; -import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -73,7 +73,7 @@ public class LoopOverConstant { //setup native memory segment static final MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE, ResourceScope.newImplicitScope()); - static final VarHandle VH_int = MemoryLayout.sequenceLayout(JAVA_INT).varHandle(int.class, sequenceElement()); + static final VarHandle VH_int = MemoryLayout.sequenceLayout(JAVA_INT).varHandle(sequenceElement()); static { for (int i = 0; i < ELEM_SIZE; i++) { diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNew.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNew.java index 0d59d83488fa6..c50148e46e682 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNew.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNew.java @@ -44,7 +44,7 @@ import java.util.concurrent.TimeUnit; import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement; -import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -61,10 +61,10 @@ public class LoopOverNew { static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE; static final MemoryLayout ALLOC_LAYOUT = MemoryLayout.sequenceLayout(ELEM_SIZE, JAVA_INT); - static final VarHandle VH_int = MemoryLayout.sequenceLayout(JAVA_INT).varHandle(int.class, sequenceElement()); + static final VarHandle VH_int = MemoryLayout.sequenceLayout(JAVA_INT).varHandle(sequenceElement()); final ResourceScope scope = ResourceScope.newConfinedScope(); - final SegmentAllocator recyclingAlloc = SegmentAllocator.ofSegment(MemorySegment.allocateNative(ALLOC_LAYOUT, scope)); + final SegmentAllocator recyclingAlloc = SegmentAllocator.prefixAllocator(MemorySegment.allocateNative(ALLOC_LAYOUT, scope)); @TearDown public void tearDown() throws Throwable { diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNewHeap.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNewHeap.java new file mode 100644 index 0000000000000..62677639f2f40 --- /dev/null +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNewHeap.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.jdk.incubator.foreign; + +import jdk.incubator.foreign.MemoryLayout; +import jdk.incubator.foreign.MemorySegment; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.CompilerControl; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; +import sun.misc.Unsafe; + +import java.lang.invoke.VarHandle; +import java.nio.IntBuffer; +import java.util.concurrent.TimeUnit; + +import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; + +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@State(org.openjdk.jmh.annotations.Scope.Thread) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Fork(value = 3, jvmArgsAppend = { "--add-modules=jdk.incubator.foreign" }) +public class LoopOverNewHeap { + + static final Unsafe unsafe = Utils.unsafe; + + static final int ELEM_SIZE = 1_000_000; + static final int CARRIER_SIZE = (int)JAVA_INT.byteSize(); + + static final VarHandle VH_int = MemoryLayout.sequenceLayout(JAVA_INT).varHandle(sequenceElement()); + + @Param(value = {"false", "true"}) + boolean polluteProfile; + + @Setup + public void setup() { + if (polluteProfile) { + for (int i = 0 ; i < 10000 ; i++) { + MemorySegment intB = MemorySegment.ofArray(new byte[ELEM_SIZE]); + MemorySegment intI = MemorySegment.ofArray(new int[ELEM_SIZE]); + MemorySegment intD = MemorySegment.ofArray(new double[ELEM_SIZE]); + MemorySegment intF = MemorySegment.ofArray(new float[ELEM_SIZE]); + } + } + } + + @Benchmark + public void unsafe_loop() { + int[] elems = new int[ELEM_SIZE]; + for (int i = 0; i < ELEM_SIZE; i++) { + unsafe.putInt(elems, Unsafe.ARRAY_INT_BASE_OFFSET + (i * CARRIER_SIZE) , i); + } + } + + + @Benchmark + public void segment_loop() { + MemorySegment segment = MemorySegment.ofArray(new int[ELEM_SIZE]); + for (int i = 0; i < ELEM_SIZE; i++) { + VH_int.set(segment, (long) i, i); + } + } + + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + @Benchmark + public void segment_loop_dontinline() { + MemorySegment segment = MemorySegment.ofArray(new int[ELEM_SIZE]); + for (int i = 0; i < ELEM_SIZE; i++) { + VH_int.set(segment, (long) i, i); + } + } + + @Benchmark + public void buffer_loop() { + IntBuffer buffer = IntBuffer.wrap(new int[ELEM_SIZE]); + for (int i = 0; i < ELEM_SIZE; i++) { + buffer.put(i , i); + } + } + +} diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstant.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstant.java index c4af846b6850b..ada578b09ba24 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstant.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstant.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,7 +22,7 @@ */ package org.openjdk.bench.jdk.incubator.foreign; -import jdk.incubator.foreign.MemoryAccess; +import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; @@ -44,14 +44,14 @@ import java.util.concurrent.TimeUnit; import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement; -import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) @State(org.openjdk.jmh.annotations.Scope.Thread) @OutputTimeUnit(TimeUnit.MILLISECONDS) -@Fork(value = 3, jvmArgsAppend = { "--add-modules=jdk.incubator.foreign" }) +@Fork(value = 3, jvmArgsAppend = { "--add-modules=jdk.incubator.foreign", "--enable-native-access=ALL-UNNAMED" }) public class LoopOverNonConstant { static final Unsafe unsafe = Utils.unsafe; @@ -60,7 +60,7 @@ public class LoopOverNonConstant { static final int CARRIER_SIZE = (int)JAVA_INT.byteSize(); static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE; - static final VarHandle VH_int = MemoryLayout.sequenceLayout(JAVA_INT).varHandle(int.class, sequenceElement()); + static final VarHandle VH_int = MemoryLayout.sequenceLayout(JAVA_INT).varHandle(sequenceElement()); MemorySegment segment; long unsafe_addr; @@ -117,19 +117,48 @@ public int unsafe_loop() { } @Benchmark - public int segment_loop_static() { - int res = 0; - for (int i = 0; i < ELEM_SIZE; i ++) { - res += MemoryAccess.getIntAtIndex(segment, i); + public int segment_loop() { + int sum = 0; + for (int i = 0; i < ELEM_SIZE; i++) { + sum += (int) VH_int.get(segment, (long) i); } - return res; + return sum; } @Benchmark - public int segment_loop() { + public int segment_loop_instance() { int sum = 0; for (int i = 0; i < ELEM_SIZE; i++) { - sum += (int) VH_int.get(segment, (long) i); + sum += segment.get(JAVA_INT, i * CARRIER_SIZE); + + } + return sum; + } + + @Benchmark + public int segment_loop_instance_index() { + int sum = 0; + for (int i = 0; i < ELEM_SIZE; i++) { + sum += segment.getAtIndex(JAVA_INT, i); + + } + return sum; + } + + @Benchmark + public int segment_loop_instance_address() { + int sum = 0; + for (int i = 0; i < ELEM_SIZE; i++) { + sum += segment.address().get(JAVA_INT, i * CARRIER_SIZE); + } + return sum; + } + + @Benchmark + public int segment_loop_instance_address_index() { + int sum = 0; + for (int i = 0; i < ELEM_SIZE; i++) { + sum += segment.address().getAtIndex(JAVA_INT, i); } return sum; } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantFP.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantFP.java index 17c8cd6b341d2..180c12240fa02 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantFP.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantFP.java @@ -22,7 +22,6 @@ */ package org.openjdk.bench.jdk.incubator.foreign; -import jdk.incubator.foreign.MemoryAccess; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; import org.openjdk.jmh.annotations.Benchmark; @@ -42,7 +41,7 @@ import java.util.concurrent.TimeUnit; import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement; -import static jdk.incubator.foreign.MemoryLayouts.JAVA_DOUBLE; +import static jdk.incubator.foreign.ValueLayout.JAVA_DOUBLE; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -75,10 +74,10 @@ public void setup() { segmentIn = MemorySegment.allocateNative(ALLOC_SIZE, ResourceScope.newConfinedScope()); segmentOut = MemorySegment.allocateNative(ALLOC_SIZE, ResourceScope.newConfinedScope()); for (int i = 0; i < ELEM_SIZE; i++) { - MemoryAccess.setDoubleAtIndex(segmentIn, i, i); + segmentIn.setAtIndex(JAVA_DOUBLE, i, i); } for (int i = 0; i < ELEM_SIZE; i++) { - MemoryAccess.setDoubleAtIndex(segmentOut, i, i); + segmentOut.setAtIndex(JAVA_DOUBLE, i, i); } byteBufferIn = ByteBuffer.allocateDirect(ALLOC_SIZE).order(ByteOrder.nativeOrder()); byteBufferOut = ByteBuffer.allocateDirect(ALLOC_SIZE).order(ByteOrder.nativeOrder()); @@ -112,9 +111,9 @@ public void unsafe_loop() { @Benchmark public void segment_loop() { for (int i = 0; i < ELEM_SIZE; i ++) { - MemoryAccess.setDoubleAtIndex(segmentOut, i, - MemoryAccess.getDoubleAtIndex(segmentIn, i) + - MemoryAccess.getDoubleAtIndex(segmentOut, i)); + segmentOut.setAtIndex(JAVA_DOUBLE, i, + segmentIn.getAtIndex(JAVA_DOUBLE, i) + + segmentOut.getAtIndex(JAVA_DOUBLE, i)); } } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantHeap.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantHeap.java index 2b2be067fdcde..5b51db08c00b2 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantHeap.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantHeap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,7 +22,6 @@ */ package org.openjdk.bench.jdk.incubator.foreign; -import jdk.incubator.foreign.MemoryAccess; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; @@ -46,7 +45,10 @@ import java.util.concurrent.TimeUnit; import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement; -import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE; +import static jdk.incubator.foreign.ValueLayout.JAVA_DOUBLE; +import static jdk.incubator.foreign.ValueLayout.JAVA_FLOAT; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -63,7 +65,7 @@ public class LoopOverNonConstantHeap { static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE; static final int UNSAFE_BYTE_BASE = unsafe.arrayBaseOffset(byte[].class); - static final VarHandle VH_int = MemoryLayout.sequenceLayout(JAVA_INT).varHandle(int.class, sequenceElement()); + static final VarHandle VH_int = MemoryLayout.sequenceLayout(JAVA_INT).varHandle(sequenceElement()); MemorySegment segment; byte[] base; @@ -79,13 +81,13 @@ public void setup() { MemorySegment intI = MemorySegment.ofArray(new int[ALLOC_SIZE]); MemorySegment intD = MemorySegment.ofArray(new double[ALLOC_SIZE]); MemorySegment intF = MemorySegment.ofArray(new float[ALLOC_SIZE]); - MemorySegment s = MemorySegment.allocateNative(ALLOC_SIZE, 1, ResourceScope.newConfinedScope(Cleaner.create())); + MemorySegment s = MemorySegment.allocateNative(ALLOC_SIZE, 1, ResourceScope.newConfinedScope()); for (int i = 0; i < ALLOC_SIZE; i++) { - MemoryAccess.setByteAtOffset(intB, i, (byte)i); - MemoryAccess.setIntAtIndex(intI, i, i); - MemoryAccess.setDoubleAtIndex(intD, i, i); - MemoryAccess.setFloatAtIndex(intF, i, i); - MemoryAccess.setByteAtOffset(s, i, (byte) i); + intB.set(JAVA_BYTE, i, (byte)i); + intI.setAtIndex(JAVA_INT, i, i); + intD.setAtIndex(JAVA_DOUBLE, i, i); + intF.setAtIndex(JAVA_FLOAT, i, i); + s.set(JAVA_BYTE, i, (byte) i); } } @@ -134,10 +136,10 @@ public int segment_loop() { } @Benchmark - public int segment_loop_static() { + public int segment_loop_instance() { int res = 0; for (int i = 0; i < ELEM_SIZE; i ++) { - res += MemoryAccess.getIntAtIndex(segment, i); + res += segment.get(JAVA_INT, i * CARRIER_SIZE); } return res; } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantMapped.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantMapped.java index 11f1c1f061eb2..5e4e22916bf23 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantMapped.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantMapped.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,7 +22,6 @@ */ package org.openjdk.bench.jdk.incubator.foreign; -import jdk.incubator.foreign.MemoryAccess; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; @@ -51,14 +50,14 @@ import java.util.concurrent.TimeUnit; import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement; -import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) @State(org.openjdk.jmh.annotations.Scope.Thread) @OutputTimeUnit(TimeUnit.MILLISECONDS) -@Fork(value = 3, jvmArgsAppend = { "--add-modules=jdk.incubator.foreign" }) +@Fork(value = 3, jvmArgsAppend = { "--add-modules=jdk.incubator.foreign", "--enable-native-access=ALL-UNNAMED" }) public class LoopOverNonConstantMapped { static final Unsafe unsafe = Utils.unsafe; @@ -81,7 +80,7 @@ public class LoopOverNonConstantMapped { } } - static final VarHandle VH_int = MemoryLayout.sequenceLayout(JAVA_INT).varHandle(int.class, sequenceElement()); + static final VarHandle VH_int = MemoryLayout.sequenceLayout(JAVA_INT).varHandle(sequenceElement()); MemorySegment segment; long unsafe_addr; @@ -143,10 +142,19 @@ public int segment_loop() { } @Benchmark - public int segment_loop_static() { + public int segment_loop_instance() { int res = 0; for (int i = 0; i < ELEM_SIZE; i ++) { - res += MemoryAccess.getIntAtIndex(segment, i); + res += segment.get(JAVA_INT, i * CARRIER_SIZE); + } + return res; + } + + @Benchmark + public int segment_loop_instance_address() { + int res = 0; + for (int i = 0; i < ELEM_SIZE; i ++) { + res += segment.address().get(JAVA_INT, i * CARRIER_SIZE); } return res; } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantShared.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantShared.java index 06c6310845a72..75c64a024f9c8 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantShared.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantShared.java @@ -22,7 +22,6 @@ */ package org.openjdk.bench.jdk.incubator.foreign; -import jdk.incubator.foreign.MemoryAccess; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; @@ -44,7 +43,7 @@ import java.util.concurrent.TimeUnit; import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement; -import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -60,7 +59,7 @@ public class LoopOverNonConstantShared { static final int CARRIER_SIZE = (int)JAVA_INT.byteSize(); static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE; - static final VarHandle VH_int = MemoryLayout.sequenceLayout(JAVA_INT).varHandle(int.class, sequenceElement()); + static final VarHandle VH_int = MemoryLayout.sequenceLayout(JAVA_INT).varHandle(sequenceElement()); MemorySegment segment; long unsafe_addr; @@ -72,7 +71,7 @@ public void setup() { for (int i = 0; i < ELEM_SIZE; i++) { unsafe.putInt(unsafe_addr + (i * CARRIER_SIZE) , i); } - segment = MemorySegment.allocateNative(ALLOC_SIZE, CARRIER_SIZE, ResourceScope.newSharedScope()); + segment = MemorySegment.allocateNative(ALLOC_SIZE, CARRIER_SIZE, ResourceScope.newConfinedScope()); for (int i = 0; i < ELEM_SIZE; i++) { VH_int.set(segment, (long) i, i); } @@ -117,10 +116,19 @@ public int unsafe_loop() { } @Benchmark - public int segment_loop_static() { + public int segment_loop_instance() { int res = 0; for (int i = 0; i < ELEM_SIZE; i ++) { - res += MemoryAccess.getIntAtIndex(segment, i); + res += segment.get(JAVA_INT, i * CARRIER_SIZE); + } + return res; + } + + @Benchmark + public int segment_loop_instance_address() { + int res = 0; + for (int i = 0; i < ELEM_SIZE; i ++) { + res += segment.get(JAVA_INT, i * CARRIER_SIZE); } return res; } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverPollutedBuffer.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverPollutedBuffer.java index 3903159c4073f..0d1c90cb987fa 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverPollutedBuffer.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverPollutedBuffer.java @@ -42,7 +42,7 @@ import java.nio.FloatBuffer; import java.util.concurrent.TimeUnit; -import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverPollutedSegments.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverPollutedSegments.java index 65ae252ee322d..4cb01f091f1cc 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverPollutedSegments.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverPollutedSegments.java @@ -22,7 +22,6 @@ */ package org.openjdk.bench.jdk.incubator.foreign; -import jdk.incubator.foreign.MemoryAccess; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; @@ -42,7 +41,8 @@ import java.util.concurrent.TimeUnit; import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement; -import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_FLOAT; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -62,7 +62,7 @@ public class LoopOverPollutedSegments { byte[] arr; long addr; - static final VarHandle intHandle = MemoryLayout.sequenceLayout(JAVA_INT).varHandle(int.class, MemoryLayout.PathElement.sequenceElement()); + static final VarHandle intHandle = MemoryLayout.sequenceLayout(JAVA_INT).varHandle(MemoryLayout.PathElement.sequenceElement()); @Setup @@ -79,14 +79,14 @@ public void setup() { for (int rep = 0 ; rep < 5 ; rep++) { for (int i = 0; i < ELEM_SIZE; i++) { unsafe.putInt(arr, Unsafe.ARRAY_BYTE_BASE_OFFSET + (i * 4), i); - MemoryAccess.setIntAtIndex(nativeSegment, i, i); - MemoryAccess.setFloatAtIndex(nativeSegment, i, i); + nativeSegment.setAtIndex(JAVA_INT, i, i); + nativeSegment.setAtIndex(JAVA_FLOAT, i, i); intHandle.set(nativeSegment, (long)i, i); - MemoryAccess.setIntAtIndex(heapSegmentBytes, i, i); - MemoryAccess.setFloatAtIndex(heapSegmentBytes, i, i); + heapSegmentBytes.setAtIndex(JAVA_INT, i, i); + heapSegmentBytes.setAtIndex(JAVA_FLOAT, i, i); intHandle.set(heapSegmentBytes, (long)i, i); - MemoryAccess.setIntAtIndex(heapSegmentFloats, i, i); - MemoryAccess.setFloatAtIndex(heapSegmentFloats, i, i); + heapSegmentFloats.setAtIndex(JAVA_INT, i, i); + heapSegmentFloats.setAtIndex(JAVA_FLOAT, i, i); intHandle.set(heapSegmentFloats, (long)i, i); } } @@ -113,11 +113,11 @@ public int native_segment_VH() { } @Benchmark - public int native_segment_static() { + public int native_segment_instance() { int sum = 0; for (int k = 0; k < ELEM_SIZE; k++) { - MemoryAccess.setIntAtOffset(nativeSegment, k, k + 1); - int v = MemoryAccess.getIntAtOffset(nativeSegment, k); + nativeSegment.setAtIndex(JAVA_INT, k, k + 1); + int v = nativeSegment.getAtIndex(JAVA_INT, k); sum += v; } return sum; @@ -135,11 +135,11 @@ public int heap_segment_ints_VH() { } @Benchmark - public int heap_segment_ints_static() { + public int heap_segment_ints_instance() { int sum = 0; for (int k = 0; k < ELEM_SIZE; k++) { - MemoryAccess.setIntAtOffset(heapSegmentBytes, k, k + 1); - int v = MemoryAccess.getIntAtOffset(heapSegmentBytes, k); + heapSegmentBytes.setAtIndex(JAVA_INT, k, k + 1); + int v = heapSegmentBytes.getAtIndex(JAVA_INT, k); sum += v; } return sum; @@ -157,11 +157,11 @@ public int heap_segment_floats_VH() { } @Benchmark - public int heap_segment_floats_static() { + public int heap_segment_floats_instance() { int sum = 0; for (int k = 0; k < ELEM_SIZE; k++) { - MemoryAccess.setIntAtOffset(heapSegmentFloats, k, k + 1); - int v = MemoryAccess.getIntAtOffset(heapSegmentFloats, k); + heapSegmentFloats.setAtIndex(JAVA_INT, k, k + 1); + int v = heapSegmentFloats.getAtIndex(JAVA_INT, k); sum += v; } return sum; diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/ParallelSum.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/ParallelSum.java index 231c606103bc0..6ec449a1ec036 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/ParallelSum.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/ParallelSum.java @@ -24,9 +24,9 @@ package org.openjdk.bench.jdk.incubator.foreign; import jdk.incubator.foreign.MemoryLayout; -import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.SequenceLayout; +import jdk.incubator.foreign.ValueLayout; import sun.misc.Unsafe; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -52,7 +52,7 @@ import java.util.function.ToIntFunction; import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement; -import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -65,9 +65,9 @@ public class ParallelSum { final static int CARRIER_SIZE = 4; final static int ALLOC_SIZE = CARRIER_SIZE * 1024 * 1024 * 256; final static int ELEM_SIZE = ALLOC_SIZE / CARRIER_SIZE; - static final VarHandle VH_int = MemoryLayout.sequenceLayout(JAVA_INT).varHandle(int.class, sequenceElement()); + static final VarHandle VH_int = MemoryLayout.sequenceLayout(JAVA_INT).varHandle(sequenceElement()); - final static MemoryLayout ELEM_LAYOUT = MemoryLayouts.JAVA_INT; + final static MemoryLayout ELEM_LAYOUT = ValueLayout.JAVA_INT; final static int BULK_FACTOR = 512; final static SequenceLayout ELEM_LAYOUT_BULK = MemoryLayout.sequenceLayout(BULK_FACTOR, ELEM_LAYOUT); diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/QSort.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/QSort.java new file mode 100644 index 0000000000000..04a10f8ed4e54 --- /dev/null +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/QSort.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.jdk.incubator.foreign; + +import jdk.incubator.foreign.Addressable; +import jdk.incubator.foreign.CLinker; +import jdk.incubator.foreign.FunctionDescriptor; +import jdk.incubator.foreign.MemoryAddress; +import jdk.incubator.foreign.MemoryLayout; +import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.NativeSymbol; +import jdk.incubator.foreign.ResourceScope; +import jdk.incubator.foreign.SymbolLookup; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; +import java.util.concurrent.TimeUnit; + +import static java.lang.invoke.MethodHandles.lookup; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; + +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@State(org.openjdk.jmh.annotations.Scope.Thread) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Fork(value = 3, jvmArgsAppend = { "--add-modules=jdk.incubator.foreign", "--enable-native-access=ALL-UNNAMED" }) +public class QSort extends CLayouts { + + static final CLinker abi = CLinker.systemCLinker(); + static final MethodHandle clib_qsort; + static final NativeSymbol native_compar; + static final NativeSymbol panama_upcall_compar; + static final long jni_upcall_compar; + + static final int[] INPUT = { 5, 3, 2, 7, 8, 12, 1, 7 }; + static final MemorySegment INPUT_SEGMENT; + + static NativeSymbol qsort_addr = abi.lookup("qsort").get(); + + static { + INPUT_SEGMENT = MemorySegment.allocateNative(MemoryLayout.sequenceLayout(INPUT.length, JAVA_INT), ResourceScope.globalScope()); + INPUT_SEGMENT.copyFrom(MemorySegment.ofArray(INPUT)); + + System.loadLibrary("QSortJNI"); + jni_upcall_compar = JNICB.makeCB("org/openjdk/bench/jdk/incubator/foreign/QSort", "jni_upcall_compar", "(II)I"); + + try { + clib_qsort = abi.downcallHandle( + qsort_addr, + FunctionDescriptor.ofVoid(C_POINTER, C_LONG_LONG, C_LONG_LONG, C_POINTER) + ); + System.loadLibrary("QSort"); + native_compar = SymbolLookup.loaderLookup().lookup("compar").orElseThrow(); + panama_upcall_compar = abi.upcallStub( + lookup().findStatic(QSort.class, + "panama_upcall_compar", + MethodType.methodType(int.class, MemoryAddress.class, MemoryAddress.class)), + FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER), + ResourceScope.globalScope() + ); + } catch (ReflectiveOperationException e) { + throw new BootstrapMethodError(e); + } + } + + static native void jni_qsort_optimized(int[] array, long cb); + static native void jni_qsort_naive(int[] array); + + @FunctionalInterface + interface JNIComparator { + int cmp(int e0, int e1); + } + + static final JNIComparator COMP = QSort::jni_upcall_compar; + + @Benchmark + public void native_qsort() throws Throwable { + clib_qsort.invokeExact((Addressable)INPUT_SEGMENT, (long) INPUT.length, JAVA_INT.byteSize(), (Addressable)native_compar); + } + + @Benchmark + public void jni_upcall_qsort_optimized() { + jni_qsort_optimized(INPUT, jni_upcall_compar); + } + + @Benchmark + public void jni_upcall_qsort_naive() { + jni_qsort_naive(INPUT); + } + + @Benchmark + public void panama_upcall_qsort() throws Throwable { + clib_qsort.invokeExact((Addressable)INPUT_SEGMENT, (long) INPUT.length, JAVA_INT.byteSize(), (Addressable)panama_upcall_compar); + } + + private static int getIntAbsolute(MemoryAddress addr) { + return addr.get(JAVA_INT, 0); + } + + static int panama_upcall_compar(MemoryAddress e0, MemoryAddress e1) { + return Integer.compare(getIntAbsolute(e0), getIntAbsolute(e1)); + } + + static int jni_upcall_compar(int j0, int j1) { + return Integer.compare(j0, j1); + } +} diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/StrLenTest.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/StrLenTest.java index ec4da5ffc8875..ec7d58cf823df 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/StrLenTest.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/StrLenTest.java @@ -25,9 +25,9 @@ package org.openjdk.bench.jdk.incubator.foreign; +import jdk.incubator.foreign.Addressable; import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.FunctionDescriptor; -import jdk.incubator.foreign.MemoryAccess; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; @@ -45,10 +45,9 @@ import org.openjdk.jmh.annotations.Warmup; import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; import java.util.concurrent.TimeUnit; -import static jdk.incubator.foreign.CLinker.*; +import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -56,12 +55,12 @@ @State(org.openjdk.jmh.annotations.Scope.Thread) @OutputTimeUnit(TimeUnit.NANOSECONDS) @Fork(value = 3, jvmArgsAppend = { "--add-modules=jdk.incubator.foreign", "--enable-native-access=ALL-UNNAMED" }) -public class StrLenTest { +public class StrLenTest extends CLayouts { - ResourceScope scope = ResourceScope.newConfinedScope(); + ResourceScope scope = ResourceScope.newImplicitScope(); SegmentAllocator segmentAllocator; - SegmentAllocator arenaAllocator = SegmentAllocator.arenaAllocator(scope); + SegmentAllocator arenaAllocator = SegmentAllocator.newNativeArena(scope); @Param({"5", "20", "100"}) public int size; @@ -72,31 +71,17 @@ public class StrLenTest { } static final MethodHandle STRLEN; - static final MethodHandle STRLEN_TRIVIAL; - static final MethodHandle MALLOC_TRIVIAL; - static final MethodHandle FREE_TRIVIAL; static { - CLinker abi = CLinker.getInstance(); - STRLEN = abi.downcallHandle(CLinker.systemLookup().lookup("strlen").get(), - MethodType.methodType(int.class, MemoryAddress.class), + CLinker abi = CLinker.systemCLinker(); + STRLEN = abi.downcallHandle(abi.lookup("strlen").get(), FunctionDescriptor.of(C_INT, C_POINTER)); - STRLEN_TRIVIAL = abi.downcallHandle(CLinker.systemLookup().lookup("strlen").get(), - MethodType.methodType(int.class, MemoryAddress.class), - FunctionDescriptor.of(C_INT, C_POINTER).withAttribute(FunctionDescriptor.TRIVIAL_ATTRIBUTE_NAME, true)); - MALLOC_TRIVIAL = abi.downcallHandle(CLinker.systemLookup().lookup("malloc").get(), - MethodType.methodType(MemoryAddress.class, long.class), - FunctionDescriptor.of(C_POINTER, C_LONG_LONG).withAttribute(FunctionDescriptor.TRIVIAL_ATTRIBUTE_NAME, true)); - - FREE_TRIVIAL = abi.downcallHandle(CLinker.systemLookup().lookup("free").get(), - MethodType.methodType(void.class, MemoryAddress.class), - FunctionDescriptor.ofVoid(C_POINTER).withAttribute(FunctionDescriptor.TRIVIAL_ATTRIBUTE_NAME, true)); } @Setup public void setup() { str = makeString(size); - segmentAllocator = SegmentAllocator.ofSegment(MemorySegment.allocateNative(size + 1, ResourceScope.newImplicitScope())); + segmentAllocator = SegmentAllocator.prefixAllocator(MemorySegment.allocateNative(size + 1, ResourceScope.newConfinedScope())); } @TearDown @@ -112,54 +97,37 @@ public int jni_strlen() throws Throwable { @Benchmark public int panama_strlen() throws Throwable { try (ResourceScope scope = ResourceScope.newConfinedScope()) { - MemorySegment segment = CLinker.toCString(str, scope); - return (int)STRLEN.invokeExact(segment.address()); + MemorySegment segment = MemorySegment.allocateNative(str.length() + 1, scope); + segment.setUtf8String(0, str); + return (int)STRLEN.invokeExact((Addressable)segment); } } @Benchmark public int panama_strlen_arena() throws Throwable { - return (int)STRLEN.invokeExact(CLinker.toCString(str, arenaAllocator).address()); + return (int)STRLEN.invokeExact((Addressable)arenaAllocator.allocateUtf8String(str)); } @Benchmark public int panama_strlen_prefix() throws Throwable { - return (int)STRLEN.invokeExact(CLinker.toCString(str, segmentAllocator).address()); + return (int)STRLEN.invokeExact((Addressable)segmentAllocator.allocateUtf8String(str)); } @Benchmark public int panama_strlen_unsafe() throws Throwable { MemoryAddress address = makeStringUnsafe(str); - int res = (int) STRLEN.invokeExact(address); - CLinker.freeMemory(address); - return res; - } - - @Benchmark - public int panama_strlen_unsafe_trivial() throws Throwable { - MemoryAddress address = makeStringUnsafeTrivial(str); - int res = (int) STRLEN_TRIVIAL.invokeExact(address); - FREE_TRIVIAL.invokeExact(address); + int res = (int) STRLEN.invokeExact((Addressable)address); + freeMemory(address); return res; } static MemoryAddress makeStringUnsafe(String s) { byte[] bytes = s.getBytes(); int len = bytes.length; - MemoryAddress address = CLinker.allocateMemory(len + 1); - MemorySegment str = address.asSegment(len + 1, ResourceScope.globalScope()); - str.copyFrom(MemorySegment.ofArray(bytes)); - MemoryAccess.setByteAtOffset(str, len, (byte)0); - return address; - } - - static MemoryAddress makeStringUnsafeTrivial(String s) throws Throwable { - byte[] bytes = s.getBytes(); - int len = bytes.length; - MemoryAddress address = (MemoryAddress)MALLOC_TRIVIAL.invokeExact((long)len + 1); - MemorySegment str = address.asSegment(len + 1, ResourceScope.globalScope()); + MemoryAddress address = allocateMemory(len + 1); + MemorySegment str = MemorySegment.ofAddress(address, len + 1, ResourceScope.globalScope()); str.copyFrom(MemorySegment.ofArray(bytes)); - MemoryAccess.setByteAtOffset(str, len, (byte)0); + str.set(JAVA_BYTE, len, (byte)0); return address; } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/TestAdaptVarHandles.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/TestAdaptVarHandles.java index f8f482b3a2848..4540b5907826b 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/TestAdaptVarHandles.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/TestAdaptVarHandles.java @@ -26,8 +26,8 @@ import jdk.incubator.foreign.MemoryHandles; import jdk.incubator.foreign.MemoryLayout; -import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.ValueLayout; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -85,8 +85,8 @@ int intValue() { static final VarHandle VH_box_int = MemoryHandles.filterValue(VH_int, INTBOX_TO_INT, INT_TO_INTBOX); - static final VarHandle VH_addr_int = MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT) - .varHandle(int.class, MemoryLayout.PathElement.sequenceElement()); + static final VarHandle VH_addr_int = MemoryLayout.sequenceLayout(ValueLayout.JAVA_INT) + .varHandle(MemoryLayout.PathElement.sequenceElement()); static final VarHandle VH_addr_box_int = MemoryHandles.filterValue(VH_addr_int, INTBOX_TO_INT, INT_TO_INTBOX); diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/TestLoadBytes.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/TestLoadBytes.java index 45c170b8dd359..6288c49cf92af 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/TestLoadBytes.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/TestLoadBytes.java @@ -23,14 +23,8 @@ package org.openjdk.bench.jdk.incubator.foreign; -import jdk.incubator.foreign.CLinker; -import jdk.incubator.foreign.MemoryAccess; -import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; -import jdk.incubator.vector.ByteVector; -import jdk.incubator.vector.IntVector; -import jdk.incubator.vector.VectorSpecies; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.CompilerControl; @@ -44,9 +38,10 @@ import org.openjdk.jmh.annotations.Warmup; import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.util.concurrent.TimeUnit; +import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE; + @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -119,7 +114,7 @@ public int bufferNativeScalarConst() { public int segmentNativeScalar() { int size = 0; for (int i = 0; i < srcArray.length; i++) { - var v = MemoryAccess.getByteAtOffset(srcSegmentImplicit, i); + var v = srcSegmentImplicit.get(JAVA_BYTE, i); size += v; } return size; @@ -129,7 +124,7 @@ public int segmentNativeScalar() { public int segmentNativeScalarConst() { int size = 0; for (int i = 0; i < 1024; i++) { - var v = MemoryAccess.getByteAtOffset(srcSegmentImplicit, i); + var v = srcSegmentImplicit.get(JAVA_BYTE, i); size += v; } return size; diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/UnrolledAccess.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/UnrolledAccess.java index 614ff42b8f474..ea72c01f7b941 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/UnrolledAccess.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/UnrolledAccess.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,6 @@ package org.openjdk.bench.jdk.incubator.foreign; -import static jdk.incubator.foreign.MemoryAccess.*; import jdk.incubator.foreign.*; import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.runner.Runner; @@ -34,20 +33,22 @@ import java.lang.invoke.VarHandle; +import static jdk.incubator.foreign.ValueLayout.JAVA_LONG; + @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) @State(org.openjdk.jmh.annotations.Scope.Thread) @OutputTimeUnit(TimeUnit.MICROSECONDS) -@Fork(3) +@Fork(value = 3, jvmArgsAppend = { "--enable-native-access=ALL-UNNAMED" }) public class UnrolledAccess { static final Unsafe U = Utils.unsafe; final static int SIZE = 1024; - static final VarHandle LONG_HANDLE = MemoryLayout.sequenceLayout(SIZE, MemoryLayouts.JAVA_LONG) - .varHandle(long.class, MemoryLayout.PathElement.sequenceElement()); + static final VarHandle LONG_HANDLE = MemoryLayout.sequenceLayout(SIZE, JAVA_LONG) + .varHandle(MemoryLayout.PathElement.sequenceElement()); @State(Scope.Benchmark) public static class Data { @@ -65,8 +66,8 @@ public Data() { this.outputArray = new double[SIZE]; this.inputAddress = U.allocateMemory(8 * SIZE); this.outputAddress = U.allocateMemory(8 * SIZE); - this.inputSegment = MemoryAddress.ofLong(inputAddress).asSegment(8*SIZE, ResourceScope.globalScope()); - this.outputSegment = MemoryAddress.ofLong(outputAddress).asSegment(8*SIZE, ResourceScope.globalScope()); + this.inputSegment = MemorySegment.ofAddress(MemoryAddress.ofLong(inputAddress), 8*SIZE, ResourceScope.globalScope()); + this.outputSegment = MemorySegment.ofAddress(MemoryAddress.ofLong(outputAddress), 8*SIZE, ResourceScope.globalScope()); } } @@ -96,15 +97,15 @@ public void handle_loop(Data state) { } @Benchmark - public void static_handle_loop(Data state) { + public void handle_loop_instance(Data state) { final MemorySegment is = state.inputSegment; final MemorySegment os = state.outputSegment; for(int i = 0; i < SIZE; i+=4) { - setLongAtIndex(os, i,getLongAtIndex(is, i) + MemoryAccess.getLongAtIndex(os, i)); - setLongAtIndex(os, i+1,getLongAtIndex(is, i+1) + getLongAtIndex(os, i+1)); - setLongAtIndex(os, i+2,getLongAtIndex(is, i+2) + getLongAtIndex(os, i+2)); - setLongAtIndex(os, i+3,getLongAtIndex(is, i+3) + getLongAtIndex(os, i+3)); + os.setAtIndex(JAVA_LONG, i, is.getAtIndex(JAVA_LONG, i) + os.get(JAVA_LONG, i)); + os.setAtIndex(JAVA_LONG, i+1, is.getAtIndex(JAVA_LONG, i+1) + os.get(JAVA_LONG, i+1)); + os.setAtIndex(JAVA_LONG, i+2, is.getAtIndex(JAVA_LONG, i+2) + os.get(JAVA_LONG, i+2)); + os.setAtIndex(JAVA_LONG, i+3, is.getAtIndex(JAVA_LONG, i+3) + os.get(JAVA_LONG, i+3)); } } } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/Upcalls.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/Upcalls.java index a25f4f9ae9fed..12a7ca3b42bfa 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/Upcalls.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/Upcalls.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,9 +22,10 @@ */ package org.openjdk.bench.jdk.incubator.foreign; -import jdk.incubator.foreign.FunctionDescriptor; -import jdk.incubator.foreign.MemoryAddress; +import jdk.incubator.foreign.Addressable; import jdk.incubator.foreign.CLinker; +import jdk.incubator.foreign.FunctionDescriptor; +import jdk.incubator.foreign.NativeSymbol; import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.SymbolLookup; import org.openjdk.jmh.annotations.Benchmark; @@ -41,10 +42,6 @@ import java.util.concurrent.TimeUnit; import static java.lang.invoke.MethodHandles.lookup; -import static jdk.incubator.foreign.CLinker.C_DOUBLE; -import static jdk.incubator.foreign.CLinker.C_INT; -import static jdk.incubator.foreign.CLinker.C_LONG_LONG; -import static jdk.incubator.foreign.CLinker.C_POINTER; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -52,18 +49,18 @@ @State(org.openjdk.jmh.annotations.Scope.Thread) @OutputTimeUnit(TimeUnit.NANOSECONDS) @Fork(value = 3, jvmArgsAppend = { "--add-modules=jdk.incubator.foreign", "--enable-native-access=ALL-UNNAMED" }) -public class Upcalls { +public class Upcalls extends CLayouts { - static final CLinker abi = CLinker.getInstance(); + static final CLinker abi = CLinker.systemCLinker(); static final MethodHandle blank; static final MethodHandle identity; static final MethodHandle args5; static final MethodHandle args10; - static final MemoryAddress cb_blank; - static final MemoryAddress cb_identity; - static final MemoryAddress cb_args5; - static final MemoryAddress cb_args10; + static final NativeSymbol cb_blank; + static final NativeSymbol cb_identity; + static final NativeSymbol cb_args5; + static final NativeSymbol cb_args10; static final long cb_blank_jni; static final long cb_identity_jni; @@ -74,10 +71,10 @@ public class Upcalls { System.loadLibrary("UpcallsJNI"); String className = "org/openjdk/bench/jdk/incubator/foreign/Upcalls"; - cb_blank_jni = makeCB(className, "blank", "()V"); - cb_identity_jni = makeCB(className, "identity", "(I)I"); - cb_args5_jni = makeCB(className, "args5", "(JDJDJ)V"); - cb_args10_jni = makeCB(className, "args10", "(JDJDJDJDJD)V"); + cb_blank_jni = JNICB.makeCB(className, "blank", "()V"); + cb_identity_jni = JNICB.makeCB(className, "identity", "(I)I"); + cb_args5_jni = JNICB.makeCB(className, "args5", "(JDJDJ)V"); + cb_args10_jni = JNICB.makeCB(className, "args10", "(JDJDJDJDJD)V"); try { System.loadLibrary("Upcalls"); @@ -86,7 +83,7 @@ public class Upcalls { MethodType mt = MethodType.methodType(void.class); FunctionDescriptor fd = FunctionDescriptor.ofVoid(); - blank = linkFunc(name, mt, fd); + blank = linkFunc(name, fd); cb_blank = makeCB(name, mt, fd); } { @@ -94,7 +91,7 @@ public class Upcalls { MethodType mt = MethodType.methodType(int.class, int.class); FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_INT); - identity = linkFunc(name, mt, fd); + identity = linkFunc(name, fd); cb_identity = makeCB(name, mt, fd); } { @@ -104,7 +101,7 @@ public class Upcalls { FunctionDescriptor fd = FunctionDescriptor.ofVoid( C_LONG_LONG, C_DOUBLE, C_LONG_LONG, C_DOUBLE, C_LONG_LONG); - args5 = linkFunc(name, mt, fd); + args5 = linkFunc(name, fd); cb_args5 = makeCB(name, mt, fd); } { @@ -116,7 +113,7 @@ public class Upcalls { C_LONG_LONG, C_DOUBLE, C_LONG_LONG, C_DOUBLE, C_LONG_LONG, C_DOUBLE, C_LONG_LONG, C_DOUBLE, C_LONG_LONG, C_DOUBLE); - args10 = linkFunc(name, mt, fd); + args10 = linkFunc(name, fd); cb_args10 = makeCB(name, mt, fd); } } catch (ReflectiveOperationException e) { @@ -124,19 +121,18 @@ public class Upcalls { } } - static MethodHandle linkFunc(String name, MethodType baseType, FunctionDescriptor baseDesc) { + static MethodHandle linkFunc(String name, FunctionDescriptor baseDesc) { return abi.downcallHandle( SymbolLookup.loaderLookup().lookup(name).orElseThrow(), - baseType.insertParameterTypes(baseType.parameterCount(), MemoryAddress.class), - baseDesc.withAppendedArgumentLayouts(C_POINTER) + baseDesc.withAppendedArgumentLayouts(C_POINTER) ); } - static MemoryAddress makeCB(String name, MethodType mt, FunctionDescriptor fd) throws ReflectiveOperationException { + static NativeSymbol makeCB(String name, MethodType mt, FunctionDescriptor fd) throws ReflectiveOperationException { return abi.upcallStub( lookup().findStatic(Upcalls.class, name, mt), fd, ResourceScope.globalScope() - ).address(); + ); } static native void blank(long cb); @@ -144,7 +140,6 @@ static MemoryAddress makeCB(String name, MethodType mt, FunctionDescriptor fd) t static native void args5(long a0, double a1, long a2, double a3, long a4, long cb); static native void args10(long a0, double a1, long a2, double a3, long a4, double a5, long a6, double a7, long a8, double a9, long cb); - static native long makeCB(String holder, String name, String signature); @Benchmark public void jni_blank() throws Throwable { @@ -153,7 +148,7 @@ public void jni_blank() throws Throwable { @Benchmark public void panama_blank() throws Throwable { - blank.invokeExact(cb_blank); + blank.invokeExact((Addressable)cb_blank); } @Benchmark @@ -173,17 +168,17 @@ public void jni_args10() throws Throwable { @Benchmark public int panama_identity() throws Throwable { - return (int) identity.invokeExact(10, cb_identity); + return (int) identity.invokeExact(10, (Addressable)cb_identity); } @Benchmark public void panama_args5() throws Throwable { - args5.invokeExact(1L, 2D, 3L, 4D, 5L, cb_args5); + args5.invokeExact(1L, 2D, 3L, 4D, 5L, (Addressable)cb_args5); } @Benchmark public void panama_args10() throws Throwable { - args10.invokeExact(1L, 2D, 3L, 4D, 5L, 6D, 7L, 8D, 9L, 10D, cb_args10); + args10.invokeExact(1L, 2D, 3L, 4D, 5L, 6D, 7L, 8D, 9L, 10D, (Addressable)cb_args10); } static void blank() {} diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/VaList.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/VaList.java index 34d74652cadd6..d904c79a73401 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/VaList.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/VaList.java @@ -22,8 +22,10 @@ */ package org.openjdk.bench.jdk.incubator.foreign; -import jdk.incubator.foreign.FunctionDescriptor; +import jdk.incubator.foreign.Addressable; import jdk.incubator.foreign.CLinker; +import jdk.incubator.foreign.FunctionDescriptor; +import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.SymbolLookup; import jdk.incubator.foreign.ResourceScope; import org.openjdk.jmh.annotations.Benchmark; @@ -36,24 +38,17 @@ import org.openjdk.jmh.annotations.Warmup; import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; import java.util.concurrent.TimeUnit; -import static jdk.incubator.foreign.CLinker.C_DOUBLE; -import static jdk.incubator.foreign.CLinker.C_INT; -import static jdk.incubator.foreign.CLinker.C_LONG_LONG; -import static jdk.incubator.foreign.CLinker.C_VA_LIST; -import static jdk.incubator.foreign.CLinker.asVarArg; - @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) @State(org.openjdk.jmh.annotations.Scope.Thread) @OutputTimeUnit(TimeUnit.NANOSECONDS) @Fork(value = 3, jvmArgsAppend = { "--add-modules=jdk.incubator.foreign", "--enable-native-access=ALL-UNNAMED" }) -public class VaList { +public class VaList extends CLayouts { - static final CLinker linker = CLinker.getInstance(); + static final CLinker linker = CLinker.systemCLinker(); static { System.loadLibrary("VaList"); } @@ -64,11 +59,9 @@ public class VaList { static { SymbolLookup lookup = SymbolLookup.loaderLookup(); MH_ellipsis = linker.downcallHandle(lookup.lookup("ellipsis").get(), - MethodType.methodType(void.class, int.class, int.class, double.class, long.class), - FunctionDescriptor.ofVoid(C_INT, asVarArg(C_INT), asVarArg(C_DOUBLE), asVarArg(C_LONG_LONG))); + FunctionDescriptor.ofVoid(C_INT).asVariadic(C_INT, C_DOUBLE, C_LONG_LONG)); MH_vaList = linker.downcallHandle(lookup.lookup("vaList").get(), - MethodType.methodType(void.class, int.class, CLinker.VaList.class), - FunctionDescriptor.ofVoid(C_INT, C_VA_LIST)); + FunctionDescriptor.ofVoid(C_INT, C_POINTER)); } @Benchmark @@ -80,12 +73,12 @@ public void ellipsis() throws Throwable { @Benchmark public void vaList() throws Throwable { try (ResourceScope scope = ResourceScope.newConfinedScope()) { - CLinker.VaList vaList = CLinker.VaList.make(b -> - b.vargFromInt(C_INT, 1) - .vargFromDouble(C_DOUBLE, 2D) - .vargFromLong(C_LONG_LONG, 3L), scope); + jdk.incubator.foreign.VaList vaList = jdk.incubator.foreign.VaList.make(b -> + b.addVarg(C_INT, 1) + .addVarg(C_DOUBLE, 2D) + .addVarg(C_LONG_LONG, 3L), scope); MH_vaList.invokeExact(3, - vaList); + (Addressable)vaList); } } } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/VarHandleExact.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/VarHandleExact.java index d560765c48c1e..0cb783473df67 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/VarHandleExact.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/VarHandleExact.java @@ -40,7 +40,7 @@ import java.nio.ByteOrder; import java.util.concurrent.TimeUnit; -import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT; +import static jdk.incubator.foreign.ValueLayout.JAVA_INT; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -54,7 +54,7 @@ public class VarHandleExact { static final VarHandle generic; static { - generic = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder()); + generic = MemoryHandles.varHandle(JAVA_INT); exact = generic.withInvokeExactBehavior(); } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/libCallOverhead.c b/test/micro/org/openjdk/bench/jdk/incubator/foreign/libCallOverhead.c index f42aec0172c4e..8ad44f58cec49 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/libCallOverhead.c +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/libCallOverhead.c @@ -34,18 +34,26 @@ EXPORT int identity(int x) { } typedef struct { - long long x; - long long y; + int x; + int y; } Point; EXPORT Point identity_struct(Point p) { return p; } +EXPORT Point identity_struct_3(Point p1, Point p2, Point p3) { + return p1; +} + EXPORT void* identity_memory_address(void* p) { return p; } +EXPORT void* identity_memory_address_3(void* p1, void* p2, void* p3) { + return p1; +} + EXPORT void args1(long long a0) {} EXPORT void args2(long long a0, double a1) {} EXPORT void args3(long long a0, double a1, long long a2) {} diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/libJNICB.c b/test/micro/org/openjdk/bench/jdk/incubator/foreign/libJNICB.c new file mode 100644 index 0000000000000..617d16d29dd96 --- /dev/null +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/libJNICB.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include + +#include "JNICB.h" +#include "jlong.h" + +#define CHECK_NULL(thing, message) \ + if (thing == NULL) { \ + jclass cls = (*env)->FindClass(env, "java/lang/Exception"); \ + (*env)->ThrowNew(env, cls, message); \ + return 0; \ + } + +JNIEXPORT jlong JNICALL Java_org_openjdk_bench_jdk_incubator_foreign_JNICB_makeCB + (JNIEnv *env, jclass cls, jstring holderName, jstring methodName, jstring descriptor) { + + const char* holderNameC = (*env)->GetStringUTFChars(env, holderName, NULL); + const char* methodNameC = (*env)->GetStringUTFChars(env, methodName, NULL); + const char* descriptorC = (*env)->GetStringUTFChars(env, descriptor, NULL); + + JNICB cb = malloc(sizeof *cb); + CHECK_NULL(cb, "Can not allocate cb"); + + jclass holder = (*env)->FindClass(env, holderNameC); + CHECK_NULL(holder, "Can not find class"); + holder = (jclass) (*env)->NewGlobalRef(env, holder); + cb->holder = holder; + + jmethodID methodID = (*env)->GetStaticMethodID(env, holder, methodNameC, descriptorC); + CHECK_NULL(methodID, "Can not find method"); + //methodID = (jmethodID) (*env)->NewGlobalRef(env, methodID); // DON'T DO THIS! -> Crashes GC + cb->mid = methodID; + + (*env)->ReleaseStringUTFChars(env, holderName, holderNameC); + (*env)->ReleaseStringUTFChars(env, methodName, methodNameC); + (*env)->ReleaseStringUTFChars(env, descriptor, descriptorC); + + return ptr_to_jlong(cb); +} diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/libQSort.c b/test/micro/org/openjdk/bench/jdk/incubator/foreign/libQSort.c new file mode 100644 index 0000000000000..cafb83d8708c8 --- /dev/null +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/libQSort.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifdef _WIN64 +#define EXPORT __declspec(dllexport) +#else +#define EXPORT +#endif + + +EXPORT int compar(const void* e0, const void* e1) { + int i0 = *((int*) e0); + int i1 = *((int*) e1); + return i0 - i1; +} diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/libQSortJNI.c b/test/micro/org/openjdk/bench/jdk/incubator/foreign/libQSortJNI.c new file mode 100644 index 0000000000000..f6fe1b0c5d1a3 --- /dev/null +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/libQSortJNI.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include + +#include + +#include "jlong.h" +#include "JNICB.h" + +#ifdef _WIN64 +#define THREAD_LOCAL __declspec(thread) +#else +#define THREAD_LOCAL __thread +#endif + +THREAD_LOCAL struct { + JNICB cb; + JNIEnv* env; +} ctx_opt; + +static int comparator(const void* e0, const void* e1) { + JNICB jniCb = ctx_opt.cb; + JNIEnv* env = ctx_opt.env; + jint j0 = *((jint*) e0); + jint j1 = *((jint*) e1); + return (*env)->CallStaticIntMethod(env, jniCb->holder, jniCb->mid, j0, j1); +} + +JNIEXPORT void JNICALL Java_org_openjdk_bench_jdk_incubator_foreign_QSort_jni_1qsort_1optimized + (JNIEnv *env, jclass cls, jintArray arr, jlong cb) { + + ctx_opt.cb = jlong_to_ptr(cb); + ctx_opt.env = env; + + jint* ints = (*env)->GetIntArrayElements(env, arr, NULL); + jsize length = (*env)->GetArrayLength(env, arr); + + qsort(ints, length, sizeof(jint), &comparator); + + (*env)->ReleaseIntArrayElements(env, arr, ints, 0); +} + +JavaVM* VM = NULL; + +int java_cmp(const void *a, const void *b) { + int v1 = *((int*)a); + int v2 = *((int*)b); + + JNIEnv* env; + (*VM)->GetEnv(VM, (void**) &env, JNI_VERSION_10); + + jclass qsortClass = (*env)->FindClass(env, "org/openjdk/bench/jdk/incubator/foreign/QSort"); + jmethodID methodId = (*env)->GetStaticMethodID(env, qsortClass, "jni_upcall_compar", "(II)I"); + + return (*env)->CallStaticIntMethod(env, qsortClass, methodId, v1, v2); +} + +JNIEXPORT void JNICALL Java_org_openjdk_bench_jdk_incubator_foreign_QSort_jni_1qsort_1naive + (JNIEnv *env, jclass cls, jintArray arr) { + if (VM == NULL) { + (*env)->GetJavaVM(env, &VM); + } + + jint* carr = (*env)->GetIntArrayElements(env, arr, 0); + jsize length = (*env)->GetArrayLength(env, arr); + qsort(carr, length, sizeof(jint), java_cmp); + (*env)->ReleaseIntArrayElements(env, arr, carr, 0); +} diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/libUpcallsJNI.c b/test/micro/org/openjdk/bench/jdk/incubator/foreign/libUpcallsJNI.c index c44979e3e62f1..4e8926376213e 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/libUpcallsJNI.c +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/libUpcallsJNI.c @@ -22,46 +22,9 @@ */ #include #include -#include "jlong.h" - -typedef struct { - jclass holder; - jmethodID mid; -} *JNICB; - -#define CHECK_NULL(thing, message) \ - if (thing == NULL) { \ - jclass cls = (*env)->FindClass(env, "java/lang/Exception"); \ - (*env)->ThrowNew(env, cls, message); \ - return 0; \ - } - -JNIEXPORT jlong JNICALL Java_org_openjdk_bench_jdk_incubator_foreign_Upcalls_makeCB - (JNIEnv *env, jclass cls, jstring holderName, jstring methodName, jstring descriptor) { - - const char* holderNameC = (*env)->GetStringUTFChars(env, holderName, NULL); - const char* methodNameC = (*env)->GetStringUTFChars(env, methodName, NULL); - const char* descriptorC = (*env)->GetStringUTFChars(env, descriptor, NULL); - - JNICB cb = malloc(sizeof *cb); - CHECK_NULL(cb, "Can not allocate cb"); - jclass holder = (*env)->FindClass(env, holderNameC); - CHECK_NULL(holder, "Can not find class"); - holder = (jclass) (*env)->NewGlobalRef(env, holder); - cb->holder = holder; - - jmethodID methodID = (*env)->GetStaticMethodID(env, holder, methodNameC, descriptorC); - CHECK_NULL(methodID, "Can not find method"); - //methodID = (jmethodID) (*env)->NewGlobalRef(env, methodID); // DON'T DO THIS! -> Crashes GC - cb->mid = methodID; - - (*env)->ReleaseStringUTFChars(env, holderName, holderNameC); - (*env)->ReleaseStringUTFChars(env, methodName, methodNameC); - (*env)->ReleaseStringUTFChars(env, descriptor, descriptorC); - - return ptr_to_jlong(cb); -} +#include "jlong.h" +#include "JNICB.h" JNIEXPORT void JNICALL Java_org_openjdk_bench_jdk_incubator_foreign_Upcalls_blank (JNIEnv *env, jclass cls, jlong cb) { diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/points/support/PanamaPoint.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/points/support/PanamaPoint.java index b12d57b20bead..87c4855771bd4 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/points/support/PanamaPoint.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/points/support/PanamaPoint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,46 +22,45 @@ */ package org.openjdk.bench.jdk.incubator.foreign.points.support; +import jdk.incubator.foreign.Addressable; +import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.FunctionDescriptor; -import jdk.incubator.foreign.SymbolLookup; import jdk.incubator.foreign.MemoryAddress; +import jdk.incubator.foreign.SymbolLookup; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; -import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.ResourceScope; +import org.openjdk.bench.jdk.incubator.foreign.CLayouts; import java.lang.invoke.MethodHandle; import java.lang.invoke.VarHandle; import static java.lang.invoke.MethodType.methodType; import static jdk.incubator.foreign.MemoryLayout.PathElement.groupElement; -import static jdk.incubator.foreign.CLinker.*; -public class PanamaPoint implements AutoCloseable { +public class PanamaPoint extends CLayouts implements AutoCloseable { public static final MemoryLayout LAYOUT = MemoryLayout.structLayout( C_INT.withName("x"), C_INT.withName("y") ); - private static final VarHandle VH_x = LAYOUT.varHandle(int.class, groupElement("x")); - private static final VarHandle VH_y = LAYOUT.varHandle(int.class, groupElement("y")); + private static final VarHandle VH_x = LAYOUT.varHandle(groupElement("x")); + private static final VarHandle VH_y = LAYOUT.varHandle(groupElement("y")); private static final MethodHandle MH_distance; private static final MethodHandle MH_distance_ptrs; static { - CLinker abi = CLinker.getInstance(); + CLinker abi = CLinker.systemCLinker(); System.loadLibrary("Point"); SymbolLookup lookup = SymbolLookup.loaderLookup(); MH_distance = abi.downcallHandle( lookup.lookup("distance").get(), - methodType(double.class, MemorySegment.class, MemorySegment.class), - FunctionDescriptor.of(C_DOUBLE, LAYOUT, LAYOUT) + FunctionDescriptor.of(C_DOUBLE, LAYOUT, LAYOUT) ); MH_distance_ptrs = abi.downcallHandle( - lookup.lookup("distance_ptrs").get(), - methodType(double.class, MemoryAddress.class, MemoryAddress.class), - FunctionDescriptor.of(C_DOUBLE, C_POINTER, C_POINTER) + lookup.lookup("distance_ptrs").get(), + FunctionDescriptor.of(C_DOUBLE, C_POINTER, C_POINTER) ); } @@ -107,7 +106,7 @@ public double distanceTo(PanamaPoint other) { public double distanceToPtrs(PanamaPoint other) { try { - return (double) MH_distance_ptrs.invokeExact(segment.address(), other.segment.address()); + return (double) MH_distance_ptrs.invokeExact((Addressable)segment, (Addressable)other.segment); } catch (Throwable throwable) { throw new InternalError(throwable); } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/TestLoadStoreBytes.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/TestLoadStoreBytes.java index 582a7b4665a01..ce0386cb028ae 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/vector/TestLoadStoreBytes.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/TestLoadStoreBytes.java @@ -26,8 +26,6 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.concurrent.TimeUnit; -import jdk.incubator.foreign.CLinker; -import jdk.incubator.foreign.MemoryAccess; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; @@ -46,6 +44,8 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; +import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE; + @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -116,8 +116,8 @@ public void setup() { dstBufferSegmentImplicit = dstSegmentImplicit.asByteBuffer(); - srcAddress = CLinker.allocateMemory(size); - dstAddress = CLinker.allocateMemory(size); + srcAddress = MemorySegment.allocateNative(size, implicitScope).address(); + dstAddress = MemorySegment.allocateNative(size, implicitScope).address(); a = new byte[size]; b = new byte[size]; @@ -232,16 +232,16 @@ public void bufferSegmentImplicit() { @CompilerControl(CompilerControl.Mode.PRINT) public void segmentImplicitScalar() { for (int i = 0; i < SPECIES.loopBound(srcArray.length); i++) { - var v = MemoryAccess.getByteAtOffset(srcSegmentImplicit, i); - MemoryAccess.setByteAtOffset(dstSegmentImplicit, i, v); + var v = srcSegmentImplicit.get(JAVA_BYTE, i); + dstSegmentImplicit.set(JAVA_BYTE, i, v); } } @Benchmark public void bufferSegmentConfined() { try (final var scope = ResourceScope.newConfinedScope()) { - final var srcBufferSegmentConfined = srcAddress.asSegment(size, scope).asByteBuffer(); - final var dstBufferSegmentConfined = dstAddress.asSegment(size, scope).asByteBuffer(); + final var srcBufferSegmentConfined = MemorySegment.ofAddress(srcAddress, size, scope).asByteBuffer(); + final var dstBufferSegmentConfined = MemorySegment.ofAddress(dstAddress, size, scope).asByteBuffer(); for (int i = 0; i < SPECIES.loopBound(srcArray.length); i += SPECIES.length()) { var v = ByteVector.fromByteBuffer(SPECIES, srcBufferSegmentConfined, i, ByteOrder.nativeOrder()); diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/TestLoadStoreShort.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/TestLoadStoreShort.java index 6bd9a85143fdf..881cfad9aa6d5 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/vector/TestLoadStoreShort.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/TestLoadStoreShort.java @@ -26,7 +26,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.concurrent.TimeUnit; -import jdk.incubator.foreign.CLinker; + import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope; @@ -112,15 +112,15 @@ public void setup() { dstBufferNative = ByteBuffer.allocateDirect(size); - implicitScope = ResourceScope.newImplicitScope(); + implicitScope = ResourceScope.newSharedScope(); srcSegmentImplicit = MemorySegment.allocateNative(size, SPECIES.vectorByteSize(), implicitScope); srcBufferSegmentImplicit = srcSegmentImplicit.asByteBuffer(); dstSegmentImplicit = MemorySegment.allocateNative(size, SPECIES.vectorByteSize(), implicitScope); dstBufferSegmentImplicit = dstSegmentImplicit.asByteBuffer(); - srcAddress = CLinker.allocateMemory(size); - dstAddress = CLinker.allocateMemory(size); + srcAddress = MemorySegment.allocateNative(size, implicitScope).address(); + dstAddress = MemorySegment.allocateNative(size, implicitScope).address(); this.longSize = longSize; @@ -130,12 +130,6 @@ public void setup() { } - @TearDown - public void tearDown() { - CLinker.freeMemory(srcAddress); - CLinker.freeMemory(dstAddress); - } - @Benchmark @CompilerControl(CompilerControl.Mode.PRINT) public void array() { @@ -216,8 +210,8 @@ public void bufferSegmentImplicit() { @Benchmark public void bufferSegmentConfined() { try (final var scope = ResourceScope.newConfinedScope()) { - final var srcBufferSegmentConfined = srcAddress.asSegment(size, scope).asByteBuffer(); - final var dstBufferSegmentConfined = dstAddress.asSegment(size, scope).asByteBuffer(); + final var srcBufferSegmentConfined = MemorySegment.ofAddress(srcAddress, size, scope).asByteBuffer(); + final var dstBufferSegmentConfined = MemorySegment.ofAddress(dstAddress, size, scope).asByteBuffer(); for (int i = 0; i < SPECIES.loopBound(srcArray.length); i += SPECIES.length()) { var v = ShortVector.fromByteBuffer(SPECIES, srcBufferSegmentConfined, i, ByteOrder.nativeOrder());