diff --git a/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp b/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp index a46a8be39d144..7a940a4b25a2e 100644 --- a/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp @@ -210,23 +210,7 @@ void DowncallStubGenerator::generate() { __ blr(_abi._target_addr_reg); // this call is assumed not to have killed rthread - if (!_needs_return_buffer) { - // Unpack native results. - switch (_ret_bt) { - case T_BOOLEAN: __ c2bool(r0); break; - case T_CHAR : __ ubfx(r0, r0, 0, 16); break; - case T_BYTE : __ sbfx(r0, r0, 0, 8); break; - case T_SHORT : __ sbfx(r0, r0, 0, 16); break; - case T_INT : __ sbfx(r0, r0, 0, 32); break; - case T_DOUBLE : - case T_FLOAT : - // Result is in v0 we'll save as needed - break; - case T_VOID: break; - case T_LONG: break; - default : ShouldNotReachHere(); - } - } else { + if (_needs_return_buffer) { assert(ret_buf_addr_sp_offset != -1, "no return buffer addr spill"); __ ldr(tmp1, Address(sp, ret_buf_addr_sp_offset)); int offset = 0; diff --git a/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp b/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp index 06bf0099a917b..b7da306ad752b 100644 --- a/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp +++ b/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp @@ -211,24 +211,7 @@ void DowncallStubGenerator::generate() { __ call(_abi._target_addr_reg); // this call is assumed not to have killed r15_thread - if (!_needs_return_buffer) { - // FIXME: this assumes we return in rax/xmm0, which might not be the case - // Unpack native results. - switch (_ret_bt) { - case T_BOOLEAN: __ c2bool(rax); break; - case T_CHAR : __ movzwl(rax, rax); break; - case T_BYTE : __ sign_extend_byte (rax); break; - case T_SHORT : __ sign_extend_short(rax); break; - case T_INT : /* nothing to do */ break; - case T_DOUBLE : - case T_FLOAT : - // Result is in xmm0 we'll save as needed - break; - case T_VOID: break; - case T_LONG: break; - default : ShouldNotReachHere(); - } - } else { + if (_needs_return_buffer) { assert(ret_buf_addr_rsp_offset != -1, "no return buffer addr spill"); __ movptr(rscratch1, Address(rsp, ret_buf_addr_rsp_offset)); int offset = 0; diff --git a/src/hotspot/share/ci/ciField.cpp b/src/hotspot/share/ci/ciField.cpp index 485db1b2748a5..24bf59dcb91a5 100644 --- a/src/hotspot/share/ci/ciField.cpp +++ b/src/hotspot/share/ci/ciField.cpp @@ -225,7 +225,7 @@ static bool trust_final_non_static_fields(ciInstanceKlass* holder) { // Even if general trusting is disabled, trust system-built closures in these packages. if (holder->is_in_package("java/lang/invoke") || holder->is_in_package("sun/invoke") || holder->is_in_package("java/lang/reflect") || holder->is_in_package("jdk/internal/reflect") || - holder->is_in_package("jdk/internal/foreign") || holder->is_in_package("java/lang/foreign") || + holder->is_in_package("jdk/internal/foreign/layout") || holder->is_in_package("jdk/internal/foreign") || holder->is_in_package("jdk/internal/vm/vector") || holder->is_in_package("jdk/incubator/vector") || holder->is_in_package("java/lang")) return true; diff --git a/src/java.base/share/classes/java/lang/Module.java b/src/java.base/share/classes/java/lang/Module.java index 2f315b42cfcde..9ca560cfcba29 100644 --- a/src/java.base/share/classes/java/lang/Module.java +++ b/src/java.base/share/classes/java/lang/Module.java @@ -52,10 +52,12 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import jdk.internal.javac.PreviewFeature; import jdk.internal.loader.BuiltinClassLoader; import jdk.internal.loader.BootLoader; import jdk.internal.loader.ClassLoaders; import jdk.internal.misc.CDS; +import jdk.internal.module.ModuleBootstrap; import jdk.internal.module.ModuleLoaderMap; import jdk.internal.module.ServicesCatalog; import jdk.internal.module.Resources; @@ -256,25 +258,77 @@ public ModuleLayer getLayer() { /** * Update this module to allow access to restricted methods. */ - Module implAddEnableNativeAccess() { + synchronized Module implAddEnableNativeAccess() { enableNativeAccess = true; return this; } /** - * Update all unnamed modules to allow access to restricted methods. + * Returns {@code true} if this module can access + * restricted methods. + * + * @since 20 + * + * @return {@code true} if this module can access restricted methods. */ - static void implAddEnableNativeAccessAllUnnamed() { - ALL_UNNAMED_MODULE.enableNativeAccess = true; + @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) + public boolean isNativeAccessEnabled() { + Module target = moduleForNativeAccess(); + synchronized(target) { + return target.enableNativeAccess; + } } + // Returns the Module object that holds the enableNativeAccess + // flag for this module. + private Module moduleForNativeAccess() { + return isNamed() ? this : ALL_UNNAMED_MODULE; + } + + // This is invoked from Reflection.ensureNativeAccess + void ensureNativeAccess(Class owner, String methodName) { + // The target module whose enableNativeAccess flag is ensured + Module target = moduleForNativeAccess(); + // racy read of the enable native access flag + boolean isNativeAccessEnabled = target.enableNativeAccess; + if (!isNativeAccessEnabled) { + synchronized (target) { + // safe read of the enableNativeAccess of the target module + isNativeAccessEnabled = target.enableNativeAccess; + + // check again with the safely read flag + if (isNativeAccessEnabled) { + // another thread beat us to it - nothing to do + return; + } else if (ModuleBootstrap.hasEnableNativeAccessFlag()) { + throw new IllegalCallerException("Illegal native access from: " + this); + } else { + // warn and set flag, so that only one warning is reported per module + String cls = owner.getName(); + String mtd = cls + "::" + methodName; + String mod = isNamed() ? "module " + getName() : "the unnamed module"; + String modflag = isNamed() ? getName() : "ALL-UNNAMED"; + System.err.printf(""" + WARNING: A restricted method in %s has been called + WARNING: %s has been called by %s + WARNING: Use --enable-native-access=%s to avoid a warning for this module + %n""", cls, mtd, mod, modflag); + + // set the flag + target.enableNativeAccess = true; + } + } + } + } + + /** - * Returns true if module m can access restricted methods. + * Update all unnamed modules to allow access to restricted methods. */ - boolean implIsEnableNativeAccess() { - return isNamed() ? - enableNativeAccess : - ALL_UNNAMED_MODULE.enableNativeAccess; + static void implAddEnableNativeAccessToAllUnnamed() { + synchronized (ALL_UNNAMED_MODULE) { + ALL_UNNAMED_MODULE.enableNativeAccess = true; + } } // -- diff --git a/src/java.base/share/classes/java/lang/ModuleLayer.java b/src/java.base/share/classes/java/lang/ModuleLayer.java index 91e4dd873beed..444f8cdeb82f4 100644 --- a/src/java.base/share/classes/java/lang/ModuleLayer.java +++ b/src/java.base/share/classes/java/lang/ModuleLayer.java @@ -44,15 +44,17 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import jdk.internal.javac.PreviewFeature; import jdk.internal.loader.ClassLoaderValue; import jdk.internal.loader.Loader; import jdk.internal.loader.LoaderPool; import jdk.internal.module.ServicesCatalog; import jdk.internal.misc.CDS; +import jdk.internal.reflect.CallerSensitive; +import jdk.internal.reflect.Reflection; import jdk.internal.vm.annotation.Stable; import sun.security.util.SecurityConstants; - /** * A layer of modules in the Java virtual machine. * @@ -297,6 +299,39 @@ public Controller addOpens(Module source, String pn, Module target) { source.implAddOpens(pn, target); return this; } + + /** + * Enables native access for a module in the layer if the caller's module + * has native access. + * + *

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 target + * The module to update + * + * @return This controller + * + * @throws IllegalArgumentException + * If {@code target} is not in the module layer + * + * @throws IllegalCallerException + * If the caller is in a module that does not have native access enabled + * + * @since 20 + */ + @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) + @CallerSensitive + public Controller enableNativeAccess(Module target) { + ensureInLayer(target); + Reflection.ensureNativeAccess(Reflection.getCallerClass(), Module.class, + "enableNativeAccess"); + target.implAddEnableNativeAccess(); + return this; + } } diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index 0cb4624ad8a9a..f429a041e3d19 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -2437,11 +2437,11 @@ public boolean isReflectivelyOpened(Module m, String pn, Module other) { public Module addEnableNativeAccess(Module m) { return m.implAddEnableNativeAccess(); } - public void addEnableNativeAccessAllUnnamed() { - Module.implAddEnableNativeAccessAllUnnamed(); + public void addEnableNativeAccessToAllUnnamed() { + Module.implAddEnableNativeAccessToAllUnnamed(); } - public boolean isEnableNativeAccess(Module m) { - return m.implIsEnableNativeAccess(); + public void ensureNativeAccess(Module m, Class owner, String methodName) { + m.ensureNativeAccess(owner, methodName); } public ServicesCatalog getServicesCatalog(ModuleLayer layer) { return layer.getServicesCatalog(); diff --git a/src/java.base/share/classes/java/lang/foreign/Arena.java b/src/java.base/share/classes/java/lang/foreign/Arena.java new file mode 100644 index 0000000000000..2fff341e65849 --- /dev/null +++ b/src/java.base/share/classes/java/lang/foreign/Arena.java @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.foreign; + +import jdk.internal.foreign.MemorySessionImpl; +import jdk.internal.javac.PreviewFeature; + +/** + * An arena controls the lifecycle of memory segments, providing both flexible allocation and timely deallocation. + *

+ * An arena has a {@linkplain #scope() scope}, called the arena scope. When the arena is {@linkplain #close() closed}, + * the arena scope is no longer {@linkplain SegmentScope#isAlive() alive}. As a result, all the + * segments associated with the arena scope are invalidated, safely and atomically, their backing memory regions are + * deallocated (where applicable) and can no longer be accessed after the arena is closed: + * + * {@snippet lang = java: + * try (Arena arena = Arena.openConfined()) { + * MemorySegment segment = MemorySegment.allocateNative(100, arena.scope()); + * ... + * } // memory released here + *} + * + * Furthermore, an arena is a {@link SegmentAllocator}. All the segments {@linkplain #allocate(long, long) allocated} by the + * arena are associated with the arena scope. This makes arenas extremely useful when interacting with foreign code, as shown below: + * + * {@snippet lang = java: + * try (Arena arena = Arena.openConfined()) { + * MemorySegment nativeArray = arena.allocateArray(ValueLayout.JAVA_INT, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + * MemorySegment nativeString = arena.allocateUtf8String("Hello!"); + * MemorySegment upcallStub = linker.upcallStub(handle, desc, arena.scope()); + * ... + * } // memory released here + *} + * + *

Safety and thread-confinement

+ * + * Arenas provide strong temporal safety guarantees: a memory segment allocated by an arena cannot be accessed + * after the arena has been closed. The cost of providing this guarantee varies based on the + * number of threads that have access to the memory segments allocated by the arena. For instance, if an arena + * is always created and closed by one thread, and the memory segments associated with the arena's scope are always + * accessed by that same thread, then ensuring correctness is trivial. + *

+ * Conversely, if an arena allocates segments that can be accessed by multiple threads, or if the arena can be closed + * by a thread other than the accessing thread, then ensuring correctness is much more complex. For example, a segment + * allocated with the arena might be accessed while another thread attempts, concurrently, to close the arena. + * To provide the strong temporal safety guarantee without forcing every client, even simple ones, to incur a performance + * impact, arenas are divided into thread-confined arenas, and shared arenas. + *

+ * Confined arenas, support strong thread-confinement guarantees. Upon creation, they are assigned an + * {@linkplain #isCloseableBy(Thread) owner thread}, typically the thread which initiated the creation operation. + * The segments created by a confined arena can only be {@linkplain SegmentScope#isAccessibleBy(Thread) accessed} + * by the owner thread. Moreover, any attempt to close the confined arena from a thread other than the owner thread will + * fail with {@link WrongThreadException}. + *

+ * Shared arenas, on the other hand, have no owner thread. The segments created by a shared arena + * can be {@linkplain SegmentScope#isAccessibleBy(Thread) accessed} by any thread. This might be useful when + * multiple threads need to access the same memory segment concurrently (e.g. in the case of parallel processing). + * Moreover, a shared arena {@linkplain #isCloseableBy(Thread) can be closed} by any thread. + * + * @since 20 + */ +@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) +public interface Arena extends SegmentAllocator, AutoCloseable { + + /** + * Returns a native memory segment with the given size (in bytes) and alignment constraint (in bytes). + * The returned segment is associated with the arena scope. + * The segment's {@link MemorySegment#address() address} is the starting address of the + * allocated off-heap memory region backing the segment, and the address is + * aligned according the provided alignment constraint. + * + * @implSpec + * The default implementation of this method is equivalent to the following code: + * {@snippet lang = java: + * MemorySegment.allocateNative(bytesSize, byteAlignment, scope()); + *} + * More generally implementations of this method must return a native segment featuring the requested size, + * and that is compatible with the provided alignment constraint. Furthermore, for any two segments + * {@code S1, S2} returned by this method, the following invariant must hold: + * + * {@snippet lang = java: + * S1.overlappingSlice(S2).isEmpty() == true + *} + * + * @param byteSize the size (in bytes) of the off-heap memory block backing the native memory segment. + * @param byteAlignment the alignment constraint (in bytes) of the off-heap region of memory backing the native memory segment. + * @return a new native memory segment. + * @throws IllegalArgumentException if {@code bytesSize < 0}, {@code alignmentBytes <= 0}, or if {@code alignmentBytes} + * is not a power of 2. + * @throws IllegalStateException if the arena has already been {@linkplain #close() closed}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @see MemorySegment#allocateNative(long, long, SegmentScope) + */ + @Override + default MemorySegment allocate(long byteSize, long byteAlignment) { + return MemorySegment.allocateNative(byteSize, byteAlignment, scope()); + } + + /** + * {@return the arena scope} + */ + SegmentScope scope(); + + /** + * Closes this arena. If this method completes normally, the arena scope is no longer {@linkplain SegmentScope#isAlive() alive}, + * and all the memory segments associated with it can no longer be accessed. Furthermore, any off-heap region of memory backing the + * segments associated with that scope are also released. + * + * @apiNote This operation is not idempotent; that is, closing an already closed arena always results in an + * exception being thrown. This reflects a deliberate design choice: failure to close an arena might reveal a bug + * in the underlying application logic. + * + * @see SegmentScope#isAlive() + * + * @throws IllegalStateException if the arena has already been closed. + * @throws IllegalStateException if the arena scope is {@linkplain SegmentScope#whileAlive(Runnable) kept alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code isCloseableBy(T) == false}. + */ + @Override + void close(); + + /** + * {@return {@code true} if the provided thread can close this arena} + * @param thread the thread to be tested. + */ + boolean isCloseableBy(Thread thread); + + /** + * {@return a new confined arena, owned by the current thread} + */ + static Arena openConfined() { + return MemorySessionImpl.createConfined(Thread.currentThread()).asArena(); + } + + /** + * {@return a new shared arena} + */ + static Arena openShared() { + return MemorySessionImpl.createShared().asArena(); + } +} diff --git a/src/java.base/share/classes/java/lang/foreign/FunctionDescriptor.java b/src/java.base/share/classes/java/lang/foreign/FunctionDescriptor.java index 3d0a22239c6e9..3b6627d7f92c5 100644 --- a/src/java.base/share/classes/java/lang/foreign/FunctionDescriptor.java +++ b/src/java.base/share/classes/java/lang/foreign/FunctionDescriptor.java @@ -25,101 +25,38 @@ package java.lang.foreign; import java.lang.invoke.MethodHandle; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import java.lang.invoke.MethodType; import java.util.Objects; import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; +import java.util.List; + +import jdk.internal.foreign.FunctionDescriptorImpl; import jdk.internal.javac.PreviewFeature; /** - * A function descriptor is made up of zero or more argument layouts and zero or one return layout. A function descriptor - * is used to model the signature of foreign functions when creating - * {@linkplain Linker#downcallHandle(Addressable, FunctionDescriptor) downcall method handles} or - * {@linkplain Linker#upcallStub(MethodHandle, FunctionDescriptor, MemorySession) upcall stubs}. + * A function descriptor models the signature of foreign functions. A function descriptor is made up of zero or more + * argument layouts and zero or one return layout. A function descriptor is typically used when creating + * {@linkplain Linker#downcallHandle(MemorySegment, FunctionDescriptor, Linker.Option...) downcall method handles} or + * {@linkplain Linker#upcallStub(MethodHandle, FunctionDescriptor, SegmentScope) upcall stubs}. * * @implSpec - * This class is immutable, thread-safe and value-based. + * Implementing classes are immutable, thread-safe and value-based. * * @see MemoryLayout * @since 19 */ @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) -public sealed class FunctionDescriptor permits FunctionDescriptor.VariadicFunction { - - private final MemoryLayout resLayout; - private final List argLayouts; - - private FunctionDescriptor(MemoryLayout resLayout, List argLayouts) { - this.resLayout = resLayout; - this.argLayouts = argLayouts; - } +public sealed interface FunctionDescriptor permits FunctionDescriptorImpl { /** * {@return the return layout (if any) associated with this function descriptor} */ - public Optional returnLayout() { - return Optional.ofNullable(resLayout); - } + Optional returnLayout(); /** * {@return the argument layouts associated with this function descriptor (as an immutable list)}. */ - public List argumentLayouts() { - return Collections.unmodifiableList(argLayouts); - } - - /** - * Creates a function descriptor with the given return and argument layouts. - * @param resLayout the return layout. - * @param argLayouts the argument layouts. - * @return the new function descriptor. - */ - public static FunctionDescriptor of(MemoryLayout resLayout, MemoryLayout... argLayouts) { - Objects.requireNonNull(resLayout); - Objects.requireNonNull(argLayouts); - Arrays.stream(argLayouts).forEach(Objects::requireNonNull); - return new FunctionDescriptor(resLayout, List.of(argLayouts)); - } - - /** - * Creates a function descriptor with the given argument layouts and no return layout. - * @param argLayouts the argument layouts. - * @return the new function descriptor. - */ - public static FunctionDescriptor ofVoid(MemoryLayout... argLayouts) { - Objects.requireNonNull(argLayouts); - Arrays.stream(argLayouts).forEach(Objects::requireNonNull); - return new FunctionDescriptor(null, List.of(argLayouts)); - } - - /** - * Creates 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 #changeReturnLayout(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 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; - } + List argumentLayouts(); /** * Returns a function descriptor with the given argument layouts appended to the argument layout array @@ -127,9 +64,7 @@ public int firstVariadicArgumentIndex() { * @param addedLayouts the argument layouts to append. * @return the new function descriptor. */ - public FunctionDescriptor appendArgumentLayouts(MemoryLayout... addedLayouts) { - return insertArgumentLayouts(argLayouts.size(), addedLayouts); - } + FunctionDescriptor appendArgumentLayouts(MemoryLayout... addedLayouts); /** * Returns a function descriptor with the given argument layouts inserted at the given index, into the argument @@ -139,110 +74,57 @@ public FunctionDescriptor appendArgumentLayouts(MemoryLayout... addedLayouts) { * @return the new function descriptor. * @throws IllegalArgumentException if {@code index < 0 || index > argumentLayouts().size()}. */ - public FunctionDescriptor insertArgumentLayouts(int index, MemoryLayout... addedLayouts) { - if (index < 0 || index > argLayouts.size()) - throw new IllegalArgumentException("Index out of bounds: " + index); - List added = List.of(addedLayouts); // null check on array and its elements - List newLayouts = new ArrayList<>(argLayouts.size() + addedLayouts.length); - newLayouts.addAll(argLayouts.subList(0, index)); - newLayouts.addAll(added); - newLayouts.addAll(argLayouts.subList(index, argLayouts.size())); - return new FunctionDescriptor(resLayout, newLayouts); - } + FunctionDescriptor insertArgumentLayouts(int index, MemoryLayout... addedLayouts); /** * Returns a function descriptor with the given memory layout as the new return layout. * @param newReturn the new return layout. * @return the new function descriptor. */ - public FunctionDescriptor changeReturnLayout(MemoryLayout newReturn) { - Objects.requireNonNull(newReturn); - return new FunctionDescriptor(newReturn, argLayouts); - } + FunctionDescriptor changeReturnLayout(MemoryLayout newReturn); /** * Returns a function descriptor with the return layout dropped. This is useful to model functions * which return no values. * @return the new function descriptor. */ - public FunctionDescriptor dropReturnLayout() { - return new FunctionDescriptor(null, argLayouts); - } + FunctionDescriptor dropReturnLayout(); /** - * {@return the string representation of this function descriptor} - */ - @Override - public String toString() { - return String.format("(%s)%s", - IntStream.range(0, argLayouts.size()) - .mapToObj(i -> (i == firstVariadicArgumentIndex() ? - "..." : "") + argLayouts.get(i)) - .collect(Collectors.joining()), - returnLayout().map(Object::toString).orElse("v")); - } - - /** - * 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 the following conditions are met: + * Returns the method type consisting of the carrier types of the layouts in this function descriptor. + *

+ * The carrier type of a layout is determined as follows: *

* - * @param other the object to be compared for equality with this function descriptor. - * @return {@code true} if the specified object is equal to this function descriptor. + * @return the method type consisting of the carrier types of the layouts in this function descriptor + * @throws IllegalArgumentException if one or more layouts in the function descriptor can not be mapped to carrier + * types (e.g. if they are sequence layouts or padding layouts). */ - @Override - public boolean equals(Object other) { - return other instanceof FunctionDescriptor f && - Objects.equals(resLayout, f.resLayout) && - Objects.equals(argLayouts, f.argLayouts) && - firstVariadicArgumentIndex() == f.firstVariadicArgumentIndex(); - } + MethodType toMethodType(); /** - * {@return the hash code value for this function descriptor} + * Creates a function descriptor with the given return and argument layouts. + * @param resLayout the return layout. + * @param argLayouts the argument layouts. + * @return the new function descriptor. */ - @Override - public int hashCode() { - return Objects.hash(argLayouts, resLayout, firstVariadicArgumentIndex()); + static FunctionDescriptor of(MemoryLayout resLayout, MemoryLayout... argLayouts) { + Objects.requireNonNull(resLayout); + // Null checks are implicit in List.of(argLayouts) + return FunctionDescriptorImpl.of(resLayout, List.of(argLayouts)); } - 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 appendArgumentLayouts(MemoryLayout... addedLayouts) { - throw new UnsupportedOperationException(); - } - - @Override - public FunctionDescriptor insertArgumentLayouts(int index, MemoryLayout... addedLayouts) { - throw new UnsupportedOperationException(); - } - - @Override - public FunctionDescriptor changeReturnLayout(MemoryLayout newReturn) { - throw new UnsupportedOperationException(); - } - - @Override - public FunctionDescriptor dropReturnLayout() { - throw new UnsupportedOperationException(); - } + /** + * Creates a function descriptor with the given argument layouts and no return layout. + * @param argLayouts the argument layouts. + * @return the new function descriptor. + */ + static FunctionDescriptor ofVoid(MemoryLayout... argLayouts) { + // Null checks are implicit in List.of(argLayouts) + return FunctionDescriptorImpl.ofVoid(List.of(argLayouts)); } } diff --git a/src/java.base/share/classes/java/lang/foreign/GroupLayout.java b/src/java.base/share/classes/java/lang/foreign/GroupLayout.java index d012735ba9f8d..56ace4ee13513 100644 --- a/src/java.base/share/classes/java/lang/foreign/GroupLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/GroupLayout.java @@ -25,19 +25,14 @@ */ package java.lang.foreign; -import java.util.Collections; import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.function.LongBinaryOperator; -import java.util.stream.Collectors; import jdk.internal.javac.PreviewFeature; /** * A compound layout that aggregates 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 + * can be combined: if member layouts are laid out one after the other, the resulting group layout is said to be a struct layout * (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...)}). + * the resulting group layout is said to be a union layout (see {@link MemoryLayout#unionLayout(MemoryLayout...)}). * * @implSpec * This class is immutable, thread-safe and value-based. @@ -45,55 +40,7 @@ * @since 19 */ @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) -public final class GroupLayout extends AbstractLayout implements MemoryLayout { - - /** - * The group kind. - */ - enum Kind { - /** - * A 'struct' kind. - */ - STRUCT("", Math::addExact), - /** - * A 'union' kind. - */ - UNION("|", Math::max); - - final String delimTag; - final LongBinaryOperator sizeOp; - - Kind(String delimTag, LongBinaryOperator sizeOp) { - this.delimTag = delimTag; - this.sizeOp = sizeOp; - } - - long sizeof(List elems) { - long size = 0; - for (MemoryLayout elem : elems) { - size = sizeOp.applyAsLong(size, elem.bitSize()); - } - return size; - } - - long alignof(List elems) { - return elems.stream().mapToLong(MemoryLayout::bitAlignment).max() // max alignment in case we have member layouts - .orElse(1); // or minimal alignment if no member layout is given - } - } - - private final Kind kind; - private final List elements; - - GroupLayout(Kind kind, List elements) { - this(kind, elements, kind.alignof(elements), Optional.empty()); - } - - GroupLayout(Kind kind, List elements, long alignment, Optional name) { - super(kind.sizeof(elements), alignment, name); - this.kind = kind; - this.elements = elements; - } +public sealed interface GroupLayout extends MemoryLayout permits StructLayout, UnionLayout { /** * Returns the member layouts associated with this group. @@ -104,84 +51,17 @@ long alignof(List elems) { * * @return the member layouts associated with this group. */ - public List memberLayouts() { - return Collections.unmodifiableList(elements); - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return decorateLayoutString(elements.stream() - .map(Object::toString) - .collect(Collectors.joining(kind.delimTag, "[", "]"))); - } - - /** - * {@return {@code true}, if this group layout is a struct layout} - */ - public boolean isStruct() { - return kind == Kind.STRUCT; - } - - /** - * {@return {@code true}, if this group layout is a union layout} - */ - public boolean isUnion() { - return kind == Kind.UNION; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } - if (!super.equals(other)) { - return false; - } - return other instanceof GroupLayout otherGroup && - kind == otherGroup.kind && - elements.equals(otherGroup.elements); - } - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), kind, elements); - } - - @Override - GroupLayout dup(long alignment, Optional name) { - return new GroupLayout(kind, elements, alignment, name); - } - - @Override - boolean hasNaturalAlignment() { - return alignment == kind.alignof(elements); - } - - //hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout - //but that causes issues with javadoc, see JDK-8224052 + List memberLayouts(); /** * {@inheritDoc} */ @Override - public GroupLayout withName(String name) { - return (GroupLayout)super.withName(name); - } + GroupLayout withName(String name); /** * {@inheritDoc} */ @Override - public GroupLayout withBitAlignment(long alignmentBits) { - return (GroupLayout)super.withBitAlignment(alignmentBits); - } + GroupLayout withBitAlignment(long bitAlignment); } diff --git a/src/java.base/share/classes/java/lang/foreign/Linker.java b/src/java.base/share/classes/java/lang/foreign/Linker.java index 53196ab2bd0c5..c8fa1b2320fa0 100644 --- a/src/java.base/share/classes/java/lang/foreign/Linker.java +++ b/src/java.base/share/classes/java/lang/foreign/Linker.java @@ -26,13 +26,13 @@ package java.lang.foreign; import jdk.internal.foreign.abi.AbstractLinker; +import jdk.internal.foreign.abi.LinkerOptions; import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.javac.PreviewFeature; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; /** * A linker provides access to foreign functions from Java code, and access to Java code from foreign functions. @@ -47,9 +47,9 @@ * in the JVM and foreign functions in the library. In particular: *
    *
  • A linker allows Java code to link against foreign functions, via - * {@linkplain #downcallHandle(Addressable, FunctionDescriptor) downcall method handles}; and
  • + * {@linkplain #downcallHandle(MemorySegment, FunctionDescriptor, Option...) downcall method handles}; and *
  • A linker allows foreign functions to call Java method handles, - * via the generation of {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, MemorySession) upcall stubs}.
  • + * via the generation of {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, SegmentScope) upcall stubs}. *
* In addition, a linker provides a way to look up foreign functions in libraries that conform to the ABI. Each linker * chooses a set of libraries that are commonly used on the OS and processor combination associated with the ABI. @@ -62,29 +62,16 @@ * *

Downcall method handles

* - * {@linkplain #downcallHandle(FunctionDescriptor) Linking a foreign function} is a process which requires a function descriptor, + * {@linkplain #downcallHandle(FunctionDescriptor, Option...) 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 foreign function. *

* The Java {@linkplain 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: + * {@linkplain FunctionDescriptor#toMethodType() derived} from the argument and return layouts in the function descriptor. + * The downcall method handle type, might then be decorated by additional leading parameters, in the given order if both are present: *

    - *
  • 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}
  • - *
- *

- * The downcall method handle type, derived as above, might be decorated by additional leading parameters, - * in the given order if both are present: - *

    - *
  • If the downcall method handle is created {@linkplain #downcallHandle(FunctionDescriptor) without specifying a target address}, - * the downcall method handle type features a leading parameter of type {@link Addressable}, from which the + *
  • If the downcall method handle is created {@linkplain #downcallHandle(FunctionDescriptor, Option...) without specifying a target address}, + * the downcall method handle type features a leading parameter of type {@link MemorySegment}, from which the * address of the target foreign 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 @@ -93,26 +80,15 @@ * *

    Upcall stubs

    * - * {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, MemorySession) Creating an upcall stub} requires a method + * {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, SegmentScope) 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. *

    - * The type of the provided method handle has to {@linkplain #upcallType(FunctionDescriptor) match} the Java - * {@linkplain 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: - *

      - *
    • If {@code L} is a {@link ValueLayout} with carrier {@code E} then there are two cases: - *
        - *
      • 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}
    • - *
    + * The type of the provided method handle's type has to match the method type associated with the upcall stub, + * which is {@linkplain FunctionDescriptor#toMethodType() derived} from the provided function descriptor. + *

    * Upcall stubs are modelled by instances of type {@link MemorySegment}; upcall stubs can be passed by reference to other - * downcall method handles (as {@link MemorySegment} implements the {@link Addressable} interface) and, - * when no longer required, they can be {@linkplain MemorySession#close() released}, via their associated {@linkplain MemorySession session}. + * downcall method handles and, they are released via their associated {@linkplain SegmentScope scope}. * *

    Safety considerations

    * @@ -121,23 +97,34 @@ * 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} - * or {@link VaList}): + * the linker runtime guarantees the following for any argument {@code A} of type {@link MemorySegment} whose corresponding + * layout is {@link ValueLayout#ADDRESS}: *
      - *
    • The memory session of {@code R} is {@linkplain MemorySession#isAlive() alive}. Otherwise, the invocation throws + *
    • The scope of {@code A} is {@linkplain SegmentScope#isAlive() alive}. Otherwise, the invocation throws * {@link IllegalStateException};
    • - *
    • The invocation occurs in same thread as the one {@linkplain MemorySession#ownerThread() owning} the memory session of {@code R}, - * if said session is confined. Otherwise, the invocation throws {@link WrongThreadException}; and
    • - *
    • The memory session of {@code R} is {@linkplain MemorySession#whileAlive(Runnable) kept alive} (and cannot be closed) during the invocation.
    • + *
    • The invocation occurs in a thread {@code T} such that {@code A.scope().isAccessibleBy(T) == true}. + * Otherwise, the invocation throws {@link WrongThreadException}; and
    • + *
    • The scope of {@code A} is {@linkplain SegmentScope#whileAlive(Runnable) kept alive} during the invocation.
    • *
    + * A downcall method handle created from a function descriptor whose return layout is an + * {@linkplain ValueLayout.OfAddress address layout} returns a native segment associated with + * the {@linkplain SegmentScope#global() global scope}. Under normal conditions, the size of the returned segment is {@code 0}. + * However, if the return layout is an {@linkplain ValueLayout.OfAddress#asUnbounded() unbounded} address layout, + * then the size of the returned segment is {@code Long.MAX_VALUE}. *

    * 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 foreign 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 memory address}, clients must ensure + * handle associated with an upcall stub returns a {@linkplain MemorySegment memory segment}, 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. + *

    + * An upcall stub argument whose corresponding layout is an {@linkplain ValueLayout.OfAddress address layout} + * is a native segment associated with the {@linkplain SegmentScope#global() global scope}. + * Under normal conditions, the size of this segment argument is {@code 0}. However, if the layout associated with + * the upcall stub argument is an {@linkplain ValueLayout.OfAddress#asUnbounded() unbounded} address layout, + * then the size of the segment argument is {@code Long.MAX_VALUE}. * * @implSpec * Implementations of this interface are immutable, thread-safe and value-based. @@ -159,22 +146,22 @@ public sealed interface Linker permits AbstractLinker { * and its corresponding layout is dependent on the ABI of the returned linker; *

  • Composite types are modelled by a {@linkplain GroupLayout group layout}. Depending on the ABI of the * returned linker, additional {@linkplain MemoryLayout#paddingLayout(long) padding} member layouts might be required to conform - * to the size and alignment constraints of a composite type definition in C (e.g. using {@code struct} or {@code union}); and
  • - *
  • Pointer types are modelled by a {@linkplain ValueLayout value layout} instance with carrier {@link MemoryAddress}. + * to the size and alignment constraint of a composite type definition in C (e.g. using {@code struct} or {@code union}); and
  • + *
  • Pointer types are modelled by a {@linkplain ValueLayout value layout} instance with carrier {@link MemorySegment}. * Examples of pointer types in C are {@code int**} and {@code int(*)(size_t*, size_t*)};
  • *
*

* Any layout not listed above is unsupported; function descriptors containing unsupported layouts * will cause an {@link IllegalArgumentException} to be thrown, when used to create a - * {@link #downcallHandle(Addressable, FunctionDescriptor) downcall method handle} or an - * {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, MemorySession) upcall stub}. + * {@link #downcallHandle(MemorySegment, FunctionDescriptor, Option...) downcall method handle} or an + * {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, SegmentScope) upcall stub}. *

* Variadic functions (e.g. a C function declared 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 - * 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, where the foreign library allows it, clients might be able to interact with variadic functions by - * passing a trailing parameter of type {@link VaList} (e.g. as in {@code vsprintf}). + * variadic function by using {@linkplain Linker.Option#firstVariadicArg(int) a linker option} to indicate + * the start of the list of variadic arguments, together with a specialized function descriptor describing a + * given variable arity callsite. Alternatively, where the foreign library allows it, clients might be able to + * interact with variadic functions by passing a trailing parameter of type {@link VaList} (e.g. as in {@code vsprintf}). *

* This method is restricted. * Restricted methods are unsafe, and, if used incorrectly, their use might crash @@ -199,55 +186,60 @@ static Linker nativeLinker() { } /** - * Creates a method handle which can be used to call a target foreign function with the given signature and address. + * Creates a method handle which can be used to call a foreign function with the given signature and address. *

* 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. + * an additional prefix parameter, of type {@link SegmentAllocator}, which will be used by the linker to allocate + * structs returned by-value. *

* Calling this method is equivalent to the following code: * {@snippet lang=java : * linker.downcallHandle(function).bindTo(symbol); * } * - * @param symbol the address of the target function. + * @param symbol the address of the target function. * @param function the function descriptor of the target function. + * @param options any linker options. * @return a downcall method handle. The method handle type is inferred * @throws IllegalArgumentException if the provided function descriptor is not supported by this linker. - * or if the symbol is {@link MemoryAddress#NULL} + * or if the symbol is {@link MemorySegment#NULL} + * @throws IllegalArgumentException if an invalid combination of linker options is given. */ - default MethodHandle downcallHandle(Addressable symbol, FunctionDescriptor function) { + default MethodHandle downcallHandle(MemorySegment symbol, FunctionDescriptor function, Option... options) { SharedUtils.checkSymbol(symbol); - return downcallHandle(function).bindTo(symbol); + return downcallHandle(function, options).bindTo(symbol); } /** - * Creates a method handle which can be used to call a target foreign function with the given signature. + * Creates a method handle which can be used to call a foreign function with the given signature. * The resulting method handle features a prefix parameter (as the first parameter) corresponding to the foreign function - * entry point, of type {@link Addressable}, which is used to specify the address of the target function + * entry point, of type {@link MemorySegment}, which is used to specify the address of the target function * to be called. *

* 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. + * which will be used by the linker to allocate structs returned by-value. *

- * The returned method handle will throw an {@link IllegalArgumentException} if the {@link Addressable} parameter passed to it is - * associated with the {@link MemoryAddress#NULL} address, or a {@link NullPointerException} if that parameter is {@code null}. + * The returned method handle will throw an {@link IllegalArgumentException} if the {@link MemorySegment} parameter passed to it is + * associated with the {@link MemorySegment#NULL} address, or a {@link NullPointerException} if that parameter is {@code null}. * * @param function the function descriptor of the target function. + * @param options any linker options. * @return a downcall method handle. The method handle type is inferred * from the provided function descriptor. * @throws IllegalArgumentException if the provided function descriptor is not supported by this linker. + * @throws IllegalArgumentException if an invalid combination of linker options is given. */ - MethodHandle downcallHandle(FunctionDescriptor function); + MethodHandle downcallHandle(FunctionDescriptor function, Option... options); /** - * Creates a stub which can be passed to other foreign functions as a function pointer, with the given - * memory session. Calling such a function pointer from foreign code will result in the execution of the provided + * Creates a stub which can be passed to other foreign functions as a function pointer, associated with the given + * scope. Calling such a function pointer from foreign code will result in the execution of the provided * method handle. *

- * The returned memory segment's base address points to the newly allocated upcall stub, and is associated with - * the provided memory session. When such session is closed, the corresponding upcall stub will be deallocated. + * The returned memory segment's address points to the newly allocated upcall stub, and is associated with + * the provided scope. As such, the corresponding upcall stub will be deallocated + * when the scope becomes not {@linkplain SegmentScope#isAlive() alive}. *

* The target method handle should not throw any exceptions. If the target method handle does throw an exception, * the VM will exit with a non-zero exit code. To avoid the VM aborting due to an uncaught exception, clients @@ -257,16 +249,16 @@ default MethodHandle downcallHandle(Addressable symbol, FunctionDescriptor funct * * @param target the target method handle. * @param function the upcall stub function descriptor. - * @param session the upcall stub memory session. - * @return a zero-length segment whose base address is the address of the upcall stub. + * @param scope the scope associated with the returned upcall stub segment. + * @return a zero-length segment whose address is the address of the upcall stub. * @throws IllegalArgumentException if the provided function descriptor is not supported by this linker. * @throws IllegalArgumentException 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 session} is not {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread - * {@linkplain MemorySession#ownerThread() owning} {@code session}. + * @throws IllegalStateException if {@code scope} is not {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope.isAccessibleBy(T) == false}. */ - MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, MemorySession session); + MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, SegmentScope scope); /** * Returns a symbol lookup for symbols in a set of commonly used libraries. @@ -284,22 +276,21 @@ default MethodHandle downcallHandle(Addressable symbol, FunctionDescriptor funct SymbolLookup defaultLookup(); /** - * {@return the downcall method handle {@linkplain MethodType type} associated with the given function descriptor} - * @param functionDescriptor a 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). + * A linker option is used to indicate additional linking requirements to the linker, + * besides what is described by a function descriptor. + * @since 20 */ - static MethodType downcallType(FunctionDescriptor functionDescriptor) { - return SharedUtils.inferMethodType(functionDescriptor, false); - } + @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) + sealed interface Option + permits LinkerOptions.FirstVariadicArg { - /** - * {@return the method handle {@linkplain MethodType type} associated with an upcall stub with the given function descriptor} - * @param functionDescriptor a 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). - */ - static MethodType upcallType(FunctionDescriptor functionDescriptor) { - return SharedUtils.inferMethodType(functionDescriptor, true); + /** + * {@return a linker option used to denote the index of the first variadic argument layout in a + * foreign function call} + * @param index the index of the first variadic argument in a downcall handle linkage request. + */ + static Option firstVariadicArg(int index) { + return new LinkerOptions.FirstVariadicArg(index); + } } } diff --git a/src/java.base/share/classes/java/lang/foreign/MemoryAddress.java b/src/java.base/share/classes/java/lang/foreign/MemoryAddress.java deleted file mode 100644 index cc4cd949d6432..0000000000000 --- a/src/java.base/share/classes/java/lang/foreign/MemoryAddress.java +++ /dev/null @@ -1,854 +0,0 @@ -/* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -package java.lang.foreign; - -import java.nio.ByteOrder; - -import jdk.internal.foreign.MemoryAddressImpl; -import jdk.internal.javac.PreviewFeature; -import jdk.internal.reflect.CallerSensitive; - -import java.lang.invoke.MethodHandle; - -/** - * A memory address models a reference into a memory location. Memory addresses are typically obtained in one of the following ways: - *

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

Dereferencing memory addresses

- * - * 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 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 {@linkplain ByteOrder#nativeOrder() default endianness}, the following code can be used: - * {@snippet lang=java : - * 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: - * {@snippet lang=java : - * 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. - * - * @implSpec - * Implementations of this interface are immutable, thread-safe and value-based. - * - * @since 19 - */ -@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) -public sealed interface MemoryAddress extends Addressable permits MemoryAddressImpl { - - /** - * {@return the raw long value associated with this memory address} - */ - long toRawLongValue(); - - /** - * Returns a memory address at given offset from this address. - * @param offset specified offset (in bytes), relative to this address, which should be used to create the new address. - * Might be negative. - * @return a memory address with the given offset from current one. - */ - MemoryAddress addOffset(long offset); - - /** - * Reads a UTF-8 encoded, null-terminated string from this address at the 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. - *

- * 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). Might be negative. - * The final address of this read operation can be expressed as {@code toRawLongValue() + offset}. - * @return a Java string constructed from the bytes read from the given starting address ({@code toRawLongValue() + offset}) - * up to (but not including) the first {@code '\0'} terminator character (assuming one is found). - * @throws IllegalArgumentException if the size of the UTF-8 string is greater than the largest string supported by the platform. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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 the 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. - *

- * 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). Might be negative. - * The final address of this read operation can be expressed as {@code toRawLongValue() + 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 specified, but 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); - - /** - * {@return the hash code value for this address} - */ - @Override - int hashCode(); - - /** - * The memory address instance modelling the {@code NULL} address. - */ - MemoryAddress NULL = new MemoryAddressImpl(0L); - - /** - * Creates a memory address from the given long value. - * @param value the long value representing a raw address. - * @return a memory address with the given raw long value. - */ - static MemoryAddress ofLong(long value) { - return value == 0 ? - NULL : - new MemoryAddressImpl(value); - } - - /** - * Reads a byte from this address at the given offset, with the 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). Might be negative. - * The final address of this read operation can be expressed as {@code toRawLongValue() + offset}. - * @return a byte value read from this address. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or - * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. - */ - @CallerSensitive - byte get(ValueLayout.OfByte layout, long offset); - - /** - * Writes a byte into this address at the given offset, with the 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). Might be negative. - * The final address of this write operation can be expressed as {@code toRawLongValue() + offset}. - * @param value the byte value to be written. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or - * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. - */ - @CallerSensitive - void set(ValueLayout.OfByte layout, long offset, byte value); - - /** - * Reads a boolean from this address at the given offset, with the 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). Might be negative. - * The final address of this read operation can be expressed as {@code toRawLongValue() + offset}. - * @return a boolean value read from this address. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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 into this address at the given offset, with the 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). Might be negative. - * The final address of this write operation can be expressed as {@code toRawLongValue() + offset}. - * @param value the boolean value to be written. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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 at the given offset, with the 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). Might be negative. - * The final address of this read operation can be expressed as {@code toRawLongValue() + offset}. - * @return a char value read from this address. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or - * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. - */ - @CallerSensitive - char get(ValueLayout.OfChar layout, long offset); - - /** - * Writes a char into this address at the given offset, with the 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). Might be negative. - * The final address of this write operation can be expressed as {@code toRawLongValue() + offset}. - * @param value the char value to be written. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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 at the given offset, with the 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). Might be negative. - * The final address of this read operation can be expressed as {@code toRawLongValue() + offset}. - * @return a short value read from this address. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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 into this address at the given offset, with the 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). Might be negative. - * The final address of this write operation can be expressed as {@code toRawLongValue() + offset}. - * @param value the short value to be written. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or - * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. - */ - @CallerSensitive - void set(ValueLayout.OfShort layout, long offset, short value); - - /** - * Reads an int from this address at the given offset, with the 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). Might be negative. - * The final address of this read operation can be expressed as {@code toRawLongValue() + offset}. - * @return an int value read from this address. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or - * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. - */ - @CallerSensitive - int get(ValueLayout.OfInt layout, long offset); - - /** - * Writes an int into this address at the given offset, with the 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). Might be negative. - * The final address of this write operation can be expressed as {@code toRawLongValue() + offset}. - * @param value the int value to be written. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or - * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. - */ - @CallerSensitive - void set(ValueLayout.OfInt layout, long offset, int value); - - /** - * Reads a float from this address at the given offset, with the 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). Might be negative. - * The final address of this read operation can be expressed as {@code toRawLongValue() + offset}. - * @return a float value read from this address. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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 into this address at the given offset, with the 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). Might be negative. - * The final address of this write operation can be expressed as {@code toRawLongValue() + offset}. - * @param value the float value to be written. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or - * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. - */ - @CallerSensitive - void set(ValueLayout.OfFloat layout, long offset, float value); - - /** - * Reads a long from this address at the given offset, with the 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). Might be negative. - * The final address of this read operation can be expressed as {@code toRawLongValue() + offset}. - * @return a long value read from this address. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or - * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. - */ - @CallerSensitive - long get(ValueLayout.OfLong layout, long offset); - - /** - * Writes a long into this address at the given offset, with the 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). Might be negative. - * The final address of this write operation can be expressed as {@code toRawLongValue() + offset}. - * @param value the long value to be written. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or - * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. - */ - @CallerSensitive - void set(ValueLayout.OfLong layout, long offset, long value); - - /** - * Reads a double from this address at the given offset, with the 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). Might be negative. - * The final address of this read operation can be expressed as {@code toRawLongValue() + offset}. - * @return a double value read from this address. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or - * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. - */ - @CallerSensitive - double get(ValueLayout.OfDouble layout, long offset); - - /** - * Writes a double into this address at the given offset, with the 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). Might be negative. - * The final address of this write operation can be expressed as {@code toRawLongValue() + offset}. - * @param value the double value to be written. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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 at the given offset, with the 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). Might be negative. - * The final address of this read operation can be expressed as {@code toRawLongValue() + offset}. - * @return an address value read from this address. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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 into this address at the given offset, with the 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). Might be negative. - * The final address of this write operation can be expressed as {@code toRawLongValue() + offset}. - * @param value the address value to be written. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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 at the given index, scaled by the 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). Might be negative. - * The final address of this read operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}. - * @return a char value read from this address. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, - * or if the layout alignment is greater than its size. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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 into this address at the given index, scaled by the 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). Might be negative. - * The final address of this write operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}. - * @param value the char value to be written. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, - * or if the layout alignment is greater than its size. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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 at the given index, scaled by the 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). Might be negative. - * The final address of this read operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}. - * @return a short value read from this address. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, - * or if the layout alignment is greater than its size. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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 into this address at the given index, scaled by the 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). Might be negative. - * The final address of this write operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}. - * @param value the short value to be written. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, - * or if the layout alignment is greater than its size. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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 at the given index, scaled by the 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). Might be negative. - * The final address of this read operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}. - * @return an int value read from this address. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, - * or if the layout alignment is greater than its size. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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 into this address at the given index, scaled by the 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). Might be negative. - * The final address of this write operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}. - * @param value the int value to be written. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, - * or if the layout alignment is greater than its size. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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 at the given index, scaled by the 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). Might be negative. - * The final address of this read operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}. - * @return a float value read from this address. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, - * or if the layout alignment is greater than its size. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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 into this address at the given index, scaled by the 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). Might be negative. - * The final address of this write operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}. - * @param value the float value to be written. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, - * or if the layout alignment is greater than its size. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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 at the given index, scaled by the 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). Might be negative. - * The final address of this read operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}. - * @return a long value read from this address. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, - * or if the layout alignment is greater than its size. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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 into this address at the given index, scaled by the 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). Might be negative. - * The final address of this write operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}. - * @param value the long value to be written. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, - * or if the layout alignment is greater than its size. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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 at the given index, scaled by the 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). Might be negative. - * The final address of this read operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}. - * @return a double value read from this address. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, - * or if the layout alignment is greater than its size. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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 into this address at the given index, scaled by the 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). Might be negative. - * The final address of this write operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}. - * @param value the double value to be written. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, - * or if the layout alignment is greater than its size. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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 at the given index, scaled by the 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). Might be negative. - * The final address of this read operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}. - * @return an address value read from this address. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, - * or if the layout alignment is greater than its size. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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 into this address at the given index, scaled by the 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). Might be negative. - * The final address of this write operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}. - * @param value the address value to be written. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, - * or if the layout alignment is greater than its size. - * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option - * {@code --enable-native-access} is specified, but 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/java.base/share/classes/java/lang/foreign/MemoryLayout.java b/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java index fe953d2aa3c4a..8d56202a04af4 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java @@ -35,15 +35,20 @@ import java.util.Set; import java.util.function.Function; import java.util.function.Supplier; -import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.internal.foreign.LayoutPath; import jdk.internal.foreign.LayoutPath.PathElementImpl.PathKind; import jdk.internal.foreign.Utils; +import jdk.internal.foreign.layout.MemoryLayoutUtil; +import jdk.internal.foreign.layout.PaddingLayoutImpl; +import jdk.internal.foreign.layout.SequenceLayoutImpl; +import jdk.internal.foreign.layout.StructLayoutImpl; +import jdk.internal.foreign.layout.UnionLayoutImpl; +import jdk.internal.foreign.layout.ValueLayouts; import jdk.internal.javac.PreviewFeature; /** - * A memory layout can be used to describe the contents of a memory segment. + * A memory layout describes 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)}). @@ -162,10 +167,11 @@ * @implSpec * Implementations of this interface are immutable, thread-safe and value-based. * + * @sealedGraph * @since 19 */ @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) -public sealed interface MemoryLayout permits AbstractLayout, SequenceLayout, GroupLayout, PaddingLayout, ValueLayout { +public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, PaddingLayout, ValueLayout { /** * {@return the layout size, in bits} @@ -185,7 +191,7 @@ public sealed interface MemoryLayout permits AbstractLayout, SequenceLayout, Gro Optional name(); /** - * Returns a memory layout with the same size and alignment constraints as this layout, + * Returns a memory layout of the same type with the same size and alignment constraint as this layout, * but with the specified name. * * @param name the layout name. @@ -235,11 +241,11 @@ default long byteAlignment() { } /** - * Returns a memory layout with the same size and name as this layout, - * but with the specified alignment constraints (in bits). + * Returns a memory layout of the same type with the same size and name as this layout, + * but with the specified alignment constraint (in bits). * * @param bitAlignment the layout alignment constraint, expressed in bits. - * @return a memory layout with the given alignment constraints. + * @return a memory layout with the given alignment constraint. * @throws IllegalArgumentException if {@code bitAlignment} is not a power of two, or if it's less than 8. */ MemoryLayout withBitAlignment(long bitAlignment); @@ -307,7 +313,7 @@ default MethodHandle bitOffsetHandle(PathElement... elements) { * in {@code elements} is {@code null}. */ default long byteOffset(PathElement... elements) { - return Utils.bitsToBytesOrThrow(bitOffset(elements), Utils.bitsToBytesThrowOffset); + return Utils.bitsToBytesOrThrow(bitOffset(elements), Utils.BITS_TO_BYTES_THROW_OFFSET); } /** @@ -343,23 +349,24 @@ default long byteOffset(PathElement... elements) { */ default MethodHandle byteOffsetHandle(PathElement... elements) { MethodHandle mh = bitOffsetHandle(elements); - mh = MethodHandles.filterReturnValue(mh, Utils.MH_bitsToBytesOrThrowForOffset); + mh = MethodHandles.filterReturnValue(mh, Utils.MH_BITS_TO_BYTES_OR_THROW_FOR_OFFSET); return mh; } /** - * Creates an access var handle that can be used to dereference memory at the layout selected by the given layout path, + * Creates a var handle that can be used to access a memory segment at the layout selected by the given layout path, * where the path is considered rooted in this layout. *

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

{@code
-     * address = base + offset
+     * address = base(segment) + offset
      * }
* - * where {@code base} denotes the base address expressed by the {@link MemorySegment} access coordinate - * (see {@link MemorySegment#address()} and {@link MemoryAddress#toRawLongValue()}) and {@code offset} - * can be expressed in the following form: + * Where {@code base(segment)} denotes a function that returns the physical base address of the accessed + * memory segment. For native segments, this function just returns the native segment's + * {@linkplain MemorySegment#address() address}. For heap segments, this function is more complex, as the address + * of heap segments is virtualized. The {@code offset} coordinate can be expressed in the following form: * *
{@code
      * offset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
@@ -378,8 +385,8 @@ default MethodHandle byteOffsetHandle(PathElement... elements) {
      * features certain access mode restrictions, which are common to all memory segment view handles.
      *
      * @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.
+     * @return a var handle which can be used to access a memory segment 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 constraint.
      * @throws IllegalArgumentException if the layout path in {@code elements} does not select a value layout (see {@link ValueLayout}).
      * @see MethodHandles#memorySegmentViewVarHandle(ValueLayout)
      */
@@ -458,11 +465,6 @@ private static  Z computePathOp(LayoutPath path, Function fina
         return finalizer.apply(path);
     }
 
-    /**
-     * {@return true, if this layout is a padding layout}
-     */
-    boolean isPadding();
-
     /**
      * An element in a layout path. There
      * are two kinds of path elements: group path elements and sequence path elements. Group
@@ -581,15 +583,15 @@ static PathElement sequenceElement() {
     /**
      * Compares the specified object with this layout for equality. Returns {@code true} if and only if the specified
      * object is also a layout, and it is equal to this layout. Two layouts are considered equal if they are of
-     * the same kind, have the same size, name and alignment constraints. Furthermore, depending on the layout kind, additional
+     * the same kind, have the same size, name and alignment constraint. Furthermore, depending on the layout kind, additional
      * conditions must be satisfied:
      * 
    *
  • two value layouts are considered equal if they have the same {@linkplain ValueLayout#order() order}, * and {@linkplain ValueLayout#carrier() carrier}
  • *
  • two sequence layouts are considered equal if they have the same element count (see {@link SequenceLayout#elementCount()}), and * if their element layouts (see {@link SequenceLayout#elementLayout()}) are also equal
  • - *
  • two group layouts are considered equal if they are of the same kind (see {@link GroupLayout#isStruct()}, - * {@link GroupLayout#isUnion()}) and if their member layouts (see {@link GroupLayout#memberLayouts()}) are also equal
  • + *
  • two group layouts are considered equal if they are of the same type (see {@link StructLayout}, + * {@link UnionLayout}) and if their member layouts (see {@link GroupLayout#memberLayouts()}) are also equal
  • *
* * @param other the object to be compared for equality with this layout. @@ -615,9 +617,9 @@ static PathElement sequenceElement() { * @return the new selector layout. * @throws IllegalArgumentException if {@code size <= 0}. */ - static MemoryLayout paddingLayout(long size) { - AbstractLayout.checkSize(size); - return new PaddingLayout(size); + static PaddingLayout paddingLayout(long size) { + MemoryLayoutUtil.checkSize(size); + return PaddingLayoutImpl.of(size); } /** @@ -632,7 +634,7 @@ static MemoryLayout paddingLayout(long size) { *
  • {@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}
  • + *
  • {@link ValueLayout.OfAddress}, for {@code MemorySegment.class}
  • * * @param carrier the value layout carrier. * @param order the value layout's byte order. @@ -643,54 +645,58 @@ static ValueLayout valueLayout(Class carrier, ByteOrder order) { Objects.requireNonNull(carrier); Objects.requireNonNull(order); if (carrier == boolean.class) { - return new ValueLayout.OfBoolean(order); + return ValueLayouts.OfBooleanImpl.of(order); } else if (carrier == char.class) { - return new ValueLayout.OfChar(order); + return ValueLayouts.OfCharImpl.of(order); } else if (carrier == byte.class) { - return new ValueLayout.OfByte(order); + return ValueLayouts.OfByteImpl.of(order); } else if (carrier == short.class) { - return new ValueLayout.OfShort(order); + return ValueLayouts.OfShortImpl.of(order); } else if (carrier == int.class) { - return new ValueLayout.OfInt(order); + return ValueLayouts.OfIntImpl.of(order); } else if (carrier == float.class) { - return new ValueLayout.OfFloat(order); + return ValueLayouts.OfFloatImpl.of(order); } else if (carrier == long.class) { - return new ValueLayout.OfLong(order); + return ValueLayouts.OfLongImpl.of(order); } else if (carrier == double.class) { - return new ValueLayout.OfDouble(order); - } else if (carrier == MemoryAddress.class) { - return new ValueLayout.OfAddress(order); + return ValueLayouts.OfDoubleImpl.of(order); + } else if (carrier == MemorySegment.class) { + return ValueLayouts.OfAddressImpl.of(order); } else { throw new IllegalArgumentException("Unsupported carrier: " + carrier.getName()); } } /** - * Creates a sequence layout with the given element layout and element count. If the element count has the - * special value {@code -1}, the element count is inferred to be the biggest possible count such that - * the sequence layout size does not overflow, using the following formula: - * - *
    {@code
    -     * inferredElementCount = Long.MAX_VALUE / elementLayout.bitSize();
    -     * }
    + * Creates a sequence layout with the given element layout and element count. * - * @param elementCount the sequence element count; if set to {@code -1}, the sequence element count is inferred. + * @param elementCount the sequence element count. * @param elementLayout the sequence element layout. * @return the new sequence layout with the given element layout and size. - * @throws IllegalArgumentException if {@code elementCount < -1}. - * @throws IllegalArgumentException if {@code elementCount != -1} and the computation {@code elementCount * elementLayout.bitSize()} overflows. + * @throws IllegalArgumentException if {@code elementCount } is negative. */ static SequenceLayout sequenceLayout(long elementCount, MemoryLayout elementLayout) { - if (elementCount == -1) { - // inferred element count - long inferredElementCount = Long.MAX_VALUE / elementLayout.bitSize(); - return new SequenceLayout(inferredElementCount, elementLayout); - } else { - // explicit element count - AbstractLayout.checkSize(elementCount, true); - return wrapOverflow(() -> - new SequenceLayout(elementCount, Objects.requireNonNull(elementLayout))); - } + MemoryLayoutUtil.checkSize(elementCount, true); + Objects.requireNonNull(elementLayout); + return wrapOverflow(() -> + SequenceLayoutImpl.of(elementCount, elementLayout)); + } + + /** + * Creates a sequence layout with the given element layout and the maximum element + * count such that it does not overflow a {@code long}. + * + * This is equivalent to the following code: + * {@snippet lang = java: + * sequenceLayout(Long.MAX_VALUE / elementLayout.bitSize(), elementLayout); + * } + * + * @param elementLayout the sequence element layout. + * @return a new sequence layout with the given element layout and maximum element count. + */ + static SequenceLayout sequenceLayout(MemoryLayout elementLayout) { + Objects.requireNonNull(elementLayout); + return sequenceLayout(Long.MAX_VALUE / elementLayout.bitSize(), elementLayout); } /** @@ -701,13 +707,12 @@ static SequenceLayout sequenceLayout(long elementCount, MemoryLayout elementLayo * @throws IllegalArgumentException if the sum of the {@linkplain #bitSize() bit sizes} of the member layouts * overflows. */ - static GroupLayout structLayout(MemoryLayout... elements) { + static StructLayout structLayout(MemoryLayout... elements) { Objects.requireNonNull(elements); return wrapOverflow(() -> - new GroupLayout(GroupLayout.Kind.STRUCT, - Stream.of(elements) - .map(Objects::requireNonNull) - .collect(Collectors.toList()))); + StructLayoutImpl.of(Stream.of(elements) + .map(Objects::requireNonNull) + .toList())); } /** @@ -716,12 +721,11 @@ static GroupLayout structLayout(MemoryLayout... elements) { * @param elements The member layouts of the union layout. * @return a union layout with the given member layouts. */ - static GroupLayout unionLayout(MemoryLayout... elements) { + static UnionLayout unionLayout(MemoryLayout... elements) { Objects.requireNonNull(elements); - return new GroupLayout(GroupLayout.Kind.UNION, - Stream.of(elements) - .map(Objects::requireNonNull) - .collect(Collectors.toList())); + return UnionLayoutImpl.of(Stream.of(elements) + .map(Objects::requireNonNull) + .toList()); } private static L wrapOverflow(Supplier layoutSupplier) { diff --git a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java index 70a52ba37d774..76c2de64242cb 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java +++ b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java @@ -27,13 +27,15 @@ package java.lang.foreign; import java.io.UncheckedIOException; -import java.lang.reflect.Array; import java.lang.invoke.MethodHandles; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.nio.CharBuffer; import java.nio.channels.FileChannel; +import java.nio.channels.FileChannel.*; import java.nio.charset.StandardCharsets; +import java.util.Arrays; import java.util.Objects; import java.util.Optional; import java.util.Spliterator; @@ -43,75 +45,91 @@ import jdk.internal.foreign.NativeMemorySegmentImpl; import jdk.internal.foreign.Utils; import jdk.internal.foreign.abi.SharedUtils; +import jdk.internal.foreign.layout.ValueLayouts; import jdk.internal.javac.PreviewFeature; import jdk.internal.misc.ScopedMemoryAccess; -import jdk.internal.misc.Unsafe; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; import jdk.internal.vm.annotation.ForceInline; /** - * A memory segment models a contiguous region of memory. A memory segment is associated with both spatial - * and temporal bounds (e.g. a {@link MemorySession}). Spatial bounds ensure that memory access operations on a memory segment cannot affect a memory location - * which falls outside the boundaries of the memory segment being accessed. Temporal bounds ensure that memory access - * operations on a segment cannot occur after the memory session associated with a memory segment has been closed (see {@link MemorySession#close()}). - * - * There are many kinds of memory segments: + * A memory segment provides access to a contiguous region of memory. + *

    + * There are two kinds of memory segments: *

      - *
    • {@linkplain MemorySegment#allocateNative(long, long, MemorySession) native memory segments}, backed by off-heap memory;
    • - *
    • {@linkplain FileChannel#map(FileChannel.MapMode, long, long, MemorySession) mapped memory segments}, obtained by mapping - * a file into main memory ({@code mmap}); the contents of a mapped memory segments can be {@linkplain #force() persisted} and - * {@linkplain #load() loaded} to and from the underlying memory-mapped file;
    • - *
    • {@linkplain MemorySegment#ofArray(int[]) array segments}, wrapping an existing, heap-allocated Java array; and
    • - *
    • {@linkplain MemorySegment#ofBuffer(Buffer) buffer segments}, wrapping an existing {@link Buffer} instance; - * buffer memory segments might be backed by either off-heap memory or on-heap memory, depending on the characteristics of the - * wrapped buffer instance. For instance, a buffer memory segment obtained from a byte buffer created with the - * {@link ByteBuffer#allocateDirect(int)} method will be backed by off-heap memory.
    • + *
    • A heap segment is backed by, and provides access to, a region of memory inside the Java heap (an "on-heap" region).
    • + *
    • A native segment is backed by, and provides access to, a region of memory outside the Java heap (an "off-heap" region).
    • *
    + * Heap segments can be obtained by calling one of the {@link MemorySegment#ofArray(int[])} factory methods. + * These methods return a memory segment backed by the on-heap region that holds the specified Java array. + *

    + * Native segments can be obtained by calling one of the {@link MemorySegment#allocateNative(long, long, SegmentScope)} + * factory methods, which return a memory segment backed by a newly allocated off-heap region with the given size + * and aligned to the given alignment constraint. Alternatively, native segments can be obtained by + * {@link FileChannel#map(MapMode, long, long, SegmentScope) mapping} a file into a new off-heap region + * (in some systems, this operation is sometimes referred to as {@code mmap}). + * Segments obtained in this way are called mapped segments, and their contents can be {@linkplain #force() persisted} and + * {@linkplain #load() loaded} to and from the underlying memory-mapped file. + *

    + * Both kinds of segments are read and written using the same methods, known as access operations. + * An access operation on a memory segment always and only provides access to the region for which the segment was obtained. * - *

    Lifecycle and confinement

    + *

    Characteristics of memory segments

    * - * Memory segments are associated with a {@linkplain MemorySegment#session() memory session}. As for all resources associated - * with a memory session, a segment cannot be accessed after its underlying session has been closed. For instance, - * the following code will result in an exception: - * {@snippet lang=java : - * MemorySegment segment = null; - * try (MemorySession session = MemorySession.openConfined()) { - * segment = MemorySegment.allocateNative(8, session); - * } - * 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 memory - * session; that is, if the segment is associated with a shared session, it can be accessed by multiple threads; - * if it is associated with a confined session, it can only be accessed by the thread which owns the memory session. + * Every memory segment has an {@linkplain #address() address}, expressed as a {@code long} value. + * The nature of a segment's address depends on the kind of the segment: + *
      + *
    • The address of a heap segment is not a physical address, but rather an offset within the region of memory + * which backs the segment. The region is inside the Java heap, so garbage collection might cause the region to be + * relocated in physical memory over time, but this is not exposed to clients of the {@code MemorySegment} API who + * see a stable virtualized address for a heap segment backed by the region. + * A heap segment obtained from one of the {@link #ofArray(int[])} factory methods has an address of zero.
    • + *
    • The address of a native segment (including mapped segments) denotes the physical address of the region of + * memory which backs the segment.
    • + *
    + *

    + * Every memory segment has a {@linkplain #byteSize() size}. The size of a heap segment is derived from the Java array + * from which it is obtained. This size is predictable across Java runtimes. + * The size of a native segment is either passed explicitly + * (as in {@link MemorySegment#allocateNative(long, SegmentScope)}) or derived from a {@link MemoryLayout} + * (as in {@link MemorySegment#allocateNative(MemoryLayout, SegmentScope)}). The size of a memory segment is typically + * a positive number but may be zero, but never negative. + *

    + * The address and size of a memory segment jointly ensure that access operations on the segment cannot fall + * outside the boundaries of the region of memory which backs the segment. + * That is, a memory segment has spatial bounds. + *

    + * Every memory segment is associated with a {@linkplain SegmentScope scope}. This ensures that access operations + * on a memory segment cannot occur when the region of memory which backs the memory segment is no longer available + * (e.g., after the scope associated with the accessed memory segment is no longer {@linkplain SegmentScope#isAlive() alive}). + * That is, a memory segment has temporal bounds. *

    - * Heap segments are always associated with the {@linkplain MemorySession#global() global} memory session. - * This session cannot be closed, and segments associated with it can be considered as always alive. - * Buffer segments are typically associated with the global memory session, with one exception: buffer segments created - * from byte buffer instances obtained calling the {@link #asByteBuffer()} method on a memory segment {@code S} - * are associated with the same memory session as {@code S}. + * Finally, access operations on a memory segment are subject to the thread-confinement checks enforced by the associated + * scope; that is, if the segment is associated with the {@linkplain SegmentScope#global() global scope} or an {@linkplain SegmentScope#auto() automatic scope}, + * it can be accessed by multiple threads. If the segment is associated with an arena scope, then it can only be + * accessed compatibly with the arena confinement characteristics. * - *

    Dereferencing memory segments

    + *

    Accessing memory segments

    * - * 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 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. + * A memory segment can be read or written using various access operations provided in this class (e.g. {@link #get(ValueLayout.OfInt, long)}). + * Each access operation takes a {@linkplain ValueLayout value layout}, which specifies the size and shape of the value, + * and an offset, expressed in bytes. * For instance, to read an int from a segment, using {@linkplain ByteOrder#nativeOrder() default endianness}, the following code can be used: * {@snippet lang=java : * MemorySegment segment = ... * int value = segment.get(ValueLayout.JAVA_INT, 0); * } * - * If the value to be read is stored in memory using {@linkplain ByteOrder#BIG_ENDIAN big-endian} encoding, the dereference operation + * If the value to be read is stored in memory using {@linkplain ByteOrder#BIG_ENDIAN big-endian} encoding, the access operation * can be expressed as follows: * {@snippet lang=java : * MemorySegment segment = ... * int value = segment.get(ValueLayout.JAVA_INT.withOrder(BIG_ENDIAN), 0); * } * - * For more complex dereference operations (e.g. structured memory access), clients can obtain a - * {@linkplain MethodHandles#memorySegmentViewVarHandle(ValueLayout) memory segment view var handle}, - * that is, a var handle that accepts a segment and a {@code long} offset. More complex access var handles + * For more complex access operations (e.g. structured memory access), clients can obtain a + * {@linkplain MethodHandles#memorySegmentViewVarHandle(ValueLayout) var handle} + * that accepts a segment and a {@code long} offset. More complex var handles * can be obtained by adapting a segment var handle view using the var handle combinator functions defined in the * {@link java.lang.invoke.MethodHandles} class: * @@ -126,7 +144,7 @@ * intHandle.get(segment, 3L); // get int element at offset 3 * 4 = 12 * } * - * Alternatively, complex access var handles can can be obtained + * Alternatively, complex var handles can can be obtained * from {@linkplain MemoryLayout#varHandle(MemoryLayout.PathElement...) memory layouts} * by providing a so called layout path: * @@ -138,62 +156,138 @@ * *

    Slicing memory segments

    * - * Memory segments support slicing. A memory segment can be used to {@linkplain MemorySegment#asSlice(long, long) obtain} - * other segments backed by the same underlying memory region, but with stricter spatial bounds than the ones - * of the original segment: + * Memory segments support {@linkplain MemorySegment#asSlice(long, long) slicing}. Slicing a memory segment + * returns a new memory segment that is backed by the same region of memory as the original. The address of the sliced + * segment is derived from the address of the original segment, by adding an offset (expressed in bytes). The size of + * the sliced segment is either derived implicitly (by subtracting the specified offset from the size of the original segment), + * or provided explicitly. In other words, a sliced segment has stricter spatial bounds than those of the original segment: * {@snippet lang=java : - * MemorySession session = ... - * MemorySegment segment = MemorySegment.allocateNative(100, session); + * Arena arena = ... + * MemorySegment segment = arena.allocate(100); * MemorySegment slice = segment.asSlice(50, 10); * slice.get(ValueLayout.JAVA_INT, 20); // Out of bounds! - * session.close(); + * arena.close(); * slice.get(ValueLayout.JAVA_INT, 0); // Already closed! * } * The above code creates a native segment that is 100 bytes long; then, it creates a slice that starts at offset 50 - * of {@code segment}, and is 10 bytes long. As a result, attempting to read an int value at offset 20 of the - * {@code slice} segment will result in an exception. The {@linkplain MemorySession temporal bounds} of the original segment - * are inherited by its slices; that is, when the memory session associated with {@code segment} is closed, {@code slice} - * will also be become inaccessible. + * of {@code segment}, and is 10 bytes long. That is, the address of the {@code slice} is {@code segment.address() + 50}, + * and its size is 10. As a result, attempting to read an int value at offset 20 of the + * {@code slice} segment will result in an exception. The {@linkplain SegmentScope temporal bounds} of the original segment + * is inherited by its slices; that is, when the scope associated with {@code segment} is no longer {@linkplain SegmentScope#isAlive() alive}, + * {@code slice} will also be become inaccessible. *

    * A client might obtain a {@link Stream} from a segment, which can then be used to slice the segment (according to a given * element layout) and even allow multiple threads to work in parallel on disjoint segment slices - * (to do this, the segment has to be associated with a shared memory session). The following code can be used to sum all int - * values in a memory segment in parallel: + * (to do this, the segment has to be associated with a scope that allows {@linkplain SegmentScope#isAccessibleBy(Thread) access} + * from multiple threads). The following code can be used to sum all int values in a memory segment in parallel: * - * {@snippet lang=java : - * try (MemorySession session = MemorySession.openShared()) { + * {@snippet lang = java: + * try (Arena arena = Arena.openShared()) { * SequenceLayout SEQUENCE_LAYOUT = MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_INT); - * MemorySegment segment = MemorySegment.allocateNative(SEQUENCE_LAYOUT, session); + * MemorySegment segment = arena.allocate(SEQUENCE_LAYOUT); * int sum = segment.elements(ValueLayout.JAVA_INT).parallel() * .mapToInt(s -> s.get(ValueLayout.JAVA_INT, 0)) * .sum(); * } - * } + *} * *

    Alignment

    * - * When dereferencing a memory segment using a layout, the runtime must check that the segment address being dereferenced - * matches the layout's {@linkplain MemoryLayout#byteAlignment() alignment constraints}. If the segment being - * dereferenced is a native segment, then it has a concrete {@linkplain #address() base address}, which can - * be used to perform the alignment check. The pseudo-function below demonstrates this: + * Access operations on a memory segment are constrained not only by the spatial and temporal bounds of the segment, + * but also by the alignment constraint of the value layout specified to the operation. An access operation can + * access only those offsets in the segment that denote addresses in physical memory which are aligned according + * to the layout. An address in physical memory is aligned according to a layout if the address is an integer + * multiple of the layout's alignment constraint. For example, the address 1000 is aligned according to an 8-byte alignment + * constraint (because 1000 is an integer multiple of 8), and to a 4-byte alignment constraint, and to a 2-byte alignment + * constraint; in contrast, the address 1004 is aligned according to a 4-byte alignment constraint, and to a 2-byte alignment + * constraint, but not to an 8-byte alignment constraint. + * Access operations are required to respect alignment because it can impact the performance of access operations, and + * can also determine which access operations are available at a given physical address. For instance, + * {@linkplain java.lang.invoke.VarHandle#compareAndSet(Object...) atomic access operations} operations using + * {@link java.lang.invoke.VarHandle} are only permitted at aligned addresses. In addition, alignment + * applies to an access operation whether the segment being accessed is a native segment or a heap segment. + *

    + * If the segment being accessed is a native segment, then its {@linkplain #address() address} in physical memory can be + * combined with the offset to obtain the target address in physical memory. The pseudo-function below demonstrates this: * - * {@snippet lang=java : + * {@snippet lang = java: * boolean isAligned(MemorySegment segment, long offset, MemoryLayout layout) { - * return ((segment.address().toRawLongValue() + offset) % layout.byteAlignment()) == 0 + * return ((segment.address() + offset) % layout.byteAlignment()) == 0; * } * } * - * If, however, the segment being dereferenced is a heap segment, the above function will not work: a heap - * segment's base address is virtualized and, as such, cannot be used to construct an alignment check. Instead, - * heap segments are assumed to produce addresses which are never more aligned than the element size of the Java array from which - * they have originated from, as shown in the following table: + * For example: + *

      + *
    • A native segment with address 1000 can be accessed at offsets 0, 8, 16, 24, etc under an 8-byte alignment constraint, + * because the target addresses (1000, 1008, 1016, 1024) are 8-byte aligned. + * Access at offsets 1-7 or 9-15 or 17-23 is disallowed because the target addresses would not be 8-byte aligned.
    • + *
    • A native segment with address 1000 can be accessed at offsets 0, 4, 8, 12, etc under a 4-byte alignment constraint, + * because the target addresses (1000, 1004, 1008, 1012) are 4-byte aligned. + * Access at offsets 1-3 or 5-7 or 9-11 is disallowed because the target addresses would not be 4-byte aligned.
    • + *
    • A native segment with address 1000 can be accessed at offsets 0, 2, 4, 6, etc under a 2-byte alignment constraint, + * because the target addresses (1000, 1002, 1004, 1006) are 2-byte aligned. + * Access at offsets 1 or 3 or 5 is disallowed because the target addresses would not be 2-byte aligned.
    • + *
    • A native segment with address 1004 can be accessed at offsets 0, 4, 8, 12, etc under a 4-byte alignment constraint, + * and at offsets 0, 2, 4, 6, etc under a 2-byte alignment constraint. + * Under an 8-byte alignment constraint, it can be accessed at offsets 4, 12, 20, 28, etc.
    • + *
    • A native segment with address 1006 can be accessed at offsets 0, 2, 4, 6, etc under a 2-byte alignment constraint. + * Under a 4-byte alignment constraint, it can be accessed at offsets 2, 6, 10, 14, etc. + * Under an 8-byte alignment constraint, it can be accessed at offsets 2, 10, 18, 26, etc. + *
    • A native segment with address 1007 can be accessed at offsets 0, 1, 2, 3, etc under a 1-byte alignment constraint. + * Under a 2-byte alignment constraint, it can be accessed at offsets 1, 3, 5, 7, etc. + * Under a 4-byte alignment constraint, it can be accessed at offsets 1, 5, 9, 13, etc. + * Under an 8-byte alignment constraint, it can be accessed at offsets 1, 9, 17, 25, etc.
    • + *
    + *

    + * The alignment constraint used to access a segment is typically dictated by the shape of the data structure stored + * in the segment. For example, if the programmer wishes to store a sequence of 8-byte values in a native segment, then + * the segment should be allocated by specifying a 8-byte alignment constraint, either via {@link #allocateNative(long, long, SegmentScope)} + * or {@link #allocateNative(MemoryLayout, SegmentScope)}. These factories ensure that the off-heap region of memory backing + * the returned segment has a starting address that is 8-byte aligned. Subsequently, the programmer can access the + * segment at the offsets of interest -- 0, 8, 16, 24, etc -- in the knowledge that every such access is aligned. + *

    + * If the segment being accessed is a heap segment, then determining whether access is aligned is more complex. + * The address of the segment in physical memory is not known, and is not even fixed (it may change when the segment + * is relocated during garbage collection). This means that the address cannot be combined with the specified offset to + * determine a target address in physical memory. Since the alignment constraint always refers to alignment of + * addresses in physical memory, it is not possible in principle to determine if any offset in a heap segment is aligned. + * For example, suppose the programmer chooses a 8-byte alignment constraint and tries + * to access offset 16 in a heap segment. If the heap segment's address 0 corresponds to physical address 1000, + * then the target address (1016) would be aligned, but if address 0 corresponds to physical address 1004, + * then the target address (1020) would not be aligned. It is undesirable to allow access to target addresses that are + * aligned according to the programmer's chosen alignment constraint, but might not be predictably aligned in physical memory + * (e.g. because of platform considerations and/or garbage collection behavior). + *

    + * In practice, the Java runtime lays out arrays in memory so that each n-byte element occurs at an n-byte + * aligned physical address. The runtime preserves this invariant even if the array is relocated during garbage + * collection. Access operations rely on this invariant to determine if the specified offset in a heap segment refers + * to an aligned address in physical memory. For example: + *

      + *
    • The starting physical address of a {@code long[]} array will be 8-byte aligned (e.g. 1000), so that successive long elements + * occur at 8-byte aligned addresses (e.g., 1000, 1008, 1016, 1024, etc.) A heap segment backed by a {@code long[]} array + * can be accessed at offsets 0, 8, 16, 24, etc under an 8-byte alignment constraint. In addition, the segment can be + * accessed at offsets 0, 4, 8, 12, etc under a 4-byte alignment constraint, because the target addresses + * (1000, 1004, 1008, 1012) are 4-byte aligned. And, the segment can be accessed at offsets 0, 2, 4, 6, etc under a + * 2-byte alignment constraint, because the target addresses (e.g. 1000, 1002, 1004, 1006) are 2-byte aligned.
    • + *
    • The starting physical address of a {@code short[]} array will be 2-byte aligned (e.g. 1006) so that successive + * short elements occur at 2-byte aligned addresses (e.g. 1006, 1008, 1010, 1012, etc). A heap segment backed by a + * {@code short[]} array can be accessed at offsets 0, 2, 4, 6, etc under a 2-byte alignment constraint. The segment cannot + * be accessed at any offset under a 4-byte alignment constraint, because there is no guarantee that the target + * address would be 4-byte aligned, e.g., offset 0 would correspond to physical address 1006 while offset 1 would correspond + * to physical address 1007. Similarly, the segment cannot be accessed at any offset under an 8-byte alignment constraint, + * because because there is no guarantee that the target address would be 8-byte aligned, e.g., offset 2 would correspond + * to physical address 1008 but offset 4 would correspond to physical address 1010.
    • + *
    + *

    + * In other words, heap segments feature a maximum alignment which is derived from the size of the elements of + * the Java array backing the segment, as shown in the following table: * *

    - * + * * * - * - * + * + * * * * @@ -216,22 +310,86 @@ * *
    Array type of an array backing a segment and its address alignmentMaximum alignment of heap segments
    Array typeAlignmentArray type (of backing region)Maximum supported alignment (in bytes)
    * - * Note that the above definition is conservative: it might be possible, for instance, that a heap segment - * constructed from a {@code byte[]} might have a subset of addresses {@code S} which happen to be 8-byte aligned. But determining - * which segment addresses belong to {@code S} requires reasoning about details which are ultimately implementation-dependent. + * Heap segments can only be accessed using a layout whose alignment is smaller or equal to the + * maximum alignment associated with the heap segment. Attempting to access a heap segment using a layout + * whose alignment is greater than the maximum alignment associated with the heap segment will fail, + * as demonstrated in the following example: + * + * {@snippet lang=java : + * MemorySegment byteSegment = MemorySegment.ofArray(new byte[10]); + * byteSegment.get(ValueLayout.JAVA_INT, 0); // fails: layout alignment is 4, segment max alignment is 1 + * } + * + * In such circumstances, clients have two options. They can use a heap segment backed by a different array + * type (e.g. {@code long[]}), capable of supporting greater maximum alignment: + * + * {@snippet lang=java : + * MemorySegment longSegment = MemorySegment.ofArray(new long[10]); + * longSegment.get(ValueLayout.JAVA_INT, 0); // ok: layout alignment is 4, segment max alignment is 8 + * } + * + * Alternatively, they can invoke the access operation with an unaligned layout. + * All unaligned layout constants (e.g. {@link ValueLayout#JAVA_INT_UNALIGNED}) have their alignment constraint set to 1: + * {@snippet lang=java : + * MemorySegment byteSegment = MemorySegment.ofArray(new byte[10]); + * byteSegment.get(ValueLayout.JAVA_INT_UNALIGNED, 0); // ok: layout alignment is 1, segment max alignment is 1 + * } + * + *

    Zero-length memory segments

    + * + * When interacting with foreign functions, it is common for those functions + * to allocate a region of memory and return a pointer to that region. Modeling the region of memory with a memory segment + * is challenging because the Java runtime has no insight into the size of the region. Only the address of the start of + * the region, stored in the pointer, is available. For example, a C function with return type {@code char*} might return + * a pointer to a region containing a single {@code char} value, or to a region containing an array of {@code char} values, + * where the size of the array might be provided in a separate parameter. The size of the array is not readily apparent + * to the code calling the foreign function and hoping to use its result. + *

    + * The {@link Linker} represents a pointer returned from a foreign function with a zero-length memory segment. + * The address of the segment is the address stored in the pointer. The size of the segment is zero. Similarly, when a + * client reads an address from a memory segment, a zero-length memory segment is returned. + *

    + * Since a zero-length segment features trivial spatial bounds, any attempt to access these segments will fail with + * {@link IndexOutOfBoundsException}. This is a crucial safety feature: as these segments are associated with a region + * of memory whose size is not known, any access operations involving these segments cannot be validated. + * In effect, a zero-length memory segment wraps an address, and it cannot be used without explicit intent. + *

    + * Zero-length memory segments obtained when interacting with foreign functions are associated with the + * {@link SegmentScope#global() global scope}. This is because the Java runtime, in addition to having no insight + * into the size of the region of memory backing a pointer returned from a foreign function, also has no insight + * into the lifetime intended for said region of memory by the foreign function that allocated it. The global scope + * ensures that the obtained segment can be passed, opaquely, to other pointer-accepting foreign functions. + *

    + * To access native zero-length memory segments, clients have two options, both of which are unsafe. Clients + * can {@linkplain java.lang.foreign.MemorySegment#ofAddress(long, long, SegmentScope) obtain} + * a new native segment, with new spatial and temporal bounds, as follows: + * + * {@snippet lang = java: + * SegmentScope scope = ... // obtains a scope + * MemorySegment foreign = someSegment.get(ValueLayout.ADDRESS, 0); // wrap address into segment (size = 0) + * MemorySegment segment = MemorySegment.ofAddress(foreign.address(), 4, scope); // create new segment (size = 4) + * int x = segment.get(ValueLayout.JAVA_INT, 0); //ok + *} + * + * Alternatively, clients can obtain an {@linkplain java.lang.foreign.ValueLayout.OfAddress#asUnbounded() unbounded} + * address value layout. When an access operation, or a function descriptor that is passed to a downcall method handle, + * uses an unbounded address value layouts, the runtime will wrap any corresponding raw addresses with native segments + * with maximal size (i.e. {@linkplain java.lang.Long#MAX_VALUE}). As such, these segments can be accessed directly, as follows: + * + * {@snippet lang = java: + * MemorySegment foreign = someSegment.get(ValueLayout.ADDRESS.asUnbounded(), 0); // wrap address into segment (size = Long.MAX_VALUE) + * int x = foreign.get(ValueLayout.JAVA_INT, 0); //ok + *} * - *

    Restricted memory 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 {@linkplain #ofAddress(MemoryAddress, long, MemorySession) obtain} - * a native segment unsafely from a give memory address, by providing the segment size, as well as the segment {@linkplain MemorySession session}. - * 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. + * Both {@link #ofAddress(long, long, SegmentScope)} and {@link ValueLayout.OfAddress#asUnbounded()} are + * restricted methods, and should be used with caution: + * for instance, sizing a segment incorrectly could result in a VM crash when attempting to access the memory segment. *

    - * Clients requiring sophisticated, low-level control over mapped memory segments, might consider writing - * custom mapped memory segment factories; using {@link Linker}, 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 MemorySegment#ofAddress(MemoryAddress, long, MemorySession)}. + * Which approach is taken largely depends on the information that a client has available when obtaining a memory segment + * wrapping a native pointer. For instance, if such pointer points to a C struct, the client might prefer to resize the + * segment unsafely, to match the size of the struct (so that out-of-bounds access will be detected by the API). + * In other instances, however, there will be no, or little information as to what spatial and/or temporal bounds should + * be associated with a given native pointer. In these cases using an unbounded address layout might be preferable. * * @implSpec * Implementations of this interface are immutable, thread-safe and value-based. @@ -239,18 +397,17 @@ * @since 19 */ @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) -public sealed interface MemorySegment extends Addressable permits AbstractMemorySegmentImpl { +public sealed interface MemorySegment permits AbstractMemorySegmentImpl { /** - * {@return 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 {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. + * {@return the address of this memory segment} */ - @Override - MemoryAddress address(); + long address(); + + /** + * {@return the Java array associated with this memory segment, if any} + */ + Optional array(); /** * Returns a spliterator for this memory segment. The returned spliterator reports {@link Spliterator#SIZED}, @@ -261,7 +418,7 @@ public sealed interface MemorySegment extends Addressable permits AbstractMemory * if the supplied layout has size N, then calling {@link Spliterator#trySplit()} will result in a spliterator serving * approximately {@code S/N} elements (depending on whether N is even or not), where {@code S} is the size of * this segment. As such, splitting is possible as long as {@code S/N >= 2}. The spliterator returns segments that - * are associated with the same memory session as this segment. + * are associated with the same scope as that associated with this segment. *

    * The returned spliterator effectively allows to slice this segment into disjoint {@linkplain #asSlice(long, long) slices}, * which can then be processed in parallel by multiple threads. @@ -270,7 +427,7 @@ public sealed interface MemorySegment extends Addressable permits AbstractMemory * @return the element spliterator for this segment * @throws IllegalArgumentException if the {@code elementLayout} size is zero, or the segment size modulo the * {@code elementLayout} size is greater than zero, if this segment is - * incompatible with the alignment constraints in the provided layout, + * incompatible with the alignment constraint in the provided layout, * or if the {@code elementLayout} alignment is greater than its size. */ Spliterator spliterator(MemoryLayout elementLayout); @@ -286,15 +443,15 @@ public sealed interface MemorySegment extends Addressable permits AbstractMemory * @return a sequential {@code Stream} over disjoint slices in this segment. * @throws IllegalArgumentException if the {@code elementLayout} size is zero, or the segment size modulo the * {@code elementLayout} size is greater than zero, if this segment is - * incompatible with the alignment constraints in the provided layout, + * incompatible with the alignment constraint in the provided layout, * or if the {@code elementLayout} alignment is greater than its size. */ Stream elements(MemoryLayout elementLayout); /** - * {@return the memory session associated with this memory segment} + * {@return the scope associated with this memory segment} */ - MemorySession session(); + SegmentScope scope(); /** * {@return the size (in bytes) of this memory segment} @@ -302,12 +459,12 @@ public sealed interface MemorySegment extends Addressable permits AbstractMemory long byteSize(); /** - * Returns a slice of this memory segment, at the given offset. The returned segment's base address is the base address + * Returns a slice of this memory segment, at the given offset. The returned segment's address is the address * of this segment plus the given offset; its size is specified by the given argument. * * @see #asSlice(long) * - * @param offset The new segment base offset (relative to the current segment base address), specified in bytes. + * @param offset The new segment base offset (relative to the address of this segment), specified in bytes. * @param newSize The new segment size, specified in bytes. * @return a slice of this memory segment. * @throws IndexOutOfBoundsException if {@code offset < 0}, {@code offset > byteSize()}, {@code newSize < 0}, or {@code newSize > byteSize() - offset} @@ -315,7 +472,7 @@ public sealed interface MemorySegment extends Addressable permits AbstractMemory MemorySegment asSlice(long offset, long newSize); /** - * Returns a slice of this memory segment, at the given offset. The returned segment's base address is the base address + * Returns a slice of this memory segment, at the given offset. The returned segment's address is the address * of this segment plus the given offset; its size is computed by subtracting the specified offset from this segment size. *

    * Equivalent to the following code: @@ -325,7 +482,7 @@ public sealed interface MemorySegment extends Addressable permits AbstractMemory * * @see #asSlice(long, long) * - * @param offset The new segment base offset (relative to the current segment base address), specified in bytes. + * @param offset The new segment base offset (relative to the address of this segment), specified in bytes. * @return a slice of this memory segment. * @throws IndexOutOfBoundsException if {@code offset < 0}, or {@code offset > byteSize()}. */ @@ -348,18 +505,17 @@ default MemorySegment asSlice(long offset) { MemorySegment asReadOnly(); /** - * Returns {@code true} if this segment is a native segment. A native memory segment is - * created using the {@link #allocateNative(long, MemorySession)} (and related) factory, or a buffer segment - * derived from a {@linkplain ByteBuffer#allocateDirect(int) direct byte buffer} using the {@link #ofBuffer(Buffer)} factory, - * or if this is a {@linkplain #isMapped() mapped} segment. + * Returns {@code true} if this segment is a native segment. A native segment is + * created e.g. using the {@link #allocateNative(long, SegmentScope)} (and related) factory, or by + * {@linkplain #ofBuffer(Buffer) wrapping} a {@linkplain ByteBuffer#allocateDirect(int) direct buffer}. * @return {@code true} if this segment is native segment. */ boolean isNative(); /** - * Returns {@code true} if this segment is a mapped segment. A mapped memory segment is - * created using the {@link FileChannel#map(FileChannel.MapMode, long, long, MemorySession)} factory, or a buffer segment - * derived from a {@link java.nio.MappedByteBuffer} using the {@link #ofBuffer(Buffer)} factory. + * Returns {@code true} if this segment is a mapped segment. A mapped memory segment is created e.g. using the + * {@link FileChannel#map(FileChannel.MapMode, long, long, SegmentScope)} factory, or by + * {@linkplain #ofBuffer(Buffer) wrapping} a {@linkplain java.nio.MappedByteBuffer mapped byte buffer}. * @return {@code true} if this segment is a mapped segment. */ boolean isMapped(); @@ -370,7 +526,7 @@ default MemorySegment asSlice(long offset) { * *

    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 + * same region of memory. As such, it is not possible for a * {@linkplain #isNative() native} segment to overlap with a heap segment; in * this case, or when no overlap occurs, {@code null} is returned. * @@ -383,19 +539,22 @@ default MemorySegment asSlice(long offset) { * 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 + *

    The offset is relative to the address of this segment and can be * a negative or positive value. For instance, if both segments are native - * segments, the resulting offset can be computed as follows: + * segments, or heap segments backed by the same array, the resulting offset + * can be computed as follows: * * {@snippet lang=java : - * other.baseAddress().toRawLongValue() - segment.baseAddress().toRawLongValue() + * other.address() - segment.address() * } * - * If the segments share the same base address, {@code 0} is returned. If + * If the segments share the same address, {@code 0} is returned. If * {@code other} is a slice of this segment, the offset is always * {@code 0 <= x < this.byteSize()}. * * @param other the segment to retrieve an offset to. + * @throws UnsupportedOperationException if the two segments cannot be compared, e.g. because they are of + * a different kind, or because they are backed by different Java arrays. * @return the relative offset, in bytes, of the provided segment. */ long segmentOffset(MemorySegment other); @@ -421,10 +580,10 @@ default MemorySegment asSlice(long offset) { * * @param value the value to fill into this segment * @return this memory segment - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. * @throws UnsupportedOperationException if this segment is read-only (see {@link #isReadOnly()}). */ MemorySegment fill(byte value); @@ -440,14 +599,14 @@ default MemorySegment asSlice(long offset) { * } * @param src the source segment. * @throws IndexOutOfBoundsException if {@code src.byteSize() > this.byteSize()}. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalStateException if the {@linkplain #session() session} associated with {@code src} is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with {@code src}. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code src} is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code src.scope().isAccessibleBy(T) == false}. * @throws UnsupportedOperationException if this segment is read-only (see {@link #isReadOnly()}). * @return this segment. */ @@ -459,7 +618,7 @@ default MemorySegment copyFrom(MemorySegment src) { /** * Finds and returns the offset, in bytes, of the first mismatch between * this segment and the given other segment. The offset is relative to the - * {@linkplain #address() base address} of each segment and will be in the + * {@linkplain #address() address} of each segment and will be in the * range of 0 (inclusive) up to the {@linkplain #byteSize() size} (in bytes) of * the smaller memory segment (exclusive). *

    @@ -474,16 +633,19 @@ default MemorySegment copyFrom(MemorySegment src) { * @param other the segment to be tested for a mismatch with this segment * @return the relative offset, in bytes, of the first mismatch between this * and the given other segment, otherwise -1 if no mismatch - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalStateException if the {@linkplain #session() session} associated with {@code other} is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with {@code other}. - */ - long mismatch(MemorySegment other); + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code other} is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code other.scope().isAccessibleBy(T) == false}. + */ + default long mismatch(MemorySegment other) { + Objects.requireNonNull(other); + return MemorySegment.mismatch(this, 0, byteSize(), other, 0, other.byteSize()); + } /** * Determines whether the contents of this mapped segment is resident in physical @@ -503,10 +665,10 @@ default MemorySegment copyFrom(MemorySegment src) { * @return {@code true} if it is likely that the contents of this segment * is resident in physical memory * - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. * @throws UnsupportedOperationException if this segment is not a mapped memory segment, e.g. if * {@code isMapped() == false}. */ @@ -520,10 +682,10 @@ default MemorySegment copyFrom(MemorySegment src) { * method may cause some number of page faults and I/O operations to * occur.

    * - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. * @throws UnsupportedOperationException if this segment is not a mapped memory segment, e.g. if * {@code isMapped() == false}. */ @@ -537,10 +699,10 @@ default MemorySegment copyFrom(MemorySegment src) { * after invoking this method may cause some number of page faults and I/O operations to * occur (as this segment's contents might need to be paged back in).

    * - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. * @throws UnsupportedOperationException if this segment is not a mapped memory segment, e.g. if * {@code isMapped() == false}. */ @@ -566,10 +728,10 @@ default MemorySegment copyFrom(MemorySegment src) { * implementation-specific mapping modes. *

    * - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. * @throws UnsupportedOperationException if this segment is not a mapped memory segment, e.g. if * {@code isMapped() == false}. * @throws UncheckedIOException if there is an I/O error writing the contents of this segment to the associated storage device @@ -580,7 +742,7 @@ default MemorySegment copyFrom(MemorySegment src) { * 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 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 @@ -589,13 +751,12 @@ default MemorySegment copyFrom(MemorySegment src) { * 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 memory session associated with this segment has been closed (see {@link MemorySession#close()}), will - * throw an {@link IllegalStateException}. Similarly, accessing the returned buffer from a thread other than - * the thread {@linkplain MemorySession#ownerThread() owning} this segment's memory session will throw - * a {@link WrongThreadException}. + * after the scope associated with this segment is no longer {@linkplain SegmentScope#isAlive() alive}, will + * throw an {@link IllegalStateException}. Similarly, accessing the returned buffer from a thread {@code T} + * such that {@code scope().isAccessible(T) == false} will throw a {@link WrongThreadException}. *

    - * If this segment is associated with a confined memory session, calling read/write I/O operations on the resulting buffer - * might result in an unspecified exception being thrown. Examples of such problematic operations are + * If this segment is associated with a scope that can only be accessed from a single thread, calling read/write I/O + * operations on the resulting buffer might result in an unspecified exception being thrown. Examples of such problematic operations are * {@link java.nio.channels.AsynchronousSocketChannel#read(ByteBuffer)} and * {@link java.nio.channels.AsynchronousSocketChannel#write(ByteBuffer)}. *

    @@ -614,10 +775,10 @@ default MemorySegment copyFrom(MemorySegment src) { * @param elementLayout the source element layout. If the byte order associated with the layout is * different from the {@linkplain ByteOrder#nativeOrder native order}, a byte swap operation will be performed on each array element. * @return a new byte array whose contents are copied from this memory segment. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. * @throws IllegalStateException if this segment's contents cannot be copied into a {@code byte[]} instance, * e.g. its size is greater than {@link Integer#MAX_VALUE}. */ @@ -628,10 +789,10 @@ default MemorySegment copyFrom(MemorySegment src) { * @param elementLayout the source element layout. If the byte order associated with the layout is * different from the {@linkplain ByteOrder#nativeOrder native order}, a byte swap operation will be performed on each array element. * @return a new short array whose contents are copied from this memory segment. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. * @throws IllegalStateException if this segment's contents cannot be copied into a {@code short[]} instance, * e.g. because {@code byteSize() % 2 != 0}, or {@code byteSize() / 2 > Integer#MAX_VALUE} */ @@ -642,10 +803,10 @@ default MemorySegment copyFrom(MemorySegment src) { * @param elementLayout the source element layout. If the byte order associated with the layout is * different from the {@linkplain ByteOrder#nativeOrder native order}, a byte swap operation will be performed on each array element. * @return a new char array whose contents are copied from this memory segment. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. * @throws IllegalStateException if this segment's contents cannot be copied into a {@code char[]} instance, * e.g. because {@code byteSize() % 2 != 0}, or {@code byteSize() / 2 > Integer#MAX_VALUE}. */ @@ -656,10 +817,10 @@ default MemorySegment copyFrom(MemorySegment src) { * @param elementLayout the source element layout. If the byte order associated with the layout is * different from the {@linkplain ByteOrder#nativeOrder native order}, a byte swap operation will be performed on each array element. * @return a new int array whose contents are copied from this memory segment. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. * @throws IllegalStateException if this segment's contents cannot be copied into a {@code int[]} instance, * e.g. because {@code byteSize() % 4 != 0}, or {@code byteSize() / 4 > Integer#MAX_VALUE}. */ @@ -670,10 +831,10 @@ default MemorySegment copyFrom(MemorySegment src) { * @param elementLayout the source element layout. If the byte order associated with the layout is * different from the {@linkplain ByteOrder#nativeOrder native order}, a byte swap operation will be performed on each array element. * @return a new float array whose contents are copied from this memory segment. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. * @throws IllegalStateException if this segment's contents cannot be copied into a {@code float[]} instance, * e.g. because {@code byteSize() % 4 != 0}, or {@code byteSize() / 4 > Integer#MAX_VALUE}. */ @@ -684,10 +845,10 @@ default MemorySegment copyFrom(MemorySegment src) { * @param elementLayout the source element layout. If the byte order associated with the layout is * different from the {@linkplain ByteOrder#nativeOrder native order}, a byte swap operation will be performed on each array element. * @return a new long array whose contents are copied from this memory segment. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. * @throws IllegalStateException if this segment's contents cannot be copied into a {@code long[]} instance, * e.g. because {@code byteSize() % 8 != 0}, or {@code byteSize() / 8 > Integer#MAX_VALUE}. */ @@ -698,10 +859,10 @@ default MemorySegment copyFrom(MemorySegment src) { * @param elementLayout the source element layout. If the byte order associated with the layout is * different from the {@linkplain ByteOrder#nativeOrder native order}, a byte swap operation will be performed on each array element. * @return a new double array whose contents are copied from this memory segment. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. * @throws IllegalStateException if this segment's contents cannot be copied into a {@code double[]} instance, * e.g. because {@code byteSize() % 8 != 0}, or {@code byteSize() / 8 > Integer#MAX_VALUE}. */ @@ -714,17 +875,16 @@ default MemorySegment copyFrom(MemorySegment src) { * 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().toRawLongValue() + offset}. + * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @return a Java string constructed from the bytes read from the given starting address up to (but not including) * the first {@code '\0'} terminator character (assuming one is found). * @throws IllegalArgumentException if the size of the UTF-8 string is greater than the largest string supported by the platform. - * @throws IndexOutOfBoundsException if {@code S + offset > byteSize()}, where {@code S} is the size of the UTF-8 + * @throws IndexOutOfBoundsException if {@code offset < 0} or {@code S + offset > byteSize()}, where {@code S} is the size of the UTF-8 * string (including the terminator character). - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. */ default String getUtf8String(long offset) { return SharedUtils.toJavaStringInternal(this, offset); @@ -742,15 +902,14 @@ default String getUtf8String(long offset) { * copied as well. This means that, depending on the method used to read * the string, such as {@link MemorySegment#getUtf8String(long)}, the string * will appear truncated when read again. - * - * @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().toRawLongValue() + offset}. + * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. + * the final address of this write operation can be expressed as {@code address() + offset}. * @param str the Java string to be written into this segment. - * @throws IndexOutOfBoundsException if {@code str.getBytes().length() + offset >= byteSize()}. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. + * @throws IndexOutOfBoundsException if {@code offset < 0} or {@code str.getBytes().length() + offset >= byteSize()}. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. */ default void setUtf8String(long offset, String str) { Utils.toCString(str.getBytes(StandardCharsets.UTF_8), SegmentAllocator.prefixAllocator(asSlice(offset))); @@ -758,112 +917,212 @@ default void setUtf8String(long offset, String str) { /** - * Creates a buffer memory segment that models the memory associated with the given {@link Buffer} instance. + * Creates a memory segment that is backed by the same region of memory that backs the given {@link Buffer} instance. * The segment starts relative to the buffer's position (inclusive) and ends relative to the buffer's limit (exclusive). *

    - * If the buffer is {@linkplain ByteBuffer#isReadOnly() read-only}, the resulting segment will also be - * {@linkplain ByteBuffer#isReadOnly() read-only}. The memory session associated with this segment can either be the - * {@linkplain MemorySession#global() global} memory session, in case the buffer has been created independently, - * or some other memory session, in case the buffer has been obtained using {@link #asByteBuffer()}. + * If the buffer is {@linkplain Buffer#isReadOnly() read-only}, the resulting segment will also be + * {@linkplain ByteBuffer#isReadOnly() read-only}. Moreover, if the buffer is a {@linkplain Buffer#isDirect() direct buffer}, + * the returned segment is a native segment; otherwise the returned memory segment is a heap segment. *

    - * The resulting memory segment keeps a reference to the backing buffer, keeping it reachable. + * The scope {@code S} associated with the returned segment is computed as follows: + *

      + *
    • if the buffer has been obtained by calling {@link #asByteBuffer()} on a memory segment whose scope + * is {@code S'}, then {@code S = S'}; or
    • + *
    • if the buffer is a heap buffer, then {@code S} is the {@linkplain SegmentScope#global() global scope}; or + *
    • if the buffer is a direct buffer, then {@code S} is a scope that is always alive and which keeps the buffer reachable. + * Therefore, the off-heap region of memory backing the buffer instance will remain available as long as the + * returned segment is reachable.
    • + *
    * - * @param buffer the buffer instance backing the buffer memory segment. - * @return a buffer memory segment. + * @param buffer the buffer instance to be turned into a new memory segment. + * @return a memory segment, derived from the given buffer instance. + * @throws IllegalArgumentException if the provided {@code buffer} is a heap buffer but is not backed by an array. + * For example, buffers directly or indirectly obtained via + * ({@link CharBuffer#wrap(CharSequence)} or {@link CharBuffer#wrap(char[], int, int)} + * are not backed by an array. */ static MemorySegment ofBuffer(Buffer buffer) { return AbstractMemorySegmentImpl.ofBuffer(buffer); } /** - * Creates an array memory segment that models the memory associated with the given heap-allocated byte array. - * The returned segment is associated with the {@linkplain MemorySession#global() global} memory session. + * Creates a heap segment backed by the on-heap region of memory that holds the given byte array. + * The returned segment is associated with the {@linkplain SegmentScope#global() global scope}, and + * its {@link #address()} is set to zero. * - * @param arr the primitive array backing the array memory segment. - * @return an array memory segment. + * @param byteArray the primitive array backing the heap memory segment. + * @return a heap memory segment backed by a byte array. */ - static MemorySegment ofArray(byte[] arr) { - return HeapMemorySegmentImpl.OfByte.fromArray(arr); + static MemorySegment ofArray(byte[] byteArray) { + return HeapMemorySegmentImpl.OfByte.fromArray(byteArray); } /** - * Creates an array memory segment that models the memory associated with the given heap-allocated char array. - * The returned segment is associated with the {@linkplain MemorySession#global() global} memory session. + * Creates a heap segment backed by the on-heap region of memory that holds the given char array. + * The returned segment is associated with the {@linkplain SegmentScope#global() global scope}, and + * its {@link #address()} is set to zero. * - * @param arr the primitive array backing the array memory segment. - * @return an array memory segment. + * @param charArray the primitive array backing the heap segment. + * @return a heap memory segment backed by a char array. */ - static MemorySegment ofArray(char[] arr) { - return HeapMemorySegmentImpl.OfChar.fromArray(arr); + static MemorySegment ofArray(char[] charArray) { + return HeapMemorySegmentImpl.OfChar.fromArray(charArray); } /** - * Creates an array memory segment that models the memory associated with the given heap-allocated short array. - * The returned segment is associated with the {@linkplain MemorySession#global() global} memory session. + * Creates a heap segment backed by the on-heap region of memory that holds the given short array. + * The returned segment is associated with the {@linkplain SegmentScope#global() global scope}, and + * its {@link #address()} is set to zero. * - * @param arr the primitive array backing the array memory segment. - * @return an array memory segment. + * @param shortArray the primitive array backing the heap segment. + * @return a heap memory segment backed by a short array. */ - static MemorySegment ofArray(short[] arr) { - return HeapMemorySegmentImpl.OfShort.fromArray(arr); + static MemorySegment ofArray(short[] shortArray) { + return HeapMemorySegmentImpl.OfShort.fromArray(shortArray); } /** - * Creates an array memory segment that models the memory associated with the given heap-allocated int array. - * The returned segment is associated with the {@linkplain MemorySession#global() global} memory session. + * Creates a heap segment backed by the on-heap region of memory that holds the given int array. + * The returned segment is associated with the {@linkplain SegmentScope#global() global scope}, and + * its {@link #address()} is set to zero. * - * @param arr the primitive array backing the array memory segment. - * @return an array memory segment. + * @param intArray the primitive array backing the heap segment. + * @return a heap memory segment backed by an int array. */ - static MemorySegment ofArray(int[] arr) { - return HeapMemorySegmentImpl.OfInt.fromArray(arr); + static MemorySegment ofArray(int[] intArray) { + return HeapMemorySegmentImpl.OfInt.fromArray(intArray); } /** - * Creates an array memory segment that models the memory associated with the given heap-allocated float array. - * The returned segment is associated with the {@linkplain MemorySession#global() global} memory session. + * Creates a heap segment backed by the on-heap region of memory that holds the given float array. + * The returned segment is associated with the {@linkplain SegmentScope#global() global scope}, and + * its {@link #address()} is set to zero. * - * @param arr the primitive array backing the array memory segment. - * @return an array memory segment. + * @param floatArray the primitive array backing the heap segment. + * @return a heap memory segment backed by a float array. */ - static MemorySegment ofArray(float[] arr) { - return HeapMemorySegmentImpl.OfFloat.fromArray(arr); + static MemorySegment ofArray(float[] floatArray) { + return HeapMemorySegmentImpl.OfFloat.fromArray(floatArray); } /** - * Creates an array memory segment that models the memory associated with the given heap-allocated long array. - * The returned segment is associated with the {@linkplain MemorySession#global() global} memory session. + * Creates a heap segment backed by the on-heap region of memory that holds the given long array. + * The returned segment is associated with the {@linkplain SegmentScope#global() global scope}, and + * its {@link #address()} is set to zero. * - * @param arr the primitive array backing the array memory segment. - * @return an array memory segment. + * @param longArray the primitive array backing the heap segment. + * @return a heap memory segment backed by a long array. */ - static MemorySegment ofArray(long[] arr) { - return HeapMemorySegmentImpl.OfLong.fromArray(arr); + static MemorySegment ofArray(long[] longArray) { + return HeapMemorySegmentImpl.OfLong.fromArray(longArray); } /** - * Creates an array memory segment that models the memory associated with the given heap-allocated double array. - * The returned segment is associated with the {@linkplain MemorySession#global() global} memory session. + * Creates a heap segment backed by the on-heap region of memory that holds the given double array. + * The returned segment is associated with the {@linkplain SegmentScope#global() global scope}, and + * its {@link #address()} is set to zero. * - * @param arr the primitive array backing the array memory segment. - * @return an array memory segment. + * @param doubleArray the primitive array backing the heap segment. + * @return a heap memory segment backed by a double array. */ - static MemorySegment ofArray(double[] arr) { - return HeapMemorySegmentImpl.OfDouble.fromArray(arr); + static MemorySegment ofArray(double[] doubleArray) { + return HeapMemorySegmentImpl.OfDouble.fromArray(doubleArray); } + /** + * A zero-length native segment modelling the {@code NULL} address. + */ + MemorySegment NULL = NativeMemorySegmentImpl.makeNativeSegmentUnchecked(0L, 0); /** - * Creates a native memory segment with the given size, base address, and memory session. + * Creates a zero-length native segment from the given {@linkplain #address() address value}. + * The returned segment is associated with the {@linkplain SegmentScope#global() global scope}. + *

    + * This is equivalent to the following code: + * {@snippet lang = java: + * ofAddress(address, 0); + *} + * @param address the address of the returned native segment. + * @return a zero-length native segment with the given address. + */ + static MemorySegment ofAddress(long address) { + return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(address, 0); + } + + /** + * Creates a native segment with the given size and {@linkplain #address() address value}. + * The returned segment is associated with the {@linkplain SegmentScope#global() global scope}. + *

    + * This is equivalent to the following code: + * {@snippet lang = java: + * ofAddress(address, byteSize, SegmentScope.global()); + *} + * 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 address of the returned native segment. + * @param byteSize the size (in bytes) of the returned native segment. + * @return a zero-length native segment with the given address and size. + * @throws IllegalArgumentException if {@code byteSize < 0}. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + static MemorySegment ofAddress(long address, long byteSize) { + Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemorySegment.class, "ofAddress"); + return MemorySegment.ofAddress(address, byteSize, SegmentScope.global()); + } + + /** + * Creates a native segment with the given size, address, and scope. * This method can be useful when interacting with custom memory sources (e.g. custom allocators), - * where an address to some underlying memory region is typically obtained from foreign code + * where an address to some underlying region of memory is typically obtained from foreign 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 memory session. + * provided scope. + *

    + * This is equivalent to the following code: + * {@snippet lang = java: + * ofAddress(address, byteSize, scope, null); + *} + * + * @param address the returned segment's address. + * @param byteSize the desired size. + * @param scope the scope associated with the returned native segment. + * @return a native segment with the given address, size and scope. + * @throws IllegalArgumentException if {@code byteSize < 0}. + * @throws IllegalStateException if {@code scope} is not {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope.isAccessibleBy(T) == false}. + * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option + * {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or + * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. + */ + @CallerSensitive + @ForceInline + static MemorySegment ofAddress(long address, long byteSize, SegmentScope scope) { + Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemorySegment.class, "ofAddress"); + Objects.requireNonNull(scope); + Utils.checkAllocationSizeAndAlign(byteSize, 1); + return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(address, byteSize, scope, null); + } + + /** + * Creates a native segment with the given size, address, and scope. + * This method can be useful when interacting with custom memory sources (e.g. custom allocators), + * where an address to some underlying region of memory is typically obtained from foreign 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 scope. + *

    + * The provided cleanup action (if any) will be invoked when the scope becomes not {@linkplain SegmentScope#isAlive() alive}. *

    * 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, + * if appropriate, writing; an attempt to access an invalid address 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. @@ -872,97 +1131,127 @@ static MemorySegment ofArray(double[] arr) { * restricted methods, and use safe and supported functionalities, where possible. * * - * @param address the returned segment's base address. - * @param bytesSize the desired size. - * @param session the native segment memory session. - * @return a native memory segment with the given base address, size and memory session. - * @throws IllegalArgumentException if {@code bytesSize < 0}. - * @throws IllegalStateException if {@code session} is not {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread - * {@linkplain MemorySession#ownerThread() owning} {@code session}. + * @param address the returned segment's address. + * @param byteSize the desired size. + * @param scope the scope associated with the returned native segment. + * @param cleanupAction the custom cleanup action to be associated to the returned segment (can be null). + * @return a native segment with the given address, size and scope. + * @throws IllegalArgumentException if {@code byteSize < 0}. + * @throws IllegalStateException if {@code scope} is not {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope.isAccessibleBy(T) == false}. * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option * {@code --enable-native-access} is specified, but 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, MemorySession session) { + static MemorySegment ofAddress(long address, long byteSize, SegmentScope scope, Runnable cleanupAction) { Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemorySegment.class, "ofAddress"); - Objects.requireNonNull(address); - Objects.requireNonNull(session); - Utils.checkAllocationSizeAndAlign(bytesSize, 1); - return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(address, bytesSize, session); + Objects.requireNonNull(scope); + Utils.checkAllocationSizeAndAlign(byteSize, 1); + return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(address, byteSize, scope, cleanupAction); } /** - * Creates a native memory segment with the given layout and memory session. - * A client is responsible for ensuring that the memory session 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. + * Creates a native segment with the given layout and scope. + *

    + * The lifetime off-heap region of memory associated with the returned native segment is determined by the + * provided scope. The off-heap memory region is deallocated when the scope becomes not + * {@linkplain SegmentScope#isAlive() alive}. If the scope has been obtained using an {@link Arena}, + * clients are responsible for ensuring that the arena is closed when the returned segment is no longer in use + * Failure to do so will result in off-heap memory leaks. As an alternative, an {@linkplain SegmentScope#auto() automatic scope} + * can be used, allowing the off-heap memory region associated with the returned native segment to be + * automatically released some unspecified time after the scope is no longer referenced. + *

    + * The {@linkplain #address() address} of the returned memory segment is the starting address of + * the newly allocated off-heap region backing the segment. Moreover, the {@linkplain #address() address} + * of the returned segment will be aligned according to the alignment constraint of the provided layout. *

    * This is equivalent to the following code: * {@snippet lang=java : - * allocateNative(layout.bytesSize(), layout.bytesAlignment(), session); + * allocateNative(layout.bytesSize(), layout.bytesAlignment(), scope); * } *

    - * The block of off-heap memory associated with the returned native memory segment is initialized to zero. + * The region of off-heap region backing the returned native segment is initialized to zero. * - * @param layout the layout of the off-heap memory block backing the native memory segment. - * @param session the segment memory session. - * @return a new native memory segment. - * @throws IllegalStateException if {@code session} is not {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread - * {@linkplain MemorySession#ownerThread() owning} {@code session}. + * @param layout the layout of the off-heap memory region backing the native segment. + * @param scope the scope associated with the returned native segment. + * @return a new native segment. + * @throws IllegalStateException if {@code scope} is not {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope.isAccessibleBy(T) == false}. */ - static MemorySegment allocateNative(MemoryLayout layout, MemorySession session) { - Objects.requireNonNull(session); + static MemorySegment allocateNative(MemoryLayout layout, SegmentScope scope) { Objects.requireNonNull(layout); - return allocateNative(layout.byteSize(), layout.byteAlignment(), session); + Objects.requireNonNull(scope); + return allocateNative(layout.byteSize(), layout.byteAlignment(), scope); } /** - * Creates a native memory segment with the given size (in bytes) and memory session. - * A client is responsible for ensuring that the memory session 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. + * Creates a native segment with the given size (in bytes) and scope. + *

    + * The lifetime off-heap region of memory associated with the returned native segment is determined by the + * provided scope. The off-heap memory region is deallocated when the scope becomes not + * {@linkplain SegmentScope#isAlive() alive}. If the scope has been obtained using an {@link Arena}, + * clients are responsible for ensuring that the arena is closed when the returned segment is no longer in use + * Failure to do so will result in off-heap memory leaks. As an alternative, an {@linkplain SegmentScope#auto() automatic scope} + * can be used, allowing the off-heap memory region associated with the returned native segment to be + * automatically released some unspecified time after the scope is no longer referenced. + *

    + * The {@linkplain #address() address} of the returned memory segment is the starting address of + * the newly allocated off-heap region backing the segment. Moreover, the {@linkplain #address() address} + * of the returned segment is guaranteed to be at least 1-byte aligned. *

    * This is equivalent to the following code: * {@snippet lang=java : - * allocateNative(bytesSize, 1, session); + * allocateNative(bytesSize, 1, scope); * } *

    - * The block of off-heap memory associated with the returned native memory segment is initialized to zero. + * The region of off-heap region backing the returned native segment is initialized to zero. * - * @param bytesSize the size (in bytes) of the off-heap memory block backing the native memory segment. - * @param session the segment temporal bounds. + * @param byteSize the size (in bytes) of the off-heap memory region of memory backing the native memory segment. + * @param scope the scope associated with the returned native segment. * @return a new native memory segment. - * @throws IllegalArgumentException if {@code bytesSize < 0}. - * @throws IllegalStateException if {@code session} is not {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread - * {@linkplain MemorySession#ownerThread() owning} {@code session}. + * @throws IllegalArgumentException if {@code byteSize < 0}. + * @throws IllegalStateException if {@code scope} is not {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope.isAccessibleBy(T) == false}. */ - static MemorySegment allocateNative(long bytesSize, MemorySession session) { - return allocateNative(bytesSize, 1, session); + static MemorySegment allocateNative(long byteSize, SegmentScope scope) { + return allocateNative(byteSize, 1, scope); } /** - * Creates a native memory segment with the given size (in bytes), alignment constraint (in bytes) and memory session. - * A client is responsible for ensuring that the memory session 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. + * Creates a native segment with the given size (in bytes), alignment (in bytes) and scope. + *

    + * The lifetime off-heap region of memory associated with the returned native segment is determined by the + * provided scope. The off-heap memory region is deallocated when the scope becomes not + * {@linkplain SegmentScope#isAlive() alive}. If the scope has been obtained using an {@link Arena}, + * clients are responsible for ensuring that the arena is closed when the returned segment is no longer in use + * Failure to do so will result in off-heap memory leaks. As an alternative, an {@linkplain SegmentScope#auto() automatic scope} + * can be used, allowing the off-heap memory region associated with the returned native segment to be + * automatically released some unspecified time after the scope is no longer referenced. *

    - * The block of off-heap memory associated with the returned native memory segment is initialized to zero. + * The {@linkplain #address() address} of the returned memory segment is the starting address of + * the newly allocated off-heap region backing the segment. Moreover, the {@linkplain #address() address} + * of the returned segment will be aligned according to the provided alignment constraint. + *

    + * The region of off-heap region backing the returned native segment is initialized to zero. * - * @param bytesSize the size (in bytes) of the off-heap memory block backing the native memory segment. - * @param alignmentBytes the alignment constraint (in bytes) of the off-heap memory block backing the native memory segment. - * @param session the segment memory session. + * @param byteSize the size (in bytes) of the off-heap region of memory backing the native memory segment. + * @param byteAlignment the alignment constraint (in bytes) of the off-heap region of memory backing the native memory segment. + * @param scope the scope associated with the returned native segment. * @return a new native memory segment. - * @throws IllegalArgumentException if {@code bytesSize < 0}, {@code alignmentBytes <= 0}, or if {@code alignmentBytes} - * is not a power of 2. - * @throws IllegalStateException if {@code session} is not {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread - * {@linkplain MemorySession#ownerThread() owning} {@code session}. - */ - static MemorySegment allocateNative(long bytesSize, long alignmentBytes, MemorySession session) { - Objects.requireNonNull(session); - Utils.checkAllocationSizeAndAlign(bytesSize, alignmentBytes); - return NativeMemorySegmentImpl.makeNativeSegment(bytesSize, alignmentBytes, session); + * @throws IllegalArgumentException if {@code byteSize < 0}, {@code byteAlignment <= 0}, or if {@code byteAlignment} + * is not a power of 2. + * @throws IllegalStateException if {@code scope} is not {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope.isAccessibleBy(T) == false}. + */ + static MemorySegment allocateNative(long byteSize, long byteAlignment, SegmentScope scope) { + Objects.requireNonNull(scope); + Utils.checkAllocationSizeAndAlign(byteSize, byteAlignment); + return NativeMemorySegmentImpl.makeNativeSegment(byteSize, byteAlignment, scope); } /** @@ -988,14 +1277,14 @@ static MemorySegment allocateNative(long bytesSize, long alignmentBytes, MemoryS * @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 the {@linkplain #session() session} associated with {@code srcSegment} is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with {@code srcSegment}. - * @throws IllegalStateException if the {@linkplain #session() session} associated with {@code dstSegment} is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with {@code dstSegment}. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code srcSegment} is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code srcSegment.scope().isAccessibleBy(T) == false}. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code dstSegment} is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code dstSegment.scope().isAccessibleBy(T) == false}. * @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}. @@ -1033,16 +1322,16 @@ static void copy(MemorySegment srcSegment, long srcOffset, MemorySegment dstSegm * @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 (resp. destination) segment/offset are - * incompatible with the alignment constraints in the source + * incompatible with the alignment constraint in the source * (resp. destination) element layout, or if the source (resp. destination) element layout alignment is greater than its size. - * @throws IllegalStateException if the {@linkplain #session() session} associated with {@code srcSegment} is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this {@code srcSegment}. - * @throws IllegalStateException if the {@linkplain #session() session} associated with {@code dstSegment} is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with {@code dstSegment}. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code srcSegment} is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code srcSegment().scope().isAccessibleBy(T) == false}. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code dstSegment} is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code dstSegment().scope().isAccessibleBy(T) == false}. * @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}. @@ -1085,429 +1374,414 @@ static void copy(MemorySegment srcSegment, ValueLayout srcElementLayout, long sr /** * Reads a byte from this segment at the given offset, with the 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().toRawLongValue() + offset}. - * @return a byte value read from this address. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @param layout the layout of the region of memory to be read. + * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. + * @return a byte value read from this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout. + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. */ @ForceInline default byte get(ValueLayout.OfByte layout, long offset) { - return (byte)layout.accessHandle().get(this, offset); + return (byte) ((ValueLayouts.OfByteImpl) layout).accessHandle().get(this, offset); } /** * Writes a byte into this segment at the given offset, with the 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().toRawLongValue() + offset}. + * @param layout the layout of the region of memory to be written. + * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @param value the byte value to be written. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout. + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline default void set(ValueLayout.OfByte layout, long offset, byte value) { - layout.accessHandle().set(this, offset, value); + ((ValueLayouts.OfByteImpl) layout).accessHandle().set(this, offset, value); } /** * Reads a boolean from this segment at the given offset, with the 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().toRawLongValue() + offset}. - * @return a boolean value read from this address. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @param layout the layout of the region of memory to be read. + * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. + * @return a boolean value read from this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout. + * @throws IndexOutOfBoundsException when the access 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); + return (boolean) ((ValueLayouts.OfBooleanImpl) layout).accessHandle().get(this, offset); } /** * Writes a boolean into this segment at the given offset, with the 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().toRawLongValue() + offset}. + * @param layout the layout of the region of memory to be written. + * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @param value the boolean value to be written. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout. + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline default void set(ValueLayout.OfBoolean layout, long offset, boolean value) { - layout.accessHandle().set(this, offset, value); + ((ValueLayouts.OfBooleanImpl) layout).accessHandle().set(this, offset, value); } /** * Reads a char from this segment at the given offset, with the 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().toRawLongValue() + offset}. - * @return a char value read from this address. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @param layout the layout of the region of memory to be read. + * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. + * @return a char value read from this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout. + * @throws IndexOutOfBoundsException when the access 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); + return (char) ((ValueLayouts.OfCharImpl) layout).accessHandle().get(this, offset); } /** * Writes a char into this segment at the given offset, with the 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().toRawLongValue() + offset}. + * @param layout the layout of the region of memory to be written. + * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @param value the char value to be written. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout. + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline default void set(ValueLayout.OfChar layout, long offset, char value) { - layout.accessHandle().set(this, offset, value); + ((ValueLayouts.OfCharImpl) layout).accessHandle().set(this, offset, value); } /** * Reads a short from this segment at the given offset, with the 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().toRawLongValue() + offset}. - * @return a short value read from this address. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @param layout the layout of the region of memory to be read. + * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. + * @return a short value read from this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout. + * @throws IndexOutOfBoundsException when the access 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); + return (short) ((ValueLayouts.OfShortImpl) layout).accessHandle().get(this, offset); } /** * Writes a short into this segment at the given offset, with the 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().toRawLongValue() + offset}. + * @param layout the layout of the region of memory to be written. + * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @param value the short value to be written. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout. + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline default void set(ValueLayout.OfShort layout, long offset, short value) { - layout.accessHandle().set(this, offset, value); + ((ValueLayouts.OfShortImpl) layout).accessHandle().set(this, offset, value); } /** * Reads an int from this segment at the given offset, with the 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().toRawLongValue() + offset}. - * @return an int value read from this address. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @param layout the layout of the region of memory to be read. + * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. + * @return an int value read from this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout. + * @throws IndexOutOfBoundsException when the access 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); + return (int) ((ValueLayouts.OfIntImpl) layout).accessHandle().get(this, offset); } /** * Writes an int into this segment at the given offset, with the 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().toRawLongValue() + offset}. + * @param layout the layout of the region of memory to be written. + * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @param value the int value to be written. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout. + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline default void set(ValueLayout.OfInt layout, long offset, int value) { - layout.accessHandle().set(this, offset, value); + ((ValueLayouts.OfIntImpl) layout).accessHandle().set(this, offset, value); } /** * Reads a float from this segment at the given offset, with the 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().toRawLongValue() + offset}. - * @return a float value read from this address. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @param layout the layout of the region of memory to be read. + * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. + * @return a float value read from this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout. + * @throws IndexOutOfBoundsException when the access 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); + return (float)((ValueLayouts.OfFloatImpl) layout).accessHandle().get(this, offset); } /** * Writes a float into this segment at the given offset, with the 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().toRawLongValue() + offset}. + * @param layout the layout of the region of memory to be written. + * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @param value the float value to be written. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout. + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline default void set(ValueLayout.OfFloat layout, long offset, float value) { - layout.accessHandle().set(this, offset, value); + ((ValueLayouts.OfFloatImpl) layout).accessHandle().set(this, offset, value); } /** * Reads a long from this segment at the given offset, with the 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().toRawLongValue() + offset}. - * @return a long value read from this address. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @param layout the layout of the region of memory to be read. + * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. + * @return a long value read from this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout. + * @throws IndexOutOfBoundsException when the access 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); + return (long) ((ValueLayouts.OfLongImpl) layout).accessHandle().get(this, offset); } /** * Writes a long into this segment at the given offset, with the 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().toRawLongValue() + offset}. + * @param layout the layout of the region of memory to be written. + * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @param value the long value to be written. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout. + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline default void set(ValueLayout.OfLong layout, long offset, long value) { - layout.accessHandle().set(this, offset, value); + ((ValueLayouts.OfLongImpl) layout).accessHandle().set(this, offset, value); } /** * Reads a double from this segment at the given offset, with the 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().toRawLongValue() + offset}. - * @return a double value read from this address. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @param layout the layout of the region of memory to be read. + * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. + * @return a double value read from this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout. + * @throws IndexOutOfBoundsException when the access 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); + return (double) ((ValueLayouts.OfDoubleImpl) layout).accessHandle().get(this, offset); } /** * Writes a double into this segment at the given offset, with the 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().toRawLongValue() + offset}. + * @param layout the layout of the region of memory to be written. + * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @param value the double value to be written. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout. + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline default void set(ValueLayout.OfDouble layout, long offset, double value) { - layout.accessHandle().set(this, offset, value); + ((ValueLayouts.OfDoubleImpl) layout).accessHandle().set(this, offset, value); } /** - * Reads an address from this segment at the given offset, with the 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().toRawLongValue() + offset}. - * @return an address value read from this address. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * Reads an address from this segment at the given offset, with the given layout. The read address is wrapped in + * a native segment, associated with the {@linkplain SegmentScope#global() global scope}. Under normal conditions, + * the size of the returned segment is {@code 0}. However, if the provided layout is an + * {@linkplain ValueLayout.OfAddress#asUnbounded() unbounded} address layout, then the size of the returned + * segment is {@code Long.MAX_VALUE}. + * @param layout the layout of the region of memory to be read. + * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. + * @return a native segment wrapping an address read from this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout. + * @throws IndexOutOfBoundsException when the access 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); + default MemorySegment get(ValueLayout.OfAddress layout, long offset) { + return (MemorySegment) ((ValueLayouts.OfAddressImpl) layout).accessHandle().get(this, offset); } /** * Writes an address into this segment at the given offset, with the 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().toRawLongValue() + offset}. + * @param layout the layout of the region of memory to be written. + * @param offset offset in bytes (relative to this segment address) at which this access operation will occur. * @param value the address value to be written. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout. + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline - default void set(ValueLayout.OfAddress layout, long offset, Addressable value) { - layout.accessHandle().set(this, offset, value.address()); + default void set(ValueLayout.OfAddress layout, long offset, MemorySegment value) { + ((ValueLayouts.OfAddressImpl) layout).accessHandle().set(this, offset, value); } /** * Reads a char from this segment at the given index, scaled by the 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().toRawLongValue() + (index * layout.byteSize())}. - * @return a char value read from this address. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, + * @param layout the layout of the region of memory to be read. + * @param index a logical index. The offset in bytes (relative to this segment address) at which the access operation + * will occur can be expressed as {@code (index * layout.byteSize())}. + * @return a char value read from this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. */ @ForceInline default char getAtIndex(ValueLayout.OfChar layout, long index) { Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - return (char)layout.accessHandle().get(this, index * layout.byteSize()); + return (char) ((ValueLayouts.OfCharImpl) layout).accessHandle().get(this, index * layout.byteSize()); } /** * Writes a char into this segment at the given index, scaled by the 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().toRawLongValue() + (index * layout.byteSize())}. + * @param layout the layout of the region of memory to be written. + * @param index a logical index. The offset in bytes (relative to this segment address) at which the access operation + * will occur can be expressed as {@code (index * layout.byteSize())}. * @param value the char value to be written. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @@ -1515,48 +1789,48 @@ default char getAtIndex(ValueLayout.OfChar layout, long index) { default void setAtIndex(ValueLayout.OfChar layout, long index, char value) { Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - layout.accessHandle().set(this, index * layout.byteSize(), value); + ((ValueLayouts.OfCharImpl) layout).accessHandle().set(this, index * layout.byteSize(), value); } /** * Reads a short from this segment at the given index, scaled by the 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().toRawLongValue() + (index * layout.byteSize())}. - * @return a short value read from this address. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, + * @param layout the layout of the region of memory to be read. + * @param index a logical index. The offset in bytes (relative to this segment address) at which the access operation + * will occur can be expressed as {@code (index * layout.byteSize())}. + * @return a short value read from this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. */ @ForceInline default short getAtIndex(ValueLayout.OfShort layout, long index) { Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - return (short)layout.accessHandle().get(this, index * layout.byteSize()); + return (short) ((ValueLayouts.OfShortImpl) layout).accessHandle().get(this, index * layout.byteSize()); } /** * Writes a short into this segment at the given index, scaled by the 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().toRawLongValue() + (index * layout.byteSize())}. + * @param layout the layout of the region of memory to be written. + * @param index a logical index. The offset in bytes (relative to this segment address) at which the access operation + * will occur can be expressed as {@code (index * layout.byteSize())}. * @param value the short value to be written. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @@ -1564,48 +1838,48 @@ default short getAtIndex(ValueLayout.OfShort layout, long index) { default void setAtIndex(ValueLayout.OfShort layout, long index, short value) { Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - layout.accessHandle().set(this, index * layout.byteSize(), value); + ((ValueLayouts.OfShortImpl) layout).accessHandle().set(this, index * layout.byteSize(), value); } /** * Reads an int from this segment at the given index, scaled by the 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().toRawLongValue() + (index * layout.byteSize())}. - * @return an int value read from this address. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, + * @param layout the layout of the region of memory to be read. + * @param index a logical index. The offset in bytes (relative to this segment address) at which the access operation + * will occur can be expressed as {@code (index * layout.byteSize())}. + * @return an int value read from this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. */ @ForceInline default int getAtIndex(ValueLayout.OfInt layout, long index) { Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - return (int)layout.accessHandle().get(this, index * layout.byteSize()); + return (int) ((ValueLayouts.OfIntImpl) layout).accessHandle().get(this, index * layout.byteSize()); } /** * Writes an int into this segment at the given index, scaled by the 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().toRawLongValue() + (index * layout.byteSize())}. + * @param layout the layout of the region of memory to be written. + * @param index a logical index. The offset in bytes (relative to this segment address) at which the access operation + * will occur can be expressed as {@code (index * layout.byteSize())}. * @param value the int value to be written. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @@ -1613,48 +1887,48 @@ default int getAtIndex(ValueLayout.OfInt layout, long index) { default void setAtIndex(ValueLayout.OfInt layout, long index, int value) { Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - layout.accessHandle().set(this, index * layout.byteSize(), value); + ((ValueLayouts.OfIntImpl) layout).accessHandle().set(this, index * layout.byteSize(), value); } /** * Reads a float from this segment at the given index, scaled by the 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().toRawLongValue() + (index * layout.byteSize())}. - * @return a float value read from this address. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, + * @param layout the layout of the region of memory to be read. + * @param index a logical index. The offset in bytes (relative to this segment address) at which the access operation + * will occur can be expressed as {@code (index * layout.byteSize())}. + * @return a float value read from this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. */ @ForceInline default float getAtIndex(ValueLayout.OfFloat layout, long index) { Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - return (float)layout.accessHandle().get(this, index * layout.byteSize()); + return (float) ((ValueLayouts.OfFloatImpl) layout).accessHandle().get(this, index * layout.byteSize()); } /** * Writes a float into this segment at the given index, scaled by the 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().toRawLongValue() + (index * layout.byteSize())}. + * @param layout the layout of the region of memory to be written. + * @param index a logical index. The offset in bytes (relative to this segment address) at which the access operation + * will occur can be expressed as {@code (index * layout.byteSize())}. * @param value the float value to be written. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @@ -1662,48 +1936,48 @@ default float getAtIndex(ValueLayout.OfFloat layout, long index) { default void setAtIndex(ValueLayout.OfFloat layout, long index, float value) { Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - layout.accessHandle().set(this, index * layout.byteSize(), value); + ((ValueLayouts.OfFloatImpl) layout).accessHandle().set(this, index * layout.byteSize(), value); } /** * Reads a long from this segment at the given index, scaled by the 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().toRawLongValue() + (index * layout.byteSize())}. - * @return a long value read from this address. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, + * @param layout the layout of the region of memory to be read. + * @param index a logical index. The offset in bytes (relative to this segment address) at which the access operation + * will occur can be expressed as {@code (index * layout.byteSize())}. + * @return a long value read from this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. */ @ForceInline default long getAtIndex(ValueLayout.OfLong layout, long index) { Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - return (long)layout.accessHandle().get(this, index * layout.byteSize()); + return (long) ((ValueLayouts.OfLongImpl) layout).accessHandle().get(this, index * layout.byteSize()); } /** * Writes a long into this segment at the given index, scaled by the 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().toRawLongValue() + (index * layout.byteSize())}. + * @param layout the layout of the region of memory to be written. + * @param index a logical index. The offset in bytes (relative to this segment address) at which the access operation + * will occur can be expressed as {@code (index * layout.byteSize())}. * @param value the long value to be written. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @@ -1711,48 +1985,48 @@ default long getAtIndex(ValueLayout.OfLong layout, long index) { default void setAtIndex(ValueLayout.OfLong layout, long index, long value) { Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - layout.accessHandle().set(this, index * layout.byteSize(), value); + ((ValueLayouts.OfLongImpl) layout).accessHandle().set(this, index * layout.byteSize(), value); } /** * Reads a double from this segment at the given index, scaled by the 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().toRawLongValue() + (index * layout.byteSize())}. - * @return a double value read from this address. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, + * @param layout the layout of the region of memory to be read. + * @param index a logical index. The offset in bytes (relative to this segment address) at which the access operation + * will occur can be expressed as {@code (index * layout.byteSize())}. + * @return a double value read from this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. */ @ForceInline default double getAtIndex(ValueLayout.OfDouble layout, long index) { Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - return (double)layout.accessHandle().get(this, index * layout.byteSize()); + return (double) ((ValueLayouts.OfDoubleImpl) layout).accessHandle().get(this, index * layout.byteSize()); } /** * Writes a double into this segment at the given index, scaled by the 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().toRawLongValue() + (index * layout.byteSize())}. + * @param layout the layout of the region of memory to be written. + * @param index a logical index. The offset in bytes (relative to this segment address) at which the access operation + * will occur can be expressed as {@code (index * layout.byteSize())}. * @param value the double value to be written. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @@ -1760,78 +2034,82 @@ default double getAtIndex(ValueLayout.OfDouble layout, long index) { default void setAtIndex(ValueLayout.OfDouble layout, long index, double value) { Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - layout.accessHandle().set(this, index * layout.byteSize(), value); + ((ValueLayouts.OfDoubleImpl) layout).accessHandle().set(this, index * layout.byteSize(), value); } /** - * Reads an address from this segment at the given index, scaled by the 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().toRawLongValue() + (index * layout.byteSize())}. - * @return an address value read from this address. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, + * Reads an address from this segment at the given at the given index, scaled by the given layout size. The read address is wrapped in + * a native segment, associated with the {@linkplain SegmentScope#global() global scope}. Under normal conditions, + * the size of the returned segment is {@code 0}. However, if the provided layout is an + * {@linkplain ValueLayout.OfAddress#asUnbounded() unbounded} address layout, then the size of the returned + * segment is {@code Long.MAX_VALUE}. + * + * @param layout the layout of the region of memory to be read. + * @param index a logical index. The offset in bytes (relative to this segment address) at which the access operation + * will occur can be expressed as {@code (index * layout.byteSize())}. + * @return a native segment wrapping an address read from this segment. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. */ @ForceInline - default MemoryAddress getAtIndex(ValueLayout.OfAddress layout, long index) { + default MemorySegment getAtIndex(ValueLayout.OfAddress layout, long index) { Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - return (MemoryAddress)layout.accessHandle().get(this, index * layout.byteSize()); + return (MemorySegment) ((ValueLayouts.OfAddressImpl) layout).accessHandle().get(this, index * layout.byteSize()); } /** * Writes an address into this segment at the given index, scaled by the 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().toRawLongValue() + (index * layout.byteSize())}. + * @param layout the layout of the region of memory to be written. + * @param index a logical index. The offset in bytes (relative to this segment address) at which the access operation + * will occur can be expressed as {@code (index * layout.byteSize())}. * @param value the address value to be written. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this segment is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this segment. - * @throws IllegalArgumentException if the dereference operation is - * incompatible with the alignment constraints in the provided layout, + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with this segment is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope().isAccessibleBy(T) == false}. + * @throws IllegalArgumentException if the access operation is + * incompatible with the alignment constraint in the provided layout, * or if the layout alignment is greater than its size. - * @throws IndexOutOfBoundsException when the dereference operation falls outside the spatial bounds of the + * @throws IndexOutOfBoundsException when the access operation falls outside the spatial bounds of the * memory segment. * @throws UnsupportedOperationException if this segment is {@linkplain #isReadOnly() read-only}. */ @ForceInline - default void setAtIndex(ValueLayout.OfAddress layout, long index, Addressable value) { + default void setAtIndex(ValueLayout.OfAddress layout, long index, MemorySegment value) { Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); // note: we know size is a small value (as it comes from ValueLayout::byteSize()) - layout.accessHandle().set(this, index * layout.byteSize(), value.address()); + ((ValueLayouts.OfAddressImpl) layout).accessHandle().set(this, index * layout.byteSize(), value); } /** * Compares the specified object with this memory segment for equality. Returns {@code true} if and only if the specified - * object is also a memory segment, and if that segment refers to the same memory region as this segment. More specifically, - * for two segments to be considered equals, all the following must be true: + * object is also a memory segment, and if the two segments refer to the same location, in some region of memory. + * More specifically, for two segments {@code s1} and {@code s2} to be considered equals, all the following must be true: *

      - *
    • the two segments must be of the same kind; either both are {@linkplain #isNative() native segments}, - * backed by off-heap memory, or both are backed by on-heap memory; - *
    • if the two segments are {@linkplain #isNative() native segments}, their {@link #address() base address} - * must be {@linkplain MemoryAddress#equals(Object) equal}. Otherwise, the two segments must wrap the - * same Java array instance, at the same starting offset;
    • - *
    • the two segments must have the same {@linkplain #byteSize() size}; and
    • - *
    • the two segments must have the {@linkplain MemorySession#equals(Object) same} {@linkplain #session() temporal bounds}. + *
    • {@code s1.array().equals(s2.array())}, that is, the two segments must be of the same kind; + * either both are {@linkplain #isNative() native segments}, backed by off-heap memory, or both are backed by + * the same on-heap Java array; + *
    • {@code s1.address() == s2.address()}, that is, the address of the two segments should be the same. + * This means that the two segments either refer to the same location in some off-heap region, or they refer + * to the same position inside their associated Java array instance.
    • *
    * @apiNote This method does not perform a structural comparison of the contents of the two memory segments. Clients can - * compare memory segments structurally by using the {@link #mismatch(MemorySegment)} method instead. + * compare memory segments structurally by using the {@link #mismatch(MemorySegment)} method instead. Note that this + * method does not compare the temporal and spatial bounds of two segments. As such it is suitable + * to perform address checks, such as checking if a native segment has the {@code NULL} address. * * @param that the object to be compared for equality with this memory segment. * @return {@code true} if the specified object is equal to this memory segment. * @see #mismatch(MemorySegment) - * @see #asOverlappingSlice(MemorySegment) */ @Override boolean equals(Object that); @@ -1855,13 +2133,13 @@ default void setAtIndex(ValueLayout.OfAddress layout, long index, Addressable va * @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 IllegalStateException if the {@linkplain #session() session} associated with {@code srcSegment} is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with {@code srcSegment}. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code srcSegment} is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code srcSegment().isAccessibleBy(T) == false}. * @throws IllegalArgumentException if {@code dstArray} is not an array, or if it is an array but whose type is not supported, * if the destination array component type does not match the carrier of the source element layout, if the source - * segment/offset are incompatible with the alignment constraints in the source element layout, + * segment/offset are incompatible with the alignment constraint in the source element layout, * or if the destination element layout alignment is greater than its size. */ @ForceInline @@ -1871,28 +2149,10 @@ static void copy( 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; - Utils.checkElementAlignment(srcLayout, "Source layout alignment greater than its size"); - if (!srcImpl.isAlignedForElement(srcOffset, srcLayout)) { - throw new IllegalArgumentException("Source segment incompatible with alignment constraints"); - } - srcImpl.checkAccess(srcOffset, elementCount * dstWidth, true); - Objects.checkFromIndexSize(dstIndex, elementCount, Array.getLength(dstArray)); - if (dstWidth == 1 || srcLayout.order() == ByteOrder.nativeOrder()) { - ScopedMemoryAccess.getScopedMemoryAccess().copyMemory(srcImpl.sessionImpl(), null, - srcImpl.unsafeGetBase(), srcImpl.unsafeGetOffset() + srcOffset, - dstArray, dstBase + (dstIndex * dstWidth), elementCount * dstWidth); - } else { - ScopedMemoryAccess.getScopedMemoryAccess().copySwapMemory(srcImpl.sessionImpl(), null, - srcImpl.unsafeGetBase(), srcImpl.unsafeGetOffset() + srcOffset, - dstArray, dstBase + (dstIndex * dstWidth), elementCount * dstWidth, dstWidth); - } + + AbstractMemorySegmentImpl.copy(srcSegment, srcLayout, srcOffset, + dstArray, dstIndex, + elementCount); } /** @@ -1907,13 +2167,13 @@ static void copy( * different from the {@linkplain ByteOrder#nativeOrder 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 IllegalStateException if the {@linkplain #session() session} associated with {@code dstSegment} is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with {@code dstSegment}. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code dstSegment} is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code dstSegment().isAccessibleBy(T) == false}. * @throws IllegalArgumentException if {@code srcArray} is not an array, or if it is an array but whose type is not supported, * if the source array component type does not match the carrier of the destination element layout, if the destination - * segment/offset are incompatible with the alignment constraints in the destination element layout, + * segment/offset are incompatible with the alignment constraint in the destination element layout, * or if the destination element layout alignment is greater than its size. */ @ForceInline @@ -1923,47 +2183,52 @@ static void copy( 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; - Utils.checkElementAlignment(dstLayout, "Destination layout alignment greater than its size"); - if (!destImpl.isAlignedForElement(dstOffset, dstLayout)) { - throw new IllegalArgumentException("Destination segment incompatible with alignment constraints"); - } - destImpl.checkAccess(dstOffset, elementCount * srcWidth, false); - if (srcWidth == 1 || dstLayout.order() == ByteOrder.nativeOrder()) { - ScopedMemoryAccess.getScopedMemoryAccess().copyMemory(null, destImpl.sessionImpl(), - srcArray, srcBase + (srcIndex * srcWidth), - destImpl.unsafeGetBase(), destImpl.unsafeGetOffset() + dstOffset, elementCount * srcWidth); - } else { - ScopedMemoryAccess.getScopedMemoryAccess().copySwapMemory(null, destImpl.sessionImpl(), - srcArray, srcBase + (srcIndex * srcWidth), - destImpl.unsafeGetBase(), destImpl.unsafeGetOffset() + dstOffset, elementCount * srcWidth, srcWidth); - } + + AbstractMemorySegmentImpl.copy(srcArray, srcIndex, + dstSegment, dstLayout, dstOffset, + elementCount); } - 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()); - } + /** + * Finds and returns the relative offset, in bytes, of the first mismatch between the source and the destination + * segments. More specifically, the bytes at offset {@code srcFromOffset} through {@code srcToOffset - 1} in the + * source segment are compared against the bytes at offset {@code dstFromOffset} through {@code dstToOffset - 1} + * in the destination segment. + *

    + * If the two segments, over the specified ranges, share a common prefix then the returned offset is the length + * of the common prefix, and it follows that there is a mismatch between the two segments at that relative offset + * within the respective segments. If one segment is a proper prefix of the other, over the specified ranges, + * then the returned offset is the smallest range, and it follows that the relative offset is only + * valid for the segment with the larger range. Otherwise, there is no mismatch and {@code -1} is returned. + * + * @param srcSegment the source segment. + * @param srcFromOffset the offset (inclusive) of the first byte in the source segment to be tested. + * @param srcToOffset the offset (exclusive) of the last byte in the source segment to be tested. + * @param dstSegment the destination segment. + * @param dstFromOffset the offset (inclusive) of the first byte in the destination segment to be tested. + * @param dstToOffset the offset (exclusive) of the last byte in the destination segment to be tested. + * @return the relative offset, in bytes, of the first mismatch between the source and destination segments, + * otherwise -1 if no mismatch. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code srcSegment} is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code srcSegment.scope().isAccessibleBy(T) == false}. + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with {@code dstSegment} is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code dstSegment.scope().isAccessibleBy(T) == false}. + * @throws IndexOutOfBoundsException if {@code srcFromOffset < 0}, {@code srcToOffset < srcFromOffset} or + * {@code srcToOffset > srcSegment.byteSize()} + * @throws IndexOutOfBoundsException if {@code dstFromOffset < 0}, {@code dstToOffset < dstFromOffset} or + * {@code dstToOffset > dstSegment.byteSize()} + * + * @see MemorySegment#mismatch(MemorySegment) + * @see Arrays#mismatch(Object[], int, int, Object[], int, int) + */ + static long mismatch(MemorySegment srcSegment, long srcFromOffset, long srcToOffset, + MemorySegment dstSegment, long dstFromOffset, long dstToOffset) { + return AbstractMemorySegmentImpl.mismatch(srcSegment, srcFromOffset, srcToOffset, + dstSegment, dstFromOffset, dstToOffset); } + } diff --git a/src/java.base/share/classes/java/lang/foreign/MemorySession.java b/src/java.base/share/classes/java/lang/foreign/MemorySession.java deleted file mode 100644 index c7f21293dfdeb..0000000000000 --- a/src/java.base/share/classes/java/lang/foreign/MemorySession.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package java.lang.foreign; - -import java.lang.ref.Cleaner; -import java.util.Objects; - -import jdk.internal.foreign.MemorySessionImpl; -import jdk.internal.javac.PreviewFeature; - -/** - * A memory session manages the lifecycle of one or more resources. Resources (e.g. {@link MemorySegment}) associated - * with a memory session can only be accessed while the memory session is {@linkplain #isAlive() alive}, - * and by the {@linkplain #ownerThread() thread} associated with the memory session (if any). - *

    - * Memory sessions can be closed. When a memory session is closed, it is no longer {@linkplain #isAlive() alive}, - * and subsequent operations on resources associated with that session (e.g. attempting to access a {@link MemorySegment} instance) - * will fail with {@link IllegalStateException}. - *

    - * A memory session is associated with one or more {@linkplain #addCloseAction(Runnable) close actions}. Close actions - * can be used to specify the cleanup code that must run when a given resource (or set of resources) is no longer in use. - * When a memory session is closed, the {@linkplain #addCloseAction(Runnable) close actions} - * associated with that session are executed (in unspecified order). For instance, closing the memory session associated with - * one or more {@linkplain MemorySegment#allocateNative(long, long, MemorySession) native memory segments} results in releasing - * the off-heap memory associated with said segments. - *

    - * The {@linkplain #global() global session} is a memory session that cannot be closed. - * As a result, resources associated with the global session are never released. Examples of resources associated with - * the global memory session are {@linkplain MemorySegment#ofArray(int[]) heap segments}. - * - *

    Thread confinement

    - * - * Memory sessions can be divided into two categories: thread-confined memory sessions, and shared - * memory sessions. - *

    - * Confined memory sessions, 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 memory session, only the owner thread will be allowed to directly manipulate the resources - * associated with this memory session. Any attempt to perform resource access from a thread other than the - * owner thread will fail with {@link WrongThreadException}. - *

    - * Shared memory sessions, on the other hand, have no owner thread; as such, resources associated with shared memory sessions - * 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). - * - *

    Closeable memory sessions

    - * - * When a session is associated with off-heap resources, it is often desirable for said resources to be released in a timely fashion, - * rather than waiting for the session to be deemed unreachable - * by the garbage collector. In this scenario, a client might consider using a {@linkplain #isCloseable() closeable} memory session. - * Closeable memory sessions are memory sessions that can be {@linkplain MemorySession#close() closed} deterministically, as demonstrated - * in the following example: - * - * {@snippet lang=java : - * try (MemorySession session = MemorySession.openConfined()) { - * MemorySegment segment1 = MemorySegment.allocateNative(100); - * MemorySegment segment1 = MemorySegment.allocateNative(200); - * ... - * } // all memory released here - * } - * - * The above code creates a confined, closeable session. Then it allocates two segments associated with that session. - * When the session is {@linkplain #close() closed} (above, this is done implicitly, using the try-with-resources construct), - * all memory allocated within the session will be released - *

    - * Closeable memory sessions, while powerful, must be used with caution. Closeable memory sessions must be closed - * when no longer in use, either explicitly (by calling the {@link #close} method), or implicitly (by wrapping the use of - * a closeable memory session in a try-with-resources construct). A failure to do so might result in memory leaks. - * To mitigate this problem, closeable memory sessions can be associated with a {@link Cleaner} instance, - * so that they are also closed automatically, once the session instance becomes unreachable. - * This can be useful to allow for predictable, deterministic resource deallocation, while still preventing accidental - * native memory leaks. In case a client closes a memory session managed by a cleaner, no further action will be taken when - * the session becomes unreachable; that is, {@linkplain #addCloseAction(Runnable) close actions} associated with a - * memory session, whether managed or not, are called exactly once. - * - *

    Non-closeable views

    - * - * There are situations in which it might not be desirable for a memory session to be reachable from one or - * more resources associated with it. For instance, an API might create a private memory session, and allocate - * a memory segment, and then expose one or more slices of this segment to its clients. Since the API's memory session - * would be reachable from the slices (using the {@link MemorySegment#session()} accessor), it might be possible for - * clients to compromise the API (e.g. by closing the session prematurely). To avoid leaking private memory sessions - * to untrusted clients, an API can instead return segments based on a non-closeable view of the session it created, as follows: - * - * {@snippet lang=java : - * MemorySession session = MemorySession.openConfined(); - * MemorySession nonCloseableSession = session.asNonCloseable(); - * MemorySegment segment = MemorySegment.allocateNative(100, nonCloseableSession); - * segment.session().close(); // throws - * session.close(); //ok - * } - * - * In other words, only the owner of the original {@code session} object can close the session. External clients can only - * access the non-closeable session, and have no access to the underlying API session. - * - * @implSpec - * Implementations of this interface are thread-safe. - * - * @see MemorySegment - * @see SymbolLookup - * @see Linker - * @see VaList - * - * @since 19 - */ -@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) -public sealed interface MemorySession extends AutoCloseable, SegmentAllocator permits MemorySessionImpl, MemorySessionImpl.NonCloseableView { - - /** - * {@return {@code true}, if this memory session is alive} - */ - boolean isAlive(); - - /** - * {@return {@code true}, if this session is a closeable memory session}. - */ - boolean isCloseable(); - - /** - * {@return the owner thread associated with this memory session, or {@code null} if this session is shared - * across multiple threads} - */ - Thread ownerThread(); - - /** - * Runs a critical action while this memory session is kept alive. - * @param action the action to be run. - */ - void whileAlive(Runnable action); - - /** - * Adds a custom cleanup action which will be executed when the memory session is closed. - * The order in which custom cleanup actions are invoked once the memory session is closed is unspecified. - * @apiNote The provided action should not keep a strong reference to this memory session, so that implicitly - * closed sessions can be handled correctly by a {@link Cleaner} instance. - * @param runnable the custom cleanup action to be associated with this memory session. - * @throws IllegalStateException if this memory session is not {@linkplain #isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread - * {@linkplain #ownerThread() owning} this memory session. - */ - void addCloseAction(Runnable runnable); - - /** - * Closes this memory session. If this operation completes without exceptions, this session - * will be marked as not alive, the {@linkplain #addCloseAction(Runnable) close actions} associated - * with this session will be executed, and all the resources associated with this session will be released. - * - * @apiNote This operation is not idempotent; that is, closing an already closed memory session always results in an - * exception being thrown. This reflects a deliberate design choice: memory session state transitions should be - * manifest in the client code; a failure in any of these transitions reveals a bug in the underlying application - * logic. - * - * @see MemorySession#isAlive() - * - * @throws IllegalStateException if this memory session is not {@linkplain #isAlive() alive}. - * @throws IllegalStateException if this session is {@linkplain #whileAlive(Runnable) kept alive} by another client. - * @throws WrongThreadException if this method is called from a thread other than the thread - * {@linkplain #ownerThread() owning} this memory session. - * @throws UnsupportedOperationException if this memory session is not {@linkplain #isCloseable() closeable}. - */ - void close(); - - /** - * Returns a non-closeable view of this memory session. If this session is {@linkplain #isCloseable() non-closeable}, - * this session is returned. Otherwise, this method returns a non-closeable view of this memory session. - * @apiNote a non-closeable view of a memory session {@code S} keeps {@code S} reachable. As such, {@code S} - * cannot be closed implicitly (e.g. by a {@link Cleaner}) as long as one or more non-closeable views of {@code S} - * are reachable. - * @return a non-closeable view of this memory session. - */ - MemorySession asNonCloseable(); - - /** - * Compares the specified object with this memory session for equality. Returns {@code true} if and only if the specified - * object is also a memory session, and it refers to the same memory session as this memory session. - * {@linkplain #asNonCloseable() A non-closeable view} {@code V} of a memory session {@code S} is considered - * equal to {@code S}. - * - * @param that the object to be compared for equality with this memory session. - * @return {@code true} if the specified object is equal to this memory session. - */ - @Override - boolean equals(Object that); - - /** - * {@return the hash code value for this memory session} - */ - @Override - int hashCode(); - - /** - * Allocates a native segment, using this session. Equivalent to the following code: - * {@snippet lang=java : - * MemorySegment.allocateNative(size, align, this); - * } - * - * @throws IllegalStateException if this memory session is not {@linkplain #isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread - * {@linkplain #ownerThread() owning} this memory session. - * @return a new native segment, associated with this session. - */ - @Override - default MemorySegment allocate(long bytesSize, long bytesAlignment) { - return MemorySegment.allocateNative(bytesSize, bytesAlignment, this); - } - - /** - * Creates a closeable confined memory session. - * @return a new closeable confined memory session. - */ - static MemorySession openConfined() { - return MemorySessionImpl.createConfined(Thread.currentThread(), null); - } - - /** - * Creates a closeable confined memory session, managed by the provided cleaner instance. - * @param cleaner the cleaner to be associated with the returned memory session. - * @return a new closeable confined memory session, managed by {@code cleaner}. - */ - static MemorySession openConfined(Cleaner cleaner) { - Objects.requireNonNull(cleaner); - return MemorySessionImpl.createConfined(Thread.currentThread(), cleaner); - } - - /** - * Creates a closeable shared memory session. - * @return a new closeable shared memory session. - */ - static MemorySession openShared() { - return MemorySessionImpl.createShared(null); - } - - /** - * Creates a closeable shared memory session, managed by the provided cleaner instance. - * @param cleaner the cleaner to be associated with the returned memory session. - * @return a new closeable shared memory session, managed by {@code cleaner}. - */ - static MemorySession openShared(Cleaner cleaner) { - Objects.requireNonNull(cleaner); - return MemorySessionImpl.createShared(cleaner); - } - - /** - * Creates a non-closeable shared memory session, managed by a private {@link Cleaner} instance. - * Equivalent to (but likely more efficient than) the following code: - * {@snippet lang=java : - * openShared(Cleaner.create()).asNonCloseable(); - * } - * @return a non-closeable shared memory session, managed by a private {@link Cleaner} instance. - */ - static MemorySession openImplicit() { - return MemorySessionImpl.createImplicit(); - } - - /** - * Returns the global memory session. - * @return the global memory session. - */ - static MemorySession global() { - return MemorySessionImpl.GLOBAL; - } -} diff --git a/src/java.base/share/classes/java/lang/foreign/PaddingLayout.java b/src/java.base/share/classes/java/lang/foreign/PaddingLayout.java index 0a21c804ad1ad..47848641edcc7 100644 --- a/src/java.base/share/classes/java/lang/foreign/PaddingLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/PaddingLayout.java @@ -25,76 +25,30 @@ */ package java.lang.foreign; -import java.util.Objects; -import java.util.Optional; +import jdk.internal.foreign.layout.PaddingLayoutImpl; +import jdk.internal.javac.PreviewFeature; /** * A padding layout. A padding layout specifies the size of extra space which is typically not accessed by applications, * and is typically used for aligning member layouts around word boundaries. * * @implSpec - * This class is immutable, thread-safe and value-based. + * Implementing classes are immutable, thread-safe and value-based. + * + * @since 20 */ -/* package-private */ final class PaddingLayout extends AbstractLayout implements MemoryLayout { - - PaddingLayout(long size) { - this(size, 1, Optional.empty()); - } - - PaddingLayout(long size, long alignment, Optional name) { - super(size, alignment, name); - } - - @Override - public String toString() { - return decorateLayoutString("x" + bitSize()); - } - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } - if (!super.equals(other)) { - return false; - } - if (!(other instanceof PaddingLayout p)) { - return false; - } - return bitSize() == p.bitSize(); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), bitSize()); - } - - @Override - PaddingLayout dup(long alignment, Optional name) { - return new PaddingLayout(bitSize(), alignment, name); - } - - @Override - public boolean hasNaturalAlignment() { - return true; - } - - //hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout - //but that causes issues with javadoc, see JDK-8224052 +@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) +public sealed interface PaddingLayout extends MemoryLayout permits PaddingLayoutImpl { /** * {@inheritDoc} */ @Override - public PaddingLayout withName(String name) { - return (PaddingLayout)super.withName(name); - } + PaddingLayout withName(String name); /** * {@inheritDoc} */ @Override - public PaddingLayout withBitAlignment(long alignmentBits) { - return (PaddingLayout)super.withBitAlignment(alignmentBits); - } + PaddingLayout withBitAlignment(long bitAlignment); } diff --git a/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java b/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java index 57bd9e704daf0..e7a3c7efb5ebb 100644 --- a/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java +++ b/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java @@ -32,7 +32,8 @@ import java.util.Objects; import java.util.function.Function; import jdk.internal.foreign.AbstractMemorySegmentImpl; -import jdk.internal.foreign.ArenaAllocator; +import jdk.internal.foreign.MemorySessionImpl; +import jdk.internal.foreign.SlicingAllocator; import jdk.internal.foreign.Utils; import jdk.internal.javac.PreviewFeature; @@ -45,17 +46,17 @@ *

    * This interface also defines factories for commonly used allocators: *

      - *
    • {@link #newNativeArena(MemorySession)} creates a more efficient arena-style allocator, where off-heap memory - * is allocated in bigger blocks, which are then sliced accordingly to fit allocation requests;
    • - *
    • {@link #implicitAllocator()} obtains an allocator which allocates native memory segment in independent, - * {@linkplain MemorySession#openImplicit() implicit memory sessions}; and
    • + *
    • {@link #nativeAllocator(SegmentScope)} obtains a simple allocator which can + * be used to allocate native segments;
    • + *
    • {@link #slicingAllocator(MemorySegment)} obtains an efficient slicing allocator, where memory + * is allocated by repeatedly slicing the provided memory segment;
    • *
    • {@link #prefixAllocator(MemorySegment)} obtains 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 Linker#downcallHandle(FunctionDescriptor) downcall method handles} can accept an additional + * {@linkplain Linker#downcallHandle(FunctionDescriptor, Linker.Option...) downcall method handles} can accept an additional * {@link SegmentAllocator} parameter if the underlying foreign 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 foreign function. */ @@ -80,7 +81,7 @@ public interface SegmentAllocator { * @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. + * @return a new native segment containing the converted C string. */ default MemorySegment allocateUtf8String(String str) { Objects.requireNonNull(str); @@ -200,11 +201,11 @@ default MemorySegment allocate(ValueLayout.OfDouble layout, double value) { * @param value the value to be set on the newly allocated memory block. * @return a segment for the newly allocated memory block. */ - default MemorySegment allocate(ValueLayout.OfAddress layout, Addressable value) { + default MemorySegment allocate(ValueLayout.OfAddress layout, MemorySegment value) { Objects.requireNonNull(value); Objects.requireNonNull(layout); MemorySegment segment = allocate(layout); - layout.varHandle().set(segment, value.address()); + layout.varHandle().set(segment, value); return segment; } @@ -287,10 +288,8 @@ default MemorySegment allocateArray(ValueLayout.OfDouble elementLayout, double.. private MemorySegment copyArrayWithSwapIfNeeded(Z array, ValueLayout elementLayout, Function heapSegmentFactory) { - Objects.requireNonNull(array); - Objects.requireNonNull(elementLayout); - int size = Array.getLength(array); - MemorySegment addr = allocateArray(elementLayout, size); + int size = Array.getLength(Objects.requireNonNull(array)); + MemorySegment addr = allocateArray(Objects.requireNonNull(elementLayout), size); if (size > 0) { MemorySegment.copy(heapSegmentFactory.apply(array), elementLayout, 0, addr, elementLayout.withOrder(ByteOrder.nativeOrder()), 0, size); @@ -327,105 +326,39 @@ default MemorySegment allocateArray(MemoryLayout elementLayout, long count) { /** * Allocates a memory segment with the given size. - * @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. + * @implSpec the default implementation for this method calls {@code this.allocate(byteSize, 1)}. + * @param byteSize the size (in bytes) of the block of memory to be allocated. * @return a segment for the newly allocated memory block. - * @throws IllegalArgumentException if {@code bytesSize < 0} + * @throws IllegalArgumentException if {@code byteSize < 0} */ - default MemorySegment allocate(long bytesSize) { - return allocate(bytesSize, 1); + default MemorySegment allocate(long byteSize) { + return allocate(byteSize, 1); } /** - * Allocates a memory segment with the 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. + * Allocates a memory segment with the given size and alignment constraint. + * @param byteSize the size (in bytes) of the block of memory to be allocated. + * @param byteAlignment the alignment (in bytes) of the block of memory to be allocated. * @return a segment for the newly allocated memory block. - * @throws IllegalArgumentException if {@code bytesSize < 0}, {@code alignmentBytes <= 0}, + * @throws IllegalArgumentException if {@code byteSize < 0}, {@code byteAlignment <= 0}, * or if {@code alignmentBytes} is not a power of 2. */ - MemorySegment allocate(long bytesSize, long bytesAlignment); - - /** - * Creates an unbounded arena-based allocator used to allocate native memory segments. - * The returned allocator features a predefined block size and maximum arena size, and the segments it allocates - * are associated with the provided memory session. Equivalent to the following code: - * {@snippet lang=java : - * SegmentAllocator.newNativeArena(Long.MAX_VALUE, predefinedBlockSize, session); - * } - * - * @param session the memory session associated with the segments allocated by the arena-based allocator. - * @return a new unbounded arena-based allocator - * @throws IllegalStateException if {@code session} is not {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread - * {@linkplain MemorySession#ownerThread() owning} {@code session}. - */ - static SegmentAllocator newNativeArena(MemorySession session) { - return newNativeArena(Long.MAX_VALUE, ArenaAllocator.DEFAULT_BLOCK_SIZE, session); - } - - /** - * Creates an arena-based allocator used to allocate native memory segments. - * The returned allocator features a block size set to the specified arena size, and the native segments - * it allocates are associated with the provided memory session. Equivalent to the following code: - * {@snippet lang=java : - * SegmentAllocator.newNativeArena(arenaSize, arenaSize, session); - * } - * - * @param arenaSize the size (in bytes) of the allocation arena. - * @param session the memory session associated with the segments allocated by the arena-based allocator. - * @return a new unbounded arena-based allocator - * @throws IllegalArgumentException if {@code arenaSize <= 0}. - * @throws IllegalStateException if {@code session} is not {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread - * {@linkplain MemorySession#ownerThread() owning} {@code session}. - */ - static SegmentAllocator newNativeArena(long arenaSize, MemorySession session) { - return newNativeArena(arenaSize, arenaSize, session); - } + MemorySegment allocate(long byteSize, long byteAlignment); /** - * Creates an arena-based allocator used to allocate native memory segments. The returned allocator features - * the given block size {@code B} and the given arena size {@code A}, and the native segments - * it allocates are associated with the provided memory session. + * Returns a segment allocator which responds to allocation requests by returning consecutive slices + * obtained from the provided segment. Each new allocation request will return a new slice starting at the + * current offset (modulo additional padding to satisfy alignment constraint), with given size. *

    - * The allocator arena is first initialized by {@linkplain MemorySegment#allocateNative(long, MemorySession) allocating} a - * native memory segment {@code S} of size {@code B}. The allocator 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'}, with size {@code B}, - * 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'}, - * 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. - *

    - * The returned allocator might throw an {@link OutOfMemoryError} if the total memory allocated with this allocator - * exceeds the arena size {@code A}, or the system capacity. Furthermore, the returned allocator is not thread safe. - * Concurrent allocation needs to be guarded with synchronization primitives. + * When the returned allocator cannot satisfy an allocation request, e.g. because a slice of the provided + * segment with the requested size cannot be found, an {@link IndexOutOfBoundsException} is thrown. * - * @param arenaSize the size (in bytes) of the allocation arena. - * @param blockSize the block size associated with the arena-based allocator. - * @param session the memory session 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 session} is not {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread - * {@linkplain MemorySession#ownerThread() owning} {@code session}. + * @param segment the segment which the returned allocator should slice from. + * @return a new slicing allocator */ - static SegmentAllocator newNativeArena(long arenaSize, long blockSize, MemorySession session) { - Objects.requireNonNull(session); - 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, session); + static SegmentAllocator slicingAllocator(MemorySegment segment) { + Objects.requireNonNull(segment); + return new SlicingAllocator(segment); } /** @@ -449,24 +382,33 @@ static SegmentAllocator newNativeArena(long arenaSize, long blockSize, MemorySes * @return an allocator which recycles an existing segment upon each new allocation request. */ static SegmentAllocator prefixAllocator(MemorySegment segment) { - Objects.requireNonNull(segment); - return (AbstractMemorySegmentImpl)segment; + return (AbstractMemorySegmentImpl)Objects.requireNonNull(segment); } /** - * Returns an allocator which allocates native segments in independent {@linkplain MemorySession#openImplicit() implicit memory sessions}. - * Equivalent to (but likely more efficient than) the following code: - * {@snippet lang=java : - * SegmentAllocator implicitAllocator = (size, align) -> MemorySegment.allocateNative(size, align, MemorySession.openImplicit()); + * Simple allocator used to allocate native segments. The returned allocator responds to an allocation request by + * returning a native segment backed by a fresh off-heap region of memory, with given byte size and alignment constraint. + *

    + * Each native segment obtained by the returned allocator is associated with the provided scope. As such, + * the off-heap region which backs the returned segment is freed when the scope becomes not + * {@linkplain SegmentScope#isAlive() alive}. + *

    + * The {@link MemorySegment#address()} of the native segments obtained by the returned allocator is the starting address of + * the newly allocated off-heap memory region backing the segment. Moreover, the {@linkplain MemorySegment#address() address} + * of the native segment will be aligned according the provided alignment constraint. + *

    + * The off-heap region of memory backing a native segment obtained by the returned allocator is initialized to zero. + *

    + * This is equivalent to the following code: + * {@snippet lang = java: + * SegmentAllocator nativeAllocator = (byteSize, byteAlignment) -> + * MemorySegment.allocateNative(byteSize, byteAlignment, scope); * } - * - * @return an allocator which allocates native segments in independent {@linkplain MemorySession#openImplicit() implicit memory sessions}. + * @param scope the scope associated with the segments returned by the native allocator. + * @return a simple allocator used to allocate native segments. */ - static SegmentAllocator implicitAllocator() { - class Holder { - static final SegmentAllocator IMPLICIT_ALLOCATOR = (size, align) -> - MemorySegment.allocateNative(size, align, MemorySession.openImplicit()); - } - return Holder.IMPLICIT_ALLOCATOR; + static SegmentAllocator nativeAllocator(SegmentScope scope) { + Objects.requireNonNull(scope); + return (MemorySessionImpl)scope; } } diff --git a/src/java.base/share/classes/java/lang/foreign/SegmentScope.java b/src/java.base/share/classes/java/lang/foreign/SegmentScope.java new file mode 100644 index 0000000000000..a85dec0fb2826 --- /dev/null +++ b/src/java.base/share/classes/java/lang/foreign/SegmentScope.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package java.lang.foreign; + +import jdk.internal.foreign.MemorySessionImpl; +import jdk.internal.javac.PreviewFeature; +import jdk.internal.ref.CleanerFactory; + +/** + * A segment scope controls access to memory segments. + *

    + * A memory segment can only be accessed while its scope is {@linkplain #isAlive() alive}. Moreover, + * depending on how the segment scope has been obtained, access might additionally be + * restricted to specific threads. + *

    + * The simplest segment scope is the {@linkplain SegmentScope#global() global scope}. The global scope + * is always alive. As a result, segments associated with the global scope are always accessible and their backing + * regions of memory are never deallocated. Moreover, memory segments associated with the global scope + * can be {@linkplain #isAccessibleBy(Thread) accessed} from any thread. + * {@snippet lang = java: + * MemorySegment segment = MemorySegment.allocateNative(100, SegmentScope.global()); + * ... + * // segment is never deallocated! + *} + *

    + * Alternatively, clients can obtain an {@linkplain SegmentScope#auto() automatic scope}, that is a segment + * scope that is managed, automatically, by the garbage collector. The regions of memory backing memory segments associated + * with an automatic scope are deallocated at some unspecified time after they become + * unreachable, as shown below: + * + * {@snippet lang = java: + * MemorySegment segment = MemorySegment.allocateNative(100, SegmentScope.auto()); + * ... + * segment = null; // the segment region becomes available for deallocation after this point + *} + * Memory segments associated with an automatic scope can also be {@linkplain #isAccessibleBy(Thread) accessed} from any thread. + *

    + * Finally, clients can obtain a segment scope from an existing {@linkplain Arena arena}, the arena scope. The regions of memory + * backing memory segments associated with an arena scope are deallocated when the arena is {@linkplain Arena#close() closed}. + * When this happens, the arena scope becomes not {@linkplain #isAlive() alive} and subsequent access operations on segments + * associated with the arena scope will fail {@link IllegalStateException}. + * + * {@snippet lang = java: + * MemorySegment segment = null; + * try (Arena arena = Arena.openConfined()) { + * segment = MemorySegment.allocateNative(100, arena.scope()); + * ... + * } // segment region deallocated here + * segment.get(ValueLayout.JAVA_BYTE, 0); // throws IllegalStateException + * } + * + * Which threads can {@link #isAccessibleBy(Thread) access} memory segments associated with an arena scope depends + * on the arena kind. For instance, segments associated with the scope of a {@linkplain Arena#openConfined() confined arena} + * can only be accessed by the thread that created the arena. Conversely, segments associated with the scope of + * {@linkplain Arena#openConfined() shared arena} can be accessed by any thread. + * + * @implSpec + * Implementations of this interface are thread-safe. + * + * @see Arena + * @see MemorySegment + * + * @since 20 + */ +@PreviewFeature(feature =PreviewFeature.Feature.FOREIGN) +sealed public interface SegmentScope permits MemorySessionImpl { + + /** + * Creates a new scope that is managed, automatically, by the garbage collector. + * Segments associated with the returned scope can be + * {@linkplain SegmentScope#isAccessibleBy(Thread) accessed} by any thread. + * + * @return a new scope that is managed, automatically, by the garbage collector. + */ + static SegmentScope auto() { + return MemorySessionImpl.createImplicit(CleanerFactory.cleaner()); + } + + /** + * Obtains the global scope. Segments associated with the global scope can be + * {@linkplain SegmentScope#isAccessibleBy(Thread) accessed} by any thread. + * + * @return the global scope. + */ + static SegmentScope global() { + return MemorySessionImpl.GLOBAL; + } + + /** + * {@return {@code true}, if this scope is alive} + */ + boolean isAlive(); + + /** + * {@return {@code true} if the provided thread can access and/or associate segments with this scope} + * @param thread the thread to be tested. + */ + boolean isAccessibleBy(Thread thread); + + /** + * Runs a critical action while this scope is kept alive. + * @param action the action to be run. + * @throws IllegalStateException if this scope is not {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code isAccessibleBy(T) == false}. + */ + void whileAlive(Runnable action); + +} diff --git a/src/java.base/share/classes/java/lang/foreign/SequenceLayout.java b/src/java.base/share/classes/java/lang/foreign/SequenceLayout.java index d25ba35ead1e3..e73cac56851c6 100644 --- a/src/java.base/share/classes/java/lang/foreign/SequenceLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/SequenceLayout.java @@ -25,9 +25,7 @@ */ package java.lang.foreign; -import java.util.Objects; -import java.util.Optional; - +import jdk.internal.foreign.layout.SequenceLayoutImpl; import jdk.internal.javac.PreviewFeature; /** @@ -55,46 +53,27 @@ * @since 19 */ @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) -public final class SequenceLayout extends AbstractLayout implements MemoryLayout { - - private final long elemCount; - private final MemoryLayout elementLayout; - - SequenceLayout(long elemCount, MemoryLayout elementLayout) { - this(elemCount, elementLayout, elementLayout.bitAlignment(), Optional.empty()); - } +public sealed interface SequenceLayout extends MemoryLayout permits SequenceLayoutImpl { - SequenceLayout(long elemCount, MemoryLayout elementLayout, long alignment, Optional name) { - super(Math.multiplyExact(elemCount, elementLayout.bitSize()), alignment, name); - this.elemCount = elemCount; - this.elementLayout = elementLayout; - } /** * {@return the element layout associated with this sequence layout} */ - public MemoryLayout elementLayout() { - return elementLayout; - } + MemoryLayout elementLayout(); /** * {@return the element count of this sequence layout} */ - public long elementCount() { - return elemCount; - } + long elementCount(); /** - * Returns a sequence layout with the same element layout, alignment constraints and name as this sequence layout, + * Returns a sequence layout with the same element layout, alignment constraint and name as this sequence layout, * but with the specified element count. * @param elementCount the new element count. * @return a sequence layout with the given element count. * @throws IllegalArgumentException if {@code elementCount < 0}. */ - public SequenceLayout withElementCount(long elementCount) { - AbstractLayout.checkSize(elementCount, true); - return new SequenceLayout(elementCount, elementLayout, alignment, name()); - } + SequenceLayout withElementCount(long elementCount); /** * Re-arrange the elements in this sequence layout into a multi-dimensional sequence layout. @@ -129,47 +108,7 @@ public SequenceLayout withElementCount(long elementCount) { * multiplying the element counts does not yield the same element count as the flattened projection of this * sequence layout. */ - public SequenceLayout reshape(long... elementCounts) { - Objects.requireNonNull(elementCounts); - if (elementCounts.length == 0) { - throw new IllegalArgumentException(); - } - SequenceLayout flat = flatten(); - long expectedCount = flat.elementCount(); - - long actualCount = 1; - int inferPosition = -1; - for (int i = 0 ; i < elementCounts.length ; i++) { - if (elementCounts[i] == -1) { - if (inferPosition == -1) { - inferPosition = i; - } else { - throw new IllegalArgumentException("Too many unspecified element counts"); - } - } else if (elementCounts[i] <= 0) { - throw new IllegalArgumentException("Invalid element count: " + elementCounts[i]); - } else { - actualCount = elementCounts[i] * actualCount; - } - } - - // infer an unspecified element count (if any) - if (inferPosition != -1) { - long inferredCount = expectedCount / actualCount; - elementCounts[inferPosition] = inferredCount; - actualCount = actualCount * inferredCount; - } - - if (actualCount != expectedCount) { - throw new IllegalArgumentException("Element counts do not match expected size: " + expectedCount); - } - - MemoryLayout res = flat.elementLayout(); - for (int i = elementCounts.length - 1 ; i >= 0 ; i--) { - res = MemoryLayout.sequenceLayout(elementCounts[i], res); - } - return (SequenceLayout)res; - } + SequenceLayout reshape(long... elementCounts); /** * Returns a flattened sequence layout. The element layout of the returned sequence layout @@ -187,66 +126,11 @@ public SequenceLayout reshape(long... elementCounts) { * @return a sequence layout with the same size as this layout (but, possibly, with different * element count), whose element layout is not a sequence layout. */ - public SequenceLayout flatten() { - long count = elementCount(); - MemoryLayout elemLayout = elementLayout(); - while (elemLayout instanceof SequenceLayout elemSeq) { - count = count * elemSeq.elementCount(); - elemLayout = elemSeq.elementLayout(); - } - return MemoryLayout.sequenceLayout(count, elemLayout); - } - - @Override - public String toString() { - return decorateLayoutString(String.format("[%s:%s]", - elemCount, elementLayout)); - } - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } - if (!super.equals(other)) { - return false; - } - return other instanceof SequenceLayout otherSeq && - elemCount == otherSeq.elemCount && - elementLayout.equals(otherSeq.elementLayout); - } + SequenceLayout flatten(); @Override - public int hashCode() { - return Objects.hash(super.hashCode(), elemCount, elementLayout); - } + SequenceLayout withName(String name); @Override - SequenceLayout dup(long alignment, Optional name) { - return new SequenceLayout(elementCount(), elementLayout, alignment, name); - } - - @Override - boolean hasNaturalAlignment() { - return alignment == elementLayout.bitAlignment(); - } - - //hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout - //but that causes issues with javadoc, see JDK-8224052 - - /** - * {@inheritDoc} - */ - @Override - public SequenceLayout withName(String name) { - return (SequenceLayout)super.withName(name); - } - - /** - * {@inheritDoc} - */ - @Override - public SequenceLayout withBitAlignment(long alignmentBits) { - return (SequenceLayout)super.withBitAlignment(alignmentBits); - } + SequenceLayout withBitAlignment(long bitAlignment); } diff --git a/src/java.base/share/classes/java/lang/foreign/StructLayout.java b/src/java.base/share/classes/java/lang/foreign/StructLayout.java new file mode 100644 index 0000000000000..b94b6a97d85e5 --- /dev/null +++ b/src/java.base/share/classes/java/lang/foreign/StructLayout.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package java.lang.foreign; + +import jdk.internal.foreign.layout.StructLayoutImpl; +import jdk.internal.javac.PreviewFeature; + +/** + * A group layout whose member layouts are laid out one after the other. + * + * @implSpec + * Implementing classes are immutable, thread-safe and value-based. + * + * @since 20 + */ +@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) +public sealed interface StructLayout extends GroupLayout permits StructLayoutImpl { + + @Override + StructLayout withName(String name); + + @Override + StructLayout withBitAlignment(long bitAlignment); +} diff --git a/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java b/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java index c6fb83b61a969..50575f6b106d4 100644 --- a/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java +++ b/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java @@ -42,35 +42,35 @@ import java.util.function.BiFunction; /** - * A symbol lookup is an object that may be used to retrieve the address of a symbol in one or more libraries. + * A symbol lookup retrieves the address of a symbol in one or more libraries. * A symbol is a named entity, such as a function or a global variable. *

    - * A symbol lookup is created with respect to a particular library (or libraries). Subsequently, the {@link SymbolLookup#lookup(String)} + * A symbol lookup is created with respect to a particular library (or libraries). Subsequently, the {@link SymbolLookup#find(String)} * method takes the name of a symbol and returns the address of the symbol in that library. *

    * The address of a symbol is modelled as a zero-length {@linkplain MemorySegment memory segment}. The segment can be used in different ways: *

      - *
    • It can be passed to a {@link Linker} to create a downcall method handle, which can then be used to call the foreign function at the segment's base address.
    • - *
    • It can be passed to an existing {@linkplain Linker#downcallHandle(FunctionDescriptor) downcall method handle}, as an argument to the underlying foreign function.
    • - *
    • It can be {@linkplain MemorySegment#set(ValueLayout.OfAddress, long, Addressable) stored} inside another memory segment.
    • - *
    • It can be used to dereference memory associated with a global variable (this might require - * {@link MemorySegment#ofAddress(MemoryAddress, long, MemorySession) resizing} the segment first).
    • + *
    • It can be passed to a {@link Linker} to create a downcall method handle, which can then be used to call the foreign function at the segment's address.
    • + *
    • It can be passed to an existing {@linkplain Linker#downcallHandle(FunctionDescriptor, Linker.Option...) downcall method handle}, as an argument to the underlying foreign function.
    • + *
    • It can be {@linkplain MemorySegment#set(ValueLayout.OfAddress, long, MemorySegment) stored} inside another memory segment.
    • + *
    • It can be used to access the region of memory backing a global variable (this might require + * {@link MemorySegment#ofAddress(long, long, SegmentScope) resizing} the segment first).
    • *
    * *

    Obtaining a symbol lookup

    * - * The factory methods {@link #libraryLookup(String, MemorySession)} and {@link #libraryLookup(Path, MemorySession)} + * The factory methods {@link #libraryLookup(String, SegmentScope)} and {@link #libraryLookup(Path, SegmentScope)} * create a symbol lookup for a library known to the operating system. The library is specified by either its name or a path. * The library is loaded if not already loaded. The symbol lookup, which is known as a library lookup, is associated - * with a {@linkplain MemorySession memory session}; when the session is {@linkplain MemorySession#close() closed}, the library is unloaded: + * with a {@linkplain SegmentScope scope}; when the scope becomes not {@link SegmentScope#isAlive()}, the library is unloaded: * - * {@snippet lang=java : - * try (MemorySession session = MemorySession.openConfined()) { - * SymbolLookup libGL = SymbolLookup.libraryLookup("libGL.so"); // libGL.so loaded here - * MemorySegment glGetString = libGL.lookup("glGetString").orElseThrow(); + * {@snippet lang = java: + * try (Arena arena = Arena.openConfined()) { + * SymbolLookup libGL = SymbolLookup.libraryLookup("libGL.so", arena.scope()); // libGL.so loaded here + * MemorySegment glGetString = libGL.find("glGetString").orElseThrow(); * ... * } // libGL.so unloaded here - * } + *} *

    * If a library was previously loaded through JNI, i.e., by {@link System#load(String)} * or {@link System#loadLibrary(String)}, then the library was also associated with a particular class loader. The factory @@ -80,7 +80,7 @@ * System.loadLibrary("GL"); // libGL.so loaded here * ... * SymbolLookup libGL = SymbolLookup.loaderLookup(); - * MemorySegment glGetString = libGL.lookup("glGetString").orElseThrow(); + * MemorySegment glGetString = libGL.find("glGetString").orElseThrow(); * } * * This symbol lookup, which is known as a loader lookup, is dynamic with respect to the libraries associated @@ -91,18 +91,18 @@ * by {@link System#load(String)} or {@link System#loadLibrary(String)}. A loader lookup does not expose symbols in libraries * that were loaded in the course of creating a library lookup: * - * {@snippet lang=java : - * libraryLookup("libGL.so", session).lookup("glGetString").isPresent(); // true - * loaderLookup().lookup("glGetString").isPresent(); // false - * } + * {@snippet lang = java: + * libraryLookup("libGL.so", scope).find("glGetString").isPresent(); // true + * loaderLookup().find("glGetString").isPresent(); // false + *} * * Note also that a library lookup for library {@code L} exposes symbols in {@code L} even if {@code L} was previously loaded * through JNI (the association with a class loader is immaterial to the library lookup): * - * {@snippet lang=java : + * {@snippet lang = java: * System.loadLibrary("GL"); // libGL.so loaded here - * libraryLookup("libGL.so", session).lookup("glGetString").isPresent(); // true - * } + * libraryLookup("libGL.so", scope).find("glGetString").isPresent(); // true + *} * *

    * Finally, each {@link Linker} provides a symbol lookup for libraries that are commonly used on the OS and processor @@ -110,11 +110,11 @@ * helps clients to quickly find addresses of well-known symbols. For example, a {@link Linker} for Linux/x64 might choose to * expose symbols in {@code libc} through the default lookup: * - * {@snippet lang=java : + * {@snippet lang = java: * Linker nativeLinker = Linker.nativeLinker(); * SymbolLookup stdlib = nativeLinker.defaultLookup(); - * MemorySegment malloc = stdlib.lookup("malloc").orElseThrow(); - * } + * MemorySegment malloc = stdlib.find("malloc").orElseThrow(); + *} */ @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) @FunctionalInterface @@ -123,9 +123,9 @@ public interface SymbolLookup { /** * Returns the address of the symbol with the given name. * @param name the symbol name. - * @return a zero-length memory segment whose base address indicates the address of the symbol, if found. + * @return a zero-length memory segment whose address indicates the address of the symbol, if found. */ - Optional lookup(String name); + Optional find(String name); /** * Returns a symbol lookup for symbols in the libraries associated with the caller's class loader. @@ -139,8 +139,8 @@ public interface SymbolLookup { *

    * Libraries associated with a class loader are unloaded when the class loader becomes * unreachable. The symbol lookup - * returned by this method is backed by a {@linkplain MemorySession#asNonCloseable() non-closeable}, shared memory - * session which keeps the caller's class loader reachable. Therefore, libraries associated with the caller's class + * returned by this method is backed by a scope that is always alive and which keeps the caller's + * class loader reachable. Therefore, libraries associated with the caller's class * loader are kept loaded (and their symbols available) as long as a loader lookup for that class loader is reachable. *

    * In cases where this method is called from a context where there is no caller frame on the stack @@ -158,24 +158,24 @@ static SymbolLookup loaderLookup() { ClassLoader loader = caller != null ? caller.getClassLoader() : ClassLoader.getSystemClassLoader(); - MemorySession loaderSession = (loader == null || loader instanceof BuiltinClassLoader) ? - MemorySession.global() : // builtin loaders never go away + SegmentScope loaderScope = (loader == null || loader instanceof BuiltinClassLoader) ? + SegmentScope.global() : // builtin loaders never go away MemorySessionImpl.heapSession(loader); return name -> { Objects.requireNonNull(name); JavaLangAccess javaLangAccess = SharedSecrets.getJavaLangAccess(); // note: ClassLoader::findNative supports a null loader - MemoryAddress addr = MemoryAddress.ofLong(javaLangAccess.findNative(loader, name)); - return addr == MemoryAddress.NULL ? + long addr = javaLangAccess.findNative(loader, name); + return addr == 0L ? Optional.empty() : - Optional.of(MemorySegment.ofAddress(addr, 0L, loaderSession)); + Optional.of(MemorySegment.ofAddress(addr, 0L, loaderScope)); }; } /** * Loads a library with the given name (if not already loaded) and creates a symbol lookup for symbols in that library. - * The library will be unloaded when the provided memory session is {@linkplain MemorySession#close() closed}, - * if no other library lookup is still using it. + * The library will be unloaded when the provided scope becomes + * not {@linkplain SegmentScope#isAlive() alive}, if no other library lookup is still using it. * @implNote The process of resolving a library name is OS-specific. For instance, in a POSIX-compliant OS, * the library name is resolved according to the specification of the {@code dlopen} function for that OS. * In Windows, the library name is resolved according to the specification of the {@code LoadLibrary} function. @@ -186,7 +186,7 @@ static SymbolLookup loaderLookup() { * restricted methods, and use safe and supported functionalities, where possible. * * @param name the name of the library in which symbols should be looked up. - * @param session the memory session which controls the library lifecycle. + * @param scope the scope associated with symbols obtained from the returned lookup. * @return a new symbol lookup suitable to find symbols in a library with the given name. * @throws IllegalArgumentException if {@code name} does not identify a valid library. * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option @@ -194,15 +194,15 @@ static SymbolLookup loaderLookup() { * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. */ @CallerSensitive - static SymbolLookup libraryLookup(String name, MemorySession session) { + static SymbolLookup libraryLookup(String name, SegmentScope scope) { Reflection.ensureNativeAccess(Reflection.getCallerClass(), SymbolLookup.class, "libraryLookup"); - return libraryLookup(name, RawNativeLibraries::load, session); + return libraryLookup(name, RawNativeLibraries::load, scope); } /** * Loads a library from the given path (if not already loaded) and creates a symbol lookup for symbols - * in that library. The library will be unloaded when the provided memory session is {@linkplain MemorySession#close() closed}, - * if no other library lookup is still using it. + * in that library. The library will be unloaded when the provided scope becomes + * not {@linkplain SegmentScope#isAlive() alive}, if no other library lookup is still using it. *

    * This method is restricted. * Restricted methods are unsafe, and, if used incorrectly, their use might crash @@ -212,7 +212,7 @@ static SymbolLookup libraryLookup(String name, MemorySession session) { * @implNote On Linux, the functionalities provided by this factory method and the returned symbol lookup are * implemented using the {@code dlopen}, {@code dlsym} and {@code dlclose} functions. * @param path the path of the library in which symbols should be looked up. - * @param session the memory session which controls the library lifecycle. + * @param scope the scope associated with symbols obtained from the returned lookup. * @return a new symbol lookup suitable to find symbols in a library with the given path. * @throws IllegalArgumentException if {@code path} does not point to a valid library. * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option @@ -220,22 +220,22 @@ static SymbolLookup libraryLookup(String name, MemorySession session) { * {@code ALL-UNNAMED} in case {@code M} is an unnamed module. */ @CallerSensitive - static SymbolLookup libraryLookup(Path path, MemorySession session) { + static SymbolLookup libraryLookup(Path path, SegmentScope scope) { Reflection.ensureNativeAccess(Reflection.getCallerClass(), SymbolLookup.class, "libraryLookup"); - return libraryLookup(path, RawNativeLibraries::load, session); + return libraryLookup(path, RawNativeLibraries::load, scope); } - private static SymbolLookup libraryLookup(Z libDesc, BiFunction loadLibraryFunc, MemorySession session) { + private static SymbolLookup libraryLookup(Z libDesc, BiFunction loadLibraryFunc, SegmentScope libScope) { Objects.requireNonNull(libDesc); - Objects.requireNonNull(session); + Objects.requireNonNull(libScope); // attempt to load native library from path or name RawNativeLibraries nativeLibraries = RawNativeLibraries.newInstance(MethodHandles.lookup()); NativeLibrary library = loadLibraryFunc.apply(nativeLibraries, libDesc); if (library == null) { throw new IllegalArgumentException("Cannot open library: " + libDesc); } - // register hook to unload library when session is closed - MemorySessionImpl.toSessionImpl(session).addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() { + // register hook to unload library when 'libScope' becomes not alive + ((MemorySessionImpl) libScope).addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() { @Override public void cleanup() { nativeLibraries.unload(library); @@ -243,10 +243,10 @@ public void cleanup() { }); return name -> { Objects.requireNonNull(name); - MemoryAddress addr = MemoryAddress.ofLong(library.find(name)); - return addr == MemoryAddress.NULL - ? Optional.empty() : - Optional.of(MemorySegment.ofAddress(addr, 0L, session)); + long addr = library.find(name); + return addr == 0L ? + Optional.empty() : + Optional.of(MemorySegment.ofAddress(addr, 0, libScope)); }; } } diff --git a/src/java.base/share/classes/java/lang/foreign/UnionLayout.java b/src/java.base/share/classes/java/lang/foreign/UnionLayout.java new file mode 100644 index 0000000000000..155f47bb4df84 --- /dev/null +++ b/src/java.base/share/classes/java/lang/foreign/UnionLayout.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package java.lang.foreign; + +import jdk.internal.foreign.layout.UnionLayoutImpl; +import jdk.internal.javac.PreviewFeature; + +/** + * A group layout whose member layouts are laid out at the same starting offset. + * + * @implSpec + * Implementing classes are immutable, thread-safe and value-based. + * + * @since 20 + */ +@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) +public sealed interface UnionLayout extends GroupLayout permits UnionLayoutImpl { + + /** + * {@inheritDoc} + */ + @Override + UnionLayout withName(String name); + + /** + * {@inheritDoc} + */ + @Override + UnionLayout withBitAlignment(long bitAlignment); +} diff --git a/src/java.base/share/classes/java/lang/foreign/VaList.java b/src/java.base/share/classes/java/lang/foreign/VaList.java index 209ae0f878807..b366a4f8c42c8 100644 --- a/src/java.base/share/classes/java/lang/foreign/VaList.java +++ b/src/java.base/share/classes/java/lang/foreign/VaList.java @@ -39,10 +39,40 @@ import jdk.internal.reflect.Reflection; /** - * A variable argument list, similar in functionality to a C {@code va_list}. + * Helper class to create and manipulate variable argument lists, 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 Linker#downcallHandle(FunctionDescriptor) downcall method handle}. + * A variable argument list can be created using the {@link #make(Consumer, SegmentScope)} factory, as follows: + * {@snippet lang = java: + * VaList vaList = VaList.make(builder -> + * builder.addVarg(C_INT, 42) + * .addVarg(C_DOUBLE, 3.8d)); + *} + * Once created, clients can obtain the platform-dependent {@linkplain #segment() memory segment} associated with a variable + * argument list, which can then be passed to {@linkplain Linker#downcallHandle(FunctionDescriptor, Linker.Option...) downcall method handles} + * targeting native functions using the C {@code va_list} type. + *

    + * The contents of a foreign memory segment modelling a variable argument list can be accessed by unsafely creating + * a variable argument list, as follows: + * {@snippet lang = java: + * void upcall(int n, MemorySegment vaListSegment) { + * try (Arena arena = Arena.openConfined()) { + * VaList vaList = VaList.ofAddress(vaListSegment.address(), arena.scope()); + * VaList copy = vaList.copy(); + * int i = vaList.nextVarg(C_INT); + * double d = vaList.nextVarg(C_DOUBLE); + * // and again + * int i = copy.nextVarg(C_INT); + * double d = copy.nextVarg(C_DOUBLE); + * } + * } + *} + * The above method receives a foreign segment modelling a variable argument list; the contents of the segment are accessed by creating + * a new variable argument list, from the segment address. Note that the variable argument list is first copied into + * a second list before any element is accessed: this will allow us to iterate through the elements twice. Elements in + * the variable argument list are accessed using {@link #nextVarg(ValueLayout.OfInt)} and + * {@link #nextVarg(ValueLayout.OfDouble)}. These methods (as well as other access methods in the {@link VaList} class) + * take the layout of the element that needs to be accessed and perform all the necessary alignment checks as well + * as endianness conversions. *

    * Per the C specification (see C99 standard 6.5.2.2 Function calls - item 6), * arguments to variadic calls are erased by way of 'default argument promotions', @@ -57,22 +87,17 @@ *

    * Whether this detection succeeds depends on the factory method used to create the variable argument list: *

      - *
    • Variable argument lists created safely, using {@link #make(Consumer, MemorySession)} are capable of detecting out-of-bounds reads;
    • - *
    • Variable argument lists created unsafely, using {@link #ofAddress(MemoryAddress, MemorySession)} are not capable of detecting out-of-bounds reads
    • + *
    • Variable argument lists created safely, using {@link #make(Consumer, SegmentScope)} are capable of detecting out-of-bounds reads;
    • + *
    • Variable argument lists created unsafely, using {@link #ofAddress(long, SegmentScope)} are not capable of detecting out-of-bounds reads
    • *
    *

    * This class is not thread safe, and all accesses should occur within a single thread - * (regardless of the memory session associated with the variable arity list). + * (regardless of the scope used to obtain the variable arity list). * * @since 19 */ @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) -sealed public interface VaList extends Addressable permits WinVaList, SysVVaList, LinuxAArch64VaList, MacOsAArch64VaList, SharedUtils.EmptyVaList { - - /** - * {@return the memory session associated with this variable argument list} - */ - MemorySession session(); +public sealed interface VaList permits WinVaList, SysVVaList, LinuxAArch64VaList, MacOsAArch64VaList, SharedUtils.EmptyVaList { /** * Reads the next value as an {@code int} and advances this variable argument list's position. The behavior of this @@ -80,10 +105,10 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList * * @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 {@linkplain #session() session} associated with this variable argument list is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this variable argument list. + * @throws IllegalStateException if the scope associated with this variable argument list is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code segment().scope().isAccessibleBy(T) == false}. * @throws NoSuchElementException if an out-of-bounds read is detected. */ int nextVarg(ValueLayout.OfInt layout); @@ -94,10 +119,10 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList * * @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 {@linkplain #session() session} associated with this variable argument list is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this variable argument list. + * @throws IllegalStateException if the scope associated with this variable argument list is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code segment().scope().isAccessibleBy(T) == false}. * @throws NoSuchElementException if an out-of-bounds read is detected. */ long nextVarg(ValueLayout.OfLong layout); @@ -108,32 +133,37 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList * * @param layout the layout of the value * @return the {@code double} value read from this variable argument list. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this variable argument list is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this variable argument list. + * @throws IllegalStateException if the scope associated with this variable argument list is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code segment().scope().isAccessibleBy(T) == false}. * @throws NoSuchElementException if an out-of-bounds read is detected. */ double nextVarg(ValueLayout.OfDouble layout); /** - * Reads the next value as a {@code MemoryAddress} and advances this variable argument list's position. The behavior of this - * method is equivalent to the C {@code va_arg} function. + * Reads the next address value, wraps it into a native segment, and advances this variable argument list's position. + * The behavior of this method is equivalent to the C {@code va_arg} function. The returned segment's base + * {@linkplain MemorySegment#address()} is set to the value read from the variable argument list, and the segment + * is associated with the {@linkplain SegmentScope#global() global scope}. Under normal conditions, the size of the returned + * segment is {@code 0}. However, if the provided layout is an {@linkplain ValueLayout.OfAddress#asUnbounded() unbounded} + * address layout, then the size of the returned segment is {@code Long.MAX_VALUE}. * * @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 {@linkplain #session() session} associated with this variable argument list is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this variable argument list. + * @return a native segment whose {@linkplain MemorySegment#address() address} is the value read from + * this variable argument list. + * @throws IllegalStateException if the scope associated with this variable argument list is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code segment().scope().isAccessibleBy(T) == false}. * @throws NoSuchElementException if an out-of-bounds read is detected. */ - MemoryAddress nextVarg(ValueLayout.OfAddress layout); + MemorySegment nextVarg(ValueLayout.OfAddress layout); /** - * Reads the next value as a {@code MemorySegment}, and advances this variable argument list's position. The behavior of this - * method is equivalent to the C {@code va_arg} function. The provided group layout must correspond to a C struct or union - * type. + * Reads the next composite value into a new {@code MemorySegment}, allocated with the provided allocator, + * and advances this variable argument list's position. The behavior of this method is equivalent to the C + * {@code va_arg} function. The provided group layout must correspond to a C struct or union type. *

    * How the value is read in the returned segment is ABI-dependent: calling this method on a group layout * with member layouts {@code L_1, L_2, ... L_n} is not guaranteed to be semantically equivalent to perform distinct @@ -145,10 +175,10 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList * @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 {@linkplain #session() session} associated with this variable argument list is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this variable argument list. + * @throws IllegalStateException if the scope associated with this variable argument list is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code segment().scope().isAccessibleBy(T) == false}. * @throws NoSuchElementException if an out-of-bounds read is detected. */ MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator); @@ -157,17 +187,17 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList * 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 {@linkplain #session() session} associated with this variable argument list is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this variable argument list. + * @throws IllegalStateException if the scope associated with this variable argument list is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code segment().scope().isAccessibleBy(T) == false}. * @throws NoSuchElementException if an out-of-bounds read is detected. */ void skip(MemoryLayout... layouts); /** * Copies this variable argument list at its current position into a new variable argument list associated - * with the same memory session as this variable argument list. The behavior of this method is equivalent to the C + * with the same scope as this variable argument list. The behavior of this method is equivalent to the C * {@code va_copy} function. *

    * Copying is useful to traverse the variable argument list elements, starting from the current position, @@ -175,59 +205,57 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList * traversed multiple times. * * @return a copy of this variable argument list. - * @throws IllegalStateException if the {@linkplain #session() session} associated with this variable argument list is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this variable argument list. + * @throws IllegalStateException if the scope associated with this variable argument list is not + * {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code segment().scope().isAccessibleBy(T) == false}. */ VaList copy(); /** - * {@return the {@linkplain MemoryAddress memory address} associated with this variable argument list} - * @throws IllegalStateException if the {@linkplain #session() session} associated with this variable argument list is not - * {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread owning - * the {@linkplain #session() session} associated with this variable argument list. + * Returns a zero-length {@linkplain MemorySegment memory segment} associated with this variable argument list. + * The contents of the returned memory segment are platform-dependent. Whether and how the contents of + * the returned segment are updated when iterating the contents of a variable argument list is also + * platform-dependent. + * @return a zero-length {@linkplain MemorySegment memory segment} associated with this variable argument list. */ - @Override - MemoryAddress address(); + MemorySegment segment(); /** - * Creates a variable argument list from a memory address pointing to an existing variable argument list, - * with the given memory session. + * Creates a variable argument list from the give address value and scope. The address is typically obtained + * by calling {@link MemorySegment#address()} on a foreign memory segment instance. The provided scope determines + * the lifecycle of the returned variable argument list: the returned variable argument list will no longer be accessible, + * and its associated off-heap memory region will be deallocated when the scope becomes not + * {@linkplain SegmentScope#isAlive() alive}. *

    * 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. * - * @implNote variable argument lists created using this method can not detect out-of-bounds reads. - * - * @param address a memory address pointing to an existing variable argument list. - * @param session the memory session 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 session} is not {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread - * {@linkplain MemorySession#ownerThread() owning} {@code session}. + * @param address the address of the variable argument list. + * @param scope the scope associated with the returned variable argument list. + * @return a new variable argument list backed by an off-heap region of memory starting at the given address value. + * @throws IllegalStateException if {@code scope} is not {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope.isAccessibleBy(T) == false}. * @throws UnsupportedOperationException if the underlying native platform is not supported. * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option * {@code --enable-native-access} is specified, but 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, MemorySession session) { + static VaList ofAddress(long address, SegmentScope scope) { Reflection.ensureNativeAccess(Reflection.getCallerClass(), VaList.class, "ofAddress"); - Objects.requireNonNull(address); - Objects.requireNonNull(session); - return SharedUtils.newVaListOfAddress(address, session); + Objects.requireNonNull(scope); + return SharedUtils.newVaListOfAddress(address, scope); } /** * Creates a variable argument list using a builder (see {@link Builder}), with the given - * memory session. - *

    - * If this method needs to allocate memory, such memory will be managed by the given - * memory session, and will be released when the memory session is {@linkplain MemorySession#close closed}. + * scope. The provided scope determines the lifecycle of the returned variable argument list: the + * returned variable argument list will no longer be accessible, and its associated off-heap memory region will be + * deallocated when the scope becomes not {@linkplain SegmentScope#isAlive() alive}. *

    * Note that when there are no elements added to the created va list, * this method will return the same as {@link #empty()}. @@ -236,23 +264,23 @@ static VaList ofAddress(MemoryAddress address, MemorySession session) { * * @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 session the memory session to be associated with the new variable arity list. + * @param scope the scope to be associated with the new variable arity list. * @return a new variable argument list. * @throws UnsupportedOperationException if the underlying native platform is not supported. - * @throws IllegalStateException if {@code session} is not {@linkplain MemorySession#isAlive() alive}. - * @throws WrongThreadException if this method is called from a thread other than the thread - * {@linkplain MemorySession#ownerThread() owning} {@code session}. + * @throws IllegalStateException if {@code scope} is not {@linkplain SegmentScope#isAlive() alive}. + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code scope.isAccessibleBy(T) == false}. */ - static VaList make(Consumer actions, MemorySession session) { + static VaList make(Consumer actions, SegmentScope scope) { Objects.requireNonNull(actions); - Objects.requireNonNull(session); - return SharedUtils.newVaList(actions, session); + Objects.requireNonNull(scope); + return SharedUtils.newVaList(actions, scope); } /** - * Returns an empty variable argument list, associated with the {@linkplain MemorySession#global() global} - * memory session. The resulting variable argument list does not contain any argument, and throws {@link UnsupportedOperationException} - * on all operations, except for {@link VaList#address()}, {@link VaList#copy()} and {@link VaList#session()}. + * Returns an empty variable argument list, associated with the {@linkplain SegmentScope#global() global scope}. + * The resulting variable argument list does not contain any argument, and throws {@link UnsupportedOperationException} + * on all operations, except for {@link VaList#segment()}, {@link VaList#copy()}. * @return an empty variable argument list. * @throws UnsupportedOperationException if the underlying native platform is not supported. */ @@ -296,16 +324,17 @@ sealed interface Builder permits WinVaList.Builder, SysVVaList.Builder, LinuxAAr Builder addVarg(ValueLayout.OfDouble layout, double value); /** - * Writes an {@code Addressable} value to the variable argument list being constructed. + * Writes the {@linkplain MemorySegment#address() address} of the provided native segment + * 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. + * @param segment the segment whose {@linkplain MemorySegment#address() address} is to be written. * @return this builder. */ - Builder addVarg(ValueLayout.OfAddress layout, Addressable value); + Builder addVarg(ValueLayout.OfAddress layout, MemorySegment segment); /** - * Writes a {@code MemorySegment} value, with the given layout, to the variable argument list being constructed. + * Writes a {@code MemorySegment}, with the 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. diff --git a/src/java.base/share/classes/java/lang/foreign/ValueLayout.java b/src/java.base/share/classes/java/lang/foreign/ValueLayout.java index 0bbea651c38e8..f9a624195989e 100644 --- a/src/java.base/share/classes/java/lang/foreign/ValueLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/ValueLayout.java @@ -28,102 +28,49 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import jdk.internal.foreign.Utils; +import jdk.internal.foreign.layout.ValueLayouts; import jdk.internal.javac.PreviewFeature; -import jdk.internal.misc.Unsafe; -import jdk.internal.vm.annotation.ForceInline; -import jdk.internal.vm.annotation.Stable; -import sun.invoke.util.Wrapper; +import jdk.internal.reflect.CallerSensitive; /** - * 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, an alignment (in bits), + * A layout that models values of basic data types. Examples of values modelled by a value layout are + * integral values (either signed or unsigned), floating-point values and + * address values. + *

    + * Each value layout has a size, an alignment (in bits), * 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. + * {@linkplain MemorySegment#get(OfInt, long) accessing} a region of memory 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}. * - * @implSpec - * This class and its subclasses are immutable, thread-safe and value-based. + * @implSpec implementing classes and subclasses are immutable, thread-safe and value-based. * * @since 19 */ @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) -public sealed class ValueLayout extends AbstractLayout implements MemoryLayout { - - private final Class carrier; - private final ByteOrder order; - - 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(Class carrier, ByteOrder order, long size, long alignment, Optional name) { - super(size, alignment, name); - this.carrier = carrier; - this.order = order; - checkCarrierSize(carrier, size); - } +public sealed interface ValueLayout extends MemoryLayout { /** * {@return the value's byte order} */ - public ByteOrder order() { - return order; - } + ByteOrder order(); /** - * Returns a value layout with the same carrier, alignment constraints and name as this value layout, + * Returns a value layout with the same carrier, alignment constraint and name as this value layout, * but with the specified byte order. * * @param order the desired byte order. * @return a value layout with the given byte order. */ - public ValueLayout withOrder(ByteOrder order) { - return new ValueLayout(carrier, Objects.requireNonNull(order), bitSize(), alignment, name()); - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - char descriptor = carrier == MemoryAddress.class ? 'A' : carrier.descriptorString().charAt(0); - if (order == ByteOrder.LITTLE_ENDIAN) { - descriptor = Character.toLowerCase(descriptor); - } - return decorateLayoutString(String.format("%s%d", descriptor, bitSize())); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } - if (!super.equals(other)) { - return false; - } - return other instanceof ValueLayout otherValue && - carrier.equals(otherValue.carrier) && - order.equals(otherValue.order); - } + ValueLayout withOrder(ByteOrder order); /** - * Creates a strided access var handle that can be used to dereference a multi-dimensional array. The - * layout of this array is a sequence layout with {@code shape.length} nested sequence layouts. The element + * Creates a strided var handle that can be used to access a memory segment as multi-dimensional + * array. The layout of this array is a sequence layout with {@code shape.length} nested sequence layouts. The element * layout of the sequence layout at depth {@code shape.length} is this value layout. * As a result, if {@code shape.length == 0}, the array layout will feature only one dimension. *

    @@ -138,15 +85,15 @@ public boolean equals(Object other) { * * Can be used to access a multi-dimensional array whose layout is as follows: * - * {@snippet lang=java : - * SequenceLayout arrayLayout = MemoryLayout.sequenceLayout(-1, + * {@snippet lang = java: + * SequenceLayout arrayLayout = MemoryLayout.sequenceLayout( * MemoryLayout.sequenceLayout(10, * MemoryLayout.sequenceLayout(20, ValueLayout.JAVA_INT))); - * } + *} * * The resulting var handle {@code arrayHandle} will feature 3 coordinates of type {@code long}; each coordinate * is interpreted as an index into the corresponding sequence layout. If we refer to the var handle coordinates, from left - * to right, as {@code x}, {@code y} and {@code z} respectively, the final offset dereferenced by the var handle can be + * to right, as {@code x}, {@code y} and {@code z} respectively, the final offset accessed by the var handle can be * computed with the following formula: * *

    {@code
    @@ -170,7 +117,8 @@ public boolean equals(Object other) {
          * as the value for {@code z} is outside its specified bounds.
          *
          * @param shape the size of each nested array dimension.
    -     * @return a var handle which can be used to dereference a multi-dimensional array, featuring {@code shape.length + 1}
    +     * @return a var handle which can be used to access a memory segment as a multi-dimensional array,
    +     * featuring {@code shape.length + 1}
          * {@code long} coordinates.
          * @throws IllegalArgumentException if {@code shape[i] < 0}, for at least one index {@code i}.
          * @throws UnsupportedOperationException if {@code bitAlignment() > bitSize()}.
    @@ -178,504 +126,385 @@ public boolean equals(Object other) {
          * @see MemoryLayout#varHandle(PathElement...)
          * @see SequenceLayout
          */
    -    public VarHandle arrayElementVarHandle(int... shape) {
    -        Objects.requireNonNull(shape);
    -        MemoryLayout layout = this;
    -        List path = new ArrayList<>();
    -        for (int i = shape.length ; i > 0 ; i--) {
    -            int size = shape[i - 1];
    -            if (size < 0) throw new IllegalArgumentException("Invalid shape size: " + size);
    -            layout = MemoryLayout.sequenceLayout(size, layout);
    -            path.add(PathElement.sequenceElement());
    -        }
    -        layout = MemoryLayout.sequenceLayout(-1, layout);
    -        path.add(PathElement.sequenceElement());
    -        return layout.varHandle(path.toArray(new PathElement[0]));
    -    }
    +    VarHandle arrayElementVarHandle(int... shape);
     
         /**
          * {@return the carrier associated with this value layout}
          */
    -    public Class carrier() {
    -        return carrier;
    -    }
    +    Class carrier();
     
         /**
          * {@inheritDoc}
          */
         @Override
    -    public int hashCode() {
    -        return Objects.hash(super.hashCode(), order, carrier);
    -    }
    -
    -    @Override
    -    ValueLayout dup(long alignment, Optional name) {
    -        return new ValueLayout(carrier, order, bitSize(), alignment, name());
    -    }
    -
    -    //hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
    -    //but that causes issues with javadoc, see JDK-8224052
    -
    -    /**
    -     * {@inheritDoc}
    -     */
    -    @Override
    -    public ValueLayout withName(String name) {
    -        return (ValueLayout)super.withName(name);
    -    }
    +    ValueLayout withName(String name);
     
         /**
          * {@inheritDoc}
          */
         @Override
    -    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.makeSegmentViewVarHandle(this);
    -        }
    -        return handle;
    -    }
    +    ValueLayout withBitAlignment(long bitAlignment);
     
         /**
          * A value layout whose carrier is {@code boolean.class}.
          *
    +     * @see #JAVA_BOOLEAN
          * @since 19
          */
    -    @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
    -    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);
    -        }
    +    @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
    +    sealed interface OfBoolean extends ValueLayout permits ValueLayouts.OfBooleanImpl {
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        OfBoolean dup(long alignment, Optional name) {
    -            return new OfBoolean(order(), alignment, name);
    -        }
    +        OfBoolean withName(String name);
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        public OfBoolean withName(String name) {
    -            return (OfBoolean)super.withName(name);
    -        }
    +        OfBoolean withBitAlignment(long bitAlignment);
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        public OfBoolean withBitAlignment(long alignmentBits) {
    -            return (OfBoolean)super.withBitAlignment(alignmentBits);
    -        }
    +        OfBoolean withOrder(ByteOrder order);
     
    -        @Override
    -        public OfBoolean withOrder(ByteOrder order) {
    -            Objects.requireNonNull(order);
    -            return new OfBoolean(order, alignment, name());
    -        }
         }
     
         /**
          * A value layout whose carrier is {@code byte.class}.
          *
    +     * @see #JAVA_BYTE
          * @since 19
          */
    -    @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
    -    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);
    -        }
    +    @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
    +    sealed interface OfByte extends ValueLayout permits ValueLayouts.OfByteImpl {
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        OfByte dup(long alignment, Optional name) {
    -            return new OfByte(order(), alignment, name);
    -        }
    +        OfByte withName(String name);
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        public OfByte withName(String name) {
    -            return (OfByte)super.withName(name);
    -        }
    +        OfByte withBitAlignment(long bitAlignment);
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        public OfByte withBitAlignment(long alignmentBits) {
    -            return (OfByte)super.withBitAlignment(alignmentBits);
    -        }
    +        OfByte withOrder(ByteOrder order);
     
    -        @Override
    -        public OfByte withOrder(ByteOrder order) {
    -            Objects.requireNonNull(order);
    -            return new OfByte(order, alignment, name());
    -        }
         }
     
         /**
          * A value layout whose carrier is {@code char.class}.
          *
    +     * @see #JAVA_CHAR
    +     * @see #JAVA_CHAR_UNALIGNED
          * @since 19
          */
    -    @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
    -    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);
    -        }
    +    @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
    +    sealed interface OfChar extends ValueLayout permits ValueLayouts.OfCharImpl {
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        OfChar dup(long alignment, Optional name) {
    -            return new OfChar(order(), alignment, name);
    -        }
    +        OfChar withName(String name);
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        public OfChar withName(String name) {
    -            return (OfChar)super.withName(name);
    -        }
    +        OfChar withBitAlignment(long bitAlignment);
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        public OfChar withBitAlignment(long alignmentBits) {
    -            return (OfChar)super.withBitAlignment(alignmentBits);
    -        }
    +        OfChar withOrder(ByteOrder order);
     
    -        @Override
    -        public OfChar withOrder(ByteOrder order) {
    -            Objects.requireNonNull(order);
    -            return new OfChar(order, alignment, name());
    -        }
         }
     
         /**
          * A value layout whose carrier is {@code short.class}.
          *
    +     * @see #JAVA_SHORT
    +     * @see #JAVA_SHORT_UNALIGNED
          * @since 19
          */
    -    @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
    -    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);
    -        }
    +    @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
    +    sealed interface OfShort extends ValueLayout permits ValueLayouts.OfShortImpl {
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        OfShort dup(long alignment, Optional name) {
    -            return new OfShort(order(), alignment, name);
    -        }
    +        OfShort withName(String name);
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        public OfShort withName(String name) {
    -            return (OfShort)super.withName(name);
    -        }
    +        OfShort withBitAlignment(long bitAlignment);
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        public OfShort withBitAlignment(long alignmentBits) {
    -            return (OfShort)super.withBitAlignment(alignmentBits);
    -        }
    +        OfShort withOrder(ByteOrder order);
     
    -        @Override
    -        public OfShort withOrder(ByteOrder order) {
    -            Objects.requireNonNull(order);
    -            return new OfShort(order, alignment, name());
    -        }
         }
     
         /**
          * A value layout whose carrier is {@code int.class}.
          *
    +     * @see #JAVA_INT
    +     * @see #JAVA_INT_UNALIGNED
          * @since 19
          */
    -    @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
    -    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);
    -        }
    +    @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
    +    sealed interface OfInt extends ValueLayout permits ValueLayouts.OfIntImpl {
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        OfInt dup(long alignment, Optional name) {
    -            return new OfInt(order(), alignment, name);
    -        }
    +        OfInt withName(String name);
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        public OfInt withName(String name) {
    -            return (OfInt)super.withName(name);
    -        }
    +        OfInt withBitAlignment(long bitAlignment);
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        public OfInt withBitAlignment(long alignmentBits) {
    -            return (OfInt)super.withBitAlignment(alignmentBits);
    -        }
    +        OfInt withOrder(ByteOrder order);
     
    -        @Override
    -        public OfInt withOrder(ByteOrder order) {
    -            Objects.requireNonNull(order);
    -            return new OfInt(order, alignment, name());
    -        }
         }
     
         /**
          * A value layout whose carrier is {@code float.class}.
          *
    +     * @see #JAVA_FLOAT
    +     * @see #JAVA_FLOAT_UNALIGNED
          * @since 19
          */
    -    @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
    -    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);
    -        }
    +    @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
    +    sealed interface OfFloat extends ValueLayout permits ValueLayouts.OfFloatImpl {
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        OfFloat dup(long alignment, Optional name) {
    -            return new OfFloat(order(), alignment, name);
    -        }
    +        OfFloat withName(String name);
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        public OfFloat withName(String name) {
    -            return (OfFloat)super.withName(name);
    -        }
    +        OfFloat withBitAlignment(long bitAlignment);
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        public OfFloat withBitAlignment(long alignmentBits) {
    -            return (OfFloat)super.withBitAlignment(alignmentBits);
    -        }
    +        OfFloat withOrder(ByteOrder order);
     
    -        @Override
    -        public OfFloat withOrder(ByteOrder order) {
    -            Objects.requireNonNull(order);
    -            return new OfFloat(order, alignment, name());
    -        }
         }
     
         /**
          * A value layout whose carrier is {@code long.class}.
          *
    +     * @see #JAVA_LONG
    +     * @see #JAVA_LONG_UNALIGNED
          * @since 19
          */
    -    @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
    -    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);
    -        }
    +    @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
    +    sealed interface OfLong extends ValueLayout permits ValueLayouts.OfLongImpl {
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        OfLong dup(long alignment, Optional name) {
    -            return new OfLong(order(), alignment, name);
    -        }
    +        OfLong withName(String name);
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        public OfLong withName(String name) {
    -            return (OfLong)super.withName(name);
    -        }
    +        OfLong withBitAlignment(long bitAlignment);
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        public OfLong withBitAlignment(long alignmentBits) {
    -            return (OfLong)super.withBitAlignment(alignmentBits);
    -        }
    +        OfLong withOrder(ByteOrder order);
     
    -        @Override
    -        public OfLong withOrder(ByteOrder order) {
    -            Objects.requireNonNull(order);
    -            return new OfLong(order, alignment, name());
    -        }
         }
     
         /**
          * A value layout whose carrier is {@code double.class}.
          *
    +     * @see #JAVA_DOUBLE
    +     * @see #JAVA_DOUBLE_UNALIGNED
          * @since 19
          */
    -    @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
    -    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);
    -        }
    +    @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
    +    sealed interface OfDouble extends ValueLayout permits ValueLayouts.OfDoubleImpl {
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        OfDouble dup(long alignment, Optional name) {
    -            return new OfDouble(order(), alignment, name);
    -        }
    +        OfDouble withName(String name);
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        public OfDouble withName(String name) {
    -            return (OfDouble)super.withName(name);
    -        }
    +        OfDouble withBitAlignment(long bitAlignment);
     
    +        /**
    +         * {@inheritDoc}
    +         */
             @Override
    -        public OfDouble withBitAlignment(long alignmentBits) {
    -            return (OfDouble)super.withBitAlignment(alignmentBits);
    -        }
    +        OfDouble withOrder(ByteOrder order);
     
    -        @Override
    -        public OfDouble withOrder(ByteOrder order) {
    -            Objects.requireNonNull(order);
    -            return new OfDouble(order, alignment, name());
    -        }
         }
     
         /**
    -     * A value layout whose carrier is {@code MemoryAddress.class}.
    +     * A value layout whose carrier is {@code MemorySegment.class}.
          *
    +     * @see #ADDRESS
    +     * @see #ADDRESS_UNALIGNED
          * @since 19
          */
    -    @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
    -    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);
    -        }
    +    @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
    +    sealed interface OfAddress extends ValueLayout permits ValueLayouts.OfAddressImpl {
    +
    +        /**
    +         * {@inheritDoc}
    +         */
    +        @Override
    +        OfAddress withName(String name);
    +
    +        /**
    +         * {@inheritDoc}
    +         */
    +        @Override
    +        OfAddress withBitAlignment(long bitAlignment);
    +
    +        /**
    +         * {@inheritDoc}
    +         */
    +        @Override
    +        OfAddress withOrder(ByteOrder order);
    +
    +        /**
    +         * Returns an unbounded address layout with the same carrier, alignment constraint, name and order as this address layout,
    +         * but with the specified pointee layout. An unbounded address layout allow raw addresses to be accessed
    +         * as {@linkplain MemorySegment memory segments} whose size is set to {@link Long#MAX_VALUE}. As such,
    +         * these segments can be used in subsequent access operations.
    +         * 

    + * 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 an unbounded address layout with same characteristics as this layout. + * @see #isUnbounded() + */ + @CallerSensitive + OfAddress asUnbounded(); + + /** + * {@return {@code true}, if this address layout is an {@linkplain #asUnbounded() unbounded address layout}}. + */ + boolean isUnbounded(); - @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 ({@code size_t}), - * bit alignment set to {@code sizeof(size_t) * 8}, and byte order set to {@link ByteOrder#nativeOrder()}. + * bit alignment set to {@code sizeof(size_t) * 8}, byte order set to {@link ByteOrder#nativeOrder()}. * Equivalent to the following code: * {@snippet lang=java : - * MemoryLayout.valueLayout(MemoryAddress.class, ByteOrder.nativeOrder()) - * .withBitAlignment(

    ); + * MemoryLayout.valueLayout(MemorySegment.class, ByteOrder.nativeOrder()); * } */ - public static final OfAddress ADDRESS = new OfAddress(ByteOrder.nativeOrder()) - .withBitAlignment(ValueLayout.ADDRESS_SIZE_BITS); + OfAddress ADDRESS = ValueLayouts.OfAddressImpl.of(ByteOrder.nativeOrder()); /** * 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: * {@snippet lang=java : - * MemoryLayout.valueLayout(byte.class, ByteOrder.nativeOrder()).withBitAlignment(8); + * MemoryLayout.valueLayout(byte.class, ByteOrder.nativeOrder()); * } */ - public static final OfByte JAVA_BYTE = new OfByte(ByteOrder.nativeOrder()).withBitAlignment(8); + OfByte JAVA_BYTE = ValueLayouts.OfByteImpl.of(ByteOrder.nativeOrder()); /** * 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: * {@snippet lang=java : - * MemoryLayout.valueLayout(boolean.class, ByteOrder.nativeOrder()).withBitAlignment(8); + * MemoryLayout.valueLayout(boolean.class, ByteOrder.nativeOrder()); * } */ - public static final OfBoolean JAVA_BOOLEAN = new OfBoolean(ByteOrder.nativeOrder()).withBitAlignment(8); + OfBoolean JAVA_BOOLEAN = ValueLayouts.OfBooleanImpl.of(ByteOrder.nativeOrder()); /** * A value layout constant whose size is the same as that of a Java {@code char}, * bit alignment set to 16, and byte order set to {@link ByteOrder#nativeOrder()}. * Equivalent to the following code: * {@snippet lang=java : - * MemoryLayout.valueLayout(char.class, ByteOrder.nativeOrder()).withBitAlignment(16); + * MemoryLayout.valueLayout(char.class, ByteOrder.nativeOrder()); * } */ - public static final OfChar JAVA_CHAR = new OfChar(ByteOrder.nativeOrder()).withBitAlignment(16); + OfChar JAVA_CHAR = ValueLayouts.OfCharImpl.of(ByteOrder.nativeOrder()); /** * A value layout constant whose size is the same as that of a Java {@code short}, * bit alignment set to 16, and byte order set to {@link ByteOrder#nativeOrder()}. * Equivalent to the following code: * {@snippet lang=java : - * MemoryLayout.valueLayout(short.class, ByteOrder.nativeOrder()).withBitAlignment(16); + * MemoryLayout.valueLayout(short.class, ByteOrder.nativeOrder()); * } */ - public static final OfShort JAVA_SHORT = new OfShort(ByteOrder.nativeOrder()).withBitAlignment(16); + OfShort JAVA_SHORT = ValueLayouts.OfShortImpl.of(ByteOrder.nativeOrder()); /** * A value layout constant whose size is the same as that of a Java {@code int}, * bit alignment set to 32, and byte order set to {@link ByteOrder#nativeOrder()}. * Equivalent to the following code: * {@snippet lang=java : - * MemoryLayout.valueLayout(int.class, ByteOrder.nativeOrder()).withBitAlignment(32); + * MemoryLayout.valueLayout(int.class, ByteOrder.nativeOrder()); * } */ - public static final OfInt JAVA_INT = new OfInt(ByteOrder.nativeOrder()).withBitAlignment(32); + OfInt JAVA_INT = ValueLayouts.OfIntImpl.of(ByteOrder.nativeOrder()); /** * A value layout constant whose size is the same as that of a Java {@code long}, * bit alignment set to 64, and byte order set to {@link ByteOrder#nativeOrder()}. * Equivalent to the following code: * {@snippet lang=java : - * MemoryLayout.valueLayout(long.class, ByteOrder.nativeOrder()).withBitAlignment(64); + * MemoryLayout.valueLayout(long.class, ByteOrder.nativeOrder()); * } */ - public static final OfLong JAVA_LONG = new OfLong(ByteOrder.nativeOrder()) - .withBitAlignment(64); + OfLong JAVA_LONG = ValueLayouts.OfLongImpl.of(ByteOrder.nativeOrder()); /** * A value layout constant whose size is the same as that of a Java {@code float}, @@ -685,15 +514,100 @@ public OfAddress withOrder(ByteOrder order) { * MemoryLayout.valueLayout(float.class, ByteOrder.nativeOrder()).withBitAlignment(32); * } */ - public static final OfFloat JAVA_FLOAT = new OfFloat(ByteOrder.nativeOrder()).withBitAlignment(32); + OfFloat JAVA_FLOAT = ValueLayouts.OfFloatImpl.of(ByteOrder.nativeOrder()); /** * A value layout constant whose size is the same as that of a Java {@code double}, * bit alignment set to 64, and byte order set to {@link ByteOrder#nativeOrder()}. * Equivalent to the following code: * {@snippet lang=java : - * MemoryLayout.valueLayout(double.class, ByteOrder.nativeOrder()).withBitAlignment(64); + * MemoryLayout.valueLayout(double.class, ByteOrder.nativeOrder()); + * } + */ + OfDouble JAVA_DOUBLE = ValueLayouts.OfDoubleImpl.of(ByteOrder.nativeOrder()); + + /** + * An unaligned value layout constant whose size is the same as that of a machine address ({@code size_t}), + * and byte order set to {@link ByteOrder#nativeOrder()}. + * Equivalent to the following code: + * {@snippet lang=java : + * ADDRESS.withBitAlignment(8); + * } + * @apiNote Care should be taken when using unaligned value layouts as they may induce + * performance and portability issues. + */ + OfAddress ADDRESS_UNALIGNED = ADDRESS.withBitAlignment(8); + + /** + * An unaligned value layout constant whose size is the same as that of a Java {@code char} + * and byte order set to {@link ByteOrder#nativeOrder()}. + * Equivalent to the following code: + * {@snippet lang=java : + * JAVA_CHAR.withBitAlignment(8); * } + * @apiNote Care should be taken when using unaligned value layouts as they may induce + * performance and portability issues. */ - public static final OfDouble JAVA_DOUBLE = new OfDouble(ByteOrder.nativeOrder()).withBitAlignment(64); + OfChar JAVA_CHAR_UNALIGNED = JAVA_CHAR.withBitAlignment(8); + + /** + * An unaligned value layout constant whose size is the same as that of a Java {@code short} + * and byte order set to {@link ByteOrder#nativeOrder()}. + * Equivalent to the following code: + * {@snippet lang=java : + * JAVA_SHORT.withBitAlignment(8); + * } + * @apiNote Care should be taken when using unaligned value layouts as they may induce + * performance and portability issues. + */ + OfShort JAVA_SHORT_UNALIGNED = JAVA_SHORT.withBitAlignment(8); + + /** + * An unaligned value layout constant whose size is the same as that of a Java {@code int} + * and byte order set to {@link ByteOrder#nativeOrder()}. + * Equivalent to the following code: + * {@snippet lang=java : + * JAVA_INT.withBitAlignment(8); + * } + * @apiNote Care should be taken when using unaligned value layouts as they may induce + * performance and portability issues. + */ + OfInt JAVA_INT_UNALIGNED = JAVA_INT.withBitAlignment(8); + + /** + * An unaligned value layout constant whose size is the same as that of a Java {@code long} + * and byte order set to {@link ByteOrder#nativeOrder()}. + * Equivalent to the following code: + * {@snippet lang=java : + * JAVA_LONG.withBitAlignment(8); + * } + * @apiNote Care should be taken when using unaligned value layouts as they may induce + * performance and portability issues. + */ + OfLong JAVA_LONG_UNALIGNED = JAVA_LONG.withBitAlignment(8); + + /** + * An unaligned value layout constant whose size is the same as that of a Java {@code float} + * and byte order set to {@link ByteOrder#nativeOrder()}. + * Equivalent to the following code: + * {@snippet lang=java : + * JAVA_FLOAT.withBitAlignment(8); + * } + * @apiNote Care should be taken when using unaligned value layouts as they may induce + * performance and portability issues. + */ + OfFloat JAVA_FLOAT_UNALIGNED = JAVA_FLOAT.withBitAlignment(8); + + /** + * An unaligned value layout constant whose size is the same as that of a Java {@code double} + * and byte order set to {@link ByteOrder#nativeOrder()}. + * Equivalent to the following code: + * {@snippet lang=java : + * JAVA_DOUBLE.withBitAlignment(8); + * } + * @apiNote Care should be taken when using unaligned value layouts as they may induce + * performance and portability issues. + */ + OfDouble JAVA_DOUBLE_UNALIGNED = JAVA_DOUBLE.withBitAlignment(8); + } diff --git a/src/java.base/share/classes/java/lang/foreign/package-info.java b/src/java.base/share/classes/java/lang/foreign/package-info.java index 87cfc200735b6..020661b1c945f 100644 --- a/src/java.base/share/classes/java/lang/foreign/package-info.java +++ b/src/java.base/share/classes/java/lang/foreign/package-info.java @@ -31,29 +31,36 @@ * *

    * The main abstraction introduced to support foreign memory access is {@link java.lang.foreign.MemorySegment}, which - * models a contiguous memory region, residing either inside or outside the Java heap. The contents of a memory + * models a contiguous region of memory, residing either inside or outside the Java heap. The contents of a memory * segment can be described using a {@link java.lang.foreign.MemoryLayout memory layout}, which provides * basic operations to query sizes, offsets and alignment constraints. Memory layouts also provide - * an alternate, more abstract way, to dereference memory segments - * using {@linkplain java.lang.foreign.MemoryLayout#varHandle(java.lang.foreign.MemoryLayout.PathElement...) access var handles}, + * an alternate, more abstract way, to access memory segments + * using {@linkplain java.lang.foreign.MemoryLayout#varHandle(java.lang.foreign.MemoryLayout.PathElement...) var handles}, * which can be computed using layout paths. * - * For example, to allocate an off-heap memory region big enough to hold 10 values of the primitive type {@code int}, and fill it with values - * ranging from {@code 0} to {@code 9}, we can use the following code: + * For example, to allocate an off-heap region of memory big enough to hold 10 values of the primitive type {@code int}, + * and fill it with values ranging from {@code 0} to {@code 9}, we can use the following code: * - * {@snippet lang=java : - * MemorySegment segment = MemorySegment.allocateNative(10 * 4, MemorySession.openImplicit()); + * {@snippet lang = java: + * MemorySegment segment = MemorySegment.allocateNative(10 * 4, SegmentScope.auto()); * for (int i = 0 ; i < 10 ; i++) { * segment.setAtIndex(ValueLayout.JAVA_INT, i, i); * } - * } + *} * * 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}. + * The segment is associated with an {@linkplain java.lang.foreign.SegmentScope#auto() automatic scope}. This + * means that the off-heap region of memory backing the segment is managed, automatically, by the garbage collector. + * As such, the off-heap memory backing the native segment will be released at some unspecified + * point after the segment becomes unreachable. + * This is similar to what happens with direct buffers created via {@link java.nio.ByteBuffer#allocateDirect(int)}. + * It is also possible to manage the lifecycle of allocated native segments more directly, as shown in a later section. + *

    * Inside a loop, we then initialize the contents of the memory segment; note how the - * {@linkplain java.lang.foreign.MemorySegment#setAtIndex(ValueLayout.OfInt, long, int) dereference method} - * accepts a {@linkplain java.lang.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, + * {@linkplain java.lang.foreign.MemorySegment#setAtIndex(ValueLayout.OfInt, long, int) access method} + * accepts a {@linkplain java.lang.foreign.ValueLayout value layout}, which specifies the size, alignment constraint, + * byte order as well as the Java type ({@code int}, in this case) associated with the access 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}. @@ -64,35 +71,37 @@ * often crucial that the resources associated with a memory segment are released when the segment is no longer in use, * and in a timely fashion. For this reason, there might be cases where waiting for the garbage collector to determine that a segment * is unreachable is not optimal. - * Clients that operate under these assumptions might want to programmatically release the memory associated - * with a memory segment. This can be done, using the {@link java.lang.foreign.MemorySession} abstraction, as shown below: + * Clients that operate under these assumptions might want to programmatically release the memory backing a memory segment. + * This can be done, using the {@link java.lang.foreign.Arena} abstraction, as shown below: * - * {@snippet lang=java : - * try (MemorySession session = MemorySession.openConfined()) { - * MemorySegment segment = MemorySegment.allocateNative(10 * 4, session); + * {@snippet lang = java: + * try (Arena arena = Arena.openConfined()) { + * MemorySegment segment = arena.allocate(10 * 4); * for (int i = 0 ; i < 10 ; i++) { * segment.setAtIndex(ValueLayout.JAVA_INT, i, i); * } * } - * } + *} * - * This example is almost identical to the prior one; this time we first create a so called memory session, - * which is used to bind the life-cycle of the segment created immediately afterwards. Note the use of the - * try-with-resources construct: this idiom ensures that all the memory resources associated with the segment will be released - * at the end of the block, according to the semantics described in Section {@jls 14.20.3} of The Java Language Specification. + * This example is almost identical to the prior one; this time we first create an arena + * which is used to allocate multiple native segments which share the same life-cycle. That is, all the segments + * allocated by the arena will be associated with the same {@linkplain java.lang.foreign.SegmentScope scope}. + * Note the use of the try-with-resources construct: this idiom ensures that the off-heap region of memory backing the + * native segment will be released at the end of the block, according to the semantics described in Section {@jls 14.20.3} + * of The Java Language Specification. * *

    Safety

    * * This API provides strong safety guarantees when it comes to memory access. First, when dereferencing a memory segment, * the access coordinates are validated (upon access), to make sure that access does not occur at any address which resides - * outside the boundaries of the memory segment used by the dereference operation. We call this guarantee spatial safety; + * outside the boundaries of the memory segment used by the access operation. We call this guarantee spatial safety; * in other words, access to memory segments is bounds-checked, in the same way as array access is, as described in * Section {@jls 15.10.4} of The Java Language Specification. *

    - * Since memory segments can be closed (see above), segments are also validated (upon access) to make sure that - * the memory session associated with the segment being accessed has not been closed prematurely. + * Since memory segments created with an arena can become invalid (see above), segments are also validated (upon access) to make sure that + * the scope associated with the segment being accessed is still alive. * We call this guarantee temporal safety. Together, spatial and temporal safety ensure that each memory access - * operation either succeeds - and accesses a valid memory location - or fails. + * operation either succeeds - and accesses a valid location within the region of memory backing the memory segment - or fails. * *

    Foreign function access

    * The key abstractions introduced to support foreign function access are {@link java.lang.foreign.SymbolLookup}, @@ -105,134 +114,118 @@ * For example, to compute the length of a string using the C standard library function {@code strlen} on a Linux x64 platform, * we can use the following code: * - * {@snippet lang=java : + * {@snippet lang = java: * Linker linker = Linker.nativeLinker(); * SymbolLookup stdlib = linker.defaultLookup(); * MethodHandle strlen = linker.downcallHandle( - * stdlib.lookup("strlen").get(), + * stdlib.find("strlen").get(), * FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS) * ); * - * try (MemorySession session = MemorySession.openConfined()) { - * MemorySegment cString = MemorySegment.allocateNative(5 + 1, session); - * cString.setUtf8String(0, "Hello"); + * try (Arena arena = Arena.openConfined()) { + * MemorySegment cString = arena.allocateUtf8String("Hello"); * long len = (long)strlen.invoke(cString); // 5 * } - * } + *} * * Here, we obtain a {@linkplain java.lang.foreign.Linker#nativeLinker() native linker} and we use it - * to {@linkplain java.lang.foreign.SymbolLookup#lookup(java.lang.String) look up} the {@code strlen} symbol in the + * to {@linkplain java.lang.foreign.SymbolLookup#find(java.lang.String) look up} the {@code strlen} symbol in the * standard C library; a downcall method handle targeting said symbol is subsequently - * {@linkplain java.lang.foreign.Linker#downcallHandle(java.lang.foreign.FunctionDescriptor) obtained}. + * {@linkplain java.lang.foreign.Linker#downcallHandle(FunctionDescriptor, Linker.Option...) obtained}. * To complete the linking successfully, we must provide a {@link java.lang.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 ABI of the underlying platform. - * The {@link java.lang.foreign.MemorySegment} class also provides many useful methods for - * interacting with foreign code, such as converting Java strings - * {@linkplain java.lang.foreign.MemorySegment#setUtf8String(long, java.lang.String) into} zero-terminated, UTF-8 strings and - * {@linkplain java.lang.foreign.MemorySegment#getUtf8String(long) back}, as demonstrated in the above example. - * - *

    Foreign addresses

    - * - * When a memory segment is created from Java code, the segment properties (spatial bounds, temporal bounds and confinement) - * are fully known at segment creation. But when interacting with foreign functions, clients will often receive raw pointers. - * Such pointers have no spatial bounds. For example, the C type {@code char*} can refer to a single {@code char} value, - * or an array of {@code char} values, of given size. Nor do said pointers have any notion of temporal bounds or thread-confinement. - *

    - * Raw pointers are modelled using the {@link java.lang.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 java.lang.foreign.MemoryAddress#get(java.lang.foreign.ValueLayout.OfInt, long) dereference methods} - * provided: - * - * {@snippet lang=java : - * MemoryAddress addr = ... // obtain address from foreign function call - * int x = addr.get(ValueLayout.JAVA_INT, 0); - * } - * - * Alternatively, the client can - * {@linkplain java.lang.foreign.MemorySegment#ofAddress(java.lang.foreign.MemoryAddress, long, java.lang.foreign.MemorySession) 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 memory address: - * - * {@snippet lang=java : - * MemorySession session = ... // initialize a memory session object - * MemoryAddress addr = ... // obtain address from foreign function call - * MemorySegment segment = MemorySegment.ofAddress(addr, 4, session); // segment is 4 bytes long - * int x = segment.get(ValueLayout.JAVA_INT, 0); - * } + * The {@link java.lang.foreign.Arena} class also provides many useful methods for + * interacting with foreign code, such as + * {@linkplain java.lang.foreign.SegmentAllocator#allocateUtf8String(java.lang.String) converting} Java strings into + * zero-terminated, UTF-8 strings, as demonstrated in the above example. * *

    Upcalls

    * The {@link java.lang.foreign.Linker} interface also allows clients to turn an existing method handle (which might point - * to a Java method) into a memory address, so that Java code can effectively be passed to other foreign functions. + * to a Java method) into a memory segment, 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: * * {@snippet lang=java : * class IntComparator { - * static int intCompare(MemoryAddress addr1, MemoryAddress addr2) { - * return addr1.get(ValueLayout.JAVA_INT, 0) - addr2.get(ValueLayout.JAVA_INT, 0); + * static int intCompare(MemorySegment addr1, MemorySegment addr2) { + * return addr1.get(ValueLayout.JAVA_INT, 0) - + * addr2.get(ValueLayout.JAVA_INT, 0); + * * } * } * } * - * The above method dereferences two memory addresses containing an integer value, and performs a simple comparison + * The above method accesses two foreign memory segments containing an integer value, and performs a simple comparison * by returning the difference between such values. We can then obtain a method handle which targets the above static * method, as follows: * - * {@snippet lang=java : - * FunctionDescriptor intCompareDescriptor = FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.ADDRESS); + * {@snippet lang = java: + * FunctionDescriptor intCompareDescriptor = FunctionDescriptor.of(ValueLayout.JAVA_INT, + * ValueLayout.ADDRESS.asUnbounded(), + * ValueLayout.ADDRESS.asUnbounded()); * MethodHandle intCompareHandle = MethodHandles.lookup().findStatic(IntComparator.class, * "intCompare", - * Linker.upcallType(comparFunction)); - * } + * intCompareDescriptor.toMethodType()); + *} * * As before, we need to create a {@link java.lang.foreign.FunctionDescriptor} instance, this time describing the signature * of the function pointer we want to create. The descriptor can be used to - * {@linkplain java.lang.foreign.Linker#upcallType(java.lang.foreign.FunctionDescriptor) derive} a method type + * {@linkplain java.lang.foreign.FunctionDescriptor#toMethodType() derive} a method type * that can be used to look up 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 java.lang.foreign.Linker} interface, as follows: * - * {@snippet lang=java : - * MemorySession session = ... - * Addressable comparFunc = Linker.nativeLinker().upcallStub( - * intCompareHandle, intCompareDescriptor, session); + * {@snippet lang = java: + * SegmentScope scope = ... + * MemorySegment comparFunc = Linker.nativeLinker().upcallStub( + * intCompareHandle, intCompareDescriptor, scope); * ); - * } + *} * * The {@link java.lang.foreign.FunctionDescriptor} instance created in the previous step is then used to - * {@linkplain java.lang.foreign.Linker#upcallStub(java.lang.invoke.MethodHandle, java.lang.foreign.FunctionDescriptor, java.lang.foreign.MemorySession) create} + * {@linkplain java.lang.foreign.Linker#upcallStub(java.lang.invoke.MethodHandle, FunctionDescriptor, SegmentScope) 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 ABI of the * underlying platform. - * The lifecycle of the upcall stub is tied to the {@linkplain java.lang.foreign.MemorySession memory session} - * provided when the upcall stub is created. This same session is made available by the {@link java.lang.foreign.MemorySegment} + * The lifecycle of the upcall stub is tied to the {@linkplain java.lang.foreign.SegmentScope scope} + * provided when the upcall stub is created. This same scope is made available by the {@link java.lang.foreign.MemorySegment} * 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 java.lang.foreign.MemorySegment#ofAddress(MemoryAddress, long, MemorySession)} + * the restricted method {@link java.lang.foreign.MemorySegment#ofAddress(long, long, SegmentScope)} * can be used to create a fresh segment with the 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 java.lang.foreign.MemorySegment#ofAddress(MemoryAddress, long, MemorySession)}, - * 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 + * 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 java.lang.foreign.MemorySegment#ofAddress(long, long, SegmentScope)}, 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 access 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. *

    - * Access to restricted methods can be controlled using the command line option {@code --enable-native-access=M1,M2, ... Mn}, - * where {@code M1}, {@code M2}, {@code ... Mn} are module names (for the unnamed module, the special value {@code ALL-UNNAMED} - * can be used). If this option is specified, access to restricted methods is only granted to the modules listed by that - * option. If this option is not specified, access to restricted methods is enabled for all modules, but - * access to restricted methods will result in runtime warnings. + * Given the potential danger of restricted methods, the Java runtime issues a warning on the standard error stream + * every time a restricted method is invoked. Such warnings can be disabled by granting access to restricted methods + * to selected modules. This can be done either via implementation-specific command line options, or programmatically, e.g. by calling + * {@link java.lang.ModuleLayer.Controller#enableNativeAccess(java.lang.Module)}. *

    * For every class in this package, unless specified otherwise, any method arguments of reference * type must not be null, and any null argument will elicit a {@code NullPointerException}. This fact is not individually * documented for methods of this API. + * + * @apiNote Usual memory model guarantees, for example stated in {@jls 6.6} and {@jls 10.4}, do not apply + * when accessing native memory segments as these segments are backed by off-heap regions of memory. + * + * @implNote + * In the reference implementation, access to restricted methods can be granted to specific modules using the command line option + * {@code --enable-native-access=M1,M2, ... Mn}, where {@code M1}, {@code M2}, {@code ... Mn} are module names + * (for the unnamed module, the special value {@code ALL-UNNAMED} can be used). If this option is specified, access to + * restricted methods is only granted to the modules listed by that option. If this option is not specified, + * access to restricted methods is enabled for all modules, but access to restricted methods will result in runtime warnings. + * */ package java.lang.foreign; + diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java index 28e7b3e8dec02..a63f53d3ca1db 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java @@ -45,8 +45,8 @@ import java.lang.constant.ConstantDescs; import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.lang.invoke.LambdaForm.BasicType; import java.lang.reflect.Constructor; @@ -7910,21 +7910,21 @@ private static MethodType tableSwitchChecks(MethodHandle defaultCase, MethodHand * access modes {@code get} and {@code set} for {@code long} and * {@code double} on 32-bit platforms. *

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

    * Alternatively, a memory access operation is partially aligned if it occurs at a memory address {@code A} * which is only compatible with the alignment constraint {@code B}; in such cases, access for anything other than the diff --git a/src/java.base/share/classes/java/lang/invoke/NativeMethodHandle.java b/src/java.base/share/classes/java/lang/invoke/NativeMethodHandle.java index 176ee7db4595c..996b51487c9f8 100644 --- a/src/java.base/share/classes/java/lang/invoke/NativeMethodHandle.java +++ b/src/java.base/share/classes/java/lang/invoke/NativeMethodHandle.java @@ -52,24 +52,32 @@ private NativeMethodHandle(MethodType type, LambdaForm form, NativeEntryPoint ne */ public static MethodHandle make(NativeEntryPoint nep) { MethodType type = nep.type(); - if (!allTypesPrimitive(type)) - throw new IllegalArgumentException("Type must only contain primitives: " + type); + if (hasIllegalType(type)) + throw new IllegalArgumentException("Illegal type(s) found: " + type); LambdaForm lform = preparedLambdaForm(type); return new NativeMethodHandle(type, lform, nep); } - private static boolean allTypesPrimitive(MethodType type) { - if (!type.returnType().isPrimitive()) - return false; + private static boolean hasIllegalType(MethodType type) { + if (isIllegalType(type.returnType())) + return true; for (Class pType : type.parameterArray()) { - if (!pType.isPrimitive()) - return false; + if (isIllegalType(pType)) + return true; } - return true; + return false; + } + + private static boolean isIllegalType(Class pType) { + return !(pType == long.class + || pType == int.class + || pType == float.class + || pType == double.class + || pType == void.class); } private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory(); diff --git a/src/java.base/share/classes/java/lang/invoke/X-VarHandleByteArrayView.java.template b/src/java.base/share/classes/java/lang/invoke/X-VarHandleByteArrayView.java.template index 05923554f19c0..08495f2e58d42 100644 --- a/src/java.base/share/classes/java/lang/invoke/X-VarHandleByteArrayView.java.template +++ b/src/java.base/share/classes/java/lang/invoke/X-VarHandleByteArrayView.java.template @@ -34,7 +34,6 @@ import jdk.internal.util.Preconditions; import jdk.internal.vm.annotation.ForceInline; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.nio.ByteBuffer; import java.nio.ReadOnlyBufferException; import java.util.List; diff --git a/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template b/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template index 1f1af9a6fd5a1..b65306f6e21eb 100644 --- a/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template +++ b/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template @@ -29,7 +29,6 @@ package java.nio; import java.io.FileDescriptor; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.lang.ref.Reference; import java.util.Objects; import jdk.internal.foreign.MemorySessionImpl; diff --git a/src/java.base/share/classes/java/nio/channels/FileChannel.java b/src/java.base/share/classes/java/nio/channels/FileChannel.java index b4a4a19502337..9ec890dcac08c 100644 --- a/src/java.base/share/classes/java/nio/channels/FileChannel.java +++ b/src/java.base/share/classes/java/nio/channels/FileChannel.java @@ -27,7 +27,7 @@ import java.io.IOException; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.spi.AbstractInterruptibleChannel; @@ -1001,6 +1001,9 @@ public abstract MappedByteBuffer map(MapMode mode, long position, long size) /** * Maps a region of this channel's file into a new mapped memory segment, * with the given offset, size and memory session. + * The {@linkplain MemorySegment#address() address} of the returned memory + * segment is the starting address of the mapped off-heap region backing + * the segment. * *

    If the specified mapping mode is * {@linkplain FileChannel.MapMode#READ_ONLY READ_ONLY}, the resulting @@ -1053,11 +1056,11 @@ public abstract MappedByteBuffer map(MapMode mode, long position, long size) * * @throws IllegalStateException * If the {@code session} is not - * {@linkplain MemorySession#isAlive() alive}. + * {@linkplain SegmentScope#isAlive() alive}. * * @throws WrongThreadException * If this method is called from a thread other than the thread - * {@linkplain MemorySession#ownerThread() owning} the + * {@linkplain SegmentScope#isAccessibleBy(Thread) owning} the * {@code session}. * * @throws NonReadableChannelException @@ -1080,7 +1083,7 @@ public abstract MappedByteBuffer map(MapMode mode, long position, long size) * @since 19 */ @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) - public MemorySegment map(MapMode mode, long offset, long size, MemorySession session) + public MemorySegment map(MapMode mode, long offset, long size, SegmentScope session) throws IOException { throw new UnsupportedOperationException(); diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index bc1aeedc7af43..be8ee9d91f693 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -270,12 +270,13 @@ public interface JavaLangAccess { /** * Updates all unnamed modules to allow access to restricted methods. */ - void addEnableNativeAccessAllUnnamed(); + void addEnableNativeAccessToAllUnnamed(); /** - * Returns true if module m can access restricted methods. + * Ensure that the given module has native access. If not, warn or + * throw exception depending on the configuration. */ - boolean isEnableNativeAccess(Module m); + void ensureNativeAccess(Module m, Class owner, String methodName); /** * Returns the ServicesCatalog for the given Layer. diff --git a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java index 3bfa39cc9b983..9080e1482a84e 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java @@ -25,12 +25,12 @@ package jdk.internal.foreign; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.lang.foreign.SegmentAllocator; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; +import java.lang.reflect.Array; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -51,6 +51,7 @@ import jdk.internal.access.SharedSecrets; import jdk.internal.access.foreign.UnmapperProxy; import jdk.internal.misc.ScopedMemoryAccess; +import jdk.internal.misc.Unsafe; import jdk.internal.util.ArraysSupport; import jdk.internal.util.Preconditions; import jdk.internal.vm.annotation.ForceInline; @@ -66,26 +67,26 @@ * are defined for each memory segment kind, see {@link NativeMemorySegmentImpl}, {@link HeapMemorySegmentImpl} and * {@link MappedMemorySegmentImpl}. */ -public abstract non-sealed class AbstractMemorySegmentImpl implements MemorySegment, SegmentAllocator, Scoped, BiFunction, RuntimeException> { +public abstract sealed class AbstractMemorySegmentImpl + implements MemorySegment, SegmentAllocator, BiFunction, RuntimeException> + permits HeapMemorySegmentImpl, NativeMemorySegmentImpl { private static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess(); - static final long NONCE = new Random().nextLong(); - - static final JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess(); + static final JavaNioAccess NIO_ACCESS = SharedSecrets.getJavaNioAccess(); final long length; final boolean readOnly; - final MemorySession session; + final SegmentScope session; @ForceInline - AbstractMemorySegmentImpl(long length, boolean readOnly, MemorySession session) { + AbstractMemorySegmentImpl(long length, boolean readOnly, SegmentScope session) { this.length = length; this.readOnly = readOnly; this.session = session; } - abstract AbstractMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySession session); + abstract AbstractMemorySegmentImpl dup(long offset, long size, boolean readOnly, SegmentScope session); abstract ByteBuffer makeByteBuffer(); @@ -125,7 +126,7 @@ public Spliterator spliterator(MemoryLayout elementLayout) { if (!isAlignedForElement(0, elementLayout)) { throw new IllegalArgumentException("Incompatible alignment constraints"); } - if (!Utils.isAligned(byteSize(), elementLayout.byteSize())) { + if ((byteSize() % elementLayout.byteSize()) != 0) { throw new IllegalArgumentException("Segment size is not a multiple of layout size"); } return new SegmentSplitter(elementLayout.byteSize(), byteSize() / elementLayout.byteSize(), @@ -145,52 +146,15 @@ public final MemorySegment fill(byte value){ } @Override - public MemorySegment allocate(long bytesSize, long bytesAlignment) { - Utils.checkAllocationSizeAndAlign(bytesSize, bytesAlignment); - return asSlice(0, bytesSize); - } - - @Override - public long mismatch(MemorySegment other) { - AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl)Objects.requireNonNull(other); - final long thisSize = this.byteSize(); - final long thatSize = that.byteSize(); - final long length = Math.min(thisSize, thatSize); - this.checkAccess(0, length, true); - that.checkAccess(0, length, true); - if (this == other) { - checkValidState(); - return -1; - } - - long i = 0; - if (length > 7) { - if (get(JAVA_BYTE, 0) != that.get(JAVA_BYTE, 0)) { - return 0; - } - i = vectorizedMismatchLargeForBytes(sessionImpl(), that.sessionImpl(), - this.unsafeGetBase(), this.unsafeGetOffset(), - that.unsafeGetBase(), that.unsafeGetOffset(), - length); - if (i >= 0) { - return i; - } - long remaining = ~i; - assert remaining < 8 : "remaining greater than 7: " + remaining; - i = length - remaining; - } - for (; i < length; i++) { - if (get(JAVA_BYTE, i) != that.get(JAVA_BYTE, i)) { - return i; - } - } - return thisSize != thatSize ? length : -1; + public MemorySegment allocate(long byteSize, long byteAlignment) { + Utils.checkAllocationSizeAndAlign(byteSize, byteAlignment); + return asSlice(0, byteSize); } /** * Mismatch over long lengths. */ - private static long vectorizedMismatchLargeForBytes(MemorySessionImpl aSession, MemorySessionImpl bSession, + public static long vectorizedMismatchLargeForBytes(MemorySessionImpl aSession, MemorySessionImpl bSession, Object a, long aOffset, Object b, long bOffset, long length) { @@ -219,11 +183,6 @@ private static long vectorizedMismatchLargeForBytes(MemorySessionImpl aSession, return ~remaining; } - @Override - public MemoryAddress address() { - throw new UnsupportedOperationException("Cannot obtain address of on-heap segment"); - } - @Override public final ByteBuffer asByteBuffer() { checkArraySize("ByteBuffer", 1); @@ -279,21 +238,25 @@ public final long segmentOffset(MemorySegment other) { @Override public void load() { - throw new UnsupportedOperationException("Not a mapped segment"); + throw notAMappedSegment(); } @Override public void unload() { - throw new UnsupportedOperationException("Not a mapped segment"); + throw notAMappedSegment(); } @Override public boolean isLoaded() { - throw new UnsupportedOperationException("Not a mapped segment"); + throw notAMappedSegment(); } @Override public void force() { + throw notAMappedSegment(); + } + + private static UnsupportedOperationException notAMappedSegment() { throw new UnsupportedOperationException("Not a mapped segment"); } @@ -366,6 +329,7 @@ public final boolean isAlignedForElement(long offset, MemoryLayout layout) { } private int checkArraySize(String typeName, int elemSize) { + // elemSize is guaranteed to be a power of two, so we can use an alignment check if (!Utils.isAligned(length, elemSize)) { throw new IllegalStateException(String.format("Segment size is not a multiple of %d. Size: %d", elemSize, length)); } @@ -394,20 +358,20 @@ public RuntimeException apply(String s, List numbers) { } @Override - public MemorySession session() { + public SegmentScope scope() { return session; } + @ForceInline + public final MemorySessionImpl sessionImpl() { + return (MemorySessionImpl)session; + } + private IndexOutOfBoundsException outOfBoundException(long offset, long length) { return new IndexOutOfBoundsException(String.format("Out of bound access on segment %s; new offset = %d; new length = %d", this, offset, length)); } - protected int id() { - //compute a stable and random id for this memory segment - return Math.abs(Objects.hash(unsafeGetBase(), unsafeGetOffset(), NONCE)); - } - static class SegmentSplitter implements Spliterator { AbstractMemorySegmentImpl segment; long elemCount; @@ -486,42 +450,38 @@ public int characteristics() { @Override public String toString() { - return "MemorySegment{ id=0x" + Long.toHexString(id()) + " limit: " + length + " }"; + return "MemorySegment{ array: " + array() + " address:" + address() + " limit: " + length + " }"; } @Override public boolean equals(Object o) { return o instanceof AbstractMemorySegmentImpl that && - isNative() == that.isNative() && - unsafeGetOffset() == that.unsafeGetOffset() && unsafeGetBase() == that.unsafeGetBase() && - length == that.length && - session.equals(that.session); + unsafeGetOffset() == that.unsafeGetOffset(); } @Override public int hashCode() { return Objects.hash( - isNative(), unsafeGetOffset(), - unsafeGetBase(), - length, - session - ); + unsafeGetBase()); } public static AbstractMemorySegmentImpl ofBuffer(Buffer bb) { Objects.requireNonNull(bb); - long bbAddress = nioAccess.getBufferAddress(bb); - Object base = nioAccess.getBufferBase(bb); - UnmapperProxy unmapper = nioAccess.unmapper(bb); + Object base = NIO_ACCESS.getBufferBase(bb); + if (!bb.isDirect() && base == null) { + throw new IllegalArgumentException("The provided heap buffer is not backed by an array."); + } + long bbAddress = NIO_ACCESS.getBufferAddress(bb); + UnmapperProxy unmapper = NIO_ACCESS.unmapper(bb); int pos = bb.position(); int limit = bb.limit(); int size = limit - pos; - AbstractMemorySegmentImpl bufferSegment = (AbstractMemorySegmentImpl)nioAccess.bufferSegment(bb); - final MemorySession bufferSession; + AbstractMemorySegmentImpl bufferSegment = (AbstractMemorySegmentImpl) NIO_ACCESS.bufferSegment(bb); + final SegmentScope bufferSession; if (bufferSegment != null) { bufferSession = bufferSegment.session; } else { @@ -574,4 +534,152 @@ private static int getScaleFactor(Buffer buffer) { throw new AssertionError("Cannot get here"); } } + + @ForceInline + public static void copy(MemorySegment srcSegment, ValueLayout srcElementLayout, long srcOffset, + MemorySegment dstSegment, ValueLayout dstElementLayout, long dstOffset, + long elementCount) { + + AbstractMemorySegmentImpl srcImpl = (AbstractMemorySegmentImpl)srcSegment; + AbstractMemorySegmentImpl dstImpl = (AbstractMemorySegmentImpl)dstSegment; + if (srcElementLayout.byteSize() != dstElementLayout.byteSize()) { + throw new IllegalArgumentException("Source and destination layouts must have same size"); + } + Utils.checkElementAlignment(srcElementLayout, "Source layout alignment greater than its size"); + Utils.checkElementAlignment(dstElementLayout, "Destination layout alignment greater than its size"); + if (!srcImpl.isAlignedForElement(srcOffset, srcElementLayout)) { + throw new IllegalArgumentException("Source segment incompatible with alignment constraints"); + } + if (!dstImpl.isAlignedForElement(dstOffset, dstElementLayout)) { + throw new IllegalArgumentException("Destination 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.sessionImpl(), dstImpl.sessionImpl(), + srcImpl.unsafeGetBase(), srcImpl.unsafeGetOffset() + srcOffset, + dstImpl.unsafeGetBase(), dstImpl.unsafeGetOffset() + dstOffset, size); + } else { + ScopedMemoryAccess.getScopedMemoryAccess().copySwapMemory(srcImpl.sessionImpl(), dstImpl.sessionImpl(), + srcImpl.unsafeGetBase(), srcImpl.unsafeGetOffset() + srcOffset, + dstImpl.unsafeGetBase(), dstImpl.unsafeGetOffset() + dstOffset, size, srcElementLayout.byteSize()); + } + } + + @ForceInline + public static void copy(MemorySegment srcSegment, ValueLayout srcLayout, long srcOffset, + Object dstArray, int dstIndex, + int elementCount) { + + long baseAndScale = getBaseAndScale(dstArray.getClass()); + if (dstArray.getClass().componentType() != srcLayout.carrier()) { + throw new IllegalArgumentException("Incompatible value layout: " + srcLayout); + } + int dstBase = (int)baseAndScale; + long dstWidth = (int)(baseAndScale >> 32); // Use long arithmetics below + AbstractMemorySegmentImpl srcImpl = (AbstractMemorySegmentImpl)srcSegment; + Utils.checkElementAlignment(srcLayout, "Source layout alignment greater than its size"); + if (!srcImpl.isAlignedForElement(srcOffset, srcLayout)) { + throw new IllegalArgumentException("Source segment incompatible with alignment constraints"); + } + srcImpl.checkAccess(srcOffset, elementCount * dstWidth, true); + Objects.checkFromIndexSize(dstIndex, elementCount, Array.getLength(dstArray)); + if (dstWidth == 1 || srcLayout.order() == ByteOrder.nativeOrder()) { + ScopedMemoryAccess.getScopedMemoryAccess().copyMemory(srcImpl.sessionImpl(), null, + srcImpl.unsafeGetBase(), srcImpl.unsafeGetOffset() + srcOffset, + dstArray, dstBase + (dstIndex * dstWidth), elementCount * dstWidth); + } else { + ScopedMemoryAccess.getScopedMemoryAccess().copySwapMemory(srcImpl.sessionImpl(), null, + srcImpl.unsafeGetBase(), srcImpl.unsafeGetOffset() + srcOffset, + dstArray, dstBase + (dstIndex * dstWidth), elementCount * dstWidth, dstWidth); + } + } + + @ForceInline + public static void copy(Object srcArray, int srcIndex, + MemorySegment dstSegment, ValueLayout dstLayout, long dstOffset, + int elementCount) { + + long baseAndScale = getBaseAndScale(srcArray.getClass()); + if (srcArray.getClass().componentType() != dstLayout.carrier()) { + throw new IllegalArgumentException("Incompatible value layout: " + dstLayout); + } + int srcBase = (int)baseAndScale; + long srcWidth = (int)(baseAndScale >> 32); // Use long arithmetics below + Objects.checkFromIndexSize(srcIndex, elementCount, Array.getLength(srcArray)); + AbstractMemorySegmentImpl destImpl = (AbstractMemorySegmentImpl)dstSegment; + Utils.checkElementAlignment(dstLayout, "Destination layout alignment greater than its size"); + if (!destImpl.isAlignedForElement(dstOffset, dstLayout)) { + throw new IllegalArgumentException("Destination segment incompatible with alignment constraints"); + } + destImpl.checkAccess(dstOffset, elementCount * srcWidth, false); + if (srcWidth == 1 || dstLayout.order() == ByteOrder.nativeOrder()) { + ScopedMemoryAccess.getScopedMemoryAccess().copyMemory(null, destImpl.sessionImpl(), + srcArray, srcBase + (srcIndex * srcWidth), + destImpl.unsafeGetBase(), destImpl.unsafeGetOffset() + dstOffset, elementCount * srcWidth); + } else { + ScopedMemoryAccess.getScopedMemoryAccess().copySwapMemory(null, destImpl.sessionImpl(), + srcArray, srcBase + (srcIndex * srcWidth), + destImpl.unsafeGetBase(), destImpl.unsafeGetOffset() + dstOffset, elementCount * srcWidth, srcWidth); + } + } + + public static long mismatch(MemorySegment srcSegment, long srcFromOffset, long srcToOffset, + MemorySegment dstSegment, long dstFromOffset, long dstToOffset) { + AbstractMemorySegmentImpl srcImpl = (AbstractMemorySegmentImpl)Objects.requireNonNull(srcSegment); + AbstractMemorySegmentImpl dstImpl = (AbstractMemorySegmentImpl)Objects.requireNonNull(dstSegment); + long srcBytes = srcToOffset - srcFromOffset; + long dstBytes = dstToOffset - dstFromOffset; + srcImpl.checkAccess(srcFromOffset, srcBytes, true); + dstImpl.checkAccess(dstFromOffset, dstBytes, true); + if (dstImpl == srcImpl) { + srcImpl.checkValidState(); + return -1; + } + + long bytes = Math.min(srcBytes, dstBytes); + long i = 0; + if (bytes > 7) { + if (srcImpl.get(JAVA_BYTE, srcFromOffset) != dstImpl.get(JAVA_BYTE, dstFromOffset)) { + return 0; + } + i = AbstractMemorySegmentImpl.vectorizedMismatchLargeForBytes(srcImpl.sessionImpl(), dstImpl.sessionImpl(), + srcImpl.unsafeGetBase(), srcImpl.unsafeGetOffset() + srcFromOffset, + dstImpl.unsafeGetBase(), dstImpl.unsafeGetOffset() + dstFromOffset, + bytes); + if (i >= 0) { + return i; + } + long remaining = ~i; + assert remaining < 8 : "remaining greater than 7: " + remaining; + i = bytes - remaining; + } + for (; i < bytes; i++) { + if (srcImpl.get(JAVA_BYTE, srcFromOffset + i) != dstImpl.get(JAVA_BYTE, dstFromOffset + i)) { + return i; + } + } + return srcBytes != dstBytes ? bytes : -1; + } + + 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/java.base/share/classes/jdk/internal/foreign/ArenaAllocator.java b/src/java.base/share/classes/jdk/internal/foreign/ArenaAllocator.java deleted file mode 100644 index 0ae38e3dc398d..0000000000000 --- a/src/java.base/share/classes/jdk/internal/foreign/ArenaAllocator.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.internal.foreign; - -import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; -import java.lang.foreign.SegmentAllocator; - -public final class ArenaAllocator implements SegmentAllocator { - - public static final long DEFAULT_BLOCK_SIZE = 4 * 1024; - - private MemorySegment segment; - - private long sp = 0L; - private long size = 0; - private final long blockSize; - private final long arenaSize; - private final MemorySession session; - - public ArenaAllocator(long blockSize, long arenaSize, MemorySession session) { - this.blockSize = blockSize; - this.arenaSize = arenaSize; - this.session = session; - this.segment = newSegment(blockSize, 1); - } - - MemorySegment trySlice(long bytesSize, long bytesAlignment) { - long min = segment.address().toRawLongValue(); - long start = Utils.alignUp(min + sp, bytesAlignment) - min; - if (segment.byteSize() - start < bytesSize) { - return null; - } else { - MemorySegment slice = segment.asSlice(start, bytesSize); - sp = start + bytesSize; - return slice; - } - } - - 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, session); - } - - @Override - public MemorySegment allocate(long bytesSize, long bytesAlignment) { - Utils.checkAllocationSizeAndAlign(bytesSize, 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 < 0) { - throw new OutOfMemoryError(); - } else if (maxPossibleAllocationSize > blockSize) { - // too big - return newSegment(bytesSize, bytesAlignment); - } else { - // allocate a new segment and slice from there - sp = 0L; - segment = newSegment(blockSize, 1L); - slice = trySlice(bytesSize, bytesAlignment); - return slice; - } - } - } -} diff --git a/src/java.base/share/classes/jdk/internal/foreign/CABI.java b/src/java.base/share/classes/jdk/internal/foreign/CABI.java index 4b4d95149f863..59d5e1e42c83a 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/CABI.java +++ b/src/java.base/share/classes/jdk/internal/foreign/CABI.java @@ -29,10 +29,10 @@ import static sun.security.action.GetPropertyAction.privilegedGetProperty; public enum CABI { - SysV, - Win64, - LinuxAArch64, - MacOsAArch64; + SYS_V, + WIN_64, + LINUX_AARCH_64, + MAC_OS_AARCH_64; private static final CABI ABI; private static final String ARCH; @@ -47,16 +47,16 @@ public enum CABI { // addressSize will be correctly 32 if ((ARCH.equals("amd64") || ARCH.equals("x86_64")) && ADDRESS_SIZE == 64) { if (OS.startsWith("Windows")) { - ABI = Win64; + ABI = WIN_64; } else { - ABI = SysV; + ABI = SYS_V; } } else if (ARCH.equals("aarch64")) { if (OS.startsWith("Mac")) { - ABI = MacOsAArch64; + ABI = MAC_OS_AARCH_64; } else { // The Linux ABI follows the standard AAPCS ABI - ABI = LinuxAArch64; + ABI = LINUX_AARCH_64; } } else { // unsupported diff --git a/src/java.base/share/classes/jdk/internal/foreign/ConfinedSession.java b/src/java.base/share/classes/jdk/internal/foreign/ConfinedSession.java index b5ef673b92b15..4961cecde5e1c 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/ConfinedSession.java +++ b/src/java.base/share/classes/jdk/internal/foreign/ConfinedSession.java @@ -51,8 +51,8 @@ final class ConfinedSession extends MemorySessionImpl { } } - public ConfinedSession(Thread owner, Cleaner cleaner) { - super(owner, new ConfinedResourceList(), cleaner); + public ConfinedSession(Thread owner) { + super(owner, new ConfinedResourceList()); } @Override @@ -81,10 +81,12 @@ public void release0() { void justClose() { checkValidState(); - if (state == 0 || state - ((int)ASYNC_RELEASE_COUNT.getVolatile(this)) == 0) { + int asyncCount = (int)ASYNC_RELEASE_COUNT.getVolatile(this); + if ((state == 0 && asyncCount == 0) + || ((state - asyncCount) == 0)) { state = CLOSED; } else { - throw alreadyAcquired(state); + throw alreadyAcquired(state - asyncCount); } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/FunctionDescriptorImpl.java b/src/java.base/share/classes/jdk/internal/foreign/FunctionDescriptorImpl.java new file mode 100644 index 0000000000000..0723af6ba0def --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/FunctionDescriptorImpl.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.internal.foreign; + +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; +import java.lang.invoke.MethodType; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import static java.util.Objects.requireNonNull; + +/** + * @implSpec This class and its subclasses are immutable, thread-safe and value-based. + */ +public final class FunctionDescriptorImpl implements FunctionDescriptor { + + private final MemoryLayout resLayout; // Nullable + private final List argLayouts; + + private FunctionDescriptorImpl(MemoryLayout resLayout, List argLayouts) { + this.resLayout = resLayout; + this.argLayouts = List.copyOf(argLayouts); + } + + /** + * {@return the return layout (if any) associated with this function descriptor} + */ + public Optional returnLayout() { + return Optional.ofNullable(resLayout); + } + + /** + * {@return the argument layouts associated with this function descriptor (as an immutable list)}. + */ + public List argumentLayouts() { + return argLayouts; + } + + /** + * Returns a function descriptor with the given argument layouts appended to the argument layout array + * of this function descriptor. + * + * @param addedLayouts the argument layouts to append. + * @return the new function descriptor. + */ + public FunctionDescriptorImpl appendArgumentLayouts(MemoryLayout... addedLayouts) { + return insertArgumentLayouts(argLayouts.size(), addedLayouts); + } + + /** + * Returns a function descriptor with the given argument layouts inserted at the given index, into the argument + * layout array of this function descriptor. + * + * @param index the index at which to insert the arguments + * @param addedLayouts the argument layouts to insert at given index. + * @return the new function descriptor. + * @throws IllegalArgumentException if {@code index < 0 || index > argumentLayouts().size()}. + */ + public FunctionDescriptorImpl insertArgumentLayouts(int index, MemoryLayout... addedLayouts) { + if (index < 0 || index > argLayouts.size()) + throw new IllegalArgumentException("Index out of bounds: " + index); + List added = List.of(addedLayouts); // null check on array and its elements + List newLayouts = new ArrayList<>(argLayouts.size() + addedLayouts.length); + newLayouts.addAll(argLayouts.subList(0, index)); + newLayouts.addAll(added); + newLayouts.addAll(argLayouts.subList(index, argLayouts.size())); + return new FunctionDescriptorImpl(resLayout, newLayouts); + } + + /** + * Returns a function descriptor with the given memory layout as the new return layout. + * + * @param newReturn the new return layout. + * @return the new function descriptor. + */ + public FunctionDescriptorImpl changeReturnLayout(MemoryLayout newReturn) { + requireNonNull(newReturn); + return new FunctionDescriptorImpl(newReturn, argLayouts); + } + + /** + * Returns a function descriptor with the return layout dropped. This is useful to model functions + * which return no values. + * + * @return the new function descriptor. + */ + public FunctionDescriptorImpl dropReturnLayout() { + return new FunctionDescriptorImpl(null, argLayouts); + } + + private static Class carrierTypeFor(MemoryLayout layout) { + if (layout instanceof ValueLayout valueLayout) { + return valueLayout.carrier(); + } else if (layout instanceof GroupLayout) { + return MemorySegment.class; + } else { + throw new IllegalArgumentException("Unsupported layout: " + layout); + } + } + + @Override + public MethodType toMethodType() { + Class returnValue = resLayout != null ? carrierTypeFor(resLayout) : void.class; + Class[] argCarriers = new Class[argLayouts.size()]; + for (int i = 0; i < argCarriers.length; i++) { + argCarriers[i] = carrierTypeFor(argLayouts.get(i)); + } + return MethodType.methodType(returnValue, argCarriers); + } + + /** + * {@return the string representation of this function descriptor} + */ + @Override + public String toString() { + return String.format("(%s)%s", + argLayouts.stream().map(Object::toString) + .collect(Collectors.joining()), + returnLayout() + .map(Object::toString) + .orElse("v")); + } + + /** + * 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 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 {@linkplain MemoryLayout#equals(Object) equal}; and
    • + *
    + * + * @param other the object to be compared for equality with this function descriptor. + * @return {@code true} if the specified object is equal to this function descriptor. + */ + @Override + public boolean equals(Object other) { + return other instanceof FunctionDescriptorImpl f && + Objects.equals(resLayout, f.resLayout) && + Objects.equals(argLayouts, f.argLayouts); + } + + /** + * {@return the hash code value for this function descriptor} + */ + @Override + public int hashCode() { + return Objects.hash(argLayouts, resLayout); + } + + public static FunctionDescriptor of(MemoryLayout resLayout, List argLayouts) { + return new FunctionDescriptorImpl(resLayout, argLayouts); + } + + public static FunctionDescriptor ofVoid(List argLayouts) { + return new FunctionDescriptorImpl(null, argLayouts); + } +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/GlobalSession.java b/src/java.base/share/classes/jdk/internal/foreign/GlobalSession.java new file mode 100644 index 0000000000000..2d78a66eb6f8d --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/GlobalSession.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.foreign; + +import jdk.internal.vm.annotation.ForceInline; + +/** + * The global, non-closeable, shared session. Similar to a shared session, but its {@link #close()} method throws unconditionally. + * Adding new resources to the global session, does nothing: as the session can never become not-alive, there is nothing to track. + * Acquiring and or releasing a memory session similarly does nothing. + */ +final class GlobalSession extends MemorySessionImpl { + + final Object ref; + + public GlobalSession(Object ref) { + super(null, null); + this.ref = ref; + } + + @Override + @ForceInline + public void release0() { + // do nothing + } + + @Override + public boolean isCloseable() { + return false; + } + + @Override + @ForceInline + public void acquire0() { + // do nothing + } + + @Override + void addInternal(ResourceList.ResourceCleanup resource) { + // do nothing + } + + @Override + public void justClose() { + throw nonCloseable(); + } +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java index 3d7d05e7d40e5..4967f438c003c 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java @@ -27,9 +27,11 @@ package jdk.internal.foreign; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.nio.ByteBuffer; import java.util.Objects; +import java.util.Optional; + import jdk.internal.access.JavaNioAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.misc.Unsafe; @@ -44,9 +46,9 @@ * the field type storing the 'base' coordinate is just Object; similarly, all the constructor in the subclasses * accept an Object 'base' parameter instead of a sharper type (e.g. {@code byte[]}). This is deliberate, as * using sharper types would require use of type-conversions, which in turn would inhibit some C2 optimizations, - * such as the elimination of store barriers in methods like {@link HeapMemorySegmentImpl#dup(long, long, int, MemorySession)}. + * such as the elimination of store barriers in methods like {@link HeapMemorySegmentImpl#dup(long, long, boolean, SegmentScope)}. */ -public abstract class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl { +public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl { private static final Unsafe UNSAFE = Unsafe.getUnsafe(); private static final int BYTE_ARR_BASE = UNSAFE.arrayBaseOffset(byte[].class); @@ -59,9 +61,14 @@ public abstract class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl { final long offset; final Object base; + @Override + public Optional array() { + return Optional.of(base); + } + @ForceInline HeapMemorySegmentImpl(long offset, Object base, long length, boolean readOnly) { - super(length, readOnly, MemorySessionImpl.GLOBAL); + super(length, readOnly, SegmentScope.global()); this.offset = offset; this.base = base; } @@ -72,7 +79,7 @@ public long unsafeGetOffset() { } @Override - abstract HeapMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySession session); + abstract HeapMemorySegmentImpl dup(long offset, long size, boolean readOnly, SegmentScope session); @Override ByteBuffer makeByteBuffer() { @@ -85,14 +92,14 @@ ByteBuffer makeByteBuffer() { // factories - public static class OfByte extends HeapMemorySegmentImpl { + public static final class OfByte extends HeapMemorySegmentImpl { OfByte(long offset, Object base, long length, boolean readOnly) { super(offset, base, length, readOnly); } @Override - OfByte dup(long offset, long size, boolean readOnly, MemorySession session) { + OfByte dup(long offset, long size, boolean readOnly, SegmentScope session) { return new OfByte(this.offset + offset, base, size, readOnly); } @@ -111,16 +118,21 @@ public static MemorySegment fromArray(byte[] arr) { public long maxAlignMask() { return MAX_ALIGN_1; } + + @Override + public long address() { + return offset - Unsafe.ARRAY_BYTE_BASE_OFFSET; + } } - public static class OfChar extends HeapMemorySegmentImpl { + public static final class OfChar extends HeapMemorySegmentImpl { OfChar(long offset, Object base, long length, boolean readOnly) { super(offset, base, length, readOnly); } @Override - OfChar dup(long offset, long size, boolean readOnly, MemorySession session) { + OfChar dup(long offset, long size, boolean readOnly, SegmentScope session) { return new OfChar(this.offset + offset, base, size, readOnly); } @@ -139,16 +151,21 @@ public static MemorySegment fromArray(char[] arr) { public long maxAlignMask() { return MAX_ALIGN_2; } + + @Override + public long address() { + return offset - Unsafe.ARRAY_CHAR_BASE_OFFSET; + } } - public static class OfShort extends HeapMemorySegmentImpl { + public static final class OfShort extends HeapMemorySegmentImpl { OfShort(long offset, Object base, long length, boolean readOnly) { super(offset, base, length, readOnly); } @Override - OfShort dup(long offset, long size, boolean readOnly, MemorySession session) { + OfShort dup(long offset, long size, boolean readOnly, SegmentScope session) { return new OfShort(this.offset + offset, base, size, readOnly); } @@ -167,16 +184,21 @@ public static MemorySegment fromArray(short[] arr) { public long maxAlignMask() { return MAX_ALIGN_2; } + + @Override + public long address() { + return offset - Unsafe.ARRAY_SHORT_BASE_OFFSET; + } } - public static class OfInt extends HeapMemorySegmentImpl { + public static final class OfInt extends HeapMemorySegmentImpl { OfInt(long offset, Object base, long length, boolean readOnly) { super(offset, base, length, readOnly); } @Override - OfInt dup(long offset, long size, boolean readOnly, MemorySession session) { + OfInt dup(long offset, long size, boolean readOnly, SegmentScope session) { return new OfInt(this.offset + offset, base, size, readOnly); } @@ -195,16 +217,21 @@ public static MemorySegment fromArray(int[] arr) { public long maxAlignMask() { return MAX_ALIGN_4; } + + @Override + public long address() { + return offset - Unsafe.ARRAY_INT_BASE_OFFSET; + } } - public static class OfLong extends HeapMemorySegmentImpl { + public static final class OfLong extends HeapMemorySegmentImpl { OfLong(long offset, Object base, long length, boolean readOnly) { super(offset, base, length, readOnly); } @Override - OfLong dup(long offset, long size, boolean readOnly, MemorySession session) { + OfLong dup(long offset, long size, boolean readOnly, SegmentScope session) { return new OfLong(this.offset + offset, base, size, readOnly); } @@ -223,16 +250,21 @@ public static MemorySegment fromArray(long[] arr) { public long maxAlignMask() { return MAX_ALIGN_8; } + + @Override + public long address() { + return offset - Unsafe.ARRAY_LONG_BASE_OFFSET; + } } - public static class OfFloat extends HeapMemorySegmentImpl { + public static final class OfFloat extends HeapMemorySegmentImpl { OfFloat(long offset, Object base, long length, boolean readOnly) { super(offset, base, length, readOnly); } @Override - OfFloat dup(long offset, long size, boolean readOnly, MemorySession session) { + OfFloat dup(long offset, long size, boolean readOnly, SegmentScope session) { return new OfFloat(this.offset + offset, base, size, readOnly); } @@ -251,16 +283,21 @@ public static MemorySegment fromArray(float[] arr) { public long maxAlignMask() { return MAX_ALIGN_4; } + + @Override + public long address() { + return offset - Unsafe.ARRAY_FLOAT_BASE_OFFSET; + } } - public static class OfDouble extends HeapMemorySegmentImpl { + public static final class OfDouble extends HeapMemorySegmentImpl { OfDouble(long offset, Object base, long length, boolean readOnly) { super(offset, base, length, readOnly); } @Override - OfDouble dup(long offset, long size, boolean readOnly, MemorySession session) { + OfDouble dup(long offset, long size, boolean readOnly, SegmentScope session) { return new OfDouble(this.offset + offset, base, size, readOnly); } @@ -279,6 +316,11 @@ public static MemorySegment fromArray(double[] arr) { public long maxAlignMask() { return MAX_ALIGN_8; } + + @Override + public long address() { + return offset - Unsafe.ARRAY_DOUBLE_BASE_OFFSET; + } } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/ImplicitSession.java b/src/java.base/share/classes/jdk/internal/foreign/ImplicitSession.java new file mode 100644 index 0000000000000..0eb3642508c09 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/ImplicitSession.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.foreign; + +import sun.nio.ch.DirectBuffer; + +import java.lang.ref.Cleaner; +import java.lang.ref.Reference; + +/** + * This is an implicit, GC-backed memory session. Implicit sessions cannot be closed explicitly. + * While it would be possible to model an implicit session as a non-closeable view of a shared + * session, it is better to capture the fact that an implicit session is not just a non-closeable + * view of some session which might be closeable. This is useful e.g. in the implementations of + * {@link DirectBuffer#address()}, where obtaining an address of a buffer instance associated + * with a potentially closeable session is forbidden. + */ +final class ImplicitSession extends SharedSession { + + public ImplicitSession(Cleaner cleaner) { + super(); + cleaner.register(this, resourceList); + } + + @Override + public void release0() { + Reference.reachabilityFence(this); + } + + @Override + public void acquire0() { + // do nothing + } + + @Override + public boolean isCloseable() { + return false; + } + + @Override + public void justClose() { + throw nonCloseable(); + } +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/LayoutPath.java b/src/java.base/share/classes/jdk/internal/foreign/LayoutPath.java index 6d339f2b88278..8e1ae388bbb9d 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/LayoutPath.java +++ b/src/java.base/share/classes/jdk/internal/foreign/LayoutPath.java @@ -31,6 +31,7 @@ import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; import java.lang.foreign.SequenceLayout; +import java.lang.foreign.StructLayout; import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -50,6 +51,9 @@ */ public class LayoutPath { + private static final long[] EMPTY_STRIDES = new long[0]; + private static final long[] EMPTY_BOUNDS = new long[0]; + private static final MethodHandle MH_ADD_SCALED_OFFSET; private static final MethodHandle MH_SLICE; @@ -123,7 +127,7 @@ public LayoutPath groupElement(String name) { l.name().get().equals(name)) { elem = l; break; - } else if (g.isStruct()) { + } else if (g instanceof StructLayout) { offset += l.bitSize(); } } @@ -180,11 +184,11 @@ public MethodHandle offsetHandle() { public MethodHandle sliceHandle() { if (strides.length == 0) { // trigger checks eagerly - Utils.bitsToBytesOrThrow(offset, Utils.bitsToBytesThrowOffset); + Utils.bitsToBytesOrThrow(offset, Utils.BITS_TO_BYTES_THROW_OFFSET); } MethodHandle offsetHandle = offsetHandle(); // bit offset - offsetHandle = MethodHandles.filterReturnValue(offsetHandle, Utils.MH_bitsToBytesOrThrowForOffset); // byte offset + offsetHandle = MethodHandles.filterReturnValue(offsetHandle, Utils.MH_BITS_TO_BYTES_OR_THROW_FOR_OFFSET); // byte offset MethodHandle sliceHandle = MH_SLICE; // (MS, long, long) -> MS sliceHandle = MethodHandles.insertArguments(sliceHandle, 2, layout.byteSize()); // (MS, long) -> MS @@ -257,9 +261,6 @@ private long[] addBound(long maxIndex) { return newBounds; } - private static final long[] EMPTY_STRIDES = new long[0]; - private static final long[] EMPTY_BOUNDS = new long[0]; - /** * This class provides an immutable implementation for the {@code PathElement} interface. A path element implementation * is simply a pointer to one of the selector methods provided by the {@code LayoutPath} class. diff --git a/src/java.base/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java index 8ca0630995abe..1d9465b8b41c3 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java @@ -26,7 +26,7 @@ package jdk.internal.foreign; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.nio.ByteBuffer; import jdk.internal.access.foreign.UnmapperProxy; import jdk.internal.misc.ScopedMemoryAccess; @@ -37,25 +37,25 @@ * memory mapped segment, such as the file descriptor associated with the mapping. This information is crucial * in order to correctly reconstruct a byte buffer object from the segment (see {@link #makeByteBuffer()}). */ -public class MappedMemorySegmentImpl extends NativeMemorySegmentImpl { +public sealed class MappedMemorySegmentImpl extends NativeMemorySegmentImpl { private final UnmapperProxy unmapper; - static ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess(); + static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess(); - public MappedMemorySegmentImpl(long min, UnmapperProxy unmapper, long length, boolean readOnly, MemorySession session) { + public MappedMemorySegmentImpl(long min, UnmapperProxy unmapper, long length, boolean readOnly, SegmentScope session) { super(min, length, readOnly, session); this.unmapper = unmapper; } @Override ByteBuffer makeByteBuffer() { - return nioAccess.newMappedByteBuffer(unmapper, min, (int)length, null, + return NIO_ACCESS.newMappedByteBuffer(unmapper, min, (int)length, null, session == MemorySessionImpl.GLOBAL ? null : this); } @Override - MappedMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySession session) { + MappedMemorySegmentImpl dup(long offset, long size, boolean readOnly, SegmentScope session) { return new MappedMemorySegmentImpl(min + offset, unmapper, size, readOnly, session); } @@ -93,7 +93,7 @@ public void force() { SCOPED_MEMORY_ACCESS.force(sessionImpl(), unmapper.fileDescriptor(), min, unmapper.isSync(), 0, length); } - public static class EmptyMappedMemorySegmentImpl extends MappedMemorySegmentImpl { + public static final class EmptyMappedMemorySegmentImpl extends MappedMemorySegmentImpl { public EmptyMappedMemorySegmentImpl(boolean readOnly, MemorySessionImpl session) { super(0, null, 0, readOnly, session); diff --git a/src/java.base/share/classes/jdk/internal/foreign/MemoryAddressImpl.java b/src/java.base/share/classes/jdk/internal/foreign/MemoryAddressImpl.java deleted file mode 100644 index 69002934c3873..0000000000000 --- a/src/java.base/share/classes/jdk/internal/foreign/MemoryAddressImpl.java +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ -package jdk.internal.foreign; - -import java.lang.foreign.Addressable; -import java.lang.foreign.MemoryAddress; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; -import java.lang.foreign.ValueLayout; -import jdk.internal.foreign.abi.SharedUtils; -import jdk.internal.reflect.CallerSensitive; -import jdk.internal.reflect.Reflection; -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, Scoped { - - private final long offset; - - public MemoryAddressImpl(long offset) { - this.offset = offset; - } - - // MemoryAddress methods - - @Override - public MemoryAddress addOffset(long offset) { - return new MemoryAddressImpl(this.offset + offset); - } - - @Override - public long toRawLongValue() { - return offset; - } - - @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, MemorySession session) { - return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(MemoryAddress.ofLong(value), byteSize, session); - } - - public static MemorySegment ofLongUnchecked(long value, long byteSize) { - return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(MemoryAddress.ofLong(value), byteSize, MemorySessionImpl.GLOBAL); - } - - @Override - public MemorySessionImpl session() { - return MemorySessionImpl.GLOBAL; - } - - @Override - @CallerSensitive - @ForceInline - public String getUtf8String(long offset) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "getUtf8String"); - SharedUtils.checkAddress(this); - return NativeMemorySegmentImpl.EVERYTHING.getUtf8String(toRawLongValue() + offset); - } - - @Override - @CallerSensitive - @ForceInline - public void setUtf8String(long offset, String str) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "setUtf8String"); - SharedUtils.checkAddress(this); - NativeMemorySegmentImpl.EVERYTHING.setUtf8String(toRawLongValue() + offset, str); - } - - @Override - @ForceInline - @CallerSensitive - public byte get(ValueLayout.OfByte layout, long offset) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "get"); - return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset); - } - - @Override - @ForceInline - @CallerSensitive - public void set(ValueLayout.OfByte layout, long offset, byte value) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "set"); - NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value); - } - - @Override - @ForceInline - @CallerSensitive - public boolean get(ValueLayout.OfBoolean layout, long offset) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "get"); - return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset); - } - - @Override - @ForceInline - @CallerSensitive - public void set(ValueLayout.OfBoolean layout, long offset, boolean value) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "set"); - NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value); - } - - @Override - @ForceInline - @CallerSensitive - public char get(ValueLayout.OfChar layout, long offset) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "get"); - return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset); - } - - @Override - @ForceInline - @CallerSensitive - public void set(ValueLayout.OfChar layout, long offset, char value) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "set"); - NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value); - } - - @Override - @ForceInline - @CallerSensitive - public short get(ValueLayout.OfShort layout, long offset) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "get"); - return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset); - } - - @Override - @ForceInline - @CallerSensitive - public void set(ValueLayout.OfShort layout, long offset, short value) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "set"); - NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value); - } - - @Override - @ForceInline - @CallerSensitive - public int get(ValueLayout.OfInt layout, long offset) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "get"); - return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset); - } - - @Override - @ForceInline - @CallerSensitive - public void set(ValueLayout.OfInt layout, long offset, int value) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "set"); - NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value); - } - - @Override - @ForceInline - @CallerSensitive - public float get(ValueLayout.OfFloat layout, long offset) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "get"); - return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset); - } - - @Override - @ForceInline - @CallerSensitive - public void set(ValueLayout.OfFloat layout, long offset, float value) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "set"); - NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value); - } - - @Override - @ForceInline - @CallerSensitive - public long get(ValueLayout.OfLong layout, long offset) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "get"); - return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset); - } - - @Override - @ForceInline - @CallerSensitive - public void set(ValueLayout.OfLong layout, long offset, long value) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "set"); - NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value); - } - - @Override - @ForceInline - @CallerSensitive - public double get(ValueLayout.OfDouble layout, long offset) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "get"); - return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset); - } - - @Override - @ForceInline - @CallerSensitive - public void set(ValueLayout.OfDouble layout, long offset, double value) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "set"); - NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value); - } - - @Override - @ForceInline - @CallerSensitive - public MemoryAddress get(ValueLayout.OfAddress layout, long offset) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "get"); - return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset); - } - - @Override - @ForceInline - @CallerSensitive - public void set(ValueLayout.OfAddress layout, long offset, Addressable value) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "set"); - NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value.address()); - } - - @Override - @ForceInline - @CallerSensitive - public char getAtIndex(ValueLayout.OfChar layout, long index) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "getAtIndex"); - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - 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(), MemoryAddress.class, "setAtIndex"); - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value); - } - - @Override - @ForceInline - @CallerSensitive - public short getAtIndex(ValueLayout.OfShort layout, long index) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "getAtIndex"); - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - 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(), MemoryAddress.class, "setAtIndex"); - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value); - } - - @Override - @ForceInline - @CallerSensitive - public int getAtIndex(ValueLayout.OfInt layout, long index) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "getAtIndex"); - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - 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(), MemoryAddress.class, "setAtIndex"); - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value); - } - - @Override - @ForceInline - @CallerSensitive - public float getAtIndex(ValueLayout.OfFloat layout, long index) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "getAtIndex"); - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - 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(), MemoryAddress.class, "setAtIndex"); - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value); - } - - @Override - @ForceInline - @CallerSensitive - public long getAtIndex(ValueLayout.OfLong layout, long index) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "getAtIndex"); - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - 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(), MemoryAddress.class, "setAtIndex"); - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value); - } - - @Override - @ForceInline - @CallerSensitive - public double getAtIndex(ValueLayout.OfDouble layout, long index) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "getAtIndex"); - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - 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(), MemoryAddress.class, "setAtIndex"); - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value); - } - - @Override - @ForceInline - @CallerSensitive - public MemoryAddress getAtIndex(ValueLayout.OfAddress layout, long index) { - Reflection.ensureNativeAccess(Reflection.getCallerClass(), MemoryAddress.class, "getAtIndex"); - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - 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(), MemoryAddress.class, "setAtIndex"); - Utils.checkElementAlignment(layout, "Layout alignment greater than its size"); - NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value.address()); - } -} diff --git a/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java b/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java index 97264782a2bd6..b8d1036baab15 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java @@ -26,23 +26,21 @@ package jdk.internal.foreign; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.SegmentAllocator; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.lang.ref.Cleaner; -import java.lang.ref.Reference; import java.util.Objects; import jdk.internal.misc.ScopedMemoryAccess; -import jdk.internal.ref.CleanerFactory; import jdk.internal.vm.annotation.ForceInline; -import sun.nio.ch.DirectBuffer; /** * This class manages the temporal bounds associated with a memory segment as well * as thread confinement. A session has a liveness bit, which is updated when the session is closed - * (this operation is triggered by {@link MemorySession#close()}). This bit is consulted prior + * (this operation is triggered by {@link MemorySessionImpl#close()}). This bit is consulted prior * to memory access (see {@link #checkValidStateRaw()}). * There are two kinds of memory session: confined memory session and shared memory session. * A confined memory session has an associated owner thread that confines some operations to @@ -52,30 +50,54 @@ * shared sessions use a more sophisticated synchronization mechanism, which guarantees that no concurrent * access is possible when a session is being closed (see {@link jdk.internal.misc.ScopedMemoryAccess}). */ -public abstract non-sealed class MemorySessionImpl implements MemorySession, SegmentAllocator { - final ResourceList resourceList; - final Cleaner.Cleanable cleanable; - final Thread owner; - +public abstract sealed class MemorySessionImpl + implements SegmentScope, SegmentAllocator + permits ConfinedSession, GlobalSession, SharedSession { static final int OPEN = 0; static final int CLOSING = -1; static final int CLOSED = -2; - int state = OPEN; - static final VarHandle STATE; + static final int MAX_FORKS = Integer.MAX_VALUE; + + public static final MemorySessionImpl GLOBAL = new GlobalSession(null); + + static final ScopedMemoryAccess.ScopedAccessError ALREADY_CLOSED = new ScopedMemoryAccess.ScopedAccessError(MemorySessionImpl::alreadyClosed); + static final ScopedMemoryAccess.ScopedAccessError WRONG_THREAD = new ScopedMemoryAccess.ScopedAccessError(MemorySessionImpl::wrongThread); + + final ResourceList resourceList; + final Thread owner; + int state = OPEN; static { try { STATE = MethodHandles.lookup().findVarHandle(MemorySessionImpl.class, "state", int.class); - } catch (Throwable ex) { + } catch (Exception ex) { throw new ExceptionInInitializerError(ex); } } - static final int MAX_FORKS = Integer.MAX_VALUE; + public Arena asArena() { + return new Arena() { + @Override + public SegmentScope scope() { + return MemorySessionImpl.this; + } + + @Override + public void close() { + MemorySessionImpl.this.close(); + } + + @Override + public boolean isCloseableBy(Thread thread) { + Objects.requireNonNull(thread); + return ownerThread() == null || // shared + ownerThread() == thread; + } + }; + } - @Override public void addCloseAction(Runnable runnable) { Objects.requireNonNull(runnable); addInternal(ResourceList.ResourceCleanup.ofRunnable(runnable)); @@ -111,45 +133,33 @@ void addInternal(ResourceList.ResourceCleanup resource) { resourceList.add(resource); } - protected MemorySessionImpl(Thread owner, ResourceList resourceList, Cleaner cleaner) { + protected MemorySessionImpl(Thread owner, ResourceList resourceList) { this.owner = owner; this.resourceList = resourceList; - cleanable = (cleaner != null) ? - cleaner.register(this, resourceList) : null; } - public static MemorySession createConfined(Thread thread, Cleaner cleaner) { - return new ConfinedSession(thread, cleaner); + public static MemorySessionImpl createConfined(Thread thread) { + return new ConfinedSession(thread); } - public static MemorySession createShared(Cleaner cleaner) { - return new SharedSession(cleaner); + public static MemorySessionImpl createShared() { + return new SharedSession(); } - public static MemorySessionImpl createImplicit() { - return new ImplicitSession(); + public static MemorySessionImpl createImplicit(Cleaner cleaner) { + return new ImplicitSession(cleaner); } @Override - public MemorySegment allocate(long bytesSize, long bytesAlignment) { - return MemorySegment.allocateNative(bytesSize, bytesAlignment, this); + public MemorySegment allocate(long byteSize, long byteAlignment) { + Utils.checkAllocationSizeAndAlign(byteSize, byteAlignment); + return NativeMemorySegmentImpl.makeNativeSegment(byteSize, byteAlignment, this); } public abstract void release0(); public abstract void acquire0(); - @Override - public final boolean equals(Object o) { - return (o instanceof MemorySession other) && - toSessionImpl(other) == this; - } - - @Override - public final int hashCode() { - return super.hashCode(); - } - @Override public void whileAlive(Runnable action) { Objects.requireNonNull(action); @@ -161,14 +171,21 @@ public void whileAlive(Runnable action) { } } - /** - * Returns "owner" thread of this session. - * @return owner thread (or null for a shared session) - */ public final Thread ownerThread() { return owner; } + public static boolean sameOwnerThread(SegmentScope session1, SegmentScope session2) { + return ((MemorySessionImpl) session1).ownerThread() == + ((MemorySessionImpl) session2).ownerThread(); + } + + @Override + public final boolean isAccessibleBy(Thread thread) { + Objects.requireNonNull(thread); + return owner == thread; + } + /** * Returns true, if this session is still open. This method may be called in any thread. * @return {@code true} if this session is not closed yet. @@ -177,18 +194,6 @@ public boolean isAlive() { return state >= OPEN; } - @Override - public MemorySession asNonCloseable() { - return isCloseable() ? - new NonCloseableView(this) : this; - } - - @ForceInline - public static MemorySessionImpl toSessionImpl(MemorySession session) { - return session instanceof MemorySessionImpl sessionImpl ? - sessionImpl : ((NonCloseableView)session).session; - } - /** * This is a faster version of {@link #checkValidState()}, which is called upon memory access, and which * relies on invariants associated with the memory session implementations (volatile access @@ -225,7 +230,6 @@ protected Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); } - @Override public boolean isCloseable() { return true; } @@ -235,163 +239,15 @@ public boolean isCloseable() { * @throws IllegalStateException if this session is already closed or if this is * a confined session and this method is called outside of the owner thread. */ - @Override public void close() { - try { - justClose(); - if (cleanable != null) { - cleanable.clean(); - } else { - resourceList.cleanup(); - } - } finally { - Reference.reachabilityFence(this); - } + justClose(); + resourceList.cleanup(); } abstract void justClose(); - /** - * The global, non-closeable, shared session. Similar to a shared session, but its {@link #close()} method throws unconditionally. - * Adding new resources to the global session, does nothing: as the session can never become not-alive, there is nothing to track. - * Acquiring and or releasing a memory session similarly does nothing. - */ - static class GlobalSessionImpl extends MemorySessionImpl { - - final Object ref; - - public GlobalSessionImpl(Object ref) { - super(null, null ,null); - this.ref = ref; - } - - @Override - @ForceInline - public void release0() { - // do nothing - } - - @Override - public boolean isCloseable() { - return false; - } - - @Override - @ForceInline - public void acquire0() { - // do nothing - } - - @Override - void addInternal(ResourceList.ResourceCleanup resource) { - // do nothing - } - - @Override - public void justClose() { - throw nonCloseable(); - } - } - - public static final MemorySessionImpl GLOBAL = new GlobalSessionImpl(null); - public static MemorySessionImpl heapSession(Object ref) { - return new GlobalSessionImpl(ref); - } - - /** - * This is an implicit, GC-backed memory session. Implicit sessions cannot be closed explicitly. - * While it would be possible to model an implicit session as a non-closeable view of a shared - * session, it is better to capture the fact that an implicit session is not just a non-closeable - * view of some session which might be closeable. This is useful e.g. in the implementations of - * {@link DirectBuffer#address()}, where obtaining an address of a buffer instance associated - * with a potentially closeable session is forbidden. - */ - static class ImplicitSession extends SharedSession { - - public ImplicitSession() { - super(CleanerFactory.cleaner()); - } - - @Override - public void release0() { - Reference.reachabilityFence(this); - } - - @Override - public void acquire0() { - // do nothing - } - - @Override - public boolean isCloseable() { - return false; - } - - @Override - public void justClose() { - throw nonCloseable(); - } - } - - /** - * This is a non-closeable view of another memory session. Instances of this class are used in resource session - * accessors (see {@link MemorySegment#session()}). This class forwards all session methods to the underlying - * "root" session implementation, and throws {@link UnsupportedOperationException} on close. This class contains - * a strong reference to the original session, so even if the original session is dropped by the client - * it would still be reachable by the GC, which is important if the session is implicitly closed. - */ - public final static class NonCloseableView implements MemorySession { - final MemorySessionImpl session; - - public NonCloseableView(MemorySessionImpl session) { - this.session = session; - } - - @Override - public boolean isAlive() { - return session.isAlive(); - } - - @Override - public boolean isCloseable() { - return false; - } - - @Override - public Thread ownerThread() { - return session.ownerThread(); - } - - @Override - public boolean equals(Object o) { - return session.equals(o); - } - - @Override - public int hashCode() { - return session.hashCode(); - } - - @Override - public void whileAlive(Runnable action) { - session.whileAlive(action); - } - - @Override - public MemorySession asNonCloseable() { - return this; - } - - @Override - public void addCloseAction(Runnable runnable) { - session.addCloseAction(runnable); - } - - @Override - public void close() { - throw new UnsupportedOperationException(); - } + return new GlobalSession(ref); } /** @@ -464,7 +320,4 @@ static UnsupportedOperationException nonCloseable() { return new UnsupportedOperationException("Attempted to close a non-closeable session"); } - static final ScopedMemoryAccess.ScopedAccessError ALREADY_CLOSED = new ScopedMemoryAccess.ScopedAccessError(MemorySessionImpl::alreadyClosed); - - static final ScopedMemoryAccess.ScopedAccessError WRONG_THREAD = new ScopedMemoryAccess.ScopedAccessError(MemorySessionImpl::wrongThread); } diff --git a/src/java.base/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java index 6d3c786550f59..388937b823dfe 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java @@ -26,10 +26,11 @@ package jdk.internal.foreign; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.nio.ByteBuffer; +import java.util.Optional; + import jdk.internal.misc.Unsafe; import jdk.internal.misc.VM; import jdk.internal.vm.annotation.ForceInline; @@ -39,51 +40,42 @@ * Implementation for native memory segments. A native memory segment is essentially a wrapper around * a native long address. */ -public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl { - - public static final MemorySegment EVERYTHING = new NativeMemorySegmentImpl(0, Long.MAX_VALUE, false, MemorySessionImpl.GLOBAL) { - @Override - void checkBounds(long offset, long length) { - // do nothing - } +public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl permits MappedMemorySegmentImpl { - @Override - NativeMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySession session) { - throw new IllegalStateException(); - } - }; - - private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); // 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; - - private static final boolean skipZeroMemory = GetBooleanAction.privilegedGetProperty("jdk.internal.foreign.skipZeroMemory"); + private static final boolean SKIP_ZERO_MEMORY = GetBooleanAction.privilegedGetProperty("jdk.internal.foreign.skipZeroMemory"); final long min; @ForceInline - NativeMemorySegmentImpl(long min, long length, boolean readOnly, MemorySession session) { + NativeMemorySegmentImpl(long min, long length, boolean readOnly, SegmentScope session) { super(length, readOnly, session); this.min = min; } - @ForceInline @Override - public MemoryAddress address() { - checkValidState(); - return MemoryAddress.ofLong(unsafeGetOffset()); + public long address() { + return min; + } + + @Override + public Optional array() { + return Optional.empty(); } + @ForceInline @Override - NativeMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySession session) { + NativeMemorySegmentImpl dup(long offset, long size, boolean readOnly, SegmentScope session) { return new NativeMemorySegmentImpl(min + offset, size, readOnly, session); } @Override ByteBuffer makeByteBuffer() { - return nioAccess.newDirectByteBuffer(min, (int) this.length, null, + return NIO_ACCESS.newDirectByteBuffer(min, (int) this.length, null, session == MemorySessionImpl.GLOBAL ? null : this); } @@ -109,42 +101,62 @@ public long maxAlignMask() { // factories - public static MemorySegment makeNativeSegment(long bytesSize, long alignmentBytes, MemorySession session) { - MemorySessionImpl sessionImpl = MemorySessionImpl.toSessionImpl(session); + public static MemorySegment makeNativeSegment(long byteSize, long byteAlignment, SegmentScope session) { + MemorySessionImpl sessionImpl = (MemorySessionImpl) session; sessionImpl.checkValidState(); if (VM.isDirectMemoryPageAligned()) { - alignmentBytes = Math.max(alignmentBytes, nioAccess.pageSize()); + byteAlignment = Math.max(byteAlignment, NIO_ACCESS.pageSize()); } - long alignedSize = Math.max(1L, alignmentBytes > MAX_MALLOC_ALIGN ? - bytesSize + (alignmentBytes - 1) : - bytesSize); + long alignedSize = Math.max(1L, byteAlignment > MAX_MALLOC_ALIGN ? + byteSize + (byteAlignment - 1) : + byteSize); - nioAccess.reserveMemory(alignedSize, bytesSize); + NIO_ACCESS.reserveMemory(alignedSize, byteSize); - long buf = unsafe.allocateMemory(alignedSize); - if (!skipZeroMemory) { - unsafe.setMemory(buf, alignedSize, (byte)0); + long buf = UNSAFE.allocateMemory(alignedSize); + if (!SKIP_ZERO_MEMORY) { + UNSAFE.setMemory(buf, alignedSize, (byte)0); } - long alignedBuf = Utils.alignUp(buf, alignmentBytes); + long alignedBuf = Utils.alignUp(buf, byteAlignment); AbstractMemorySegmentImpl segment = new NativeMemorySegmentImpl(buf, alignedSize, false, session); sessionImpl.addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() { @Override public void cleanup() { - unsafe.freeMemory(buf); - nioAccess.unreserveMemory(alignedSize, bytesSize); + UNSAFE.freeMemory(buf); + NIO_ACCESS.unreserveMemory(alignedSize, byteSize); } }); - if (alignedSize != bytesSize) { + if (alignedSize != byteSize) { long delta = alignedBuf - buf; - segment = segment.asSlice(delta, bytesSize); + segment = segment.asSlice(delta, byteSize); } return segment; } - public static MemorySegment makeNativeSegmentUnchecked(MemoryAddress min, long bytesSize, MemorySession session) { - MemorySessionImpl.toSessionImpl(session).checkValidState(); - AbstractMemorySegmentImpl segment = new NativeMemorySegmentImpl(min.toRawLongValue(), bytesSize, false, session); - return segment; + // Unsafe native segment factories. These are used by the implementation code, to skip the sanity checks + // associated with MemorySegment::ofAddress. + + @ForceInline + public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, SegmentScope session, Runnable action) { + MemorySessionImpl sessionImpl = (MemorySessionImpl) session; + if (action == null) { + sessionImpl.checkValidState(); + } else { + sessionImpl.addCloseAction(action); + } + return new NativeMemorySegmentImpl(min, byteSize, false, session); + } + + @ForceInline + public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, SegmentScope session) { + MemorySessionImpl sessionImpl = (MemorySessionImpl) session; + sessionImpl.checkValidState(); + return new NativeMemorySegmentImpl(min, byteSize, false, session); + } + + @ForceInline + public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize) { + return new NativeMemorySegmentImpl(min, byteSize, false, SegmentScope.global()); } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/PlatformLayouts.java b/src/java.base/share/classes/jdk/internal/foreign/PlatformLayouts.java index ab99b1fabebca..6307b7a573792 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/PlatformLayouts.java +++ b/src/java.base/share/classes/jdk/internal/foreign/PlatformLayouts.java @@ -25,10 +25,13 @@ */ package jdk.internal.foreign; -import java.lang.foreign.MemoryLayout; import java.lang.foreign.ValueLayout; -public class PlatformLayouts { +public final class PlatformLayouts { + + private PlatformLayouts() { + //just the one + } /** * This class defines layout constants modelling standard primitive types supported by the x64 SystemV ABI. @@ -81,7 +84,7 @@ private SysV() { /** * The {@code T*} native type. */ - public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64); + public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64).asUnbounded(); /** * The {@code va_list} native type, as it is passed to a function. @@ -140,7 +143,7 @@ private Win64() { /** * The {@code T*} native type. */ - public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64); + public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64).asUnbounded(); /** * The {@code va_list} native type, as it is passed to a function. @@ -200,7 +203,7 @@ private AArch64() { /** * The {@code T*} native type. */ - public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64); + public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64).asUnbounded(); /** * The {@code va_list} native type, as it is passed to a function. diff --git a/src/java.base/share/classes/jdk/internal/foreign/SharedSession.java b/src/java.base/share/classes/jdk/internal/foreign/SharedSession.java index 0955d66d91f9f..984d9d9992161 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/SharedSession.java +++ b/src/java.base/share/classes/jdk/internal/foreign/SharedSession.java @@ -40,12 +40,12 @@ * Since it is the responsibility of the closing thread to make sure that no concurrent access is possible, * checking the liveness bit upon access can be performed in plain mode, as in the confined case. */ -class SharedSession extends MemorySessionImpl { +sealed class SharedSession extends MemorySessionImpl permits ImplicitSession { private static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess(); - SharedSession(Cleaner cleaner) { - super(null, new SharedResourceList(), cleaner); + SharedSession() { + super(null, new SharedResourceList()); } @Override diff --git a/src/java.base/share/classes/java/lang/foreign/Addressable.java b/src/java.base/share/classes/jdk/internal/foreign/SlicingAllocator.java similarity index 51% rename from src/java.base/share/classes/java/lang/foreign/Addressable.java rename to src/java.base/share/classes/jdk/internal/foreign/SlicingAllocator.java index c1b8f5fc29d22..435381a3021b6 100644 --- a/src/java.base/share/classes/java/lang/foreign/Addressable.java +++ b/src/java.base/share/classes/jdk/internal/foreign/SlicingAllocator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,25 +23,35 @@ * questions. */ -package java.lang.foreign; +package jdk.internal.foreign; -import jdk.internal.javac.PreviewFeature; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SegmentAllocator; -/** - * An object that may be projected down to a {@linkplain #address() memory address}. - * Examples of addressable types are {@link MemorySegment}, {@link MemoryAddress} and {@link VaList}. - *

    - * The {@link Addressable} type is used by a {@linkplain Linker linker} to model the types of - * {@linkplain Linker#downcallHandle(FunctionDescriptor) downcall handle} parameters that must be passed by reference - * (e.g. memory addresses, variable argument lists and upcall stubs). - * - * @since 19 - */ -@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) -public sealed interface Addressable permits MemorySegment, MemoryAddress, VaList { +public final class SlicingAllocator implements SegmentAllocator { + + private final MemorySegment segment; + private final long maxAlign; + + private long sp = 0L; + + public SlicingAllocator(MemorySegment segment) { + this.segment = segment; + this.maxAlign = ((AbstractMemorySegmentImpl)segment).maxAlignMask(); + } + + MemorySegment trySlice(long byteSize, long byteAlignment) { + long min = segment.address(); + long start = Utils.alignUp(min + sp, byteAlignment) - min; + MemorySegment slice = segment.asSlice(start, byteSize); + sp = start + byteSize; + return slice; + } - /** - * {@return the {@linkplain MemoryAddress memory address} associated with this addressable} - */ - MemoryAddress address(); + @Override + public MemorySegment allocate(long byteSize, long byteAlignment) { + Utils.checkAllocationSizeAndAlign(byteSize, byteAlignment, maxAlign); + // try to slice from current segment first... + return trySlice(byteSize, byteAlignment); + } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/SystemLookup.java b/src/java.base/share/classes/jdk/internal/foreign/SystemLookup.java index 0e526577c6292..8fad1abf2944f 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/SystemLookup.java +++ b/src/java.base/share/classes/jdk/internal/foreign/SystemLookup.java @@ -25,9 +25,8 @@ package jdk.internal.foreign; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.SymbolLookup; import java.lang.invoke.MethodHandles; import java.nio.file.Files; @@ -41,32 +40,32 @@ import static java.lang.foreign.ValueLayout.ADDRESS; -public class SystemLookup implements SymbolLookup { +public final class SystemLookup implements SymbolLookup { private SystemLookup() { } - static final SystemLookup INSTANCE = new SystemLookup(); + private static final SystemLookup INSTANCE = new SystemLookup(); /* A fallback lookup, used when creation of system lookup fails. */ - private static final SymbolLookup fallbackLookup = name -> Optional.empty(); + private static final SymbolLookup FALLBACK_LOOKUP = name -> Optional.empty(); /* * On POSIX systems, dlsym will allow us to lookup symbol in library dependencies; the same trick doesn't work * on Windows. For this reason, on Windows we do not generate any side-library, and load msvcrt.dll directly instead. */ - private static final SymbolLookup syslookup = makeSystemLookup(); + private static final SymbolLookup SYSTEM_LOOKUP = makeSystemLookup(); - private static final SymbolLookup makeSystemLookup() { + private static SymbolLookup makeSystemLookup() { try { return switch (CABI.current()) { - case SysV, LinuxAArch64, MacOsAArch64 -> libLookup(libs -> libs.load(jdkLibraryPath("syslookup"))); - case Win64 -> makeWindowsLookup(); // out of line to workaround javac crash + case SYS_V, LINUX_AARCH_64, MAC_OS_AARCH_64 -> libLookup(libs -> libs.load(jdkLibraryPath("syslookup"))); + case WIN_64 -> makeWindowsLookup(); // out of line to workaround javac crash }; } catch (Throwable ex) { // This can happen in the event of a library loading failure - e.g. if one of the libraries the // system lookup depends on cannot be loaded for some reason. In such extreme cases, rather than // fail, return a dummy lookup. - return fallbackLookup; + return FALLBACK_LOOKUP; } } @@ -86,14 +85,14 @@ private static SymbolLookup makeWindowsLookup() { libLookup(libs -> libs.load(jdkLibraryPath("syslookup"))); int numSymbols = WindowsFallbackSymbols.values().length; - MemorySegment funcs = MemorySegment.ofAddress(fallbackLibLookup.lookup("funcs").orElseThrow().address(), - ADDRESS.byteSize() * numSymbols, MemorySession.global()); + MemorySegment funcs = MemorySegment.ofAddress(fallbackLibLookup.find("funcs").orElseThrow().address(), + ADDRESS.byteSize() * numSymbols, SegmentScope.global()); Function> fallbackLookup = name -> Optional.ofNullable(WindowsFallbackSymbols.valueOfOrNull(name)) - .map(symbol -> MemorySegment.ofAddress(funcs.getAtIndex(ADDRESS, symbol.ordinal()), 0L, MemorySession.global())); + .map(symbol -> MemorySegment.ofAddress(funcs.getAtIndex(ADDRESS, symbol.ordinal()).address(), 0L, SegmentScope.global())); final SymbolLookup finalLookup = lookup; - lookup = name -> finalLookup.lookup(name).or(() -> fallbackLookup.apply(name)); + lookup = name -> finalLookup.find(name).or(() -> fallbackLookup.apply(name)); } return lookup; @@ -107,7 +106,7 @@ private static SymbolLookup libLookup(Function "lib"; - case Win64 -> "bin"; + case SYS_V, LINUX_AARCH_64, MAC_OS_AARCH_64 -> "lib"; + case WIN_64 -> "bin"; }; String libname = System.mapLibraryName(name); return javahome.resolve(lib).resolve(libname); @@ -133,8 +132,8 @@ public static SystemLookup getInstance() { } @Override - public Optional lookup(String name) { - return syslookup.lookup(name); + public Optional find(String name) { + return SYSTEM_LOOKUP.find(name); } // fallback symbols missing from ucrtbase.dll diff --git a/src/java.base/share/classes/jdk/internal/foreign/Utils.java b/src/java.base/share/classes/jdk/internal/foreign/Utils.java index 0c43fefa28469..f00c49bb442eb 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/Utils.java +++ b/src/java.base/share/classes/jdk/internal/foreign/Utils.java @@ -26,7 +26,6 @@ package jdk.internal.foreign; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; import java.lang.foreign.SegmentAllocator; @@ -49,11 +48,12 @@ public final class Utils { 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; + private static final MethodHandle LONG_TO_ADDRESS_SAFE; + private static final MethodHandle LONG_TO_ADDRESS_UNSAFE; + public static final MethodHandle MH_BITS_TO_BYTES_OR_THROW_FOR_OFFSET; - public static final Supplier bitsToBytesThrowOffset - = () -> new UnsupportedOperationException("Cannot compute byte offset; bit offset is not a multiple of 8"); + public static final Supplier BITS_TO_BYTES_THROW_OFFSET + = () -> new UnsupportedOperationException("Cannot compute byte offset; bit offset is not a multiple of 8"); static { try { @@ -62,15 +62,17 @@ public final class Utils { 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", + ADDRESS_TO_LONG = lookup.findVirtual(MemorySegment.class, "address", 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)), - 1, - bitsToBytesThrowOffset); + LONG_TO_ADDRESS_SAFE = lookup.findStatic(Utils.class, "longToAddressSafe", + MethodType.methodType(MemorySegment.class, long.class)); + LONG_TO_ADDRESS_UNSAFE = lookup.findStatic(Utils.class, "longToAddressUnsafe", + MethodType.methodType(MemorySegment.class, long.class)); + MH_BITS_TO_BYTES_OR_THROW_FOR_OFFSET = MethodHandles.insertArguments( + lookup.findStatic(Utils.class, "bitsToBytesOrThrow", + MethodType.methodType(long.class, long.class, Supplier.class)), + 1, + BITS_TO_BYTES_THROW_OFFSET); } catch (Throwable ex) { throw new ExceptionInInitializerError(ex); } @@ -80,13 +82,8 @@ public static long alignUp(long n, long alignment) { return (n + alignment - 1) & -alignment; } - public static MemoryAddress alignUp(MemoryAddress ma, long alignment) { - long offset = ma.toRawLongValue(); - return ma.addOffset(alignUp(offset, alignment) - offset); - } - public static MemorySegment alignUp(MemorySegment ms, long alignment) { - long offset = ms.address().toRawLongValue(); + long offset = ms.address(); return ms.asSlice(alignUp(offset, alignment) - offset); } @@ -108,7 +105,7 @@ static VarHandle put(ValueLayout layout, VarHandle handle) { } } Class baseCarrier = layout.carrier(); - if (layout.carrier() == MemoryAddress.class) { + if (layout.carrier() == MemorySegment.class) { baseCarrier = switch ((int) ValueLayout.ADDRESS.byteSize()) { case 8 -> long.class; case 4 -> int.class; @@ -123,15 +120,16 @@ static VarHandle put(ValueLayout layout, VarHandle handle) { if (layout.carrier() == boolean.class) { handle = MethodHandles.filterValue(handle, BOOL_TO_BYTE, BYTE_TO_BOOL); - } else if (layout.carrier() == MemoryAddress.class) { + } else if (layout instanceof ValueLayout.OfAddress addressLayout) { handle = MethodHandles.filterValue(handle, - MethodHandles.explicitCastArguments(ADDRESS_TO_LONG, MethodType.methodType(baseCarrier, MemoryAddress.class)), - MethodHandles.explicitCastArguments(LONG_TO_ADDRESS, MethodType.methodType(MemoryAddress.class, baseCarrier))); + MethodHandles.explicitCastArguments(ADDRESS_TO_LONG, MethodType.methodType(baseCarrier, MemorySegment.class)), + MethodHandles.explicitCastArguments(addressLayout.isUnbounded() ? + LONG_TO_ADDRESS_UNSAFE : LONG_TO_ADDRESS_SAFE, MethodType.methodType(MemorySegment.class, baseCarrier))); } return VarHandleCache.put(layout, handle); } - private static boolean byteToBoolean(byte b) { + public static boolean byteToBoolean(byte b) { return b != 0; } @@ -139,6 +137,16 @@ private static byte booleanToByte(boolean b) { return b ? (byte)1 : (byte)0; } + @ForceInline + private static MemorySegment longToAddressSafe(long addr) { + return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, 0); + } + + @ForceInline + private static MemorySegment longToAddressUnsafe(long addr) { + return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, Long.MAX_VALUE); + } + public static void copy(MemorySegment addr, byte[] bytes) { var heapSegment = MemorySegment.ofArray(bytes); addr.copyFrom(heapSegment); @@ -163,16 +171,31 @@ public static void checkElementAlignment(MemoryLayout layout, String msg) { } } - public static void checkAllocationSizeAndAlign(long bytesSize, long alignmentBytes) { + public static long pointeeSize(MemoryLayout layout) { + if (layout instanceof ValueLayout.OfAddress addressLayout) { + return addressLayout.isUnbounded() ? Long.MAX_VALUE : 0L; + } else { + throw new UnsupportedOperationException(); + } + } + + public static void checkAllocationSizeAndAlign(long byteSize, long byteAlignment, long maxAlignment) { + checkAllocationSizeAndAlign(byteSize, byteAlignment); + if (maxAlignment != 0 && byteAlignment > maxAlignment) { + throw new IllegalArgumentException("Invalid alignment constraint : " + byteAlignment + " > " + maxAlignment); + } + } + + public static void checkAllocationSizeAndAlign(long byteSize, long byteAlignment) { // size should be >= 0 - if (bytesSize < 0) { - throw new IllegalArgumentException("Invalid allocation size : " + bytesSize); + if (byteSize < 0) { + throw new IllegalArgumentException("Invalid allocation size : " + byteSize); } // alignment should be > 0, and power of two - if (alignmentBytes <= 0 || - ((alignmentBytes & (alignmentBytes - 1)) != 0L)) { - throw new IllegalArgumentException("Invalid alignment constraint : " + alignmentBytes); + if (byteAlignment <= 0 || + ((byteAlignment & (byteAlignment - 1)) != 0L)) { + throw new IllegalArgumentException("Invalid alignment constraint : " + byteAlignment); } } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java index 2cafff4f424ed..ef5ab91142d04 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java @@ -29,11 +29,15 @@ 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.foreign.layout.AbstractLayout; +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.SegmentScope; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.Linker; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SequenceLayout; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.util.Objects; @@ -41,29 +45,35 @@ public abstract sealed class AbstractLinker implements Linker permits LinuxAArch64Linker, MacOsAArch64Linker, SysVx64Linker, Windowsx64Linker { - private final SoftReferenceCache DOWNCALL_CACHE = new SoftReferenceCache<>(); + private record LinkRequest(FunctionDescriptor descriptor, LinkerOptions options) {} + private final SoftReferenceCache DOWNCALL_CACHE = new SoftReferenceCache<>(); @Override - public MethodHandle downcallHandle(FunctionDescriptor function) { + public MethodHandle downcallHandle(FunctionDescriptor function, Option... options) { Objects.requireNonNull(function); + Objects.requireNonNull(options); + checkHasNaturalAlignment(function); + LinkerOptions optionSet = LinkerOptions.of(options); - return DOWNCALL_CACHE.get(function, fd -> { - MethodType type = SharedUtils.inferMethodType(fd, false); - MethodHandle handle = arrangeDowncall(type, fd); - handle = SharedUtils.maybeInsertAllocator(handle); + return DOWNCALL_CACHE.get(new LinkRequest(function, optionSet), linkRequest -> { + FunctionDescriptor fd = linkRequest.descriptor(); + MethodType type = fd.toMethodType(); + MethodHandle handle = arrangeDowncall(type, fd, linkRequest.options()); + handle = SharedUtils.maybeInsertAllocator(fd, handle); return handle; }); } - protected abstract MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function); + protected abstract MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function, LinkerOptions options); @Override - public MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, MemorySession scope) { + public MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, SegmentScope scope) { Objects.requireNonNull(scope); Objects.requireNonNull(target); Objects.requireNonNull(function); + checkHasNaturalAlignment(function); SharedUtils.checkExceptions(target); - MethodType type = SharedUtils.inferMethodType(function, true); + MethodType type = function.toMethodType(); if (!type.equals(target.type())) { throw new IllegalArgumentException("Wrong method handle type: " + target.type()); } @@ -71,10 +81,35 @@ public MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function } protected abstract MemorySegment arrangeUpcall(MethodHandle target, MethodType targetType, - FunctionDescriptor function, MemorySession scope); + FunctionDescriptor function, SegmentScope scope); @Override public SystemLookup defaultLookup() { return SystemLookup.getInstance(); } + + // Current limitation of the implementation: + // We don't support packed structs on some platforms, + // so reject them here explicitly + private static void checkHasNaturalAlignment(FunctionDescriptor descriptor) { + descriptor.returnLayout().ifPresent(AbstractLinker::checkHasNaturalAlignmentRecursive); + descriptor.argumentLayouts().forEach(AbstractLinker::checkHasNaturalAlignmentRecursive); + } + + private static void checkHasNaturalAlignmentRecursive(MemoryLayout layout) { + checkHasNaturalAlignment(layout); + if (layout instanceof GroupLayout gl) { + for (MemoryLayout member : gl.memberLayouts()) { + checkHasNaturalAlignmentRecursive(member); + } + } else if (layout instanceof SequenceLayout sl) { + checkHasNaturalAlignmentRecursive(sl.elementLayout()); + } + } + + private static void checkHasNaturalAlignment(MemoryLayout layout) { + if (!((AbstractLayout) layout).hasNaturalAlignment()) { + throw new IllegalArgumentException("Layout bit alignment must be natural alignment: " + layout); + } + } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/Binding.java b/src/java.base/share/classes/jdk/internal/foreign/abi/Binding.java index 96a74a897b2b9..39c1116a2ca68 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/Binding.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/Binding.java @@ -24,27 +24,19 @@ */ package jdk.internal.foreign.abi; -import java.lang.foreign.Addressable; -import java.lang.foreign.MemoryAddress; +import jdk.internal.foreign.NativeMemorySegmentImpl; + +import java.lang.foreign.Arena; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.SegmentAllocator; -import java.lang.foreign.ValueLayout; -import jdk.internal.foreign.MemoryAddressImpl; - 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; -import java.util.Objects; - -import java.lang.invoke.VarHandle; -import java.nio.ByteOrder; - -import static java.lang.invoke.MethodType.methodType; /** * The binding operators defined in the Binding class can be combined into argument and return value processing 'recipes'. @@ -199,19 +191,19 @@ * * -------------------- */ -public abstract class Binding { +public interface Binding { /** * A binding context is used as an helper to carry out evaluation of certain bindings; for instance, * it helps {@link Allocate} bindings, by providing the {@link SegmentAllocator} that should be used for - * the allocation operation, or {@link ToSegment} bindings, by providing the {@link MemorySession} that + * the allocation operation, or {@link BoxAddress} bindings, by providing the {@link SegmentScope} that * should be used to create an unsafe struct from a memory address. */ - public static class Context implements AutoCloseable { + class Context implements AutoCloseable { private final SegmentAllocator allocator; - private final MemorySession session; + private final SegmentScope session; - private Context(SegmentAllocator allocator, MemorySession session) { + private Context(SegmentAllocator allocator, SegmentScope session) { this.allocator = allocator; this.session = session; } @@ -220,21 +212,26 @@ public SegmentAllocator allocator() { return allocator; } - public MemorySession session() { + public SegmentScope session() { return session; } @Override public void close() { - session().close(); + throw new UnsupportedOperationException(); } /** * Create a binding context from given native scope. */ public static Context ofBoundedAllocator(long size) { - MemorySession scope = MemorySession.openConfined(); - return new Context(SegmentAllocator.newNativeArena(size, scope), scope); + Arena arena = Arena.openConfined(); + return new Context(SegmentAllocator.slicingAllocator(MemorySegment.allocateNative(size, arena.scope())), arena.scope()) { + @Override + public void close() { + arena.close(); + } + }; } /** @@ -244,7 +241,7 @@ public static Context ofBoundedAllocator(long size) { public static Context ofAllocator(SegmentAllocator allocator) { return new Context(allocator, null) { @Override - public MemorySession session() { + public SegmentScope session() { throw new UnsupportedOperationException(); } }; @@ -255,10 +252,15 @@ public MemorySession session() { * the context's allocator is accessed. */ public static Context ofSession() { - MemorySession scope = MemorySession.openConfined(); - return new Context(null, scope) { + Arena arena = Arena.openConfined(); + return new Context(null, arena.scope()) { @Override public SegmentAllocator allocator() { throw new UnsupportedOperationException(); } + + @Override + public void close() { + arena.close(); + } }; } @@ -273,7 +275,7 @@ public SegmentAllocator allocator() { } @Override - public MemorySession session() { + public SegmentScope session() { throw new UnsupportedOperationException(); } @@ -293,24 +295,16 @@ enum Tag { ALLOC_BUFFER, BOX_ADDRESS, UNBOX_ADDRESS, - TO_SEGMENT, - DUP + DUP, + CAST } - private final Tag tag; + Tag tag(); - private Binding(Tag tag) { - this.tag = tag; - } - - public Tag tag() { - return tag; - } + void verify(Deque> stack); - public abstract void verify(Deque> stack); - - public abstract void interpret(Deque stack, BindingInterpreter.StoreFunc storeFunc, - BindingInterpreter.LoadFunc loadFunc, Context context); + void interpret(Deque stack, BindingInterpreter.StoreFunc storeFunc, + BindingInterpreter.LoadFunc loadFunc, Context context); private static void checkType(Class type) { if (!type.isPrimitive() || type == void.class) @@ -322,91 +316,93 @@ private static void checkOffset(long offset) { throw new IllegalArgumentException("Negative offset: " + offset); } - public static VMStore vmStore(VMStorage storage, Class type) { + static VMStore vmStore(VMStorage storage, Class type) { checkType(type); return new VMStore(storage, type); } - public static VMLoad vmLoad(VMStorage storage, Class type) { + static VMLoad vmLoad(VMStorage storage, Class type) { checkType(type); return new VMLoad(storage, type); } - public static BufferStore bufferStore(long offset, Class type) { + static BufferStore bufferStore(long offset, Class type) { checkType(type); checkOffset(offset); return new BufferStore(offset, type); } - public static BufferLoad bufferLoad(long offset, Class type) { + static BufferLoad bufferLoad(long offset, Class type) { checkType(type); checkOffset(offset); return new BufferLoad(offset, type); } - public static Copy copy(MemoryLayout layout) { + static Copy copy(MemoryLayout layout) { return new Copy(layout.byteSize(), layout.byteAlignment()); } - public static Allocate allocate(MemoryLayout layout) { + static Allocate allocate(MemoryLayout layout) { return new Allocate(layout.byteSize(), layout.byteAlignment()); } - public static BoxAddress boxAddress() { - return BoxAddress.INSTANCE; - } - - public static UnboxAddress unboxAddress() { - return UnboxAddress.INSTANCE.get(MemoryAddress.class); + static BoxAddress boxAddressRaw(long size) { + return new BoxAddress(size, false); } - public static UnboxAddress unboxAddress(Class carrier) { - return UnboxAddress.INSTANCE.get(carrier); + static BoxAddress boxAddress(MemoryLayout layout) { + return new BoxAddress(layout.byteSize(), true); } - public static ToSegment toSegment(MemoryLayout layout) { - return new ToSegment(layout.byteSize()); + static BoxAddress boxAddress(long byteSize) { + return new BoxAddress(byteSize, true); } - public static ToSegment toSegment(long byteSize) { - return new ToSegment(byteSize); + static UnboxAddress unboxAddress() { + return UnboxAddress.INSTANCE; } - public static Dup dup() { + static Dup dup() { return Dup.INSTANCE; } - - public static Binding.Builder builder() { - return new Binding.Builder(); + static Binding cast(Class fromType, Class toType) { + return new Cast(fromType, toType); } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Binding binding = (Binding) o; - return tag == binding.tag; - } - @Override - public int hashCode() { - return Objects.hash(tag); + static Binding.Builder builder() { + return new Binding.Builder(); } /** * A builder helper class for generating lists of Bindings */ - public static class Builder { + class Builder { private final List bindings = new ArrayList<>(); + private static boolean isSubIntType(Class type) { + return type == boolean.class || type == byte.class || type == short.class || type == char.class; + } + public Binding.Builder vmStore(VMStorage storage, Class type) { + if (isSubIntType(type)) { + bindings.add(Binding.cast(type, int.class)); + type = int.class; + } bindings.add(Binding.vmStore(storage, type)); return this; } public Binding.Builder vmLoad(VMStorage storage, Class type) { - bindings.add(Binding.vmLoad(storage, type)); + Class loadType = type; + if (isSubIntType(type)) { + loadType = int.class; + } + bindings.add(Binding.vmLoad(storage, loadType)); + if (isSubIntType(type)) { + bindings.add(Binding.cast(int.class, type)); + } return this; } @@ -430,23 +426,18 @@ public Binding.Builder allocate(MemoryLayout layout) { return this; } - public Binding.Builder boxAddress() { - bindings.add(Binding.boxAddress()); - return this; - } - - public Binding.Builder unboxAddress() { - bindings.add(Binding.unboxAddress()); + public Binding.Builder boxAddressRaw(long size) { + bindings.add(Binding.boxAddressRaw(size)); return this; } - public Binding.Builder unboxAddress(Class carrier) { - bindings.add(Binding.unboxAddress(carrier)); + public Binding.Builder boxAddress(MemoryLayout layout) { + bindings.add(Binding.boxAddress(layout)); return this; } - public Binding.Builder toSegment(MemoryLayout layout) { - bindings.add(Binding.toSegment(layout)); + public Binding.Builder unboxAddress() { + bindings.add(Binding.unboxAddress()); return this; } @@ -456,42 +447,13 @@ public Binding.Builder dup() { } public List build() { - return new ArrayList<>(bindings); + return List.copyOf(bindings); } } - abstract static class Move extends Binding { - private final VMStorage storage; - private final Class type; - - private Move(Tag tag, VMStorage storage, Class type) { - super(tag); - this.storage = storage; - this.type = type; - } - - public VMStorage storage() { - return storage; - } - - public Class type() { - return type; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; - Move move = (Move) o; - return Objects.equals(storage, move.storage) && - Objects.equals(type, move.type); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), storage, type); - } + interface Move extends Binding { + VMStorage storage(); + Class type(); } /** @@ -499,9 +461,10 @@ public int hashCode() { * Pops a [type] from the operand stack, and moves it to [storage location] * The [type] must be one of byte, short, char, int, long, float, or double */ - public static class VMStore extends Move { - private VMStore(VMStorage storage, Class type) { - super(Tag.VM_STORE, storage, type); + record VMStore(VMStorage storage, Class type) implements Move { + @Override + public Tag tag() { + return Tag.VM_STORE; } @Override @@ -516,14 +479,6 @@ public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFun BindingInterpreter.LoadFunc loadFunc, Context context) { storeFunc.store(storage(), type(), stack.pop()); } - - @Override - public String toString() { - return "VMStore{" + - "storage=" + storage() + - ", type=" + type() + - '}'; - } } /** @@ -531,9 +486,10 @@ public String toString() { * Loads a [type] from [storage location], and pushes it onto the operand stack. * The [type] must be one of byte, short, char, int, long, float, or double */ - public static class VMLoad extends Move { - private VMLoad(VMStorage storage, Class type) { - super(Tag.VM_LOAD, storage, type); + record VMLoad(VMStorage storage, Class type) implements Move { + @Override + public Tag tag() { + return Tag.VM_LOAD; } @Override @@ -546,56 +502,11 @@ public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFun BindingInterpreter.LoadFunc loadFunc, Context context) { stack.push(loadFunc.load(storage(), type())); } - - @Override - public String toString() { - return "VMLoad{" + - "storage=" + storage() + - ", type=" + type() + - '}'; - } } - private abstract static class Dereference extends Binding { - private final long offset; - private final Class type; - - private Dereference(Tag tag, long offset, Class type) { - super(tag); - this.offset = offset; - this.type = type; - } - - public long offset() { - return offset; - } - - public Class type() { - return type; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; - Dereference that = (Dereference) o; - return offset == that.offset && - Objects.equals(type, that.type); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), offset, type); - } - - 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) - ValueLayout layout = MemoryLayout.valueLayout(type(), ByteOrder.nativeOrder()).withBitAlignment(8); - return MethodHandles.insertCoordinates(MethodHandles.memorySegmentViewVarHandle(layout), 1, offset); - } + interface Dereference extends Binding { + long offset(); + Class type(); } /** @@ -604,9 +515,10 @@ public VarHandle varHandle() { * Stores the [type] to [offset into memory region]. * The [type] must be one of byte, short, char, int, long, float, or double */ - public static class BufferStore extends Dereference { - private BufferStore(long offset, Class type) { - super(Tag.BUFFER_STORE, offset, type); + record BufferStore(long offset, Class type) implements Dereference { + @Override + public Tag tag() { + return Tag.BUFFER_STORE; } @Override @@ -625,14 +537,6 @@ public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFun MemorySegment writeAddress = operand.asSlice(offset()); SharedUtils.write(writeAddress, type(), value); } - - @Override - public String toString() { - return "BufferStore{" + - "offset=" + offset() + - ", type=" + type() + - '}'; - } } /** @@ -641,9 +545,10 @@ public String toString() { * and then stores [type] to [offset into memory region] of the MemorySegment. * The [type] must be one of byte, short, char, int, long, float, or double */ - public static class BufferLoad extends Dereference { - private BufferLoad(long offset, Class type) { - super(Tag.BUFFER_LOAD, offset, type); + record BufferLoad(long offset, Class type) implements Dereference { + @Override + public Tag tag() { + return Tag.BUFFER_LOAD; } @Override @@ -661,14 +566,6 @@ public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFun MemorySegment readAddress = operand.asSlice(offset()); stack.push(SharedUtils.read(readAddress, type())); } - - @Override - public String toString() { - return "BufferLoad{" + - "offset=" + offset() + - ", type=" + type() + - '}'; - } } /** @@ -677,36 +574,15 @@ public String toString() { * and copies contents from a MemorySegment popped from the top of the operand stack into this new buffer, * and pushes the new buffer onto the operand stack */ - public static class Copy extends Binding { - private final long size; - private final long alignment; - - private Copy(long size, long alignment) { - super(Tag.COPY_BUFFER); - this.size = size; - this.alignment = alignment; - } - + record Copy(long size, long alignment) implements Binding { private static MemorySegment copyBuffer(MemorySegment operand, long size, long alignment, Context context) { return context.allocator().allocate(size, alignment) .copyFrom(operand.asSlice(0, size)); } - public long size() { - return size; - } - - public long alignment() { - return alignment; - } - @Override - public String toString() { - return "Copy{" + - "tag=" + tag() + - ", size=" + size + - ", alignment=" + alignment + - '}'; + public Tag tag() { + return Tag.COPY_BUFFER; } @Override @@ -723,56 +599,20 @@ public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFun MemorySegment copy = copyBuffer(operand, size, alignment, context); stack.push(copy); } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; - Copy copy = (Copy) o; - return size == copy.size && - alignment == copy.alignment; - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), size, alignment); - } } /** * ALLOCATE([size], [alignment]) * Creates a new MemorySegment with the give [size] and [alignment], and pushes it onto the operand stack. */ - public static class Allocate extends Binding { - private final long size; - private final long alignment; - - private Allocate(long size, long alignment) { - super(Tag.ALLOC_BUFFER); - this.size = size; - this.alignment = alignment; - } - + record Allocate(long size, long alignment) implements Binding { private static MemorySegment allocateBuffer(long size, long alignment, Context context) { return context.allocator().allocate(size, alignment); } - public long size() { - return size; - } - - public long alignment() { - return alignment; - } - @Override - public String toString() { - return "AllocateBuffer{" + - "tag=" + tag() + - "size=" + size + - ", alignment=" + alignment + - '}'; + public Tag tag() { + return Tag.ALLOC_BUFFER; } @Override @@ -785,189 +625,120 @@ public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFun BindingInterpreter.LoadFunc loadFunc, Context context) { stack.push(allocateBuffer(size, alignment, context)); } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; - Allocate allocate = (Allocate) o; - return size == allocate.size && - alignment == allocate.alignment; - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), size, alignment); - } } /** * UNBOX_ADDRESS() * Pops a 'MemoryAddress' from the operand stack, converts it to a 'long', - * and pushes that onto the operand stack. + * with the given size, and pushes that onto the operand stack */ - public static class UnboxAddress extends Binding { - - static final ClassValue INSTANCE = new ClassValue<>() { - @Override - protected UnboxAddress computeValue(Class type) { - return new UnboxAddress(type); - } - }; - - final Class carrier; - final MethodHandle toAddress; + record UnboxAddress() implements Binding { + static final UnboxAddress INSTANCE = new UnboxAddress(); - 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 Tag tag() { + return Tag.UNBOX_ADDRESS; } @Override public void verify(Deque> stack) { Class actualType = stack.pop(); - SharedUtils.checkType(actualType, carrier); + SharedUtils.checkType(actualType, MemorySegment.class); stack.push(long.class); } @Override public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFunc, BindingInterpreter.LoadFunc loadFunc, Context context) { - stack.push(((Addressable)stack.pop()).address().toRawLongValue()); - } - - @Override - public String toString() { - return "UnboxAddress{}"; + stack.push(((MemorySegment)stack.pop()).address()); } } /** * BOX_ADDRESS() - * Pops a 'long' from the operand stack, converts it to a 'MemoryAddress', - * and pushes that onto the operand stack. + * Pops a 'long' from the operand stack, converts it to a 'MemorySegment', with the given size and memory session + * (either the context session, or the global session), and pushes that onto the operand stack. */ - public static class BoxAddress extends Binding { - private static final BoxAddress INSTANCE = new BoxAddress(); - private BoxAddress() { - super(Tag.BOX_ADDRESS); + record BoxAddress(long size, boolean needsSession) implements Binding { + + @Override + public Tag tag() { + return Tag.BOX_ADDRESS; } @Override public void verify(Deque> stack) { Class actualType = stack.pop(); SharedUtils.checkType(actualType, long.class); - stack.push(MemoryAddress.class); + stack.push(MemorySegment.class); } @Override public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFunc, BindingInterpreter.LoadFunc loadFunc, Context context) { - stack.push(MemoryAddress.ofLong((long) stack.pop())); - } - - @Override - public String toString() { - return "BoxAddress{}"; + SegmentScope session = needsSession ? + context.session() : SegmentScope.global(); + stack.push(NativeMemorySegmentImpl.makeNativeSegmentUnchecked((long) stack.pop(), size, session)); } } /** - * TO_SEGMENT([size]) - * Pops a MemoryAddress from the operand stack, and converts it to a MemorySegment - * with the given size, and pushes that onto the operand stack + * DUP() + * Duplicates the value on the top of the operand stack (without popping it!), + * and pushes the duplicate onto the operand stack */ - public static class ToSegment extends Binding { - private final long size; - // FIXME alignment? - - public ToSegment(long size) { - super(Tag.TO_SEGMENT); - this.size = size; - } - - public long size() { - return size; - } + record Dup() implements Binding { + static final Dup INSTANCE = new Dup(); - private static MemorySegment toSegment(MemoryAddress operand, long size, Context context) { - return MemoryAddressImpl.ofLongUnchecked(operand.toRawLongValue(), size, context.session); + @Override + public Tag tag() { + return Tag.DUP; } @Override public void verify(Deque> stack) { - Class actualType = stack.pop(); - SharedUtils.checkType(actualType, MemoryAddress.class); - stack.push(MemorySegment.class); + stack.push(stack.peekLast()); } @Override public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFunc, BindingInterpreter.LoadFunc loadFunc, Context context) { - MemoryAddress operand = (MemoryAddress) stack.pop(); - MemorySegment segment = toSegment(operand, size, context); - stack.push(segment); - } - - @Override - public String toString() { - return "ToSegemnt{" + - "size=" + size + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; - ToSegment toSegemnt = (ToSegment) o; - return size == toSegemnt.size; - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), size); + stack.push(stack.peekLast()); } } /** - * DUP() - * Duplicates the value on the top of the operand stack (without popping it!), - * and pushes the duplicate onto the operand stack + * CAST([fromType], [toType]) + * Pop a [fromType] from the stack, convert it to [toType], and push the resulting + * value onto the stack. + * */ - public static class Dup extends Binding { - private static final Dup INSTANCE = new Dup(); - private Dup() { - super(Tag.DUP); + record Cast(Class fromType, Class toType) implements Binding { + + @Override + public Tag tag() { + return Tag.CAST; } @Override public void verify(Deque> stack) { - stack.push(stack.peekLast()); + Class actualType = stack.pop(); + SharedUtils.checkType(actualType, fromType); + stack.push(toType); } @Override public void interpret(Deque stack, BindingInterpreter.StoreFunc storeFunc, BindingInterpreter.LoadFunc loadFunc, Context context) { - stack.push(stack.peekLast()); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - return o != null && getClass() == o.getClass(); - } - - @Override - public String toString() { - return "Dup{}"; + Object arg = stack.pop(); + MethodHandle converter = MethodHandles.explicitCastArguments(MethodHandles.identity(toType), + MethodType.methodType(toType, fromType)); + try { + Object result = converter.invoke(arg); + stack.push(result); + } catch (Throwable e) { + throw new InternalError(e); + } } } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/BindingSpecializer.java b/src/java.base/share/classes/jdk/internal/foreign/abi/BindingSpecializer.java index 328cac67af8f7..13bfb7054d638 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/BindingSpecializer.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/BindingSpecializer.java @@ -24,9 +24,10 @@ */ package jdk.internal.foreign.abi; -import jdk.internal.foreign.MemoryAddressImpl; +import jdk.internal.foreign.AbstractMemorySegmentImpl; import jdk.internal.foreign.MemorySessionImpl; -import jdk.internal.foreign.Scoped; +import jdk.internal.foreign.NativeMemorySegmentImpl; +import jdk.internal.foreign.Utils; import jdk.internal.misc.VM; import jdk.internal.org.objectweb.asm.ClassReader; import jdk.internal.org.objectweb.asm.ClassWriter; @@ -43,11 +44,10 @@ import java.io.IOException; import java.io.PrintWriter; import java.lang.constant.ConstantDescs; -import java.lang.foreign.Addressable; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryAddress; +import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.SegmentAllocator; import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; @@ -60,9 +60,7 @@ import java.util.Arrays; import java.util.Deque; import java.util.List; -import java.util.function.BiPredicate; -import static java.lang.foreign.ValueLayout.*; import static java.lang.invoke.MethodType.methodType; import static jdk.internal.org.objectweb.asm.Opcodes.*; @@ -84,14 +82,13 @@ public class BindingSpecializer { private static final String OF_BOUNDED_ALLOCATOR_DESC = methodType(Binding.Context.class, long.class).descriptorString(); private static final String OF_SESSION_DESC = methodType(Binding.Context.class).descriptorString(); private static final String ALLOCATOR_DESC = methodType(SegmentAllocator.class).descriptorString(); - private static final String SESSION_DESC = methodType(MemorySession.class).descriptorString(); + private static final String SESSION_DESC = methodType(SegmentScope.class).descriptorString(); private static final String SESSION_IMPL_DESC = methodType(MemorySessionImpl.class).descriptorString(); private static final String CLOSE_DESC = VOID_DESC; - private static final String ADDRESS_DESC = methodType(MemoryAddress.class).descriptorString(); + private static final String UNBOX_SEGMENT_DESC = methodType(long.class, MemorySegment.class).descriptorString(); private static final String COPY_DESC = methodType(void.class, MemorySegment.class, long.class, MemorySegment.class, long.class, long.class).descriptorString(); - private static final String TO_RAW_LONG_VALUE_DESC = methodType(long.class).descriptorString(); - private static final String OF_LONG_DESC = methodType(MemoryAddress.class, long.class).descriptorString(); - private static final String OF_LONG_UNCHECKED_DESC = methodType(MemorySegment.class, long.class, long.class, MemorySession.class).descriptorString(); + private static final String OF_LONG_DESC = methodType(MemorySegment.class, long.class, long.class).descriptorString(); + private static final String OF_LONG_UNCHECKED_DESC = methodType(MemorySegment.class, long.class, long.class, SegmentScope.class).descriptorString(); private static final String ALLOCATE_DESC = methodType(MemorySegment.class, long.class, long.class).descriptorString(); private static final String HANDLE_UNCAUGHT_EXCEPTION_DESC = methodType(void.class, Throwable.class).descriptorString(); private static final String METHOD_HANDLES_INTRN = Type.getInternalName(MethodHandles.class); @@ -131,7 +128,7 @@ public class BindingSpecializer { private int[] scopeSlots; private int curScopeLocalIdx = -1; private int returnAllocatorIdx = -1; - private int CONTEXT_IDX = -1; + private int contextIdx = -1; private int returnBufferIdx = -1; private int retValIdx = -1; private Deque> typeStack; @@ -248,12 +245,8 @@ private void pushType(Class type) { } private Class popType(Class expected) { - return popType(expected, ASSERT_EQUALS); - } - - private Class popType(Class expected, BiPredicate, Class> typePredicate) { - Class found; - if (!typePredicate.test(expected, found = typeStack.pop())) { + Class found = typeStack.pop(); + if (!expected.equals(found)) { throw new IllegalStateException( String.format("Invalid type on binding operand stack; found %s - expected %s", found.descriptorString(), expected.descriptorString())); @@ -261,9 +254,6 @@ private Class popType(Class expected, BiPredicate, Class> type return found; } - private static final BiPredicate, Class> ASSERT_EQUALS = Class::equals; - private static final BiPredicate, Class> ASSERT_ASSIGNABLE = Class::isAssignableFrom; - // specialization private void specialize() { @@ -288,7 +278,7 @@ private void specialize() { int[] initialScopeSlots = new int[callerMethodType.parameterCount()]; int numScopes = 0; for (int i = 0; i < callerMethodType.parameterCount(); i++) { - if (shouldAcquire(callerMethodType.parameterType(i))) { + if (shouldAcquire(i)) { int scopeLocal = newLocal(Object.class); initialScopeSlots[numScopes++] = scopeLocal; emitConst(null); @@ -308,8 +298,8 @@ private void specialize() { } else { emitGetStatic(Binding.Context.class, "DUMMY", BINDING_CONTEXT_DESC); } - CONTEXT_IDX = newLocal(Object.class); - emitStore(Object.class, CONTEXT_IDX); + contextIdx = newLocal(Object.class); + emitStore(Object.class, contextIdx); // in case the call needs a return buffer, allocate it here. // for upcalls the VM wrapper stub allocates the buffer. @@ -443,11 +433,26 @@ private void specialize() { } private boolean needsSession() { - return callingSequence.argumentBindings().anyMatch(Binding.ToSegment.class::isInstance); + return callingSequence.argumentBindings() + .filter(Binding.BoxAddress.class::isInstance) + .map(Binding.BoxAddress.class::cast) + .anyMatch(Binding.BoxAddress::needsSession); } - private static boolean shouldAcquire(Class type) { - return type == Addressable.class; + private boolean shouldAcquire(int paramIndex) { + if (!callingSequence.forDowncall() || // we only acquire in downcalls + paramIndex == 0) { // the first parameter in a downcall is SegmentAllocator + return false; + } + + // if call needs return buffer, the descriptor has an extra leading layout + int offset = callingSequence.needsReturnBuffer() ? 0 : 1; + MemoryLayout paramLayout = callingSequence.functionDesc() + .argumentLayouts() + .get(paramIndex - offset); + + // is this an address layout? + return paramLayout instanceof ValueLayout.OfAddress; } private void emitCleanup() { @@ -466,10 +471,10 @@ private void doBindings(List bindings) { case BUFFER_LOAD -> emitBufferLoad((Binding.BufferLoad) binding); case COPY_BUFFER -> emitCopyBuffer((Binding.Copy) binding); case ALLOC_BUFFER -> emitAllocBuffer((Binding.Allocate) binding); - case BOX_ADDRESS -> emitBoxAddress(); + case BOX_ADDRESS -> emitBoxAddress((Binding.BoxAddress) binding); case UNBOX_ADDRESS -> emitUnboxAddress(); - case TO_SEGMENT -> emitToSegment((Binding.ToSegment) binding); case DUP -> emitDupBinding(); + case CAST -> emitCast((Binding.Cast) binding); } } } @@ -483,7 +488,7 @@ private void emitGetInput() { Class highLevelType = callerMethodType.parameterType(paramIndex); emitLoad(highLevelType, paramIndex2ParamSlot[paramIndex]); - if (shouldAcquire(highLevelType)) { + if (shouldAcquire(paramIndex)) { emitDup(Object.class); emitAcquireScope(); } @@ -493,8 +498,8 @@ private void emitGetInput() { } private void emitAcquireScope() { - emitCheckCast(Scoped.class); - emitInvokeInterface(Scoped.class, "sessionImpl", SESSION_IMPL_DESC); + emitCheckCast(AbstractMemorySegmentImpl.class); + emitInvokeVirtual(AbstractMemorySegmentImpl.class, "sessionImpl", SESSION_IMPL_DESC); Label skipAcquire = new Label(); Label end = new Label(); @@ -510,8 +515,9 @@ private void emitAcquireScope() { // 1 scope to acquire on the stack emitDup(Object.class); int nextScopeLocal = scopeSlots[curScopeLocalIdx++]; - emitStore(Object.class, nextScopeLocal); // store off one to release later + // call acquire first here. So that if it fails, we don't call release emitInvokeVirtual(MemorySessionImpl.class, "acquire0", ACQUIRE0_DESC); // call acquire on the other + emitStore(Object.class, nextScopeLocal); // store off one to release later if (hasOtherScopes) { // avoid ASM generating a bunch of nops for the dead code mv.visitJumpInsn(GOTO, end); @@ -553,43 +559,33 @@ private int newLocal(Class type) { } private void emitLoadInternalSession() { - assert CONTEXT_IDX != -1; - emitLoad(Object.class, CONTEXT_IDX); + assert contextIdx != -1; + emitLoad(Object.class, contextIdx); emitInvokeVirtual(Binding.Context.class, "session", SESSION_DESC); } private void emitLoadInternalAllocator() { - assert CONTEXT_IDX != -1; - emitLoad(Object.class, CONTEXT_IDX); + assert contextIdx != -1; + emitLoad(Object.class, contextIdx); emitInvokeVirtual(Binding.Context.class, "allocator", ALLOCATOR_DESC); } private void emitCloseContext() { - assert CONTEXT_IDX != -1; - emitLoad(Object.class, CONTEXT_IDX); + assert contextIdx != -1; + emitLoad(Object.class, contextIdx); emitInvokeVirtual(Binding.Context.class, "close", CLOSE_DESC); } - private void emitToSegment(Binding.ToSegment binding) { - long size = binding.size(); - popType(MemoryAddress.class); - - emitToRawLongValue(); - emitConst(size); - emitLoadInternalSession(); - emitInvokeStatic(MemoryAddressImpl.class, "ofLongUnchecked", OF_LONG_UNCHECKED_DESC); - - pushType(MemorySegment.class); - } - - private void emitToRawLongValue() { - emitInvokeInterface(MemoryAddress.class, "toRawLongValue", TO_RAW_LONG_VALUE_DESC); - } - - private void emitBoxAddress() { + private void emitBoxAddress(Binding.BoxAddress boxAddress) { popType(long.class); - emitInvokeStatic(MemoryAddress.class, "ofLong", OF_LONG_DESC); - pushType(MemoryAddress.class); + emitConst(boxAddress.size()); + if (needsSession()) { + emitLoadInternalSession(); + emitInvokeStatic(NativeMemorySegmentImpl.class, "makeNativeSegmentUnchecked", OF_LONG_UNCHECKED_DESC); + } else { + emitInvokeStatic(NativeMemorySegmentImpl.class, "makeNativeSegmentUnchecked", OF_LONG_DESC); + } + pushType(MemorySegment.class); } private void emitAllocBuffer(Binding.Allocate binding) { @@ -619,7 +615,6 @@ private void emitBufferStore(Binding.BufferStore bufferStore) { emitInvokeInterface(MemorySegment.class, "set", descriptor); } - // VM_STORE and VM_LOAD are emulated, which is different for down/upcalls private void emitVMStore(Binding.VMStore vmStore) { Class storeType = vmStore.type(); @@ -670,16 +665,56 @@ private void emitVMLoad(Binding.VMLoad vmLoad) { emitGetInput(); } } + private void emitDupBinding() { Class dupType = typeStack.peek(); emitDup(dupType); pushType(dupType); } + private void emitCast(Binding.Cast cast) { + Class fromType = cast.fromType(); + Class toType = cast.toType(); + + if (fromType == int.class) { + popType(int.class); + + if (toType == boolean.class) { + // implement least significant byte non-zero test + + // select first byte + emitConst(0xFF); + mv.visitInsn(IAND); + + // convert to boolean + emitInvokeStatic(Utils.class, "byteToBoolean", "(B)Z"); + } else if (toType == byte.class) { + mv.visitInsn(I2B); + } else if (toType == short.class) { + mv.visitInsn(I2S); + } else { + assert toType == char.class; + mv.visitInsn(I2C); + } + + pushType(toType); + } else { + popType(fromType); + + assert fromType == boolean.class + || fromType == byte.class + || fromType == short.class + || fromType == char.class; + // no-op in bytecode + + assert toType == int.class; + pushType(int.class); + } + } + private void emitUnboxAddress() { - popType(Addressable.class, ASSERT_ASSIGNABLE); - emitInvokeInterface(Addressable.class, "address", ADDRESS_DESC); - emitToRawLongValue(); + popType(MemorySegment.class); + emitInvokeStatic(SharedUtils.class, "unboxSegment", UNBOX_SEGMENT_DESC); pushType(long.class); } @@ -730,15 +765,15 @@ private void emitAllocateCall(long size, long alignment) { private Class emitLoadLayoutConstant(Class type) { Class valueLayoutType = valueLayoutTypeFor(type); String valueLayoutConstantName = valueLayoutConstantFor(type); - emitGetStatic(BindingSpecializer.Runtime.class, valueLayoutConstantName, valueLayoutType.descriptorString()); + emitGetStatic(ValueLayout.class, valueLayoutConstantName, valueLayoutType.descriptorString()); return valueLayoutType; } private static String valueLayoutConstantFor(Class type) { if (type == boolean.class) { - return "JAVA_BOOLEAN_UNALIGNED"; + return "JAVA_BOOLEAN"; } else if (type == byte.class) { - return "JAVA_BYTE_UNALIGNED"; + return "JAVA_BYTE"; } else if (type == short.class) { return "JAVA_SHORT_UNALIGNED"; } else if (type == char.class) { @@ -751,7 +786,7 @@ private static String valueLayoutConstantFor(Class type) { return "JAVA_FLOAT_UNALIGNED"; } else if (type == double.class) { return "JAVA_DOUBLE_UNALIGNED"; - } else if (type == MemoryAddress.class) { + } else if (type == MemorySegment.class) { return "ADDRESS_UNALIGNED"; } else { throw new IllegalStateException("Unknown type: " + type); @@ -775,7 +810,7 @@ private static Class valueLayoutTypeFor(Class type) { return ValueLayout.OfFloat.class; } else if (type == double.class) { return ValueLayout.OfDouble.class; - } else if (type == MemoryAddress.class) { + } else if (type == MemorySegment.class) { return ValueLayout.OfAddress.class; } else { throw new IllegalStateException("Unknown type: " + type); @@ -920,18 +955,4 @@ private void emitReturn(Class type) { mv.visitInsn(opcode); } - // constants that are accessed from the generated bytecode - // see emitLoadLayoutConstant - static class Runtime { - // unaligned constants - static final ValueLayout.OfBoolean JAVA_BOOLEAN_UNALIGNED = JAVA_BOOLEAN; - static final ValueLayout.OfByte JAVA_BYTE_UNALIGNED = JAVA_BYTE; - static final ValueLayout.OfShort JAVA_SHORT_UNALIGNED = JAVA_SHORT.withBitAlignment(8); - static final ValueLayout.OfChar JAVA_CHAR_UNALIGNED = JAVA_CHAR.withBitAlignment(8); - static final ValueLayout.OfInt JAVA_INT_UNALIGNED = JAVA_INT.withBitAlignment(8); - static final ValueLayout.OfLong JAVA_LONG_UNALIGNED = JAVA_LONG.withBitAlignment(8); - static final ValueLayout.OfFloat JAVA_FLOAT_UNALIGNED = JAVA_FLOAT.withBitAlignment(8); - static final ValueLayout.OfDouble JAVA_DOUBLE_UNALIGNED = JAVA_DOUBLE.withBitAlignment(8); - static final ValueLayout.OfAddress ADDRESS_UNALIGNED = ADDRESS.withBitAlignment(8); - } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequenceBuilder.java b/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequenceBuilder.java index adf0ee302563a..665f0204fd382 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequenceBuilder.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequenceBuilder.java @@ -27,7 +27,6 @@ import jdk.internal.foreign.Utils; import sun.security.action.GetPropertyAction; -import java.lang.foreign.Addressable; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; @@ -96,12 +95,12 @@ public CallingSequence build() { MethodType callerMethodType; MethodType calleeMethodType; if (!forUpcall) { - addArgumentBinding(0, Addressable.class, ValueLayout.ADDRESS, List.of( - Binding.unboxAddress(Addressable.class), + addArgumentBinding(0, MemorySegment.class, ValueLayout.ADDRESS, List.of( + Binding.unboxAddress(), Binding.vmStore(abi.targetAddrStorage(), long.class))); if (needsReturnBuffer) { addArgumentBinding(0, MemorySegment.class, ValueLayout.ADDRESS, List.of( - Binding.unboxAddress(MemorySegment.class), + Binding.unboxAddress(), Binding.vmStore(abi.retBufAddrStorage(), long.class))); } @@ -111,8 +110,7 @@ public CallingSequence build() { if (needsReturnBuffer) { addArgumentBinding(0, MemorySegment.class, ValueLayout.ADDRESS, List.of( Binding.vmLoad(abi.retBufAddrStorage(), long.class), - Binding.boxAddress(), - Binding.toSegment(returnBufferSize))); + Binding.boxAddress(returnBufferSize))); } callerMethodType = computeCallerTypeForUpcall(); @@ -195,8 +193,8 @@ private void verifyBindings(boolean forArguments, Class carrier, List inType, List bindings) { @@ -223,8 +221,8 @@ private static void verifyUnboxBindings(Class inType, List bindings) ALLOC_BUFFER, BOX_ADDRESS, //UNBOX_ADDRESS, - TO_SEGMENT, - DUP + DUP, + CAST ); private static void verifyBoxBindings(Class expectedOutType, List bindings) { diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java index a0475725b415c..0275b20921d11 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java @@ -28,7 +28,6 @@ import jdk.internal.access.SharedSecrets; import sun.security.action.GetPropertyAction; -import java.lang.foreign.Addressable; import java.lang.foreign.MemorySegment; import java.lang.foreign.SegmentAllocator; import java.lang.invoke.MethodHandle; @@ -60,7 +59,7 @@ public class DowncallLinker { MH_INVOKE_INTERP_BINDINGS = lookup.findVirtual(DowncallLinker.class, "invokeInterpBindings", methodType(Object.class, SegmentAllocator.class, Object[].class, InvocationData.class)); MH_CHECK_SYMBOL = lookup.findStatic(SharedUtils.class, "checkSymbol", - methodType(void.class, Addressable.class)); + methodType(void.class, MemorySegment.class)); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } @@ -110,7 +109,7 @@ public MethodHandle getBoundMethodHandle() { } assert handle.type().parameterType(0) == SegmentAllocator.class; - assert handle.type().parameterType(1) == Addressable.class; + assert handle.type().parameterType(1) == MemorySegment.class; handle = foldArguments(handle, 1, MH_CHECK_SYMBOL); handle = SharedUtils.swapArguments(handle, 0, 1); // normalize parameter order @@ -169,9 +168,7 @@ Object invokeInterpBindings(SegmentAllocator allocator, Object[] args, Invocatio for (int i = 0; i < args.length; i++) { Object arg = args[i]; BindingInterpreter.unbox(arg, callingSequence.argumentBindings(i), - (storage, type, value) -> { - leafArgs[invData.argIndexMap.get(storage)] = value; - }, unboxContext); + (storage, type, value) -> leafArgs[invData.argIndexMap.get(storage)] = value, unboxContext); } // call leaf diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java b/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java new file mode 100644 index 0000000000000..9b0d08c125789 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.internal.foreign.abi; + +import java.lang.foreign.Linker; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class LinkerOptions { + + private static final LinkerOptions EMPTY = LinkerOptions.of(); + private final Map, Linker.Option> optionsMap; + + private LinkerOptions(Map, Linker.Option> optionsMap) { + this.optionsMap = optionsMap; + } + + public static LinkerOptions of(Linker.Option... options) { + Map, Linker.Option> optionMap = new HashMap<>(); + + for (Linker.Option option : options) { + if (optionMap.containsKey(option.getClass())) { + throw new IllegalArgumentException("Duplicate option: " + option); + } + optionMap.put(option.getClass(), option); + } + + return new LinkerOptions(optionMap); + } + + public static LinkerOptions empty() { + return EMPTY; + } + + private T getOption(Class type) { + return type.cast(optionsMap.get(type)); + } + + public boolean isVarargsIndex(int argIndex) { + FirstVariadicArg fva = getOption(FirstVariadicArg.class); + return fva != null && argIndex >= fva.index(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof LinkerOptions that + && Objects.equals(optionsMap, that.optionsMap); + } + + @Override + public int hashCode() { + return Objects.hash(optionsMap); + } + + public record FirstVariadicArg(int index) implements Linker.Option { } +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java b/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java index d9e27af746b7d..991b44067f2db 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java @@ -28,24 +28,19 @@ import jdk.internal.access.JavaLangInvokeAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.foreign.CABI; -import jdk.internal.foreign.MemoryAddressImpl; -import jdk.internal.foreign.MemorySessionImpl; -import jdk.internal.foreign.Scoped; 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.foreign.Addressable; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.SegmentAllocator; -import java.lang.foreign.SequenceLayout; import java.lang.foreign.VaList; import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; @@ -66,26 +61,26 @@ import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.methodType; -public class SharedUtils { +public final class SharedUtils { + + private 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; private static final MethodHandle MH_BUFFER_COPY; - private static final MethodHandle MH_REACHBILITY_FENCE; + private static final MethodHandle MH_REACHABILITY_FENCE; static { try { MethodHandles.Lookup lookup = MethodHandles.lookup(); MH_ALLOC_BUFFER = lookup.findVirtual(SegmentAllocator.class, "allocate", methodType(MemorySegment.class, MemoryLayout.class)); - MH_BASEADDRESS = lookup.findVirtual(MemorySegment.class, "address", - methodType(MemoryAddress.class)); MH_BUFFER_COPY = lookup.findStatic(SharedUtils.class, "bufferCopy", - methodType(MemoryAddress.class, MemoryAddress.class, MemorySegment.class)); - MH_REACHBILITY_FENCE = lookup.findStatic(Reference.class, "reachabilityFence", + methodType(MemorySegment.class, MemorySegment.class, MemorySegment.class)); + MH_REACHABILITY_FENCE = lookup.findStatic(Reference.class, "reachabilityFence", methodType(void.class, Object.class)); } catch (ReflectiveOperationException e) { throw new BootstrapMethodError(e); @@ -97,62 +92,12 @@ public class SharedUtils { throw new IllegalStateException("Cannot get here"); }; - /** - * Align the specified type from a given address - * @return The address the data should be at based on alignment requirement - */ - public static long align(MemoryLayout t, boolean isVar, long addr) { - return alignUp(addr, alignment(t, isVar)); - } - public static long alignUp(long addr, long alignment) { return ((addr - 1) | (alignment - 1)) + 1; } /** - * The alignment requirement for a given type - * @param isVar indicate if the type is a standalone variable. This change how - * array is aligned. for example. - */ - public static long alignment(MemoryLayout t, boolean isVar) { - if (t instanceof ValueLayout) { - return alignmentOfScalar((ValueLayout) t); - } else if (t instanceof SequenceLayout) { - // when array is used alone - return alignmentOfArray((SequenceLayout) t, isVar); - } else if (t instanceof GroupLayout) { - return alignmentOfContainer((GroupLayout) t); - } else if (t.isPadding()) { - return 1; - } else { - throw new IllegalArgumentException("Invalid type: " + t); - } - } - - private static long alignmentOfScalar(ValueLayout st) { - return st.byteSize(); - } - - private static long alignmentOfArray(SequenceLayout ar, boolean isVar) { - if (ar.elementCount() == 0) { - // VLA or incomplete - return 16; - } else if ((ar.byteSize()) >= 16 && isVar) { - return 16; - } else { - // align as element type - MemoryLayout elementType = ar.elementLayout(); - return alignment(elementType, false); - } - } - - private static long alignmentOfContainer(GroupLayout ct) { - // Most strict member - return ct.memberLayouts().stream().mapToLong(t -> alignment(t, false)).max().orElse(1); - } - - /** - * Takes a MethodHandle that takes an input buffer as a first argument (a MemoryAddress), and returns nothing, + * Takes a MethodHandle that takes an input buffer as a first argument (a MemorySegment), and returns nothing, * and adapts it to return a MemorySegment, by allocating a MemorySegment for the input * buffer, calling the target MethodHandle, and then returning the allocated MemorySegment. * @@ -166,24 +111,23 @@ private static long alignmentOfContainer(GroupLayout ct) { public static MethodHandle adaptDowncallForIMR(MethodHandle handle, FunctionDescriptor cDesc) { if (handle.type().returnType() != void.class) throw new IllegalArgumentException("return expected to be void for in memory returns: " + handle.type()); - if (handle.type().parameterType(2) != MemoryAddress.class) - throw new IllegalArgumentException("MemoryAddress expected as third param: " + handle.type()); + if (handle.type().parameterType(2) != MemorySegment.class) + throw new IllegalArgumentException("MemorySegment expected as third param: " + handle.type()); if (cDesc.returnLayout().isEmpty()) throw new IllegalArgumentException("Return layout needed: " + cDesc); MethodHandle ret = identity(MemorySegment.class); // (MemorySegment) MemorySegment - handle = collectArguments(ret, 1, handle); // (MemorySegment, Addressable, SegmentAllocator, MemoryAddress, ...) MemorySegment - handle = collectArguments(handle, 3, MH_BASEADDRESS); // (MemorySegment, Addressable, SegmentAllocator, MemorySegment, ...) MemorySegment - handle = mergeArguments(handle, 0, 3); // (MemorySegment, Addressable, SegmentAllocator, ...) MemorySegment - handle = collectArguments(handle, 0, insertArguments(MH_ALLOC_BUFFER, 1, cDesc.returnLayout().get())); // (SegmentAllocator, Addressable, SegmentAllocator, ...) MemoryAddress - handle = mergeArguments(handle, 0, 2); // (SegmentAllocator, Addressable, ...) MemoryAddress - handle = swapArguments(handle, 0, 1); // (Addressable, SegmentAllocator, ...) MemoryAddress + handle = collectArguments(ret, 1, handle); // (MemorySegment, MemorySegment, SegmentAllocator, MemorySegment, ...) MemorySegment + handle = mergeArguments(handle, 0, 3); // (MemorySegment, MemorySegment, SegmentAllocator, ...) MemorySegment + handle = collectArguments(handle, 0, insertArguments(MH_ALLOC_BUFFER, 1, cDesc.returnLayout().get())); // (SegmentAllocator, MemorySegment, SegmentAllocator, ...) MemorySegment + handle = mergeArguments(handle, 0, 2); // (SegmentAllocator, MemorySegment, ...) MemorySegment + handle = swapArguments(handle, 0, 1); // (MemorySegment, SegmentAllocator, ...) MemorySegment return handle; } /** * Takes a MethodHandle that returns a MemorySegment, and adapts it to take an input buffer as a first argument - * (a MemoryAddress), and upon invocation, copies the contents of the returned MemorySegment into the input buffer + * (a MemorySegment), and upon invocation, copies the contents of the returned MemorySegment into the input buffer * passed as the first argument. * * @param target the target handle to adapt @@ -193,22 +137,21 @@ public static MethodHandle adaptUpcallForIMR(MethodHandle target, boolean dropRe if (target.type().returnType() != MemorySegment.class) throw new IllegalArgumentException("Must return MemorySegment for IMR"); - target = collectArguments(MH_BUFFER_COPY, 1, target); // (MemoryAddress, ...) MemoryAddress + target = collectArguments(MH_BUFFER_COPY, 1, target); // (MemorySegment, ...) MemorySegment if (dropReturn) { // no handling for return value, need to drop it target = dropReturn(target); } else { // adjust return type so it matches the inferred type of the effective // function descriptor - target = target.asType(target.type().changeReturnType(Addressable.class)); + target = target.asType(target.type().changeReturnType(MemorySegment.class)); } return target; } - private static MemoryAddress bufferCopy(MemoryAddress dest, MemorySegment buffer) { - MemoryAddressImpl.ofLongUnchecked(dest.toRawLongValue(), buffer.byteSize()).copyFrom(buffer); - return dest; + private static MemorySegment bufferCopy(MemorySegment dest, MemorySegment buffer) { + return dest.copyFrom(buffer); } public static Class primitiveCarrierForSize(long size, boolean useFloat) { @@ -235,10 +178,10 @@ public static Class primitiveCarrierForSize(long size, boolean useFloat) { public static Linker getSystemLinker() { return switch (CABI.current()) { - case Win64 -> Windowsx64Linker.getInstance(); - case SysV -> SysVx64Linker.getInstance(); - case LinuxAArch64 -> LinuxAArch64Linker.getInstance(); - case MacOsAArch64 -> MacOsAArch64Linker.getInstance(); + case WIN_64 -> Windowsx64Linker.getInstance(); + case SYS_V -> SysVx64Linker.getInstance(); + case LINUX_AARCH_64 -> LinuxAArch64Linker.getInstance(); + case MAC_OS_AARCH_64 -> MacOsAArch64Linker.getInstance(); }; } @@ -305,7 +248,7 @@ static MethodHandle swapArguments(MethodHandle mh, int firstArg, int secondArg) } private static MethodHandle reachabilityFenceHandle(Class type) { - return MH_REACHBILITY_FENCE.asType(MethodType.methodType(void.class, type)); + return MH_REACHABILITY_FENCE.asType(MethodType.methodType(void.class, type)); } static void handleUncaughtException(Throwable t) { @@ -315,6 +258,13 @@ static void handleUncaughtException(Throwable t) { } } + static long unboxSegment(MemorySegment segment) { + if (!segment.isNative()) { + throw new IllegalArgumentException("Heap segment not allowed: " + segment); + } + return segment.address(); + } + public static void checkExceptions(MethodHandle target) { Class[] exceptions = JLIA.exceptionTypes(target); if (exceptions != null && exceptions.length != 0) { @@ -322,52 +272,45 @@ public static void checkExceptions(MethodHandle target) { } } - public static MethodHandle maybeInsertAllocator(MethodHandle handle) { - if (!handle.type().returnType().equals(MemorySegment.class)) { + public static MethodHandle maybeInsertAllocator(FunctionDescriptor descriptor, MethodHandle handle) { + if (descriptor.returnLayout().isEmpty() || !(descriptor.returnLayout().get() instanceof GroupLayout)) { // not returning segment, just insert a throwing allocator handle = insertArguments(handle, 1, THROWING_ALLOCATOR); } return handle; } - public static void checkSymbol(Addressable symbol) { - checkAddressable(symbol, "Symbol is NULL"); - } - - public static void checkAddress(MemoryAddress address) { - checkAddressable(address, "Address is NULL"); - } - - private static void checkAddressable(Addressable symbol, String msg) { + @ForceInline + public static void checkSymbol(MemorySegment symbol) { Objects.requireNonNull(symbol); - if (symbol.address().toRawLongValue() == 0) + if (symbol.equals(MemorySegment.NULL)) throw new IllegalArgumentException("Symbol is NULL: " + symbol); } - public static VaList newVaList(Consumer actions, MemorySession session) { + public static VaList newVaList(Consumer actions, SegmentScope session) { return switch (CABI.current()) { - case Win64 -> Windowsx64Linker.newVaList(actions, session); - case SysV -> SysVx64Linker.newVaList(actions, session); - case LinuxAArch64 -> LinuxAArch64Linker.newVaList(actions, session); - case MacOsAArch64 -> MacOsAArch64Linker.newVaList(actions, session); + case WIN_64 -> Windowsx64Linker.newVaList(actions, session); + case SYS_V -> SysVx64Linker.newVaList(actions, session); + case LINUX_AARCH_64 -> LinuxAArch64Linker.newVaList(actions, session); + case MAC_OS_AARCH_64 -> MacOsAArch64Linker.newVaList(actions, session); }; } - public static VaList newVaListOfAddress(MemoryAddress ma, MemorySession session) { + public static VaList newVaListOfAddress(long address, SegmentScope session) { return switch (CABI.current()) { - case Win64 -> Windowsx64Linker.newVaListOfAddress(ma, session); - case SysV -> SysVx64Linker.newVaListOfAddress(ma, session); - case LinuxAArch64 -> LinuxAArch64Linker.newVaListOfAddress(ma, session); - case MacOsAArch64 -> MacOsAArch64Linker.newVaListOfAddress(ma, session); + case WIN_64 -> Windowsx64Linker.newVaListOfAddress(address, session); + case SYS_V -> SysVx64Linker.newVaListOfAddress(address, session); + case LINUX_AARCH_64 -> LinuxAArch64Linker.newVaListOfAddress(address, session); + case MAC_OS_AARCH_64 -> MacOsAArch64Linker.newVaListOfAddress(address, session); }; } public static VaList emptyVaList() { return switch (CABI.current()) { - case Win64 -> Windowsx64Linker.emptyVaList(); - case SysV -> SysVx64Linker.emptyVaList(); - case LinuxAArch64 -> LinuxAArch64Linker.emptyVaList(); - case MacOsAArch64 -> MacOsAArch64Linker.emptyVaList(); + case WIN_64 -> Windowsx64Linker.emptyVaList(); + case SYS_V -> SysVx64Linker.emptyVaList(); + case LINUX_AARCH_64 -> LinuxAArch64Linker.emptyVaList(); + case MAC_OS_AARCH_64 -> MacOsAArch64Linker.emptyVaList(); }; } @@ -378,16 +321,11 @@ static void checkType(Class actualType, Class expectedType) { } } - public static boolean isVarargsIndex(FunctionDescriptor descriptor, int argIndex) { - int firstPos = descriptor.firstVariadicArgumentIndex(); - return firstPos != -1 && argIndex >= firstPos; - } - public static NoSuchElementException newVaListNSEE(MemoryLayout layout) { return new NoSuchElementException("No such element: " + layout); } - public static class SimpleVaArg { + public static final class SimpleVaArg { public final MemoryLayout layout; public final Object value; @@ -401,11 +339,11 @@ public VarHandle varHandle() { } } - public static non-sealed class EmptyVaList implements VaList, Scoped { + public static final class EmptyVaList implements VaList { - private final MemoryAddress address; + private final MemorySegment address; - public EmptyVaList(MemoryAddress address) { + public EmptyVaList(MemorySegment address) { this.address = address; } @@ -429,7 +367,7 @@ public double nextVarg(ValueLayout.OfDouble layout) { } @Override - public MemoryAddress nextVarg(ValueLayout.OfAddress layout) { + public MemorySegment nextVarg(ValueLayout.OfAddress layout) { throw uoe(); } @@ -443,18 +381,13 @@ public void skip(MemoryLayout... layouts) { throw uoe(); } - @Override - public MemorySession session() { - return MemorySessionImpl.GLOBAL; - } - @Override public VaList copy() { return this; } @Override - public MemoryAddress address() { + public MemorySegment segment() { return address; } } @@ -526,32 +459,4 @@ static Object read(MemorySegment ptr, Class type) { throw new IllegalArgumentException("Unsupported carrier: " + type); } } - - // unaligned constants - public final static ValueLayout.OfShort JAVA_SHORT_UNALIGNED = JAVA_SHORT.withBitAlignment(8); - public final static ValueLayout.OfChar JAVA_CHAR_UNALIGNED = JAVA_CHAR.withBitAlignment(8); - public final static ValueLayout.OfInt JAVA_INT_UNALIGNED = JAVA_INT.withBitAlignment(8); - public final static ValueLayout.OfLong JAVA_LONG_UNALIGNED = JAVA_LONG.withBitAlignment(8); - public final static ValueLayout.OfFloat JAVA_FLOAT_UNALIGNED = JAVA_FLOAT.withBitAlignment(8); - public final static ValueLayout.OfDouble JAVA_DOUBLE_UNALIGNED = JAVA_DOUBLE.withBitAlignment(8); - - 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/java.base/share/classes/jdk/internal/foreign/abi/SoftReferenceCache.java b/src/java.base/share/classes/jdk/internal/foreign/abi/SoftReferenceCache.java index be02a69ba7500..3e801600cb6b7 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/SoftReferenceCache.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/SoftReferenceCache.java @@ -38,7 +38,7 @@ public V get(K key, Function valueFactory) { .get(key, valueFactory); // long lock, but just for the particular key } - private class Node { + private final class Node { private volatile SoftReference ref; public Node() { diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallLinker.java index bb2bff5b067c1..40af3d64dd97c 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallLinker.java @@ -28,7 +28,7 @@ import sun.security.action.GetPropertyAction; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -61,7 +61,7 @@ public class UpcallLinker { } } - public static MemorySegment make(ABIDescriptor abi, MethodHandle target, CallingSequence callingSequence, MemorySession session) { + public static MemorySegment make(ABIDescriptor abi, MethodHandle target, CallingSequence callingSequence, SegmentScope session) { assert callingSequence.forUpcall(); Binding.VMLoad[] argMoves = argMoveBindings(callingSequence); Binding.VMStore[] retMoves = retMoveBindings(callingSequence); @@ -196,7 +196,7 @@ private static Object invokeInterpBindings(Object[] lowLevelArgs, InvocationData } // used for transporting data into native code - private static record CallRegs(VMStorage[] argRegs, VMStorage[] retRegs) {} + private record CallRegs(VMStorage[] argRegs, VMStorage[] retRegs) {} static native long makeUpcallStub(MethodHandle mh, ABIDescriptor abi, CallRegs conv, boolean needsReturnBuffer, long returnBufferSize); diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallStubs.java b/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallStubs.java index dec3b92220925..041a96ac1c0fc 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallStubs.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallStubs.java @@ -24,13 +24,15 @@ */ package jdk.internal.foreign.abi; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import jdk.internal.foreign.MemorySessionImpl; -public class UpcallStubs { +public final class UpcallStubs { + + private UpcallStubs() { + } private static void freeUpcallStub(long stubAddress) { if (!freeUpcallStub0(stubAddress)) { @@ -48,13 +50,13 @@ private static void freeUpcallStub(long stubAddress) { registerNatives(); } - static MemorySegment makeUpcall(long entry, MemorySession session) { - MemorySessionImpl.toSessionImpl(session).addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() { + static MemorySegment makeUpcall(long entry, SegmentScope session) { + ((MemorySessionImpl) session).addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() { @Override public void cleanup() { freeUpcallStub(entry); } }); - return MemorySegment.ofAddress(MemoryAddress.ofLong(entry), 0, session); + return MemorySegment.ofAddress(entry, 0, session); } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java index 59123bfd0c7fe..c0353581d338d 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java @@ -27,7 +27,6 @@ import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; import jdk.internal.foreign.abi.ABIDescriptor; @@ -35,6 +34,7 @@ import jdk.internal.foreign.abi.CallingSequence; import jdk.internal.foreign.abi.CallingSequenceBuilder; import jdk.internal.foreign.abi.DowncallLinker; +import jdk.internal.foreign.abi.LinkerOptions; import jdk.internal.foreign.abi.UpcallLinker; import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.VMStorage; @@ -42,7 +42,7 @@ import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64CallArranger; import jdk.internal.foreign.Utils; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.util.List; @@ -92,15 +92,8 @@ public abstract class CallArranger { r10 // return buffer addr reg ); - // record - public static class Bindings { - public final CallingSequence callingSequence; - public final boolean isInMemoryReturn; - - Bindings(CallingSequence callingSequence, boolean isInMemoryReturn) { - this.callingSequence = callingSequence; - this.isInMemoryReturn = isInMemoryReturn; - } + public record Bindings(CallingSequence callingSequence, + boolean isInMemoryReturn) { } public static final CallArranger LINUX = new LinuxAArch64CallArranger(); @@ -122,6 +115,10 @@ public static class Bindings { protected CallArranger() {} public Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall) { + return getBindings(mt, cDesc, forUpcall, LinkerOptions.empty()); + } + + public Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall, LinkerOptions options) { CallingSequenceBuilder csb = new CallingSequenceBuilder(C, forUpcall); BindingCalculator argCalc = forUpcall ? new BoxBindingCalculator(true) : new UnboxBindingCalculator(true); @@ -129,7 +126,7 @@ public Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean for boolean returnInMemory = isInMemoryReturn(cDesc.returnLayout()); if (returnInMemory) { - csb.addArgumentBindings(MemoryAddress.class, AArch64.C_POINTER, + csb.addArgumentBindings(MemorySegment.class, AArch64.C_POINTER, argCalc.getIndirectBindings()); } else if (cDesc.returnLayout().isPresent()) { Class carrier = mt.returnType(); @@ -140,7 +137,7 @@ public Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean for 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)) { + if (varArgsOnStack() && options.isVarargsIndex(i)) { argCalc.storageCalculator.adjustForVarArgs(); } csb.addArgumentBindings(carrier, layout, argCalc.getBindings(carrier, layout)); @@ -149,8 +146,8 @@ public Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean for return new Bindings(csb.build(), returnInMemory); } - public MethodHandle arrangeDowncall(MethodType mt, FunctionDescriptor cDesc) { - Bindings bindings = getBindings(mt, cDesc, false); + public MethodHandle arrangeDowncall(MethodType mt, FunctionDescriptor cDesc, LinkerOptions options) { + Bindings bindings = getBindings(mt, cDesc, false, options); MethodHandle handle = new DowncallLinker(C, bindings.callingSequence).getBoundMethodHandle(); @@ -161,7 +158,7 @@ public MethodHandle arrangeDowncall(MethodType mt, FunctionDescriptor cDesc) { return handle; } - public MemorySegment arrangeUpcall(MethodHandle target, MethodType mt, FunctionDescriptor cDesc, MemorySession session) { + public MemorySegment arrangeUpcall(MethodHandle target, MethodType mt, FunctionDescriptor cDesc, SegmentScope session) { Bindings bindings = getBindings(mt, cDesc, true); if (bindings.isInMemoryReturn) { @@ -214,7 +211,7 @@ VMStorage stackAlloc(long size, long alignment) { } VMStorage stackAlloc(MemoryLayout layout) { - return stackAlloc(layout.byteSize(), SharedUtils.alignment(layout, true)); + return stackAlloc(layout.byteSize(), layout.byteAlignment()); } VMStorage[] regAlloc(int type, int count) { @@ -353,7 +350,7 @@ List getBindings(Class carrier, MemoryLayout layout) { case STRUCT_REFERENCE: { assert carrier == MemorySegment.class; bindings.copy(layout) - .unboxAddress(MemorySegment.class); + .unboxAddress(); VMStorage storage = storageCalculator.nextStorage( StorageClasses.INTEGER, AArch64.C_POINTER); bindings.vmStore(storage, long.class); @@ -384,7 +381,7 @@ List getBindings(Class carrier, MemoryLayout layout) { break; } case POINTER: { - bindings.unboxAddress(carrier); + bindings.unboxAddress(); VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER, layout); bindings.vmStore(storage, long.class); @@ -418,7 +415,7 @@ class BoxBindingCalculator extends BindingCalculator { List getIndirectBindings() { return Binding.builder() .vmLoad(INDIRECT_RESULT, long.class) - .boxAddress() + .boxAddressRaw(Long.MAX_VALUE) .build(); } @@ -427,11 +424,11 @@ List getBindings(Class carrier, MemoryLayout layout) { TypeClass argumentClass = TypeClass.classifyLayout(layout); Binding.Builder bindings = Binding.builder(); switch (argumentClass) { - case STRUCT_REGISTER: { + case STRUCT_REGISTER -> { assert carrier == MemorySegment.class; bindings.allocate(layout); VMStorage[] regs = storageCalculator.regAlloc( - StorageClasses.INTEGER, layout); + StorageClasses.INTEGER, layout); if (regs != null) { int regIndex = 0; long offset = 0; @@ -448,23 +445,20 @@ List getBindings(Class carrier, MemoryLayout layout) { } else { spillStructBox(bindings, layout); } - break; } - case STRUCT_REFERENCE: { + case STRUCT_REFERENCE -> { assert carrier == MemorySegment.class; VMStorage storage = storageCalculator.nextStorage( - StorageClasses.INTEGER, AArch64.C_POINTER); + StorageClasses.INTEGER, AArch64.C_POINTER); bindings.vmLoad(storage, long.class) - .boxAddress() - .toSegment(layout); - break; + .boxAddress(layout); } - case STRUCT_HFA: { + case STRUCT_HFA -> { assert carrier == MemorySegment.class; bindings.allocate(layout); - GroupLayout group = (GroupLayout)layout; + GroupLayout group = (GroupLayout) layout; VMStorage[] regs = storageCalculator.regAlloc( - StorageClasses.VECTOR, group.memberLayouts().size()); + StorageClasses.VECTOR, group.memberLayouts().size()); if (regs != null) { long offset = 0; for (int i = 0; i < group.memberLayouts().size(); i++) { @@ -480,29 +474,24 @@ List getBindings(Class carrier, MemoryLayout layout) { } else { spillStructBox(bindings, layout); } - break; } - case POINTER: { + case POINTER -> { VMStorage storage = - storageCalculator.nextStorage(StorageClasses.INTEGER, layout); + storageCalculator.nextStorage(StorageClasses.INTEGER, layout); bindings.vmLoad(storage, long.class) - .boxAddress(); - break; + .boxAddressRaw(Utils.pointeeSize(layout)); } - case INTEGER: { + case INTEGER -> { VMStorage storage = - storageCalculator.nextStorage(StorageClasses.INTEGER, layout); + storageCalculator.nextStorage(StorageClasses.INTEGER, layout); bindings.vmLoad(storage, carrier); - break; } - case FLOAT: { + case FLOAT -> { VMStorage storage = - storageCalculator.nextStorage(StorageClasses.VECTOR, layout); + storageCalculator.nextStorage(StorageClasses.VECTOR, layout); bindings.vmLoad(storage, carrier); - break; } - default: - throw new UnsupportedOperationException("Unhandled class " + argumentClass); + default -> throw new UnsupportedOperationException("Unhandled class " + argumentClass); } return bindings.build(); } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/TypeClass.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/TypeClass.java index 7a32e99c95eda..722e47b8c066b 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/TypeClass.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/TypeClass.java @@ -26,9 +26,8 @@ package jdk.internal.foreign.abi.aarch64; import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemoryLayout; -import java.lang.foreign.SequenceLayout; +import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; public enum TypeClass { @@ -48,7 +47,7 @@ private static TypeClass classifyValueType(ValueLayout type) { return INTEGER; } else if (carrier == float.class || carrier == double.class) { return FLOAT; - } else if (carrier == MemoryAddress.class) { + } else if (carrier == MemorySegment.class) { return POINTER; } else { throw new IllegalStateException("Cannot get here: " + carrier.getName()); @@ -60,11 +59,9 @@ static boolean isRegisterAggregate(MemoryLayout type) { } static boolean isHomogeneousFloatAggregate(MemoryLayout type) { - if (!(type instanceof GroupLayout)) + if (!(type instanceof GroupLayout groupLayout)) return false; - GroupLayout groupLayout = (GroupLayout)type; - final int numElements = groupLayout.memberLayouts().size(); if (numElements > 4 || numElements == 0) return false; @@ -107,10 +104,8 @@ public static TypeClass classifyLayout(MemoryLayout type) { return classifyValueType((ValueLayout) type); } else if (type instanceof GroupLayout) { return classifyStructType(type); - } else if (type instanceof SequenceLayout) { - return TypeClass.INTEGER; } else { - throw new IllegalArgumentException("Unhandled type " + type); + throw new IllegalArgumentException("Unsupported layout: " + type); } } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64Linker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64Linker.java index 58dd7c793cd18..9114005695d04 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64Linker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64Linker.java @@ -26,12 +26,12 @@ package jdk.internal.foreign.abi.aarch64.linux; import jdk.internal.foreign.abi.AbstractLinker; +import jdk.internal.foreign.abi.LinkerOptions; import jdk.internal.foreign.abi.aarch64.CallArranger; +import java.lang.foreign.SegmentScope; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.lang.foreign.VaList; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; @@ -42,33 +42,37 @@ * the ARM 64-bit Architecture". */ public final class LinuxAArch64Linker extends AbstractLinker { - private static LinuxAArch64Linker instance; public static LinuxAArch64Linker getInstance() { - if (instance == null) { - instance = new LinuxAArch64Linker(); + final class Holder { + private static final LinuxAArch64Linker INSTANCE = new LinuxAArch64Linker(); } - return instance; + + return Holder.INSTANCE; + } + + private LinuxAArch64Linker() { + // Ensure there is only one instance } @Override - protected MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function) { - return CallArranger.LINUX.arrangeDowncall(inferredMethodType, function); + protected MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function, LinkerOptions options) { + return CallArranger.LINUX.arrangeDowncall(inferredMethodType, function, options); } @Override - protected MemorySegment arrangeUpcall(MethodHandle target, MethodType targetType, FunctionDescriptor function, MemorySession scope) { + protected MemorySegment arrangeUpcall(MethodHandle target, MethodType targetType, FunctionDescriptor function, SegmentScope scope) { return CallArranger.LINUX.arrangeUpcall(target, targetType, function, scope); } - public static VaList newVaList(Consumer actions, MemorySession session) { + public static VaList newVaList(Consumer actions, SegmentScope session) { LinuxAArch64VaList.Builder builder = LinuxAArch64VaList.builder(session); actions.accept(builder); return builder.build(); } - public static VaList newVaListOfAddress(MemoryAddress ma, MemorySession session) { - return LinuxAArch64VaList.ofAddress(ma, session); + public static VaList newVaListOfAddress(long address, SegmentScope session) { + return LinuxAArch64VaList.ofAddress(address, session); } public static VaList emptyVaList() { diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64VaList.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64VaList.java index 656f5fd6a51e1..fc43f0193aee0 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64VaList.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64VaList.java @@ -25,13 +25,17 @@ */ package jdk.internal.foreign.abi.aarch64.linux; -import java.lang.foreign.*; +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SegmentScope; +import java.lang.foreign.ValueLayout; +import java.lang.foreign.VaList; +import java.lang.foreign.SegmentAllocator; import jdk.internal.foreign.abi.aarch64.TypeClass; import jdk.internal.foreign.MemorySessionImpl; -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.util.ArrayList; @@ -49,8 +53,7 @@ * 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, Scoped { - private static final Unsafe U = Unsafe.getUnsafe(); +public non-sealed class LinuxAArch64VaList implements VaList { // See AAPCS Appendix B "Variable Argument Lists" for definition of // va_list on AArch64. @@ -117,50 +120,42 @@ private LinuxAArch64VaList(MemorySegment segment, MemorySegment stack, this.fpLimit = fpLimit; } - private static LinuxAArch64VaList readFromSegment(MemorySegment segment) { - MemorySegment stack = MemorySegment.ofAddress(stackPtr(segment), - Long.MAX_VALUE, segment.session()); // size unknown - - MemorySegment gpRegsArea = MemorySegment.ofAddress(grTop(segment).addOffset(-MAX_GP_OFFSET), - MAX_GP_OFFSET, segment.session()); - - MemorySegment fpRegsArea = MemorySegment.ofAddress(vrTop(segment).addOffset(-MAX_FP_OFFSET), - MAX_FP_OFFSET, segment.session()); + private static LinuxAArch64VaList readFromAddress(long address, SegmentScope session) { + MemorySegment segment = MemorySegment.ofAddress(address, LAYOUT.byteSize(), session); + MemorySegment stack = stackPtr(segment); // size unknown + MemorySegment gpRegsArea = MemorySegment.ofAddress(grTop(segment).address() - MAX_GP_OFFSET, MAX_GP_OFFSET, session); + MemorySegment fpRegsArea = MemorySegment.ofAddress(vrTop(segment).address() - MAX_FP_OFFSET, MAX_FP_OFFSET, session); return new LinuxAArch64VaList(segment, stack, gpRegsArea, MAX_GP_OFFSET, fpRegsArea, MAX_FP_OFFSET); } - private static MemoryAddress emptyListAddress() { - long ptr = U.allocateMemory(LAYOUT.byteSize()); - MemorySession session = MemorySession.openImplicit(); - session.addCloseAction(() -> U.freeMemory(ptr)); - MemorySegment ms = MemorySegment.ofAddress(MemoryAddress.ofLong(ptr), - LAYOUT.byteSize(), session); - VH_stack.set(ms, MemoryAddress.NULL); - VH_gr_top.set(ms, MemoryAddress.NULL); - VH_vr_top.set(ms, MemoryAddress.NULL); + private static MemorySegment emptyListAddress() { + MemorySegment ms = MemorySegment.allocateNative(LAYOUT, SegmentScope.auto()); + VH_stack.set(ms, MemorySegment.NULL); + VH_gr_top.set(ms, MemorySegment.NULL); + VH_vr_top.set(ms, MemorySegment.NULL); VH_gr_offs.set(ms, 0); VH_vr_offs.set(ms, 0); - return ms.address(); + return ms.asSlice(0, 0); } public static VaList empty() { return EMPTY; } - private MemoryAddress grTop() { + private MemorySegment grTop() { return grTop(segment); } - private static MemoryAddress grTop(MemorySegment segment) { - return (MemoryAddress) VH_gr_top.get(segment); + private static MemorySegment grTop(MemorySegment segment) { + return (MemorySegment) VH_gr_top.get(segment); } - private MemoryAddress vrTop() { + private MemorySegment vrTop() { return vrTop(segment); } - private static MemoryAddress vrTop(MemorySegment segment) { - return (MemoryAddress) VH_vr_top.get(segment); + private static MemorySegment vrTop(MemorySegment segment) { + return (MemorySegment) VH_vr_top.get(segment); } private int grOffs() { @@ -175,17 +170,17 @@ private int vrOffs() { return offs; } - private static MemoryAddress stackPtr(MemorySegment segment) { - return (MemoryAddress) VH_stack.get(segment); + private static MemorySegment stackPtr(MemorySegment segment) { + return (MemorySegment) VH_stack.get(segment); } - private MemoryAddress stackPtr() { + private MemorySegment stackPtr() { return stackPtr(segment); } private void setStack(MemorySegment newStack) { stack = newStack; - VH_stack.set(segment, stack.address()); + VH_stack.set(segment, stack); } private void consumeGPSlots(int num) { @@ -217,7 +212,7 @@ private long currentFPOffset() { private long preAlignOffset(MemoryLayout layout) { long alignmentOffset = 0; if (layout.byteAlignment() > STACK_SLOT_SIZE) { - long addr = stack.address().toRawLongValue(); + long addr = stack.address(); alignmentOffset = Utils.alignUp(addr, 16) - addr; } return alignmentOffset; @@ -247,8 +242,8 @@ public double nextVarg(ValueLayout.OfDouble layout) { } @Override - public MemoryAddress nextVarg(ValueLayout.OfAddress layout) { - return (MemoryAddress) read(layout); + public MemorySegment nextVarg(ValueLayout.OfAddress layout) { + return (MemorySegment) read(layout); } @Override @@ -318,11 +313,11 @@ private Object read(MemoryLayout layout, SegmentAllocator allocator) { checkGPElement(layout, 1); // Struct is passed indirectly via a pointer in an integer register. VarHandle ptrReader = AArch64.C_POINTER.varHandle(); - MemoryAddress ptr = (MemoryAddress) ptrReader.get( + MemorySegment ptr = (MemorySegment) ptrReader.get( gpRegsArea.asSlice(currentGPOffset())); consumeGPSlots(1); - MemorySegment slice = MemorySegment.ofAddress(ptr, layout.byteSize(), session()); + MemorySegment slice = MemorySegment.ofAddress(ptr.address(), layout.byteSize(), segment.scope()); MemorySegment seg = allocator.allocate(layout); seg.copyFrom(slice); yield seg; @@ -366,7 +361,7 @@ private void checkStackElement(MemoryLayout layout) { @Override public void skip(MemoryLayout... layouts) { Objects.requireNonNull(layouts); - sessionImpl().checkValidState(); + ((MemorySessionImpl) segment.scope()).checkValidState(); for (MemoryLayout layout : layouts) { Objects.requireNonNull(layout); TypeClass typeClass = TypeClass.classifyLayout(layout); @@ -389,29 +384,25 @@ public void skip(MemoryLayout... layouts) { } } - static LinuxAArch64VaList.Builder builder(MemorySession session) { + static LinuxAArch64VaList.Builder builder(SegmentScope session) { return new LinuxAArch64VaList.Builder(session); } - public static VaList ofAddress(MemoryAddress ma, MemorySession session) { - return readFromSegment(MemorySegment.ofAddress(ma, LAYOUT.byteSize(), session)); - } - - @Override - public MemorySession session() { - return segment.session(); + public static VaList ofAddress(long address, SegmentScope session) { + return readFromAddress(address, session); } @Override public VaList copy() { - MemorySegment copy = MemorySegment.allocateNative(LAYOUT, segment.session()); + MemorySegment copy = MemorySegment.allocateNative(LAYOUT, segment.scope()); copy.copyFrom(segment); return new LinuxAArch64VaList(copy, stack, gpRegsArea, gpLimit, fpRegsArea, fpLimit); } @Override - public MemoryAddress address() { - return segment.address(); + public MemorySegment segment() { + // make sure that returned segment cannot be accessed + return segment.asSlice(0, 0); } private static long numSlots(MemoryLayout layout) { @@ -441,7 +432,7 @@ public String toString() { } public static non-sealed class Builder implements VaList.Builder { - private final MemorySession session; + private final SegmentScope session; private final MemorySegment gpRegs; private final MemorySegment fpRegs; @@ -449,7 +440,7 @@ public static non-sealed class Builder implements VaList.Builder { private long currentFPOffset = 0; private final List stackArgs = new ArrayList<>(); - Builder(MemorySession session) { + Builder(SegmentScope session) { this.session = session; this.gpRegs = MemorySegment.allocateNative(LAYOUT_GP_REGS, session); this.fpRegs = MemorySegment.allocateNative(LAYOUT_FP_REGS, session); @@ -471,8 +462,8 @@ public Builder addVarg(ValueLayout.OfDouble layout, double value) { } @Override - public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) { - return arg(layout, value.address()); + public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) { + return arg(layout, value); } @Override @@ -518,7 +509,7 @@ private Builder arg(MemoryLayout layout, Object value) { MemorySegment valueSegment = (MemorySegment) value; VarHandle writer = AArch64.C_POINTER.varHandle(); writer.set(gpRegs.asSlice(currentGPOffset), - valueSegment.address()); + valueSegment); currentGPOffset += GP_SLOT_SIZE; } case POINTER, INTEGER -> { @@ -545,13 +536,12 @@ public VaList build() { return EMPTY; } - SegmentAllocator allocator = SegmentAllocator.newNativeArena(session); - MemorySegment vaListSegment = allocator.allocate(LAYOUT); + MemorySegment vaListSegment = MemorySegment.allocateNative(LAYOUT, session); MemorySegment stackArgsSegment; if (!stackArgs.isEmpty()) { long stackArgsSize = stackArgs.stream() .reduce(0L, (acc, e) -> acc + Utils.alignUp(e.layout.byteSize(), STACK_SLOT_SIZE), Long::sum); - stackArgsSegment = allocator.allocate(stackArgsSize, 16); + stackArgsSegment = MemorySegment.allocateNative(stackArgsSize, 16, session); MemorySegment writeCursor = stackArgsSegment; for (SimpleVaArg arg : stackArgs) { final long alignedSize = Utils.alignUp(arg.layout.byteSize(), STACK_SLOT_SIZE); @@ -561,17 +551,17 @@ public VaList build() { writeCursor = writeCursor.asSlice(alignedSize); } } else { - stackArgsSegment = MemorySegment.ofAddress(MemoryAddress.NULL, 0, session); + stackArgsSegment = MemorySegment.NULL; } - VH_gr_top.set(vaListSegment, gpRegs.asSlice(gpRegs.byteSize()).address()); - VH_vr_top.set(vaListSegment, fpRegs.asSlice(fpRegs.byteSize()).address()); - VH_stack.set(vaListSegment, stackArgsSegment.address()); + VH_gr_top.set(vaListSegment, gpRegs.asSlice(gpRegs.byteSize())); + VH_vr_top.set(vaListSegment, fpRegs.asSlice(fpRegs.byteSize())); + VH_stack.set(vaListSegment, stackArgsSegment); VH_gr_offs.set(vaListSegment, -MAX_GP_OFFSET); VH_vr_offs.set(vaListSegment, -MAX_FP_OFFSET); - assert gpRegs.session().ownerThread() == vaListSegment.session().ownerThread(); - assert fpRegs.session().ownerThread() == vaListSegment.session().ownerThread(); + assert MemorySessionImpl.sameOwnerThread(gpRegs.scope(), vaListSegment.scope()); + assert MemorySessionImpl.sameOwnerThread(fpRegs.scope(), vaListSegment.scope()); return new LinuxAArch64VaList(vaListSegment, stackArgsSegment, gpRegs, currentGPOffset, fpRegs, currentFPOffset); } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64Linker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64Linker.java index c533207b02bfd..ab478f70faaee 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64Linker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64Linker.java @@ -26,12 +26,12 @@ package jdk.internal.foreign.abi.aarch64.macos; import jdk.internal.foreign.abi.AbstractLinker; +import jdk.internal.foreign.abi.LinkerOptions; import jdk.internal.foreign.abi.aarch64.CallArranger; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.VaList; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; @@ -42,33 +42,37 @@ * changes to va_list and passing arguments on the stack. */ public final class MacOsAArch64Linker extends AbstractLinker { - private static MacOsAArch64Linker instance; public static MacOsAArch64Linker getInstance() { - if (instance == null) { - instance = new MacOsAArch64Linker(); + final class Holder { + private static final MacOsAArch64Linker INSTANCE = new MacOsAArch64Linker(); } - return instance; + + return Holder.INSTANCE; + } + + private MacOsAArch64Linker() { + // Ensure there is only one instance } @Override - protected MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function) { - return CallArranger.MACOS.arrangeDowncall(inferredMethodType, function); + protected MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function, LinkerOptions options) { + return CallArranger.MACOS.arrangeDowncall(inferredMethodType, function, options); } @Override - protected MemorySegment arrangeUpcall(MethodHandle target, MethodType targetType, FunctionDescriptor function, MemorySession scope) { + protected MemorySegment arrangeUpcall(MethodHandle target, MethodType targetType, FunctionDescriptor function, SegmentScope scope) { return CallArranger.MACOS.arrangeUpcall(target, targetType, function, scope); } - public static VaList newVaList(Consumer actions, MemorySession session) { + public static VaList newVaList(Consumer actions, SegmentScope session) { MacOsAArch64VaList.Builder builder = MacOsAArch64VaList.builder(session); actions.accept(builder); return builder.build(); } - public static VaList newVaListOfAddress(MemoryAddress ma, MemorySession session) { - return MacOsAArch64VaList.ofAddress(ma, session); + public static VaList newVaListOfAddress(long address, SegmentScope session) { + return MacOsAArch64VaList.ofAddress(address, session); } public static VaList emptyVaList() { diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64VaList.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64VaList.java index 72715aa865437..210b66ecde3ef 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64VaList.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64VaList.java @@ -25,10 +25,15 @@ */ package jdk.internal.foreign.abi.aarch64.macos; -import java.lang.foreign.*; +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SegmentScope; +import java.lang.foreign.SegmentAllocator; +import java.lang.foreign.VaList; +import java.lang.foreign.ValueLayout; import jdk.internal.foreign.abi.aarch64.TypeClass; import jdk.internal.foreign.MemorySessionImpl; -import jdk.internal.foreign.Scoped; import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.SharedUtils.SimpleVaArg; @@ -45,21 +50,19 @@ * 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, Scoped { +public non-sealed class MacOsAArch64VaList implements VaList { private static final long VA_SLOT_SIZE_BYTES = 8; private static final VarHandle VH_address = C_POINTER.varHandle(); - private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemoryAddress.NULL); + private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemorySegment.NULL); private MemorySegment segment; - private final MemorySession session; - private MacOsAArch64VaList(MemorySegment segment, MemorySession session) { + private MacOsAArch64VaList(MemorySegment segment) { this.segment = segment; - this.session = session; } - public static final VaList empty() { + public static VaList empty() { return EMPTY; } @@ -79,8 +82,8 @@ public double nextVarg(ValueLayout.OfDouble layout) { } @Override - public MemoryAddress nextVarg(ValueLayout.OfAddress layout) { - return (MemoryAddress) read(layout); + public MemorySegment nextVarg(ValueLayout.OfAddress layout) { + return (MemorySegment) read(layout); } @Override @@ -101,8 +104,8 @@ private Object read(MemoryLayout layout, SegmentAllocator allocator) { res = switch (typeClass) { case STRUCT_REFERENCE -> { checkElement(layout, VA_SLOT_SIZE_BYTES); - MemoryAddress structAddr = (MemoryAddress) VH_address.get(segment); - MemorySegment struct = MemorySegment.ofAddress(structAddr, layout.byteSize(), session()); + MemorySegment structAddr = (MemorySegment) VH_address.get(segment); + MemorySegment struct = MemorySegment.ofAddress(structAddr.address(), layout.byteSize(), segment.scope()); MemorySegment seg = allocator.allocate(layout); seg.copyFrom(struct); segment = segment.asSlice(VA_SLOT_SIZE_BYTES); @@ -137,7 +140,7 @@ private static long sizeOf(MemoryLayout layout) { @Override public void skip(MemoryLayout... layouts) { Objects.requireNonNull(layouts); - sessionImpl().checkValidState(); + ((MemorySessionImpl) segment.scope()).checkValidState(); for (MemoryLayout layout : layouts) { Objects.requireNonNull(layout); @@ -153,38 +156,34 @@ private void checkElement(MemoryLayout layout, long size) { } } - static MacOsAArch64VaList ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment segment = MemorySegment.ofAddress(addr, Long.MAX_VALUE, session); - return new MacOsAArch64VaList(segment, session); + static MacOsAArch64VaList ofAddress(long address, SegmentScope session) { + MemorySegment segment = MemorySegment.ofAddress(address, Long.MAX_VALUE, session); + return new MacOsAArch64VaList(segment); } - static Builder builder(MemorySession session) { + static Builder builder(SegmentScope session) { return new Builder(session); } - @Override - public MemorySession session() { - return session; - } - @Override public VaList copy() { - sessionImpl().checkValidState(); - return new MacOsAArch64VaList(segment, session); + ((MemorySessionImpl) segment.scope()).checkValidState(); + return new MacOsAArch64VaList(segment); } @Override - public MemoryAddress address() { - return segment.address(); + public MemorySegment segment() { + // make sure that returned segment cannot be accessed + return segment.asSlice(0, 0); } public static non-sealed class Builder implements VaList.Builder { - private final MemorySession session; + private final SegmentScope session; private final List args = new ArrayList<>(); - public Builder(MemorySession session) { - MemorySessionImpl.toSessionImpl(session).checkValidState(); + public Builder(SegmentScope session) { + ((MemorySessionImpl) session).checkValidState(); this.session = session; } @@ -211,8 +210,8 @@ public Builder addVarg(ValueLayout.OfDouble layout, double value) { } @Override - public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) { - return arg(layout, value.address()); + public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) { + return arg(layout, value); } @Override @@ -225,10 +224,8 @@ public VaList build() { return EMPTY; } - SegmentAllocator allocator = SegmentAllocator.newNativeArena(session); - long allocationSize = args.stream().reduce(0L, (acc, e) -> acc + sizeOf(e.layout), Long::sum); - MemorySegment segment = allocator.allocate(allocationSize); + MemorySegment segment = MemorySegment.allocateNative(allocationSize, session); MemorySegment cursor = segment; for (SimpleVaArg arg : args) { @@ -237,9 +234,9 @@ public VaList build() { TypeClass typeClass = TypeClass.classifyLayout(arg.layout); switch (typeClass) { case STRUCT_REFERENCE -> { - MemorySegment copy = allocator.allocate(arg.layout); + MemorySegment copy = MemorySegment.allocateNative(arg.layout, session); copy.copyFrom(msArg); // by-value - VH_address.set(cursor, copy.address()); + VH_address.set(cursor, copy); cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES); } case STRUCT_REGISTER, STRUCT_HFA -> @@ -254,7 +251,7 @@ public VaList build() { } } - return new MacOsAArch64VaList(segment, session); + return new MacOsAArch64VaList(segment); } } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/CallArranger.java b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/CallArranger.java index 897ac3b9be901..441529c27655c 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/CallArranger.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/CallArranger.java @@ -25,6 +25,7 @@ */ package jdk.internal.foreign.abi.x64.sysv; +import jdk.internal.foreign.Utils; import jdk.internal.foreign.abi.ABIDescriptor; import jdk.internal.foreign.abi.Binding; import jdk.internal.foreign.abi.CallingSequence; @@ -34,12 +35,11 @@ import jdk.internal.foreign.abi.UpcallLinker; import jdk.internal.foreign.abi.VMStorage; +import java.lang.foreign.SegmentScope; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -73,17 +73,10 @@ public class CallArranger { r11 // ret buf addr reg ); - // record - public static class Bindings { - public final CallingSequence callingSequence; - public final boolean isInMemoryReturn; - public final int nVectorArgs; - - Bindings(CallingSequence callingSequence, boolean isInMemoryReturn, int nVectorArgs) { - this.callingSequence = callingSequence; - this.isInMemoryReturn = isInMemoryReturn; - this.nVectorArgs = nVectorArgs; - } + public record Bindings( + CallingSequence callingSequence, + boolean isInMemoryReturn, + int nVectorArgs) { } public static Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall) { @@ -94,7 +87,7 @@ public static Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, bool boolean returnInMemory = isInMemoryReturn(cDesc.returnLayout()); if (returnInMemory) { - Class carrier = MemoryAddress.class; + Class carrier = MemorySegment.class; MemoryLayout layout = SysV.C_POINTER; csb.addArgumentBindings(carrier, layout, argCalc.getBindings(carrier, layout)); } else if (cDesc.returnLayout().isPresent()) { @@ -131,7 +124,7 @@ public static MethodHandle arrangeDowncall(MethodType mt, FunctionDescriptor cDe return handle; } - public static MemorySegment arrangeUpcall(MethodHandle target, MethodType mt, FunctionDescriptor cDesc, MemorySession session) { + public static MemorySegment arrangeUpcall(MethodHandle target, MethodType mt, FunctionDescriptor cDesc, SegmentScope session) { Bindings bindings = getBindings(mt, cDesc, true); if (bindings.isInMemoryReturn) { @@ -212,26 +205,18 @@ VMStorage[] structStorages(TypeClass typeClass) { } int registerCount(int type) { - switch (type) { - case StorageClasses.INTEGER: - return nIntegerReg; - case StorageClasses.VECTOR: - return nVectorReg; - default: - throw new IllegalStateException(); - } + return switch (type) { + case StorageClasses.INTEGER -> nIntegerReg; + case StorageClasses.VECTOR -> nVectorReg; + default -> throw new IllegalStateException(); + }; } void incrementRegisterCount(int type) { switch (type) { - case StorageClasses.INTEGER: - nIntegerReg++; - break; - case StorageClasses.VECTOR: - nVectorReg++; - break; - default: - throw new IllegalStateException(); + case StorageClasses.INTEGER -> nIntegerReg++; + case StorageClasses.VECTOR -> nVectorReg++; + default -> throw new IllegalStateException(); } } } @@ -257,7 +242,7 @@ List getBindings(Class carrier, MemoryLayout layout) { TypeClass argumentClass = TypeClass.classifyLayout(layout); Binding.Builder bindings = Binding.builder(); switch (argumentClass.kind()) { - case STRUCT: { + case STRUCT -> { assert carrier == MemorySegment.class; VMStorage[] regs = storageCalculator.structStorages(argumentClass); int regIndex = 0; @@ -274,26 +259,21 @@ List getBindings(Class carrier, MemoryLayout layout) { .vmStore(storage, type); offset += copy; } - break; } - case POINTER: { - bindings.unboxAddress(carrier); + case POINTER -> { + bindings.unboxAddress(); VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER); bindings.vmStore(storage, long.class); - break; - } - case INTEGER: { + } + case INTEGER -> { VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER); bindings.vmStore(storage, carrier); - break; } - case FLOAT: { + case FLOAT -> { VMStorage storage = storageCalculator.nextStorage(StorageClasses.VECTOR); bindings.vmStore(storage, carrier); - break; } - default: - throw new UnsupportedOperationException("Unhandled class " + argumentClass); + default -> throw new UnsupportedOperationException("Unhandled class " + argumentClass); } return bindings.build(); } @@ -310,7 +290,7 @@ List getBindings(Class carrier, MemoryLayout layout) { TypeClass argumentClass = TypeClass.classifyLayout(layout); Binding.Builder bindings = Binding.builder(); switch (argumentClass.kind()) { - case STRUCT: { + case STRUCT -> { assert carrier == MemorySegment.class; bindings.allocate(layout); VMStorage[] regs = storageCalculator.structStorages(argumentClass); @@ -326,26 +306,21 @@ List getBindings(Class carrier, MemoryLayout layout) { .bufferStore(offset, type); offset += copy; } - break; } - case POINTER: { + case POINTER -> { VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER); bindings.vmLoad(storage, long.class) - .boxAddress(); - break; + .boxAddressRaw(Utils.pointeeSize(layout)); } - case INTEGER: { + case INTEGER -> { VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER); bindings.vmLoad(storage, carrier); - break; } - case FLOAT: { + case FLOAT -> { VMStorage storage = storageCalculator.nextStorage(StorageClasses.VECTOR); bindings.vmLoad(storage, carrier); - break; } - default: - throw new UnsupportedOperationException("Unhandled class " + argumentClass); + default -> throw new UnsupportedOperationException("Unhandled class " + argumentClass); } return bindings.build(); } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVVaList.java b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVVaList.java index 6cafc83396e99..78264f69ae31f 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVVaList.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVVaList.java @@ -25,13 +25,17 @@ */ package jdk.internal.foreign.abi.x64.sysv; -import java.lang.foreign.*; +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SegmentScope; +import java.lang.foreign.SegmentAllocator; +import java.lang.foreign.VaList; +import java.lang.foreign.ValueLayout; import jdk.internal.foreign.MemorySessionImpl; -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.util.ArrayList; @@ -45,8 +49,7 @@ import static jdk.internal.foreign.abi.SharedUtils.THROWING_ALLOCATOR; // 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, Scoped { - private static final Unsafe U = Unsafe.getUnsafe(); +public non-sealed class SysVVaList implements VaList { // struct typedef __va_list_tag __va_list_tag { // unsigned int gp_offset; /* 0 4 */ @@ -127,23 +130,20 @@ private SysVVaList(MemorySegment segment, this.fpLimit = fpLimit; } - private static SysVVaList readFromSegment(MemorySegment segment) { + private static SysVVaList readFromAddress(long address, SegmentScope session) { + MemorySegment segment = MemorySegment.ofAddress(address, LAYOUT.byteSize(), session); MemorySegment regSaveArea = getRegSaveArea(segment); MemorySegment overflowArgArea = getArgOverflowArea(segment); return new SysVVaList(segment, overflowArgArea, regSaveArea, MAX_GP_OFFSET, MAX_FP_OFFSET); } - private static MemoryAddress emptyListAddress() { - long ptr = U.allocateMemory(LAYOUT.byteSize()); - MemorySession session = MemorySession.openImplicit(); - session.addCloseAction(() -> U.freeMemory(ptr)); - MemorySegment base = MemorySegment.ofAddress(MemoryAddress.ofLong(ptr), - LAYOUT.byteSize(), session); + private static MemorySegment emptyListAddress() { + MemorySegment base = MemorySegment.allocateNative(LAYOUT, SegmentScope.auto()); VH_gp_offset.set(base, MAX_GP_OFFSET); VH_fp_offset.set(base, MAX_FP_OFFSET); - VH_overflow_arg_area.set(base, MemoryAddress.NULL); - VH_reg_save_area.set(base, MemoryAddress.NULL); - return base.address(); + VH_overflow_arg_area.set(base, MemorySegment.NULL); + VH_reg_save_area.set(base, MemorySegment.NULL); + return base.asSlice(0, 0); } public static VaList empty() { @@ -167,19 +167,18 @@ private void currentFPOffset(int i) { } private static MemorySegment getRegSaveArea(MemorySegment segment) { - return MemorySegment.ofAddress(((MemoryAddress)VH_reg_save_area.get(segment)), - LAYOUT_REG_SAVE_AREA.byteSize(), segment.session()); + return ((MemorySegment)VH_reg_save_area.get(segment)) + .asSlice(0, LAYOUT_REG_SAVE_AREA.byteSize()); } private static MemorySegment getArgOverflowArea(MemorySegment segment) { - return MemorySegment.ofAddress(((MemoryAddress)VH_overflow_arg_area.get(segment)), - Long.MAX_VALUE, segment.session()); // size unknown + return (MemorySegment)VH_overflow_arg_area.get(segment); // size unknown } private long preAlignOffset(MemoryLayout layout) { long alignmentOffset = 0; if (layout.byteAlignment() > STACK_SLOT_SIZE) { - long addr = overflowArgArea.address().toRawLongValue(); + long addr = overflowArgArea.address(); alignmentOffset = Utils.alignUp(addr, 16) - addr; } return alignmentOffset; @@ -187,7 +186,7 @@ private long preAlignOffset(MemoryLayout layout) { private void setOverflowArgArea(MemorySegment newSegment) { overflowArgArea = newSegment; - VH_overflow_arg_area.set(segment, overflowArgArea.address()); + VH_overflow_arg_area.set(segment, overflowArgArea); } private void preAlignStack(MemoryLayout layout) { @@ -214,8 +213,8 @@ public double nextVarg(ValueLayout.OfDouble layout) { } @Override - public MemoryAddress nextVarg(ValueLayout.OfAddress layout) { - return (MemoryAddress) read(layout); + public MemorySegment nextVarg(ValueLayout.OfAddress layout) { + return (MemorySegment) read(layout); } @Override @@ -307,7 +306,7 @@ private void checkStackElement(MemoryLayout layout) { @Override public void skip(MemoryLayout... layouts) { Objects.requireNonNull(layouts); - sessionImpl().checkValidState(); + ((MemorySessionImpl) segment.scope()).checkValidState(); for (MemoryLayout layout : layouts) { Objects.requireNonNull(layout); TypeClass typeClass = TypeClass.classifyLayout(layout); @@ -323,29 +322,25 @@ public void skip(MemoryLayout... layouts) { } } - static SysVVaList.Builder builder(MemorySession session) { + static SysVVaList.Builder builder(SegmentScope session) { return new SysVVaList.Builder(session); } - public static VaList ofAddress(MemoryAddress ma, MemorySession session) { - return readFromSegment(MemorySegment.ofAddress(ma, LAYOUT.byteSize(), session)); - } - - @Override - public MemorySession session() { - return segment.session(); + public static VaList ofAddress(long address, SegmentScope session) { + return readFromAddress(address, session); } @Override public VaList copy() { - MemorySegment copy = MemorySegment.allocateNative(LAYOUT, segment.session()); + MemorySegment copy = MemorySegment.allocateNative(LAYOUT, segment.scope()); copy.copyFrom(segment); return new SysVVaList(copy, overflowArgArea, regSaveArea, gpLimit, fpLimit); } @Override - public MemoryAddress address() { - return segment.address(); + public MemorySegment segment() { + // make sure that returned segment cannot be accessed + return segment.asSlice(0, 0); } private static boolean isRegOverflow(long currentGPOffset, long currentFPOffset, TypeClass typeClass) { @@ -364,13 +359,13 @@ public String toString() { } public static non-sealed class Builder implements VaList.Builder { - private final MemorySession session; + private final SegmentScope session; private final MemorySegment reg_save_area; private long currentGPOffset = 0; private long currentFPOffset = FP_OFFSET; private final List stackArgs = new ArrayList<>(); - public Builder(MemorySession session) { + public Builder(SegmentScope session) { this.session = session; this.reg_save_area = MemorySegment.allocateNative(LAYOUT_REG_SAVE_AREA, session); } @@ -391,8 +386,8 @@ public Builder addVarg(ValueLayout.OfDouble layout, double value) { } @Override - public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) { - return arg(layout, value.address()); + public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) { + return arg(layout, value); } @Override @@ -451,19 +446,18 @@ public VaList build() { return EMPTY; } - SegmentAllocator allocator = SegmentAllocator.newNativeArena(session); - MemorySegment vaListSegment = allocator.allocate(LAYOUT); + MemorySegment vaListSegment = MemorySegment.allocateNative(LAYOUT, session); MemorySegment stackArgsSegment; if (!stackArgs.isEmpty()) { long stackArgsSize = stackArgs.stream().reduce(0L, (acc, e) -> acc + Utils.alignUp(e.layout.byteSize(), STACK_SLOT_SIZE), Long::sum); - stackArgsSegment = allocator.allocate(stackArgsSize, 16); + stackArgsSegment = MemorySegment.allocateNative(stackArgsSize, 16, session); MemorySegment writeCursor = stackArgsSegment; for (SimpleVaArg arg : stackArgs) { if (arg.layout.byteSize() > 8) { writeCursor = Utils.alignUp(writeCursor, Math.min(16, arg.layout.byteSize())); } - if (arg.value instanceof MemorySegment) { + if (arg.layout instanceof GroupLayout) { writeCursor.copyFrom((MemorySegment) arg.value); } else { VarHandle writer = arg.varHandle(); @@ -472,13 +466,13 @@ public VaList build() { writeCursor = writeCursor.asSlice(Utils.alignUp(arg.layout.byteSize(), STACK_SLOT_SIZE)); } } else { - stackArgsSegment = MemorySegment.ofAddress(MemoryAddress.NULL, 0, session); + stackArgsSegment = MemorySegment.NULL; } VH_fp_offset.set(vaListSegment, (int) FP_OFFSET); - VH_overflow_arg_area.set(vaListSegment, stackArgsSegment.address()); - VH_reg_save_area.set(vaListSegment, reg_save_area.address()); - assert reg_save_area.session().ownerThread() == vaListSegment.session().ownerThread(); + VH_overflow_arg_area.set(vaListSegment, stackArgsSegment); + VH_reg_save_area.set(vaListSegment, reg_save_area); + assert MemorySessionImpl.sameOwnerThread(reg_save_area.scope(), vaListSegment.scope()); return new SysVVaList(vaListSegment, stackArgsSegment, reg_save_area, currentGPOffset, currentFPOffset); } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVx64Linker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVx64Linker.java index f0b9f59e4c9b3..80896bdb9fcff 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVx64Linker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/SysVx64Linker.java @@ -26,11 +26,11 @@ import jdk.internal.foreign.abi.AbstractLinker; +import jdk.internal.foreign.abi.LinkerOptions; +import java.lang.foreign.SegmentScope; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.lang.foreign.VaList; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; @@ -40,33 +40,36 @@ * ABI implementation based on System V ABI AMD64 supplement v.0.99.6 */ public final class SysVx64Linker extends AbstractLinker { - private static SysVx64Linker instance; public static SysVx64Linker getInstance() { - if (instance == null) { - instance = new SysVx64Linker(); + final class Holder { + private static final SysVx64Linker INSTANCE = new SysVx64Linker(); } - return instance; + + return Holder.INSTANCE; } + private SysVx64Linker() { + // Ensure there is only one instance + } @Override - protected MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function) { + protected MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function, LinkerOptions options) { return CallArranger.arrangeDowncall(inferredMethodType, function); } @Override - protected MemorySegment arrangeUpcall(MethodHandle target, MethodType targetType, FunctionDescriptor function, MemorySession scope) { + protected MemorySegment arrangeUpcall(MethodHandle target, MethodType targetType, FunctionDescriptor function, SegmentScope scope) { return CallArranger.arrangeUpcall(target, targetType, function, scope); } - public static VaList newVaList(Consumer actions, MemorySession scope) { + public static VaList newVaList(Consumer actions, SegmentScope scope) { SysVVaList.Builder builder = SysVVaList.builder(scope); actions.accept(builder); return builder.build(); } - public static VaList newVaListOfAddress(MemoryAddress ma, MemorySession session) { - return SysVVaList.ofAddress(ma, session); + public static VaList newVaListOfAddress(long address, SegmentScope session) { + return SysVVaList.ofAddress(address, session); } public static VaList emptyVaList() { diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/TypeClass.java b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/TypeClass.java index 4a3627af6513d..8106f27059a42 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/TypeClass.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/TypeClass.java @@ -25,9 +25,11 @@ package jdk.internal.foreign.abi.x64.sysv; import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.PaddingLayout; import java.lang.foreign.SequenceLayout; +import java.lang.foreign.StructLayout; import java.lang.foreign.ValueLayout; import jdk.internal.foreign.Utils; @@ -106,14 +108,14 @@ private static List createMemoryClassArray(long size) { .collect(Collectors.toCollection(ArrayList::new)); } - private static ArgumentClassImpl argumentClassFor(MemoryLayout layout) { - Class carrier = ((ValueLayout)layout).carrier(); + private static ArgumentClassImpl argumentClassFor(ValueLayout layout) { + Class carrier = 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) { + } else if (carrier == MemorySegment.class) { return ArgumentClassImpl.POINTER; } else { throw new IllegalStateException("Cannot get here: " + carrier.getName()); @@ -173,12 +175,12 @@ private static List classifyStructType(GroupLayout type) { static TypeClass classifyLayout(MemoryLayout type) { try { - if (type instanceof ValueLayout) { - return ofValue((ValueLayout)type); - } else if (type instanceof GroupLayout) { - return ofStruct((GroupLayout)type); + if (type instanceof ValueLayout valueLayout) { + return ofValue(valueLayout); + } else if (type instanceof GroupLayout groupLayout) { + return ofStruct(groupLayout); } else { - throw new IllegalArgumentException("Unhandled type " + type); + throw new IllegalArgumentException("Unsupported layout: " + type); } } catch (UnsupportedOperationException e) { System.err.println("Failed to classify layout: " + type); @@ -193,7 +195,7 @@ private static List[] groupByEightBytes(GroupLayout group) { List[] groups = new List[nEightbytes]; for (MemoryLayout l : group.memberLayouts()) { groupByEightBytes(l, offset, groups); - if (group.isStruct()) { + if (group instanceof StructLayout) { offset += l.byteSize(); } } @@ -201,32 +203,30 @@ private static List[] groupByEightBytes(GroupLayout group) { } private static void groupByEightBytes(MemoryLayout l, long offset, List[] groups) { - if (l instanceof GroupLayout) { - GroupLayout group = (GroupLayout)l; + if (l instanceof GroupLayout group) { for (MemoryLayout m : group.memberLayouts()) { groupByEightBytes(m, offset, groups); - if (group.isStruct()) { + if (group instanceof StructLayout) { offset += m.byteSize(); } } - } else if (l.isPadding()) { + } else if (l instanceof PaddingLayout) { return; - } else if (l instanceof SequenceLayout) { - SequenceLayout seq = (SequenceLayout)l; + } else if (l instanceof SequenceLayout seq) { MemoryLayout elem = seq.elementLayout(); for (long i = 0 ; i < seq.elementCount() ; i++) { groupByEightBytes(elem, offset, groups); offset += elem.byteSize(); } - } else if (l instanceof ValueLayout) { + } else if (l instanceof ValueLayout vl) { List layouts = groups[(int)offset / 8]; if (layouts == null) { layouts = new ArrayList<>(); groups[(int)offset / 8] = layouts; } // if the aggregate contains unaligned fields, it has class MEMORY - ArgumentClassImpl argumentClass = (offset % l.byteAlignment()) == 0 ? - argumentClassFor(l) : + ArgumentClassImpl argumentClass = (offset % vl.byteAlignment()) == 0 ? + argumentClassFor(vl) : ArgumentClassImpl.MEMORY; layouts.add(argumentClass); } else { diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/CallArranger.java b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/CallArranger.java index e0403e71894e7..ff68ea08ea8c4 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/CallArranger.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/CallArranger.java @@ -30,17 +30,17 @@ import jdk.internal.foreign.abi.CallingSequence; import jdk.internal.foreign.abi.CallingSequenceBuilder; import jdk.internal.foreign.abi.DowncallLinker; +import jdk.internal.foreign.abi.LinkerOptions; import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.UpcallLinker; import jdk.internal.foreign.abi.VMStorage; import jdk.internal.foreign.abi.x64.X86_64Architecture; +import java.lang.foreign.SegmentScope; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.util.List; @@ -73,18 +73,16 @@ public class CallArranger { r11 // ret buf addr reg ); - // record - public static class Bindings { - public final CallingSequence callingSequence; - public final boolean isInMemoryReturn; - - Bindings(CallingSequence callingSequence, boolean isInMemoryReturn) { - this.callingSequence = callingSequence; - this.isInMemoryReturn = isInMemoryReturn; - } + public record Bindings( + CallingSequence callingSequence, + boolean isInMemoryReturn) { } public static Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall) { + return getBindings(mt, cDesc, forUpcall, LinkerOptions.empty()); + } + + public static Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall, LinkerOptions options) { class CallingSequenceBuilderHelper { final CallingSequenceBuilder csb = new CallingSequenceBuilder(CWindows, forUpcall); final BindingCalculator argCalc = @@ -104,7 +102,7 @@ void setReturnBindings(Class carrier, MemoryLayout layout) { boolean returnInMemory = isInMemoryReturn(cDesc.returnLayout()); if (returnInMemory) { - Class carrier = MemoryAddress.class; + Class carrier = MemorySegment.class; MemoryLayout layout = Win64.C_POINTER; csb.addArgumentBindings(carrier, layout, false); if (forUpcall) { @@ -115,14 +113,14 @@ void setReturnBindings(Class carrier, MemoryLayout layout) { } for (int i = 0; i < mt.parameterCount(); i++) { - csb.addArgumentBindings(mt.parameterType(i), cDesc.argumentLayouts().get(i), SharedUtils.isVarargsIndex(cDesc, i)); + csb.addArgumentBindings(mt.parameterType(i), cDesc.argumentLayouts().get(i), options.isVarargsIndex(i)); } return new Bindings(csb.csb.build(), returnInMemory); } - public static MethodHandle arrangeDowncall(MethodType mt, FunctionDescriptor cDesc) { - Bindings bindings = getBindings(mt, cDesc, false); + public static MethodHandle arrangeDowncall(MethodType mt, FunctionDescriptor cDesc, LinkerOptions options) { + Bindings bindings = getBindings(mt, cDesc, false, options); MethodHandle handle = new DowncallLinker(CWindows, bindings.callingSequence).getBoundMethodHandle(); @@ -133,7 +131,7 @@ public static MethodHandle arrangeDowncall(MethodType mt, FunctionDescriptor cDe return handle; } - public static MemorySegment arrangeUpcall(MethodHandle target, MethodType mt, FunctionDescriptor cDesc, MemorySession session) { + public static MemorySegment arrangeUpcall(MethodHandle target, MethodType mt, FunctionDescriptor cDesc, SegmentScope session) { Bindings bindings = getBindings(mt, cDesc, true); if (bindings.isInMemoryReturn) { @@ -160,12 +158,11 @@ public StorageCalculator(boolean forArguments) { this.forArguments = forArguments; } - VMStorage nextStorage(int type, MemoryLayout layout) { + VMStorage nextStorage(int type) { if (nRegs >= MAX_REGISTER_ARGUMENTS) { assert forArguments : "no stack returns"; // stack - long alignment = Math.max(SharedUtils.alignment(layout, true), STACK_SLOT_SIZE); - stackOffset = Utils.alignUp(stackOffset, alignment); + assert stackOffset == Utils.alignUp(stackOffset, STACK_SLOT_SIZE); // should always be aligned VMStorage storage = X86_64Architecture.stackStorage((int) (stackOffset / STACK_SLOT_SIZE)); stackOffset += STACK_SLOT_SIZE; @@ -201,7 +198,7 @@ public List getBindings(Class carrier, MemoryLayout layout, boolean switch (argumentClass) { case STRUCT_REGISTER: { assert carrier == MemorySegment.class; - VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER, layout); + VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER); Class type = SharedUtils.primitiveCarrierForSize(layout.byteSize(), false); bindings.bufferLoad(0, type) .vmStore(storage, type); @@ -210,29 +207,29 @@ public List getBindings(Class carrier, MemoryLayout layout, boolean case STRUCT_REFERENCE: { assert carrier == MemorySegment.class; bindings.copy(layout) - .unboxAddress(MemorySegment.class); - VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER, layout); + .unboxAddress(); + VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER); bindings.vmStore(storage, long.class); break; } case POINTER: { - bindings.unboxAddress(carrier); - VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER, layout); + bindings.unboxAddress(); + VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER); bindings.vmStore(storage, long.class); break; } case INTEGER: { - VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER, layout); + VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER); bindings.vmStore(storage, carrier); break; } case FLOAT: { - VMStorage storage = storageCalculator.nextStorage(StorageClasses.VECTOR, layout); + VMStorage storage = storageCalculator.nextStorage(StorageClasses.VECTOR); bindings.vmStore(storage, carrier); break; } case VARARG_FLOAT: { - VMStorage storage = storageCalculator.nextStorage(StorageClasses.VECTOR, layout); + VMStorage storage = storageCalculator.nextStorage(StorageClasses.VECTOR); if (!INSTANCE.isStackType(storage.type())) { // need extra for register arg VMStorage extraStorage = storageCalculator.extraVarargsStorage(); bindings.dup() @@ -265,7 +262,7 @@ public List getBindings(Class carrier, MemoryLayout layout, boolean assert carrier == MemorySegment.class; bindings.allocate(layout) .dup(); - VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER, layout); + VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER); Class type = SharedUtils.primitiveCarrierForSize(layout.byteSize(), false); bindings.vmLoad(storage, type) .bufferStore(0, type); @@ -273,25 +270,24 @@ public List getBindings(Class carrier, MemoryLayout layout, boolean } case STRUCT_REFERENCE: { assert carrier == MemorySegment.class; - VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER, layout); + VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER); bindings.vmLoad(storage, long.class) - .boxAddress() - .toSegment(layout); + .boxAddress(layout); break; } case POINTER: { - VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER, layout); + VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER); bindings.vmLoad(storage, long.class) - .boxAddress(); + .boxAddressRaw(Utils.pointeeSize(layout)); break; } case INTEGER: { - VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER, layout); + VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER); bindings.vmLoad(storage, carrier); break; } case FLOAT: { - VMStorage storage = storageCalculator.nextStorage(StorageClasses.VECTOR, layout); + VMStorage storage = storageCalculator.nextStorage(StorageClasses.VECTOR); bindings.vmLoad(storage, carrier); break; } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/TypeClass.java b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/TypeClass.java index 420fe027297d3..892e4c49318cd 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/TypeClass.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/TypeClass.java @@ -25,8 +25,8 @@ package jdk.internal.foreign.abi.x64.windows; import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; enum TypeClass { @@ -38,7 +38,7 @@ enum TypeClass { VARARG_FLOAT; 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 + // 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 @@ -57,7 +57,7 @@ private static TypeClass classifyValueType(ValueLayout type, boolean isVararg) { } else { return FLOAT; } - } else if (carrier == MemoryAddress.class) { + } else if (carrier == MemorySegment.class) { return POINTER; } else { throw new IllegalStateException("Cannot get here: " + carrier.getName()); @@ -85,7 +85,7 @@ static TypeClass typeClassFor(MemoryLayout type, boolean isVararg) { } else if (type instanceof GroupLayout) { return classifyStructType(type); } else { - throw new IllegalArgumentException("Unhandled type " + type); + throw new IllegalArgumentException("Unsupported layout: " + type); } } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/WinVaList.java b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/WinVaList.java index f8dfe0a2e441a..6d9bcf82f48b3 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/WinVaList.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/WinVaList.java @@ -25,10 +25,15 @@ */ package jdk.internal.foreign.abi.x64.windows; -import java.lang.foreign.*; +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SegmentScope; +import java.lang.foreign.SegmentAllocator; +import java.lang.foreign.VaList; +import java.lang.foreign.ValueLayout; import jdk.internal.foreign.MemorySessionImpl; -import jdk.internal.foreign.Scoped; import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.SharedUtils.SimpleVaArg; @@ -36,7 +41,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.stream.Stream; import static jdk.internal.foreign.PlatformLayouts.Win64.C_POINTER; @@ -56,18 +60,16 @@ // ? **(t**)((ap += sizeof(__int64)) - sizeof(__int64)) \ // : *(t* )((ap += sizeof(__int64)) - sizeof(__int64))) // -public non-sealed class WinVaList implements VaList, Scoped { +public non-sealed class WinVaList implements VaList { private static final long VA_SLOT_SIZE_BYTES = 8; private static final VarHandle VH_address = C_POINTER.varHandle(); - private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemoryAddress.NULL); + private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemorySegment.NULL); private MemorySegment segment; - private final MemorySession session; - private WinVaList(MemorySegment segment, MemorySession session) { + private WinVaList(MemorySegment segment) { this.segment = segment; - this.session = session; } public static final VaList empty() { @@ -90,8 +92,8 @@ public double nextVarg(ValueLayout.OfDouble layout) { } @Override - public MemoryAddress nextVarg(ValueLayout.OfAddress layout) { - return (MemoryAddress) read(layout); + public MemorySegment nextVarg(ValueLayout.OfAddress layout) { + return (MemorySegment) read(layout); } @Override @@ -112,8 +114,8 @@ private Object read(MemoryLayout layout, SegmentAllocator allocator) { TypeClass typeClass = TypeClass.typeClassFor(layout, false); res = switch (typeClass) { case STRUCT_REFERENCE -> { - MemoryAddress structAddr = (MemoryAddress) VH_address.get(segment); - MemorySegment struct = MemorySegment.ofAddress(structAddr, layout.byteSize(), session()); + MemorySegment structAddr = (MemorySegment) VH_address.get(segment); + MemorySegment struct = MemorySegment.ofAddress(structAddr.address(), layout.byteSize(), segment.scope()); MemorySegment seg = allocator.allocate(layout); seg.copyFrom(struct); yield seg; @@ -139,7 +141,7 @@ private void checkElement(MemoryLayout layout) { @Override public void skip(MemoryLayout... layouts) { Objects.requireNonNull(layouts); - sessionImpl().checkValidState(); + ((MemorySessionImpl) segment.scope()).checkValidState(); for (MemoryLayout layout : layouts) { Objects.requireNonNull(layout); checkElement(layout); @@ -147,38 +149,33 @@ public void skip(MemoryLayout... layouts) { } } - static WinVaList ofAddress(MemoryAddress addr, MemorySession session) { - MemorySegment segment = MemorySegment.ofAddress(addr, Long.MAX_VALUE, session); - return new WinVaList(segment, session); + static WinVaList ofAddress(long address, SegmentScope session) { + return new WinVaList(MemorySegment.ofAddress(address, Long.MAX_VALUE, session)); } - static Builder builder(MemorySession session) { + static Builder builder(SegmentScope session) { return new Builder(session); } - @Override - public MemorySession session() { - return session; - } - @Override public VaList copy() { - sessionImpl().checkValidState(); - return new WinVaList(segment, session); + ((MemorySessionImpl) segment.scope()).checkValidState(); + return new WinVaList(segment); } @Override - public MemoryAddress address() { - return segment.address(); + public MemorySegment segment() { + // make sure that returned segment cannot be accessed + return segment.asSlice(0, 0); } public static non-sealed class Builder implements VaList.Builder { - private final MemorySession session; + private final SegmentScope session; private final List args = new ArrayList<>(); - public Builder(MemorySession session) { - MemorySessionImpl.toSessionImpl(session).checkValidState(); + public Builder(SegmentScope session) { + ((MemorySessionImpl) session).checkValidState(); this.session = session; } @@ -205,8 +202,8 @@ public Builder addVarg(ValueLayout.OfDouble layout, double value) { } @Override - public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) { - return arg(layout, value.address()); + public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) { + return arg(layout, value); } @Override @@ -218,8 +215,8 @@ public VaList build() { if (args.isEmpty()) { return EMPTY; } - SegmentAllocator allocator = SegmentAllocator.newNativeArena(session); - MemorySegment segment = allocator.allocate(VA_SLOT_SIZE_BYTES * args.size()); + + MemorySegment segment = MemorySegment.allocateNative(VA_SLOT_SIZE_BYTES * args.size(), session); MemorySegment cursor = segment; for (SimpleVaArg arg : args) { @@ -228,9 +225,9 @@ public VaList build() { TypeClass typeClass = TypeClass.typeClassFor(arg.layout, false); switch (typeClass) { case STRUCT_REFERENCE -> { - MemorySegment copy = allocator.allocate(arg.layout); + MemorySegment copy = MemorySegment.allocateNative(arg.layout, session); copy.copyFrom(msArg); // by-value - VH_address.set(cursor, copy.address()); + VH_address.set(cursor, copy); } case STRUCT_REGISTER -> cursor.copyFrom(msArg.asSlice(0, VA_SLOT_SIZE_BYTES)); @@ -243,7 +240,7 @@ public VaList build() { cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES); } - return new WinVaList(segment, session); + return new WinVaList(segment); } } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/Windowsx64Linker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/Windowsx64Linker.java index e36651ecc8156..24d12dd8c560c 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/Windowsx64Linker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/Windowsx64Linker.java @@ -25,11 +25,11 @@ package jdk.internal.foreign.abi.x64.windows; import jdk.internal.foreign.abi.AbstractLinker; +import jdk.internal.foreign.abi.LinkerOptions; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.VaList; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; @@ -39,33 +39,37 @@ * ABI implementation based on Windows ABI AMD64 supplement v.0.99.6 */ public final class Windowsx64Linker extends AbstractLinker { - private static Windowsx64Linker instance; public static Windowsx64Linker getInstance() { - if (instance == null) { - instance = new Windowsx64Linker(); + final class Holder { + private static final Windowsx64Linker INSTANCE = new Windowsx64Linker(); } - return instance; + + return Holder.INSTANCE; + } + + private Windowsx64Linker() { + // Ensure there is only one instance } @Override - protected MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function) { - return CallArranger.arrangeDowncall(inferredMethodType, function); + protected MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function, LinkerOptions options) { + return CallArranger.arrangeDowncall(inferredMethodType, function, options); } @Override - protected MemorySegment arrangeUpcall(MethodHandle target, MethodType targetType, FunctionDescriptor function, MemorySession scope) { + protected MemorySegment arrangeUpcall(MethodHandle target, MethodType targetType, FunctionDescriptor function, SegmentScope scope) { return CallArranger.arrangeUpcall(target, targetType, function, scope); } - public static VaList newVaList(Consumer actions, MemorySession scope) { + public static VaList newVaList(Consumer actions, SegmentScope scope) { WinVaList.Builder builder = WinVaList.builder(scope); actions.accept(builder); return builder.build(); } - public static VaList newVaListOfAddress(MemoryAddress ma, MemorySession session) { - return WinVaList.ofAddress(ma, session); + public static VaList newVaListOfAddress(long address, SegmentScope session) { + return WinVaList.ofAddress(address, session); } public static VaList emptyVaList() { diff --git a/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractGroupLayout.java b/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractGroupLayout.java new file mode 100644 index 0000000000000..7bb78eabd8438 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractGroupLayout.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package jdk.internal.foreign.layout; + +import java.lang.foreign.MemoryLayout; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.LongBinaryOperator; +import java.util.stream.Collectors; + +/** + * A compound layout that aggregates 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...)}). + * + * @implSpec + * This class is immutable, thread-safe and value-based. + * + * @since 19 + */ +public sealed abstract class AbstractGroupLayout & MemoryLayout> + extends AbstractLayout + permits StructLayoutImpl, UnionLayoutImpl { + + private final Kind kind; + private final List elements; + + AbstractGroupLayout(Kind kind, List elements) { + this(kind, elements, kind.alignof(elements), Optional.empty()); + } + + AbstractGroupLayout(Kind kind, List elements, long bitAlignment, Optional name) { + super(kind.sizeof(elements), bitAlignment, name); // Subclassing creates toctou problems here + this.kind = kind; + this.elements = List.copyOf(elements); + } + + /** + * Returns the member layouts associated with this group. + * + * @apiNote the order in which member layouts are returned is the same order in which member layouts have + * been passed to one of the group layout factory methods (see {@link MemoryLayout#structLayout(MemoryLayout...)}, + * {@link MemoryLayout#unionLayout(MemoryLayout...)}). + * + * @return the member layouts associated with this group. + */ + public final List memberLayouts() { + return elements; // "elements" are already unmodifiable. + } + + /** + * {@inheritDoc} + */ + @Override + public final String toString() { + return decorateLayoutString(elements.stream() + .map(Object::toString) + .collect(Collectors.joining(kind.delimTag, "[", "]"))); + } + + /** + * {@inheritDoc} + */ + @Override + public final boolean equals(Object other) { + if (this == other) { + return true; + } + if (!super.equals(other)) { + return false; + } + return other instanceof AbstractGroupLayout otherGroup && + kind == otherGroup.kind && + elements.equals(otherGroup.elements); + } + + /** + * {@inheritDoc} + */ + @Override + public final int hashCode() { + return Objects.hash(super.hashCode(), kind, elements); + } + + @Override + public final boolean hasNaturalAlignment() { + return bitAlignment() == kind.alignof(elements); + } + + /** + * The group kind. + */ + enum Kind { + /** + * A 'struct' kind. + */ + STRUCT("", Math::addExact), + /** + * A 'union' kind. + */ + UNION("|", Math::max); + + final String delimTag; + final LongBinaryOperator sizeOp; + + Kind(String delimTag, LongBinaryOperator sizeOp) { + this.delimTag = delimTag; + this.sizeOp = sizeOp; + } + + long sizeof(List elems) { + long size = 0; + for (MemoryLayout elem : elems) { + size = sizeOp.applyAsLong(size, elem.bitSize()); + } + return size; + } + + long alignof(List elems) { + return elems.stream() + .mapToLong(MemoryLayout::bitAlignment) + .max() // max alignment in case we have member layouts + .orElse(1); // or minimal alignment if no member layout is given + } + } +} diff --git a/src/java.base/share/classes/java/lang/foreign/AbstractLayout.java b/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractLayout.java similarity index 67% rename from src/java.base/share/classes/java/lang/foreign/AbstractLayout.java rename to src/java.base/share/classes/jdk/internal/foreign/layout/AbstractLayout.java index 5b9bc1e8da6ae..48c64eb7fc447 100644 --- a/src/java.base/share/classes/java/lang/foreign/AbstractLayout.java +++ b/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractLayout.java @@ -23,101 +23,69 @@ * questions. * */ -package java.lang.foreign; +package jdk.internal.foreign.layout; -import java.util.Objects; -import java.util.Optional; import jdk.internal.foreign.Utils; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.Stable; -abstract non-sealed class AbstractLayout implements MemoryLayout { +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.SequenceLayout; +import java.lang.foreign.StructLayout; +import java.lang.foreign.UnionLayout; +import java.lang.foreign.ValueLayout; +import java.util.Objects; +import java.util.Optional; - private final long size; - final long alignment; +public abstract sealed class AbstractLayout & MemoryLayout> + permits AbstractGroupLayout, PaddingLayoutImpl, SequenceLayoutImpl, ValueLayouts.AbstractValueLayout { + + private final long bitSize; + private final long bitAlignment; private final Optional name; @Stable - long cachedSize; + private long byteSize; - public AbstractLayout(long size, long alignment, Optional name) { - this.size = size; - this.alignment = alignment; + AbstractLayout(long bitSize, long bitAlignment, Optional name) { + this.bitSize = bitSize; + this.bitAlignment = bitAlignment; this.name = name; } - @Override - public AbstractLayout withName(String name) { + public final L withName(String name) { Objects.requireNonNull(name); - return dup(alignment, Optional.of(name)); + return dup(bitAlignment, Optional.of(name)); } - @Override public final Optional name() { return name; } - abstract AbstractLayout dup(long alignment, Optional name); - - @Override - public AbstractLayout withBitAlignment(long alignmentBits) { - checkAlignment(alignmentBits); - return dup(alignmentBits, name); + public final L withBitAlignment(long bitAlignment) { + checkAlignment(bitAlignment); + return dup(bitAlignment, name); } - void checkAlignment(long alignmentBitCount) { - if (((alignmentBitCount & (alignmentBitCount - 1)) != 0L) || //alignment must be a power of two - (alignmentBitCount < 8)) { //alignment must be greater than 8 - throw new IllegalArgumentException("Invalid alignment: " + alignmentBitCount); - } - } - - static void checkSize(long size) { - checkSize(size, false); - } - - static void checkSize(long size, boolean includeZero) { - if (size < 0 || (!includeZero && size == 0)) { - throw new IllegalArgumentException("Invalid size for layout: " + size); - } - } - - @Override public final long bitAlignment() { - return alignment; + return bitAlignment; } - @Override @ForceInline - public long byteSize() { - if (cachedSize == 0) { - cachedSize = Utils.bitsToBytesOrThrow(bitSize(), + public final long byteSize() { + if (byteSize == 0) { + byteSize = Utils.bitsToBytesOrThrow(bitSize(), () -> new UnsupportedOperationException("Cannot compute byte size; bit size is not a multiple of 8")); } - return cachedSize; + return byteSize; } - @Override - public long bitSize() { - return size; + public final long bitSize() { + return bitSize; } - String decorateLayoutString(String s) { - if (name().isPresent()) { - s = String.format("%s(%s)", s, name().get()); - } - if (!hasNaturalAlignment()) { - s = alignment + "%" + s; - } - return s; - } - - boolean hasNaturalAlignment() { - return size == alignment; - } - - @Override - public boolean isPadding() { - return this instanceof PaddingLayout; + public boolean hasNaturalAlignment() { + return bitSize == bitAlignment; } // the following methods have to copy the same Javadoc as in MemoryLayout, or subclasses will just show @@ -128,7 +96,7 @@ public boolean isPadding() { */ @Override public int hashCode() { - return Objects.hash(name, size, alignment); + return Objects.hash(name, bitSize, bitAlignment); } /** @@ -141,8 +109,8 @@ public int hashCode() { * and {@linkplain ValueLayout#carrier() carrier} *
  • two sequence layouts are considered equal if they have the same element count (see {@link SequenceLayout#elementCount()}), and * if their element layouts (see {@link SequenceLayout#elementLayout()}) are also equal
  • - *
  • two group layouts are considered equal if they are of the same kind (see {@link GroupLayout#isStruct()}, - * {@link GroupLayout#isUnion()}) and if their member layouts (see {@link GroupLayout#memberLayouts()}) are also equal
  • + *
  • two group layouts are considered equal if they are of the same type (see {@link StructLayout}, + * {@link UnionLayout}) and if their member layouts (see {@link GroupLayout#memberLayouts()}) are also equal
  • * * * @param other the object to be compared for equality with this layout. @@ -154,14 +122,35 @@ public boolean equals(Object other) { return true; } - return other instanceof AbstractLayout otherLayout && + return other instanceof AbstractLayout otherLayout && name.equals(otherLayout.name) && - size == otherLayout.size && - alignment == otherLayout.alignment; + bitSize == otherLayout.bitSize && + bitAlignment == otherLayout.bitAlignment; } /** * {@return the string representation of this layout} */ public abstract String toString(); + + abstract L dup(long alignment, Optional name); + + String decorateLayoutString(String s) { + if (name().isPresent()) { + s = String.format("%s(%s)", s, name().get()); + } + if (!hasNaturalAlignment()) { + s = bitAlignment + "%" + s; + } + return s; + } + + private static void checkAlignment(long alignmentBitCount) { + if (((alignmentBitCount & (alignmentBitCount - 1)) != 0L) || //alignment must be a power of two + (alignmentBitCount < 8)) { //alignment must be greater than 8 + throw new IllegalArgumentException("Invalid alignment: " + alignmentBitCount); + } + } + + } diff --git a/src/java.base/share/classes/jdk/internal/foreign/layout/MemoryLayoutUtil.java b/src/java.base/share/classes/jdk/internal/foreign/layout/MemoryLayoutUtil.java new file mode 100644 index 0000000000000..fa154bd09babc --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/layout/MemoryLayoutUtil.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package jdk.internal.foreign.layout; + +public final class MemoryLayoutUtil { + + private MemoryLayoutUtil() { + } + + public static void checkSize(long size) { + checkSize(size, false); + } + + public static void checkSize(long size, boolean includeZero) { + if (size < 0 || (!includeZero && size == 0)) { + throw new IllegalArgumentException("Invalid size for layout: " + size); + } + } + +} \ No newline at end of file diff --git a/src/java.base/share/classes/jdk/internal/foreign/layout/PaddingLayoutImpl.java b/src/java.base/share/classes/jdk/internal/foreign/layout/PaddingLayoutImpl.java new file mode 100644 index 0000000000000..3559a8d0f297d --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/layout/PaddingLayoutImpl.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package jdk.internal.foreign.layout; + +import java.lang.foreign.PaddingLayout; +import java.util.Objects; +import java.util.Optional; + +public final class PaddingLayoutImpl extends AbstractLayout implements PaddingLayout { + + private PaddingLayoutImpl(long bitSize) { + this(bitSize, 1, Optional.empty()); + } + + private PaddingLayoutImpl(long bitSize, long bitAlignment, Optional name) { + super(bitSize, bitAlignment, name); + } + + @Override + public String toString() { + return decorateLayoutString("x" + bitSize()); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!super.equals(other)) { + return false; + } + if (!(other instanceof PaddingLayoutImpl p)) { + return false; + } + return bitSize() == p.bitSize(); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), bitSize()); + } + + @Override + PaddingLayoutImpl dup(long bitAlignment, Optional name) { + return new PaddingLayoutImpl(bitSize(), bitAlignment, name); + } + + @Override + public boolean hasNaturalAlignment() { + return true; + } + + public static PaddingLayout of(long bitSize) { + return new PaddingLayoutImpl(bitSize); + } + +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/layout/SequenceLayoutImpl.java b/src/java.base/share/classes/jdk/internal/foreign/layout/SequenceLayoutImpl.java new file mode 100644 index 0000000000000..c249f76f9ee2d --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/layout/SequenceLayoutImpl.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package jdk.internal.foreign.layout; + +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.SequenceLayout; +import java.util.Objects; +import java.util.Optional; + +public final class SequenceLayoutImpl extends AbstractLayout implements SequenceLayout { + + private final long elemCount; + private final MemoryLayout elementLayout; + + private SequenceLayoutImpl(long elemCount, MemoryLayout elementLayout) { + this(elemCount, elementLayout, elementLayout.bitAlignment(), Optional.empty()); + } + + private SequenceLayoutImpl(long elemCount, MemoryLayout elementLayout, long bitAlignment, Optional name) { + super(Math.multiplyExact(elemCount, elementLayout.bitSize()), bitAlignment, name); + this.elemCount = elemCount; + this.elementLayout = elementLayout; + } + + /** + * {@return the element layout associated with this sequence layout} + */ + public MemoryLayout elementLayout() { + return elementLayout; + } + + /** + * {@return the element count of this sequence layout} + */ + public long elementCount() { + return elemCount; + } + + /** + * Returns a sequence layout with the same element layout, alignment constraints and name as this sequence layout, + * but with the specified element count. + * + * @param elementCount the new element count. + * @return a sequence layout with the given element count. + * @throws IllegalArgumentException if {@code elementCount < 0}. + */ + public SequenceLayout withElementCount(long elementCount) { + MemoryLayoutUtil.checkSize(elementCount, true); + return new SequenceLayoutImpl(elementCount, elementLayout, bitAlignment(), name()); + } + + /** + * Re-arrange the elements in this sequence layout into a multi-dimensional sequence layout. + * The resulting layout is a sequence layout where element layouts in the flattened projection of this + * sequence layout (see {@link #flatten()}) are re-arranged into one or more nested sequence layouts + * according to the provided element counts. This transformation preserves the layout size; + * that is, multiplying the provided element counts must yield the same element count + * as the flattened projection of this sequence layout. + *

    + * For instance, given a sequence layout of the kind: + * {@snippet lang = java: + * var seq = MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(3, ValueLayout.JAVA_INT)); + *} + * calling {@code seq.reshape(2, 6)} will yield the following sequence layout: + * {@snippet lang = java: + * 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 + * count in that position will be inferred from the remaining element counts and the + * element count of the flattened projection of this layout. For instance, a layout equivalent to + * the above {@code reshapeSeq} can also be computed in the following ways: + * {@snippet lang = java: + * var reshapeSeqImplicit1 = seq.reshape(-1, 6); + * var reshapeSeqImplicit2 = seq.reshape(2, -1); + *} + * + * @param elementCounts an array of element counts, of which at most one can be {@code -1}. + * @return a sequence layout where element layouts in the flattened projection of this + * sequence layout (see {@link #flatten()}) are re-arranged into one or more nested sequence layouts. + * @throws IllegalArgumentException if two or more element counts are set to {@code -1}, or if one + * or more element count is {@code <= 0} (but other than {@code -1}) or, if, after any required inference, + * multiplying the element counts does not yield the same element count as the flattened projection of this + * sequence layout. + */ + public SequenceLayout reshape(long... elementCounts) { + Objects.requireNonNull(elementCounts); + if (elementCounts.length == 0) { + throw new IllegalArgumentException(); + } + SequenceLayout flat = flatten(); + long expectedCount = flat.elementCount(); + + long actualCount = 1; + int inferPosition = -1; + for (int i = 0; i < elementCounts.length; i++) { + if (elementCounts[i] == -1) { + if (inferPosition == -1) { + inferPosition = i; + } else { + throw new IllegalArgumentException("Too many unspecified element counts"); + } + } else if (elementCounts[i] <= 0) { + throw new IllegalArgumentException("Invalid element count: " + elementCounts[i]); + } else { + actualCount = elementCounts[i] * actualCount; + } + } + + // infer an unspecified element count (if any) + if (inferPosition != -1) { + long inferredCount = expectedCount / actualCount; + elementCounts[inferPosition] = inferredCount; + actualCount = actualCount * inferredCount; + } + + if (actualCount != expectedCount) { + throw new IllegalArgumentException("Element counts do not match expected size: " + expectedCount); + } + + MemoryLayout res = flat.elementLayout(); + for (int i = elementCounts.length - 1; i >= 0; i--) { + res = MemoryLayout.sequenceLayout(elementCounts[i], res); + } + return (SequenceLayoutImpl) res; + } + + /** + * Returns a flattened sequence layout. The element layout of the returned sequence layout + * is the first non-sequence element layout found by recursively traversing the element layouts of this sequence layout. + * This transformation preserves the layout size; nested sequence layout in this sequence layout will + * 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: + * {@snippet lang = java: + * var seq = MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(3, ValueLayout.JAVA_INT)); + *} + * calling {@code seq.flatten()} will yield the following sequence layout: + * {@snippet lang = java: + * var flattenedSeq = MemoryLayout.sequenceLayout(12, ValueLayout.JAVA_INT); + *} + * + * @return a sequence layout with the same size as this layout (but, possibly, with different + * element count), whose element layout is not a sequence layout. + */ + public SequenceLayout flatten() { + long count = elementCount(); + MemoryLayout elemLayout = elementLayout(); + while (elemLayout instanceof SequenceLayoutImpl elemSeq) { + count = count * elemSeq.elementCount(); + elemLayout = elemSeq.elementLayout(); + } + return MemoryLayout.sequenceLayout(count, elemLayout); + } + + @Override + public String toString() { + return decorateLayoutString(String.format("[%s:%s]", + elemCount, elementLayout)); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!super.equals(other)) { + return false; + } + return other instanceof SequenceLayoutImpl otherSeq && + elemCount == otherSeq.elemCount && + elementLayout.equals(otherSeq.elementLayout); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), elemCount, elementLayout); + } + + @Override + SequenceLayoutImpl dup(long bitAlignment, Optional name) { + return new SequenceLayoutImpl(elementCount(), elementLayout, bitAlignment, name); + } + + @Override + public boolean hasNaturalAlignment() { + return bitAlignment() == elementLayout.bitAlignment(); + } + + public static SequenceLayout of(long elementCount, MemoryLayout elementLayout) { + return new SequenceLayoutImpl(elementCount, elementLayout); + } + +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/layout/StructLayoutImpl.java b/src/java.base/share/classes/jdk/internal/foreign/layout/StructLayoutImpl.java new file mode 100644 index 0000000000000..339afd735bbc3 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/layout/StructLayoutImpl.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package jdk.internal.foreign.layout; + +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.StructLayout; +import java.util.List; +import java.util.Optional; + +public final class StructLayoutImpl extends AbstractGroupLayout implements StructLayout { + + private StructLayoutImpl(List elements) { + super(Kind.STRUCT, elements); + } + + private StructLayoutImpl(List elements, long bitAlignment, Optional name) { + super(Kind.STRUCT, elements, bitAlignment, name); + } + + @Override + StructLayoutImpl dup(long bitAlignment, Optional name) { + return new StructLayoutImpl(memberLayouts(), bitAlignment, name); + } + + public static StructLayout of(List elements) { + return new StructLayoutImpl(elements); + } + +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/layout/UnionLayoutImpl.java b/src/java.base/share/classes/jdk/internal/foreign/layout/UnionLayoutImpl.java new file mode 100644 index 0000000000000..d8adb186c62db --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/layout/UnionLayoutImpl.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package jdk.internal.foreign.layout; + +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.UnionLayout; +import java.util.List; +import java.util.Optional; + +public final class UnionLayoutImpl extends AbstractGroupLayout implements UnionLayout { + + private UnionLayoutImpl(List elements) { + super(Kind.UNION, elements); + } + + private UnionLayoutImpl(List elements, long bitAlignment, Optional name) { + super(Kind.UNION, elements, bitAlignment, name); + } + + @Override + UnionLayoutImpl dup(long bitAlignment, Optional name) { + return new UnionLayoutImpl(memberLayouts(), bitAlignment, name); + } + + public static UnionLayout of(List elements) { + return new UnionLayoutImpl(elements); + } + +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/layout/ValueLayouts.java b/src/java.base/share/classes/jdk/internal/foreign/layout/ValueLayouts.java new file mode 100644 index 0000000000000..079cb123a39a9 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/layout/ValueLayouts.java @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package jdk.internal.foreign.layout; + +import jdk.internal.foreign.Utils; +import jdk.internal.misc.Unsafe; +import jdk.internal.reflect.CallerSensitive; +import jdk.internal.reflect.Reflection; +import jdk.internal.vm.annotation.ForceInline; +import jdk.internal.vm.annotation.Stable; +import sun.invoke.util.Wrapper; + +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; +import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +/** + * 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, an alignment (in bits), + * a {@linkplain ByteOrder byte order}, and a carrier, that is, the Java type that should be used when + * {@linkplain MemorySegment#get(ValueLayout.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}. + * + * @implSpec This class and its subclasses are immutable, thread-safe and value-based. + */ +public final class ValueLayouts { + + private ValueLayouts() { + } + + abstract sealed static class AbstractValueLayout & ValueLayout> extends AbstractLayout { + + static final int ADDRESS_SIZE_BITS = Unsafe.ADDRESS_SIZE * 8; + + private final Class carrier; + private final ByteOrder order; + @Stable + private VarHandle handle; + + AbstractValueLayout(Class carrier, ByteOrder order, long bitSize) { + this(carrier, order, bitSize, bitSize, Optional.empty()); + } + + AbstractValueLayout(Class carrier, ByteOrder order, long bitSize, long bitAlignment, Optional name) { + super(bitSize, bitAlignment, name); + this.carrier = carrier; + this.order = order; + checkCarrierSize(carrier, bitSize); + } + + /** + * {@return the value's byte order} + */ + public final ByteOrder order() { + return order; + } + + /** + * Returns a value layout with the same carrier, alignment constraints and name as this value layout, + * but with the specified byte order. + * + * @param order the desired byte order. + * @return a value layout with the given byte order. + */ + abstract V withOrder(ByteOrder order); + + @Override + public final String toString() { + char descriptor = carrier == MemorySegment.class ? 'A' : carrier.descriptorString().charAt(0); + if (order == ByteOrder.LITTLE_ENDIAN) { + descriptor = Character.toLowerCase(descriptor); + } + return decorateLayoutString(String.format("%s%d", descriptor, bitSize())); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!super.equals(other)) { + return false; + } + return other instanceof AbstractValueLayout otherValue && + carrier.equals(otherValue.carrier) && + order.equals(otherValue.order); + } + + public final VarHandle arrayElementVarHandle(int... shape) { + Objects.requireNonNull(shape); + MemoryLayout layout = self(); + List path = new ArrayList<>(); + for (int i = shape.length; i > 0; i--) { + int size = shape[i - 1]; + if (size < 0) throw new IllegalArgumentException("Invalid shape size: " + size); + layout = MemoryLayout.sequenceLayout(size, layout); + path.add(MemoryLayout.PathElement.sequenceElement()); + } + layout = MemoryLayout.sequenceLayout(layout); + path.add(MemoryLayout.PathElement.sequenceElement()); + return layout.varHandle(path.toArray(new MemoryLayout.PathElement[0])); + } + + /** + * {@return the carrier associated with this value layout} + */ + public final Class carrier() { + return carrier; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), order, carrier); + } + + @Override + abstract V dup(long bitAlignment, Optional name); + + static void checkCarrierSize(Class carrier, long size) { + if (!isValidCarrier(carrier)) { + throw new IllegalArgumentException("Invalid carrier: " + carrier.getName()); + } + if (carrier == MemorySegment.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 == MemorySegment.class; + } + + + @ForceInline + public final VarHandle accessHandle() { + if (handle == null) { + // this store to stable field is safe, because return value of 'makeMemoryAccessVarHandle' has stable identity + handle = Utils.makeSegmentViewVarHandle(self()); + } + return handle; + } + + @SuppressWarnings("unchecked") + final V self() { + return (V) this; + } + } + + public static final class OfBooleanImpl extends AbstractValueLayout implements ValueLayout.OfBoolean { + + private OfBooleanImpl(ByteOrder order) { + super(boolean.class, order, 8); + } + + private OfBooleanImpl(ByteOrder order, long bitAlignment, Optional name) { + super(boolean.class, order, 8, bitAlignment, name); + } + + @Override + OfBooleanImpl dup(long bitAlignment, Optional name) { + return new OfBooleanImpl(order(), bitAlignment, name); + } + + @Override + public OfBooleanImpl withOrder(ByteOrder order) { + Objects.requireNonNull(order); + return new OfBooleanImpl(order, bitAlignment(), name()); + } + + public static OfBoolean of(ByteOrder order) { + return new OfBooleanImpl(order); + } + } + + public static final class OfByteImpl extends AbstractValueLayout implements ValueLayout.OfByte { + + private OfByteImpl(ByteOrder order) { + super(byte.class, order, 8); + } + + private OfByteImpl(ByteOrder order, long bitAlignment, Optional name) { + super(byte.class, order, 8, bitAlignment, name); + } + + @Override + OfByteImpl dup(long bitAlignment, Optional name) { + return new OfByteImpl(order(), bitAlignment, name); + } + + @Override + public OfByteImpl withOrder(ByteOrder order) { + Objects.requireNonNull(order); + return new OfByteImpl(order, bitAlignment(), name()); + } + + public static OfByte of(ByteOrder order) { + return new OfByteImpl(order); + } + } + + public static final class OfCharImpl extends AbstractValueLayout implements ValueLayout.OfChar { + + private OfCharImpl(ByteOrder order) { + super(char.class, order, 16); + } + + private OfCharImpl(ByteOrder order, long bitAlignment, Optional name) { + super(char.class, order, 16, bitAlignment, name); + } + + @Override + OfCharImpl dup(long bitAlignment, Optional name) { + return new OfCharImpl(order(), bitAlignment, name); + } + + @Override + public OfCharImpl withOrder(ByteOrder order) { + Objects.requireNonNull(order); + return new OfCharImpl(order, bitAlignment(), name()); + } + + public static OfChar of(ByteOrder order) { + return new OfCharImpl(order); + } + } + + public static final class OfShortImpl extends AbstractValueLayout implements ValueLayout.OfShort { + + private OfShortImpl(ByteOrder order) { + super(short.class, order, 16); + } + + private OfShortImpl(ByteOrder order, long bitAlignment, Optional name) { + super(short.class, order, 16, bitAlignment, name); + } + + @Override + OfShortImpl dup(long bitAlignment, Optional name) { + return new OfShortImpl(order(), bitAlignment, name); + } + + @Override + public OfShortImpl withOrder(ByteOrder order) { + Objects.requireNonNull(order); + return new OfShortImpl(order, bitAlignment(), name()); + } + + public static OfShort of(ByteOrder order) { + return new OfShortImpl(order); + } + } + + public static final class OfIntImpl extends AbstractValueLayout implements ValueLayout.OfInt { + + private OfIntImpl(ByteOrder order) { + super(int.class, order, 32); + } + + private OfIntImpl(ByteOrder order, long bitAlignment, Optional name) { + super(int.class, order, 32, bitAlignment, name); + } + + @Override + OfIntImpl dup(long bitAlignment, Optional name) { + return new OfIntImpl(order(), bitAlignment, name); + } + + @Override + public OfIntImpl withOrder(ByteOrder order) { + Objects.requireNonNull(order); + return new OfIntImpl(order, bitAlignment(), name()); + } + + public static OfInt of(ByteOrder order) { + return new OfIntImpl(order); + } + } + + public static final class OfFloatImpl extends AbstractValueLayout implements ValueLayout.OfFloat { + + private OfFloatImpl(ByteOrder order) { + super(float.class, order, 32); + } + + private OfFloatImpl(ByteOrder order, long bitAlignment, Optional name) { + super(float.class, order, 32, bitAlignment, name); + } + + @Override + OfFloatImpl dup(long bitAlignment, Optional name) { + return new OfFloatImpl(order(), bitAlignment, name); + } + + @Override + public OfFloatImpl withOrder(ByteOrder order) { + Objects.requireNonNull(order); + return new OfFloatImpl(order, bitAlignment(), name()); + } + + public static OfFloat of(ByteOrder order) { + return new OfFloatImpl(order); + } + } + + public static final class OfLongImpl extends AbstractValueLayout implements ValueLayout.OfLong { + + private OfLongImpl(ByteOrder order) { + super(long.class, order, 64); + } + + private OfLongImpl(ByteOrder order, long bitAlignment, Optional name) { + super(long.class, order, 64, bitAlignment, name); + } + + @Override + OfLongImpl dup(long bitAlignment, Optional name) { + return new OfLongImpl(order(), bitAlignment, name); + } + + @Override + public OfLongImpl withOrder(ByteOrder order) { + Objects.requireNonNull(order); + return new OfLongImpl(order, bitAlignment(), name()); + } + + public static OfLong of(ByteOrder order) { + return new OfLongImpl(order); + } + } + + public static final class OfDoubleImpl extends AbstractValueLayout implements ValueLayout.OfDouble { + + private OfDoubleImpl(ByteOrder order) { + super(double.class, order, 64); + } + + private OfDoubleImpl(ByteOrder order, long bitAlignment, Optional name) { + super(double.class, order, 64, bitAlignment, name); + } + + @Override + OfDoubleImpl dup(long bitAlignment, Optional name) { + return new OfDoubleImpl(order(), bitAlignment, name); + } + + @Override + public OfDoubleImpl withOrder(ByteOrder order) { + Objects.requireNonNull(order); + return new OfDoubleImpl(order, bitAlignment(), name()); + } + + public static OfDouble of(ByteOrder order) { + return new OfDoubleImpl(order); + } + + } + + public static final class OfAddressImpl extends AbstractValueLayout implements ValueLayout.OfAddress { + + private final boolean isUnbounded; + + private OfAddressImpl(ByteOrder order) { + super(MemorySegment.class, order, ADDRESS_SIZE_BITS); + this.isUnbounded = false; // safe + } + + private OfAddressImpl(ByteOrder order, long size, long bitAlignment, boolean isUnbounded, Optional name) { + super(MemorySegment.class, order, size, bitAlignment, name); + this.isUnbounded = isUnbounded; + } + + @Override + OfAddressImpl dup(long alignment, Optional name) { + return new OfAddressImpl(order(), bitSize(), alignment, isUnbounded, name); + } + + @Override + public OfAddressImpl withOrder(ByteOrder order) { + Objects.requireNonNull(order); + return new OfAddressImpl(order, bitSize(), bitAlignment(), isUnbounded, name()); + } + + @Override + public boolean equals(Object other) { + return super.equals(other) && + ((OfAddressImpl) other).isUnbounded == this.isUnbounded; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), isUnbounded); + } + + @Override + @CallerSensitive + public OfAddress asUnbounded() { + Reflection.ensureNativeAccess(Reflection.getCallerClass(), OfAddress.class, "asUnbounded"); + return new OfAddressImpl(order(), bitSize(), bitAlignment(), true, name()); + } + + @Override + public boolean isUnbounded() { + return isUnbounded; + } + + public static OfAddress of(ByteOrder order) { + return new OfAddressImpl(order); + } + } + +} diff --git a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java index c0d9f4e60b350..181f0e749b858 100644 --- a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java +++ b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java @@ -70,7 +70,7 @@ public enum Feature { RECORD_PATTERNS, @JEP(number=425, title="Virtual Threads") VIRTUAL_THREADS, - @JEP(number=424, title="Foreign Function & Memory API") + @JEP(number=434, title="Foreign Function & Memory API", status="Second Preview") FOREIGN, /** * A key for testing. diff --git a/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java b/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java index ddb06be377530..80faf5ecf9a1f 100644 --- a/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java +++ b/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java @@ -789,7 +789,7 @@ public static boolean hasEnableNativeAccessFlag() { private static void addEnableNativeAccess(ModuleLayer layer) { for (String name : NATIVE_ACCESS_MODULES) { if (name.equals("ALL-UNNAMED")) { - JLA.addEnableNativeAccessAllUnnamed(); + JLA.addEnableNativeAccessToAllUnnamed(); } else { Optional module = layer.findModule(name); if (module.isPresent()) { 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 7171b4fb53d2e..81eaf1545ffdd 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/Reflection.java +++ b/src/java.base/share/classes/jdk/internal/reflect/Reflection.java @@ -115,33 +115,7 @@ public static void ensureNativeAccess(Class currentClass, Class owner, Str Module module = currentClass != null ? currentClass.getModule() : ClassLoader.getSystemClassLoader().getUnnamedModule(); - boolean isNativeAccessEnabled = SharedSecrets.getJavaLangAccess().isEnableNativeAccess(module); - if (!isNativeAccessEnabled) { - synchronized(module) { - isNativeAccessEnabled = SharedSecrets.getJavaLangAccess().isEnableNativeAccess(module); - if (isNativeAccessEnabled) { - // some other thread got to it, do nothing - } else if (ModuleBootstrap.hasEnableNativeAccessFlag()) { - throw new IllegalCallerException("Illegal native access from: " + module); - } else { - // warn and set flag, so that only one warning is reported per module - String cls = owner.getName(); - String mtd = cls + "::" + methodName; - String mod = module.isNamed() ? "module " + module.getName() : "the unnamed module"; - String modflag = module.isNamed() ? module.getName() : "ALL-UNNAMED"; - System.err.printf(""" - WARNING: A restricted method in %s has been called - WARNING: %s has been called by %s - WARNING: Use --enable-native-access=%s to avoid a warning for this module - %n""", cls, mtd, mod, modflag); - if (module.isNamed()) { - SharedSecrets.getJavaLangAccess().addEnableNativeAccess(module); - } else { - SharedSecrets.getJavaLangAccess().addEnableNativeAccessAllUnnamed(); - } - } - } - } + SharedSecrets.getJavaLangAccess().ensureNativeAccess(module, owner, methodName); } /** diff --git a/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java b/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java index b57b56bb051e5..d203910bf8ca8 100644 --- a/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java @@ -29,7 +29,7 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.ref.Cleaner.Cleanable; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; @@ -1208,12 +1208,12 @@ public MappedByteBuffer map(MapMode mode, long position, long size) throws IOExc @Override public MemorySegment map(MapMode mode, long offset, long size, - MemorySession session) + SegmentScope session) throws IOException { Objects.requireNonNull(mode,"Mode is null"); Objects.requireNonNull(session, "Session is null"); - MemorySessionImpl sessionImpl = MemorySessionImpl.toSessionImpl(session); + MemorySessionImpl sessionImpl = (MemorySessionImpl) session; sessionImpl.checkValidState(); if (offset < 0) throw new IllegalArgumentException("Requested bytes offset must be >= 0."); diff --git a/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java b/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java index aed3c9350de42..25f1813412726 100644 --- a/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java +++ b/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java @@ -50,7 +50,7 @@ public class TestLinkToNativeRBP { final static Linker abi = Linker.nativeLinker(); static final SymbolLookup lookup = SymbolLookup.loaderLookup(); - final static MethodHandle foo = abi.downcallHandle(lookup.lookup("foo").get(), + final static MethodHandle foo = abi.downcallHandle(lookup.find("foo").get(), FunctionDescriptor.of(ValueLayout.JAVA_INT)); static int foo() throws Throwable { diff --git a/test/hotspot/jtreg/runtime/ClassFile/ClassFileVersionTest.java b/test/hotspot/jtreg/runtime/ClassFile/ClassFileVersionTest.java index 0192118235302..0c41fe75791f1 100644 --- a/test/hotspot/jtreg/runtime/ClassFile/ClassFileVersionTest.java +++ b/test/hotspot/jtreg/runtime/ClassFile/ClassFileVersionTest.java @@ -42,7 +42,7 @@ public class ClassFileVersionTest { * compilation. If a particular class becomes non-preview, any * currently preview class can be substituted in. */ - private static final Class PREVIEW_API = java.lang.foreign.MemoryAddress.class; + private static final Class PREVIEW_API = java.lang.foreign.MemorySegment.class; static Method m; public static void testIt(String className, int expectedResult) throws Exception { diff --git a/test/jdk/com/sun/jdi/JdbLastErrorTest.java b/test/jdk/com/sun/jdi/JdbLastErrorTest.java index c5eceb855e191..9cde54207a37f 100644 --- a/test/jdk/com/sun/jdi/JdbLastErrorTest.java +++ b/test/jdk/com/sun/jdi/JdbLastErrorTest.java @@ -54,10 +54,10 @@ private static void testWindows() throws Throwable { System.loadLibrary("Kernel32"); SymbolLookup lookup = SymbolLookup.loaderLookup(); MethodHandle getLastError = linker.downcallHandle( - lookup.lookup("GetLastError").orElseThrow(), + lookup.find("GetLastError").orElseThrow(), FunctionDescriptor.of(ValueLayout.JAVA_INT)); MethodHandle setLastError = linker.downcallHandle( - lookup.lookup("SetLastError").orElseThrow(), + lookup.find("SetLastError").orElseThrow(), FunctionDescriptor.ofVoid(ValueLayout.JAVA_INT)); for (int i = 0; i < 10; i++) { diff --git a/test/jdk/java/foreign/CallGeneratorHelper.java b/test/jdk/java/foreign/CallGeneratorHelper.java index f075616f5827d..e1ef460603bc7 100644 --- a/test/jdk/java/foreign/CallGeneratorHelper.java +++ b/test/jdk/java/foreign/CallGeneratorHelper.java @@ -22,17 +22,9 @@ * */ -import java.lang.foreign.Addressable; -import java.lang.foreign.Linker; -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemoryAddress; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; -import java.lang.foreign.SegmentAllocator; -import java.lang.foreign.ValueLayout; +import java.lang.foreign.*; +import java.lang.foreign.SegmentScope; import java.lang.invoke.MethodHandle; import java.lang.invoke.VarHandle; import java.util.ArrayList; @@ -387,21 +379,21 @@ static void generateUpcallFunction(String fName, Ret ret, List params @SuppressWarnings("unchecked") static Object makeArg(MemoryLayout layout, List> checks, boolean check) throws ReflectiveOperationException { if (layout instanceof GroupLayout) { - MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openImplicit()); + MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto()); initStruct(segment, (GroupLayout)layout, checks, check); return segment; } else if (isPointer(layout)) { - MemorySegment segment = MemorySegment.allocateNative(1, MemorySession.openImplicit()); + MemorySegment segment = MemorySegment.allocateNative(1L, SegmentScope.auto()); if (check) { checks.add(o -> { try { - assertEquals(o, segment.address()); + assertEquals(o, segment); } catch (Throwable ex) { throw new IllegalStateException(ex); } }); } - return segment.address(); + return segment; } else if (layout instanceof ValueLayout) { if (isIntegral(layout)) { if (check) { @@ -426,7 +418,7 @@ 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; + if (l instanceof PaddingLayout) continue; VarHandle accessor = g.varHandle(MemoryLayout.PathElement.groupElement(l.name().get())); List> fieldsCheck = new ArrayList<>(); Object value = makeArg(l, fieldsCheck, check); @@ -447,19 +439,17 @@ static void initStruct(MemorySegment str, GroupLayout g, List> } } - static Class carrier(MemoryLayout layout, boolean param) { + static Class carrier(MemoryLayout layout) { if (layout instanceof GroupLayout) { return MemorySegment.class; - } if (isPointer(layout)) { - return param ? Addressable.class : MemoryAddress.class; - } else if (layout instanceof ValueLayout valueLayout) { + } if (layout instanceof ValueLayout valueLayout) { return valueLayout.carrier(); } else { throw new IllegalStateException("Unexpected layout: " + layout); } } - MethodHandle downcallHandle(Linker abi, Addressable symbol, SegmentAllocator allocator, FunctionDescriptor descriptor) { + MethodHandle downcallHandle(Linker abi, MemorySegment symbol, SegmentAllocator allocator, FunctionDescriptor descriptor) { MethodHandle mh = abi.downcallHandle(symbol, descriptor); if (descriptor.returnLayout().isPresent() && descriptor.returnLayout().get() instanceof GroupLayout) { mh = mh.bindTo(allocator); diff --git a/test/jdk/java/foreign/LibraryLookupTest.java b/test/jdk/java/foreign/LibraryLookupTest.java index 9a7f321807ef9..b5108b5a63efb 100644 --- a/test/jdk/java/foreign/LibraryLookupTest.java +++ b/test/jdk/java/foreign/LibraryLookupTest.java @@ -23,11 +23,11 @@ import org.testng.annotations.Test; -import java.lang.foreign.Addressable; +import java.lang.foreign.Arena; +import java.lang.foreign.SegmentScope; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.lang.foreign.SymbolLookup; import java.lang.invoke.MethodHandle; import java.nio.file.Path; @@ -51,12 +51,12 @@ public class LibraryLookupTest { @Test void testLoadLibraryConfined() { - try (MemorySession session0 = MemorySession.openConfined()) { - callFunc(loadLibrary(session0)); - try (MemorySession session1 = MemorySession.openConfined()) { - callFunc(loadLibrary(session1)); - try (MemorySession session2 = MemorySession.openConfined()) { - callFunc(loadLibrary(session2)); + try (Arena arena0 = Arena.openConfined()) { + callFunc(loadLibrary(arena0.scope())); + try (Arena arena1 = Arena.openConfined()) { + callFunc(loadLibrary(arena1.scope())); + try (Arena arena2 = Arena.openConfined()) { + callFunc(loadLibrary(arena2.scope())); } } } @@ -64,21 +64,21 @@ void testLoadLibraryConfined() { @Test(expectedExceptions = IllegalStateException.class) void testLoadLibraryConfinedClosed() { - Addressable addr; - try (MemorySession session = MemorySession.openConfined()) { - addr = loadLibrary(session); + MemorySegment addr; + try (Arena arena = Arena.openConfined()) { + addr = loadLibrary(arena.scope()); } callFunc(addr); } - private static Addressable loadLibrary(MemorySession session) { + private static MemorySegment loadLibrary(SegmentScope session) { SymbolLookup lib = SymbolLookup.libraryLookup(LIB_PATH, session); - MemorySegment addr = lib.lookup("inc").get(); - assertEquals(addr.session(), session); + MemorySegment addr = lib.find("inc").get(); + assertEquals(addr.scope(), session); return addr; } - private static void callFunc(Addressable addr) { + private static void callFunc(MemorySegment addr) { try { INC.invokeExact(addr); } catch (IllegalStateException ex) { @@ -94,12 +94,12 @@ private static void callFunc(Addressable addr) { @Test(expectedExceptions = IllegalArgumentException.class) void testBadLibraryLookupName() { - SymbolLookup.libraryLookup("nonExistent", MemorySession.global()); + SymbolLookup.libraryLookup("nonExistent", SegmentScope.global()); } @Test(expectedExceptions = IllegalArgumentException.class) void testBadLibraryLookupPath() { - SymbolLookup.libraryLookup(Path.of("nonExistent"), MemorySession.global()); + SymbolLookup.libraryLookup(Path.of("nonExistent"), SegmentScope.global()); } @Test @@ -116,8 +116,8 @@ static class LibraryLoadAndAccess implements Runnable { @Override public void run() { for (int i = 0 ; i < ITERATIONS ; i++) { - try (MemorySession session = MemorySession.openConfined()) { - callFunc(loadLibrary(session)); + try (Arena arena = Arena.openConfined()) { + callFunc(loadLibrary(arena.scope())); } } } @@ -125,15 +125,15 @@ public void run() { @Test void testLoadLibrarySharedClosed() throws Throwable { - MemorySession session = MemorySession.openShared(); - Addressable addr = loadLibrary(session); + Arena arena = Arena.openShared(); + MemorySegment addr = loadLibrary(arena.scope()); ExecutorService accessExecutor = Executors.newCachedThreadPool(); for (int i = 0; i < NUM_ACCESSORS ; i++) { accessExecutor.execute(new LibraryAccess(addr)); } while (true) { try { - session.close(); + arena.close(); break; } catch (IllegalStateException ex) { // wait for addressable parameter to be released @@ -146,9 +146,9 @@ void testLoadLibrarySharedClosed() throws Throwable { static class LibraryAccess implements Runnable { - final Addressable addr; + final MemorySegment addr; - LibraryAccess(Addressable addr) { + LibraryAccess(MemorySegment addr) { this.addr = addr; } diff --git a/test/jdk/java/foreign/MemoryLayoutPrincipalTotalityTest.java b/test/jdk/java/foreign/MemoryLayoutPrincipalTotalityTest.java new file mode 100644 index 0000000000000..49d4f7d4f697f --- /dev/null +++ b/test/jdk/java/foreign/MemoryLayoutPrincipalTotalityTest.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @enablePreview + * @run testng/othervm MemoryLayoutPrincipalTotalityTest + */ + +import org.testng.annotations.*; + +import java.lang.foreign.*; + +import static java.lang.foreign.ValueLayout.*; +import static org.testng.Assert.*; + +public class MemoryLayoutPrincipalTotalityTest extends NativeTestHelper { + + // The tests in this class is mostly there to ensure compile-time pattern matching totality. + + @Test + public void testBasicTotality() { + MemoryLayout memoryLayout = javaIntMemoryLayout(); + int v0 = switch (memoryLayout) { + case MemoryLayout ml -> 1; + }; + assertEquals(v0, 1); + } + + @Test + public void testMLRemovedTotality() { + MemoryLayout memoryLayout = javaIntMemoryLayout(); + var v1 = switch (memoryLayout) { + case GroupLayout gl -> 0; + case PaddingLayout pl -> 0; // leaf + case SequenceLayout sl -> 0; // leaf + case ValueLayout vl -> 1; + }; + assertEquals(v1, 1); + } + + @Test + public void testMLGLRemovedTotality() { + MemoryLayout memoryLayout = javaIntMemoryLayout(); + var v2 = switch (memoryLayout) { + case PaddingLayout pl -> 0; // leaf + case SequenceLayout sl -> 0; // leaf + case ValueLayout vl -> 1; + case StructLayout sl -> 0; // leaf + case UnionLayout ul -> 0; // leaf + }; + assertEquals(v2, 1); + } + + @Test + public void testMLGLVLRemovedTotality() { + MemoryLayout memoryLayout = javaIntMemoryLayout(); + var v3 = switch (memoryLayout) { + case PaddingLayout pl -> 0; // leaf + case SequenceLayout sl -> 0; // leaf + case StructLayout sl -> 0; // leaf + case UnionLayout ul -> 0; // leaf + case OfAddress oa -> 0; // leaf + case OfBoolean ob -> 0; // leaf + case OfByte ob -> 0; // leaf + case OfChar oc -> 0; // leaf + case OfDouble od -> 0; // leaf + case OfFloat of -> 0; // leaf + case OfInt oi -> 1; // leaf + case OfLong ol -> 0; // leaf + case OfShort os -> 0; // leaf + }; + assertEquals(v3, 1); + } + + @Test + public void testMLVLRemovedTotality() { + MemoryLayout memoryLayout = javaIntMemoryLayout(); + var v4 = switch (memoryLayout) { + case GroupLayout gl -> 0; + case PaddingLayout pl -> 0; // leaf + case SequenceLayout sl -> 0; // leaf + case OfAddress oa -> 0; // leaf + case OfBoolean ob -> 0; // leaf + case OfByte ob -> 0; // leaf + case OfChar oc -> 0; // leaf + case OfDouble od -> 0; // leaf + case OfFloat of -> 0; // leaf + case OfInt oi -> 1; // leaf + case OfLong ol -> 0; // leaf + case OfShort os -> 0; // leaf + }; + assertEquals(v4, 1); + } + + private static MemoryLayout javaIntMemoryLayout() { + return JAVA_INT; + } + +} diff --git a/test/jdk/java/foreign/MemoryLayoutTypeRetentionTest.java b/test/jdk/java/foreign/MemoryLayoutTypeRetentionTest.java new file mode 100644 index 0000000000000..9abccdaf9f8e6 --- /dev/null +++ b/test/jdk/java/foreign/MemoryLayoutTypeRetentionTest.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @enablePreview + * @run testng/othervm MemoryLayoutTypeRetentionTest + */ + +import org.testng.annotations.*; + +import java.lang.foreign.*; +import java.nio.ByteOrder; + +import static java.lang.foreign.ValueLayout.*; +import static org.testng.Assert.*; + +public class MemoryLayoutTypeRetentionTest extends NativeTestHelper { + + // These tests check both compile-time and runtime properties. + // withName() et al. should return the same type as the original object. + + private static final String NAME = "a"; + private static final long BIT_ALIGNMENT = 64; + private static final ByteOrder BYTE_ORDER = ByteOrder.LITTLE_ENDIAN; + + @Test + public void testOfBoolean() { + OfBoolean v = JAVA_BOOLEAN + .withBitAlignment(BIT_ALIGNMENT) + .withName(NAME) + .withOrder(BYTE_ORDER); + check(v); + } + + @Test + public void testOfByte() { + OfByte v = JAVA_BYTE + .withBitAlignment(BIT_ALIGNMENT) + .withName(NAME) + .withOrder(BYTE_ORDER); + check(v); + } + + @Test + public void testOfShort() { + OfShort v = JAVA_SHORT + .withBitAlignment(BIT_ALIGNMENT) + .withName(NAME) + .withOrder(BYTE_ORDER); + check(v); + } + + @Test + public void testOfInt() { + OfInt v = JAVA_INT + .withBitAlignment(BIT_ALIGNMENT) + .withName(NAME) + .withOrder(BYTE_ORDER); + check(v); + } + + @Test + public void testOfChar() { + OfChar v = JAVA_CHAR + .withBitAlignment(BIT_ALIGNMENT) + .withName(NAME) + .withOrder(BYTE_ORDER); + check(v); + } + + @Test + public void testOfLong() { + OfLong v = JAVA_LONG + .withBitAlignment(BIT_ALIGNMENT) + .withName(NAME) + .withOrder(BYTE_ORDER); + check(v); + } + + @Test + public void testOfFloat() { + OfFloat v = JAVA_FLOAT + .withBitAlignment(BIT_ALIGNMENT) + .withName(NAME) + .withOrder(BYTE_ORDER); + check(v); + } + + @Test + public void testOfDouble() { + OfDouble v = JAVA_DOUBLE + .withBitAlignment(BIT_ALIGNMENT) + .withName(NAME) + .withOrder(BYTE_ORDER); + check(v); + } + + @Test + public void testOfAddress() { + OfAddress v = ADDRESS + .withBitAlignment(BIT_ALIGNMENT) + .withName(NAME) + .withOrder(BYTE_ORDER); + check(v); + assertFalse(v.isUnbounded()); + OfAddress v2 = v.asUnbounded(); + assertTrue(v2.isUnbounded()); + } + + @Test + public void testPaddingLayout() { + PaddingLayout v = MemoryLayout.paddingLayout(8) + .withBitAlignment(BIT_ALIGNMENT) + .withName(NAME); + check(v); + } + + @Test + public void testGroupLayout() { + GroupLayout v = MemoryLayout.structLayout(JAVA_INT, JAVA_LONG) + .withBitAlignment(BIT_ALIGNMENT) + .withName(NAME); + check(v); + } + + @Test + public void testStructLayout() { + StructLayout v = MemoryLayout.structLayout(JAVA_INT, JAVA_LONG) + .withBitAlignment(BIT_ALIGNMENT) + .withName(NAME); + check(v); + } + + @Test + public void testUnionLayout() { + UnionLayout v = MemoryLayout.unionLayout(JAVA_INT, JAVA_LONG) + .withBitAlignment(BIT_ALIGNMENT) + .withName(NAME); + check(v); + } + + public void check(ValueLayout v) { + check((MemoryLayout) v); + assertEquals(v.order(), BYTE_ORDER); + } + + public void check(MemoryLayout v) { + assertEquals(v.name().orElseThrow(), NAME); + assertEquals(v.bitAlignment(), BIT_ALIGNMENT); + assertEquals(v.byteSize() * 8, v.bitSize()); + } + +} diff --git a/test/jdk/java/foreign/NativeTestHelper.java b/test/jdk/java/foreign/NativeTestHelper.java index e4fa9cc6be37e..2d48f2e386cb6 100644 --- a/test/jdk/java/foreign/NativeTestHelper.java +++ b/test/jdk/java/foreign/NativeTestHelper.java @@ -22,18 +22,21 @@ * */ -import java.lang.foreign.Addressable; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.Linker; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SegmentScope; import java.lang.foreign.SymbolLookup; import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; public class NativeTestHelper { + public static final boolean IS_WINDOWS = System.getProperty("os.name").startsWith("Windows"); + public static boolean isIntegral(MemoryLayout layout) { return layout instanceof ValueLayout valueLayout && isIntegral(valueLayout.carrier()); } @@ -44,7 +47,7 @@ static boolean isIntegral(Class clazz) { } public static boolean isPointer(MemoryLayout layout) { - return layout instanceof ValueLayout valueLayout && valueLayout.carrier() == MemoryAddress.class; + return layout instanceof ValueLayout valueLayout && valueLayout.carrier() == MemorySegment.class; } // the constants below are useful aliases for C types. The type/carrier association is only valid for 64-bit platforms. @@ -81,17 +84,17 @@ public static boolean isPointer(MemoryLayout layout) { /** * The {@code T*} native type. */ - public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64); + public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64).asUnbounded(); - private static Linker LINKER = Linker.nativeLinker(); + private static final Linker LINKER = Linker.nativeLinker(); private static final MethodHandle FREE = LINKER.downcallHandle( - LINKER.defaultLookup().lookup("free").get(), FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)); + LINKER.defaultLookup().find("free").get(), FunctionDescriptor.ofVoid(C_POINTER)); private static final MethodHandle MALLOC = LINKER.downcallHandle( - LINKER.defaultLookup().lookup("malloc").get(), FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.JAVA_LONG)); + LINKER.defaultLookup().find("malloc").get(), FunctionDescriptor.of(C_POINTER, C_LONG_LONG)); - public static void freeMemory(Addressable address) { + public static void freeMemory(MemorySegment address) { try { FREE.invokeExact(address); } catch (Throwable ex) { @@ -99,15 +102,28 @@ public static void freeMemory(Addressable address) { } } - public static MemoryAddress allocateMemory(long size) { + public static MemorySegment allocateMemory(long size) { try { - return (MemoryAddress)MALLOC.invokeExact(size); + return (MemorySegment) MALLOC.invokeExact(size); } catch (Throwable ex) { throw new IllegalStateException(ex); } } - public static Addressable findNativeOrThrow(String name) { - return SymbolLookup.loaderLookup().lookup(name).orElseThrow(); + public static MemorySegment findNativeOrThrow(String name) { + return SymbolLookup.loaderLookup().find(name).orElseThrow(); + } + + public static MethodHandle downcallHandle(String symbol, FunctionDescriptor desc, Linker.Option... options) { + return LINKER.downcallHandle(findNativeOrThrow(symbol), desc, options); + } + + public static MemorySegment upcallStub(Class holder, String name, FunctionDescriptor descriptor) { + try { + MethodHandle target = MethodHandles.lookup().findStatic(holder, name, descriptor.toMethodType()); + return LINKER.upcallStub(target, descriptor, SegmentScope.auto()); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } } } diff --git a/test/jdk/java/foreign/SafeFunctionAccessTest.java b/test/jdk/java/foreign/SafeFunctionAccessTest.java index f638814c4f9fb..5873afb2308d2 100644 --- a/test/jdk/java/foreign/SafeFunctionAccessTest.java +++ b/test/jdk/java/foreign/SafeFunctionAccessTest.java @@ -28,18 +28,20 @@ * @run testng/othervm --enable-native-access=ALL-UNNAMED SafeFunctionAccessTest */ -import java.lang.foreign.Addressable; +import java.lang.foreign.Arena; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.SegmentScope; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.foreign.VaList; +import java.util.stream.Stream; + import org.testng.annotations.*; import static org.testng.Assert.*; @@ -56,10 +58,10 @@ public class SafeFunctionAccessTest extends NativeTestHelper { @Test(expectedExceptions = IllegalStateException.class) public void testClosedStruct() throws Throwable { MemorySegment segment; - try (MemorySession session = MemorySession.openConfined()) { - segment = MemorySegment.allocateNative(POINT, session); + try (Arena arena = Arena.openConfined()) { + segment = arena.allocate(POINT); } - assertFalse(segment.session().isAlive()); + assertFalse(segment.scope().isAlive()); MethodHandle handle = Linker.nativeLinker().downcallHandle( findNativeOrThrow("struct_func"), FunctionDescriptor.ofVoid(POINT)); @@ -72,33 +74,39 @@ public void testClosedStructAddr_6() throws Throwable { MethodHandle handle = Linker.nativeLinker().downcallHandle( findNativeOrThrow("addr_func_6"), FunctionDescriptor.ofVoid(C_POINTER, C_POINTER, C_POINTER, C_POINTER, C_POINTER, C_POINTER)); + record Allocation(Arena drop, MemorySegment segment) { + static Allocation of(MemoryLayout layout) { + Arena arena = Arena.openShared(); + return new Allocation(arena, arena.allocate(layout)); + } + } for (int i = 0 ; i < 6 ; i++) { - MemorySegment[] segments = new MemorySegment[]{ - MemorySegment.allocateNative(POINT, MemorySession.openShared()), - MemorySegment.allocateNative(POINT, MemorySession.openShared()), - MemorySegment.allocateNative(POINT, MemorySession.openShared()), - MemorySegment.allocateNative(POINT, MemorySession.openShared()), - MemorySegment.allocateNative(POINT, MemorySession.openShared()), - MemorySegment.allocateNative(POINT, MemorySession.openShared()) + Allocation[] allocations = new Allocation[]{ + Allocation.of(POINT), + Allocation.of(POINT), + Allocation.of(POINT), + Allocation.of(POINT), + Allocation.of(POINT), + Allocation.of(POINT) }; // check liveness - segments[i].session().close(); + allocations[i].drop().close(); for (int j = 0 ; j < 6 ; j++) { if (i == j) { - assertFalse(segments[j].session().isAlive()); + assertFalse(allocations[j].drop().scope().isAlive()); } else { - assertTrue(segments[j].session().isAlive()); + assertTrue(allocations[j].drop().scope().isAlive()); } } try { - handle.invokeWithArguments(segments); + handle.invokeWithArguments(Stream.of(allocations).map(Allocation::segment).toArray()); fail(); } catch (IllegalStateException ex) { assertTrue(ex.getMessage().contains("Already closed")); } for (int j = 0 ; j < 6 ; j++) { if (i != j) { - segments[j].session().close(); // should succeed! + allocations[j].drop().close(); // should succeed! } } } @@ -107,30 +115,30 @@ public void testClosedStructAddr_6() throws Throwable { @Test(expectedExceptions = IllegalStateException.class) public void testClosedVaList() throws Throwable { VaList list; - try (MemorySession session = MemorySession.openConfined()) { - list = VaList.make(b -> b.addVarg(C_INT, 42), session); + try (Arena arena = Arena.openConfined()) { + list = VaList.make(b -> b.addVarg(C_INT, 42), arena.scope()); } - assertFalse(list.session().isAlive()); + assertFalse(list.segment().scope().isAlive()); MethodHandle handle = Linker.nativeLinker().downcallHandle( findNativeOrThrow("addr_func"), FunctionDescriptor.ofVoid(C_POINTER)); - handle.invokeExact((Addressable)list); + handle.invokeExact(list.segment()); } @Test(expectedExceptions = IllegalStateException.class) public void testClosedUpcall() throws Throwable { MemorySegment upcall; - try (MemorySession session = MemorySession.openConfined()) { + try (Arena arena = Arena.openConfined()) { MethodHandle dummy = MethodHandles.lookup().findStatic(SafeFunctionAccessTest.class, "dummy", MethodType.methodType(void.class)); - upcall = Linker.nativeLinker().upcallStub(dummy, FunctionDescriptor.ofVoid(), session); + upcall = Linker.nativeLinker().upcallStub(dummy, FunctionDescriptor.ofVoid(), arena.scope()); } - assertFalse(upcall.session().isAlive()); + assertFalse(upcall.scope().isAlive()); MethodHandle handle = Linker.nativeLinker().downcallHandle( findNativeOrThrow("addr_func"), FunctionDescriptor.ofVoid(C_POINTER)); - handle.invokeExact((Addressable)upcall); + handle.invokeExact(upcall); } static void dummy() { } @@ -141,9 +149,9 @@ public void testClosedVaListCallback() throws Throwable { findNativeOrThrow("addr_func_cb"), FunctionDescriptor.ofVoid(C_POINTER, C_POINTER)); - try (MemorySession session = MemorySession.openConfined()) { - VaList list = VaList.make(b -> b.addVarg(C_INT, 42), session); - handle.invoke(list, sessionChecker(session)); + try (Arena arena = Arena.openConfined()) { + VaList list = VaList.make(b -> b.addVarg(C_INT, 42), arena.scope()); + handle.invokeExact(list.segment(), sessionChecker(arena)); } } @@ -153,9 +161,9 @@ public void testClosedStructCallback() throws Throwable { findNativeOrThrow("addr_func_cb"), FunctionDescriptor.ofVoid(C_POINTER, C_POINTER)); - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(POINT, session); - handle.invoke(segment, sessionChecker(session)); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = arena.allocate(POINT); + handle.invokeExact(segment, sessionChecker(arena)); } } @@ -165,27 +173,27 @@ public void testClosedUpcallCallback() throws Throwable { findNativeOrThrow("addr_func_cb"), FunctionDescriptor.ofVoid(C_POINTER, C_POINTER)); - try (MemorySession session = MemorySession.openConfined()) { + try (Arena arena = Arena.openConfined()) { MethodHandle dummy = MethodHandles.lookup().findStatic(SafeFunctionAccessTest.class, "dummy", MethodType.methodType(void.class)); - MemorySegment upcall = Linker.nativeLinker().upcallStub(dummy, FunctionDescriptor.ofVoid(), session); - handle.invoke(upcall, sessionChecker(session)); + MemorySegment upcall = Linker.nativeLinker().upcallStub(dummy, FunctionDescriptor.ofVoid(), arena.scope()); + handle.invokeExact(upcall, sessionChecker(arena)); } } - MemorySegment sessionChecker(MemorySession session) { + MemorySegment sessionChecker(Arena arena) { try { MethodHandle handle = MethodHandles.lookup().findStatic(SafeFunctionAccessTest.class, "checkSession", - MethodType.methodType(void.class, MemorySession.class)); - handle = handle.bindTo(session); - return Linker.nativeLinker().upcallStub(handle, FunctionDescriptor.ofVoid(), MemorySession.openImplicit()); + MethodType.methodType(void.class, Arena.class)); + handle = handle.bindTo(arena); + return Linker.nativeLinker().upcallStub(handle, FunctionDescriptor.ofVoid(), SegmentScope.auto()); } catch (Throwable ex) { throw new AssertionError(ex); } } - static void checkSession(MemorySession session) { + static void checkSession(Arena arena) { try { - session.close(); + arena.close(); fail("Session closed unexpectedly!"); } catch (IllegalStateException ex) { assertTrue(ex.getMessage().contains("acquired")); //if acquired, fine diff --git a/test/jdk/java/foreign/StdLibTest.java b/test/jdk/java/foreign/StdLibTest.java index 98bf9f50703cd..05c4faac992ab 100644 --- a/test/jdk/java/foreign/StdLibTest.java +++ b/test/jdk/java/foreign/StdLibTest.java @@ -152,37 +152,35 @@ void test_vprintf(List args) throws Throwable { static class StdLibHelper { - final static MethodHandle strcat = abi.downcallHandle(abi.defaultLookup().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 strcat = abi.downcallHandle(abi.defaultLookup().find("strcat").get(), + FunctionDescriptor.of(C_POINTER, C_POINTER, C_POINTER)); - - final static MethodHandle strcmp = abi.downcallHandle(abi.defaultLookup().lookup("strcmp").get(), + final static MethodHandle strcmp = abi.downcallHandle(abi.defaultLookup().find("strcmp").get(), FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER)); - final static MethodHandle puts = abi.downcallHandle(abi.defaultLookup().lookup("puts").get(), + final static MethodHandle puts = abi.downcallHandle(abi.defaultLookup().find("puts").get(), FunctionDescriptor.of(C_INT, C_POINTER)); - final static MethodHandle strlen = abi.downcallHandle(abi.defaultLookup().lookup("strlen").get(), + final static MethodHandle strlen = abi.downcallHandle(abi.defaultLookup().find("strlen").get(), FunctionDescriptor.of(C_INT, C_POINTER)); - final static MethodHandle gmtime = abi.downcallHandle(abi.defaultLookup().lookup("gmtime").get(), + final static MethodHandle gmtime = abi.downcallHandle(abi.defaultLookup().find("gmtime").get(), FunctionDescriptor.of(C_POINTER, C_POINTER)); - final static MethodHandle qsort = abi.downcallHandle(abi.defaultLookup().lookup("qsort").get(), + final static MethodHandle qsort = abi.downcallHandle(abi.defaultLookup().find("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(abi.defaultLookup().lookup("rand").get(), + final static MethodHandle rand = abi.downcallHandle(abi.defaultLookup().find("rand").get(), FunctionDescriptor.of(C_INT)); - final static MethodHandle vprintf = abi.downcallHandle(abi.defaultLookup().lookup("vprintf").get(), + final static MethodHandle vprintf = abi.downcallHandle(abi.defaultLookup().find("vprintf").get(), FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER)); - final static Addressable printfAddr = abi.defaultLookup().lookup("printf").get(); + final static MemorySegment printfAddr = abi.defaultLookup().find("printf").get(); final static FunctionDescriptor printfBase = FunctionDescriptor.of(C_INT, C_POINTER); @@ -190,48 +188,48 @@ static class StdLibHelper { try { //qsort upcall handle qsortCompar = MethodHandles.lookup().findStatic(StdLibTest.StdLibHelper.class, "qsortCompare", - Linker.upcallType(qsortComparFunction)); + qsortComparFunction.toMethodType()); } catch (ReflectiveOperationException ex) { throw new IllegalStateException(ex); } } String strcat(String s1, String s2) throws Throwable { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment buf = session.allocate(s1.length() + s2.length() + 1); + try (var arena = Arena.openConfined()) { + MemorySegment buf = arena.allocate(s1.length() + s2.length() + 1); buf.setUtf8String(0, s1); - MemorySegment other = session.allocateUtf8String(s2); - return ((MemoryAddress)strcat.invokeExact(buf, other)).getUtf8String(0); + MemorySegment other = arena.allocateUtf8String(s2); + return ((MemorySegment)strcat.invokeExact(buf, other)).getUtf8String(0); } } int strcmp(String s1, String s2) throws Throwable { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment ns1 = session.allocateUtf8String(s1); - MemorySegment ns2 = session.allocateUtf8String(s2); - return (int)strcmp.invoke(ns1, ns2); + try (var arena = Arena.openConfined()) { + MemorySegment ns1 = arena.allocateUtf8String(s1); + MemorySegment ns2 = arena.allocateUtf8String(s2); + return (int)strcmp.invokeExact(ns1, ns2); } } int puts(String msg) throws Throwable { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment s = session.allocateUtf8String(msg); - return (int)puts.invoke(s); + try (var arena = Arena.openConfined()) { + MemorySegment s = arena.allocateUtf8String(msg); + return (int)puts.invokeExact(s); } } int strlen(String msg) throws Throwable { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment s = session.allocateUtf8String(msg); - return (int)strlen.invoke(s); + try (var arena = Arena.openConfined()) { + MemorySegment s = arena.allocateUtf8String(msg); + return (int)strlen.invokeExact(s); } } Tm gmtime(long arg) throws Throwable { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment time = session.allocate(8); + try (var arena = Arena.openConfined()) { + MemorySegment time = arena.allocate(8); time.set(C_LONG_LONG, 0, arg); - return new Tm((MemoryAddress)gmtime.invoke(time)); + return new Tm((MemorySegment)gmtime.invokeExact(time)); } } @@ -242,8 +240,8 @@ static class Tm { static final long SIZE = 56; - Tm(MemoryAddress addr) { - this.base = MemorySegment.ofAddress(addr, SIZE, MemorySession.global()); + Tm(MemorySegment addr) { + this.base = addr.asSlice(0, SIZE); } int sec() { @@ -277,20 +275,20 @@ boolean isdst() { int[] qsort(int[] arr) throws Throwable { //init native array - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment nativeArr = session.allocateArray(C_INT, arr); + try (var arena = Arena.openConfined()) { + MemorySegment nativeArr = arena.allocateArray(C_INT, arr); //call qsort - Addressable qsortUpcallStub = abi.upcallStub(qsortCompar, qsortComparFunction, session); + MemorySegment qsortUpcallStub = abi.upcallStub(qsortCompar, qsortComparFunction, arena.scope()); - qsort.invoke(nativeArr, (long)arr.length, C_INT.byteSize(), qsortUpcallStub); + qsort.invokeExact(nativeArr, (long)arr.length, C_INT.byteSize(), qsortUpcallStub); //convert back to Java array return nativeArr.toArray(C_INT); } } - static int qsortCompare(MemoryAddress addr1, MemoryAddress addr2) { + static int qsortCompare(MemorySegment addr1, MemorySegment addr2) { return addr1.get(C_INT, 0) - addr2.get(C_INT, 0); } @@ -300,32 +298,34 @@ int rand() throws Throwable { } int printf(String format, List args) throws Throwable { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment formatStr = session.allocateUtf8String(format); - return (int)specializedPrintf(args).invoke(formatStr, - args.stream().map(a -> a.nativeValue(session)).toArray()); + try (var arena = Arena.openConfined()) { + MemorySegment formatStr = arena.allocateUtf8String(format); + return (int)specializedPrintf(args).invokeExact(formatStr, + args.stream().map(a -> a.nativeValue(arena)).toArray()); } } int vprintf(String format, List args) throws Throwable { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment formatStr = session.allocateUtf8String(format); - VaList vaList = VaList.make(b -> args.forEach(a -> a.accept(b, session)), session); - return (int)vprintf.invoke(formatStr, vaList); + try (var arena = Arena.openConfined()) { + MemorySegment formatStr = arena.allocateUtf8String(format); + VaList vaList = VaList.make(b -> args.forEach(a -> a.accept(b, arena)), arena.scope()); + return (int)vprintf.invokeExact(formatStr, vaList.segment()); } } private MethodHandle specializedPrintf(List args) { //method type - MethodType mt = MethodType.methodType(int.class, MemoryAddress.class); + MethodType mt = MethodType.methodType(int.class, MemorySegment.class); FunctionDescriptor fd = printfBase; List variadicLayouts = new ArrayList<>(args.size()); for (PrintfArg arg : args) { mt = mt.appendParameterTypes(arg.carrier); variadicLayouts.add(arg.layout); } + Linker.Option varargIndex = Linker.Option.firstVariadicArg(fd.argumentLayouts().size()); MethodHandle mh = abi.downcallHandle(printfAddr, - fd.asVariadic(variadicLayouts.toArray(new MemoryLayout[args.size()]))); + fd.appendArgumentLayouts(variadicLayouts.toArray(new MemoryLayout[args.size()])), + varargIndex); return mh.asSpreader(1, Object[].class, args.size()); } } @@ -384,26 +384,24 @@ public static Object[][] printfArgs() { .toArray(Object[][]::new); } - enum PrintfArg implements BiConsumer { + enum PrintfArg implements BiConsumer { - INTEGRAL(int.class, C_INT, "%d", session -> 42, 42, VaList.Builder::addVarg), - STRING(MemoryAddress.class, C_POINTER, "%s", session -> { - var segment = MemorySegment.allocateNative(4, session); - segment.setUtf8String(0, "str"); - return segment.address(); + INTEGRAL(int.class, C_INT, "%d", arena -> 42, 42, VaList.Builder::addVarg), + STRING(MemorySegment.class, C_POINTER, "%s", arena -> { + return arena.allocateUtf8String("str"); }, "str", VaList.Builder::addVarg), - CHAR(byte.class, C_CHAR, "%c", session -> (byte) 'h', 'h', (builder, layout, value) -> builder.addVarg(C_INT, (int)value)), - DOUBLE(double.class, C_DOUBLE, "%.4f", session ->1.2345d, 1.2345d, VaList.Builder::addVarg); + CHAR(byte.class, C_CHAR, "%c", arena -> (byte) 'h', 'h', (builder, layout, value) -> builder.addVarg(C_INT, (int)value)), + DOUBLE(double.class, C_DOUBLE, "%.4f", arena ->1.2345d, 1.2345d, VaList.Builder::addVarg); final Class carrier; final ValueLayout layout; final String format; - final Function nativeValueFactory; + final Function nativeValueFactory; final Object javaValue; @SuppressWarnings("rawtypes") final VaListBuilderCall builderCall; - PrintfArg(Class carrier, L 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; @@ -414,16 +412,16 @@ PrintfArg(Class carrier, L layout, String format, @Override @SuppressWarnings("unchecked") - public void accept(VaList.Builder builder, MemorySession session) { - builderCall.build(builder, layout, nativeValueFactory.apply(session)); + public void accept(VaList.Builder builder, Arena arena) { + builderCall.build(builder, layout, nativeValueFactory.apply(arena)); } interface VaListBuilderCall { void build(VaList.Builder builder, L layout, V value); } - public Object nativeValue(MemorySession session) { - return nativeValueFactory.apply(session); + public Object nativeValue(Arena arena) { + return nativeValueFactory.apply(arena); } } diff --git a/test/jdk/java/foreign/TestAdaptVarHandles.java b/test/jdk/java/foreign/TestAdaptVarHandles.java index c16701991c6e6..de74ed8c6b246 100644 --- a/test/jdk/java/foreign/TestAdaptVarHandles.java +++ b/test/jdk/java/foreign/TestAdaptVarHandles.java @@ -31,8 +31,9 @@ * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true -Xverify:all TestAdaptVarHandles */ +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import org.testng.annotations.*; import static org.testng.Assert.*; @@ -93,7 +94,7 @@ public class TestAdaptVarHandles { @Test public void testFilterValue() throws Throwable { ValueLayout layout = ValueLayout.JAVA_INT; - MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openImplicit()); + MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto()); VarHandle intHandle = layout.varHandle(); VarHandle i2SHandle = MethodHandles.filterValue(intHandle, S2I, I2S); i2SHandle.set(segment, "1"); @@ -112,7 +113,7 @@ public void testFilterValue() throws Throwable { @Test public void testFilterValueComposite() throws Throwable { ValueLayout layout = ValueLayout.JAVA_INT; - MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openImplicit()); + MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto()); VarHandle intHandle = layout.varHandle(); MethodHandle CTX_S2I = MethodHandles.dropArguments(S2I, 0, String.class, String.class); VarHandle i2SHandle = MethodHandles.filterValue(intHandle, CTX_S2I, CTX_I2S); @@ -133,7 +134,7 @@ public void testFilterValueComposite() throws Throwable { @Test public void testFilterValueLoose() throws Throwable { ValueLayout layout = ValueLayout.JAVA_INT; - MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openImplicit()); + MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto()); VarHandle intHandle = layout.varHandle(); VarHandle i2SHandle = MethodHandles.filterValue(intHandle, O2I, I2O); i2SHandle.set(segment, "1"); @@ -190,8 +191,8 @@ public void testBadFilterUnboxException() { public void testBadFilterBoxHandleException() { VarHandle intHandle = ValueLayout.JAVA_INT.varHandle(); VarHandle vh = MethodHandles.filterValue(intHandle, S2I, I2S_EX); - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment seg = MemorySegment.allocateNative(ValueLayout.JAVA_INT, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment seg = MemorySegment.allocateNative(ValueLayout.JAVA_INT, arena.scope()); vh.set(seg, "42"); String x = (String) vh.get(seg); // should throw } @@ -201,8 +202,8 @@ public void testBadFilterBoxHandleException() { public void testBadFilterUnboxHandleException() { VarHandle intHandle = ValueLayout.JAVA_INT.varHandle(); VarHandle vh = MethodHandles.filterValue(intHandle, S2I_EX, I2S); - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment seg = MemorySegment.allocateNative(ValueLayout.JAVA_INT, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment seg = MemorySegment.allocateNative(ValueLayout.JAVA_INT, arena.scope()); vh.set(seg, "42"); // should throw } } @@ -210,7 +211,7 @@ public void testBadFilterUnboxHandleException() { @Test public void testFilterCoordinates() throws Throwable { ValueLayout layout = ValueLayout.JAVA_INT; - MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openImplicit()); + MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto()); VarHandle intHandle_longIndex = MethodHandles.filterCoordinates(intHandleIndexed, 0, BASE_ADDR, S2L); intHandle_longIndex.set(segment, "0", 1); int oldValue = (int)intHandle_longIndex.getAndAdd(segment, "0", 42); @@ -253,7 +254,7 @@ public void testBadFilterCoordinatesTooManyFilters() { @Test public void testInsertCoordinates() throws Throwable { ValueLayout layout = ValueLayout.JAVA_INT; - MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openImplicit()); + MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto()); VarHandle intHandle_longIndex = MethodHandles.insertCoordinates(intHandleIndexed, 0, segment, 0L); intHandle_longIndex.set(1); int oldValue = (int)intHandle_longIndex.getAndAdd(42); @@ -291,7 +292,7 @@ public void testBadInsertCoordinatesTooManyValues() { @Test public void testPermuteCoordinates() throws Throwable { ValueLayout layout = ValueLayout.JAVA_INT; - MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openImplicit()); + MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto()); VarHandle intHandle_swap = MethodHandles.permuteCoordinates(intHandleIndexed, List.of(long.class, MemorySegment.class), 1, 0); intHandle_swap.set(0L, segment, 1); @@ -330,7 +331,7 @@ public void testBadPermuteCoordinatesIndexTooSmall() { @Test public void testCollectCoordinates() throws Throwable { ValueLayout layout = ValueLayout.JAVA_INT; - MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openImplicit()); + MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto()); VarHandle intHandle_sum = MethodHandles.collectCoordinates(intHandleIndexed, 1, SUM_OFFSETS); intHandle_sum.set(segment, -2L, 2L, 1); int oldValue = (int)intHandle_sum.getAndAdd(segment, -2L, 2L, 42); @@ -373,7 +374,7 @@ public void testBadCollectCoordinatesWrongFilterException() { @Test public void testDropCoordinates() throws Throwable { ValueLayout layout = ValueLayout.JAVA_INT; - MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openImplicit()); + MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto()); VarHandle intHandle_dummy = MethodHandles.dropCoordinates(intHandleIndexed, 1, float.class, String.class); intHandle_dummy.set(segment, 1f, "hello", 0L, 1); int oldValue = (int)intHandle_dummy.getAndAdd(segment, 1f, "hello", 0L, 42); diff --git a/test/jdk/java/foreign/TestArrays.java b/test/jdk/java/foreign/TestArrays.java index 978993e1fb12d..e16dfb0d851c3 100644 --- a/test/jdk/java/foreign/TestArrays.java +++ b/test/jdk/java/foreign/TestArrays.java @@ -28,11 +28,11 @@ * @run testng/othervm --enable-native-access=ALL-UNNAMED TestArrays */ -import java.lang.foreign.MemoryAddress; +import java.lang.foreign.Arena; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemoryLayout.PathElement; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.SequenceLayout; import java.lang.invoke.VarHandle; @@ -108,7 +108,7 @@ static void checkBytes(MemorySegment base, SequenceLayout layout, Function init, Consumer checker, MemoryLayout layout) { - MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openImplicit()); + MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto()); init.accept(segment); assertFalse(segment.isReadOnly()); checker.accept(segment); @@ -119,7 +119,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 = MemorySegment.ofAddress(MemoryAddress.NULL, seq.byteSize(), MemorySession.global()); + MemorySegment segment = MemorySegment.ofAddress(0, seq.byteSize(), SegmentScope.global()); arrayFactory.apply(segment); } @@ -127,8 +127,8 @@ public void testTooBigForArray(MemoryLayout layout, Function arrayFactory) { if (layout.byteSize() == 1) throw new IllegalStateException(); //make it fail - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(layout.byteSize() + 1, layout.byteSize(), session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(layout.byteSize() + 1, layout.byteSize(), arena.scope()); arrayFactory.apply(segment); } } @@ -136,8 +136,9 @@ public void testBadSize(MemoryLayout layout, Function arr @Test(dataProvider = "elemLayouts", expectedExceptions = IllegalStateException.class) public void testArrayFromClosedSegment(MemoryLayout layout, Function arrayFactory) { - MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openConfined()); - segment.session().close(); + Arena arena = Arena.openConfined(); + MemorySegment segment = MemorySegment.allocateNative(layout, arena.scope()); + arena.close(); arrayFactory.apply(segment); } diff --git a/test/jdk/java/foreign/TestByteBuffer.java b/test/jdk/java/foreign/TestByteBuffer.java index 998a3197548bf..4f13b9112e627 100644 --- a/test/jdk/java/foreign/TestByteBuffer.java +++ b/test/jdk/java/foreign/TestByteBuffer.java @@ -28,11 +28,11 @@ * @run testng/othervm --enable-native-access=ALL-UNNAMED TestByteBuffer */ +import java.lang.foreign.Arena; import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemorySegment; import java.lang.foreign.MemoryLayout.PathElement; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.SequenceLayout; import java.io.File; @@ -63,7 +63,6 @@ import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -186,8 +185,8 @@ static void checkBytes(MemorySegment base, SequenceLayout lay @Test public void testOffheap() { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(tuples, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(tuples, arena.scope());; initTuples(segment, tuples.elementCount()); ByteBuffer bb = segment.asByteBuffer(); @@ -231,15 +230,15 @@ public void testChannel() throws Throwable { @Test public void testDefaultAccessModesMappedSegment() throws Throwable { - try (MemorySession session = MemorySession.openConfined(); + try (Arena arena = Arena.openConfined(); FileChannel fileChannel = FileChannel.open(tempPath, StandardOpenOption.READ, StandardOpenOption.WRITE)) { - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 8L, session); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 8L, arena.scope()); assertFalse(segment.isReadOnly()); } - try (MemorySession session = MemorySession.openConfined(); + try (Arena arena = Arena.openConfined(); FileChannel fileChannel = FileChannel.open(tempPath, StandardOpenOption.READ)) { - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, 8L, session); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, 8L, arena.scope()); assertTrue(segment.isReadOnly()); } } @@ -250,18 +249,18 @@ public void testMappedSegment() throws Throwable { f.createNewFile(); f.deleteOnExit(); - try (MemorySession session = MemorySession.openConfined(); + try (Arena arena = Arena.openConfined(); FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { //write to channel - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, tuples.byteSize(), session); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, tuples.byteSize(), arena.scope()); initTuples(segment, tuples.elementCount()); segment.force(); } - try (MemorySession session = MemorySession.openConfined(); + try (Arena arena = Arena.openConfined(); FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) { //read from channel - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, tuples.byteSize(), session); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, tuples.byteSize(), arena.scope()); checkTuples(segment, segment.asByteBuffer(), tuples.elementCount()); } } @@ -272,11 +271,11 @@ public void testMappedSegmentOperations(MappedSegmentOp mappedBufferOp) throws T f.createNewFile(); f.deleteOnExit(); - try (MemorySession session = MemorySession.openConfined(); - FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 8L, session); + Arena arena = Arena.openConfined(); + try (FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 8L, arena.scope()); assertTrue(segment.isMapped()); - segment.session().close(); + arena.close(); mappedBufferOp.apply(segment); } } @@ -291,10 +290,10 @@ public void testMappedSegmentOffset() throws Throwable { // write one at a time for (int i = 0 ; i < tuples.byteSize() ; i += tupleLayout.byteSize()) { - try (MemorySession session = MemorySession.openConfined(); + try (Arena arena = Arena.openConfined(); FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { //write to channel - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, i, tuples.byteSize(), session); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, i, tuples.byteSize(), arena.scope()); initTuples(segment, 1); segment.force(); } @@ -302,10 +301,10 @@ public void testMappedSegmentOffset() throws Throwable { // check one at a time for (int i = 0 ; i < tuples.byteSize() ; i += tupleLayout.byteSize()) { - try (MemorySession session = MemorySession.openConfined(); + try (Arena arena = Arena.openConfined(); FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) { //read from channel - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, tuples.byteSize(), session); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, tuples.byteSize(), arena.scope()); checkTuples(segment, segment.asByteBuffer(), 1); } } @@ -323,9 +322,9 @@ public void testLargeMappedSegment() throws Throwable { f.createNewFile(); f.deleteOnExit(); - try (MemorySession session = MemorySession.openConfined(); + try (Arena arena = Arena.openConfined(); FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, LARGE_SIZE, session); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, LARGE_SIZE, arena.scope()); segment.isLoaded(); segment.load(); segment.isLoaded(); @@ -361,8 +360,8 @@ static void checkByteArrayAlignment(MemoryLayout layout) { @Test(dataProvider = "bufferOps") public void testScopedBuffer(Function bufferFactory, @NoInjection Method method, Object[] args) { Buffer bb; - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(bytes, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(bytes, arena.scope());; bb = bufferFactory.apply(segment.asByteBuffer()); } //outside of session!! @@ -387,8 +386,8 @@ public void testScopedBuffer(Function bufferFactory, @NoInje @Test(dataProvider = "bufferHandleOps") public void testScopedBufferAndVarHandle(VarHandle bufferHandle) { ByteBuffer bb; - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(bytes, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(bytes, arena.scope());; bb = segment.asByteBuffer(); for (Map.Entry e : varHandleMembers(bb, bufferHandle).entrySet()) { MethodHandle handle = e.getKey().bindTo(bufferHandle) @@ -421,12 +420,12 @@ public void testScopedBufferAndVarHandle(VarHandle bufferHandle) { @Test(dataProvider = "bufferOps") public void testDirectBuffer(Function bufferFactory, @NoInjection Method method, Object[] args) { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(bytes, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(bytes, arena.scope());; Buffer bb = bufferFactory.apply(segment.asByteBuffer()); assertTrue(bb.isDirect()); DirectBuffer directBuffer = ((DirectBuffer)bb); - assertEquals(directBuffer.address(), segment.address().toRawLongValue()); + assertEquals(directBuffer.address(), segment.address()); assertTrue((directBuffer.attachment() == null) == (bb instanceof ByteBuffer)); assertTrue(directBuffer.cleaner() == null); } @@ -434,8 +433,8 @@ public void testDirectBuffer(Function bufferFactory, @NoInje @Test(dataProvider="resizeOps") public void testResizeOffheap(Consumer checker, Consumer initializer, SequenceLayout seq) { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(seq, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(seq, arena.scope());; initializer.accept(segment); checker.accept(segment); } @@ -472,8 +471,8 @@ public void testResizeRoundtripHeap(Consumer checker, Consumer checker, Consumer initializer, SequenceLayout seq) { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(seq, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(seq, arena.scope());; initializer.accept(segment); MemorySegment second = MemorySegment.ofBuffer(segment.asByteBuffer()); checker.accept(second); @@ -483,8 +482,8 @@ public void testResizeRoundtripNative(Consumer checker, Consumer< @Test(expectedExceptions = IllegalStateException.class) public void testBufferOnClosedSession() { MemorySegment leaked; - try (MemorySession session = MemorySession.openConfined()) { - leaked = MemorySegment.allocateNative(bytes, session); + try (Arena arena = Arena.openConfined()) { + leaked = MemorySegment.allocateNative(bytes, arena.scope());; } ByteBuffer byteBuffer = leaked.asByteBuffer(); // ok byteBuffer.get(); // should throw @@ -492,7 +491,7 @@ public void testBufferOnClosedSession() { @Test(expectedExceptions = IllegalStateException.class) public void testTooBigForByteBuffer() { - MemorySegment segment = MemorySegment.ofAddress(MemoryAddress.NULL, Integer.MAX_VALUE + 10L, MemorySession.openImplicit()); + MemorySegment segment = MemorySegment.ofAddress(0, Integer.MAX_VALUE + 10L, SegmentScope.auto()); segment.asByteBuffer(); } @@ -502,7 +501,7 @@ public void testBadMapNegativeSize() throws IOException { f.createNewFile(); f.deleteOnExit(); try (FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { - fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, -1L, MemorySession.openImplicit()); + fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, -1L, SegmentScope.auto()); } } @@ -512,7 +511,7 @@ public void testBadMapNegativeOffset() throws IOException { f.createNewFile(); f.deleteOnExit(); try (FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { - fileChannel.map(FileChannel.MapMode.READ_WRITE, -1L, 1L, MemorySession.openImplicit()); + fileChannel.map(FileChannel.MapMode.READ_WRITE, -1L, 1L, SegmentScope.auto()); } } @@ -524,9 +523,9 @@ public void testMapOffset() throws IOException { int SIZE = Byte.MAX_VALUE; - try (MemorySession session = MemorySession.openConfined(); + try (Arena arena = Arena.openConfined(); FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, SIZE, session); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, SIZE, arena.scope()); for (byte offset = 0; offset < SIZE; offset++) { segment.set(JAVA_BYTE, offset, offset); } @@ -534,9 +533,9 @@ public void testMapOffset() throws IOException { } for (int offset = 0 ; offset < SIZE ; offset++) { - try (MemorySession session = MemorySession.openConfined(); + try (Arena arena = Arena.openConfined(); FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) { - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, offset, SIZE - offset, session); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, offset, SIZE - offset, arena.scope()); assertEquals(segment.get(JAVA_BYTE, 0), offset); } } @@ -548,9 +547,9 @@ public void testMapZeroSize() throws IOException { f.createNewFile(); f.deleteOnExit(); //RW - try (MemorySession session = MemorySession.openConfined(); + try (Arena arena = Arena.openConfined(); FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 0L, session); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 0L, arena.scope()); assertEquals(segment.byteSize(), 0); assertEquals(segment.isMapped(), true); assertFalse(segment.isReadOnly()); @@ -560,9 +559,9 @@ public void testMapZeroSize() throws IOException { segment.unload(); } //RO - try (MemorySession session = MemorySession.openConfined(); + try (Arena arena = Arena.openConfined(); FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) { - MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, 0L, session); + MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, 0L, arena.scope()); assertEquals(segment.byteSize(), 0); assertEquals(segment.isMapped(), true); assertTrue(segment.isReadOnly()); @@ -577,7 +576,7 @@ public void testMapZeroSize() throws IOException { public void testMapCustomPath() throws IOException { Path path = Path.of(URI.create("jrt:/")); try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE)) { - fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 0L, MemorySession.openImplicit()); + fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 0L, SegmentScope.auto()); } } @@ -585,8 +584,8 @@ public void testMapCustomPath() throws IOException { public void testCopyHeapToNative(Consumer checker, Consumer initializer, SequenceLayout seq) { checkByteArrayAlignment(seq.elementLayout()); int bytes = (int)seq.byteSize(); - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment nativeArray = MemorySegment.allocateNative(bytes, 1, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment nativeArray = MemorySegment.allocateNative(bytes, 1, arena.scope());; MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes]); initializer.accept(heapArray); nativeArray.copyFrom(heapArray); @@ -598,8 +597,8 @@ public void testCopyHeapToNative(Consumer checker, Consumer checker, Consumer initializer, SequenceLayout seq) { checkByteArrayAlignment(seq.elementLayout()); int bytes = (int)seq.byteSize(); - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment nativeArray = MemorySegment.allocateNative(seq, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment nativeArray = MemorySegment.allocateNative(seq, arena.scope());; MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes]); initializer.accept(nativeArray); heapArray.copyFrom(nativeArray); @@ -670,8 +669,8 @@ public void bufferProperties(ByteBuffer bb, Predicate _unused) { @Test public void testRoundTripAccess() { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment ms = MemorySegment.allocateNative(4, 1, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment ms = MemorySegment.allocateNative(4, 1, arena.scope());; MemorySegment msNoAccess = ms.asReadOnly(); MemorySegment msRoundTrip = MemorySegment.ofBuffer(msNoAccess.asByteBuffer()); assertEquals(msNoAccess.isReadOnly(), msRoundTrip.isReadOnly()); @@ -680,23 +679,23 @@ public void testRoundTripAccess() { @Test(expectedExceptions = IllegalStateException.class) public void testDeadAccessOnClosedBufferSegment() { - MemorySegment s1 = MemorySegment.allocateNative(JAVA_INT, MemorySession.openConfined()); + Arena arena = Arena.openConfined(); + MemorySegment s1 = MemorySegment.allocateNative(JAVA_INT, arena.scope()); MemorySegment s2 = MemorySegment.ofBuffer(s1.asByteBuffer()); // memory freed - s1.session().close(); + arena.close(); s2.set(JAVA_INT, 0, 10); // Dead access! } - @Test(dataProvider = "allSessions") - public void testIOOnSegmentBuffer(Supplier sessionSupplier) throws IOException { + @Test(dataProvider = "closeableArenas") + public void closeableArenas(Supplier arenaSupplier) throws IOException { File tmp = File.createTempFile("tmp", "txt"); tmp.deleteOnExit(); - MemorySession session; try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE) ; - MemorySession scp = closeableSessionOrNull(session = sessionSupplier.get())) { - MemorySegment segment = MemorySegment.allocateNative(10, 1, session); + Arena arena = arenaSupplier.get()) { + MemorySegment segment = MemorySegment.allocateNative(10, 1, arena.scope());; for (int i = 0; i < 10; i++) { segment.set(JAVA_BYTE, i, (byte) i); } @@ -711,17 +710,18 @@ public void testIOOnSegmentBuffer(Supplier sessionSupplier) throw static final Class ISE = IllegalStateException.class; - @Test(dataProvider = "closeableSessions") - public void testIOOnClosedSegmentBuffer(Supplier sessionSupplier) throws IOException { + @Test(dataProvider = "closeableArenas") + public void testIOOnClosedSegmentBuffer(Supplier arenaSupplier) throws IOException { File tmp = File.createTempFile("tmp", "txt"); tmp.deleteOnExit(); + Arena arena = arenaSupplier.get(); try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { - MemorySegment segment = MemorySegment.allocateNative(10, sessionSupplier.get()); + MemorySegment segment = MemorySegment.allocateNative(10, arena.scope()); for (int i = 0; i < 10; i++) { segment.set(JAVA_BYTE, i, (byte) i); } ByteBuffer bb = segment.asByteBuffer(); - segment.session().close(); + arena.close(); assertThrows(ISE, () -> channel.read(bb)); assertThrows(ISE, () -> channel.read(new ByteBuffer[] {bb})); assertThrows(ISE, () -> channel.read(new ByteBuffer[] {bb}, 0, 1)); @@ -733,8 +733,8 @@ public void testIOOnClosedSegmentBuffer(Supplier sessionSupplier) @Test public void buffersAndArraysFromSlices() { - try (MemorySession session = MemorySession.openShared()) { - MemorySegment segment = MemorySegment.allocateNative(16, session); + try (Arena arena = Arena.openShared()) { + MemorySegment segment = MemorySegment.allocateNative(16, arena.scope());; int newSize = 8; var slice = segment.asSlice(4, newSize); @@ -751,8 +751,8 @@ public void buffersAndArraysFromSlices() { @Test public void viewsFromSharedSegment() { - try (MemorySession session = MemorySession.openShared()) { - MemorySegment segment = MemorySegment.allocateNative(16, session); + try (Arena arena = Arena.openShared()) { + MemorySegment segment = MemorySegment.allocateNative(16, arena.scope());; var byteBuffer = segment.asByteBuffer(); byteBuffer.asReadOnlyBuffer(); byteBuffer.slice(0, 8); @@ -762,33 +762,20 @@ public void viewsFromSharedSegment() { @DataProvider(name = "segments") public static Object[][] segments() throws Throwable { return new Object[][] { - { (Supplier) () -> MemorySegment.allocateNative(16, MemorySession.openImplicit()) }, - { (Supplier) () -> MemorySegment.allocateNative(16, MemorySession.openConfined()) }, + { (Supplier) () -> MemorySegment.allocateNative(16, SegmentScope.auto()) }, + { (Supplier) () -> MemorySegment.allocateNative(16, Arena.openConfined().scope()) }, { (Supplier) () -> MemorySegment.ofArray(new byte[16]) } }; } - @DataProvider(name = "closeableSessions") - public static Object[][] closeableSessions() { + @DataProvider(name = "closeableArenas") + public static Object[][] closeableArenas() { return new Object[][] { - { (Supplier) () -> MemorySession.openShared() }, - { (Supplier) () -> MemorySession.openConfined() }, - { (Supplier) () -> MemorySession.openShared(Cleaner.create()) }, - { (Supplier) () -> MemorySession.openConfined(Cleaner.create()) }, + { (Supplier) Arena::openConfined }, + { (Supplier) Arena::openShared }, }; } - @DataProvider(name = "allSessions") - public static Object[][] allSessions() { - return Stream.of(new Object[][] { { (Supplier) MemorySession::global} }, closeableSessions()) - .flatMap(Arrays::stream) - .toArray(Object[][]::new); - } - - static MemorySession closeableSessionOrNull(MemorySession session) { - return session.isCloseable() ? session : null; - } - @DataProvider(name = "bufferOps") public static Object[][] bufferOps() throws Throwable { List args = new ArrayList<>(); diff --git a/test/jdk/java/foreign/TestClassLoaderFindNative.java b/test/jdk/java/foreign/TestClassLoaderFindNative.java index d83ca5bea9b17..b462180b135cb 100644 --- a/test/jdk/java/foreign/TestClassLoaderFindNative.java +++ b/test/jdk/java/foreign/TestClassLoaderFindNative.java @@ -29,7 +29,7 @@ */ import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.SymbolLookup; import java.lang.foreign.ValueLayout; import org.testng.annotations.Test; @@ -49,20 +49,20 @@ public class TestClassLoaderFindNative { @Test public void testSimpleLookup() { - assertFalse(SymbolLookup.loaderLookup().lookup("f").isEmpty()); + assertFalse(SymbolLookup.loaderLookup().find("f").isEmpty()); } @Test public void testInvalidSymbolLookup() { - assertTrue(SymbolLookup.loaderLookup().lookup("nonExistent").isEmpty()); + assertTrue(SymbolLookup.loaderLookup().find("nonExistent").isEmpty()); } @Test public void testVariableSymbolLookup() { MemorySegment segment = MemorySegment.ofAddress( - SymbolLookup.loaderLookup().lookup("c").get().address(), + SymbolLookup.loaderLookup().find("c").get().address(), ValueLayout.JAVA_INT.byteSize(), - MemorySession.global()); + SegmentScope.global()); assertEquals(segment.get(JAVA_BYTE, 0), 42); } } diff --git a/test/jdk/java/foreign/TestDowncallBase.java b/test/jdk/java/foreign/TestDowncallBase.java index 3e992228a6a7d..14d8928afc895 100644 --- a/test/jdk/java/foreign/TestDowncallBase.java +++ b/test/jdk/java/foreign/TestDowncallBase.java @@ -22,9 +22,9 @@ * */ -import java.lang.foreign.Addressable; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.MemorySegment; import java.lang.foreign.MemoryLayout; import java.lang.foreign.SegmentAllocator; import java.lang.invoke.MethodHandle; @@ -36,7 +36,7 @@ public class TestDowncallBase extends CallGeneratorHelper { static Linker LINKER = Linker.nativeLinker(); - Object doCall(Addressable symbol, SegmentAllocator allocator, FunctionDescriptor descriptor, Object[] args) throws Throwable { + Object doCall(MemorySegment symbol, SegmentAllocator allocator, FunctionDescriptor descriptor, Object[] args) throws Throwable { MethodHandle mh = downcallHandle(LINKER, symbol, allocator, descriptor); Object res = mh.invokeWithArguments(args); return res; diff --git a/test/jdk/java/foreign/TestDowncallScope.java b/test/jdk/java/foreign/TestDowncallScope.java index 67e63273e0cc3..1d586274ff142 100644 --- a/test/jdk/java/foreign/TestDowncallScope.java +++ b/test/jdk/java/foreign/TestDowncallScope.java @@ -38,11 +38,10 @@ import org.testng.annotations.Test; -import java.lang.foreign.Addressable; +import java.lang.foreign.Arena; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.GroupLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.lang.foreign.SegmentAllocator; import java.util.ArrayList; import java.util.List; @@ -61,20 +60,20 @@ public void testDowncall(int count, String fName, CallGeneratorHelper.Ret ret, List paramTypes, List fields) throws Throwable { List> checks = new ArrayList<>(); - Addressable addr = findNativeOrThrow(fName); + MemorySegment addr = findNativeOrThrow(fName); FunctionDescriptor descriptor = function(ret, paramTypes, fields); Object[] args = makeArgs(paramTypes, fields, checks); - try (MemorySession session = MemorySession.openShared()) { + try (Arena arena = Arena.openShared()) { boolean needsScope = descriptor.returnLayout().map(GroupLayout.class::isInstance).orElse(false); SegmentAllocator allocator = needsScope ? - SegmentAllocator.newNativeArena(session) : + SegmentAllocator.nativeAllocator(arena.scope()) : THROWING_ALLOCATOR; Object res = doCall(addr, allocator, descriptor, args); if (ret == CallGeneratorHelper.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).session(), session); + assertEquals(((MemorySegment)res).scope(), arena.scope()); } } } diff --git a/test/jdk/java/foreign/TestDowncallStack.java b/test/jdk/java/foreign/TestDowncallStack.java index abf7d32b968c2..1426604a93102 100644 --- a/test/jdk/java/foreign/TestDowncallStack.java +++ b/test/jdk/java/foreign/TestDowncallStack.java @@ -34,11 +34,10 @@ import org.testng.annotations.Test; -import java.lang.foreign.Addressable; +import java.lang.foreign.Arena; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.GroupLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.lang.foreign.SegmentAllocator; import java.util.ArrayList; import java.util.List; @@ -57,20 +56,20 @@ public void testDowncallStack(int count, String fName, CallGeneratorHelper.Ret r List paramTypes, List fields) throws Throwable { List> checks = new ArrayList<>(); - Addressable addr = findNativeOrThrow("s" + fName); + MemorySegment addr = findNativeOrThrow("s" + fName); FunctionDescriptor descriptor = functionStack(ret, paramTypes, fields); Object[] args = makeArgsStack(paramTypes, fields, checks); - try (MemorySession session = MemorySession.openShared()) { + try (Arena arena = Arena.openShared()) { boolean needsScope = descriptor.returnLayout().map(GroupLayout.class::isInstance).orElse(false); SegmentAllocator allocator = needsScope ? - SegmentAllocator.newNativeArena(session) : + SegmentAllocator.nativeAllocator(arena.scope()) : THROWING_ALLOCATOR; Object res = doCall(addr, allocator, descriptor, args); if (ret == CallGeneratorHelper.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).session(), session); + assertEquals(((MemorySegment)res).scope(), arena.scope()); } } } diff --git a/test/jdk/java/foreign/TestFallbackLookup.java b/test/jdk/java/foreign/TestFallbackLookup.java index d129db228c59f..71363590b9a30 100644 --- a/test/jdk/java/foreign/TestFallbackLookup.java +++ b/test/jdk/java/foreign/TestFallbackLookup.java @@ -38,6 +38,6 @@ public class TestFallbackLookup { void testBadSystemLookupRequest() { // we request a Linker, forcing OS name to be "Windows". This should trigger an exception when // attempting to load a non-existent ucrtbase.dll. Make sure that no error is generated at this stage. - assertTrue(Linker.nativeLinker().defaultLookup().lookup("nonExistentSymbol").isEmpty()); + assertTrue(Linker.nativeLinker().defaultLookup().find("nonExistentSymbol").isEmpty()); } } diff --git a/test/jdk/java/foreign/TestFree.java b/test/jdk/java/foreign/TestFree.java index 2a5dcac2796f7..a61499a2194fa 100644 --- a/test/jdk/java/foreign/TestFree.java +++ b/test/jdk/java/foreign/TestFree.java @@ -30,25 +30,17 @@ * @run testng/othervm --enable-native-access=ALL-UNNAMED TestFree */ -import java.lang.foreign.MemoryAddress; -import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import static org.testng.Assert.assertEquals; public class TestFree extends NativeTestHelper { - private static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements) { - return MemorySegment.ofAddress(addr, numElements * layout.byteSize(), MemorySession.global()); - } - public void test() throws Throwable { String str = "hello world"; - MemoryAddress addr = allocateMemory(str.length() + 1); - MemorySegment seg = asArray(addr, C_CHAR, str.length() + 1); - seg.copyFrom(MemorySegment.ofArray(str.getBytes())); - seg.set(C_CHAR, str.length(), (byte)0); - assertEquals(str, seg.getUtf8String(0)); + MemorySegment addr = allocateMemory(str.length() + 1); + addr.copyFrom(MemorySegment.ofArray(str.getBytes())); + addr.set(C_CHAR, str.length(), (byte)0); + assertEquals(str, addr.getUtf8String(0)); freeMemory(addr); } } diff --git a/test/jdk/java/foreign/TestFunctionDescriptor.java b/test/jdk/java/foreign/TestFunctionDescriptor.java index 2438ce23c4837..915c97ad50725 100644 --- a/test/jdk/java/foreign/TestFunctionDescriptor.java +++ b/test/jdk/java/foreign/TestFunctionDescriptor.java @@ -31,6 +31,8 @@ import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.invoke.MethodType; import java.util.List; import java.util.Optional; import org.testng.annotations.Test; @@ -98,10 +100,25 @@ public void testDropReturnLayout() { @Test public void testEquals() { FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_INT, C_INT); - FunctionDescriptor fd_va1 = FunctionDescriptor.of(C_INT).asVariadic(C_INT, C_INT); - FunctionDescriptor fd_va2 = FunctionDescriptor.of(C_INT, C_INT).asVariadic(C_INT); assertEquals(fd, fd); - assertNotEquals(fd, fd_va1); - assertNotEquals(fd, fd_va2); + } + + @Test + public void testCarrierMethodType() { + FunctionDescriptor fd = FunctionDescriptor.of(C_INT, + C_INT, + MemoryLayout.structLayout(C_INT, C_INT)); + MethodType cmt = fd.toMethodType(); + assertEquals(cmt, MethodType.methodType(int.class, int.class, MemorySegment.class)); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testBadCarrierMethodType() { + FunctionDescriptor fd = FunctionDescriptor.of(C_INT, + C_INT, + MemoryLayout.structLayout(C_INT, C_INT), + MemoryLayout.sequenceLayout(3, C_INT), + MemoryLayout.paddingLayout(32)); + fd.toMethodType(); // should throw } } diff --git a/test/jdk/java/foreign/TestHandshake.java b/test/jdk/java/foreign/TestHandshake.java index 8d33b5b64ce79..519739cb1eb14 100644 --- a/test/jdk/java/foreign/TestHandshake.java +++ b/test/jdk/java/foreign/TestHandshake.java @@ -32,8 +32,8 @@ * @run testng/othervm -XX:-TieredCompilation TestHandshake */ +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.nio.ByteBuffer; @@ -67,8 +67,8 @@ public class TestHandshake { @Test(dataProvider = "accessors") public void testHandshake(String testName, AccessorFactory accessorFactory) throws InterruptedException { for (int it = 0 ; it < ITERATIONS ; it++) { - MemorySession session = MemorySession.openShared(); - MemorySegment segment = MemorySegment.allocateNative(SEGMENT_SIZE, 1, session); + Arena arena = Arena.openShared(); + MemorySegment segment = MemorySegment.allocateNative(SEGMENT_SIZE, 1, arena.scope()); System.out.println("ITERATION " + it); ExecutorService accessExecutor = Executors.newCachedThreadPool(); start.set(System.currentTimeMillis()); @@ -79,10 +79,10 @@ public void testHandshake(String testName, AccessorFactory accessorFactory) thro int delay = ThreadLocalRandom.current().nextInt(MAX_DELAY_MILLIS); System.out.println("Starting handshaker with delay set to " + delay + " millis"); Thread.sleep(delay); - accessExecutor.execute(new Handshaker(session)); + accessExecutor.execute(new Handshaker(arena)); accessExecutor.shutdown(); assertTrue(accessExecutor.awaitTermination(MAX_EXECUTOR_WAIT_SECONDS, TimeUnit.SECONDS)); - assertTrue(!segment.session().isAlive()); + assertTrue(!segment.scope().isAlive()); } } @@ -99,7 +99,7 @@ static abstract class AbstractSegmentAccessor implements Runnable { @Override public final void run() { start("\"Accessor #\" + id"); - outer: while (segment.session().isAlive()) { + outer: while (segment.scope().isAlive()) { try { doAccess(); } catch (IllegalStateException ex) { @@ -193,7 +193,7 @@ static class SegmentMismatchAccessor extends AbstractSegmentAccessor { SegmentMismatchAccessor(int id, MemorySegment segment) { super(id, segment); - this.copy = MemorySegment.allocateNative(SEGMENT_SIZE, 1, segment.session()); + this.copy = MemorySegment.allocateNative(SEGMENT_SIZE, 1, segment.scope()); copy.copyFrom(segment); copy.set(JAVA_BYTE, ThreadLocalRandom.current().nextInt(SEGMENT_SIZE), (byte)42); } @@ -238,10 +238,10 @@ public void doAccess() { static class Handshaker implements Runnable { - final MemorySession session; + final Arena arena; - Handshaker(MemorySession session) { - this.session = session; + Handshaker(Arena arena) { + this.arena = arena; } @Override @@ -249,7 +249,7 @@ public void run() { start("Handshaker"); while (true) { try { - session.close(); + arena.close(); break; } catch (IllegalStateException ex) { Thread.onSpinWait(); diff --git a/test/jdk/java/foreign/TestHeapAlignment.java b/test/jdk/java/foreign/TestHeapAlignment.java index 91a154538166e..eff8dca98020e 100644 --- a/test/jdk/java/foreign/TestHeapAlignment.java +++ b/test/jdk/java/foreign/TestHeapAlignment.java @@ -29,10 +29,9 @@ * @run testng/othervm --enable-native-access=ALL-UNNAMED TestHeapAlignment */ -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import java.util.ArrayList; import java.util.List; @@ -101,7 +100,7 @@ enum SegmentAndAlignment { HEAP_FLOAT(MemorySegment.ofArray(new float[2]), 4), HEAP_LONG(MemorySegment.ofArray(new long[1]), 8), HEAP_DOUBLE(MemorySegment.ofArray(new double[1]), 8), - NATIVE(MemorySegment.allocateNative(8, MemorySession.openImplicit()), -1); + NATIVE(MemorySegment.allocateNative(8, SegmentScope.auto()), -1); final MemorySegment segment; final int align; @@ -124,7 +123,7 @@ public static Object[][] layouts() { layouts.add(new Object[] { testCase.segment, testCase.align, 42f, new float[]{42}, JAVA_FLOAT_ALIGNED, (Function)MemorySegment::ofArray }); layouts.add(new Object[] { testCase.segment, testCase.align, 42L, new long[]{42}, JAVA_LONG_ALIGNED, (Function)MemorySegment::ofArray }); layouts.add(new Object[] { testCase.segment, testCase.align, 42d, new double[]{42}, JAVA_DOUBLE_ALIGNED, (Function)MemorySegment::ofArray }); - layouts.add(new Object[] { testCase.segment, testCase.align, MemoryAddress.ofLong(42), null, ADDRESS_ALIGNED, null }); + layouts.add(new Object[] { testCase.segment, testCase.align, MemorySegment.ofAddress(42), null, ADDRESS_ALIGNED, null }); } return layouts.toArray(new Object[0][]); } diff --git a/test/jdk/java/foreign/TestIllegalLink.java b/test/jdk/java/foreign/TestIllegalLink.java index 3219d15015820..670ac641eb2b8 100644 --- a/test/jdk/java/foreign/TestIllegalLink.java +++ b/test/jdk/java/foreign/TestIllegalLink.java @@ -29,11 +29,12 @@ * @run testng/othervm --enable-native-access=ALL-UNNAMED TestIllegalLink */ -import java.lang.foreign.Addressable; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryAddress; +import java.lang.foreign.MemorySegment; import java.lang.foreign.MemoryLayout; +import java.nio.ByteOrder; + import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -42,7 +43,7 @@ public class TestIllegalLink extends NativeTestHelper { - private static final Addressable DUMMY_TARGET = MemoryAddress.ofLong(1); + private static final MemorySegment DUMMY_TARGET = MemorySegment.ofAddress(1); private static final Linker ABI = Linker.nativeLinker(); @Test(dataProvider = "types") @@ -51,7 +52,8 @@ public void testTypeMismatch(FunctionDescriptor desc, String expectedExceptionMe ABI.downcallHandle(DUMMY_TARGET, desc); fail("Expected IllegalArgumentException was not thrown"); } catch (IllegalArgumentException e) { - assertTrue(e.getMessage().contains(expectedExceptionMessage)); + assertTrue(e.getMessage().contains(expectedExceptionMessage), + e.getMessage() + " != " + expectedExceptionMessage); } } @@ -59,12 +61,12 @@ public void testTypeMismatch(FunctionDescriptor desc, String expectedExceptionMe public static Object[][] types() { return new Object[][]{ { - FunctionDescriptor.of(MemoryLayout.paddingLayout(64)), - "Unsupported layout: x64" + FunctionDescriptor.of(MemoryLayout.paddingLayout(64)), + "Unsupported layout: x64" }, { - FunctionDescriptor.ofVoid(MemoryLayout.paddingLayout(64)), - "Unsupported layout: x64" + FunctionDescriptor.ofVoid(MemoryLayout.paddingLayout(64)), + "Unsupported layout: x64" }, { FunctionDescriptor.of(MemoryLayout.sequenceLayout(2, C_INT)), @@ -74,6 +76,42 @@ public static Object[][] types() { FunctionDescriptor.ofVoid(MemoryLayout.sequenceLayout(2, C_INT)), "Unsupported layout: [2:i32]" }, + { + FunctionDescriptor.ofVoid(C_INT.withBitAlignment(16)), + "Layout bit alignment must be natural alignment" + }, + { + FunctionDescriptor.ofVoid(C_POINTER.withBitAlignment(16)), + "Layout bit alignment must be natural alignment" + }, + { + FunctionDescriptor.ofVoid(MemoryLayout.valueLayout(char.class, ByteOrder.nativeOrder()).withBitAlignment(32)), + "Layout bit alignment must be natural alignment" + }, + { + FunctionDescriptor.ofVoid(MemoryLayout.structLayout( + C_CHAR.withName("x").withBitAlignment(8), + C_SHORT.withName("y").withBitAlignment(8), + C_INT.withName("z").withBitAlignment(8) + ).withBitAlignment(8)), + "Layout bit alignment must be natural alignment" + }, + { + FunctionDescriptor.ofVoid(MemoryLayout.structLayout( + MemoryLayout.structLayout( + C_CHAR.withName("x").withBitAlignment(8), + C_SHORT.withName("y").withBitAlignment(8), + C_INT.withName("z").withBitAlignment(8) + ))), + "Layout bit alignment must be natural alignment" + }, + { + FunctionDescriptor.ofVoid(MemoryLayout.structLayout( + MemoryLayout.sequenceLayout( + C_INT.withBitAlignment(8) + ))), + "Layout bit alignment must be natural alignment" + }, }; } diff --git a/test/jdk/java/foreign/TestIntrinsics.java b/test/jdk/java/foreign/TestIntrinsics.java index 60ed3faf757e6..38c742015bb62 100644 --- a/test/jdk/java/foreign/TestIntrinsics.java +++ b/test/jdk/java/foreign/TestIntrinsics.java @@ -32,18 +32,18 @@ * TestIntrinsics */ -import java.lang.foreign.Addressable; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.MemorySegment; import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; import java.util.ArrayList; import java.util.List; import java.lang.foreign.MemoryLayout; import org.testng.annotations.*; +import static java.lang.foreign.Linker.Option.firstVariadicArg; import static java.lang.invoke.MethodType.methodType; import static java.lang.foreign.ValueLayout.JAVA_CHAR; import static org.testng.Assert.assertEquals; @@ -84,8 +84,7 @@ interface AddIdentity { } AddIdentity addIdentity = (name, carrier, layout, arg) -> { - Addressable ma = findNativeOrThrow(name); - MethodType mt = methodType(carrier, carrier); + MemorySegment ma = findNativeOrThrow(name); FunctionDescriptor fd = FunctionDescriptor.of(layout, layout); tests.add(abi.downcallHandle(ma, fd), arg, arg); @@ -93,8 +92,7 @@ interface AddIdentity { }; { // empty - Addressable ma = findNativeOrThrow("empty"); - MethodType mt = methodType(void.class); + MemorySegment ma = findNativeOrThrow("empty"); FunctionDescriptor fd = FunctionDescriptor.ofVoid(); tests.add(abi.downcallHandle(ma, fd), null); } @@ -108,21 +106,18 @@ interface AddIdentity { addIdentity.add("identity_double", double.class, C_DOUBLE, 10D); { // identity_va - Addressable ma = findNativeOrThrow("identity_va"); - MethodType mt = methodType(int.class, int.class, double.class, int.class, float.class, long.class); - 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); + MemorySegment ma = findNativeOrThrow("identity_va"); + FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_INT, + C_DOUBLE, C_INT, C_FLOAT, C_LONG_LONG); + tests.add(abi.downcallHandle(ma, fd, firstVariadicArg(1)), 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, JAVA_CHAR); Object[] args = {1, 10D, 2L, 3F, (byte) 0, (short) 13, 'a'}; for (int i = 0; i < args.length; i++) { - Addressable ma = findNativeOrThrow("invoke_high_arity" + i); - MethodType mt = baseMT.changeReturnType(baseMT.parameterType(i)); + MemorySegment ma = findNativeOrThrow("invoke_high_arity" + i); FunctionDescriptor fd = baseFD.changeReturnLayout(baseFD.argumentLayouts().get(i)); Object expected = args[i]; tests.add(abi.downcallHandle(ma, fd), expected, args); diff --git a/test/jdk/java/foreign/TestLargeSegmentCopy.java b/test/jdk/java/foreign/TestLargeSegmentCopy.java new file mode 100644 index 0000000000000..0ed9b3971afbb --- /dev/null +++ b/test/jdk/java/foreign/TestLargeSegmentCopy.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/* + * @test + * @enablePreview + * @bug 8292851 + * @run testng/othervm -Xmx4G TestLargeSegmentCopy + */ + +import org.testng.annotations.Test; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; + +import static java.lang.foreign.ValueLayout.JAVA_LONG; + +public class TestLargeSegmentCopy { + + @Test + public void testLargeSegmentCopy() { + // Make sure the byte size is bigger than Integer.MAX_VALUE + final int longArrayLength = Integer.MAX_VALUE / Long.BYTES + 100; + final long[] array = new long[longArrayLength]; + + try (var arena = Arena.openConfined()) { + var segment = MemorySegment.allocateNative((long) longArrayLength * Long.BYTES, Long.SIZE, arena.scope()); + // Should not throw an exception or error + MemorySegment.copy(segment, JAVA_LONG, 0, array, 0, longArrayLength); + // Should not throw an exception or error + MemorySegment.copy(array,0, segment, JAVA_LONG, 0, longArrayLength); + } + + } + +} diff --git a/test/jdk/java/foreign/TestLayoutEquality.java b/test/jdk/java/foreign/TestLayoutEquality.java index 66c8a8e3fc87b..e07638e5078f0 100644 --- a/test/jdk/java/foreign/TestLayoutEquality.java +++ b/test/jdk/java/foreign/TestLayoutEquality.java @@ -29,7 +29,7 @@ * @run testng TestLayoutEquality */ -import java.lang.foreign.MemoryAddress; +import java.lang.foreign.MemoryLayout; import java.lang.foreign.ValueLayout; import jdk.internal.foreign.PlatformLayouts; import org.testng.annotations.DataProvider; @@ -39,24 +39,17 @@ import java.util.ArrayList; import java.util.List; -import static java.lang.foreign.ValueLayout.ADDRESS; -import static java.lang.foreign.ValueLayout.JAVA_BOOLEAN; -import static java.lang.foreign.ValueLayout.JAVA_BYTE; -import static java.lang.foreign.ValueLayout.JAVA_CHAR; -import static java.lang.foreign.ValueLayout.JAVA_DOUBLE; -import static java.lang.foreign.ValueLayout.JAVA_FLOAT; -import static java.lang.foreign.ValueLayout.JAVA_INT; -import static java.lang.foreign.ValueLayout.JAVA_LONG; -import static java.lang.foreign.ValueLayout.JAVA_SHORT; import static org.testng.Assert.*; public class TestLayoutEquality { @Test(dataProvider = "layoutConstants") public void testReconstructedEquality(ValueLayout layout) { - ValueLayout newLayout = valueLayoutForCarrier(layout.carrier()); + ValueLayout newLayout = MemoryLayout.valueLayout(layout.carrier(), layout.order()); newLayout = newLayout.withBitAlignment(layout.bitAlignment()); - newLayout = newLayout.withOrder(layout.order()); + if (layout instanceof ValueLayout.OfAddress addressLayout && addressLayout.isUnbounded()) { + newLayout = ((ValueLayout.OfAddress)newLayout).asUnbounded(); + } // properties should be equal assertEquals(newLayout.bitSize(), layout.bitSize()); @@ -84,28 +77,4 @@ private static void addLayoutConstants(List testValues, Class cl testValues.add((ValueLayout) f.get(null)); } } - - 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 d3daa3ec3edb8..59663af82003d 100644 --- a/test/jdk/java/foreign/TestLayoutPaths.java +++ b/test/jdk/java/foreign/TestLayoutPaths.java @@ -28,11 +28,11 @@ * @run testng TestLayoutPaths */ +import java.lang.foreign.Arena; import java.lang.foreign.GroupLayout; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemoryLayout.PathElement; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.lang.foreign.SequenceLayout; import java.lang.foreign.ValueLayout; @@ -433,10 +433,10 @@ public void testSliceHandle(MemoryLayout layout, PathElement[] pathElements, lon MethodHandle sliceHandle = layout.sliceHandle(pathElements); sliceHandle = sliceHandle.asSpreader(long[].class, indexes.length); - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(layout, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(layout, arena.scope()); MemorySegment slice = (MemorySegment) sliceHandle.invokeExact(segment, indexes); - assertEquals(slice.address().toRawLongValue() - segment.address().toRawLongValue(), expectedBitOffset / 8); + assertEquals(slice.address() - segment.address(), expectedBitOffset / 8); assertEquals(slice.byteSize(), selected.byteSize()); } } @@ -468,8 +468,8 @@ public void testSliceHandleUOEInvalidOffsetLate() throws Throwable { return; } - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(layout, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(layout, arena.scope()); try { sliceHandle.invokeExact(segment, 1); // should work diff --git a/test/jdk/java/foreign/TestLayouts.java b/test/jdk/java/foreign/TestLayouts.java index 843c88d5e3662..e09eaab508a86 100644 --- a/test/jdk/java/foreign/TestLayouts.java +++ b/test/jdk/java/foreign/TestLayouts.java @@ -52,8 +52,8 @@ public void testBadLayoutAlignment(MemoryLayout layout, long alignment) { @Test public void testIndexedSequencePath() { MemoryLayout seq = MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_INT); - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(seq, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(seq, arena.scope());; VarHandle indexHandle = seq.varHandle(MemoryLayout.PathElement.sequenceElement()); // init segment for (int i = 0 ; i < 10 ; i++) { @@ -138,10 +138,15 @@ public void testSequenceBadCount() { @Test(dataProvider = "basicLayouts") public void testSequenceInferredCount(MemoryLayout layout) { - assertEquals(MemoryLayout.sequenceLayout(-1, layout), + assertEquals(MemoryLayout.sequenceLayout(layout), MemoryLayout.sequenceLayout(Long.MAX_VALUE / layout.bitSize(), layout)); } + public void testSequenceNegativeElementCount() { + assertThrows(IllegalArgumentException.class, // negative + () -> MemoryLayout.sequenceLayout(-1, JAVA_SHORT)); + } + @Test public void testSequenceOverflow() { assertThrows(IllegalArgumentException.class, // negative @@ -163,7 +168,7 @@ public void testStructOverflow() { @Test(dataProvider = "layoutKinds") public void testPadding(LayoutKind kind) { - assertEquals(kind == LayoutKind.PADDING, kind.layout.isPadding()); + assertEquals(kind == LayoutKind.PADDING, kind.layout instanceof PaddingLayout); } @Test(dataProvider="layoutsAndAlignments") diff --git a/test/jdk/java/foreign/TestLinker.java b/test/jdk/java/foreign/TestLinker.java new file mode 100644 index 0000000000000..8077e3ef8418a --- /dev/null +++ b/test/jdk/java/foreign/TestLinker.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @enablePreview + * @run testng TestLinker + */ + +import org.testng.annotations.Test; + +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.invoke.MethodHandle; + +import static org.testng.Assert.assertNotSame; + +public class TestLinker extends NativeTestHelper { + + @Test + public void testLinkerOptionsCache() { + Linker linker = Linker.nativeLinker(); + FunctionDescriptor descriptor = FunctionDescriptor.ofVoid(C_INT, C_INT); + MethodHandle mh1 = linker.downcallHandle(descriptor); + MethodHandle mh2 = linker.downcallHandle(descriptor, Linker.Option.firstVariadicArg(1)); + // assert that these are 2 distinct link request. No caching allowed + assertNotSame(mh1, mh2); + } + +} diff --git a/test/jdk/java/foreign/TestMemoryAccess.java b/test/jdk/java/foreign/TestMemoryAccess.java index 1fa39a40fd85c..7028635b68253 100644 --- a/test/jdk/java/foreign/TestMemoryAccess.java +++ b/test/jdk/java/foreign/TestMemoryAccess.java @@ -30,12 +30,11 @@ * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true -Xverify:all TestMemoryAccess */ +import java.lang.foreign.Arena; import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemoryLayout.PathElement; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.lang.foreign.SequenceLayout; import java.lang.foreign.ValueLayout; @@ -92,8 +91,8 @@ public void testPaddedArrayAccessByIndexSeq(Function viewFactory, MemoryLayout layout, VarHandle handle, Checker checker) { MemorySegment outer_segment; - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(layout, session)); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(layout, arena.scope())); boolean isRO = segment.isReadOnly(); try { checker.check(handle, segment); @@ -124,8 +123,8 @@ private void testAccessInternal(Function viewFacto private void testArrayAccessInternal(Function viewFactory, SequenceLayout seq, VarHandle handle, ArrayChecker checker) { MemorySegment outer_segment; - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(seq, session)); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(seq, arena.scope())); boolean isRO = segment.isReadOnly(); try { for (int i = 0; i < seq.elementCount(); i++) { @@ -193,8 +192,8 @@ public void testPaddedMatrixAccessByIndexSeq(Function viewFactory, SequenceLayout seq, VarHandle handle, MatrixChecker checker) { MemorySegment outer_segment; - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(seq, session)); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(seq, arena.scope())); boolean isRO = segment.isReadOnly(); try { for (int i = 0; i < seq.elementCount(); i++) { @@ -465,8 +464,8 @@ interface MatrixChecker { }; 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)); + handle.set(segment, r, c, MemorySegment.ofAddress(r + c)); + assertEquals(MemorySegment.ofAddress(r + c), (MemorySegment) handle.get(segment, r, c)); }; MatrixChecker FLOAT = (handle, segment, r, c) -> { diff --git a/test/jdk/java/foreign/TestMemoryAccessInstance.java b/test/jdk/java/foreign/TestMemoryAccessInstance.java index c75aeb4597885..d2a8ba14f9c7d 100644 --- a/test/jdk/java/foreign/TestMemoryAccessInstance.java +++ b/test/jdk/java/foreign/TestMemoryAccessInstance.java @@ -27,9 +27,8 @@ * @run testng/othervm --enable-native-access=ALL-UNNAMED TestMemoryAccessInstance */ -import java.lang.foreign.MemoryAddress; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.lang.foreign.ValueLayout; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -80,8 +79,8 @@ interface BufferSetter { } void test() { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(128, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(128, arena.scope());; ByteBuffer buffer = segment.asByteBuffer(); T t = transform.apply(segment); segmentSetter.set(t, layout, 8, value); @@ -93,8 +92,8 @@ void test() { @SuppressWarnings("unchecked") void testHyperAligned() { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(64, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(64, arena.scope());; T t = transform.apply(segment); L alignedLayout = (L)layout.withBitAlignment(layout.byteSize() * 8 * 2); try { @@ -117,12 +116,6 @@ static Accessor ofSegment(L layo 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") @@ -130,11 +123,6 @@ public void testSegmentAccess(String testName, Accessor accessor) { accessor.test(); } - @Test(dataProvider = "addressAccessors") - public void testAddressAccess(String testName, Accessor accessor) { - accessor.test(); - } - @Test(dataProvider = "segmentAccessors") public void testSegmentAccessHyper(String testName, Accessor accessor) { if (testName.contains("index")) { @@ -144,15 +132,6 @@ public void testSegmentAccessHyper(String testName, Accessor accessor) } } - @Test(dataProvider = "addressAccessors") - public void testAddressAccessHyper(String testName, Accessor accessor) { - if (testName.contains("index")) { - accessor.testHyperAligned(); - } else { - throw new SkipException("Skipping"); - } - } - static final ByteOrder NE = ByteOrder.nativeOrder(); @DataProvider(name = "segmentAccessors") @@ -187,20 +166,20 @@ static Object[][] segmentAccessors() { 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), + { "address", Accessor.ofSegment(ValueLayout.ADDRESS, MemorySegment.ofAddress(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); + return MemorySegment.ofAddress(addr); }, (bb, pos, v) -> { ByteBuffer nb = bb.order(NE); if (ValueLayout.ADDRESS.byteSize() == 8) { - nb.putLong(pos, v.toRawLongValue()); + nb.putLong(pos, v.address()); } else { - nb.putInt(pos, (int)v.toRawLongValue()); + nb.putInt(pos, (int)v.address()); } }) }, @@ -225,112 +204,23 @@ static Object[][] segmentAccessors() { 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), + { "address/index", Accessor.ofSegment(ValueLayout.ADDRESS, MemorySegment.ofAddress(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); + return MemorySegment.ofAddress(addr); }, (bb, pos, v) -> { ByteBuffer nb = bb.order(NE); if (ValueLayout.ADDRESS.byteSize() == 8) { - nb.putLong(pos * 8, v.toRawLongValue()); + nb.putLong(pos * 8, v.address()); } else { - nb.putInt(pos * 4, (int)v.toRawLongValue()); + nb.putInt(pos * 4, (int)v.address()); } }) }, }; } - - @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/TestMemoryAlignment.java b/test/jdk/java/foreign/TestMemoryAlignment.java index 225557d18f72e..cc92085422b81 100644 --- a/test/jdk/java/foreign/TestMemoryAlignment.java +++ b/test/jdk/java/foreign/TestMemoryAlignment.java @@ -27,11 +27,11 @@ * @run testng TestMemoryAlignment */ +import java.lang.foreign.Arena; import java.lang.foreign.GroupLayout; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemoryLayout.PathElement; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.lang.foreign.SequenceLayout; import java.lang.foreign.ValueLayout; import java.lang.invoke.VarHandle; @@ -52,8 +52,8 @@ public void testAlignedAccess(long align) { ValueLayout aligned = layout.withBitAlignment(align); assertEquals(aligned.bitAlignment(), align); //unreasonable alignment here, to make sure access throws VarHandle vh = aligned.varHandle(); - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(aligned, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(aligned, arena.scope());; vh.set(segment, -42); int val = (int)vh.get(segment); assertEquals(val, -42); @@ -70,8 +70,8 @@ public void testUnalignedAccess(long align) { MemoryLayout alignedGroup = MemoryLayout.structLayout(MemoryLayout.paddingLayout(8), aligned); assertEquals(alignedGroup.bitAlignment(), align); VarHandle vh = aligned.varHandle(); - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(alignedGroup, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(alignedGroup, arena.scope());; vh.set(segment.asSlice(1L), -42); assertEquals(align, 8); //this is the only case where access is aligned } catch (IllegalArgumentException ex) { @@ -97,8 +97,8 @@ public void testUnalignedSequence(long align) { SequenceLayout layout = MemoryLayout.sequenceLayout(5, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN).withBitAlignment(align)); try { VarHandle vh = layout.varHandle(PathElement.sequenceElement()); - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(layout, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(layout, arena.scope());; for (long i = 0 ; i < 5 ; i++) { vh.set(segment, i, -42); } @@ -121,8 +121,8 @@ public void testPackedAccess() { 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 (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(g, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(g, arena.scope());; vh_c.set(segment, Byte.MIN_VALUE); assertEquals(vh_c.get(segment), Byte.MIN_VALUE); vh_s.set(segment, Short.MIN_VALUE); diff --git a/test/jdk/java/foreign/TestMemoryDereference.java b/test/jdk/java/foreign/TestMemoryDereference.java index d6d5af5f94909..13dd26ce749d8 100644 --- a/test/jdk/java/foreign/TestMemoryDereference.java +++ b/test/jdk/java/foreign/TestMemoryDereference.java @@ -27,7 +27,6 @@ * @run testng TestMemoryDereference */ -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemorySegment; import java.nio.ByteBuffer; @@ -36,14 +35,7 @@ import java.lang.foreign.ValueLayout; import org.testng.annotations.*; -import static java.lang.foreign.ValueLayout.ADDRESS; -import static java.lang.foreign.ValueLayout.JAVA_BOOLEAN; -import static java.lang.foreign.ValueLayout.JAVA_BYTE; -import static java.lang.foreign.ValueLayout.JAVA_CHAR; -import static java.lang.foreign.ValueLayout.JAVA_DOUBLE; -import static java.lang.foreign.ValueLayout.JAVA_FLOAT; -import static java.lang.foreign.ValueLayout.JAVA_INT; -import static java.lang.foreign.ValueLayout.JAVA_SHORT; +import static java.lang.foreign.ValueLayout.*; import static org.testng.Assert.*; public class TestMemoryDereference { @@ -98,14 +90,6 @@ Accessor of(Z value, } } - // unaligned constants - public final static ValueLayout.OfShort JAVA_SHORT_UNALIGNED = JAVA_SHORT.withBitAlignment(8); - public final static ValueLayout.OfChar JAVA_CHAR_UNALIGNED = JAVA_CHAR.withBitAlignment(8); - public final static ValueLayout.OfInt JAVA_INT_UNALIGNED = JAVA_INT.withBitAlignment(8); - public final static ValueLayout.OfFloat JAVA_FLOAT_UNALIGNED = JAVA_FLOAT.withBitAlignment(8); - public final static ValueLayout.OfDouble JAVA_DOUBLE_UNALIGNED = JAVA_DOUBLE.withBitAlignment(8); - public final static ValueLayout.OfAddress ADDRESS_UNALIGNED = ADDRESS.withBitAlignment(8); - @Test(dataProvider = "accessors") public void testMemoryAccess(String testName, Accessor accessor) { accessor.test(); @@ -204,20 +188,20 @@ static Object[][] accessors() { (s, x) -> s.set(JAVA_DOUBLE_UNALIGNED.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), + { "address/offset", new Accessor<>(MemorySegment.ofAddress(42), s -> s.get(ADDRESS_UNALIGNED, 8), (s, x) -> s.set(ADDRESS_UNALIGNED, 8, x), (bb) -> { ByteBuffer nb = bb.order(NE); long addr = ADDRESS_UNALIGNED.byteSize() == 8 ? nb.getLong(8) : nb.getInt(8); - return MemoryAddress.ofLong(addr); + return MemorySegment.ofAddress(addr); }, (bb, v) -> { ByteBuffer nb = bb.order(NE); if (ADDRESS_UNALIGNED.byteSize() == 8) { - nb.putLong(8, v.toRawLongValue()); + nb.putLong(8, v.address()); } else { - nb.putInt(8, (int)v.toRawLongValue()); + nb.putInt(8, (int)v.address()); } }) }, diff --git a/test/jdk/java/foreign/TestMemorySession.java b/test/jdk/java/foreign/TestMemorySession.java index ff206255cc42b..6501978f725ed 100644 --- a/test/jdk/java/foreign/TestMemorySession.java +++ b/test/jdk/java/foreign/TestMemorySession.java @@ -28,13 +28,11 @@ * @run testng/othervm TestMemorySession */ -import java.lang.ref.Cleaner; +import java.lang.foreign.Arena; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import jdk.internal.foreign.MemorySessionImpl; -import jdk.internal.ref.CleanerFactory; - import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import static org.testng.Assert.*; @@ -43,57 +41,39 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Function; import java.util.function.Supplier; -import java.util.function.UnaryOperator; import java.util.stream.IntStream; public class TestMemorySession { final static int N_THREADS = 100; - @Test(dataProvider = "cleaners") - public void testConfined(Supplier cleanerSupplier, UnaryOperator sessionFunc) { + @Test + public void testConfined() { AtomicInteger acc = new AtomicInteger(); - Cleaner cleaner = cleanerSupplier.get(); - MemorySession session = cleaner != null ? - MemorySession.openConfined(cleaner) : - MemorySession.openConfined(); - session = sessionFunc.apply(session); + Arena arena = Arena.openConfined(); for (int i = 0 ; i < N_THREADS ; i++) { int delta = i; - session.addCloseAction(() -> acc.addAndGet(delta)); + addCloseAction(arena.scope(), () -> acc.addAndGet(delta)); } assertEquals(acc.get(), 0); - if (cleaner == null) { - session.close(); - assertEquals(acc.get(), IntStream.range(0, N_THREADS).sum()); - } else { - session = null; - int expected = IntStream.range(0, N_THREADS).sum(); - while (acc.get() != expected) { - kickGC(); - } - } + arena.close(); + assertEquals(acc.get(), IntStream.range(0, N_THREADS).sum()); } - @Test(dataProvider = "cleaners") - public void testSharedSingleThread(Supplier cleanerSupplier, UnaryOperator sessionFunc) { + @Test(dataProvider = "sharedSessions") + public void testSharedSingleThread(SessionSupplier sessionSupplier) { AtomicInteger acc = new AtomicInteger(); - Cleaner cleaner = cleanerSupplier.get(); - MemorySession session = cleaner != null ? - MemorySession.openShared(cleaner) : - MemorySession.openShared(); - session = sessionFunc.apply(session); + SegmentScope session = sessionSupplier.get(); for (int i = 0 ; i < N_THREADS ; i++) { int delta = i; - session.addCloseAction(() -> acc.addAndGet(delta)); + addCloseAction(session, () -> acc.addAndGet(delta)); } assertEquals(acc.get(), 0); - if (cleaner == null) { - session.close(); + if (!SessionSupplier.isImplicit(session)) { + SessionSupplier.close(session); assertEquals(acc.get(), IntStream.range(0, N_THREADS).sum()); } else { session = null; @@ -104,21 +84,17 @@ public void testSharedSingleThread(Supplier cleanerSupplier, UnaryOpera } } - @Test(dataProvider = "cleaners") - public void testSharedMultiThread(Supplier cleanerSupplier, UnaryOperator sessionFunc) { + @Test(dataProvider = "sharedSessions") + public void testSharedMultiThread(SessionSupplier sessionSupplier) { AtomicInteger acc = new AtomicInteger(); - Cleaner cleaner = cleanerSupplier.get(); List threads = new ArrayList<>(); - MemorySession session = cleaner != null ? - MemorySession.openShared(cleaner) : - MemorySession.openShared(); - session = sessionFunc.apply(session); - AtomicReference sessionRef = new AtomicReference<>(session); + SegmentScope session = sessionSupplier.get(); + AtomicReference sessionRef = new AtomicReference<>(session); for (int i = 0 ; i < N_THREADS ; i++) { int delta = i; Thread thread = new Thread(() -> { try { - sessionRef.get().addCloseAction(() -> { + addCloseAction(sessionRef.get(), () -> { acc.addAndGet(delta); }); } catch (IllegalStateException ex) { @@ -133,10 +109,10 @@ public void testSharedMultiThread(Supplier cleanerSupplier, UnaryOperat // if no cleaner, close - not all segments might have been added to the session! // if cleaner, don't unset the session - after all, the session is kept alive by threads - if (cleaner == null) { + if (!SessionSupplier.isImplicit(session)) { while (true) { try { - session.close(); + SessionSupplier.close(session); break; } catch (IllegalStateException ise) { // session is acquired (by add) - wait some more @@ -152,7 +128,7 @@ public void testSharedMultiThread(Supplier cleanerSupplier, UnaryOperat } }); - if (cleaner == null) { + if (!SessionSupplier.isImplicit(session)) { assertEquals(acc.get(), IntStream.range(0, N_THREADS).sum()); } else { session = null; @@ -166,22 +142,22 @@ public void testSharedMultiThread(Supplier cleanerSupplier, UnaryOperat @Test public void testLockSingleThread() { - MemorySession session = MemorySession.openConfined(); - List handles = new ArrayList<>(); + Arena arena = Arena.openConfined(); + List handles = new ArrayList<>(); for (int i = 0 ; i < N_THREADS ; i++) { - MemorySession handle = MemorySession.openConfined(); - keepAlive(handle, session); + Arena handle = Arena.openConfined(); + keepAlive(handle.scope(), arena.scope()); handles.add(handle); } while (true) { try { - session.close(); + arena.close(); assertEquals(handles.size(), 0); break; } catch (IllegalStateException ex) { assertTrue(handles.size() > 0); - MemorySession handle = handles.remove(0); + Arena handle = handles.remove(0); handle.close(); } } @@ -189,16 +165,15 @@ public void testLockSingleThread() { @Test public void testLockSharedMultiThread() { - MemorySession session = MemorySession.openShared(); + Arena arena = Arena.openShared(); AtomicInteger lockCount = new AtomicInteger(); for (int i = 0 ; i < N_THREADS ; i++) { new Thread(() -> { - try (MemorySession handle = MemorySession.openConfined()) { - keepAlive(handle, session); + try (Arena handle = Arena.openConfined()) { + keepAlive(handle.scope(), arena.scope()); lockCount.incrementAndGet(); waitSomeTime(); lockCount.decrementAndGet(); - handle.close(); } catch (IllegalStateException ex) { // might be already closed - do nothing } @@ -207,7 +182,7 @@ public void testLockSharedMultiThread() { while (true) { try { - session.close(); + arena.close(); assertEquals(lockCount.get(), 0); break; } catch (IllegalStateException ex) { @@ -218,19 +193,19 @@ public void testLockSharedMultiThread() { @Test public void testCloseEmptyConfinedSession() { - MemorySession.openConfined().close(); + Arena.openConfined().close(); } @Test public void testCloseEmptySharedSession() { - MemorySession.openShared().close(); + Arena.openShared().close(); } @Test public void testCloseConfinedLock() { - MemorySession session = MemorySession.openConfined(); - MemorySession handle = MemorySession.openConfined(); - keepAlive(handle, session); + Arena arena = Arena.openConfined(); + Arena handle = Arena.openConfined(); + keepAlive(handle.scope(), arena.scope()); AtomicReference failure = new AtomicReference<>(); Thread t = new Thread(() -> { try { @@ -249,34 +224,33 @@ public void testCloseConfinedLock() { } } - @Test(dataProvider = "sessions") - public void testSessionAcquires(Supplier sessionFactory) { - MemorySession session = sessionFactory.get(); + @Test(dataProvider = "allSessions") + public void testSessionAcquires(SessionSupplier sessionSupplier) { + SegmentScope session = sessionSupplier.get(); acquireRecursive(session, 5); - if (session.isCloseable()) { - session.close(); - } + if (!SessionSupplier.isImplicit(session)) + SessionSupplier.close(session); } - private void acquireRecursive(MemorySession session, int acquireCount) { - try (MemorySession handle = MemorySession.openConfined()) { - keepAlive(handle, session); + private void acquireRecursive(SegmentScope session, int acquireCount) { + try (Arena arena = Arena.openConfined()) { + keepAlive(arena.scope(), session); if (acquireCount > 0) { // recursive acquire acquireRecursive(session, acquireCount - 1); } - if (session.isCloseable()) { - assertThrows(IllegalStateException.class, session::close); + if (!SessionSupplier.isImplicit(session)) { + assertThrows(IllegalStateException.class, () -> SessionSupplier.close(session)); } } } @Test public void testConfinedSessionWithImplicitDependency() { - MemorySession root = MemorySession.openConfined(); + Arena root = Arena.openConfined(); // Create many implicit sessions which depend on 'root', and let them become unreachable. for (int i = 0; i < N_THREADS; i++) { - keepAlive(MemorySession.openConfined(Cleaner.create()), root); + keepAlive(SegmentScope.auto(), root.scope()); } // 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 session lock count is updated racily. @@ -288,8 +262,8 @@ public void testConfinedSessionWithImplicitDependency() { } catch (IllegalStateException ex) { kickGC(); for (int i = 0 ; i < N_THREADS ; i++) { // add more races from current thread - try (MemorySession session = MemorySession.openConfined()) { - keepAlive(session, root); + try (Arena arena = Arena.openConfined()) { + keepAlive(arena.scope(), root.scope()); // dummy } } @@ -300,19 +274,19 @@ public void testConfinedSessionWithImplicitDependency() { @Test public void testConfinedSessionWithSharedDependency() { - MemorySession root = MemorySession.openConfined(); + Arena root = Arena.openConfined(); List threads = new ArrayList<>(); // Create many implicit sessions which depend on 'root', and let them become unreachable. for (int i = 0; i < N_THREADS; i++) { - MemorySession session = MemorySession.openShared(); // create session inside same thread! - keepAlive(session, root); - Thread t = new Thread(session::close); // close from another thread! + Arena arena = Arena.openShared(); // create session inside same thread! + keepAlive(arena.scope(), root.scope()); + Thread t = new Thread(arena::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 (MemorySession session = MemorySession.openConfined()) { - keepAlive(session, root); + try (Arena arena = Arena.openConfined()) { + keepAlive(arena.scope(), root.scope()); // dummy } } @@ -345,33 +319,57 @@ private void kickGC() { } @DataProvider - static Object[][] cleaners() { + static Object[][] drops() { return new Object[][] { - { (Supplier)() -> null, UnaryOperator.identity() }, - { (Supplier)Cleaner::create, UnaryOperator.identity() }, - { (Supplier)CleanerFactory::cleaner, UnaryOperator.identity() }, - { (Supplier)Cleaner::create, (UnaryOperator)MemorySession::asNonCloseable }, - { (Supplier)CleanerFactory::cleaner, (UnaryOperator)MemorySession::asNonCloseable } + { (Supplier) Arena::openConfined}, + { (Supplier) Arena::openShared}, }; } - @DataProvider - static Object[][] sessions() { + private void keepAlive(SegmentScope child, SegmentScope parent) { + MemorySessionImpl parentImpl = (MemorySessionImpl) parent; + parentImpl.acquire0(); + addCloseAction(child, parentImpl::release0); + } + + private void addCloseAction(SegmentScope session, Runnable action) { + MemorySessionImpl sessionImpl = (MemorySessionImpl) session; + sessionImpl.addCloseAction(action); + } + + interface SessionSupplier extends Supplier { + + static void close(SegmentScope session) { + ((MemorySessionImpl)session).close(); + } + + static boolean isImplicit(SegmentScope session) { + return !((MemorySessionImpl)session).isCloseable(); + } + + static SessionSupplier ofImplicit() { + return SegmentScope::auto; + } + + static SessionSupplier ofArena(Supplier arenaSupplier) { + return () -> arenaSupplier.get().scope(); + } + } + + @DataProvider(name = "sharedSessions") + static Object[][] sharedSessions() { return new Object[][] { - { (Supplier) MemorySession::openConfined}, - { (Supplier) MemorySession::openShared}, - { (Supplier) MemorySession::openImplicit}, - { (Supplier) MemorySession::global}, - { (Supplier) () -> MemorySession.openConfined().asNonCloseable() }, - { (Supplier) () -> MemorySession.openShared().asNonCloseable() }, - { (Supplier) () -> MemorySession.openImplicit().asNonCloseable() }, - { (Supplier) () -> MemorySession.global().asNonCloseable()}, + { SessionSupplier.ofArena(Arena::openShared) }, + { SessionSupplier.ofImplicit() }, }; } - private void keepAlive(MemorySession child, MemorySession parent) { - MemorySessionImpl parentImpl = MemorySessionImpl.toSessionImpl(parent); - parentImpl.acquire0(); - child.addCloseAction(parentImpl::release0); + @DataProvider(name = "allSessions") + static Object[][] allSessions() { + return new Object[][] { + { SessionSupplier.ofArena(Arena::openConfined) }, + { SessionSupplier.ofArena(Arena::openShared) }, + { SessionSupplier.ofImplicit() }, + }; } } diff --git a/test/jdk/java/foreign/TestMismatch.java b/test/jdk/java/foreign/TestMismatch.java index acdf2046d319f..e7a381b9ce516 100644 --- a/test/jdk/java/foreign/TestMismatch.java +++ b/test/jdk/java/foreign/TestMismatch.java @@ -27,8 +27,8 @@ * @run testng TestMismatch */ -import java.lang.foreign.MemorySession; -import java.lang.invoke.VarHandle; +import java.lang.foreign.Arena; +import java.lang.foreign.SegmentScope; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicReference; @@ -36,6 +36,7 @@ import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.util.function.IntFunction; +import java.util.stream.Stream; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -45,16 +46,44 @@ public class TestMismatch { - 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) { for (int i = 0 ; i < segment.byteSize() ; i++) { - BYTE_HANDLE.set(segment.asSlice(i), (byte)i); + segment.set(ValueLayout.JAVA_BYTE, i, (byte)i); } return segment; } + @Test(dataProvider = "slices", expectedExceptions = IndexOutOfBoundsException.class) + public void testNegativeSrcFromOffset(MemorySegment s1, MemorySegment s2) { + MemorySegment.mismatch(s1, -1, 0, s2, 0, 0); + } + + @Test(dataProvider = "slices", expectedExceptions = IndexOutOfBoundsException.class) + public void testNegativeDstFromOffset(MemorySegment s1, MemorySegment s2) { + MemorySegment.mismatch(s1, 0, 0, s2, -1, 0); + } + + @Test(dataProvider = "slices", expectedExceptions = IndexOutOfBoundsException.class) + public void testNegativeSrcToOffset(MemorySegment s1, MemorySegment s2) { + MemorySegment.mismatch(s1, 0, -1, s2, 0, 0); + } + + @Test(dataProvider = "slices", expectedExceptions = IndexOutOfBoundsException.class) + public void testNegativeDstToOffset(MemorySegment s1, MemorySegment s2) { + MemorySegment.mismatch(s1, 0, 0, s2, 0, -1); + } + + @Test(dataProvider = "slices", expectedExceptions = IndexOutOfBoundsException.class) + public void testNegativeSrcLength(MemorySegment s1, MemorySegment s2) { + MemorySegment.mismatch(s1, 3, 2, s2, 0, 0); + } + + @Test(dataProvider = "slices", expectedExceptions = IndexOutOfBoundsException.class) + public void testNegativeDstLength(MemorySegment s1, MemorySegment s2) { + MemorySegment.mismatch(s1, 0, 0, s2, 3, 2); + } + @Test(dataProvider = "slices") public void testSameValues(MemorySegment ss1, MemorySegment ss2) { out.format("testSameValues s1:%s, s2:%s\n", ss1, ss2); @@ -74,6 +103,26 @@ public void testSameValues(MemorySegment ss1, MemorySegment ss2) { } } + @Test(dataProvider = "slicesStatic") + public void testSameValuesStatic(SliceOffsetAndSize ss1, SliceOffsetAndSize ss2) { + out.format("testSameValuesStatic s1:%s, s2:%s\n", ss1, ss2); + MemorySegment s1 = initializeSegment(ss1.toSlice()); + MemorySegment s2 = initializeSegment(ss2.toSlice()); + + for (long i = ss2.offset ; i < ss2.size ; i++) { + long bytes = i - ss2.offset; + long expected = (bytes == ss1.size) ? + -1 : Long.min(ss1.size, bytes); + assertEquals(MemorySegment.mismatch(ss1.segment, ss1.offset, ss1.endOffset(), ss2.segment, ss2.offset, i), expected); + } + for (long i = ss1.offset ; i < ss1.size ; i++) { + long bytes = i - ss1.offset; + long expected = (bytes == ss2.size) ? + -1 : Long.min(ss2.size, bytes); + assertEquals(MemorySegment.mismatch(ss2.segment, ss2.offset, ss2.endOffset(), ss1.segment, ss1.offset, i), expected); + } + } + @Test(dataProvider = "slices") public void testDifferentValues(MemorySegment s1, MemorySegment s2) { out.format("testDifferentValues s1:%s, s2:%s\n", s1, s2); @@ -82,7 +131,7 @@ public void testDifferentValues(MemorySegment s1, MemorySegment s2) { for (long i = s2.byteSize() -1 ; i >= 0; i--) { long expectedMismatchOffset = i; - BYTE_HANDLE.set(s2.asSlice(i), (byte) 0xFF); + s2.set(ValueLayout.JAVA_BYTE, i, (byte) 0xFF); if (s1.byteSize() == s2.byteSize()) { assertEquals(s1.mismatch(s2), expectedMismatchOffset); @@ -99,12 +148,32 @@ public void testDifferentValues(MemorySegment s1, MemorySegment s2) { } } + @Test(dataProvider = "slicesStatic") + public void testDifferentValuesStatic(SliceOffsetAndSize ss1, SliceOffsetAndSize ss2) { + out.format("testDifferentValues s1:%s, s2:%s\n", ss1, ss2); + + for (long i = ss2.size - 1 ; i >= 0; i--) { + if (i >= ss1.size) continue; + initializeSegment(ss1.toSlice()); + initializeSegment(ss2.toSlice()); + long expectedMismatchOffset = i; + ss2.toSlice().set(ValueLayout.JAVA_BYTE, i, (byte) 0xFF); + + for (long j = expectedMismatchOffset + 1 ; j < ss2.size ; j++) { + assertEquals(MemorySegment.mismatch(ss1.segment, ss1.offset, ss1.endOffset(), ss2.segment, ss2.offset, j + ss2.offset), expectedMismatchOffset); + } + for (long j = expectedMismatchOffset + 1 ; j < ss1.size ; j++) { + assertEquals(MemorySegment.mismatch(ss2.segment, ss2.offset, ss2.endOffset(), ss1.segment, ss1.offset, j + ss1.offset), expectedMismatchOffset); + } + } + } + @Test public void testEmpty() { var s1 = MemorySegment.ofArray(new byte[0]); assertEquals(s1.mismatch(s1), -1); - try (MemorySession session = MemorySession.openConfined()) { - var nativeSegment = MemorySegment.allocateNative(4, 4, session); + try (Arena arena = Arena.openConfined()) { + var nativeSegment = MemorySegment.allocateNative(4, 4, arena.scope());; var s2 = nativeSegment.asSlice(0, 0); assertEquals(s1.mismatch(s2), -1); assertEquals(s2.mismatch(s1), -1); @@ -115,9 +184,9 @@ public void testEmpty() { public void testLarge() { // skip if not on 64 bits if (ValueLayout.ADDRESS.byteSize() > 32) { - try (MemorySession session = MemorySession.openConfined()) { - var s1 = MemorySegment.allocateNative((long) Integer.MAX_VALUE + 10L, 8, session); - var s2 = MemorySegment.allocateNative((long) Integer.MAX_VALUE + 10L, 8, session); + try (Arena arena = Arena.openConfined()) { + var s1 = MemorySegment.allocateNative((long) Integer.MAX_VALUE + 10L, 8, arena.scope());; + var s2 = MemorySegment.allocateNative((long) Integer.MAX_VALUE + 10L, 8, arena.scope());; assertEquals(s1.mismatch(s1), -1); assertEquals(s1.mismatch(s2), -1); assertEquals(s2.mismatch(s1), -1); @@ -133,15 +202,20 @@ private void testLargeAcrossMaxBoundary(MemorySegment s1, MemorySegment s2) { for (long i = s2.byteSize() -1 ; i >= Integer.MAX_VALUE - 10L; i--) { var s3 = s1.asSlice(0, i); var s4 = s2.asSlice(0, i); + // instance assertEquals(s3.mismatch(s3), -1); assertEquals(s3.mismatch(s4), -1); assertEquals(s4.mismatch(s3), -1); + // static + assertEquals(MemorySegment.mismatch(s1, 0, s1.byteSize(), s1, 0, i), -1); + assertEquals(MemorySegment.mismatch(s2, 0, s1.byteSize(), s1, 0, i), -1); + assertEquals(MemorySegment.mismatch(s1, 0, s1.byteSize(), s2, 0, i), -1); } } private void testLargeMismatchAcrossMaxBoundary(MemorySegment s1, MemorySegment s2) { for (long i = s2.byteSize() -1 ; i >= Integer.MAX_VALUE - 10L; i--) { - BYTE_HANDLE.set(s2.asSlice(i), (byte) 0xFF); + s2.set(ValueLayout.JAVA_BYTE, i, (byte) 0xFF); long expectedMismatchOffset = i; assertEquals(s1.mismatch(s2), expectedMismatchOffset); assertEquals(s2.mismatch(s1), expectedMismatchOffset); @@ -154,9 +228,9 @@ private void testLargeMismatchAcrossMaxBoundary(MemorySegment s1, MemorySegment @Test public void testClosed() { MemorySegment s1, s2; - try (MemorySession session = MemorySession.openConfined()) { - s1 = MemorySegment.allocateNative(4, 1, session); - s2 = MemorySegment.allocateNative(4, 1, session); + try (Arena arena = Arena.openConfined()) { + s1 = MemorySegment.allocateNative(4, 1, arena.scope());; + s2 = MemorySegment.allocateNative(4, 1, arena.scope());; } assertThrows(ISE, () -> s1.mismatch(s1)); assertThrows(ISE, () -> s1.mismatch(s2)); @@ -165,8 +239,8 @@ public void testClosed() { @Test public void testThreadAccess() throws Exception { - try (MemorySession session = MemorySession.openConfined()) { - var segment = MemorySegment.allocateNative(4, 1, session); + try (Arena arena = Arena.openConfined()) { + var segment = MemorySegment.allocateNative(4, 1, arena.scope());; { AtomicReference exception = new AtomicReference<>(); Runnable action = () -> { @@ -207,7 +281,7 @@ public void testThreadAccess() throws Exception { } enum SegmentKind { - NATIVE(i -> MemorySegment.allocateNative(i, MemorySession.openImplicit())), + NATIVE(i -> MemorySegment.allocateNative(i, SegmentScope.auto())), ARRAY(i -> MemorySegment.ofArray(new byte[i])); final IntFunction segmentFactory; @@ -221,30 +295,48 @@ MemorySegment makeSegment(int elems) { } } - @DataProvider(name = "slices") - static Object[][] slices() { + record SliceOffsetAndSize(MemorySegment segment, long offset, long size) { + MemorySegment toSlice() { + return segment.asSlice(offset, size); + } + long endOffset() { + return offset + size; + } + }; + + @DataProvider(name = "slicesStatic") + static Object[][] slicesStatic() { int[] sizes = { 16, 8, 1 }; - List aSlices = new ArrayList<>(); - List bSlices = new ArrayList<>(); - for (List slices : List.of(aSlices, bSlices)) { + List aSliceOffsetAndSizes = new ArrayList<>(); + List bSliceOffsetAndSizes = new ArrayList<>(); + for (List slices : List.of(aSliceOffsetAndSizes, bSliceOffsetAndSizes)) { for (SegmentKind kind : SegmentKind.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(slice); + slices.add(new SliceOffsetAndSize(segment, index, size)); } } } } - assert aSlices.size() == bSlices.size(); - Object[][] sliceArray = new Object[aSlices.size() * bSlices.size()][]; - for (int i = 0 ; i < aSlices.size() ; i++) { - for (int j = 0 ; j < bSlices.size() ; j++) { - sliceArray[i * aSlices.size() + j] = new Object[] { aSlices.get(i), bSlices.get(j) }; + assert aSliceOffsetAndSizes.size() == bSliceOffsetAndSizes.size(); + Object[][] sliceArray = new Object[aSliceOffsetAndSizes.size() * bSliceOffsetAndSizes.size()][]; + for (int i = 0 ; i < aSliceOffsetAndSizes.size() ; i++) { + for (int j = 0 ; j < bSliceOffsetAndSizes.size() ; j++) { + sliceArray[i * aSliceOffsetAndSizes.size() + j] = new Object[] { aSliceOffsetAndSizes.get(i), bSliceOffsetAndSizes.get(j) }; } } return sliceArray; } + + @DataProvider(name = "slices") + static Object[][] slices() { + Object[][] slicesStatic = slicesStatic(); + return Stream.of(slicesStatic) + .map(arr -> new Object[]{ + ((SliceOffsetAndSize) arr[0]).toSlice(), + ((SliceOffsetAndSize) arr[1]).toSlice() + }).toArray(Object[][]::new); + } } diff --git a/test/jdk/java/foreign/TestNULLAddress.java b/test/jdk/java/foreign/TestNULLAddress.java index 93d9fe7ab6b9f..803b5d7fed5f5 100644 --- a/test/jdk/java/foreign/TestNULLAddress.java +++ b/test/jdk/java/foreign/TestNULLAddress.java @@ -32,20 +32,27 @@ import org.testng.annotations.Test; -import java.lang.foreign.Addressable; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryAddress; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SymbolLookup; +import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; +import static org.testng.Assert.*; + public class TestNULLAddress { + static { + System.loadLibrary("Null"); + } + static final Linker LINKER = Linker.nativeLinker(); @Test(expectedExceptions = IllegalArgumentException.class) public void testNULLLinking() { LINKER.downcallHandle( - MemoryAddress.NULL, + MemorySegment.NULL, FunctionDescriptor.ofVoid()); } @@ -53,16 +60,22 @@ public void testNULLLinking() { public void testNULLVirtual() throws Throwable { MethodHandle mh = LINKER.downcallHandle( FunctionDescriptor.ofVoid()); - mh.invokeExact((Addressable)MemoryAddress.NULL); + mh.invokeExact(MemorySegment.NULL); } - @Test(expectedExceptions = IllegalArgumentException.class) - public void testNULLgetString() { - MemoryAddress.NULL.getUtf8String(0); + @Test + public void testNULLReturn_unbounded() throws Throwable { + MethodHandle mh = LINKER.downcallHandle(SymbolLookup.loaderLookup().find("get_null").get(), + FunctionDescriptor.of(ValueLayout.ADDRESS.asUnbounded())); + MemorySegment ret = (MemorySegment)mh.invokeExact(); + assertTrue(ret.equals(MemorySegment.NULL)); } - @Test(expectedExceptions = IllegalArgumentException.class) - public void testNULLsetString() { - MemoryAddress.NULL.setUtf8String(0, "hello"); + @Test + public void testNULLReturn_plain() throws Throwable { + MethodHandle mh = LINKER.downcallHandle(SymbolLookup.loaderLookup().find("get_null").get(), + FunctionDescriptor.of(ValueLayout.ADDRESS)); + MemorySegment ret = (MemorySegment)mh.invokeExact(); + assertTrue(ret.equals(MemorySegment.NULL)); } } diff --git a/test/jdk/java/foreign/TestNative.java b/test/jdk/java/foreign/TestNative.java index 4f6aa24a3e4a2..672d6d7db2ea1 100644 --- a/test/jdk/java/foreign/TestNative.java +++ b/test/jdk/java/foreign/TestNative.java @@ -29,11 +29,11 @@ * @run testng/othervm --enable-native-access=ALL-UNNAMED TestNative */ -import java.lang.foreign.MemoryAddress; +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemoryLayout.PathElement; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.SequenceLayout; import java.lang.foreign.ValueLayout; import org.testng.annotations.DataProvider; @@ -112,7 +112,7 @@ static void checkBytes(MemorySegment base, SequenceLayout lay for (long i = 0 ; i < nelems ; i++) { Object handleValue = handleExtractor.apply(base, i); Object bufferValue = nativeBufferExtractor.apply(z, (int)i); - Object rawValue = nativeRawExtractor.apply(base.address().toRawLongValue(), (int)i); + Object rawValue = nativeRawExtractor.apply(base.address(), (int)i); if (handleValue instanceof Number) { assertEquals(((Number)handleValue).longValue(), i); assertEquals(((Number)bufferValue).longValue(), i); @@ -145,8 +145,8 @@ static void checkBytes(MemorySegment base, SequenceLayout lay @Test(dataProvider="nativeAccessOps") public void testNativeAccess(Consumer checker, Consumer initializer, SequenceLayout seq) { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(seq, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(seq, arena.scope());; initializer.accept(segment); checker.accept(segment); } @@ -155,8 +155,8 @@ public void testNativeAccess(Consumer checker, Consumer bufferFunction, int elemSize) { int capacity = (int)doubles.byteSize(); - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(doubles, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(doubles, arena.scope());; ByteBuffer bb = segment.asByteBuffer(); Buffer buf = bufferFunction.apply(bb); int expected = capacity / elemSize; @@ -167,30 +167,30 @@ public void testNativeCapacity(Function bufferFunction, int @Test public void testDefaultAccessModes() { - MemoryAddress addr = allocateMemory(12); - try (MemorySession session = MemorySession.openConfined()) { - session.addCloseAction(() -> freeMemory(addr)); - MemorySegment mallocSegment = MemorySegment.ofAddress(addr, 12, session); + MemorySegment addr = allocateMemory(12); + try (Arena arena = Arena.openConfined()) { + MemorySegment mallocSegment = MemorySegment.ofAddress(addr.address(), 12, + arena.scope(), () -> freeMemory(addr)); assertFalse(mallocSegment.isReadOnly()); } } @Test public void testMallocSegment() { - MemoryAddress addr = allocateMemory(12); + MemorySegment addr = allocateMemory(12); MemorySegment mallocSegment = null; - try (MemorySession session = MemorySession.openConfined()) { - session.addCloseAction(() -> freeMemory(addr)); - mallocSegment = MemorySegment.ofAddress(addr, 12, session); + try (Arena arena = Arena.openConfined()) { + mallocSegment = MemorySegment.ofAddress(addr.address(), 12, + arena.scope(), () -> freeMemory(addr)); assertEquals(mallocSegment.byteSize(), 12); //free here } - assertTrue(!mallocSegment.session().isAlive()); + assertTrue(!mallocSegment.scope().isAlive()); } @Test public void testAddressAccess() { - MemoryAddress addr = allocateMemory(4); + MemorySegment addr = allocateMemory(4); addr.set(JAVA_INT, 0, 42); assertEquals(addr.get(JAVA_INT, 0), 42); freeMemory(addr); @@ -198,9 +198,9 @@ public void testAddressAccess() { @Test(expectedExceptions = IllegalArgumentException.class) public void testBadResize() { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(4, 1, session); - MemorySegment.ofAddress(segment.address(), -1, MemorySession.global()); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(4, 1, arena.scope());; + MemorySegment.ofAddress(segment.address(), -1, SegmentScope.global()); } } diff --git a/test/jdk/java/foreign/TestNulls.java b/test/jdk/java/foreign/TestNulls.java index 3c79d4cff6d6a..c72b5fefd61ad 100644 --- a/test/jdk/java/foreign/TestNulls.java +++ b/test/jdk/java/foreign/TestNulls.java @@ -32,12 +32,14 @@ */ import java.lang.foreign.*; + import jdk.internal.ref.CleanerFactory; import org.testng.annotations.DataProvider; import org.testng.annotations.NoInjection; import org.testng.annotations.Test; import java.lang.constant.Constable; +import java.lang.foreign.SegmentScope; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -79,8 +81,8 @@ public class TestNulls { static final Class[] CLASSES = new Class[] { + Arena.class, MemorySegment.class, - MemoryAddress.class, MemoryLayout.class, MemoryLayout.PathElement.class, SequenceLayout.class, @@ -95,19 +97,19 @@ public class TestNulls { ValueLayout.OfDouble.class, ValueLayout.OfAddress.class, GroupLayout.class, - Addressable.class, Linker.class, VaList.class, VaList.Builder.class, FunctionDescriptor.class, SegmentAllocator.class, - MemorySession.class, + SegmentScope.class, SymbolLookup.class }; static final Set EXCLUDE_LIST = Set.of( - "java.lang.foreign.MemorySession/openConfined(java.lang.ref.Cleaner)/0/0", - "java.lang.foreign.MemorySession/openShared(java.lang.ref.Cleaner)/0/0", + "java.lang.foreign.MemorySegment/ofAddress(long,long,java.lang.foreign.SegmentScope,java.lang.Runnable)/3/0", + "java.lang.foreign.MemorySegment.MemorySession/openConfined(java.lang.ref.Cleaner)/0/0", + "java.lang.foreign.MemorySegment.MemorySession/openShared(java.lang.ref.Cleaner)/0/0", "java.lang.foreign.MemoryLayout/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", "java.lang.foreign.SequenceLayout/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", "java.lang.foreign.ValueLayout/withAttribute(java.lang.String,java.lang.constant.Constable)/1/0", @@ -162,8 +164,6 @@ static void addDefaultMapping(Class carrier, Z value) { addDefaultMapping(Charset.class, Charset.defaultCharset()); addDefaultMapping(Consumer.class, x -> {}); addDefaultMapping(MethodType.class, MethodType.methodType(void.class)); - addDefaultMapping(MemoryAddress.class, MemoryAddress.ofLong(1)); - addDefaultMapping(Addressable.class, MemoryAddress.ofLong(1)); addDefaultMapping(MemoryLayout.class, ValueLayout.JAVA_INT); addDefaultMapping(ValueLayout.class, ValueLayout.JAVA_INT); addDefaultMapping(ValueLayout.OfAddress.class, ValueLayout.ADDRESS); @@ -183,7 +183,8 @@ static void addDefaultMapping(Class carrier, Z value) { addDefaultMapping(Linker.class, Linker.nativeLinker()); addDefaultMapping(VaList.class, VaListHelper.vaList); addDefaultMapping(VaList.Builder.class, VaListHelper.vaListBuilder); - addDefaultMapping(MemorySession.class, MemorySession.openShared()); + addDefaultMapping(Arena.class, Arena.openConfined()); + addDefaultMapping(SegmentScope.class, SegmentScope.auto()); addDefaultMapping(SegmentAllocator.class, SegmentAllocator.prefixAllocator(MemorySegment.ofArray(new byte[10]))); addDefaultMapping(Supplier.class, () -> null); addDefaultMapping(ClassLoader.class, TestNulls.class.getClassLoader()); @@ -198,7 +199,7 @@ static class VaListHelper { vaList = VaList.make(b -> { builderRef.set(b); b.addVarg(JAVA_LONG, 42L); - }, MemorySession.openImplicit()); + }, SegmentScope.auto()); vaListBuilder = builderRef.get(); } } diff --git a/test/jdk/java/foreign/TestOfBufferIssue.java b/test/jdk/java/foreign/TestOfBufferIssue.java new file mode 100644 index 0000000000000..df8d62ccf201f --- /dev/null +++ b/test/jdk/java/foreign/TestOfBufferIssue.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +import org.testng.annotations.*; + +import java.lang.foreign.MemorySegment; +import java.nio.CharBuffer; + +import static org.testng.Assert.*; + +/* + * @test + * @bug 8294621 + * @summary test that StringCharBuffer is not accepted by MemorySegment::ofBuffer + * @enablePreview + * @run testng TestOfBufferIssue + */ + +public class TestOfBufferIssue { + + @Test + public void ensure8294621Fixed() { + try { + final CharBuffer cb = CharBuffer.wrap("Hello"); + MemorySegment src2 = MemorySegment.ofBuffer(cb); + fail("A StringCharBuffer is not allowed as an argument."); + } catch (IllegalArgumentException iae) { + // Ignored. Happy path + } + } + +} diff --git a/test/jdk/java/foreign/TestScopedOperations.java b/test/jdk/java/foreign/TestScopedOperations.java index fa15e48ce0a1d..986703a02a52c 100644 --- a/test/jdk/java/foreign/TestScopedOperations.java +++ b/test/jdk/java/foreign/TestScopedOperations.java @@ -28,13 +28,14 @@ * @run testng/othervm --enable-native-access=ALL-UNNAMED TestScopedOperations */ -import java.lang.foreign.MemoryAddress; +import java.lang.foreign.Arena; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.SegmentAllocator; import java.lang.foreign.VaList; import java.lang.foreign.ValueLayout; + import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -73,9 +74,9 @@ public class TestScopedOperations { @Test(dataProvider = "scopedOperations") public void testOpAfterClose(String name, ScopedOperation scopedOperation) { - MemorySession session = MemorySession.openConfined(); - Z obj = scopedOperation.apply(session); - session.close(); + Arena arena = Arena.openConfined(); + Z obj = scopedOperation.apply(arena.scope()); + arena.close(); try { scopedOperation.accept(obj); fail(); @@ -86,8 +87,8 @@ public void testOpAfterClose(String name, ScopedOperation scopedOperation @Test(dataProvider = "scopedOperations") public void testOpOutsideConfinement(String name, ScopedOperation scopedOperation) { - try (MemorySession session = MemorySession.openConfined()) { - Z obj = scopedOperation.apply(session); + try (Arena arena = Arena.openConfined()) { + Z obj = scopedOperation.apply(arena.scope()); AtomicReference failed = new AtomicReference<>(); Thread t = new Thread(() -> { try { @@ -110,9 +111,7 @@ public void testOpOutsideConfinement(String name, ScopedOperation scopedO static { // session operations - ScopedOperation.ofScope(session -> session.addCloseAction(() -> { - }), "MemorySession::addCloseAction"); - ScopedOperation.ofScope(session -> MemorySegment.allocateNative(100, session), "MemorySegment::allocateNative"); + ScopedOperation.ofScope(session -> MemorySegment.allocateNative(100, session), "MemorySession::allocate");; ScopedOperation.ofScope(session -> { try (FileChannel fileChannel = FileChannel.open(tempPath, StandardOpenOption.READ, StandardOpenOption.WRITE)) { fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 10L, session); @@ -121,16 +120,13 @@ public void testOpOutsideConfinement(String name, ScopedOperation scopedO } }, "FileChannel::map"); ScopedOperation.ofScope(session -> VaList.make(b -> b.addVarg(JAVA_INT, 42), session), "VaList::make"); - ScopedOperation.ofScope(session -> VaList.ofAddress(MemoryAddress.ofLong(42), session), "VaList::make"); - ScopedOperation.ofScope(SegmentAllocator::newNativeArena, "SegmentAllocator::arenaAllocator"); + ScopedOperation.ofScope(session -> VaList.ofAddress(42, session), "VaList::make"); // segment operations ScopedOperation.ofSegment(s -> s.toArray(JAVA_BYTE), "MemorySegment::toArray(BYTE)"); - ScopedOperation.ofSegment(MemorySegment::address, "MemorySegment::address"); 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"); // valist operations - 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"); @@ -165,13 +161,13 @@ static Object[][] scopedOperations() { return scopedOperations.stream().map(op -> new Object[] { op.name, op }).toArray(Object[][]::new); } - static class ScopedOperation implements Consumer, Function { + static class ScopedOperation implements Consumer, Function { - final Function factory; + final Function factory; final Consumer operation; final String name; - private ScopedOperation(Function factory, Consumer operation, String name) { + private ScopedOperation(Function factory, Consumer operation, String name) { this.factory = factory; this.operation = operation; this.name = name; @@ -183,15 +179,15 @@ public void accept(X obj) { } @Override - public X apply(MemorySession session) { + public X apply(SegmentScope session) { return factory.apply(session); } - static void of(Function factory, Consumer consumer, String name) { + static void of(Function factory, Consumer consumer, String name) { scopedOperations.add(new ScopedOperation<>(factory, consumer, name)); } - static void ofScope(Consumer scopeConsumer, String name) { + static void ofScope(Consumer scopeConsumer, String name) { scopedOperations.add(new ScopedOperation<>(Function.identity(), scopeConsumer, name)); } @@ -228,7 +224,7 @@ enum SegmentFactory { throw new AssertionError(ex); } }), - UNSAFE(session -> MemorySegment.ofAddress(MemoryAddress.NULL, 10, session)); + UNSAFE(session -> MemorySegment.ofAddress(0, 10, session)); static { try { @@ -240,20 +236,19 @@ enum SegmentFactory { } } - final Function segmentFactory; + final Function segmentFactory; - SegmentFactory(Function segmentFactory) { + SegmentFactory(Function segmentFactory) { this.segmentFactory = segmentFactory; } } enum AllocatorFactory { - ARENA_BOUNDED(session -> SegmentAllocator.newNativeArena(1000, session)), - ARENA_UNBOUNDED(SegmentAllocator::newNativeArena); + NATIVE_ALLOCATOR(SegmentAllocator::nativeAllocator); - final Function allocatorFactory; + final Function allocatorFactory; - AllocatorFactory(Function allocatorFactory) { + AllocatorFactory(Function allocatorFactory) { this.allocatorFactory = allocatorFactory; } } diff --git a/test/jdk/java/foreign/TestSegmentAllocators.java b/test/jdk/java/foreign/TestSegmentAllocators.java index a5cca846dc592..94dd265563ee1 100644 --- a/test/jdk/java/foreign/TestSegmentAllocators.java +++ b/test/jdk/java/foreign/TestSegmentAllocators.java @@ -32,6 +32,7 @@ import org.testng.annotations.*; +import java.lang.foreign.SegmentScope; import java.lang.invoke.VarHandle; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -46,8 +47,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiFunction; import java.util.function.Function; -import java.util.stream.IntStream; -import java.util.stream.LongStream; import static org.testng.Assert.*; @@ -69,13 +68,13 @@ public void testAllocation(Z value, AllocationFactory for (L alignedLayout : layouts) { List addressList = new ArrayList<>(); int elems = ELEMS / ((int)alignedLayout.byteAlignment() / (int)layout.byteAlignment()); - MemorySession[] sessions = { - MemorySession.openConfined(), - MemorySession.openShared() + Arena[] arenas = { + Arena.openConfined(), + Arena.openShared() }; - for (MemorySession session : sessions) { - try (session) { - SegmentAllocator allocator = allocationFactory.allocator(alignedLayout.byteSize() * ELEMS, session); + for (Arena arena : arenas) { + try (arena) { + SegmentAllocator allocator = allocationFactory.allocator(alignedLayout.byteSize() * ELEMS, arena); for (int i = 0; i < elems; i++) { MemorySegment address = allocationFunction.allocate(allocator, alignedLayout, value); assertEquals(address.byteSize(), alignedLayout.byteSize()); @@ -87,16 +86,14 @@ public void testAllocation(Z value, AllocationFactory try { allocationFunction.allocate(allocator, alignedLayout, value); assertFalse(isBound); - } catch (OutOfMemoryError ex) { + } catch (IndexOutOfBoundsException ex) { //failure is expected if bound assertTrue(isBound); } } - if (allocationFactory != AllocationFactory.IMPLICIT_ALLOCATOR) { - // addresses should be invalid now - for (MemorySegment address : addressList) { - assertFalse(address.session().isAlive()); - } + // addresses should be invalid now + for (MemorySegment address : addressList) { + assertFalse(address.scope().isAlive()); } } } @@ -106,40 +103,27 @@ public void testAllocation(Z value, AllocationFactory @Test public void testBigAllocationInUnboundedSession() { - try (MemorySession session = MemorySession.openConfined()) { - SegmentAllocator allocator = SegmentAllocator.newNativeArena(session); + try (Arena arena = Arena.openConfined()) { for (int i = 8 ; i < SIZE_256M ; i *= 8) { + SegmentAllocator allocator = SegmentAllocator.slicingAllocator(arena.allocate(i * 2 + 1)); MemorySegment address = allocator.allocate(i, i); //check size assertEquals(address.byteSize(), i); //check alignment - assertEquals(address.address().toRawLongValue() % i, 0); + assertEquals(address.address() % i, 0); } } } @Test public void testTooBigForBoundedArena() { - try (MemorySession session = MemorySession.openConfined()) { - SegmentAllocator allocator = SegmentAllocator.newNativeArena(10, session); - assertThrows(OutOfMemoryError.class, () -> allocator.allocate(12)); - allocator.allocate(5); // ok - } - } - - @Test - public void testBiggerThanBlockForBoundedArena() { - try (MemorySession session = MemorySession.openConfined()) { - SegmentAllocator allocator = SegmentAllocator.newNativeArena(4 * 1024 * 2, session); - allocator.allocate(4 * 1024 + 1); // should be ok + try (Arena arena = Arena.openConfined()) { + SegmentAllocator allocator = SegmentAllocator.slicingAllocator(arena.allocate(10)); + assertThrows(IndexOutOfBoundsException.class, () -> allocator.allocate(12)); + allocator.allocate(5); } } - @Test(expectedExceptions = IllegalArgumentException.class) - public void testBadUnboundedArenaSize() { - SegmentAllocator.newNativeArena( -1, MemorySession.global()); - } - @Test(dataProvider = "allocators", expectedExceptions = IllegalArgumentException.class) public void testBadAllocationSize(SegmentAllocator allocator) { allocator.allocate(-1); @@ -167,8 +151,9 @@ public void testBadAllocationArrayNegSize(SegmentAllocator allocator) { @Test(expectedExceptions = OutOfMemoryError.class) public void testBadArenaNullReturn() { - SegmentAllocator segmentAllocator = SegmentAllocator.newNativeArena(MemorySession.openImplicit()); - segmentAllocator.allocate(Long.MAX_VALUE, 2); + try (Arena arena = Arena.openConfined()) { + arena.allocate(Long.MAX_VALUE, 2); + } } @Test @@ -176,7 +161,7 @@ public void testArrayAllocateDelegation() { AtomicInteger calls = new AtomicInteger(); SegmentAllocator allocator = new SegmentAllocator() { @Override - public MemorySegment allocate(long bytesSize, long bytesAlignment) { + public MemorySegment allocate(long bytesSize, long byteAlignment) { return null; } @@ -201,8 +186,9 @@ public void testStringAllocateDelegation() { AtomicInteger calls = new AtomicInteger(); SegmentAllocator allocator = new SegmentAllocator() { @Override - public MemorySegment allocate(long bytesSize, long bytesAlignment) { - return MemorySegment.allocateNative(bytesSize, bytesAlignment, MemorySession.openImplicit()); + + public MemorySegment allocate(long byteSize, long byteAlignment) { + return MemorySegment.allocateNative(byteSize, byteAlignment, SegmentScope.auto()); } @Override @@ -219,13 +205,13 @@ public MemorySegment allocate(long size) { @Test(dataProvider = "arrayAllocations") public void testArray(AllocationFactory allocationFactory, ValueLayout layout, AllocationFunction allocationFunction, ToArrayHelper arrayHelper) { Z arr = arrayHelper.array(); - MemorySession[] sessions = { - MemorySession.openConfined(), - MemorySession.openShared() + Arena[] arenas = { + Arena.openConfined(), + Arena.openShared() }; - for (MemorySession session : sessions) { - try (session) { - SegmentAllocator allocator = allocationFactory.allocator(100, session); + for (Arena arena : arenas) { + try (arena) { + SegmentAllocator allocator = allocationFactory.allocator(100, arena); MemorySegment address = allocationFunction.allocate(allocator, layout, arr); Z found = arrayHelper.toArray(address, layout); assertEquals(found, arr); @@ -259,7 +245,7 @@ static Object[][] scalarAllocations() { scalarAllocations.add(new Object[] { 42d, factory, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), (AllocationFunction.OfDouble) SegmentAllocator::allocate, (Function)l -> l.varHandle() }); - scalarAllocations.add(new Object[] { MemoryAddress.ofLong(42), factory, ValueLayout.ADDRESS.withOrder(ByteOrder.BIG_ENDIAN), + scalarAllocations.add(new Object[] { MemorySegment.ofAddress(42), factory, ValueLayout.ADDRESS.withOrder(ByteOrder.BIG_ENDIAN), (AllocationFunction.OfAddress) SegmentAllocator::allocate, (Function)l -> l.varHandle() }); @@ -282,7 +268,7 @@ static Object[][] scalarAllocations() { scalarAllocations.add(new Object[] { 42d, factory, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN), (AllocationFunction.OfDouble) SegmentAllocator::allocate, (Function)l -> l.varHandle() }); - scalarAllocations.add(new Object[] { MemoryAddress.ofLong(42), factory, ValueLayout.ADDRESS.withOrder(ByteOrder.BIG_ENDIAN), + scalarAllocations.add(new Object[] { MemorySegment.ofAddress(42), factory, ValueLayout.ADDRESS.withOrder(ByteOrder.BIG_ENDIAN), (AllocationFunction.OfAddress) SegmentAllocator::allocate, (Function)l -> l.varHandle() }); } @@ -350,7 +336,7 @@ interface OfInt extends AllocationFunction { } interface OfFloat extends AllocationFunction { } interface OfLong extends AllocationFunction { } interface OfDouble extends AllocationFunction { } - interface OfAddress extends AllocationFunction { } + interface OfAddress extends AllocationFunction { } interface OfByteArray extends AllocationFunction { } interface OfCharArray extends AllocationFunction { } @@ -362,21 +348,19 @@ interface OfDoubleArray extends AllocationFunction SegmentAllocator.newNativeArena(session)), - NATIVE_ALLOCATOR(false, (size, session) -> session), - IMPLICIT_ALLOCATOR(false, (size, session) -> SegmentAllocator.implicitAllocator()); + SLICING(true, (size, drop) -> SegmentAllocator.slicingAllocator(MemorySegment.allocateNative(size, drop.scope()))), + NATIVE_ALLOCATOR(false, (size, drop) -> SegmentAllocator.nativeAllocator(drop.scope())); private final boolean isBound; - private final BiFunction factory; + private final BiFunction factory; - AllocationFactory(boolean isBound, BiFunction factory) { + AllocationFactory(boolean isBound, BiFunction factory) { this.isBound = isBound; this.factory = factory; } - SegmentAllocator allocator(long size, MemorySession session) { - return factory.apply(size, session); + SegmentAllocator allocator(long size, Arena arena) { + return factory.apply(size, arena); } public boolean isBound() { @@ -492,42 +476,13 @@ public double[] toArray(MemorySegment segment, ValueLayout layout) { return found; } }; - - ToArrayHelper toAddressArray = new ToArrayHelper<>() { - @Override - public MemoryAddress[] array() { - return switch ((int) ValueLayout.ADDRESS.byteSize()) { - case 4 -> wrap(toIntArray.array()); - case 8 -> wrap(toLongArray.array()); - default -> throw new IllegalStateException("Cannot get here"); - }; - } - - @Override - public MemoryAddress[] toArray(MemorySegment segment, ValueLayout layout) { - return switch ((int)layout.byteSize()) { - case 4 -> wrap(toIntArray.toArray(segment, layout)); - case 8 -> wrap(toLongArray.toArray(segment, layout)); - default -> throw new IllegalStateException("Cannot get here"); - }; - } - - private MemoryAddress[] wrap(int[] ints) { - return IntStream.of(ints).mapToObj(MemoryAddress::ofLong).toArray(MemoryAddress[]::new); - } - - private MemoryAddress[] wrap(long[] ints) { - return LongStream.of(ints).mapToObj(MemoryAddress::ofLong).toArray(MemoryAddress[]::new); - } - }; } @DataProvider(name = "allocators") static Object[][] allocators() { return new Object[][] { - { SegmentAllocator.implicitAllocator() }, - { SegmentAllocator.newNativeArena(MemorySession.global()) }, - { SegmentAllocator.prefixAllocator(MemorySegment.allocateNative(10, MemorySession.global())) }, + { SegmentAllocator.nativeAllocator(SegmentScope.global()) }, + { SegmentAllocator.prefixAllocator(MemorySegment.allocateNative(10, SegmentScope.global())) }, }; } } diff --git a/test/jdk/java/foreign/TestSegmentCopy.java b/test/jdk/java/foreign/TestSegmentCopy.java index 0a3de9a24767a..c82d4e5ded8c6 100644 --- a/test/jdk/java/foreign/TestSegmentCopy.java +++ b/test/jdk/java/foreign/TestSegmentCopy.java @@ -29,7 +29,7 @@ */ import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; @@ -144,7 +144,7 @@ void check(SegmentSlice slice, int index, int val) { static class SegmentSlice { enum Kind { - NATIVE(i -> MemorySegment.allocateNative(i, MemorySession.openImplicit())), + NATIVE(i -> MemorySegment.allocateNative(i, SegmentScope.auto())), ARRAY(i -> MemorySegment.ofArray(new byte[i])); final IntFunction segmentFactory; diff --git a/test/jdk/java/foreign/TestSegmentOffset.java b/test/jdk/java/foreign/TestSegmentOffset.java index bb4263b66d948..f3adcb67c89f1 100644 --- a/test/jdk/java/foreign/TestSegmentOffset.java +++ b/test/jdk/java/foreign/TestSegmentOffset.java @@ -28,9 +28,11 @@ */ import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; + import org.testng.annotations.DataProvider; import org.testng.annotations.Test; + +import java.lang.foreign.SegmentScope; import java.util.ArrayList; import java.util.List; import java.util.function.IntFunction; @@ -79,7 +81,7 @@ public void testOffset(SegmentSlice s1, SegmentSlice s2) { static class SegmentSlice { enum Kind { - NATIVE(i -> MemorySegment.allocateNative(i, MemorySession.openConfined())), + NATIVE(i -> MemorySegment.allocateNative(i, SegmentScope.auto())), ARRAY(i -> MemorySegment.ofArray(new byte[i])); final IntFunction segmentFactory; diff --git a/test/jdk/java/foreign/TestSegmentOverlap.java b/test/jdk/java/foreign/TestSegmentOverlap.java index 1c08240dbde73..bb0e07da81713 100644 --- a/test/jdk/java/foreign/TestSegmentOverlap.java +++ b/test/jdk/java/foreign/TestSegmentOverlap.java @@ -29,6 +29,7 @@ import java.io.File; import java.io.IOException; +import java.lang.foreign.SegmentScope; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; @@ -36,7 +37,7 @@ import java.util.List; import java.util.function.Supplier; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; + import org.testng.annotations.Test; import org.testng.annotations.DataProvider; import static java.lang.System.out; @@ -61,10 +62,10 @@ public class TestSegmentOverlap { @DataProvider(name = "segmentFactories") public Object[][] segmentFactories() { List> l = List.of( - () -> MemorySegment.allocateNative(16, MemorySession.openConfined()), + () -> MemorySegment.allocateNative(16, SegmentScope.auto()), () -> { try (FileChannel fileChannel = FileChannel.open(tempPath, StandardOpenOption.READ, StandardOpenOption.WRITE)) { - return fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 16L, MemorySession.openConfined()); + return fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 16L, SegmentScope.auto()); } catch (IOException e) { throw new RuntimeException(e); } @@ -98,10 +99,10 @@ public void testIdentical(Supplier segmentSupplier) { var s2 = s1.asReadOnly(); out.format("testIdentical s1:%s, s2:%s\n", s1, s2); assertEquals(s1.asOverlappingSlice(s2).get().byteSize(), s1.byteSize()); - assertEquals(s1.asOverlappingSlice(s2).get().session(), s1.session()); + assertEquals(s1.asOverlappingSlice(s2).get().scope(), s1.scope()); assertEquals(s2.asOverlappingSlice(s1).get().byteSize(), s2.byteSize()); - assertEquals(s2.asOverlappingSlice(s1).get().session(), s2.session()); + assertEquals(s2.asOverlappingSlice(s1).get().scope(), s2.scope()); if (s1.isNative()) { assertEquals(s1.asOverlappingSlice(s2).get().address(), s1.address()); @@ -117,13 +118,13 @@ public void testSlices(Supplier segmentSupplier) { 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).get().byteSize(), s1.byteSize() - offset); - assertEquals(s1.asOverlappingSlice(slice).get().session(), s1.session()); + assertEquals(s1.asOverlappingSlice(slice).get().scope(), s1.scope()); assertEquals(slice.asOverlappingSlice(s1).get().byteSize(), slice.byteSize()); - assertEquals(slice.asOverlappingSlice(s1).get().session(), slice.session()); + assertEquals(slice.asOverlappingSlice(s1).get().scope(), slice.scope()); if (s1.isNative()) { - assertEquals(s1.asOverlappingSlice(slice).get().address(), s1.address().addOffset(offset)); + assertEquals(s1.asOverlappingSlice(slice).get().address(), s1.address() + offset); assertEquals(slice.asOverlappingSlice(s1).get().address(), slice.address()); } assertTrue(s2.asOverlappingSlice(slice).isEmpty()); @@ -131,7 +132,7 @@ public void testSlices(Supplier segmentSupplier) { } enum OtherSegmentFactory { - NATIVE(() -> MemorySegment.allocateNative(16, MemorySession.openConfined())), + NATIVE(() -> MemorySegment.allocateNative(16, SegmentScope.auto())), HEAP(() -> MemorySegment.ofArray(new byte[]{16})); final Supplier factory; diff --git a/test/jdk/java/foreign/TestSegments.java b/test/jdk/java/foreign/TestSegments.java index 113c5039acdb5..3363a61134a07 100644 --- a/test/jdk/java/foreign/TestSegments.java +++ b/test/jdk/java/foreign/TestSegments.java @@ -28,9 +28,10 @@ * @run testng/othervm -Xmx4G -XX:MaxDirectMemorySize=1M --enable-native-access=ALL-UNNAMED TestSegments */ +import java.lang.foreign.Arena; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -39,7 +40,6 @@ import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Function; import java.util.function.IntFunction; import java.util.function.Supplier; @@ -50,43 +50,44 @@ public class TestSegments { @Test(dataProvider = "badSizeAndAlignments", expectedExceptions = IllegalArgumentException.class) public void testBadAllocateAlign(long size, long align) { - MemorySegment.allocateNative(size, align, MemorySession.openImplicit()); + MemorySegment.allocateNative(size, align, SegmentScope.auto()); } @Test public void testZeroLengthNativeSegment() { - try (MemorySession session = MemorySession.openConfined()) { + try (Arena arena = Arena.openConfined()) { + SegmentScope session = arena.scope(); var segment = MemorySegment.allocateNative(0, session); assertEquals(segment.byteSize(), 0); MemoryLayout seq = MemoryLayout.sequenceLayout(0, JAVA_INT); segment = MemorySegment.allocateNative(seq, session); assertEquals(segment.byteSize(), 0); - assertEquals(segment.address().toRawLongValue() % seq.byteAlignment(), 0); + assertEquals(segment.address() % seq.byteAlignment(), 0); segment = MemorySegment.allocateNative(0, 4, session); assertEquals(segment.byteSize(), 0); - assertEquals(segment.address().toRawLongValue() % 4, 0); - segment = MemorySegment.ofAddress(segment.address(), 0, session); - assertEquals(segment.byteSize(), 0); - assertEquals(segment.address().toRawLongValue() % 4, 0); + assertEquals(segment.address() % 4, 0); + MemorySegment rawAddress = MemorySegment.ofAddress(segment.address(), 0, session); + assertEquals(rawAddress.byteSize(), 0); + assertEquals(rawAddress.address() % 4, 0); } } @Test(expectedExceptions = { OutOfMemoryError.class, IllegalArgumentException.class }) public void testAllocateTooBig() { - MemorySegment.allocateNative(Long.MAX_VALUE, MemorySession.openImplicit()); + MemorySegment.allocateNative(Long.MAX_VALUE, SegmentScope.auto()); } @Test(expectedExceptions = OutOfMemoryError.class) public void testNativeAllocationTooBig() { - MemorySegment segment = MemorySegment.allocateNative(1024 * 1024 * 8 * 2, MemorySession.openImplicit()); // 2M + MemorySegment segment = MemorySegment.allocateNative(1024L * 1024 * 8 * 2, SegmentScope.auto()); // 2M } @Test public void testNativeSegmentIsZeroed() { VarHandle byteHandle = ValueLayout.JAVA_BYTE.arrayElementVarHandle(); - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(1000, 1, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(1000, 1, arena.scope()); for (long i = 0 ; i < segment.byteSize() ; i++) { assertEquals(0, (byte)byteHandle.get(segment, i)); } @@ -96,8 +97,8 @@ public void testNativeSegmentIsZeroed() { @Test public void testSlices() { VarHandle byteHandle = ValueLayout.JAVA_BYTE.arrayElementVarHandle(); - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(10, 1, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(10, 1, arena.scope()); //init for (byte i = 0 ; i < segment.byteSize() ; i++) { byteHandle.set(segment, (long)i, i); @@ -116,15 +117,14 @@ public void testSlices() { @Test public void testEqualsOffHeap() { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(100, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(100, arena.scope()); assertEquals(segment, segment.asReadOnly()); assertEquals(segment, segment.asSlice(0, 100)); assertNotEquals(segment, segment.asSlice(10, 90)); - assertNotEquals(segment, segment.asSlice(0, 90)); - assertEquals(segment, MemorySegment.ofAddress(segment.address(), 100, session.asNonCloseable())); - assertNotEquals(segment, MemorySegment.ofAddress(segment.address(), 100, MemorySession.global())); - MemorySegment segment2 = MemorySegment.allocateNative(100, session); + assertEquals(segment, segment.asSlice(0, 90)); + assertEquals(segment, MemorySegment.ofAddress(segment.address(), 100, SegmentScope.global())); + MemorySegment segment2 = MemorySegment.allocateNative(100, arena.scope()); assertNotEquals(segment, segment2); } } @@ -135,29 +135,48 @@ public void testEqualsOnHeap() { assertEquals(segment, segment.asReadOnly()); assertEquals(segment, segment.asSlice(0, 100)); assertNotEquals(segment, segment.asSlice(10, 90)); - assertNotEquals(segment, segment.asSlice(0, 90)); + assertEquals(segment, segment.asSlice(0, 90)); MemorySegment segment2 = MemorySegment.ofArray(new byte[100]); assertNotEquals(segment, segment2); } + @Test + public void testHashCodeOffHeap() { + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(100, arena.scope()); + assertEquals(segment.hashCode(), segment.asReadOnly().hashCode()); + assertEquals(segment.hashCode(), segment.asSlice(0, 100).hashCode()); + assertEquals(segment.hashCode(), segment.asSlice(0, 90).hashCode()); + assertEquals(segment.hashCode(), MemorySegment.ofAddress(segment.address(), 100, SegmentScope.global()).hashCode()); + } + } + + @Test + public void testHashCodeOnHeap() { + MemorySegment segment = MemorySegment.ofArray(new byte[100]); + assertEquals(segment.hashCode(), segment.asReadOnly().hashCode()); + assertEquals(segment.hashCode(), segment.asSlice(0, 100).hashCode()); + assertEquals(segment.hashCode(), segment.asSlice(0, 90).hashCode()); + } + @Test(expectedExceptions = IndexOutOfBoundsException.class) 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, MemorySession.openImplicit()); + long offset = (long)Integer.MAX_VALUE + (long)Integer.MAX_VALUE + 2L + 6L; // overflows to 6 when cast to int + MemorySegment memorySegment = MemorySegment.allocateNative(10, SegmentScope.auto()); 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, MemorySession.openImplicit()); + long offset = ((long)Integer.MIN_VALUE * 2L) + 6L; // underflows to 6 when cast to int + MemorySegment memorySegment = MemorySegment.allocateNative(10L, SegmentScope.auto()); memorySegment.get(JAVA_INT, offset); } @Test public void testSegmentOOBMessage() { try { - var segment = MemorySegment.allocateNative(10, MemorySession.global()); + var segment = MemorySegment.allocateNative(10, SegmentScope.global()); segment.getAtIndex(ValueLayout.JAVA_INT, 2); } catch (IndexOutOfBoundsException ex) { assertTrue(ex.getMessage().contains("Out of bound access")); @@ -170,13 +189,6 @@ public void testSegmentOOBMessage() { public void testAccessModesOfFactories(Supplier segmentSupplier) { MemorySegment segment = segmentSupplier.get(); assertFalse(segment.isReadOnly()); - tryClose(segment); - } - - static void tryClose(MemorySegment segment) { - if (segment.session().isCloseable()) { - segment.session().close(); - } } @DataProvider(name = "segmentFactories") @@ -189,45 +201,17 @@ public Object[][] segmentFactories() { () -> MemorySegment.ofArray(new int[] { 1, 2, 3, 4 }), () -> MemorySegment.ofArray(new long[] { 1l, 2l, 3l, 4l } ), () -> MemorySegment.ofArray(new short[] { 1, 2, 3, 4 } ), - () -> MemorySegment.allocateNative(4, MemorySession.openImplicit()), - () -> MemorySegment.allocateNative(4, 8, MemorySession.openImplicit()), - () -> MemorySegment.allocateNative(JAVA_INT, MemorySession.openImplicit()), - () -> MemorySegment.allocateNative(4, MemorySession.openImplicit()), - () -> MemorySegment.allocateNative(4, 8, MemorySession.openImplicit()), - () -> MemorySegment.allocateNative(JAVA_INT, MemorySession.openImplicit()) + () -> MemorySegment.allocateNative(4L, SegmentScope.auto()), + () -> MemorySegment.allocateNative(4L, 8, SegmentScope.auto()), + () -> MemorySegment.allocateNative(JAVA_INT, SegmentScope.auto()), + () -> MemorySegment.allocateNative(4L, SegmentScope.auto()), + () -> MemorySegment.allocateNative(4L, 8, SegmentScope.auto()), + () -> MemorySegment.allocateNative(JAVA_INT, SegmentScope.auto()) ); return l.stream().map(s -> new Object[] { s }).toArray(Object[][]::new); } - static class SegmentFactory { - final MemorySession session; - final Function segmentFunc; - - SegmentFactory(MemorySession session, Function segmentFunc) { - this.session = session; - this.segmentFunc = segmentFunc; - } - - public void tryClose() { - if (session.isCloseable()) { - session.close(); - } - } - - public MemorySegment segment() { - return segmentFunc.apply(session); - } - - static SegmentFactory ofArray(Supplier segmentSupplier) { - return new SegmentFactory(MemorySession.global(), (_ignored) -> segmentSupplier.get()); - } - - static SegmentFactory ofImplicitSession(Function segmentFunc) { - return new SegmentFactory(MemorySession.openImplicit(), segmentFunc); - } - } - @Test(dataProvider = "segmentFactories") public void testFill(Supplier segmentSupplier) { VarHandle byteHandle = ValueLayout.JAVA_BYTE.arrayElementVarHandle(); @@ -250,41 +234,19 @@ public void testFill(Supplier segmentSupplier) { assertEquals((byte) byteHandle.get(segment, l), (byte) ~value); } assertEquals((byte) byteHandle.get(segment, segment.byteSize() - 1L), value); - tryClose(segment); - } - } - - @Test(dataProvider = "segmentFactories") - public void testFillClosed(Supplier segmentSupplier) { - MemorySegment segment = segmentSupplier.get(); - tryClose(segment); - if (!segment.session().isAlive()) { - try { - segment.fill((byte) 0xFF); - fail(); - } catch (IllegalStateException ex) { - assertTrue(true); - } } } @Test(dataProvider = "segmentFactories") public void testNativeSegments(Supplier segmentSupplier) { MemorySegment segment = segmentSupplier.get(); - try { - segment.address(); - assertTrue(segment.isNative()); - } catch (UnsupportedOperationException exception) { - assertFalse(segment.isNative()); - } - tryClose(segment); + assertEquals(segment.isNative(), !segment.array().isPresent()); } @Test(dataProvider = "segmentFactories", expectedExceptions = UnsupportedOperationException.class) public void testFillIllegalAccessMode(Supplier segmentSupplier) { MemorySegment segment = segmentSupplier.get(); segment.asReadOnly().fill((byte) 0xFF); - tryClose(segment); } @Test(dataProvider = "segmentFactories") @@ -302,7 +264,7 @@ public void testFillThread(Supplier segmentSupplier) throws Excep thread.start(); thread.join(); - if (segment.session().ownerThread() != null) { + if (segment.scope().isAccessibleBy(Thread.currentThread())) { RuntimeException e = exception.get(); if (!(e instanceof IllegalStateException)) { throw e; @@ -310,7 +272,6 @@ public void testFillThread(Supplier segmentSupplier) throws Excep } else { assertNull(exception.get()); } - tryClose(segment); } @Test @@ -321,22 +282,11 @@ public void testFillEmpty() { } @Test(dataProvider = "heapFactories") - public void testBigHeapSegments(IntFunction heapSegmentFactory, int factor) { - int bigSize = (Integer.MAX_VALUE / factor) + 1; - MemorySegment segment = heapSegmentFactory.apply(bigSize); - assertTrue(segment.byteSize() > 0); - } - - @Test - public void testSegmentAccessorWithWrappedLifetime() { - MemorySession session = MemorySession.openConfined(); - MemorySession publicSession = session.asNonCloseable(); - assertEquals(session, publicSession); - MemorySegment segment = publicSession.allocate(100); - assertThrows(UnsupportedOperationException.class, publicSession::close); - assertThrows(UnsupportedOperationException.class, segment.session()::close); - session.close(); - assertFalse(publicSession.isAlive()); + public void testVirtualizedBaseAddress(IntFunction heapSegmentFactory, int factor) { + MemorySegment segment = heapSegmentFactory.apply(10); + assertEquals(segment.address(), 0); // base address should be zero (no leaking of impl details) + MemorySegment end = segment.asSlice(segment.byteSize(), 0); + assertEquals(end.address(), segment.byteSize()); // end address should be equal to segment byte size } @DataProvider(name = "badSizeAndAlignments") @@ -351,6 +301,7 @@ public Object[][] sizesAndAlignments() { @DataProvider(name = "heapFactories") public Object[][] heapFactories() { return new Object[][] { + { (IntFunction) size -> MemorySegment.ofArray(new byte[size]), 1 }, { (IntFunction) size -> MemorySegment.ofArray(new char[size]), 2 }, { (IntFunction) size -> MemorySegment.ofArray(new short[size]), 2 }, { (IntFunction) size -> MemorySegment.ofArray(new int[size]), 4 }, diff --git a/test/jdk/java/foreign/TestSharedAccess.java b/test/jdk/java/foreign/TestSharedAccess.java index e711d06e0c023..5c88b35a0780a 100644 --- a/test/jdk/java/foreign/TestSharedAccess.java +++ b/test/jdk/java/foreign/TestSharedAccess.java @@ -37,6 +37,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; + import org.testng.annotations.*; import static org.testng.Assert.*; @@ -48,8 +49,8 @@ public class TestSharedAccess { @Test public void testShared() throws Throwable { SequenceLayout layout = MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_INT); - try (MemorySession session = MemorySession.openShared()) { - MemorySegment s = MemorySegment.allocateNative(layout, session); + try (Arena arena = Arena.openShared()) { + MemorySegment s = MemorySegment.allocateNative(layout, arena.scope());; for (int i = 0 ; i < layout.elementCount() ; i++) { setInt(s.asSlice(i * 4), 42); } @@ -93,12 +94,12 @@ public void testShared() throws Throwable { @Test public void testSharedUnsafe() throws Throwable { - try (MemorySession session = MemorySession.openShared()) { - MemorySegment s = MemorySegment.allocateNative(4, 1, session); + try (Arena arena = Arena.openShared()) { + MemorySegment s = MemorySegment.allocateNative(4, 1, arena.scope());; setInt(s, 42); assertEquals(getInt(s), 42); List threads = new ArrayList<>(); - MemorySegment sharedSegment = MemorySegment.ofAddress(s.address(), s.byteSize(), session); + MemorySegment sharedSegment = MemorySegment.ofAddress(s.address(), s.byteSize(), arena.scope()); for (int i = 0 ; i < 1000 ; i++) { threads.add(new Thread(() -> { assertEquals(getInt(sharedSegment), 42); @@ -120,8 +121,8 @@ public void testOutsideConfinementThread() throws Throwable { CountDownLatch a = new CountDownLatch(1); CountDownLatch b = new CountDownLatch(1); CompletableFuture r; - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment s1 = MemorySegment.allocateNative(MemoryLayout.sequenceLayout(2, ValueLayout.JAVA_INT), session); + try (Arena arena = Arena.openConfined()) { + MemorySegment s1 = MemorySegment.allocateNative(MemoryLayout.sequenceLayout(2, ValueLayout.JAVA_INT), arena.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 76ce186d4088c..9cceaee9b75ff 100644 --- a/test/jdk/java/foreign/TestSlices.java +++ b/test/jdk/java/foreign/TestSlices.java @@ -22,9 +22,9 @@ * */ +import java.lang.foreign.Arena; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.lang.foreign.ValueLayout; import java.lang.invoke.VarHandle; @@ -46,8 +46,8 @@ public class TestSlices { @Test(dataProvider = "slices") public void testSlices(VarHandle handle, int lo, int hi, int[] values) { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(LAYOUT, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(LAYOUT, arena.scope());; //init for (long i = 0 ; i < 2 ; i++) { for (long j = 0 ; j < 5 ; j++) { @@ -61,8 +61,8 @@ public void testSlices(VarHandle handle, int lo, int hi, int[] values) { @Test(dataProvider = "slices") public void testSliceBadIndex(VarHandle handle, int lo, int hi, int[] values) { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(LAYOUT, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(LAYOUT, arena.scope());; assertThrows(() -> handle.get(segment, lo, 0)); assertThrows(() -> handle.get(segment, 0, hi)); } diff --git a/test/jdk/java/foreign/TestSpliterator.java b/test/jdk/java/foreign/TestSpliterator.java index e933706fd028e..574c184522fe1 100644 --- a/test/jdk/java/foreign/TestSpliterator.java +++ b/test/jdk/java/foreign/TestSpliterator.java @@ -27,9 +27,10 @@ * @run testng TestSpliterator */ +import java.lang.foreign.Arena; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.SequenceLayout; import java.lang.invoke.VarHandle; @@ -42,6 +43,7 @@ import java.util.stream.LongStream; import java.lang.foreign.ValueLayout; + import org.testng.annotations.*; import static org.testng.Assert.*; @@ -57,8 +59,8 @@ public void testSum(int size, int threshold) { SequenceLayout layout = MemoryLayout.sequenceLayout(size, ValueLayout.JAVA_INT); //setup - try (MemorySession session = MemorySession.openShared()) { - MemorySegment segment = MemorySegment.allocateNative(layout, session); + try (Arena arena = Arena.openShared()) { + MemorySegment segment = MemorySegment.allocateNative(layout, arena.scope());; for (int i = 0; i < layout.elementCount(); i++) { INT_HANDLE.set(segment, (long) i, i); } @@ -84,7 +86,7 @@ public void testSumSameThread() { SequenceLayout layout = MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_INT); //setup - MemorySegment segment = MemorySegment.allocateNative(layout, MemorySession.openImplicit()); + MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto()); for (int i = 0; i < layout.elementCount(); i++) { INT_HANDLE.set(segment, (long) i, i); } @@ -99,37 +101,58 @@ public void testSumSameThread() { @Test(expectedExceptions = IllegalArgumentException.class) public void testBadSpliteratorElementSizeTooBig() { - MemorySegment.ofArray(new byte[2]).spliterator(ValueLayout.JAVA_INT); + MemorySegment.allocateNative(2, SegmentScope.auto()) + .spliterator(ValueLayout.JAVA_INT); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadStreamElementSizeTooBig() { - MemorySegment.ofArray(new byte[2]).elements(ValueLayout.JAVA_INT); + MemorySegment.allocateNative(2, SegmentScope.auto()) + .elements(ValueLayout.JAVA_INT); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadSpliteratorElementSizeNotMultiple() { - MemorySegment.ofArray(new byte[7]).spliterator(ValueLayout.JAVA_INT); + MemorySegment.allocateNative(7, SegmentScope.auto()) + .spliterator(ValueLayout.JAVA_INT); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadStreamElementSizeNotMultiple() { - MemorySegment.ofArray(new byte[7]).elements(ValueLayout.JAVA_INT); + MemorySegment.allocateNative(7, SegmentScope.auto()) + .elements(ValueLayout.JAVA_INT); + } + + @Test + public void testSpliteratorElementSizeMultipleButNotPowerOfTwo() { + MemorySegment.allocateNative(12, SegmentScope.auto()) + .spliterator(ValueLayout.JAVA_INT); + } + + @Test + public void testStreamElementSizeMultipleButNotPowerOfTwo() { + MemorySegment.allocateNative(12, SegmentScope.auto()) + .elements(ValueLayout.JAVA_INT); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadSpliteratorElementSizeZero() { - MemorySegment.ofArray(new byte[7]).spliterator(MemoryLayout.sequenceLayout(0, ValueLayout.JAVA_INT)); + MemorySegment.allocateNative(7, SegmentScope.auto()) + .spliterator(MemoryLayout.sequenceLayout(0, ValueLayout.JAVA_INT)); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadStreamElementSizeZero() { - MemorySegment.ofArray(new byte[7]).elements(MemoryLayout.sequenceLayout(0, ValueLayout.JAVA_INT)); + MemorySegment.allocateNative(7, SegmentScope.auto()) + .elements(MemoryLayout.sequenceLayout(0, ValueLayout.JAVA_INT)); } @Test(expectedExceptions = IllegalArgumentException.class) public void testHyperAligned() { - MemorySegment.ofArray(new byte[8]).elements(MemoryLayout.sequenceLayout(2, ValueLayout.JAVA_INT.withBitAlignment(64))); + MemorySegment segment = MemorySegment.allocateNative(8, SegmentScope.auto()); + // compute an alignment constraint (in bytes) which exceed that of the native segment + long bigByteAlign = Long.lowestOneBit(segment.address()) << 1; + segment.elements(MemoryLayout.sequenceLayout(2, ValueLayout.JAVA_INT.withBitAlignment(bigByteAlign * 8))); } static long sumSingle(long acc, MemorySegment segment) { diff --git a/test/jdk/java/foreign/TestStringEncoding.java b/test/jdk/java/foreign/TestStringEncoding.java index c595e671c5b74..ae86e7d4ca592 100644 --- a/test/jdk/java/foreign/TestStringEncoding.java +++ b/test/jdk/java/foreign/TestStringEncoding.java @@ -22,9 +22,9 @@ * */ +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; -import java.lang.foreign.SegmentAllocator; + import org.testng.annotations.*; import static org.testng.Assert.*; @@ -39,9 +39,8 @@ public class TestStringEncoding { @Test(dataProvider = "strings") public void testStrings(String testString, int expectedByteLength) { - try (MemorySession session = MemorySession.openConfined()) { - SegmentAllocator allocator = SegmentAllocator.newNativeArena(expectedByteLength, session); - MemorySegment text = allocator.allocateUtf8String(testString); + try (Arena arena = Arena.openConfined()) { + MemorySegment text = arena.allocateUtf8String(testString); assertEquals(text.byteSize(), expectedByteLength); diff --git a/test/jdk/java/foreign/TestTypeAccess.java b/test/jdk/java/foreign/TestTypeAccess.java index 2739881b191f7..e84af14aa0583 100644 --- a/test/jdk/java/foreign/TestTypeAccess.java +++ b/test/jdk/java/foreign/TestTypeAccess.java @@ -28,8 +28,8 @@ * @run testng TestTypeAccess */ +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.lang.foreign.ValueLayout; import org.testng.annotations.*; @@ -53,33 +53,33 @@ public void testMemoryCoordinatePrimitive() { @Test(expectedExceptions=ClassCastException.class) public void testMemoryAddressValueGetAsString() { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment s = MemorySegment.allocateNative(8, 8, session); - String address = (String)ADDR_HANDLE.get(s.address()); + try (Arena arena = Arena.openConfined()) { + MemorySegment s = MemorySegment.allocateNative(8, 8, arena.scope());; + String address = (String)ADDR_HANDLE.get(s); } } @Test(expectedExceptions=ClassCastException.class) public void testMemoryAddressValueSetAsString() { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment s = MemorySegment.allocateNative(8, 8, session); - ADDR_HANDLE.set(s.address(), "string"); + try (Arena arena = Arena.openConfined()) { + MemorySegment s = MemorySegment.allocateNative(8, 8, arena.scope());; + ADDR_HANDLE.set(s, "string"); } } @Test(expectedExceptions=WrongMethodTypeException.class) public void testMemoryAddressValueGetAsPrimitive() { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment s = MemorySegment.allocateNative(8, 8, session); - int address = (int)ADDR_HANDLE.get(s.address()); + try (Arena arena = Arena.openConfined()) { + MemorySegment s = MemorySegment.allocateNative(8, 8, arena.scope());; + int address = (int)ADDR_HANDLE.get(s); } } @Test(expectedExceptions=WrongMethodTypeException.class) public void testMemoryAddressValueSetAsPrimitive() { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment s = MemorySegment.allocateNative(8, 8, session); - ADDR_HANDLE.set(s.address(), 1); + try (Arena arena = Arena.openConfined()) { + MemorySegment s = MemorySegment.allocateNative(8, 8, arena.scope());; + ADDR_HANDLE.set(s, 1); } } diff --git a/test/jdk/java/foreign/TestUnsupportedLinker.java b/test/jdk/java/foreign/TestUnsupportedLinker.java index 8d223919b975a..d16ba1646b249 100644 --- a/test/jdk/java/foreign/TestUnsupportedLinker.java +++ b/test/jdk/java/foreign/TestUnsupportedLinker.java @@ -29,8 +29,7 @@ */ import java.lang.foreign.Linker; -import java.lang.foreign.MemoryAddress; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.VaList; import java.lang.foreign.ValueLayout; @@ -50,11 +49,11 @@ public void testEmptyVaList() { @Test(expectedExceptions = UnsupportedOperationException.class) public void testNonEmptyVaList() { - VaList.make(builder -> builder.addVarg(ValueLayout.JAVA_INT, 42), MemorySession.openImplicit()); + VaList.make(builder -> builder.addVarg(ValueLayout.JAVA_INT, 42), SegmentScope.auto()); } @Test(expectedExceptions = UnsupportedOperationException.class) public void testUnsafeVaList() { - VaList.ofAddress(MemoryAddress.NULL, MemorySession.openImplicit()); + VaList.ofAddress(0L, SegmentScope.auto()); } } diff --git a/test/jdk/java/foreign/TestUpcallAsync.java b/test/jdk/java/foreign/TestUpcallAsync.java index 6457302958829..4463bc0475cb1 100644 --- a/test/jdk/java/foreign/TestUpcallAsync.java +++ b/test/jdk/java/foreign/TestUpcallAsync.java @@ -33,15 +33,15 @@ * TestUpcallAsync */ -import java.lang.foreign.Addressable; -import java.lang.foreign.Linker; +import java.lang.foreign.Arena; import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.GroupLayout; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; -import java.lang.foreign.SegmentAllocator; + import org.testng.annotations.Test; +import java.lang.foreign.SegmentScope; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.util.ArrayList; @@ -61,24 +61,24 @@ public class TestUpcallAsync extends TestUpcallBase { public void testUpcallsAsync(int count, String fName, Ret ret, List paramTypes, List fields) throws Throwable { List> returnChecks = new ArrayList<>(); List> argChecks = new ArrayList<>(); - Addressable addr = findNativeOrThrow(fName); - try (MemorySession session = MemorySession.openShared()) { - SegmentAllocator allocator = SegmentAllocator.newNativeArena(session); + MemorySegment addr = findNativeOrThrow(fName); + try (Arena arena = Arena.openShared()) { FunctionDescriptor descriptor = function(ret, paramTypes, fields); - MethodHandle mh = downcallHandle(ABI, addr, allocator, descriptor); - Object[] args = makeArgs(MemorySession.openImplicit(), ret, paramTypes, fields, returnChecks, argChecks); + MethodHandle mh = downcallHandle(ABI, addr, arena, descriptor); + Object[] args = makeArgs(SegmentScope.auto(), 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()); - Addressable callback = ABI.upcallStub(mh.asType(Linker.upcallType(callbackDesc)), callbackDesc, session); + MemorySegment callback = ABI.upcallStub(mh, callbackDesc, arena.scope()); MethodHandle invoker = asyncInvoker(ret, ret == Ret.VOID ? null : paramTypes.get(0), fields); - Object res = invoker.type().returnType() == MemorySegment.class - ? invoker.invoke(allocator, callback) + Object res = (descriptor.returnLayout().isPresent() && + descriptor.returnLayout().get() instanceof GroupLayout) + ? invoker.invoke(arena.scope(), callback) : invoker.invoke(callback); argChecks.forEach(c -> c.accept(args)); if (ret == Ret.NON_VOID) { @@ -102,7 +102,7 @@ private MethodHandle asyncInvoker(Ret ret, ParamType returnType, List { - Addressable invokerSymbol = findNativeOrThrow(symbol); + MemorySegment invokerSymbol = findNativeOrThrow(symbol); MemoryLayout returnLayout = returnType.layout(fields); FunctionDescriptor desc = FunctionDescriptor.of(returnLayout, C_POINTER); diff --git a/test/jdk/java/foreign/TestUpcallBase.java b/test/jdk/java/foreign/TestUpcallBase.java index a9b750d82199f..85da103a80158 100644 --- a/test/jdk/java/foreign/TestUpcallBase.java +++ b/test/jdk/java/foreign/TestUpcallBase.java @@ -22,10 +22,10 @@ * */ -import java.lang.foreign.Addressable; +import java.lang.foreign.GroupLayout; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; @@ -34,6 +34,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; @@ -54,17 +55,17 @@ public abstract class TestUpcallBase extends CallGeneratorHelper { try { DUMMY = MethodHandles.lookup().findStatic(TestUpcallBase.class, "dummy", MethodType.methodType(void.class)); PASS_AND_SAVE = MethodHandles.lookup().findStatic(TestUpcallBase.class, "passAndSave", - MethodType.methodType(Object.class, Object[].class, AtomicReference.class, int.class)); + MethodType.methodType(Object.class, Object[].class, AtomicReference.class, int.class, List.class)); } catch (Throwable ex) { throw new IllegalStateException(ex); } } - private static Addressable DUMMY_STUB; + private static MemorySegment DUMMY_STUB; @BeforeClass void setup() { - DUMMY_STUB = ABI.upcallStub(DUMMY, FunctionDescriptor.ofVoid(), MemorySession.openImplicit()); + DUMMY_STUB = ABI.upcallStub(DUMMY, FunctionDescriptor.ofVoid(), SegmentScope.auto()); } static FunctionDescriptor function(Ret ret, List params, List fields) { @@ -80,11 +81,11 @@ static FunctionDescriptor function(Ret ret, List params, List params, List fields, List> checks, List> argChecks) throws ReflectiveOperationException { + static Object[] makeArgs(SegmentScope session, Ret ret, List params, List fields, List> checks, List> argChecks) throws ReflectiveOperationException { return makeArgs(session, ret, params, fields, checks, argChecks, List.of()); } - static Object[] makeArgs(MemorySession session, Ret ret, List params, List fields, List> checks, List> argChecks, List prefix) throws ReflectiveOperationException { + static Object[] makeArgs(SegmentScope session, Ret ret, List params, List fields, List> checks, List> argChecks, List prefix) throws ReflectiveOperationException { Object[] args = new Object[prefix.size() + params.size() + 1]; int argNum = 0; for (MemoryLayout layout : prefix) { @@ -97,27 +98,32 @@ static Object[] makeArgs(MemorySession session, Ret ret, List params, return args; } - static Addressable makeCallback(MemorySession session, Ret ret, List params, List fields, List> checks, List> argChecks, List prefix) { + static MemorySegment makeCallback(SegmentScope session, Ret ret, List params, List fields, List> checks, List> argChecks, List prefix) { if (params.isEmpty()) { return DUMMY_STUB; } AtomicReference box = new AtomicReference<>(); - MethodHandle mh = insertArguments(PASS_AND_SAVE, 1, box, prefix.size()); + List layouts = new ArrayList<>(); + layouts.addAll(prefix); + for (int i = 0 ; i < params.size() ; i++) { + layouts.add(params.get(i).layout(fields)); + } + MethodHandle mh = insertArguments(PASS_AND_SAVE, 1, box, prefix.size(), layouts); mh = mh.asCollector(Object[].class, prefix.size() + params.size()); for(int i = 0; i < prefix.size(); i++) { - mh = mh.asType(mh.type().changeParameterType(i, carrier(prefix.get(i), false))); + mh = mh.asType(mh.type().changeParameterType(i, carrier(prefix.get(i)))); } for (int i = 0; i < params.size(); i++) { ParamType pt = params.get(i); MemoryLayout layout = pt.layout(fields); - Class carrier = carrier(layout, false); + Class carrier = carrier(layout); mh = mh.asType(mh.type().changeParameterType(prefix.size() + i, carrier)); final int finalI = prefix.size() + i; - if (carrier == MemorySegment.class) { + if (layout instanceof GroupLayout) { argChecks.add(o -> assertStructEquals((MemorySegment) box.get()[finalI], (MemorySegment) o[finalI], layout)); } else { argChecks.add(o -> assertEquals(box.get()[finalI], o[finalI])); @@ -126,9 +132,8 @@ static Addressable makeCallback(MemorySession session, Ret ret, List ParamType firstParam = params.get(0); MemoryLayout firstlayout = firstParam.layout(fields); - Class firstCarrier = carrier(firstlayout, true); - - if (firstCarrier == MemorySegment.class) { + Class firstCarrier = carrier(firstlayout); + if (firstlayout instanceof GroupLayout) { checks.add(o -> assertStructEquals((MemorySegment) box.get()[prefix.size()], (MemorySegment) o, firstlayout)); } else { checks.add(o -> assertEquals(o, box.get()[prefix.size()])); @@ -143,11 +148,11 @@ static Addressable makeCallback(MemorySession session, Ret ret, List return ABI.upcallStub(mh, func, session); } - static Object passAndSave(Object[] o, AtomicReference ref, int retArg) { + static Object passAndSave(Object[] o, AtomicReference ref, int retArg, List layouts) { for (int i = 0; i < o.length; i++) { - if (o[i] instanceof MemorySegment) { + if (layouts.get(i) instanceof GroupLayout) { MemorySegment ms = (MemorySegment) o[i]; - MemorySegment copy = MemorySegment.allocateNative(ms.byteSize(), MemorySession.openImplicit()); + MemorySegment copy = MemorySegment.allocateNative(ms.byteSize(), SegmentScope.auto()); copy.copyFrom(ms); o[i] = copy; } diff --git a/test/jdk/java/foreign/TestUpcallException.java b/test/jdk/java/foreign/TestUpcallException.java index c27fde54dc07e..d9dc32bacb263 100644 --- a/test/jdk/java/foreign/TestUpcallException.java +++ b/test/jdk/java/foreign/TestUpcallException.java @@ -33,71 +33,26 @@ * TestUpcallException */ -import jdk.test.lib.Utils; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.file.Paths; -import java.util.List; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotEquals; -import static org.testng.Assert.assertTrue; +public class TestUpcallException extends UpcallTestHelper { -public class TestUpcallException { - - @Test - public void testExceptionInterpreted() throws InterruptedException, IOException { - run(/* useSpec = */ false, /* isVoid = */ true); - run(/* useSpec = */ false, /* isVoid = */ false); - } - - @Test - public void testExceptionSpecialized() throws IOException, InterruptedException { - run(/* useSpec = */ true, /* isVoid = */ true); - run(/* useSpec = */ true, /* isVoid = */ false); - } - - private void run(boolean useSpec, boolean isVoid) throws IOException, InterruptedException { - Process process = new ProcessBuilder() - .command( - Paths.get(Utils.TEST_JDK) - .resolve("bin") - .resolve("java") - .toAbsolutePath() - .toString(), - "--enable-preview", - "--enable-native-access=ALL-UNNAMED", - "-Djava.library.path=" + System.getProperty("java.library.path"), - "-Djdk.internal.foreign.ProgrammableUpcallHandler.USE_SPEC=" + useSpec, - "-cp", Utils.TEST_CLASS_PATH, - "ThrowingUpcall", - isVoid ? "void" : "non-void") - .start(); - - int result = process.waitFor(); - assertNotEquals(result, 0); - - List outLines = linesFromStream(process.getInputStream()); - outLines.forEach(System.out::println); - List errLines = linesFromStream(process.getErrorStream()); - errLines.forEach(System.err::println); - - // Exception message would be found in stack trace - String shouldInclude = "Testing upcall exceptions"; - assertTrue(linesContain(errLines, shouldInclude), "Did not find '" + shouldInclude + "' in stderr"); - } - - private boolean linesContain(List errLines, String shouldInclude) { - return errLines.stream().anyMatch(line -> line.contains(shouldInclude)); + @Test(dataProvider = "cases") + public void testException(boolean useSpec, boolean isVoid) throws InterruptedException, IOException { + runInNewProcess(ThrowingUpcall.class, useSpec, isVoid ? "void" : "") + .assertStdErrContains("Testing upcall exceptions"); } - private static List linesFromStream(InputStream stream) throws IOException { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) { - return reader.lines().toList(); - } + @DataProvider + public static Object[][] cases() { + return new Object[][]{ + { false, true, }, + { false, false, }, + { true, true, }, + { true, false, } + }; } } diff --git a/test/jdk/java/foreign/TestUpcallHighArity.java b/test/jdk/java/foreign/TestUpcallHighArity.java index f36da3bb94874..54e34fa2f9c89 100644 --- a/test/jdk/java/foreign/TestUpcallHighArity.java +++ b/test/jdk/java/foreign/TestUpcallHighArity.java @@ -33,16 +33,17 @@ * TestUpcallHighArity */ -import java.lang.foreign.Addressable; -import java.lang.foreign.Linker; +import java.lang.foreign.Arena; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryAddress; +import java.lang.foreign.GroupLayout; +import java.lang.foreign.Linker; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; + import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import java.lang.foreign.SegmentScope; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -76,17 +77,17 @@ public class TestUpcallHighArity extends CallGeneratorHelper { S_PDI_LAYOUT, C_INT, C_DOUBLE, C_POINTER) ); MH_passAndSave = MethodHandles.lookup().findStatic(TestUpcallHighArity.class, "passAndSave", - MethodType.methodType(void.class, Object[].class, AtomicReference.class)); + MethodType.methodType(void.class, Object[].class, AtomicReference.class, List.class)); } catch (ReflectiveOperationException e) { throw new InternalError(e); } } - static void passAndSave(Object[] o, AtomicReference ref) { + static void passAndSave(Object[] o, AtomicReference ref, List layouts) { for (int i = 0; i < o.length; i++) { - if (o[i] instanceof MemorySegment) { + if (layouts.get(i) instanceof GroupLayout) { MemorySegment ms = (MemorySegment) o[i]; - MemorySegment copy = MemorySegment.allocateNative(ms.byteSize(), MemorySession.openImplicit()); + MemorySegment copy = MemorySegment.allocateNative(ms.byteSize(), SegmentScope.auto()); copy.copyFrom(ms); o[i] = copy; } @@ -98,11 +99,11 @@ static void passAndSave(Object[] o, AtomicReference ref) { public void testUpcall(MethodHandle downcall, MethodType upcallType, FunctionDescriptor upcallDescriptor) throws Throwable { AtomicReference capturedArgs = new AtomicReference<>(); - MethodHandle target = MethodHandles.insertArguments(MH_passAndSave, 1, capturedArgs) + MethodHandle target = MethodHandles.insertArguments(MH_passAndSave, 1, capturedArgs, upcallDescriptor.argumentLayouts()) .asCollector(Object[].class, upcallType.parameterCount()) .asType(upcallType); - try (MemorySession session = MemorySession.openConfined()) { - Addressable upcallStub = LINKER.upcallStub(target, upcallDescriptor, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment upcallStub = LINKER.upcallStub(target, upcallDescriptor, arena.scope()); Object[] args = new Object[upcallType.parameterCount() + 1]; args[0] = upcallStub; List argLayouts = upcallDescriptor.argumentLayouts(); @@ -114,7 +115,7 @@ public void testUpcall(MethodHandle downcall, MethodType upcallType, Object[] capturedArgsArr = capturedArgs.get(); for (int i = 0; i < capturedArgsArr.length; i++) { - if (upcallType.parameterType(i) == MemorySegment.class) { + if (upcallDescriptor.argumentLayouts().get(i) instanceof GroupLayout) { assertStructEquals((MemorySegment) capturedArgsArr[i], (MemorySegment) args[i + 1], argLayouts.get(i)); } else { assertEquals(capturedArgsArr[i], args[i + 1], "For index " + i); @@ -128,10 +129,10 @@ public static Object[][] args() { return new Object[][]{ { MH_do_upcall, MethodType.methodType(void.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), + MemorySegment.class, int.class, double.class, MemorySegment.class, + MemorySegment.class, int.class, double.class, MemorySegment.class, + MemorySegment.class, int.class, double.class, MemorySegment.class, + MemorySegment.class, int.class, double.class, MemorySegment.class), FunctionDescriptor.ofVoid( S_PDI_LAYOUT, C_INT, C_DOUBLE, C_POINTER, S_PDI_LAYOUT, C_INT, C_DOUBLE, C_POINTER, diff --git a/test/jdk/java/foreign/TestUpcallScope.java b/test/jdk/java/foreign/TestUpcallScope.java index e5bb77c6af2bf..f59e9c0d42752 100644 --- a/test/jdk/java/foreign/TestUpcallScope.java +++ b/test/jdk/java/foreign/TestUpcallScope.java @@ -32,9 +32,9 @@ * TestUpcallScope */ -import java.lang.foreign.Addressable; -import java.lang.foreign.MemorySession; -import java.lang.foreign.SegmentAllocator; +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; + import org.testng.annotations.Test; import java.lang.invoke.MethodHandle; @@ -52,11 +52,10 @@ public class TestUpcallScope extends TestUpcallBase { public void testUpcalls(int count, String fName, Ret ret, List paramTypes, List fields) throws Throwable { List> returnChecks = new ArrayList<>(); List> argChecks = new ArrayList<>(); - Addressable addr = findNativeOrThrow(fName); - try (MemorySession session = MemorySession.openConfined()) { - SegmentAllocator allocator = SegmentAllocator.newNativeArena(session); - MethodHandle mh = downcallHandle(ABI, addr, allocator, function(ret, paramTypes, fields)); - Object[] args = makeArgs(session, ret, paramTypes, fields, returnChecks, argChecks); + MemorySegment addr = findNativeOrThrow(fName); + try (Arena arena = Arena.openConfined()) { + MethodHandle mh = downcallHandle(ABI, addr, arena, function(ret, paramTypes, fields)); + Object[] args = makeArgs(arena.scope(), ret, paramTypes, fields, returnChecks, argChecks); Object[] callArgs = args; Object res = mh.invokeWithArguments(callArgs); argChecks.forEach(c -> c.accept(args)); diff --git a/test/jdk/java/foreign/TestUpcallStack.java b/test/jdk/java/foreign/TestUpcallStack.java index 9454d77a996b4..5df32ec2af615 100644 --- a/test/jdk/java/foreign/TestUpcallStack.java +++ b/test/jdk/java/foreign/TestUpcallStack.java @@ -32,10 +32,11 @@ * TestUpcallStack */ -import java.lang.foreign.Addressable; +import java.lang.foreign.Arena; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemorySession; -import java.lang.foreign.SegmentAllocator; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SegmentScope; + import org.testng.annotations.Test; import java.lang.invoke.MethodHandle; @@ -53,11 +54,10 @@ public class TestUpcallStack extends TestUpcallBase { public void testUpcallsStack(int count, String fName, Ret ret, List paramTypes, List fields) throws Throwable { List> returnChecks = new ArrayList<>(); List> argChecks = new ArrayList<>(); - Addressable addr = findNativeOrThrow("s" + fName); - try (MemorySession session = MemorySession.openConfined()) { - SegmentAllocator allocator = SegmentAllocator.newNativeArena(session); - MethodHandle mh = downcallHandle(ABI, addr, allocator, functionStack(ret, paramTypes, fields)); - Object[] args = makeArgsStack(session, ret, paramTypes, fields, returnChecks, argChecks); + MemorySegment addr = findNativeOrThrow("s" + fName); + try (Arena arena = Arena.openConfined()) { + MethodHandle mh = downcallHandle(ABI, addr, arena, functionStack(ret, paramTypes, fields)); + Object[] args = makeArgsStack(arena.scope(), ret, paramTypes, fields, returnChecks, argChecks); Object[] callArgs = args; Object res = mh.invokeWithArguments(callArgs); argChecks.forEach(c -> c.accept(args)); @@ -71,7 +71,7 @@ static FunctionDescriptor functionStack(Ret ret, List params, List params, List fields, List> checks, List> argChecks) throws ReflectiveOperationException { + static Object[] makeArgsStack(SegmentScope session, Ret ret, List params, List fields, List> checks, List> argChecks) throws ReflectiveOperationException { return makeArgs(session, ret, params, fields, checks, argChecks, STACK_PREFIX_LAYOUTS); } diff --git a/test/jdk/java/foreign/TestUpcallStructScope.java b/test/jdk/java/foreign/TestUpcallStructScope.java index ca2cab17d586c..680893cd57fa8 100644 --- a/test/jdk/java/foreign/TestUpcallStructScope.java +++ b/test/jdk/java/foreign/TestUpcallStructScope.java @@ -37,12 +37,12 @@ * TestUpcallStructScope */ -import java.lang.foreign.Addressable; +import java.lang.foreign.Arena; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; + import org.testng.annotations.Test; import java.lang.invoke.MethodHandle; @@ -89,14 +89,14 @@ public void testUpcall() throws Throwable { AtomicReference capturedSegment = new AtomicReference<>(); MethodHandle target = methodHandle(capturedSegment::set); FunctionDescriptor upcallDesc = FunctionDescriptor.ofVoid(S_PDI_LAYOUT); - try (MemorySession session = MemorySession.openConfined()) { - Addressable upcallStub = LINKER.upcallStub(target, upcallDesc, session); - MemorySegment argSegment = MemorySegment.allocateNative(S_PDI_LAYOUT, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment upcallStub = LINKER.upcallStub(target, upcallDesc, arena.scope()); + MemorySegment argSegment = MemorySegment.allocateNative(S_PDI_LAYOUT, arena.scope());; MH_do_upcall.invoke(upcallStub, argSegment); } MemorySegment captured = capturedSegment.get(); - assertFalse(captured.session().isAlive()); + assertFalse(captured.scope().isAlive()); } } diff --git a/test/jdk/java/foreign/TestValueLayouts.java b/test/jdk/java/foreign/TestValueLayouts.java new file mode 100644 index 0000000000000..56019f907a83a --- /dev/null +++ b/test/jdk/java/foreign/TestValueLayouts.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @enablePreview + * @modules java.base/jdk.internal.misc + * @run testng TestValueLayouts + */ + +import org.testng.annotations.*; + +import java.lang.foreign.*; +import java.nio.ByteOrder; +import jdk.internal.misc.Unsafe; + +import static java.lang.foreign.ValueLayout.*; +import static org.testng.Assert.*; + +public class TestValueLayouts { + + @Test + public void testByte() { + testAligned(JAVA_BYTE, byte.class, Byte.SIZE); + } + + @Test + public void testBoolean() { + testAligned(JAVA_BOOLEAN, boolean.class, Byte.SIZE); + } + + @Test + public void testShort() { + testAligned(JAVA_SHORT, short.class, Short.SIZE); + } + + @Test + public void testShortUnaligned() { + testUnaligned(JAVA_SHORT_UNALIGNED, short.class, Short.SIZE); + } + + @Test + public void testInt() { + testAligned(JAVA_INT, int.class, Integer.SIZE); + } + + @Test + public void testIntUnaligned() { + testUnaligned(JAVA_INT_UNALIGNED, int.class, Integer.SIZE); + } + + @Test + public void testLong() { + testAligned(JAVA_LONG, long.class, Long.SIZE); + } + + @Test + public void testLongUnaligned() { + testUnaligned(JAVA_LONG_UNALIGNED, long.class, Long.SIZE); + } + + @Test + public void testFloat() { + testAligned(JAVA_FLOAT, float.class, Float.SIZE); + } + + @Test + public void testFloatUnaligned() { + testUnaligned(JAVA_FLOAT_UNALIGNED, float.class, Float.SIZE); + } + + @Test + public void testDouble() { + testAligned(JAVA_DOUBLE, double.class, Double.SIZE); + } + + @Test + public void testDoubleUnaligned() { + testUnaligned(JAVA_DOUBLE_UNALIGNED, double.class, Double.SIZE); + } + + @Test + public void testChar() { + testAligned(JAVA_CHAR, char.class, Character.SIZE); + } + + @Test + public void testCharUnaligned() { + testUnaligned(JAVA_CHAR_UNALIGNED, char.class, Character.SIZE); + } + + @Test + public void testAddress() { + testAligned(ADDRESS, MemorySegment.class, Unsafe.ADDRESS_SIZE * 8L); + } + + @Test + public void testAddressUnaligned() { + testUnaligned(ADDRESS_UNALIGNED, MemorySegment.class, Unsafe.ADDRESS_SIZE * 8L); + } + + void testAligned(ValueLayout layout, + Class carrier, + long bitSize) { + test(layout, carrier, bitSize, bitSize); + } + + void testUnaligned(ValueLayout layout, + Class carrier, + long bitSize) { + test(layout, carrier, bitSize, Byte.SIZE); + } + + void test(ValueLayout layout, + Class carrier, + long bitSize, + long bitAlignment) { + assertEquals(layout.carrier(), carrier); + assertEquals(layout.bitSize(), bitSize); + assertEquals(layout.order(), ByteOrder.nativeOrder()); + assertEquals(layout.bitAlignment(), bitAlignment); + assertTrue(layout.name().isEmpty()); + assertEquals(layout.byteSize(), layout.bitSize() / 8); + assertEquals(layout.byteAlignment(), layout.bitAlignment() / 8); + + } + +} diff --git a/test/jdk/java/foreign/TestVarArgs.java b/test/jdk/java/foreign/TestVarArgs.java index bd485bc0c0e12..90ad9842d3d40 100644 --- a/test/jdk/java/foreign/TestVarArgs.java +++ b/test/jdk/java/foreign/TestVarArgs.java @@ -29,13 +29,11 @@ * @run testng/othervm --enable-native-access=ALL-UNNAMED -Dgenerator.sample.factor=17 TestVarArgs */ -import java.lang.foreign.Addressable; +import java.lang.foreign.Arena; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import org.testng.annotations.Test; @@ -59,26 +57,26 @@ public class TestVarArgs extends CallGeneratorHelper { System.loadLibrary("VarArgs"); try { MH_CHECK = MethodHandles.lookup().findStatic(TestVarArgs.class, "check", - MethodType.methodType(void.class, int.class, MemoryAddress.class, List.class)); + MethodType.methodType(void.class, int.class, MemorySegment.class, List.class)); } catch (ReflectiveOperationException e) { throw new ExceptionInInitializerError(e); } } - static final Addressable VARARGS_ADDR = findNativeOrThrow("varargs"); + static final MemorySegment VARARGS_ADDR = findNativeOrThrow("varargs"); @Test(dataProvider = "functions") public void testVarArgs(int count, String fName, Ret ret, // ignore this stuff List paramTypes, List fields) throws Throwable { List args = makeArgs(paramTypes, fields); - try (MemorySession session = MemorySession.openConfined()) { + try (Arena arena = Arena.openConfined()) { MethodHandle checker = MethodHandles.insertArguments(MH_CHECK, 2, args); - MemorySegment writeBack = LINKER.upcallStub(checker, FunctionDescriptor.ofVoid(C_INT, C_POINTER), session); - MemorySegment callInfo = MemorySegment.allocateNative(CallInfo.LAYOUT, session); - MemorySegment argIDs = MemorySegment.allocateNative(MemoryLayout.sequenceLayout(args.size(), C_INT), session); + MemorySegment writeBack = LINKER.upcallStub(checker, FunctionDescriptor.ofVoid(C_INT, C_POINTER), arena.scope()); + MemorySegment callInfo = MemorySegment.allocateNative(CallInfo.LAYOUT, arena.scope());; + MemorySegment argIDs = MemorySegment.allocateNative(MemoryLayout.sequenceLayout(args.size(), C_INT), arena.scope());; - MemoryAddress callInfoPtr = callInfo.address(); + MemorySegment callInfoPtr = callInfo; CallInfo.writeback(callInfo, writeBack); CallInfo.argIDs(callInfo, argIDs); @@ -91,10 +89,11 @@ public void testVarArgs(int count, String fName, Ret ret, // ignore this stuff argLayouts.add(C_POINTER); // call info argLayouts.add(C_INT); // size - FunctionDescriptor desc = FunctionDescriptor.ofVoid(argLayouts.toArray(MemoryLayout[]::new)) - .asVariadic(args.stream().map(a -> a.layout).toArray(MemoryLayout[]::new)); + FunctionDescriptor baseDesc = FunctionDescriptor.ofVoid(argLayouts.toArray(MemoryLayout[]::new)); + Linker.Option varargIndex = Linker.Option.firstVariadicArg(baseDesc.argumentLayouts().size()); + FunctionDescriptor desc = baseDesc.appendArgumentLayouts(args.stream().map(a -> a.layout).toArray(MemoryLayout[]::new)); - MethodHandle downcallHandle = LINKER.downcallHandle(VARARGS_ADDR, desc); + MethodHandle downcallHandle = LINKER.downcallHandle(VARARGS_ADDR, desc, varargIndex); List argValues = new ArrayList<>(); argValues.add(callInfoPtr); // call info @@ -121,13 +120,13 @@ private static List makeArgs(List paramTypes, List args) { + private static void check(int index, MemorySegment ptr, List args) { Arg varArg = args.get(index); MemoryLayout layout = varArg.layout; MethodHandle getter = varArg.getter; List> checks = varArg.checks; - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment seg = MemorySegment.ofAddress(ptr, layout.byteSize(), session); + try (Arena arena = Arena.openConfined()) { + MemorySegment seg = MemorySegment.ofAddress(ptr.address(), layout.byteSize(), arena.scope()); Object obj = getter.invoke(seg); checks.forEach(check -> check.accept(obj)); } catch (Throwable e) { @@ -143,11 +142,11 @@ private static class CallInfo { static final VarHandle VH_writeback = LAYOUT.varHandle(groupElement("writeback")); static final VarHandle VH_argIDs = LAYOUT.varHandle(groupElement("argIDs")); - static void writeback(MemorySegment seg, Addressable addr) { - VH_writeback.set(seg, addr.address()); + static void writeback(MemorySegment seg, MemorySegment addr) { + VH_writeback.set(seg, addr); } - static void argIDs(MemorySegment seg, Addressable addr) { - VH_argIDs.set(seg, addr.address()); + static void argIDs(MemorySegment seg, MemorySegment addr) { + VH_argIDs.set(seg, addr); } } diff --git a/test/jdk/java/foreign/TestVarHandleCombinators.java b/test/jdk/java/foreign/TestVarHandleCombinators.java index e09d6292098a5..28b05d02d6257 100644 --- a/test/jdk/java/foreign/TestVarHandleCombinators.java +++ b/test/jdk/java/foreign/TestVarHandleCombinators.java @@ -28,7 +28,8 @@ * @run testng TestVarHandleCombinators */ -import java.lang.foreign.MemorySession; +import java.lang.foreign.Arena; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import org.testng.annotations.Test; @@ -65,7 +66,7 @@ public void testUnalignedElement() { public void testAlign() { VarHandle vh = MethodHandles.memorySegmentViewVarHandle(ValueLayout.JAVA_BYTE.withBitAlignment(16)); - MemorySegment segment = MemorySegment.allocateNative(1, 2, MemorySession.openImplicit()); + MemorySegment segment = MemorySegment.allocateNative(1L, 2, SegmentScope.auto()); vh.set(segment, 0L, (byte) 10); // fine, memory region is aligned assertEquals((byte) vh.get(segment, 0L), (byte) 10); } @@ -101,8 +102,8 @@ public void testNestedSequenceAccess() { VarHandle vh = MethodHandles.memorySegmentViewVarHandle(ValueLayout.JAVA_INT.withBitAlignment(32)); int count = 0; - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(inner_size * outer_size * 8, 4, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = MemorySegment.allocateNative(inner_size * outer_size * 8, 4, arena.scope());; for (long i = 0; i < outer_size; i++) { for (long j = 0; j < inner_size; j++) { vh.set(segment, i * 40 + j * 8, count); diff --git a/test/jdk/java/foreign/ThrowingUpcall.java b/test/jdk/java/foreign/ThrowingUpcall.java index 40ea4e51a7054..70d2aba66ec5f 100644 --- a/test/jdk/java/foreign/ThrowingUpcall.java +++ b/test/jdk/java/foreign/ThrowingUpcall.java @@ -21,10 +21,10 @@ * questions. */ -import java.lang.foreign.Addressable; +import java.lang.foreign.Arena; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemorySession; +import java.lang.foreign.MemorySegment; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -72,8 +72,8 @@ public static void testVoid() throws Throwable { MethodHandle invoker = MethodHandles.exactInvoker(MethodType.methodType(void.class)); handle = MethodHandles.insertArguments(invoker, 0, handle); - try (MemorySession session = MemorySession.openConfined()) { - Addressable stub = Linker.nativeLinker().upcallStub(handle, FunctionDescriptor.ofVoid(), session); + try (Arena arena = Arena.openConfined()) { + MemorySegment stub = Linker.nativeLinker().upcallStub(handle, FunctionDescriptor.ofVoid(), arena.scope()); downcallVoid.invoke(stub); // should call Shutdown.exit(1); } @@ -85,8 +85,8 @@ public static void testNonVoid() throws Throwable { MethodHandle invoker = MethodHandles.exactInvoker(MethodType.methodType(int.class, int.class)); handle = MethodHandles.insertArguments(invoker, 0, handle); - try (MemorySession session = MemorySession.openConfined()) { - Addressable stub = Linker.nativeLinker().upcallStub(handle, FunctionDescriptor.of(C_INT, C_INT), session); + try (Arena arena = Arena.openConfined()) { + MemorySegment stub = Linker.nativeLinker().upcallStub(handle, FunctionDescriptor.of(C_INT, C_INT), arena.scope()); downcallNonVoid.invoke(42, stub); // should call Shutdown.exit(1); } diff --git a/test/jdk/java/foreign/UpcallTestHelper.java b/test/jdk/java/foreign/UpcallTestHelper.java new file mode 100644 index 0000000000000..fa4ff862d510d --- /dev/null +++ b/test/jdk/java/foreign/UpcallTestHelper.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.test.lib.Utils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.testng.Assert.assertNotEquals; +import static org.testng.Assert.assertTrue; + +public class UpcallTestHelper extends NativeTestHelper { + public record Output(List stdout, List stderr) { + private static void assertContains(List lines, String shouldInclude) { + assertTrue(lines.stream().anyMatch(line -> line.contains(shouldInclude)), + "Did not find '" + shouldInclude + "' in stderr"); + } + + public void assertStdErrContains(String shouldInclude) { + assertContains(stderr, shouldInclude); + } + } + + public Output runInNewProcess(Class target, boolean useSpec, String... programArgs) throws IOException, InterruptedException { + assert !target.isArray(); + + List command = new ArrayList<>(List.of( + Paths.get(Utils.TEST_JDK) + .resolve("bin") + .resolve("java") + .toAbsolutePath() + .toString(), + "--enable-preview", + "--enable-native-access=ALL-UNNAMED", + "-Djava.library.path=" + System.getProperty("java.library.path"), + "-Djdk.internal.foreign.ProgrammableUpcallHandler.USE_SPEC=" + useSpec, + "-cp", Utils.TEST_CLASS_PATH, + target.getName() + )); + command.addAll(Arrays.asList(programArgs)); + Process process = new ProcessBuilder() + .command(command) + .start(); + + int result = process.waitFor(); + assertNotEquals(result, 0); + + List outLines = linesFromStream(process.getInputStream()); + outLines.forEach(System.out::println); + List errLines = linesFromStream(process.getErrorStream()); + errLines.forEach(System.err::println); + + return new Output(outLines, errLines); + } + + private static List linesFromStream(InputStream stream) throws IOException { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) { + return reader.lines().toList(); + } + } +} diff --git a/test/jdk/java/foreign/callarranger/TestAarch64CallArranger.java b/test/jdk/java/foreign/callarranger/TestAarch64CallArranger.java index a2c49e504327c..c4c8670e8bd00 100644 --- a/test/jdk/java/foreign/callarranger/TestAarch64CallArranger.java +++ b/test/jdk/java/foreign/callarranger/TestAarch64CallArranger.java @@ -32,23 +32,24 @@ * @run testng TestAarch64CallArranger */ -import java.lang.foreign.Addressable; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; import jdk.internal.foreign.abi.Binding; import jdk.internal.foreign.abi.CallingSequence; +import jdk.internal.foreign.abi.LinkerOptions; import jdk.internal.foreign.abi.aarch64.CallArranger; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.lang.invoke.MethodType; +import static java.lang.foreign.Linker.Option.firstVariadicArg; import static java.lang.foreign.ValueLayout.ADDRESS; import static jdk.internal.foreign.PlatformLayouts.AArch64.*; import static jdk.internal.foreign.abi.Binding.*; import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.*; + import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; @@ -61,13 +62,13 @@ public void testEmpty() { FunctionDescriptor fd = FunctionDescriptor.ofVoid(); CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r9, long.class) } + { unboxAddress(), vmStore(r9, long.class) } }); checkReturnBindings(callingSequence, new Binding[]{}); @@ -85,13 +86,13 @@ public void testInteger() { C_INT, C_INT); CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r9, long.class) }, + { unboxAddress(), vmStore(r9, long.class) }, { vmStore(r0, int.class) }, { vmStore(r1, int.class) }, { vmStore(r2, int.class) }, @@ -115,13 +116,13 @@ public void testTwoIntTwoFloat() { C_INT, C_INT, C_FLOAT, C_FLOAT); CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r9, long.class) }, + { unboxAddress(), vmStore(r9, long.class) }, { vmStore(r0, int.class) }, { vmStore(r1, int.class) }, { vmStore(v0, float.class) }, @@ -137,13 +138,13 @@ public void testStruct(MemoryLayout struct, Binding[] expectedBindings) { FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct); CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r9, long.class) }, + { unboxAddress(), vmStore(r9, long.class) }, expectedBindings }); @@ -165,7 +166,7 @@ public static Object[][] structs() { // struct s { int32_t a, b; double c; int32_t d }; { struct2, new Binding[] { copy(struct2), - unboxAddress(MemorySegment.class), + unboxAddress(), vmStore(r0, long.class) }}, // struct s { int32_t a[2]; float b[2] }; @@ -197,21 +198,21 @@ public void testMultipleStructs() { FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct1, struct2, C_INT); CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r9, long.class) }, + { unboxAddress(), vmStore(r9, long.class) }, { copy(struct1), - unboxAddress(MemorySegment.class), + unboxAddress(), vmStore(r0, long.class) }, { copy(struct2), - unboxAddress(MemorySegment.class), + unboxAddress(), vmStore(r1, long.class) }, { vmStore(r2, int.class) } @@ -228,13 +229,13 @@ public void testReturnStruct1() { FunctionDescriptor fd = FunctionDescriptor.of(struct); CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); - assertTrue(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), MethodType.methodType(void.class, Addressable.class, MemoryAddress.class)); + assertTrue(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), FunctionDescriptor.ofVoid(ADDRESS, C_POINTER)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r9, long.class) }, + { unboxAddress(), vmStore(r9, long.class) }, { unboxAddress(), vmStore(r8, long.class) @@ -252,14 +253,14 @@ public void testReturnStruct2() { FunctionDescriptor fd = FunctionDescriptor.of(struct); CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(MemorySegment.class), vmStore(r10, long.class) }, - { unboxAddress(Addressable.class), vmStore(r9, long.class) } + { unboxAddress(), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r9, long.class) } }); checkReturnBindings(callingSequence, new Binding[]{ @@ -281,14 +282,14 @@ public void testStructHFA1() { FunctionDescriptor fd = FunctionDescriptor.of(hfa, C_FLOAT, C_INT, hfa); CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(MemorySegment.class), vmStore(r10, long.class) }, - { unboxAddress(Addressable.class), vmStore(r9, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r9, long.class) }, { vmStore(v0, float.class) }, { vmStore(r0, int.class) }, { @@ -319,13 +320,13 @@ public void testStructHFA3() { FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct, struct, struct); CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r9, long.class) }, + { unboxAddress(), vmStore(r9, long.class) }, { dup(), bufferLoad(0, float.class), @@ -373,22 +374,22 @@ public void testStructStackSpill() { struct, struct, C_INT, C_INT, C_INT, C_INT, C_INT, C_INT, struct, C_INT); CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r9, long.class) }, - { copy(struct), unboxAddress(MemorySegment.class), vmStore(r0, long.class) }, - { copy(struct), unboxAddress(MemorySegment.class), vmStore(r1, long.class) }, + { unboxAddress(), vmStore(r9, long.class) }, + { copy(struct), unboxAddress(), vmStore(r0, long.class) }, + { copy(struct), unboxAddress(), 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), unboxAddress(MemorySegment.class), vmStore(stackStorage(0), long.class) }, + { copy(struct), unboxAddress(), vmStore(stackStorage(0), long.class) }, { vmStore(stackStorage(1), int.class) }, }); @@ -398,18 +399,18 @@ public void testStructStackSpill() { @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); + FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_INT, C_INT, C_FLOAT); FunctionDescriptor fdExpected = FunctionDescriptor.ofVoid(ADDRESS, C_INT, C_INT, C_FLOAT); - CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); + CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false, LinkerOptions.of(firstVariadicArg(1))); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fdExpected); // This is identical to the non-variadic calling sequence checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r9, long.class) }, + { unboxAddress(), vmStore(r9, long.class) }, { vmStore(r0, int.class) }, { vmStore(r1, int.class) }, { vmStore(v0, float.class) }, @@ -421,18 +422,18 @@ public void testVarArgsInRegs() { @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); + FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_INT, C_INT, C_FLOAT); FunctionDescriptor fdExpected = FunctionDescriptor.ofVoid(ADDRESS, C_INT, C_INT, C_FLOAT); - CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false); + CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false, LinkerOptions.of(firstVariadicArg(1))); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fdExpected); // The two variadic arguments should be allocated on the stack checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r9, long.class) }, + { unboxAddress(), vmStore(r9, long.class) }, { vmStore(r0, int.class) }, { vmStore(stackStorage(0), int.class) }, { vmStore(stackStorage(1), float.class) }, diff --git a/test/jdk/java/foreign/callarranger/TestSysVCallArranger.java b/test/jdk/java/foreign/callarranger/TestSysVCallArranger.java index 1e8da4fd45691..01066b550aec0 100644 --- a/test/jdk/java/foreign/callarranger/TestSysVCallArranger.java +++ b/test/jdk/java/foreign/callarranger/TestSysVCallArranger.java @@ -34,7 +34,6 @@ */ import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; import jdk.internal.foreign.abi.Binding; @@ -43,13 +42,13 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -import java.lang.foreign.Addressable; import java.lang.invoke.MethodType; import static java.lang.foreign.ValueLayout.ADDRESS; import static jdk.internal.foreign.PlatformLayouts.SysV.*; import static jdk.internal.foreign.abi.Binding.*; import static jdk.internal.foreign.abi.x64.X86_64Architecture.*; + import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; @@ -62,19 +61,19 @@ public void testEmpty() { FunctionDescriptor fd = FunctionDescriptor.ofVoid(); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG).insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, { vmStore(rax, long.class) } }); checkReturnBindings(callingSequence, new Binding[]{}); - assertEquals(bindings.nVectorArgs, 0); + assertEquals(bindings.nVectorArgs(), 0); } @Test @@ -90,13 +89,13 @@ public void testNestedStructs() { FunctionDescriptor fd = FunctionDescriptor.ofVoid(POINT); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG).insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, { dup(), bufferLoad(0, long.class), vmStore(rdi, long.class), bufferLoad(8, int.class), vmStore(rsi, int.class)}, { vmStore(rax, long.class) }, @@ -104,7 +103,7 @@ public void testNestedStructs() { checkReturnBindings(callingSequence, new Binding[]{}); - assertEquals(bindings.nVectorArgs, 0); + assertEquals(bindings.nVectorArgs(), 0); } @Test @@ -121,13 +120,13 @@ public void testNestedUnion() { FunctionDescriptor fd = FunctionDescriptor.ofVoid(POINT); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG).insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, { dup(), bufferLoad(0, long.class), vmStore(rdi, long.class), bufferLoad(8, long.class), vmStore(rsi, long.class)}, { vmStore(rax, long.class) }, @@ -135,7 +134,7 @@ public void testNestedUnion() { checkReturnBindings(callingSequence, new Binding[]{}); - assertEquals(bindings.nVectorArgs, 0); + assertEquals(bindings.nVectorArgs(), 0); } @Test @@ -151,13 +150,13 @@ public void testNestedStructsUnaligned() { FunctionDescriptor fd = FunctionDescriptor.ofVoid(POINT); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG).insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, { dup(), bufferLoad(0, long.class), vmStore(stackStorage(0), long.class), bufferLoad(8, long.class), vmStore(stackStorage(1), long.class)}, { vmStore(rax, long.class) }, @@ -165,7 +164,7 @@ public void testNestedStructsUnaligned() { checkReturnBindings(callingSequence, new Binding[]{}); - assertEquals(bindings.nVectorArgs, 0); + assertEquals(bindings.nVectorArgs(), 0); } @Test @@ -181,13 +180,13 @@ public void testNestedUnionUnaligned() { FunctionDescriptor fd = FunctionDescriptor.ofVoid(POINT); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG).insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, { dup(), bufferLoad(0, long.class), vmStore(stackStorage(0), long.class), bufferLoad(8, int.class), vmStore(stackStorage(1), int.class)}, { vmStore(rax, long.class) }, @@ -195,7 +194,7 @@ public void testNestedUnionUnaligned() { checkReturnBindings(callingSequence, new Binding[]{}); - assertEquals(bindings.nVectorArgs, 0); + assertEquals(bindings.nVectorArgs(), 0); } @Test @@ -206,13 +205,13 @@ public void testIntegerRegs() { C_INT, C_INT, C_INT, C_INT, C_INT, C_INT); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG).insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, { vmStore(rdi, int.class) }, { vmStore(rsi, int.class) }, { vmStore(rdx, int.class) }, @@ -224,7 +223,7 @@ public void testIntegerRegs() { checkReturnBindings(callingSequence, new Binding[]{}); - assertEquals(bindings.nVectorArgs, 0); + assertEquals(bindings.nVectorArgs(), 0); } @Test @@ -237,13 +236,13 @@ public void testDoubleRegs() { C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG).insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, { vmStore(xmm0, double.class) }, { vmStore(xmm1, double.class) }, { vmStore(xmm2, double.class) }, @@ -257,7 +256,7 @@ public void testDoubleRegs() { checkReturnBindings(callingSequence, new Binding[]{}); - assertEquals(bindings.nVectorArgs, 8); + assertEquals(bindings.nVectorArgs(), 8); } @Test @@ -272,13 +271,13 @@ public void testMixed() { C_FLOAT, C_FLOAT, C_FLOAT, C_FLOAT, C_FLOAT, C_FLOAT); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG).insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, { vmStore(rdi, long.class) }, { vmStore(rsi, long.class) }, { vmStore(rdx, long.class) }, @@ -302,7 +301,7 @@ public void testMixed() { checkReturnBindings(callingSequence, new Binding[]{}); - assertEquals(bindings.nVectorArgs, 8); + assertEquals(bindings.nVectorArgs(), 8); } /** @@ -329,13 +328,13 @@ public void testAbiExample() { C_INT, C_INT, struct, C_INT, C_INT, C_DOUBLE, C_DOUBLE, C_INT, C_INT, C_INT); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG).insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, { vmStore(rdi, int.class) }, { vmStore(rsi, int.class) }, { @@ -355,7 +354,7 @@ public void testAbiExample() { checkReturnBindings(callingSequence, new Binding[]{}); - assertEquals(bindings.nVectorArgs, 3); + assertEquals(bindings.nVectorArgs(), 3); } /** @@ -368,24 +367,24 @@ public void testAbiExample() { */ @Test public void testMemoryAddress() { - MethodType mt = MethodType.methodType(void.class, MemoryAddress.class); + MethodType mt = MethodType.methodType(void.class, MemorySegment.class); FunctionDescriptor fd = FunctionDescriptor.ofVoid( C_POINTER); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG).insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, { unboxAddress(), vmStore(rdi, long.class) }, { vmStore(rax, long.class) }, }); checkReturnBindings(callingSequence, new Binding[]{}); - assertEquals(bindings.nVectorArgs, 0); + assertEquals(bindings.nVectorArgs(), 0); } @Test(dataProvider = "structs") @@ -394,20 +393,20 @@ public void testStruct(MemoryLayout struct, Binding[] expectedBindings) { FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG).insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, expectedBindings, { vmStore(rax, long.class) }, }); checkReturnBindings(callingSequence, new Binding[]{}); - assertEquals(bindings.nVectorArgs, 0); + assertEquals(bindings.nVectorArgs(), 0); } @@ -453,14 +452,14 @@ public void testReturnRegisterStruct() { FunctionDescriptor fd = FunctionDescriptor.of(struct); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, MemorySegment.class, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.appendParameterTypes(long.class).insertParameterTypes(0, MemorySegment.class, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.appendArgumentLayouts(C_LONG).insertArgumentLayouts(0, ADDRESS, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(MemorySegment.class), vmStore(r11, long.class) }, - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r11, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, { vmStore(rax, long.class) } }); @@ -474,7 +473,7 @@ public void testReturnRegisterStruct() { bufferStore(8, long.class) }); - assertEquals(bindings.nVectorArgs, 0); + assertEquals(bindings.nVectorArgs(), 0); } @Test @@ -485,20 +484,20 @@ public void testIMR() { FunctionDescriptor fd = FunctionDescriptor.of(struct); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertTrue(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), MethodType.methodType(void.class, Addressable.class, MemoryAddress.class, long.class)); + assertTrue(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class, long.class)); assertEquals(callingSequence.functionDesc(), FunctionDescriptor.ofVoid(ADDRESS, C_POINTER, C_LONG)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, { unboxAddress(), vmStore(rdi, long.class) }, { vmStore(rax, long.class) } }); checkReturnBindings(callingSequence, new Binding[] {}); - assertEquals(bindings.nVectorArgs, 0); + assertEquals(bindings.nVectorArgs(), 0); } @Test @@ -509,8 +508,8 @@ public void testFloatStructsUpcall() { FunctionDescriptor fd = FunctionDescriptor.of(struct, struct); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, true); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); assertEquals(callingSequence.calleeMethodType(), mt); assertEquals(callingSequence.functionDesc(), fd); @@ -522,7 +521,7 @@ public void testFloatStructsUpcall() { bufferLoad(0, float.class), vmStore(xmm0, float.class) }); - assertEquals(bindings.nVectorArgs, 1); + assertEquals(bindings.nVectorArgs(), 1); } } diff --git a/test/jdk/java/foreign/callarranger/TestWindowsCallArranger.java b/test/jdk/java/foreign/callarranger/TestWindowsCallArranger.java index 0c72d95070904..2deedfa10e69d 100644 --- a/test/jdk/java/foreign/callarranger/TestWindowsCallArranger.java +++ b/test/jdk/java/foreign/callarranger/TestWindowsCallArranger.java @@ -34,22 +34,23 @@ */ import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.Addressable; import jdk.internal.foreign.abi.Binding; import jdk.internal.foreign.abi.CallingSequence; +import jdk.internal.foreign.abi.LinkerOptions; import jdk.internal.foreign.abi.x64.windows.CallArranger; import org.testng.annotations.Test; import java.lang.invoke.MethodType; +import static java.lang.foreign.Linker.Option.firstVariadicArg; import static java.lang.foreign.ValueLayout.ADDRESS; import static jdk.internal.foreign.PlatformLayouts.Win64.*; import static jdk.internal.foreign.abi.Binding.*; import static jdk.internal.foreign.abi.Binding.copy; import static jdk.internal.foreign.abi.x64.X86_64Architecture.*; + import static org.testng.Assert.*; public class TestWindowsCallArranger extends CallArrangerTestBase { @@ -60,13 +61,13 @@ public void testEmpty() { FunctionDescriptor fd = FunctionDescriptor.ofVoid(); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) } + { unboxAddress(), vmStore(r10, long.class) } }); checkReturnBindings(callingSequence, new Binding[]{}); } @@ -77,13 +78,13 @@ public void testIntegerRegs() { FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_INT, C_INT, C_INT, C_INT); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, { vmStore(rcx, int.class) }, { vmStore(rdx, int.class) }, { vmStore(r8, int.class) }, @@ -99,13 +100,13 @@ public void testDoubleRegs() { FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, { vmStore(xmm0, double.class) }, { vmStore(xmm1, double.class) }, { vmStore(xmm2, double.class) }, @@ -123,13 +124,13 @@ public void testMixed() { C_LONG_LONG, C_LONG_LONG, C_FLOAT, C_FLOAT, C_LONG_LONG, C_LONG_LONG, C_FLOAT, C_FLOAT); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, { vmStore(rcx, long.class) }, { vmStore(rdx, long.class) }, { vmStore(xmm2, float.class) }, @@ -154,18 +155,18 @@ public void testAbiExample() { C_DOUBLE, C_DOUBLE, C_DOUBLE, C_INT, C_INT, C_INT); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, { vmStore(rcx, int.class) }, { vmStore(rdx, int.class) }, { copy(structLayout), - unboxAddress(MemorySegment.class), + unboxAddress(), vmStore(r8, long.class) }, { vmStore(r9, int.class) }, @@ -186,18 +187,18 @@ 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).asVariadic(C_INT, C_DOUBLE, C_DOUBLE); + C_INT, C_DOUBLE, C_INT, C_DOUBLE, C_DOUBLE); FunctionDescriptor fdExpected = FunctionDescriptor.ofVoid( ADDRESS, C_INT, C_DOUBLE, C_INT, C_DOUBLE, C_DOUBLE); - CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); + CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false, LinkerOptions.of(firstVariadicArg(2))); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fdExpected); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, { vmStore(rcx, int.class) }, { vmStore(xmm1, double.class) }, { vmStore(r8, int.class) }, @@ -225,13 +226,13 @@ public void testStructRegister() { FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, { bufferLoad(0, long.class), vmStore(rcx, long.class) } }); @@ -255,16 +256,16 @@ public void testStructReference() { FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, { copy(struct), - unboxAddress(MemorySegment.class), + unboxAddress(), vmStore(rcx, long.class) } }); @@ -282,17 +283,17 @@ public void testStructReference() { */ @Test public void testMemoryAddress() { - MethodType mt = MethodType.methodType(void.class, MemoryAddress.class); + MethodType mt = MethodType.methodType(void.class, MemorySegment.class); FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_POINTER); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, { unboxAddress(), vmStore(rcx, long.class) } }); @@ -307,13 +308,13 @@ public void testReturnRegisterStruct() { FunctionDescriptor fd = FunctionDescriptor.of(struct); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, }); checkReturnBindings(callingSequence, @@ -331,13 +332,13 @@ public void testIMR() { FunctionDescriptor fd = FunctionDescriptor.of(struct); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertTrue(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), MethodType.methodType(void.class, Addressable.class, MemoryAddress.class)); + assertTrue(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), FunctionDescriptor.ofVoid(ADDRESS, C_POINTER)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, { unboxAddress(), vmStore(rcx, long.class) } }); @@ -349,10 +350,10 @@ public void testStackStruct() { MemoryLayout struct = MemoryLayout.structLayout(C_POINTER, C_DOUBLE, C_INT); MethodType mt = MethodType.methodType(void.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); + MemorySegment.class, int.class, double.class, MemorySegment.class, + MemorySegment.class, int.class, double.class, MemorySegment.class, + MemorySegment.class, int.class, double.class, MemorySegment.class, + MemorySegment.class, int.class, double.class, MemorySegment.class); FunctionDescriptor fd = FunctionDescriptor.ofVoid( struct, C_INT, C_DOUBLE, C_POINTER, struct, C_INT, C_DOUBLE, C_POINTER, @@ -360,26 +361,26 @@ public void testStackStruct() { struct, C_INT, C_DOUBLE, C_POINTER); CallArranger.Bindings bindings = CallArranger.getBindings(mt, fd, false); - assertFalse(bindings.isInMemoryReturn); - CallingSequence callingSequence = bindings.callingSequence; - assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, Addressable.class)); + assertFalse(bindings.isInMemoryReturn()); + CallingSequence callingSequence = bindings.callingSequence(); + assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); checkArgumentBindings(callingSequence, new Binding[][]{ - { unboxAddress(Addressable.class), vmStore(r10, long.class) }, - { copy(struct), unboxAddress(MemorySegment.class), vmStore(rcx, long.class) }, + { unboxAddress(), vmStore(r10, long.class) }, + { copy(struct), unboxAddress(), vmStore(rcx, long.class) }, { vmStore(rdx, int.class) }, { vmStore(xmm2, double.class) }, { unboxAddress(), vmStore(r9, long.class) }, - { copy(struct), unboxAddress(MemorySegment.class), vmStore(stackStorage(0), long.class) }, + { copy(struct), unboxAddress(), vmStore(stackStorage(0), long.class) }, { vmStore(stackStorage(1), int.class) }, { vmStore(stackStorage(2), double.class) }, { unboxAddress(), vmStore(stackStorage(3), long.class) }, - { copy(struct), unboxAddress(MemorySegment.class), vmStore(stackStorage(4), long.class) }, + { copy(struct), unboxAddress(), vmStore(stackStorage(4), long.class) }, { vmStore(stackStorage(5), int.class) }, { vmStore(stackStorage(6), double.class) }, { unboxAddress(), vmStore(stackStorage(7), long.class) }, - { copy(struct), unboxAddress(MemorySegment.class), vmStore(stackStorage(8), long.class) }, + { copy(struct), unboxAddress(), 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 1fae62d353ea3..d3c96bba9863c 100644 --- a/test/jdk/java/foreign/channels/AbstractChannelsTest.java +++ b/test/jdk/java/foreign/channels/AbstractChannelsTest.java @@ -22,8 +22,9 @@ */ import java.io.IOException; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Random; @@ -51,11 +52,6 @@ interface ThrowingConsumer { void accept(T action) throws X; } - static MemorySession closeableSessionOrNull(MemorySession session) { - return (session.isCloseable()) ? - session : null; - } - static long remaining(ByteBuffer[] buffers) { return Arrays.stream(buffers).mapToLong(ByteBuffer::remaining).sum(); } @@ -72,7 +68,7 @@ static ByteBuffer[] clear(ByteBuffer[] buffers) { static final Random RANDOM = RandomFactory.getRandom(); - static ByteBuffer segmentBufferOfSize(MemorySession session, int size) { + static ByteBuffer segmentBufferOfSize(SegmentScope session, int size) { var segment = MemorySegment.allocateNative(size, 1, session); for (int i = 0; i < size; i++) { segment.set(JAVA_BYTE, i, ((byte)RANDOM.nextInt())); @@ -80,7 +76,7 @@ static ByteBuffer segmentBufferOfSize(MemorySession session, int size) { return segment.asByteBuffer(); } - static ByteBuffer[] segmentBuffersOfSize(int len, MemorySession session, int size) { + static ByteBuffer[] segmentBuffersOfSize(int len, SegmentScope session, int size) { ByteBuffer[] bufs = new ByteBuffer[len]; for (int i = 0; i < len; i++) bufs[i] = segmentBufferOfSize(session, size); @@ -92,7 +88,7 @@ static ByteBuffer[] segmentBuffersOfSize(int len, MemorySession session, int siz * where heap can be from the global session or session-less, and direct are * associated with the given session. */ - static ByteBuffer[] mixedBuffersOfSize(int len, MemorySession session, int size) { + static ByteBuffer[] mixedBuffersOfSize(int len, SegmentScope session, int size) { ByteBuffer[] bufs; boolean atLeastOneSessionBuffer = false; do { @@ -125,75 +121,50 @@ static void assertCauses(Throwable ex, Class... exceptions) } } - @DataProvider(name = "confinedSessions") - public static Object[][] confinedSessions() { + @DataProvider(name = "confinedArenas") + public static Object[][] confinedArenas() { return new Object[][] { - { SessionSupplier.NEW_CONFINED }, + { ArenaSupplier.NEW_CONFINED }, }; } - @DataProvider(name = "sharedSessions") - public static Object[][] sharedSessions() { - return new Object[][] { - { SessionSupplier.NEW_SHARED }, - }; - } - - @DataProvider(name = "closeableSessions") - public static Object[][] closeableSessions() { - return Stream.of(sharedSessions(), confinedSessions()) - .flatMap(Arrays::stream) - .toArray(Object[][]::new); - } - - @DataProvider(name = "implicitSessions") - public static Object[][] implicitSessions() { + @DataProvider(name = "sharedArenas") + public static Object[][] sharedArenas() { return new Object[][] { - { SessionSupplier.GLOBAL }, + { ArenaSupplier.NEW_SHARED }, }; } - @DataProvider(name = "sharedAndImplicitSessions") - public static Object[][] sharedAndImplicitSessions() { - return Stream.of(sharedSessions(), implicitSessions()) - .flatMap(Arrays::stream) - .toArray(Object[][]::new); - } - - @DataProvider(name = "allSessions") - public static Object[][] allSessions() { - return Stream.of(implicitSessions(), closeableSessions()) + @DataProvider(name = "closeableArenas") + public static Object[][] closeableArenas() { + return Stream.of(sharedArenas(), confinedArenas()) .flatMap(Arrays::stream) .toArray(Object[][]::new); } - @DataProvider(name = "sharedSessionsAndTimeouts") - public static Object[][] sharedSessionsAndTimeouts() { + @DataProvider(name = "sharedArenasAndTimeouts") + public static Object[][] sharedArenasAndTimeouts() { return new Object[][] { - { SessionSupplier.NEW_SHARED , 0 }, - { SessionSupplier.NEW_SHARED , 30 }, + { ArenaSupplier.NEW_SHARED , 0 }, + { ArenaSupplier.NEW_SHARED , 30 }, }; } - static class SessionSupplier implements Supplier { + static class ArenaSupplier implements Supplier { - static final Supplier NEW_CONFINED = - new SessionSupplier(MemorySession::openConfined, "newConfinedSession()"); - static final Supplier NEW_SHARED = - new SessionSupplier(MemorySession::openShared, "newSharedSession()"); - static final Supplier NEW_IMPLICIT = - new SessionSupplier(MemorySession::openImplicit, "newImplicitSession()"); - static final Supplier GLOBAL = - new SessionSupplier(MemorySession::global, "globalSession()"); + static final Supplier NEW_CONFINED = + new ArenaSupplier(Arena::openConfined, "confined arena"); + static final Supplier NEW_SHARED = + new ArenaSupplier(Arena::openShared, "shared arena"); - private final Supplier supplier; + private final Supplier supplier; private final String str; - private SessionSupplier(Supplier supplier, String str) { + private ArenaSupplier(Supplier supplier, String str) { this.supplier = supplier; this.str = str; } @Override public String toString() { return str; } - @Override public MemorySession get() { return supplier.get(); } + @Override public Arena get() { return supplier.get(); } } } diff --git a/test/jdk/java/foreign/channels/TestAsyncSocketChannels.java b/test/jdk/java/foreign/channels/TestAsyncSocketChannels.java index 9278c7748a7ca..3091514cc5831 100644 --- a/test/jdk/java/foreign/channels/TestAsyncSocketChannels.java +++ b/test/jdk/java/foreign/channels/TestAsyncSocketChannels.java @@ -33,8 +33,8 @@ */ import java.io.IOException; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.StandardSocketOptions; @@ -67,15 +67,15 @@ public class TestAsyncSocketChannels extends AbstractChannelsTest { static final Class ISE = IllegalStateException.class; /** Tests that confined sessions are not supported. */ - @Test(dataProvider = "confinedSessions") - public void testWithConfined(Supplier sessionSupplier) + @Test(dataProvider = "confinedArenas") + public void testWithConfined(Supplier arenaSupplier) throws Throwable { try (var channel = AsynchronousSocketChannel.open(); var server = AsynchronousServerSocketChannel.open(); var connectedChannel = connectChannels(server, channel); - var session = closeableSessionOrNull(sessionSupplier.get())) { - var segment = MemorySegment.allocateNative(10, 1, session); + var drop = arenaSupplier.get()) { + var segment = MemorySegment.allocateNative(10, 1, drop.scope()); var bb = segment.asByteBuffer(); var bba = new ByteBuffer[] { bb }; List> ioOps = List.of( @@ -100,17 +100,17 @@ public void testWithConfined(Supplier sessionSupplier) } /** Tests that I/O with a closed session throws a suitable exception. */ - @Test(dataProvider = "sharedSessionsAndTimeouts") - public void testIOWithClosedSharedSession(Supplier sessionSupplier, int timeout) + @Test(dataProvider = "sharedArenasAndTimeouts") + public void testIOWithClosedSharedSession(Supplier arenaSupplier, int timeout) throws Exception { try (var channel = AsynchronousSocketChannel.open(); var server = AsynchronousServerSocketChannel.open(); var connectedChannel = connectChannels(server, channel)) { - MemorySession session = sessionSupplier.get(); - ByteBuffer bb = segmentBufferOfSize(session, 64); - ByteBuffer[] buffers = segmentBuffersOfSize(8, session, 32); - ((MemorySession)session).close(); + Arena drop = arenaSupplier.get(); + ByteBuffer bb = segmentBufferOfSize(drop.scope(), 64); + ByteBuffer[] buffers = segmentBuffersOfSize(8, drop.scope(), 32); + drop.close(); { assertCauses(expectThrows(EE, () -> connectedChannel.read(bb).get()), IOE, ISE); } @@ -151,17 +151,17 @@ public void testIOWithClosedSharedSession(Supplier sessionSupplie } /** Tests basic I/O operations work with views over implicit and shared sessions. */ - @Test(dataProvider = "sharedAndImplicitSessions") - public void testBasicIOWithSupportedSession(Supplier sessionSupplier) + @Test(dataProvider = "sharedArenas") + public void testBasicIOWithSupportedSession(Supplier arenaSupplier) throws Exception { - MemorySession session; + Arena drop; try (var asc1 = AsynchronousSocketChannel.open(); var assc = AsynchronousServerSocketChannel.open(); var asc2 = connectChannels(assc, asc1); - var scp = closeableSessionOrNull(session = sessionSupplier.get())) { - MemorySegment segment1 = MemorySegment.allocateNative(10, 1, session); - MemorySegment segment2 = MemorySegment.allocateNative(10, 1, session); + var scp = drop = arenaSupplier.get()) { + MemorySegment segment1 = MemorySegment.allocateNative(10, 1, drop.scope()); + MemorySegment segment2 = MemorySegment.allocateNative(10, 1, drop.scope()); for (int i = 0; i < 10; i++) { segment1.set(JAVA_BYTE, i, (byte) i); } @@ -184,8 +184,8 @@ public void testBasicIOWithSupportedSession(Supplier sessionSuppl assertEquals(bb2.flip(), ByteBuffer.wrap(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); } { // Gathering/Scattering variants - var writeBuffers = mixedBuffersOfSize(16, session, 32); - var readBuffers = mixedBuffersOfSize(16, session, 32); + var writeBuffers = mixedBuffersOfSize(16, drop.scope(), 32); + var readBuffers = mixedBuffersOfSize(16, drop.scope(), 32); long expectedCount = remaining(writeBuffers); var writeHandler = new TestHandler(); asc1.write(writeBuffers, 0, 16, 30L, SECONDS, null, writeHandler); @@ -199,15 +199,15 @@ public void testBasicIOWithSupportedSession(Supplier sessionSuppl } /** Tests that a session is not closeable when there is an outstanding read operation. */ - @Test(dataProvider = "sharedSessionsAndTimeouts") - public void testCloseWithOutstandingRead(Supplier sessionSupplier, int timeout) + @Test(dataProvider = "sharedArenasAndTimeouts") + public void testCloseWithOutstandingRead(Supplier arenaSupplier, int timeout) throws Throwable { try (var asc1 = AsynchronousSocketChannel.open(); var assc = AsynchronousServerSocketChannel.open(); var asc2 = connectChannels(assc, asc1); - var session = closeableSessionOrNull(sessionSupplier.get())) { - var segment = MemorySegment.allocateNative(10, 1, session); + var drop = arenaSupplier.get()) { + var segment = MemorySegment.allocateNative(10, 1, drop.scope()); var bb = segment.asByteBuffer(); var bba = new ByteBuffer[] { bb }; List> readOps = List.of( @@ -221,28 +221,28 @@ public void testCloseWithOutstandingRead(Supplier sessionSupplier var handler = new TestHandler(); ioOp.accept(handler); assertFalse(handler.isDone()); - assertTrue(session.isAlive()); - assertMessage(expectThrows(ISE, () -> session.close()), "Session is acquired by"); + assertTrue(drop.scope().isAlive()); + assertMessage(expectThrows(ISE, () -> drop.close()), "Session is acquired by"); // write to allow the blocking read complete, which will // in turn unlock the session and allow it to be closed. asc2.write(ByteBuffer.wrap(new byte[] { 0x01 })).get(); handler.await().assertCompleteWith(1L); - assertTrue(session.isAlive()); + assertTrue(drop.scope().isAlive()); } } } /** Tests that a session is not closeable when there is an outstanding write operation. */ // Note: limited scenarios are checked, given the 5 sec sleep! - @Test(dataProvider = "sharedSessionsAndTimeouts") - public void testCloseWithOutstandingWrite(Supplier sessionSupplier, int timeout) + @Test(dataProvider = "sharedArenasAndTimeouts") + public void testCloseWithOutstandingWrite(Supplier arenaSupplier, int timeout) throws Throwable { try (var asc1 = AsynchronousSocketChannel.open(); var assc = AsynchronousServerSocketChannel.open(); var asc2 = connectChannels(assc, asc1); - var session = closeableSessionOrNull(sessionSupplier.get())) { + var drop = arenaSupplier.get()) { // number of bytes written final AtomicLong bytesWritten = new AtomicLong(0); @@ -252,7 +252,7 @@ public void testCloseWithOutstandingWrite(Supplier sessionSupplie // write until socket buffer is full so as to create the conditions // for when a write does not complete immediately - var bba = segmentBuffersOfSize(32, session, 128); + var bba = segmentBuffersOfSize(32, drop.scope(), 128); TestHandler handler; outstandingWriteOps.getAndIncrement(); asc1.write(bba, 0, bba.length, timeout, SECONDS, null, @@ -261,7 +261,7 @@ public void completed(Long result, Void att) { super.completed(result, att); bytesWritten.addAndGet(result); if (continueWriting.get()) { - var bba = segmentBuffersOfSize(32, session, 128); + var bba = segmentBuffersOfSize(32, drop.scope(), 128); outstandingWriteOps.getAndIncrement(); asc1.write(bba, 0, bba.length, timeout, SECONDS, null, this); } @@ -271,8 +271,8 @@ public void completed(Long result, Void att) { // give time for socket buffer to fill up. awaitNoFurtherWrites(bytesWritten); - assertMessage(expectThrows(ISE, () -> session.close()), "Session is acquired by"); - assertTrue(session.isAlive()); + assertMessage(expectThrows(ISE, () -> drop.close()), "Session is acquired by"); + assertTrue(drop.scope().isAlive()); // signal handler to stop further writing continueWriting.set(false); @@ -280,7 +280,7 @@ public void completed(Long result, Void att) { // read to allow the outstanding write complete, which will // in turn unlock the session and allow it to be closed. readNBytes(asc2, bytesWritten.get()); - assertTrue(session.isAlive()); + assertTrue(drop.scope().isAlive()); awaitOutstandingWrites(outstandingWriteOps); handler.await(); } diff --git a/test/jdk/java/foreign/channels/TestSocketChannels.java b/test/jdk/java/foreign/channels/TestSocketChannels.java index 4b4aa91c8866e..98cc84e0bf720 100644 --- a/test/jdk/java/foreign/channels/TestSocketChannels.java +++ b/test/jdk/java/foreign/channels/TestSocketChannels.java @@ -30,6 +30,7 @@ * @run testng/othervm TestSocketChannels */ +import java.lang.foreign.Arena; import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.ByteBuffer; @@ -42,7 +43,7 @@ import java.util.stream.Stream; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; + import org.testng.annotations.*; import static java.lang.foreign.ValueLayout.JAVA_BYTE; @@ -56,16 +57,16 @@ public class TestSocketChannels extends AbstractChannelsTest { static final Class ISE = IllegalStateException.class; static final Class WTE = WrongThreadException.class; - @Test(dataProvider = "closeableSessions") - public void testBasicIOWithClosedSegment(Supplier sessionSupplier) + @Test(dataProvider = "closeableArenas") + public void testBasicIOWithClosedSegment(Supplier arenaSupplier) throws Exception { try (var channel = SocketChannel.open(); var server = ServerSocketChannel.open(); var connectedChannel = connectChannels(server, channel)) { - MemorySession session = sessionSupplier.get(); - ByteBuffer bb = segmentBufferOfSize(session, 16); - ((MemorySession)session).close(); + Arena drop = arenaSupplier.get(); + ByteBuffer bb = segmentBufferOfSize(drop.scope(), 16); + drop.close(); assertMessage(expectThrows(ISE, () -> channel.read(bb)), "Already closed"); assertMessage(expectThrows(ISE, () -> channel.read(new ByteBuffer[] {bb})), "Already closed"); assertMessage(expectThrows(ISE, () -> channel.read(new ByteBuffer[] {bb}, 0, 1)), "Already closed"); @@ -75,16 +76,16 @@ public void testBasicIOWithClosedSegment(Supplier sessionSupplier } } - @Test(dataProvider = "closeableSessions") - public void testScatterGatherWithClosedSegment(Supplier sessionSupplier) + @Test(dataProvider = "closeableArenas") + public void testScatterGatherWithClosedSegment(Supplier arenaSupplier) throws Exception { try (var channel = SocketChannel.open(); var server = ServerSocketChannel.open(); var connectedChannel = connectChannels(server, channel)) { - MemorySession session = (MemorySession)sessionSupplier.get(); - ByteBuffer[] buffers = segmentBuffersOfSize(8, session, 16); - session.close(); + Arena drop = arenaSupplier.get(); + ByteBuffer[] buffers = segmentBuffersOfSize(8, drop.scope(), 16); + drop.close(); assertMessage(expectThrows(ISE, () -> channel.write(buffers)), "Already closed"); assertMessage(expectThrows(ISE, () -> channel.read(buffers)), "Already closed"); assertMessage(expectThrows(ISE, () -> channel.write(buffers, 0 ,8)), "Already closed"); @@ -92,17 +93,17 @@ public void testScatterGatherWithClosedSegment(Supplier sessionSu } } - @Test(dataProvider = "allSessions") - public void testBasicIO(Supplier sessionSupplier) + @Test(dataProvider = "closeableArenas") + public void testBasicIO(Supplier arenaSupplier) throws Exception { - MemorySession session; + Arena drop; try (var sc1 = SocketChannel.open(); var ssc = ServerSocketChannel.open(); var sc2 = connectChannels(ssc, sc1); - var scp = closeableSessionOrNull(session = sessionSupplier.get())) { - MemorySegment segment1 = MemorySegment.allocateNative(10, 1, session); - MemorySegment segment2 = MemorySegment.allocateNative(10, 1, session); + var scp = drop = arenaSupplier.get()) { + MemorySegment segment1 = MemorySegment.allocateNative(10, 1, drop.scope()); + MemorySegment segment2 = MemorySegment.allocateNative(10, 1, drop.scope()); for (int i = 0; i < 10; i++) { segment1.set(JAVA_BYTE, i, (byte) i); } @@ -132,15 +133,15 @@ public void testBasicHeapIOWithGlobalSession() throws Exception { } } - @Test(dataProvider = "confinedSessions") - public void testIOOnConfinedFromAnotherThread(Supplier sessionSupplier) + @Test(dataProvider = "confinedArenas") + public void testIOOnConfinedFromAnotherThread(Supplier arenaSupplier) throws Exception { try (var channel = SocketChannel.open(); var server = ServerSocketChannel.open(); var connected = connectChannels(server, channel); - var session = closeableSessionOrNull(sessionSupplier.get())) { - var segment = MemorySegment.allocateNative(10, 1, session); + var drop = arenaSupplier.get()) { + var segment = MemorySegment.allocateNative(10, 1, drop.scope()); ByteBuffer bb = segment.asByteBuffer(); List ioOps = List.of( () -> channel.write(bb), @@ -161,17 +162,17 @@ public void testIOOnConfinedFromAnotherThread(Supplier sessionSup } } - @Test(dataProvider = "allSessions") - public void testScatterGatherIO(Supplier sessionSupplier) + @Test(dataProvider = "closeableArenas") + public void testScatterGatherIO(Supplier arenaSupplier) throws Exception { - MemorySession session; + Arena drop; try (var sc1 = SocketChannel.open(); var ssc = ServerSocketChannel.open(); var sc2 = connectChannels(ssc, sc1); - var scp = closeableSessionOrNull(session = sessionSupplier.get())) { - var writeBuffers = mixedBuffersOfSize(32, session, 64); - var readBuffers = mixedBuffersOfSize(32, session, 64); + var scp = drop = arenaSupplier.get()) { + var writeBuffers = mixedBuffersOfSize(32, drop.scope(), 64); + var readBuffers = mixedBuffersOfSize(32, drop.scope(), 64); long expectedCount = remaining(writeBuffers); assertEquals(writeNBytes(sc1, writeBuffers, 0, 32, expectedCount), expectedCount); assertEquals(readNBytes(sc2, readBuffers, 0, 32, expectedCount), expectedCount); @@ -179,19 +180,19 @@ public void testScatterGatherIO(Supplier sessionSupplier) } } - @Test(dataProvider = "closeableSessions") - public void testBasicIOWithDifferentSessions(Supplier sessionSupplier) + @Test(dataProvider = "closeableArenas") + public void testBasicIOWithDifferentSessions(Supplier arenaSupplier) throws Exception { try (var sc1 = SocketChannel.open(); var ssc = ServerSocketChannel.open(); var sc2 = connectChannels(ssc, sc1); - var session1 = closeableSessionOrNull(sessionSupplier.get()); - var session2 = closeableSessionOrNull(sessionSupplier.get())) { - var writeBuffers = Stream.of(mixedBuffersOfSize(16, session1, 64), mixedBuffersOfSize(16, session2, 64)) + var drop1 = arenaSupplier.get(); + var drop2 = arenaSupplier.get()) { + var writeBuffers = Stream.of(mixedBuffersOfSize(16, drop1.scope(), 64), mixedBuffersOfSize(16, drop2.scope(), 64)) .flatMap(Arrays::stream) .toArray(ByteBuffer[]::new); - var readBuffers = Stream.of(mixedBuffersOfSize(16, session1, 64), mixedBuffersOfSize(16, session2, 64)) + var readBuffers = Stream.of(mixedBuffersOfSize(16, drop1.scope(), 64), mixedBuffersOfSize(16, drop2.scope(), 64)) .flatMap(Arrays::stream) .toArray(ByteBuffer[]::new); diff --git a/test/jdk/java/foreign/dontrelease/TestDontRelease.java b/test/jdk/java/foreign/dontrelease/TestDontRelease.java new file mode 100644 index 0000000000000..1d8ca974c9080 --- /dev/null +++ b/test/jdk/java/foreign/dontrelease/TestDontRelease.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @enablePreview + * @library ../ /test/lib + * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" + * @run testng/othervm --enable-native-access=ALL-UNNAMED TestDontRelease + */ + +import org.testng.annotations.Test; + +import java.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.MemorySegment; +import java.lang.invoke.MethodHandle; + +import static java.lang.foreign.ValueLayout.ADDRESS; +import static java.lang.foreign.ValueLayout.JAVA_INT; +import static org.testng.Assert.assertTrue; + +public class TestDontRelease extends NativeTestHelper { + + static { + System.loadLibrary("DontRelease"); + } + + @Test + public void testDontRelease() { + MethodHandle handle = downcallHandle("test_ptr", FunctionDescriptor.ofVoid(ADDRESS)); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = arena.allocate(JAVA_INT); + arena.scope().whileAlive(() -> { + Thread t = new Thread(() -> { + try { + // acquire of the segment should fail here, + // due to wrong thread + handle.invokeExact(segment); + } catch (Throwable e) { + // catch the exception. + assertTrue(e instanceof WrongThreadException); + assertTrue(e.getMessage().matches(".*Attempted access outside owning thread.*")); + } + }); + t.start(); + try { + t.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + // the downcall above should not have called release on the session + // so doing it here should succeed without error + }); + } + } +} + diff --git a/test/jdk/java/foreign/dontrelease/libDontRelease.c b/test/jdk/java/foreign/dontrelease/libDontRelease.c new file mode 100644 index 0000000000000..c7a8f74308315 --- /dev/null +++ b/test/jdk/java/foreign/dontrelease/libDontRelease.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifdef _WIN64 +#define EXPORT __declspec(dllexport) +#else +#define EXPORT +#endif + +EXPORT void test_ptr(void* ptr) {} diff --git a/test/jdk/java/foreign/enablenativeaccess/NativeAccessDynamicMain.java b/test/jdk/java/foreign/enablenativeaccess/NativeAccessDynamicMain.java new file mode 100644 index 0000000000000..c76cdfdbe86d2 --- /dev/null +++ b/test/jdk/java/foreign/enablenativeaccess/NativeAccessDynamicMain.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.lang.Module; +import java.lang.ModuleLayer; +import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReference; +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; +import java.util.stream.Collectors; + +// This class creates a dynamic module layer and loads the +// panama_module in it. enableNativeAccess on that dynamic +// module is called depending on the command line option. +// +// Usage: +// java --enable-native-access=ALL-UNNAMED NativeAccessDynamicMain [main-args] +public class NativeAccessDynamicMain { + public static void main(String[] args) throws Exception { + String modulePath = args[0]; + String moduleAndClsName = args[1]; + boolean enableNativeAccess = Boolean.parseBoolean(args[2]); + String[] mainArgs = args.length > 2? Arrays.copyOfRange(args, 3, args.length) : new String[0]; + + int idx = moduleAndClsName.indexOf('/'); + String moduleName = moduleAndClsName.substring(0, idx); + String className = moduleAndClsName.substring(idx+1); + + Path[] paths = Stream.of(modulePath.split(File.pathSeparator)) + .map(Paths::get) + .toArray(Path[]::new); + ModuleFinder mf = ModuleFinder.of(paths); + var mrefs = mf.findAll(); + if (mrefs.isEmpty()) { + throw new RuntimeException("No modules module path: " + modulePath); + } + + var rootMods = mrefs.stream(). + map(mr->mr.descriptor().name()). + collect(Collectors.toSet()); + + ModuleLayer boot = ModuleLayer.boot(); + var conf = boot.configuration(). + resolve(mf, ModuleFinder.of(), rootMods); + String firstMod = rootMods.iterator().next(); + URLClassLoader cl = new URLClassLoader(new URL[] { paths[0].toFile().toURL() }); + ModuleLayer.Controller controller = boot.defineModulesWithOneLoader(conf, List.of(boot), cl); + ModuleLayer layer = controller.layer(); + Module mod = layer.findModule(firstMod).get(); + + // conditionally grant native access to the dynamic module created + if (enableNativeAccess) { + controller.enableNativeAccess(mod); + } + Class mainCls = Class.forName(mod, className); + var main = mainCls.getMethod("main", String[].class); + main.invoke(null, (Object)mainArgs); + } +} diff --git a/test/jdk/java/foreign/enablenativeaccess/TestEnableNativeAccessDynamic.java b/test/jdk/java/foreign/enablenativeaccess/TestEnableNativeAccessDynamic.java new file mode 100644 index 0000000000000..6be989ec1acb3 --- /dev/null +++ b/test/jdk/java/foreign/enablenativeaccess/TestEnableNativeAccessDynamic.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @enablePreview + * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" + * @requires !vm.musl + * + * @library /test/lib + * @build TestEnableNativeAccessDynamic + * panama_module/* + NativeAccessDynamicMain + * @run testng/othervm/timeout=180 TestEnableNativeAccessDynamic + * @summary Test for dynamically setting --enable-native-access flag for a module + */ + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +@Test +public class TestEnableNativeAccessDynamic { + + static final String MODULE_PATH = System.getProperty("jdk.module.path"); + + static final String PANAMA_MAIN = "panama_module/org.openjdk.foreigntest.PanamaMainDirect"; + static final String PANAMA_REFLECTION = "panama_module/org.openjdk.foreigntest.PanamaMainReflection"; + static final String PANAMA_INVOKE = "panama_module/org.openjdk.foreigntest.PanamaMainInvoke"; + static final String PANAMA_JNI = "panama_module/org.openjdk.foreigntest.PanamaMainJNI"; + + /** + * Represents the expected result of a test. + */ + static final class Result { + private final boolean success; + private final List expectedOutput = new ArrayList<>(); + private final List notExpectedOutput = new ArrayList<>(); + + Result(boolean success) { + this.success = success; + } + + Result expect(String msg) { + expectedOutput.add(msg); + return this; + } + + Result doNotExpect(String msg) { + notExpectedOutput.add(msg); + return this; + } + + boolean shouldSucceed() { + return success; + } + + Stream expectedOutput() { + return expectedOutput.stream(); + } + + Stream notExpectedOutput() { + return notExpectedOutput.stream(); + } + + @Override + public String toString() { + String s = (success) ? "success" : "failure"; + for (String msg : expectedOutput) { + s += "/" + msg; + } + return s; + } + } + + static Result success() { + return new Result(true); + } + + static Result successNoWarning() { + return success().doNotExpect("WARNING"); + } + + static Result failWithError(String expectedOutput) { + return new Result(false).expect(expectedOutput); + } + + @DataProvider(name = "succeedCases") + public Object[][] succeedCases() { + return new Object[][] { + { "panama_enable_native_access", PANAMA_MAIN, successNoWarning() }, + { "panama_enable_native_access_reflection", PANAMA_REFLECTION, successNoWarning() }, + { "panama_enable_native_access_invoke", PANAMA_INVOKE, successNoWarning() }, + }; + } + + @DataProvider(name = "failureCases") + public Object[][] failureCases() { + String errMsg = "Illegal native access from: module panama_module"; + return new Object[][] { + { "panama_enable_native_access_fail", PANAMA_MAIN, failWithError(errMsg) }, + { "panama_enable_native_access_fail_reflection", PANAMA_REFLECTION, failWithError(errMsg) }, + { "panama_enable_native_access_fail_invoke", PANAMA_INVOKE, failWithError(errMsg) }, + }; + } + + /** + * Checks an expected result with the output captured by the given + * OutputAnalyzer. + */ + void checkResult(Result expectedResult, OutputAnalyzer outputAnalyzer) { + expectedResult.expectedOutput().forEach(outputAnalyzer::shouldContain); + expectedResult.notExpectedOutput().forEach(outputAnalyzer::shouldNotContain); + int exitValue = outputAnalyzer.getExitValue(); + if (expectedResult.shouldSucceed()) { + assertTrue(exitValue == 0); + } else { + assertTrue(exitValue != 0); + } + } + + /** + * Runs the test to execute the given test action. The VM is run with the + * given VM options and the output checked to see that it matches the + * expected result. + */ + OutputAnalyzer run(String action, String moduleAndCls, boolean enableNativeAccess, + Result expectedResult, boolean panamaModuleInBootLayer) throws Exception + { + List list = new ArrayList<>(); + list.add("--enable-preview"); + if (panamaModuleInBootLayer) { + list.addAll(List.of("-p", MODULE_PATH)); + list.add("--add-modules=panama_module"); + list.add("--enable-native-access=panama_module"); + } else { + list.add("--enable-native-access=ALL-UNNAMED"); + } + list.addAll(List.of("NativeAccessDynamicMain", MODULE_PATH, + moduleAndCls, Boolean.toString(enableNativeAccess), action)); + String[] opts = list.toArray(String[]::new); + OutputAnalyzer outputAnalyzer = ProcessTools + .executeTestJava(opts) + .outputTo(System.out) + .errorTo(System.out); + checkResult(expectedResult, outputAnalyzer); + return outputAnalyzer; + } + + @Test(dataProvider = "succeedCases") + public void testSucceed(String action, String moduleAndCls, + Result expectedResult) throws Exception { + run(action, moduleAndCls, true, expectedResult, false); + } + + @Test(dataProvider = "failureCases") + public void testFailures(String action, String moduleAndCls, + Result expectedResult) throws Exception { + run(action, moduleAndCls, false, expectedResult, false); + } + + // make sure that having a same named module in boot layer with native access + // does not influence same named dynamic module. + @Test(dataProvider = "failureCases") + public void testFailuresWithPanamaModuleInBootLayer(String action, String moduleAndCls, + Result expectedResult) throws Exception { + run(action, moduleAndCls, false, expectedResult, true); + } +} diff --git a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainDirect.java b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainDirect.java index e042a773fda31..3743ac73835ae 100644 --- a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainDirect.java +++ b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainDirect.java @@ -24,6 +24,7 @@ package org.openjdk.foreigntest; import java.lang.foreign.*; +import java.lang.foreign.SegmentScope; public class PanamaMainDirect { public static void main(String[] args) { @@ -39,7 +40,7 @@ public static void testDirectAccessCLinker() { public static void testDirectAccessMemorySegment() { System.out.println("Trying to get MemorySegment"); - MemorySegment.ofAddress(MemoryAddress.NULL, 4000, MemorySession.global()); + MemorySegment.ofAddress(0, 4000, SegmentScope.global()); System.out.println("Got MemorySegment"); } } 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 b50ebe9ca8030..0d4d7bb4bb004 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 @@ -24,6 +24,7 @@ package org.openjdk.foreigntest; import java.lang.foreign.*; +import java.lang.foreign.SegmentScope; import java.lang.invoke.*; public class PanamaMainInvoke { @@ -43,8 +44,8 @@ public static void testInvokenativeLinker() throws Throwable { public static void testInvokeMemorySegment() throws Throwable { System.out.println("Trying to get MemorySegment"); var mh = MethodHandles.lookup().findStatic(MemorySegment.class, "ofAddress", - MethodType.methodType(MemorySegment.class, MemoryAddress.class, long.class, MemorySession.class)); - var seg = (MemorySegment)mh.invokeExact(MemoryAddress.NULL, 4000L, MemorySession.global()); + MethodType.methodType(MemorySegment.class, long.class, long.class, SegmentScope.class)); + var seg = (MemorySegment)mh.invokeExact(0L, 4000L, SegmentScope.global()); System.out.println("Got MemorySegment"); } } 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 7a8eab027e9e8..68aebc73ab558 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 @@ -24,6 +24,7 @@ package org.openjdk.foreigntest; import java.lang.foreign.*; +import java.lang.foreign.SegmentScope; import java.lang.reflect.Method; public class PanamaMainReflection { @@ -41,8 +42,8 @@ public static void testReflectionnativeLinker() throws Throwable { public static void testReflectionMemorySegment() throws Throwable { System.out.println("Trying to get MemorySegment"); - Method method = MemorySegment.class.getDeclaredMethod("ofAddress", MemoryAddress.class, long.class, MemorySession.class); - method.invoke(null, MemoryAddress.NULL, 4000L, MemorySession.global()); + Method method = MemorySegment.class.getDeclaredMethod("ofAddress", long.class, long.class, SegmentScope.class); + method.invoke(null, 0L, 4000L, SegmentScope.global()); System.out.println("Got MemorySegment"); } } 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 319fcde66004f..090a57bcc7bcd 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 @@ -23,13 +23,11 @@ package handle.invoker; -import java.lang.foreign.Addressable; +import java.lang.foreign.SegmentScope; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.lang.foreign.SegmentAllocator; import java.lang.foreign.SymbolLookup; import java.lang.foreign.ValueLayout; @@ -71,11 +69,10 @@ static void addDefaultMapping(Class carrier, Z value) { addDefaultMapping(MethodHandle.class, MethodHandles.identity(int.class)); addDefaultMapping(Charset.class, Charset.defaultCharset()); addDefaultMapping(MethodType.class, MethodType.methodType(void.class)); - addDefaultMapping(MemoryAddress.class, MemoryAddress.NULL); - addDefaultMapping(Addressable.class, MemoryAddress.NULL); + addDefaultMapping(MemorySegment.class, MemorySegment.NULL); addDefaultMapping(MemoryLayout.class, ValueLayout.JAVA_INT); addDefaultMapping(FunctionDescriptor.class, FunctionDescriptor.ofVoid()); - addDefaultMapping(MemorySession.class, MemorySession.openImplicit()); + addDefaultMapping(SegmentScope.class, SegmentScope.auto()); addDefaultMapping(SegmentAllocator.class, SegmentAllocator.prefixAllocator(MemorySegment.ofArray(new byte[10]))); addDefaultMapping(ValueLayout.OfByte.class, ValueLayout.JAVA_BYTE); addDefaultMapping(ValueLayout.OfBoolean.class, ValueLayout.JAVA_BOOLEAN); 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 f3f5c6238e997..6bd7121c651e1 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,19 +23,15 @@ package handle.lookup; +import java.lang.foreign.SegmentScope; import java.lang.foreign.Linker; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; -import java.lang.foreign.Addressable; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.lang.foreign.SymbolLookup; -import java.lang.foreign.VaList; -import java.lang.foreign.ValueLayout; import java.nio.file.Path; @@ -54,120 +50,15 @@ static Object[][] restrictedMethods() { return new Object[][]{ { MethodHandles.lookup().findStatic(Linker.class, "nativeLinker", MethodType.methodType(Linker.class)), "Linker::nativeLinker" }, - { MethodHandles.lookup().findStatic(VaList.class, "ofAddress", - MethodType.methodType(VaList.class, MemoryAddress.class, MemorySession.class)), - "VaList::ofAddress/1" }, { MethodHandles.lookup().findStatic(MemorySegment.class, "ofAddress", - MethodType.methodType(MemorySegment.class, MemoryAddress.class, long.class, MemorySession.class)), - "MemorySegment::ofAddress" }, + MethodType.methodType(MemorySegment.class, long.class, long.class, SegmentScope.class)), + "MemorySegment::ofAddressNative" }, { MethodHandles.lookup().findStatic(SymbolLookup.class, "libraryLookup", - MethodType.methodType(SymbolLookup.class, String.class, MemorySession.class)), + MethodType.methodType(SymbolLookup.class, String.class, SegmentScope.class)), "SymbolLookup::libraryLookup(String)" }, { MethodHandles.lookup().findStatic(SymbolLookup.class, "libraryLookup", - MethodType.methodType(SymbolLookup.class, Path.class, MemorySession.class)), + MethodType.methodType(SymbolLookup.class, Path.class, SegmentScope.class)), "SymbolLookup::libraryLookup(Path)" }, - { 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" }, - { MethodHandles.lookup().findVirtual(MemoryAddress.class, "getAtIndex", - MethodType.methodType(char.class, ValueLayout.OfChar.class, long.class)), - "MemoryAddress::getAtIndex/char" }, - { MethodHandles.lookup().findVirtual(MemoryAddress.class, "getAtIndex", - MethodType.methodType(short.class, ValueLayout.OfShort.class, long.class)), - "MemoryAddress::getAtIndex/short" }, - { MethodHandles.lookup().findVirtual(MemoryAddress.class, "getAtIndex", - MethodType.methodType(int.class, ValueLayout.OfInt.class, long.class)), - "MemoryAddress::getAtIndex/int" }, - { MethodHandles.lookup().findVirtual(MemoryAddress.class, "getAtIndex", - MethodType.methodType(float.class, ValueLayout.OfFloat.class, long.class)), - "MemoryAddress::getAtIndex/float" }, - { MethodHandles.lookup().findVirtual(MemoryAddress.class, "getAtIndex", - MethodType.methodType(long.class, ValueLayout.OfLong.class, long.class)), - "MemoryAddress::getAtIndex/long" }, - { MethodHandles.lookup().findVirtual(MemoryAddress.class, "getAtIndex", - MethodType.methodType(double.class, ValueLayout.OfDouble.class, long.class)), - "MemoryAddress::getAtIndex/double" }, - { MethodHandles.lookup().findVirtual(MemoryAddress.class, "getAtIndex", - MethodType.methodType(MemoryAddress.class, ValueLayout.OfAddress.class, long.class)), - "MemoryAddress::getAtIndex/address" }, - { MethodHandles.lookup().findVirtual(MemoryAddress.class, "setAtIndex", - MethodType.methodType(void.class, ValueLayout.OfChar.class, long.class, char.class)), - "MemoryAddress::setAtIndex/char" }, - { MethodHandles.lookup().findVirtual(MemoryAddress.class, "setAtIndex", - MethodType.methodType(void.class, ValueLayout.OfShort.class, long.class, short.class)), - "MemoryAddress::setAtIndex/short" }, - { MethodHandles.lookup().findVirtual(MemoryAddress.class, "setAtIndex", - MethodType.methodType(void.class, ValueLayout.OfInt.class, long.class, int.class)), - "MemoryAddress::setAtIndex/int" }, - { MethodHandles.lookup().findVirtual(MemoryAddress.class, "setAtIndex", - MethodType.methodType(void.class, ValueLayout.OfFloat.class, long.class, float.class)), - "MemoryAddress::setAtIndex/float" }, - { MethodHandles.lookup().findVirtual(MemoryAddress.class, "setAtIndex", - MethodType.methodType(void.class, ValueLayout.OfLong.class, long.class, long.class)), - "MemoryAddress::set/long" }, - { MethodHandles.lookup().findVirtual(MemoryAddress.class, "setAtIndex", - MethodType.methodType(void.class, ValueLayout.OfDouble.class, long.class, double.class)), - "MemoryAddress::set/double" }, - { MethodHandles.lookup().findVirtual(MemoryAddress.class, "setAtIndex", - 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/libNull.c b/test/jdk/java/foreign/libNull.c new file mode 100644 index 0000000000000..52ea8493f216e --- /dev/null +++ b/test/jdk/java/foreign/libNull.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifdef _WIN64 +#define EXPORT __declspec(dllexport) +#else +#define EXPORT +#endif + +#include + +EXPORT void* get_null() { return NULL; } diff --git a/test/jdk/java/foreign/loaderLookup/TestLoaderLookup.java b/test/jdk/java/foreign/loaderLookup/TestLoaderLookup.java index aff471cc241ef..b6a4b82616ba0 100644 --- a/test/jdk/java/foreign/loaderLookup/TestLoaderLookup.java +++ b/test/jdk/java/foreign/loaderLookup/TestLoaderLookup.java @@ -42,11 +42,11 @@ public static void main(String[] args) throws ReflectiveOperationException { ClassLoader loader1 = newClassLoader("lookup"); Class lookup = loader1.loadClass("lookup.Lookup"); Method fooSymbol = lookup.getDeclaredMethod("fooSymbol"); - Addressable foo = (Addressable)fooSymbol.invoke(null); + MemorySegment foo = (MemorySegment) fooSymbol.invoke(null); ClassLoader loader2 = newClassLoader("invoker"); Class invoker = loader2.loadClass("invoker.Invoker"); - Method invoke = invoker.getDeclaredMethod("invoke", Addressable.class); + Method invoke = invoker.getDeclaredMethod("invoke", MemorySegment.class); invoke.invoke(null, foo); loader1 = null; diff --git a/test/jdk/java/foreign/loaderLookup/TestLoaderLookupJNI.java b/test/jdk/java/foreign/loaderLookup/TestLoaderLookupJNI.java index 1d981722fc074..f5d0a8073a5ad 100644 --- a/test/jdk/java/foreign/loaderLookup/TestLoaderLookupJNI.java +++ b/test/jdk/java/foreign/loaderLookup/TestLoaderLookupJNI.java @@ -42,10 +42,10 @@ public class TestLoaderLookupJNI { @Test void testLoaderLookupJNI() { SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); - assertTrue(loaderLookup.lookup("Java_TestLoaderLookupJNI_loaderLookup0").isPresent()); + assertTrue(loaderLookup.find("Java_TestLoaderLookupJNI_loaderLookup0").isPresent()); // now try calling via JNI loaderLookup = loaderLookup0(); // lookup backed by application loader, so can see same symbols - assertTrue(loaderLookup.lookup("Java_TestLoaderLookupJNI_loaderLookup0").isPresent()); + assertTrue(loaderLookup.find("Java_TestLoaderLookupJNI_loaderLookup0").isPresent()); } static native SymbolLookup loaderLookup0(); diff --git a/test/jdk/java/foreign/loaderLookup/invoker/Invoker.java b/test/jdk/java/foreign/loaderLookup/invoker/Invoker.java index eb188719a98c6..06bf9eac4a875 100644 --- a/test/jdk/java/foreign/loaderLookup/invoker/Invoker.java +++ b/test/jdk/java/foreign/loaderLookup/invoker/Invoker.java @@ -26,7 +26,7 @@ import java.lang.foreign.*; public class Invoker { - public static void invoke(Addressable symbol) throws Throwable { + public static void invoke(MemorySegment symbol) throws Throwable { var linker = Linker.nativeLinker(); var handle = linker.downcallHandle(symbol, FunctionDescriptor.ofVoid()); handle.invokeExact(); diff --git a/test/jdk/java/foreign/loaderLookup/lookup/Lookup.java b/test/jdk/java/foreign/loaderLookup/lookup/Lookup.java index 00946f75c2084..6efa9e0ac091e 100644 --- a/test/jdk/java/foreign/loaderLookup/lookup/Lookup.java +++ b/test/jdk/java/foreign/loaderLookup/lookup/Lookup.java @@ -23,7 +23,7 @@ package lookup; -import java.lang.foreign.Addressable; +import java.lang.foreign.MemorySegment; import java.lang.foreign.SymbolLookup; public class Lookup { @@ -31,7 +31,7 @@ public class Lookup { System.loadLibrary("Foo"); } - public static Addressable fooSymbol() { - return SymbolLookup.loaderLookup().lookup("foo").get(); + public static MemorySegment fooSymbol() { + return SymbolLookup.loaderLookup().find("foo").get(); } } diff --git a/test/jdk/java/foreign/normalize/TestNormalize.java b/test/jdk/java/foreign/normalize/TestNormalize.java new file mode 100644 index 0000000000000..0ba8ef6d36f83 --- /dev/null +++ b/test/jdk/java/foreign/normalize/TestNormalize.java @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @enablePreview + * @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 + * -Xbatch + * -XX:CompileCommand=dontinline,TestNormalize::doCall* + * TestNormalize + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +import static java.lang.foreign.ValueLayout.ADDRESS; +import static java.lang.foreign.ValueLayout.JAVA_BOOLEAN; +import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static java.lang.foreign.ValueLayout.JAVA_CHAR; +import static java.lang.foreign.ValueLayout.JAVA_INT; +import static java.lang.foreign.ValueLayout.JAVA_SHORT; +import static org.testng.Assert.assertEquals; + +// test normalization of smaller than int primitive types +public class TestNormalize extends NativeTestHelper { + + private static final Linker LINKER = Linker.nativeLinker(); + private static final MethodHandle SAVE_BOOLEAN_AS_INT; + private static final MethodHandle SAVE_BYTE_AS_INT; + private static final MethodHandle SAVE_SHORT_AS_INT; + private static final MethodHandle SAVE_CHAR_AS_INT; + + private static final MethodHandle BOOLEAN_TO_INT; + private static final MethodHandle BYTE_TO_INT; + private static final MethodHandle SHORT_TO_INT; + private static final MethodHandle CHAR_TO_INT; + + private static final MethodHandle NATIVE_BOOLEAN_TO_INT; + + private static final int BOOLEAN_HOB_MASK = ~0b1; + private static final int BYTE_HOB_MASK = ~0xFF; + private static final int SHORT_HOB_MASK = ~0xFFFF; + private static final int CHAR_HOB_MASK = ~0xFFFF; + + private static final MethodHandle SAVE_BOOLEAN; + + static { + System.loadLibrary("Normalize"); + + try { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + SAVE_BOOLEAN_AS_INT = lookup.findStatic(TestNormalize.class, "saveBooleanAsInt", MethodType.methodType(void.class, boolean.class, int[].class)); + SAVE_BYTE_AS_INT = lookup.findStatic(TestNormalize.class, "saveByteAsInt", MethodType.methodType(void.class, byte.class, int[].class)); + SAVE_SHORT_AS_INT = lookup.findStatic(TestNormalize.class, "saveShortAsInt", MethodType.methodType(void.class, short.class, int[].class)); + SAVE_CHAR_AS_INT = lookup.findStatic(TestNormalize.class, "saveCharAsInt", MethodType.methodType(void.class, char.class, int[].class)); + + BOOLEAN_TO_INT = lookup.findStatic(TestNormalize.class, "booleanToInt", MethodType.methodType(int.class, boolean.class)); + BYTE_TO_INT = lookup.findStatic(TestNormalize.class, "byteToInt", MethodType.methodType(int.class, byte.class)); + SHORT_TO_INT = lookup.findStatic(TestNormalize.class, "shortToInt", MethodType.methodType(int.class, short.class)); + CHAR_TO_INT = lookup.findStatic(TestNormalize.class, "charToInt", MethodType.methodType(int.class, char.class)); + + NATIVE_BOOLEAN_TO_INT = LINKER.downcallHandle(findNativeOrThrow("int_identity"), FunctionDescriptor.of(JAVA_INT, JAVA_BOOLEAN)); + + SAVE_BOOLEAN = lookup.findStatic(TestNormalize.class, "saveBoolean", MethodType.methodType(void.class, boolean.class, boolean[].class)); + } catch (ReflectiveOperationException e) { + throw new ExceptionInInitializerError(e); + } + } + + // The idea of this test is that we pass a 'dirty' int value down to native code, and then receive it back + // as the argument to an upcall, as well as the result of the downcall, but with a sub-int type (boolean, byte, short, char). + // When we do either of those, argument normalization should take place, so that the resulting value is sane (1). + // After that we convert the value back to int again, the JVM can/will skip value normalization here. + // We then check the high order bits of the resulting int. If argument normalization took place at (1), they should all be 0. + @Test(dataProvider = "cases") + public void testNormalize(ValueLayout layout, int testValue, int hobMask, MethodHandle toInt, MethodHandle saver) throws Throwable { + // use actual type as parameter type to test upcall arg normalization + FunctionDescriptor upcallDesc = FunctionDescriptor.ofVoid(layout); + // use actual type as return type to test downcall return normalization + FunctionDescriptor downcallDesc = FunctionDescriptor.of(layout, ADDRESS, JAVA_INT); + + MemorySegment target = findNativeOrThrow("test"); + MethodHandle downcallHandle = LINKER.downcallHandle(target, downcallDesc); + downcallHandle = MethodHandles.filterReturnValue(downcallHandle, toInt); + + try (Arena arena = Arena.openConfined()) { + int[] box = new int[1]; + saver = MethodHandles.insertArguments(saver, 1, box); + MemorySegment upcallStub = LINKER.upcallStub(saver, upcallDesc, arena.scope()); + int dirtyValue = testValue | hobMask; // set all bits that should not be set + + // test after JIT as well + for (int i = 0; i < 20_000; i++) { + doCall(downcallHandle, upcallStub, box, dirtyValue, hobMask); + } + } + } + + private static void doCall(MethodHandle downcallHandle, MemorySegment upcallStub, + int[] box, int dirtyValue, int hobMask) throws Throwable { + int result = (int) downcallHandle.invokeExact(upcallStub, dirtyValue); + assertEquals(box[0] & hobMask, 0); // check normalized upcall arg + assertEquals(result & hobMask, 0); // check normalized downcall return value + } + + public static void saveBooleanAsInt(boolean b, int[] box) { + box[0] = booleanToInt(b); + } + public static void saveByteAsInt(byte b, int[] box) { + box[0] = byteToInt(b); + } + public static void saveShortAsInt(short s, int[] box) { + box[0] = shortToInt(s); + } + public static void saveCharAsInt(char c, int[] box) { + box[0] = charToInt(c); + } + + public static int booleanToInt(boolean b) { + try { + return (int) NATIVE_BOOLEAN_TO_INT.invokeExact(b); // FIXME do in pure Java? + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + public static int byteToInt(byte b) { + return b; + } + public static int charToInt(char c) { + return c; + } + public static int shortToInt(short s) { + return s; + } + + @DataProvider + public static Object[][] cases() { + return new Object[][] { + { JAVA_BOOLEAN, booleanToInt(true), BOOLEAN_HOB_MASK, BOOLEAN_TO_INT, SAVE_BOOLEAN_AS_INT }, + { JAVA_BYTE, byteToInt((byte) 42), BYTE_HOB_MASK, BYTE_TO_INT, SAVE_BYTE_AS_INT }, + { JAVA_SHORT, shortToInt((short) 42), SHORT_HOB_MASK, SHORT_TO_INT, SAVE_SHORT_AS_INT }, + { JAVA_CHAR, charToInt('a'), CHAR_HOB_MASK, CHAR_TO_INT, SAVE_CHAR_AS_INT } + }; + } + + // test which int values are considered true and false + // we currently convert any int with a non-zero first byte to true, otherwise false. + @Test(dataProvider = "bools") + public void testBool(int testValue, boolean expected) throws Throwable { + MemorySegment addr = findNativeOrThrow("test"); + MethodHandle target = LINKER.downcallHandle(addr, FunctionDescriptor.of(JAVA_BOOLEAN, ADDRESS, JAVA_INT)); + + boolean[] box = new boolean[1]; + MethodHandle upcallTarget = MethodHandles.insertArguments(SAVE_BOOLEAN, 1, box); + + try (Arena arena = Arena.openConfined()) { + MemorySegment callback = LINKER.upcallStub(upcallTarget, FunctionDescriptor.ofVoid(JAVA_BOOLEAN), arena.scope()); + boolean result = (boolean) target.invokeExact(callback, testValue); + assertEquals(box[0], expected); + assertEquals(result, expected); + } + } + + private static void saveBoolean(boolean b, boolean[] box) { + box[0] = b; + } + + @DataProvider + public static Object[][] bools() { + return new Object[][]{ + { 0b10, true }, // zero least significant bit, but non-zero first byte + { 0b1_0000_0000, false } // zero first byte + }; + } +} diff --git a/test/jdk/java/foreign/normalize/libNormalize.c b/test/jdk/java/foreign/normalize/libNormalize.c new file mode 100644 index 0000000000000..4a21551278fe4 --- /dev/null +++ b/test/jdk/java/foreign/normalize/libNormalize.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifdef _WIN64 +#define EXPORT __declspec(dllexport) +#else +#define EXPORT +#endif + +// we use 'int' here to make sure the native code doesn't touch any of the bits +// the important part is that our Java code performs argument normalization +EXPORT int test(void (*cb)(int), int x) { + cb(x); // check upcall arg normalization + return x; // check return value normalization +} + +EXPORT int int_identity(int x) { + return x; +} diff --git a/test/jdk/java/foreign/passheapsegment/TestPassHeapSegment.java b/test/jdk/java/foreign/passheapsegment/TestPassHeapSegment.java new file mode 100644 index 0000000000000..9a745113e3fcb --- /dev/null +++ b/test/jdk/java/foreign/passheapsegment/TestPassHeapSegment.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @enablePreview + * @library ../ /test/lib + * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" + * @run testng/othervm --enable-native-access=ALL-UNNAMED TestPassHeapSegment + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.MemorySegment; +import java.lang.invoke.MethodHandle; + +import static java.lang.foreign.ValueLayout.ADDRESS; + +public class TestPassHeapSegment extends UpcallTestHelper { + + static { + System.loadLibrary("PassHeapSegment"); + } + + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = ".*Heap segment not allowed.*") + public void testNoHeapArgs() throws Throwable { + MethodHandle handle = downcallHandle("test_args", FunctionDescriptor.ofVoid(ADDRESS)); + MemorySegment segment = MemorySegment.ofArray(new byte[]{ 0, 1, 2 }); + handle.invoke(segment); // should throw + } + + @Test(dataProvider = "specs") + public void testNoHeapReturns(boolean spec) throws IOException, InterruptedException { + runInNewProcess(Runner.class, spec).assertStdErrContains("Heap segment not allowed"); + } + + public static class Runner { + + static { + System.loadLibrary("PassHeapSegment"); + } + + public static void main(String[] args) throws Throwable { + MethodHandle handle = downcallHandle("test_return", FunctionDescriptor.ofVoid(ADDRESS)); + MemorySegment upcallStub = upcallStub(Runner.class, "target", FunctionDescriptor.of(ADDRESS)); + handle.invoke(upcallStub); + } + + public static MemorySegment target() { + return MemorySegment.ofArray(new byte[]{ 0, 1, 2 }); // should throw + } + } + + @DataProvider + public static Object[][] specs() { + return new Object[][]{ + { true }, + { false } + }; + } +} + diff --git a/test/jdk/java/foreign/passheapsegment/libPassHeapSegment.c b/test/jdk/java/foreign/passheapsegment/libPassHeapSegment.c new file mode 100644 index 0000000000000..fdb1981ac6539 --- /dev/null +++ b/test/jdk/java/foreign/passheapsegment/libPassHeapSegment.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifdef _WIN64 +#define EXPORT __declspec(dllexport) +#else +#define EXPORT +#endif + +EXPORT void test_args(void* ptr) {} + +EXPORT void test_return(void* (*cb)(void)) { + cb(); +} diff --git a/test/jdk/java/foreign/stackwalk/TestAsyncStackWalk.java b/test/jdk/java/foreign/stackwalk/TestAsyncStackWalk.java index 643bf3d931729..c9b3582d4d083 100644 --- a/test/jdk/java/foreign/stackwalk/TestAsyncStackWalk.java +++ b/test/jdk/java/foreign/stackwalk/TestAsyncStackWalk.java @@ -78,15 +78,14 @@ * TestAsyncStackWalk */ -import java.lang.foreign.Addressable; +import java.lang.foreign.Arena; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryAddress; +import java.lang.foreign.MemorySegment; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; -import java.lang.foreign.MemorySession; import jdk.test.whitebox.WhiteBox; import static java.lang.invoke.MethodHandles.lookup; @@ -116,17 +115,16 @@ public class TestAsyncStackWalk extends NativeTestHelper { static boolean didStackWalk; public static void main(String[] args) throws Throwable { - try (MemorySession session = MemorySession.openConfined()) { - Addressable stub = linker.upcallStub(MH_m, FunctionDescriptor.ofVoid(), session); - MemoryAddress stubAddress = stub.address(); + try (Arena arena = Arena.openConfined()) { + MemorySegment stub = linker.upcallStub(MH_m, FunctionDescriptor.ofVoid(), arena.scope()); invocations = 0; didStackWalk = false; - payload(stubAddress); + payload(stub); assertTrue(didStackWalk); } } - static void payload(MemoryAddress cb) throws Throwable { + static void payload(MemorySegment cb) throws Throwable { MH_asyncStackWalk.invoke(cb); } diff --git a/test/jdk/java/foreign/stackwalk/TestStackWalk.java b/test/jdk/java/foreign/stackwalk/TestStackWalk.java index 23b56c5e219a4..9b3679c9f35a4 100644 --- a/test/jdk/java/foreign/stackwalk/TestStackWalk.java +++ b/test/jdk/java/foreign/stackwalk/TestStackWalk.java @@ -78,11 +78,10 @@ * TestStackWalk */ -import java.lang.foreign.Addressable; +import java.lang.foreign.Arena; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryAddress; -import java.lang.foreign.MemorySession; +import java.lang.foreign.MemorySegment; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; @@ -115,20 +114,19 @@ public class TestStackWalk extends NativeTestHelper { static boolean armed; public static void main(String[] args) throws Throwable { - try (MemorySession session = MemorySession.openConfined()) { - Addressable stub = linker.upcallStub(MH_m, FunctionDescriptor.ofVoid(), session); - MemoryAddress stubAddress = stub.address(); + try (Arena arena = Arena.openConfined()) { + MemorySegment stub = linker.upcallStub(MH_m, FunctionDescriptor.ofVoid(), arena.scope()); armed = false; for (int i = 0; i < 20_000; i++) { - payload(stubAddress); // warmup + payload(stub); // warmup } armed = true; - payload(stubAddress); // test + payload(stub); // test } } - static void payload(MemoryAddress cb) throws Throwable { + static void payload(MemorySegment cb) throws Throwable { MH_foo.invoke(cb); Reference.reachabilityFence(cb); // keep oop alive across call } diff --git a/test/jdk/java/foreign/upcalldeopt/TestUpcallDeopt.java b/test/jdk/java/foreign/upcalldeopt/TestUpcallDeopt.java index 4d82eb677fe69..a0f36c479fc5f 100644 --- a/test/jdk/java/foreign/upcalldeopt/TestUpcallDeopt.java +++ b/test/jdk/java/foreign/upcalldeopt/TestUpcallDeopt.java @@ -40,10 +40,10 @@ * TestUpcallDeopt */ -import java.lang.foreign.Addressable; +import java.lang.foreign.Arena; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemorySession; +import java.lang.foreign.MemorySegment; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; @@ -79,8 +79,8 @@ public class TestUpcallDeopt extends NativeTestHelper { // we need to deoptimize through an uncommon trap in the callee of the optimized upcall stub // that is created when calling upcallStub below public static void main(String[] args) throws Throwable { - try (MemorySession session = MemorySession.openConfined()) { - Addressable stub = linker.upcallStub(MH_m, FunctionDescriptor.ofVoid(C_INT, C_INT, C_INT, C_INT), session); + try (Arena arena = Arena.openConfined()) { + MemorySegment stub = linker.upcallStub(MH_m, FunctionDescriptor.ofVoid(C_INT, C_INT, C_INT, C_INT), arena.scope()); armed = false; for (int i = 0; i < 20_000; i++) { payload(stub); // warmup @@ -91,8 +91,8 @@ public static void main(String[] args) throws Throwable { } } - static void payload(Addressable cb) throws Throwable { - MH_foo.invokeExact((Addressable) cb, 0, 1, 2, 3); + static void payload(MemorySegment cb) throws Throwable { + MH_foo.invokeExact(cb, 0, 1, 2, 3); 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 e8846490e251b..9c8ce23b5ee87 100644 --- a/test/jdk/java/foreign/valist/VaListTest.java +++ b/test/jdk/java/foreign/valist/VaListTest.java @@ -40,6 +40,7 @@ */ import java.lang.foreign.*; +import java.lang.foreign.SegmentScope; import java.lang.foreign.VaList; import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker; import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker; @@ -63,13 +64,9 @@ import static java.lang.foreign.MemoryLayout.PathElement.groupElement; import static java.lang.foreign.ValueLayout.ADDRESS; -import static java.lang.foreign.ValueLayout.JAVA_BYTE; -import static java.lang.foreign.ValueLayout.JAVA_CHAR; import static java.lang.foreign.ValueLayout.JAVA_DOUBLE; -import static java.lang.foreign.ValueLayout.JAVA_FLOAT; import static java.lang.foreign.ValueLayout.JAVA_INT; import static java.lang.foreign.ValueLayout.JAVA_LONG; -import static java.lang.foreign.ValueLayout.JAVA_SHORT; import static jdk.internal.foreign.PlatformLayouts.*; import static org.testng.Assert.*; @@ -80,11 +77,13 @@ public class VaListTest extends NativeTestHelper { System.loadLibrary("VaList"); } - private static final MethodHandle ADDRESS_TO_VALIST; + private static final MethodHandle VALIST_TO_ADDRESS; + private static final MethodHandle SEGMENT_TO_VALIST; static { try { - ADDRESS_TO_VALIST = MethodHandles.lookup().findStatic(VaList.class, "ofAddress", MethodType.methodType(VaList.class, MemoryAddress.class, MemorySession.class)); + VALIST_TO_ADDRESS = MethodHandles.lookup().findVirtual(VaList.class, "segment", MethodType.methodType(MemorySegment.class)); + SEGMENT_TO_VALIST = MethodHandles.lookup().findStatic(VaListTest.class, "segmentToValist", MethodType.methodType(VaList.class, MemorySegment.class)); } catch (Throwable ex) { throw new ExceptionInInitializerError(ex); } @@ -113,7 +112,7 @@ private static MethodHandle link(String symbol, FunctionDescriptor fd) { } private static MethodHandle linkVaList(String symbol, FunctionDescriptor fd) { - return linkInternal(symbol, fd); + return MethodHandles.filterArguments(linkInternal(symbol, fd), fd.argumentLayouts().size() - 1, VALIST_TO_ADDRESS); } @@ -128,25 +127,25 @@ private static MethodHandle linkVaListCB(String symbol) { } private static final Function, VaList> winVaListFactory - = actions -> Windowsx64Linker.newVaList(actions, MemorySession.openImplicit()); + = actions -> Windowsx64Linker.newVaList(actions, SegmentScope.auto()); private static final Function, VaList> sysvVaListFactory - = actions -> SysVx64Linker.newVaList(actions, MemorySession.openImplicit()); + = actions -> SysVx64Linker.newVaList(actions, SegmentScope.auto()); private static final Function, VaList> linuxAArch64VaListFactory - = actions -> LinuxAArch64Linker.newVaList(actions, MemorySession.openImplicit()); + = actions -> LinuxAArch64Linker.newVaList(actions, SegmentScope.auto()); private static final Function, VaList> macAArch64VaListFactory - = actions -> MacOsAArch64Linker.newVaList(actions, MemorySession.openImplicit()); + = actions -> MacOsAArch64Linker.newVaList(actions, SegmentScope.auto()); private static final Function, VaList> platformVaListFactory - = (builder) -> VaList.make(builder, MemorySession.openConfined()); + = (builder) -> VaList.make(builder, SegmentScope.auto()); - private static final BiFunction, MemorySession, VaList> winVaListScopedFactory + private static final BiFunction, SegmentScope, VaList> winVaListScopedFactory = Windowsx64Linker::newVaList; - private static final BiFunction, MemorySession, VaList> sysvVaListScopedFactory + private static final BiFunction, SegmentScope, VaList> sysvVaListScopedFactory = SysVx64Linker::newVaList; - private static final BiFunction, MemorySession, VaList> linuxAArch64VaListScopedFactory + private static final BiFunction, SegmentScope, VaList> linuxAArch64VaListScopedFactory = LinuxAArch64Linker::newVaList; - private static final BiFunction, MemorySession, VaList> macAArch64VaListScopedFactory + private static final BiFunction, SegmentScope, VaList> macAArch64VaListScopedFactory = MacOsAArch64Linker::newVaList; - private static final BiFunction, MemorySession, VaList> platformVaListScopedFactory + private static final BiFunction, SegmentScope, VaList> platformVaListScopedFactory = VaList::make; @DataProvider @@ -170,9 +169,9 @@ public void testIntSum(Function, VaList> vaListFactory, BiFunction sumInts, ValueLayout.OfInt intLayout) { VaList vaList = vaListFactory.apply(b -> - b.addVarg(intLayout, 10) - .addVarg(intLayout, 15) - .addVarg(intLayout, 20)); + b.addVarg(intLayout, 10) + .addVarg(intLayout, 15) + .addVarg(intLayout, 20)); int x = sumInts.apply(3, vaList); assertEquals(x, 45); } @@ -198,9 +197,9 @@ public void testDoubleSum(Function, VaList> vaListFacto BiFunction sumDoubles, ValueLayout.OfDouble doubleLayout) { VaList vaList = vaListFactory.apply(b -> - b.addVarg(doubleLayout, 3.0D) - .addVarg(doubleLayout, 4.0D) - .addVarg(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); } @@ -210,7 +209,7 @@ public void testDoubleSum(Function, VaList> vaListFacto public static Object[][] pointers() { Function> getIntJavaFact = layout -> list -> { - MemoryAddress ma = list.nextVarg(layout); + MemorySegment ma = list.nextVarg(layout); return ma.get(JAVA_INT, 0); }; Function getIntNative = MethodHandleProxies.asInterfaceInstance(Function.class, MH_getInt); @@ -224,13 +223,13 @@ public static Object[][] pointers() { } @Test(dataProvider = "pointers") - public void testVaListMemoryAddress(Function, VaList> vaListFactory, + public void testVaListMemorySegment(Function, VaList> vaListFactory, Function getFromPointer, ValueLayout.OfAddress pointerLayout) { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment msInt = MemorySegment.allocateNative(JAVA_INT, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment msInt = MemorySegment.allocateNative(JAVA_INT, arena.scope());; msInt.set(JAVA_INT, 0, 10); - VaList vaList = vaListFactory.apply(b -> b.addVarg(pointerLayout, msInt.address())); + VaList vaList = vaListFactory.apply(b -> b.addVarg(pointerLayout, msInt)); int x = getFromPointer.apply(vaList); assertEquals(x, 10); } @@ -246,7 +245,7 @@ public static Object[][] structs() { TriFunction> sumStructJavaFact = (pointLayout, VH_Point_x, VH_Point_y) -> list -> { - MemorySegment struct = MemorySegment.allocateNative(pointLayout, MemorySession.openImplicit()); + MemorySegment struct = MemorySegment.allocateNative(pointLayout, SegmentScope.auto()); list.nextVarg(pointLayout, SegmentAllocator.prefixAllocator(struct)); int x = (int) VH_Point_x.get(struct); int y = (int) VH_Point_y.get(struct); @@ -282,8 +281,8 @@ public static Object[][] structs() { public void testStruct(Function, VaList> vaListFactory, Function sumStruct, GroupLayout Point_LAYOUT, VarHandle VH_Point_x, VarHandle VH_Point_y) { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment struct = MemorySegment.allocateNative(Point_LAYOUT, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment struct = MemorySegment.allocateNative(Point_LAYOUT, arena.scope());; VH_Point_x.set(struct, 5); VH_Point_y.set(struct, 10); @@ -299,7 +298,7 @@ public static Object[][] bigStructs() { TriFunction> sumStructJavaFact = (BigPoint_LAYOUT, VH_BigPoint_x, VH_BigPoint_y) -> list -> { - MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, MemorySession.openImplicit()); + MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, SegmentScope.auto()); list.nextVarg(BigPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); long x = (long) VH_BigPoint_x.get(struct); long y = (long) VH_BigPoint_y.get(struct); @@ -335,8 +334,8 @@ public static Object[][] bigStructs() { public void testBigStruct(Function, VaList> vaListFactory, Function sumBigStruct, GroupLayout BigPoint_LAYOUT, VarHandle VH_BigPoint_x, VarHandle VH_BigPoint_y) { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, arena.scope());; VH_BigPoint_x.set(struct, 5); VH_BigPoint_y.set(struct, 10); @@ -352,7 +351,7 @@ public static Object[][] floatStructs() { TriFunction> sumStructJavaFact = (FloatPoint_LAYOUT, VH_FloatPoint_x, VH_FloatPoint_y) -> list -> { - MemorySegment struct = MemorySegment.allocateNative(FloatPoint_LAYOUT, MemorySession.openImplicit()); + MemorySegment struct = MemorySegment.allocateNative(FloatPoint_LAYOUT, SegmentScope.auto()); list.nextVarg(FloatPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); float x = (float) VH_FloatPoint_x.get(struct); float y = (float) VH_FloatPoint_y.get(struct); @@ -389,8 +388,8 @@ public void testFloatStruct(Function, VaList> vaListFac Function sumFloatStruct, GroupLayout FloatPoint_LAYOUT, VarHandle VH_FloatPoint_x, VarHandle VH_FloatPoint_y) { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment struct = MemorySegment.allocateNative(FloatPoint_LAYOUT, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment struct = MemorySegment.allocateNative(FloatPoint_LAYOUT, arena.scope());; VH_FloatPoint_x.set(struct, 1.234f); VH_FloatPoint_y.set(struct, 3.142f); @@ -410,7 +409,7 @@ public static Object[][] hugeStructs() { QuadFunc> sumStructJavaFact = (HugePoint_LAYOUT, VH_HugePoint_x, VH_HugePoint_y, VH_HugePoint_z) -> list -> { - MemorySegment struct = MemorySegment.allocateNative(HugePoint_LAYOUT, MemorySession.openImplicit()); + MemorySegment struct = MemorySegment.allocateNative(HugePoint_LAYOUT, SegmentScope.auto()); list.nextVarg(HugePoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); long x = (long) VH_HugePoint_x.get(struct); long y = (long) VH_HugePoint_y.get(struct); @@ -453,8 +452,8 @@ public void testHugeStruct(Function, VaList> vaListFact VarHandle VH_HugePoint_x, VarHandle VH_HugePoint_y, VarHandle VH_HugePoint_z) { // On AArch64 a struct needs to be larger than 16 bytes to be // passed by reference. - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment struct = MemorySegment.allocateNative(HugePoint_LAYOUT, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment struct = MemorySegment.allocateNative(HugePoint_LAYOUT, arena.scope());; VH_HugePoint_x.set(struct, 1); VH_HugePoint_y.set(struct, 2); VH_HugePoint_z.set(struct, 3); @@ -486,7 +485,7 @@ public static Object[][] sumStack() { }; SumStackFunc sumStackNative = (longSum, doubleSum, list) -> { try { - MH_sumStack.invoke(longSum, doubleSum, list); + MH_sumStack.invokeExact(longSum, doubleSum, list); } catch (Throwable ex) { throw new AssertionError(ex); } @@ -505,9 +504,9 @@ public void testStack(Function, VaList> vaListFactory, SumStackFunc sumStack, ValueLayout.OfLong longLayout, ValueLayout.OfDouble doubleLayout) { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment longSum = MemorySegment.allocateNative(longLayout, session); - MemorySegment doubleSum = MemorySegment.allocateNative(doubleLayout, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment longSum = MemorySegment.allocateNative(longLayout, arena.scope());; + MemorySegment doubleSum = MemorySegment.allocateNative(doubleLayout, arena.scope());; longSum.set(JAVA_LONG, 0, 0L); doubleSum.set(JAVA_DOUBLE, 0, 0D); @@ -533,9 +532,9 @@ public void testStack(Function, VaList> vaListFactory, @Test(dataProvider = "upcalls") public void testUpcall(MethodHandle target, MethodHandle callback) throws Throwable { FunctionDescriptor desc = FunctionDescriptor.ofVoid(C_POINTER); - try (MemorySession session = MemorySession.openConfined()) { - Addressable stub = abi.upcallStub(callback, desc, session); - target.invoke(stub); + try (Arena arena = Arena.openConfined()) { + MemorySegment stub = abi.upcallStub(callback, desc, arena.scope()); + target.invokeExact(stub); } } @@ -570,18 +569,18 @@ public static Object[][] sumIntsScoped() { } @Test(dataProvider = "sumIntsScoped") - public void testScopedVaList(BiFunction, MemorySession, VaList> vaListFactory, + public void testScopedVaList(BiFunction, SegmentScope, VaList> vaListFactory, BiFunction sumInts, ValueLayout.OfInt intLayout) { VaList listLeaked; - try (MemorySession session = MemorySession.openConfined()) { + try (Arena arena = Arena.openConfined()) { VaList list = vaListFactory.apply(b -> b.addVarg(intLayout, 4) - .addVarg(intLayout, 8), session); + .addVarg(intLayout, 8), arena.scope()); int x = sumInts.apply(2, list); assertEquals(x, 12); listLeaked = list; } - assertFalse(listLeaked.session().isAlive()); + assertFalse(listLeaked.segment().scope().isAlive()); } @Test(dataProvider = "structs") @@ -589,21 +588,21 @@ public void testScopeMSRead(Function, VaList> vaListFac Function sumStruct, // ignored GroupLayout Point_LAYOUT, VarHandle VH_Point_x, VarHandle VH_Point_y) { MemorySegment pointOut; - try (MemorySession session = MemorySession.openConfined()) { - try (MemorySession innerSession = MemorySession.openConfined()) { - MemorySegment pointIn = MemorySegment.allocateNative(Point_LAYOUT, innerSession); + try (Arena arena = Arena.openConfined()) { + try (Arena innerArena = Arena.openConfined()) { + MemorySegment pointIn = MemorySegment.allocateNative(Point_LAYOUT, innerArena.scope());; VH_Point_x.set(pointIn, 3); VH_Point_y.set(pointIn, 6); VaList list = vaListFactory.apply(b -> b.addVarg(Point_LAYOUT, pointIn)); - pointOut = MemorySegment.allocateNative(Point_LAYOUT, session); + pointOut = MemorySegment.allocateNative(Point_LAYOUT, arena.scope());; list.nextVarg(Point_LAYOUT, SegmentAllocator.prefixAllocator(pointOut)); assertEquals((int) VH_Point_x.get(pointOut), 3); assertEquals((int) VH_Point_y.get(pointOut), 6); - assertTrue(pointOut.session().isAlive()); // after VaList freed + assertTrue(pointOut.scope().isAlive()); // after VaList freed } - assertTrue(pointOut.session().isAlive()); // after inner session freed + assertTrue(pointOut.scope().isAlive()); // after inner session freed } - assertFalse(pointOut.session().isAlive()); // after outer session freed + assertFalse(pointOut.scope().isAlive()); // after outer session freed } @DataProvider @@ -617,10 +616,10 @@ public Object[][] copy() { } @Test(dataProvider = "copy") - public void testCopy(BiFunction, MemorySession, VaList> vaListFactory, ValueLayout.OfInt intLayout) { - try (var session = MemorySession.openConfined()) { + public void testCopy(BiFunction, SegmentScope, VaList> vaListFactory, ValueLayout.OfInt intLayout) { + try (var arena = Arena.openConfined()) { VaList list = vaListFactory.apply(b -> b.addVarg(intLayout, 4) - .addVarg(intLayout, 8), session); + .addVarg(intLayout, 8), arena.scope()); VaList copy = list.copy(); assertEquals(copy.nextVarg(intLayout), 4); assertEquals(copy.nextVarg(intLayout), 8); @@ -639,12 +638,12 @@ public void testCopy(BiFunction, MemorySession, VaList> @Test(dataProvider = "copy", expectedExceptions = IllegalStateException.class) - public void testCopyUnusableAfterOriginalClosed(BiFunction, MemorySession, VaList> vaListFactory, + public void testCopyUnusableAfterOriginalClosed(BiFunction, SegmentScope, VaList> vaListFactory, ValueLayout.OfInt intLayout) { VaList copy; - try (var session = MemorySession.openConfined()) { + try (var arena = Arena.openConfined()) { VaList list = vaListFactory.apply(b -> b.addVarg(intLayout, 4) - .addVarg(intLayout, 8), session); + .addVarg(intLayout, 8), arena.scope()); copy = list.copy(); } @@ -682,14 +681,14 @@ public static Object[][] upcalls() { return new Object[][]{ { linkVaListCB("upcallBigStruct"), VaListConsumer.mh(vaList -> { - MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, MemorySession.openImplicit()); + MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, SegmentScope.auto()); 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 = MemorySegment.allocateNative(BigPoint_LAYOUT, MemorySession.openImplicit()); + MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, SegmentScope.auto()); vaList.nextVarg(BigPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); assertEquals((long) VH_BigPoint_x.get(struct), 8); assertEquals((long) VH_BigPoint_y.get(struct), 16); @@ -703,7 +702,7 @@ public static Object[][] upcalls() { assertEquals((long) VH_BigPoint_y.get(struct), 16); })}, { linkVaListCB("upcallBigStructPlusScalar"), VaListConsumer.mh(vaList -> { - MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, MemorySession.openImplicit()); + MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, SegmentScope.auto()); vaList.nextVarg(BigPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct)); assertEquals((long) VH_BigPoint_x.get(struct), 8); assertEquals((long) VH_BigPoint_y.get(struct), 16); @@ -715,28 +714,27 @@ public static Object[][] upcalls() { assertEquals(vaList.nextVarg(C_LONG_LONG), 42); })}, { linkVaListCB("upcallStruct"), VaListConsumer.mh(vaList -> { - MemorySegment struct = MemorySegment.allocateNative(Point_LAYOUT, MemorySession.openImplicit()); + MemorySegment struct = MemorySegment.allocateNative(Point_LAYOUT, SegmentScope.auto()); 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 = MemorySegment.allocateNative(HugePoint_LAYOUT, MemorySession.openImplicit()); + MemorySegment struct = MemorySegment.allocateNative(HugePoint_LAYOUT, SegmentScope.auto()); 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 = MemorySegment.allocateNative(FloatPoint_LAYOUT, MemorySession.openImplicit()); + MemorySegment struct = MemorySegment.allocateNative(FloatPoint_LAYOUT, SegmentScope.auto()); 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.nextVarg(C_POINTER); - MemorySegment ms = MemorySegment.ofAddress(intPtr, C_INT.byteSize(), MemorySession.global()); - int x = ms.get(JAVA_INT, 0); + MemorySegment intPtr = vaList.nextVarg(C_POINTER); + int x = intPtr.get(JAVA_INT, 0); assertEquals(x, 10); })}, { linkVaListCB("upcallDoubles"), VaListConsumer.mh(vaList -> { @@ -774,7 +772,7 @@ public static Object[][] upcalls() { assertEquals((float) vaList.nextVarg(C_DOUBLE), 13.0F); assertEquals(vaList.nextVarg(C_DOUBLE), 14.0D); - MemorySegment buffer = MemorySegment.allocateNative(BigPoint_LAYOUT, MemorySession.openImplicit()); + MemorySegment buffer = MemorySegment.allocateNative(BigPoint_LAYOUT, SegmentScope.auto()); SegmentAllocator bufferAllocator = SegmentAllocator.prefixAllocator(buffer); MemorySegment point = vaList.nextVarg(Point_LAYOUT, bufferAllocator); @@ -816,13 +814,17 @@ static MethodHandle mh(VaListConsumer instance) { 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, MemorySession.openConfined())); + SEGMENT_TO_VALIST); } catch (ReflectiveOperationException e) { throw new InternalError(e); } } } + static VaList segmentToValist(MemorySegment segment) { + return VaList.ofAddress(segment.address(), SegmentScope.auto()); + } + @DataProvider public static Object[][] overflow() { List, VaList>> factories = List.of( @@ -868,7 +870,7 @@ private static void buildVaList(VaList.Builder builder, List conte } else if (layout instanceof ValueLayout.OfDouble ofDouble) { builder.addVarg(ofDouble, 1D); } else if (layout instanceof ValueLayout.OfAddress ofAddress) { - builder.addVarg(ofAddress, MemoryAddress.ofLong(1)); + builder.addVarg(ofAddress, MemorySegment.ofAddress(1)); } } } @@ -890,7 +892,7 @@ private static void nextVarg(VaList vaList, MemoryLayout layout) { } else if (layout instanceof ValueLayout.OfDouble ofDouble) { assertEquals(vaList.nextVarg(ofDouble), 1D); } else if (layout instanceof ValueLayout.OfAddress ofAddress) { - assertEquals(vaList.nextVarg(ofAddress), MemoryAddress.ofLong(1)); + assertEquals(vaList.nextVarg(ofAddress), MemorySegment.ofAddress(1)); } } diff --git a/test/jdk/java/foreign/virtual/TestVirtualCalls.java b/test/jdk/java/foreign/virtual/TestVirtualCalls.java index 7f0e7f1f1ff50..eacccd4d893d2 100644 --- a/test/jdk/java/foreign/virtual/TestVirtualCalls.java +++ b/test/jdk/java/foreign/virtual/TestVirtualCalls.java @@ -31,10 +31,10 @@ * TestVirtualCalls */ -import java.lang.foreign.Addressable; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.MemorySegment; import java.lang.invoke.MethodHandle; import org.testng.annotations.*; @@ -46,9 +46,9 @@ public class TestVirtualCalls extends NativeTestHelper { static final Linker abi = Linker.nativeLinker(); static final MethodHandle func; - static final Addressable funcA; - static final Addressable funcB; - static final Addressable funcC; + static final MemorySegment funcA; + static final MemorySegment funcB; + static final MemorySegment funcC; static { func = abi.downcallHandle( @@ -69,7 +69,7 @@ public void testVirtualCalls() throws Throwable { @Test(expectedExceptions = NullPointerException.class) public void testNullTarget() throws Throwable { - int x = (int) func.invokeExact((Addressable) null); + int x = (int) func.invokeExact((MemorySegment)null); } } diff --git a/test/jdk/java/lang/Thread/jni/AttachCurrentThread/ImplicitAttach.java b/test/jdk/java/lang/Thread/jni/AttachCurrentThread/ImplicitAttach.java index ae5d5a6f2bfd0..5e676da446bbd 100644 --- a/test/jdk/java/lang/Thread/jni/AttachCurrentThread/ImplicitAttach.java +++ b/test/jdk/java/lang/Thread/jni/AttachCurrentThread/ImplicitAttach.java @@ -58,11 +58,11 @@ public static void main(String[] args) throws Throwable { .findStatic(ImplicitAttach.class, "callback", MethodType.methodType(void.class)); MemorySegment upcallStub = abi.upcallStub(callback, FunctionDescriptor.ofVoid(), - MemorySession.global()); + SegmentScope.global()); // void start_threads(int count, void *(*f)(void *)) SymbolLookup symbolLookup = SymbolLookup.loaderLookup(); - MemorySegment symbol = symbolLookup.lookup("start_threads").orElseThrow(); + MemorySegment symbol = symbolLookup.find("start_threads").orElseThrow(); FunctionDescriptor desc = FunctionDescriptor.ofVoid(C_INT, C_POINTER); MethodHandle start_threads = abi.downcallHandle(symbol, desc); diff --git a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestExact.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestExact.java index fa29d2a85e2f1..261483c0ac2b8 100644 --- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestExact.java +++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestExact.java @@ -46,9 +46,9 @@ * VarHandleTestExact */ +import java.lang.foreign.Arena; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import org.testng.SkipException; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -170,8 +170,8 @@ public void testExactBufferSet(Class arrayClass, Object testValue, SetBufferX @Test(dataProvider = "dataSetMemorySegment") public void testExactSegmentSet(Class carrier, Object testValue, SetSegmentX setter) { VarHandle vh = MethodHandles.memorySegmentViewVarHandle(MemoryLayout.valueLayout(carrier, ByteOrder.nativeOrder())); - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment seg = MemorySegment.allocateNative(8, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment seg = arena.allocate(8); doTest(vh, tvh -> tvh.set(seg, 0L, testValue), tvh -> setter.set(tvh, seg, 0L, testValue), diff --git a/test/jdk/java/nio/channels/FileChannel/LargeMapTest.java b/test/jdk/java/nio/channels/FileChannel/LargeMapTest.java index 62a48b26b25f5..afd067605aed3 100644 --- a/test/jdk/java/nio/channels/FileChannel/LargeMapTest.java +++ b/test/jdk/java/nio/channels/FileChannel/LargeMapTest.java @@ -24,7 +24,7 @@ import jdk.test.lib.RandomFactory; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.io.IOException; import java.nio.ByteBuffer; @@ -78,7 +78,7 @@ public static void main(String[] args) throws IOException { try (FileChannel fc = FileChannel.open(p, READ, WRITE)) { MemorySegment mappedMemorySegment = fc.map(FileChannel.MapMode.READ_WRITE, 0, p.toFile().length(), - MemorySession.openImplicit()); + SegmentScope.auto()); MemorySegment target = mappedMemorySegment.asSlice(BASE, EXTRA); if (!target.asByteBuffer().equals(bb)) { throw new RuntimeException("Expected buffers to be equal"); diff --git a/test/jdk/java/nio/channels/FileChannel/MapToMemorySegmentTest.java b/test/jdk/java/nio/channels/FileChannel/MapToMemorySegmentTest.java index 3a817cd9e28f8..b06af75c48da8 100644 --- a/test/jdk/java/nio/channels/FileChannel/MapToMemorySegmentTest.java +++ b/test/jdk/java/nio/channels/FileChannel/MapToMemorySegmentTest.java @@ -30,8 +30,9 @@ import java.io.File; import java.io.IOException; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; @@ -61,21 +62,21 @@ public class MapToMemorySegmentTest { @Test(expectedExceptions = UnsupportedOperationException.class) public void testCustomFileChannel() throws IOException { - var session = MemorySession.openConfined(); + var arena = Arena.openConfined(); var fc = FileChannel.open(tempPath, StandardOpenOption.WRITE, StandardOpenOption.READ); var fileChannel = new CustomFileChannel(fc); - try (session; fileChannel){ - fileChannel.map(FileChannel.MapMode.READ_WRITE, 1L, 10L, session); + try (arena; fileChannel){ + fileChannel.map(FileChannel.MapMode.READ_WRITE, 1L, 10L, arena.scope()); } } @Test public void testCustomFileChannelOverride() throws IOException { - var session = MemorySession.openConfined(); + var arena = Arena.openConfined(); var fc = FileChannel.open(tempPath, StandardOpenOption.WRITE, StandardOpenOption.READ); var fileChannel = new CustomFileChannelOverride(fc); - try (session; fileChannel){ - fileChannel.map(FileChannel.MapMode.READ_WRITE, 1L, 10L, session); + try (arena; fileChannel){ + fileChannel.map(FileChannel.MapMode.READ_WRITE, 1L, 10L, arena.scope()); } } @@ -160,10 +161,10 @@ static class CustomFileChannelOverride extends CustomFileChannel { public CustomFileChannelOverride(FileChannel fc) { super(fc); } @Override - public MemorySegment map(MapMode mode, long offset, long size, MemorySession session) + public MemorySegment map(MapMode mode, long offset, long size, SegmentScope scope) throws IOException, UnsupportedOperationException { - return fc.map(mode, offset, size, session); + return fc.map(mode, offset, size, scope); } } } diff --git a/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/SpliteratorTest.java b/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/SpliteratorTest.java index 1e8ddae42bbd8..efe69ef3c3662 100644 --- a/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/SpliteratorTest.java +++ b/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/SpliteratorTest.java @@ -22,8 +22,8 @@ */ package org.openjdk.tests.java.util.stream; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.lang.foreign.SequenceLayout; import org.testng.annotations.Test; @@ -65,8 +65,8 @@ public void testDoubleSpliterator(String name, Supplier su @Test(dataProvider = "SegmentSpliterator", dataProviderClass = SegmentTestDataProvider.class ) public void testSegmentSpliterator(String name, SequenceLayout layout, SpliteratorTestHelper.ContentAsserter contentAsserter) { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(layout, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = arena.allocate(layout); SegmentTestDataProvider.initSegment(segment); SpliteratorTestHelper.testSpliterator(() -> segment.spliterator(layout), SegmentTestDataProvider::segmentCopier, contentAsserter); diff --git a/test/jdk/jdk/incubator/vector/AbstractVectorLoadStoreTest.java b/test/jdk/jdk/incubator/vector/AbstractVectorLoadStoreTest.java index a664f65092b3e..8139931f41dac 100644 --- a/test/jdk/jdk/incubator/vector/AbstractVectorLoadStoreTest.java +++ b/test/jdk/jdk/incubator/vector/AbstractVectorLoadStoreTest.java @@ -22,7 +22,7 @@ */ import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Collection; @@ -36,25 +36,23 @@ public class AbstractVectorLoadStoreTest extends AbstractVectorTest { ByteOrder.BIG_ENDIAN, ByteOrder.LITTLE_ENDIAN); static final List> BYTE_BUFFER_GENERATORS = List.of( - withToString("HB:RW:NE", (int s) -> { - return ByteBuffer.allocate(s) - .order(ByteOrder.nativeOrder()); - }), - withToString("DB:RW:NE", (int s) -> { - return ByteBuffer.allocateDirect(s) - .order(ByteOrder.nativeOrder()); - }), - withToString("MS:RW:NE", (int s) -> { - return MemorySegment.allocateNative(s, MemorySession.openImplicit()) + withToString("HB:RW:NE", (int s) -> + ByteBuffer.allocate(s) + .order(ByteOrder.nativeOrder())), + withToString("DB:RW:NE", (int s) -> + ByteBuffer.allocateDirect(s) + .order(ByteOrder.nativeOrder())), + withToString("MS:RW:NE", (int s) -> + MemorySegment.allocateNative(s, SegmentScope.auto()) .asByteBuffer() - .order(ByteOrder.nativeOrder()); - }) + .order(ByteOrder.nativeOrder()) + ) ); static final List> MEMORY_SEGMENT_GENERATORS = List.of( - withToString("HMS", (int s) -> { - return MemorySegment.allocateNative(s, MemorySession.openImplicit()); - }), + withToString("HMS", (int s) -> + MemorySegment.allocateNative(s, SegmentScope.auto()) + ), withToString("DMS", (int s) -> { byte[] b = new byte[s]; return MemorySegment.ofArray(b); @@ -62,4 +60,3 @@ public class AbstractVectorLoadStoreTest extends AbstractVectorTest { ); } - diff --git a/test/jdk/jdk/incubator/vector/Byte128VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Byte128VectorLoadStoreTests.java index 930c8ce791e09..c588b03fb6829 100644 --- a/test/jdk/jdk/incubator/vector/Byte128VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Byte128VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "byteByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction @Test(dataProvider = "byteByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "byteByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Byte256VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Byte256VectorLoadStoreTests.java index 0c52ddde4f3a9..0a60dc2742cdf 100644 --- a/test/jdk/jdk/incubator/vector/Byte256VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Byte256VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "byteByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction @Test(dataProvider = "byteByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "byteByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Byte512VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Byte512VectorLoadStoreTests.java index c9990cdf2ce77..807a1647532de 100644 --- a/test/jdk/jdk/incubator/vector/Byte512VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Byte512VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "byteByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction @Test(dataProvider = "byteByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "byteByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Byte64VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Byte64VectorLoadStoreTests.java index 10312b7cb5f46..1274106caffb2 100644 --- a/test/jdk/jdk/incubator/vector/Byte64VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Byte64VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "byteByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction @Test(dataProvider = "byteByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "byteByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/ByteMaxVectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/ByteMaxVectorLoadStoreTests.java index a9042cb65f9fe..1d956bc339c03 100644 --- a/test/jdk/jdk/incubator/vector/ByteMaxVectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/ByteMaxVectorLoadStoreTests.java @@ -33,7 +33,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorMask; @@ -486,8 +486,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "byteByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -515,8 +515,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction @Test(dataProvider = "byteByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -579,8 +579,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "byteByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -610,8 +610,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Byte.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Byte.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Double128VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Double128VectorLoadStoreTests.java index 68dd22c966ed7..4ff63cf1fdaf6 100644 --- a/test/jdk/jdk/incubator/vector/Double128VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Double128VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.DoubleVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "doubleByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "doubleByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Double256VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Double256VectorLoadStoreTests.java index b6a3fe5fe6104..ef3ab3d5b265d 100644 --- a/test/jdk/jdk/incubator/vector/Double256VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Double256VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.DoubleVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "doubleByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "doubleByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Double512VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Double512VectorLoadStoreTests.java index 99b44ada0aa22..4a3a0bc4f8aa7 100644 --- a/test/jdk/jdk/incubator/vector/Double512VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Double512VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.DoubleVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "doubleByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "doubleByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Double64VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Double64VectorLoadStoreTests.java index b69de8938a523..7d242995c1fd0 100644 --- a/test/jdk/jdk/incubator/vector/Double64VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Double64VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.DoubleVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "doubleByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "doubleByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/DoubleMaxVectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/DoubleMaxVectorLoadStoreTests.java index eb8d201bee562..137f7ba579195 100644 --- a/test/jdk/jdk/incubator/vector/DoubleMaxVectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/DoubleMaxVectorLoadStoreTests.java @@ -33,7 +33,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.DoubleVector; import jdk.incubator.vector.VectorMask; @@ -486,8 +486,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "doubleByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -515,8 +515,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -579,8 +579,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "doubleByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -610,8 +610,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Double.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Double.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Float128VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Float128VectorLoadStoreTests.java index 2efaf867c6673..719b803b69b99 100644 --- a/test/jdk/jdk/incubator/vector/Float128VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Float128VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.FloatVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "floatByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction @Test(dataProvider = "floatByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "floatByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Float256VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Float256VectorLoadStoreTests.java index 18baffa0e90f4..26f2f02883fd9 100644 --- a/test/jdk/jdk/incubator/vector/Float256VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Float256VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.FloatVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "floatByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction @Test(dataProvider = "floatByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "floatByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Float512VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Float512VectorLoadStoreTests.java index d1ba7a644dec4..53e75fa62c78f 100644 --- a/test/jdk/jdk/incubator/vector/Float512VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Float512VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.FloatVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "floatByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction @Test(dataProvider = "floatByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "floatByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Float64VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Float64VectorLoadStoreTests.java index 83ecf751cb597..6ea43adfec478 100644 --- a/test/jdk/jdk/incubator/vector/Float64VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Float64VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.FloatVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "floatByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction @Test(dataProvider = "floatByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "floatByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/FloatMaxVectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/FloatMaxVectorLoadStoreTests.java index 64148eaabdbfe..f67bf1e32678f 100644 --- a/test/jdk/jdk/incubator/vector/FloatMaxVectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/FloatMaxVectorLoadStoreTests.java @@ -33,7 +33,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.FloatVector; import jdk.incubator.vector.VectorMask; @@ -486,8 +486,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "floatByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -515,8 +515,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction @Test(dataProvider = "floatByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -579,8 +579,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "floatByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -610,8 +610,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Float.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Float.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Int128VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Int128VectorLoadStoreTests.java index 7ddc9f6ee553e..d6bf4dedc3e4b 100644 --- a/test/jdk/jdk/incubator/vector/Int128VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Int128VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.IntVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "intByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction f @Test(dataProvider = "intByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "intByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Int256VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Int256VectorLoadStoreTests.java index 97b2e1ecc144a..c2d0077a8cdc4 100644 --- a/test/jdk/jdk/incubator/vector/Int256VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Int256VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.IntVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "intByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction f @Test(dataProvider = "intByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "intByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Int512VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Int512VectorLoadStoreTests.java index d6e3f18a2c58a..e9b70d9c93b5a 100644 --- a/test/jdk/jdk/incubator/vector/Int512VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Int512VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.IntVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "intByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction f @Test(dataProvider = "intByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "intByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Int64VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Int64VectorLoadStoreTests.java index b9157d932957d..28633c1b34535 100644 --- a/test/jdk/jdk/incubator/vector/Int64VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Int64VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.IntVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "intByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction f @Test(dataProvider = "intByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "intByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/IntMaxVectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/IntMaxVectorLoadStoreTests.java index 2891c0c142e3c..f128184026b31 100644 --- a/test/jdk/jdk/incubator/vector/IntMaxVectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/IntMaxVectorLoadStoreTests.java @@ -33,7 +33,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.IntVector; import jdk.incubator.vector.VectorMask; @@ -486,8 +486,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "intByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -515,8 +515,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction f @Test(dataProvider = "intByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -579,8 +579,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "intByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -610,8 +610,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Integer.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Integer.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Long128VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Long128VectorLoadStoreTests.java index cebd1c4cb320c..81b5ebd8fffbc 100644 --- a/test/jdk/jdk/incubator/vector/Long128VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Long128VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.LongVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "longByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction @Test(dataProvider = "longByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "longByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Long256VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Long256VectorLoadStoreTests.java index 2be1356e3bda5..ca6d4d742432a 100644 --- a/test/jdk/jdk/incubator/vector/Long256VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Long256VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.LongVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "longByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction @Test(dataProvider = "longByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "longByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Long512VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Long512VectorLoadStoreTests.java index 43cb189857349..5ad197156229a 100644 --- a/test/jdk/jdk/incubator/vector/Long512VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Long512VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.LongVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "longByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction @Test(dataProvider = "longByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "longByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Long64VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Long64VectorLoadStoreTests.java index 48a3e94bbbbb4..1041d47d96c48 100644 --- a/test/jdk/jdk/incubator/vector/Long64VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Long64VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.LongVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "longByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction @Test(dataProvider = "longByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "longByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/LongMaxVectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/LongMaxVectorLoadStoreTests.java index e21cedfae0926..e20d33d2026ff 100644 --- a/test/jdk/jdk/incubator/vector/LongMaxVectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/LongMaxVectorLoadStoreTests.java @@ -33,7 +33,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.LongVector; import jdk.incubator.vector.VectorMask; @@ -486,8 +486,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "longByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -515,8 +515,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction @Test(dataProvider = "longByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -579,8 +579,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "longByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -610,8 +610,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Long.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Long.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Short128VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Short128VectorLoadStoreTests.java index 13b59c5ffbce5..2a153318cae31 100644 --- a/test/jdk/jdk/incubator/vector/Short128VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Short128VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.ShortVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "shortByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction @Test(dataProvider = "shortByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "shortByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Short256VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Short256VectorLoadStoreTests.java index ad2204cc82242..407f5affdf463 100644 --- a/test/jdk/jdk/incubator/vector/Short256VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Short256VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.ShortVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "shortByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction @Test(dataProvider = "shortByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "shortByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Short512VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Short512VectorLoadStoreTests.java index fb04ca5779d19..d32d6735030e0 100644 --- a/test/jdk/jdk/incubator/vector/Short512VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Short512VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.ShortVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "shortByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction @Test(dataProvider = "shortByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "shortByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/Short64VectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/Short64VectorLoadStoreTests.java index 157a0638bdbef..6ab8817b22dd6 100644 --- a/test/jdk/jdk/incubator/vector/Short64VectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/Short64VectorLoadStoreTests.java @@ -32,7 +32,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.ShortVector; import jdk.incubator.vector.VectorMask; @@ -479,8 +479,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "shortByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -508,8 +508,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction @Test(dataProvider = "shortByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -572,8 +572,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "shortByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -603,8 +603,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/ShortMaxVectorLoadStoreTests.java b/test/jdk/jdk/incubator/vector/ShortMaxVectorLoadStoreTests.java index 1129f5cd83a29..7fb1c43ba3bc3 100644 --- a/test/jdk/jdk/incubator/vector/ShortMaxVectorLoadStoreTests.java +++ b/test/jdk/jdk/incubator/vector/ShortMaxVectorLoadStoreTests.java @@ -33,7 +33,7 @@ // -- This file was mechanically generated: Do not edit! -- // import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.ShortVector; import jdk.incubator.vector.VectorMask; @@ -486,8 +486,8 @@ static void loadStoreMemorySegment(IntFunction fa, @Test(dataProvider = "shortByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -515,8 +515,8 @@ static void loadMemorySegmentIOOBE(IntFunction fa, IntFunction @Test(dataProvider = "shortByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -579,8 +579,8 @@ static void loadStoreMemorySegmentMask(IntFunction fa, @Test(dataProvider = "shortByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); @@ -610,8 +610,8 @@ static void loadMemorySegmentMaskIOOBE(IntFunction fa, IntFunction fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, Short.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), Short.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/jdk/jdk/incubator/vector/gen-template.sh b/test/jdk/jdk/incubator/vector/gen-template.sh index 8c495bc4f9643..8a009bb9fb93a 100644 --- a/test/jdk/jdk/incubator/vector/gen-template.sh +++ b/test/jdk/jdk/incubator/vector/gen-template.sh @@ -223,6 +223,7 @@ function gen_op_tmpl { replace_variables $unit_filename $unit_output "$kernel" "$test" "$op" "$init" "$guard" "$masked" "$op_name" "$kernel_smoke" local gen_perf_tests=$generate_perf_tests + gen_perf_tests=true if [[ $template == *"-Broadcast-"* ]] || [[ $template == "Miscellaneous" ]] || [[ $template == *"Compare-Masked"* ]] || [[ $template == *"Compare-Broadcast"* ]]; then gen_perf_tests=false diff --git a/test/jdk/jdk/incubator/vector/templates/X-LoadStoreTest.java.template b/test/jdk/jdk/incubator/vector/templates/X-LoadStoreTest.java.template index 7e27bfe96bf4e..ada362cee8f9f 100644 --- a/test/jdk/jdk/incubator/vector/templates/X-LoadStoreTest.java.template +++ b/test/jdk/jdk/incubator/vector/templates/X-LoadStoreTest.java.template @@ -37,7 +37,7 @@ #warn This file is preprocessed before being compiled import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.ValueLayout; import jdk.incubator.vector.$Type$Vector; import jdk.incubator.vector.VectorMask; @@ -499,8 +499,8 @@ public class $vectorteststype$ extends AbstractVectorLoadStoreTest { @Test(dataProvider = "$type$ByteProviderForIOOBE") static void loadMemorySegmentIOOBE(IntFunction<$type$[]> fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, $Boxtype$.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), $Boxtype$.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, $Boxtype$.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), $Boxtype$.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -528,8 +528,8 @@ public class $vectorteststype$ extends AbstractVectorLoadStoreTest { @Test(dataProvider = "$type$ByteProviderForIOOBE") static void storeMemorySegmentIOOBE(IntFunction<$type$[]> fa, IntFunction fi) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, $Boxtype$.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), $Boxtype$.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, $Boxtype$.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), $Boxtype$.SIZE, SegmentScope.auto()); int l = (int) a.byteSize(); int s = SPECIES.vectorByteSize(); @@ -592,8 +592,8 @@ public class $vectorteststype$ extends AbstractVectorLoadStoreTest { @Test(dataProvider = "$type$ByteMaskProviderForIOOBE") static void loadMemorySegmentMaskIOOBE(IntFunction<$type$[]> fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, $Boxtype$.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), $Boxtype$.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, $Boxtype$.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), $Boxtype$.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask<$Boxtype$> vmask = VectorMask.fromValues(SPECIES, mask); @@ -623,8 +623,8 @@ public class $vectorteststype$ extends AbstractVectorLoadStoreTest { @Test(dataProvider = "$type$ByteMaskProviderForIOOBE") static void storeMemorySegmentMaskIOOBE(IntFunction<$type$[]> fa, IntFunction fi, IntFunction fm) { - MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, $Boxtype$.SIZE, MemorySession.openImplicit())); - MemorySegment r = MemorySegment.allocateNative(a.byteSize(), $Boxtype$.SIZE, MemorySession.openImplicit()); + MemorySegment a = toSegment(fa.apply(SPECIES.length()), i -> MemorySegment.allocateNative(i, $Boxtype$.SIZE, SegmentScope.auto())); + MemorySegment r = MemorySegment.allocateNative(a.byteSize(), $Boxtype$.SIZE, SegmentScope.auto()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask<$Boxtype$> vmask = VectorMask.fromValues(SPECIES, mask); diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/BulkMismatchAcquire.java b/test/micro/org/openjdk/bench/java/lang/foreign/BulkMismatchAcquire.java index a8fb7d9fc37d4..61b705e5b2643 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/BulkMismatchAcquire.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/BulkMismatchAcquire.java @@ -37,8 +37,8 @@ import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.annotations.Warmup; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.nio.ByteBuffer; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; @@ -52,18 +52,17 @@ public class BulkMismatchAcquire { public enum SessionKind { - CONFINED(MemorySession::openConfined), - SHARED(MemorySession::openShared), - IMPLICIT(MemorySession::openImplicit); + CONFINED(Arena::openConfined), + SHARED(Arena::openShared); - final Supplier sessionFactory; + final Supplier arenaFactory; - SessionKind(Supplier sessionFactory) { - this.sessionFactory = sessionFactory; + SessionKind(Supplier arenaFactory) { + this.arenaFactory = arenaFactory; } - MemorySession makeSession() { - return sessionFactory.get(); + Arena makeArena() { + return arenaFactory.get(); } } @@ -73,7 +72,7 @@ MemorySession makeSession() { // large(ish) segments/buffers with same content, 0, for mismatch, non-multiple-of-8 sized static final int SIZE_WITH_TAIL = (1024 * 1024) + 7; - MemorySession session; + Arena arena; MemorySegment mismatchSegmentLarge1; MemorySegment mismatchSegmentLarge2; ByteBuffer mismatchBufferLarge1; @@ -85,15 +84,15 @@ MemorySession makeSession() { @Setup public void setup() { - session = sessionKind.makeSession(); - mismatchSegmentLarge1 = MemorySegment.allocateNative(SIZE_WITH_TAIL, session); - mismatchSegmentLarge2 = MemorySegment.allocateNative(SIZE_WITH_TAIL, session); + arena = sessionKind.makeArena(); + mismatchSegmentLarge1 = arena.allocate(SIZE_WITH_TAIL); + mismatchSegmentLarge2 = arena.allocate(SIZE_WITH_TAIL); mismatchBufferLarge1 = ByteBuffer.allocateDirect(SIZE_WITH_TAIL); mismatchBufferLarge2 = ByteBuffer.allocateDirect(SIZE_WITH_TAIL); // mismatch at first byte - mismatchSegmentSmall1 = MemorySegment.allocateNative(7, session); - mismatchSegmentSmall2 = MemorySegment.allocateNative(7, session); + mismatchSegmentSmall1 = arena.allocate(7); + mismatchSegmentSmall2 = arena.allocate(7); mismatchBufferSmall1 = ByteBuffer.allocateDirect(7); mismatchBufferSmall2 = ByteBuffer.allocateDirect(7); { @@ -117,9 +116,7 @@ public void setup() { @TearDown public void tearDown() { - if (session.isCloseable()) { - session.close(); - } + arena.close(); } @Benchmark @@ -132,7 +129,7 @@ public long mismatch_large_segment() { @OutputTimeUnit(TimeUnit.NANOSECONDS) public long mismatch_large_segment_acquire() { long[] arr = new long[1]; - mismatchSegmentLarge1.session().whileAlive(() -> { + mismatchSegmentLarge1.scope().whileAlive(() -> { arr[0] = mismatchSegmentLarge1.mismatch(mismatchSegmentSmall2); }); return arr[0]; @@ -154,7 +151,7 @@ public long mismatch_small_segment() { @OutputTimeUnit(TimeUnit.NANOSECONDS) public long mismatch_small_segment_acquire() { long[] arr = new long[1]; - mismatchSegmentLarge1.session().whileAlive(() -> { + mismatchSegmentLarge1.scope().whileAlive(() -> { arr[0] = mismatchSegmentSmall1.mismatch(mismatchSegmentSmall2); }); return arr[0]; diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/BulkOps.java b/test/micro/org/openjdk/bench/java/lang/foreign/BulkOps.java index cfb11d937e5d4..64ad5c4b377ee 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/BulkOps.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/BulkOps.java @@ -37,14 +37,15 @@ import org.openjdk.jmh.annotations.Warmup; import sun.misc.Unsafe; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.util.concurrent.TimeUnit; import static java.lang.foreign.ValueLayout.JAVA_BYTE; import static java.lang.foreign.ValueLayout.JAVA_INT; +import static java.lang.foreign.ValueLayout.JAVA_INT_UNALIGNED; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -60,10 +61,10 @@ public class BulkOps { static final int CARRIER_SIZE = (int)JAVA_INT.byteSize(); static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE; - final MemorySession session = MemorySession.openConfined(); + final Arena arena = Arena.openShared(); final long unsafe_addr = unsafe.allocateMemory(ALLOC_SIZE); - final MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE, MemorySession.openConfined()); + final MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE, arena.scope()); final IntBuffer buffer = IntBuffer.allocate(ELEM_SIZE); final int[] ints = new int[ELEM_SIZE]; @@ -72,14 +73,14 @@ public class BulkOps { // large(ish) segments/buffers with same content, 0, for mismatch, non-multiple-of-8 sized static final int SIZE_WITH_TAIL = (1024 * 1024) + 7; - final MemorySegment mismatchSegmentLarge1 = MemorySegment.allocateNative(SIZE_WITH_TAIL, session); - final MemorySegment mismatchSegmentLarge2 = MemorySegment.allocateNative(SIZE_WITH_TAIL, session); + final MemorySegment mismatchSegmentLarge1 = MemorySegment.allocateNative(SIZE_WITH_TAIL, arena.scope()); + final MemorySegment mismatchSegmentLarge2 = MemorySegment.allocateNative(SIZE_WITH_TAIL, arena.scope());; final ByteBuffer mismatchBufferLarge1 = ByteBuffer.allocateDirect(SIZE_WITH_TAIL); final ByteBuffer mismatchBufferLarge2 = ByteBuffer.allocateDirect(SIZE_WITH_TAIL); // mismatch at first byte - final MemorySegment mismatchSegmentSmall1 = MemorySegment.allocateNative(7, session); - final MemorySegment mismatchSegmentSmall2 = MemorySegment.allocateNative(7, session); + final MemorySegment mismatchSegmentSmall1 = MemorySegment.allocateNative(7, arena.scope());; + final MemorySegment mismatchSegmentSmall2 = MemorySegment.allocateNative(7, arena.scope());; final ByteBuffer mismatchBufferSmall1 = ByteBuffer.allocateDirect(7); final ByteBuffer mismatchBufferSmall2 = ByteBuffer.allocateDirect(7); @@ -108,7 +109,7 @@ public void setup() { @TearDown public void tearDown() { - session.close(); + arena.close(); } @Benchmark @@ -138,20 +139,20 @@ public void segment_copy() { @Benchmark @OutputTimeUnit(TimeUnit.NANOSECONDS) public void segment_copy_static() { - MemorySegment.copy(ints, 0, segment, JAVA_BYTE, 0, ints.length); + MemorySegment.copy(ints, 0, segment, JAVA_INT_UNALIGNED, 0, ints.length); } @Benchmark @OutputTimeUnit(TimeUnit.NANOSECONDS) public void segment_copy_static_small() { - MemorySegment.copy(ints, 0, segment, JAVA_BYTE, 0, 10); + MemorySegment.copy(ints, 0, segment, JAVA_INT_UNALIGNED, 0, 10); } @Benchmark @CompilerControl(CompilerControl.Mode.DONT_INLINE) @OutputTimeUnit(TimeUnit.NANOSECONDS) public void segment_copy_static_small_dontinline() { - MemorySegment.copy(ints, 0, segment, JAVA_BYTE, 0, 10); + MemorySegment.copy(ints, 0, segment, JAVA_INT_UNALIGNED, 0, 10); } @Benchmark @@ -176,7 +177,7 @@ public void buffer_copy() { @CompilerControl(CompilerControl.Mode.DONT_INLINE) @OutputTimeUnit(TimeUnit.NANOSECONDS) public void segment_copy_static_dontinline() { - MemorySegment.copy(ints, 0, segment, JAVA_BYTE, 0, ints.length); + MemorySegment.copy(ints, 0, segment, JAVA_INT_UNALIGNED, 0, ints.length); } @Benchmark diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/CLayouts.java b/test/micro/org/openjdk/bench/java/lang/foreign/CLayouts.java index 24cc10047d397..d88838905fdd7 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/CLayouts.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/CLayouts.java @@ -23,10 +23,9 @@ package org.openjdk.bench.java.lang.foreign; -import java.lang.foreign.Addressable; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryAddress; +import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; @@ -67,17 +66,17 @@ public class CLayouts { /** * The {@code T*} native type. */ - public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS; + public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.asUnbounded(); private static Linker LINKER = Linker.nativeLinker(); private static final MethodHandle FREE = LINKER.downcallHandle( - LINKER.defaultLookup().lookup("free").get(), FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)); + LINKER.defaultLookup().find("free").get(), FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)); private static final MethodHandle MALLOC = LINKER.downcallHandle( - LINKER.defaultLookup().lookup("malloc").get(), FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.JAVA_LONG)); + LINKER.defaultLookup().find("malloc").get(), FunctionDescriptor.of(ValueLayout.ADDRESS.asUnbounded(), ValueLayout.JAVA_LONG)); - public static void freeMemory(Addressable address) { + public static void freeMemory(MemorySegment address) { try { FREE.invokeExact(address); } catch (Throwable ex) { @@ -85,9 +84,9 @@ public static void freeMemory(Addressable address) { } } - public static MemoryAddress allocateMemory(long size) { + public static MemorySegment allocateMemory(long size) { try { - return (MemoryAddress)MALLOC.invokeExact(size); + return (MemorySegment)MALLOC.invokeExact(size); } catch (Throwable ex) { throw new IllegalStateException(ex); } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadConstant.java b/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadConstant.java index d87c48b56f5f3..e542276ccf6cd 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadConstant.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadConstant.java @@ -22,8 +22,6 @@ */ package org.openjdk.bench.java.lang.foreign; -import java.lang.foreign.Addressable; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemorySegment; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -87,53 +85,38 @@ public MemorySegment panama_identity_struct_shared_3() throws Throwable { } @Benchmark - public MemoryAddress panama_identity_memory_address_shared() throws Throwable { - return (MemoryAddress) identity_memory_address.invokeExact((Addressable)sharedPoint.address()); + public MemorySegment panama_identity_memory_address_shared() throws Throwable { + return (MemorySegment) identity_memory_address.invokeExact(sharedPoint); } @Benchmark - public MemoryAddress panama_identity_memory_address_confined() throws Throwable { - return (MemoryAddress) identity_memory_address.invokeExact((Addressable)confinedPoint.address()); + public MemorySegment panama_identity_memory_address_confined() throws Throwable { + return (MemorySegment) identity_memory_address.invokeExact(confinedPoint); } @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()); + public MemorySegment panama_identity_memory_address_shared_3() throws Throwable { + return (MemorySegment) identity_memory_address_3.invokeExact(sharedPoint, sharedPoint, sharedPoint); } @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()); + public MemorySegment panama_identity_memory_address_confined_3() throws Throwable { + return (MemorySegment) identity_memory_address_3.invokeExact(confinedPoint, confinedPoint, confinedPoint); } @Benchmark - public MemoryAddress panama_identity_struct_ref_shared() throws Throwable { - return (MemoryAddress) identity_memory_address.invokeExact((Addressable)sharedPoint); + public MemorySegment panama_identity_memory_address_null() throws Throwable { + return (MemorySegment) identity_memory_address.invokeExact(MemorySegment.NULL); } @Benchmark - public MemoryAddress panama_identity_struct_ref_confined() throws Throwable { - return (MemoryAddress) identity_memory_address.invokeExact((Addressable)confinedPoint); + public MemorySegment panama_identity_memory_address_null_3() throws Throwable { + return (MemorySegment) identity_memory_address_3.invokeExact(MemorySegment.NULL, MemorySegment.NULL, MemorySegment.NULL); } @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 MemoryAddress panama_identity_memory_address_null() throws Throwable { - return (MemoryAddress) identity_memory_address.invokeExact((Addressable)MemoryAddress.NULL); - } - - @Benchmark - public MemoryAddress panama_identity_memory_address_null_non_exact() throws Throwable { - return (MemoryAddress) identity_memory_address.invoke(MemoryAddress.NULL); + public MemorySegment panama_identity_memory_address_null_non_exact() throws Throwable { + return (MemorySegment) identity_memory_address.invoke(MemorySegment.NULL); } @Benchmark diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadHelper.java b/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadHelper.java index 32ff0ffdee179..bbbc0afbb0f53 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadHelper.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadHelper.java @@ -22,12 +22,12 @@ */ package org.openjdk.bench.java.lang.foreign; -import java.lang.foreign.Addressable; +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.SegmentAllocator; import java.lang.foreign.SymbolLookup; @@ -42,51 +42,51 @@ public class CallOverheadHelper extends CLayouts { static final MethodHandle func; static final MethodHandle func_v; - static Addressable func_addr; + static MemorySegment func_addr; static final MethodHandle identity; static final MethodHandle identity_v; - static Addressable identity_addr; + static MemorySegment identity_addr; static final MethodHandle identity_struct; static final MethodHandle identity_struct_v; - static Addressable identity_struct_addr; + static MemorySegment identity_struct_addr; static final MethodHandle identity_struct_3; static final MethodHandle identity_struct_3_v; - static Addressable identity_struct_3_addr; + static MemorySegment identity_struct_3_addr; static final MethodHandle identity_memory_address; static final MethodHandle identity_memory_address_v; - static Addressable identity_memory_address_addr; + static MemorySegment identity_memory_address_addr; static final MethodHandle identity_memory_address_3; static final MethodHandle identity_memory_address_3_v; - static Addressable identity_memory_address_3_addr; + static MemorySegment identity_memory_address_3_addr; static final MethodHandle args1; static final MethodHandle args1_v; - static Addressable args1_addr; + static MemorySegment args1_addr; static final MethodHandle args2; static final MethodHandle args2_v; - static Addressable args2_addr; + static MemorySegment args2_addr; static final MethodHandle args3; static final MethodHandle args3_v; - static Addressable args3_addr; + static MemorySegment args3_addr; static final MethodHandle args4; static final MethodHandle args4_v; - static Addressable args4_addr; + static MemorySegment args4_addr; static final MethodHandle args5; static final MethodHandle args5_v; - static Addressable args5_addr; + static MemorySegment args5_addr; static final MethodHandle args10; static final MethodHandle args10_v; - static Addressable args10_addr; + static MemorySegment args10_addr; static final MemoryLayout POINT_LAYOUT = MemoryLayout.structLayout( C_INT, C_INT ); - static final MemorySegment sharedPoint = MemorySegment.allocateNative(POINT_LAYOUT, MemorySession.openShared()); - static final MemorySegment confinedPoint = MemorySegment.allocateNative(POINT_LAYOUT, MemorySession.openConfined()); + static final MemorySegment sharedPoint = MemorySegment.allocateNative(POINT_LAYOUT, Arena.openShared().scope()); + static final MemorySegment confinedPoint = MemorySegment.allocateNative(POINT_LAYOUT, Arena.openConfined().scope()); - static final MemorySegment point = MemorySegment.allocateNative(POINT_LAYOUT, MemorySession.openImplicit()); + static final MemorySegment point = MemorySegment.allocateNative(POINT_LAYOUT, SegmentScope.auto()); - static final SegmentAllocator recycling_allocator = SegmentAllocator.prefixAllocator(MemorySegment.allocateNative(POINT_LAYOUT, MemorySession.openImplicit())); + static final SegmentAllocator recycling_allocator = SegmentAllocator.prefixAllocator(MemorySegment.allocateNative(POINT_LAYOUT, SegmentScope.auto())); static { System.loadLibrary("CallOverheadJNI"); @@ -94,64 +94,64 @@ public class CallOverheadHelper extends CLayouts { System.loadLibrary("CallOverhead"); SymbolLookup loaderLibs = SymbolLookup.loaderLookup(); { - func_addr = loaderLibs.lookup("func").orElseThrow(); + func_addr = loaderLibs.find("func").orElseThrow(); MethodType mt = MethodType.methodType(void.class); FunctionDescriptor fd = FunctionDescriptor.ofVoid(); func_v = abi.downcallHandle(fd); func = insertArguments(func_v, 0, func_addr); } { - identity_addr = loaderLibs.lookup("identity").orElseThrow(); + identity_addr = loaderLibs.find("identity").orElseThrow(); FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_INT); identity_v = abi.downcallHandle(fd); identity = insertArguments(identity_v, 0, identity_addr); } - identity_struct_addr = loaderLibs.lookup("identity_struct").orElseThrow(); + identity_struct_addr = loaderLibs.find("identity_struct").orElseThrow(); identity_struct_v = abi.downcallHandle( FunctionDescriptor.of(POINT_LAYOUT, POINT_LAYOUT)); identity_struct = insertArguments(identity_struct_v, 0, identity_struct_addr); - identity_struct_3_addr = loaderLibs.lookup("identity_struct_3").orElseThrow(); + identity_struct_3_addr = loaderLibs.find("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 = loaderLibs.lookup("identity_memory_address").orElseThrow(); + identity_memory_address_addr = loaderLibs.find("identity_memory_address").orElseThrow(); identity_memory_address_v = abi.downcallHandle( FunctionDescriptor.of(C_POINTER, C_POINTER)); identity_memory_address = insertArguments(identity_memory_address_v, 0, identity_memory_address_addr); - identity_memory_address_3_addr = loaderLibs.lookup("identity_memory_address_3").orElseThrow(); + identity_memory_address_3_addr = loaderLibs.find("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 = loaderLibs.lookup("args1").orElseThrow(); + args1_addr = loaderLibs.find("args1").orElseThrow(); args1_v = abi.downcallHandle( FunctionDescriptor.ofVoid(C_LONG_LONG)); args1 = insertArguments(args1_v, 0, args1_addr); - args2_addr = loaderLibs.lookup("args2").orElseThrow(); + args2_addr = loaderLibs.find("args2").orElseThrow(); args2_v = abi.downcallHandle( FunctionDescriptor.ofVoid(C_LONG_LONG, C_DOUBLE)); args2 = insertArguments(args2_v, 0, args2_addr); - args3_addr = loaderLibs.lookup("args3").orElseThrow(); + args3_addr = loaderLibs.find("args3").orElseThrow(); args3_v = abi.downcallHandle( FunctionDescriptor.ofVoid(C_LONG_LONG, C_DOUBLE, C_LONG_LONG)); args3 = insertArguments(args3_v, 0, args3_addr); - args4_addr = loaderLibs.lookup("args4").orElseThrow(); + args4_addr = loaderLibs.find("args4").orElseThrow(); args4_v = abi.downcallHandle( FunctionDescriptor.ofVoid(C_LONG_LONG, C_DOUBLE, C_LONG_LONG, C_DOUBLE)); args4 = insertArguments(args4_v, 0, args4_addr); - args5_addr = loaderLibs.lookup("args5").orElseThrow(); + args5_addr = loaderLibs.find("args5").orElseThrow(); args5_v = abi.downcallHandle( FunctionDescriptor.ofVoid(C_LONG_LONG, C_DOUBLE, C_LONG_LONG, C_DOUBLE, C_LONG_LONG)); args5 = insertArguments(args5_v, 0, args5_addr); - args10_addr = loaderLibs.lookup("args10").orElseThrow(); + args10_addr = loaderLibs.find("args10").orElseThrow(); args10_v = abi.downcallHandle( 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)); diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadVirtual.java b/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadVirtual.java index 10fe5a9055ba4..de3b275e034b5 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadVirtual.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadVirtual.java @@ -22,8 +22,6 @@ */ package org.openjdk.bench.java.lang.foreign; -import java.lang.foreign.Addressable; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemorySegment; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -81,43 +79,23 @@ public MemorySegment panama_identity_struct_shared_3() throws Throwable { } @Benchmark - public MemoryAddress panama_identity_memory_address_shared() throws Throwable { - return (MemoryAddress) identity_memory_address_v.invokeExact(identity_memory_address_addr, (Addressable)sharedPoint.address()); + public MemorySegment panama_identity_memory_address_shared() throws Throwable { + return (MemorySegment) identity_memory_address_v.invokeExact(identity_memory_address_addr, sharedPoint); } @Benchmark - public MemoryAddress panama_identity_memory_address_confined() throws Throwable { - return (MemoryAddress) identity_memory_address_v.invokeExact(identity_memory_address_addr, (Addressable)confinedPoint.address()); + public MemorySegment panama_identity_memory_address_confined() throws Throwable { + return (MemorySegment) identity_memory_address_v.invokeExact(identity_memory_address_addr, confinedPoint); } @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()); + public MemorySegment panama_identity_memory_address_shared_3() throws Throwable { + return (MemorySegment) identity_memory_address_3_v.invokeExact(identity_memory_address_3_addr, sharedPoint, sharedPoint, sharedPoint); } @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); + public MemorySegment panama_identity_memory_address_confined_3() throws Throwable { + return (MemorySegment) identity_memory_address_3_v.invokeExact(identity_memory_address_3_addr, confinedPoint, confinedPoint, confinedPoint); } @Benchmark @@ -131,8 +109,8 @@ public MemorySegment panama_identity_struct() throws Throwable { } @Benchmark - public MemoryAddress panama_identity_memory_address_null() throws Throwable { - return (MemoryAddress) identity_memory_address_v.invokeExact(identity_memory_address_addr, (Addressable)MemoryAddress.NULL); + public MemorySegment panama_identity_memory_address_null() throws Throwable { + return (MemorySegment) identity_memory_address_v.invokeExact(identity_memory_address_addr, MemorySegment.NULL); } @Benchmark diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/JavaLayouts.java b/test/micro/org/openjdk/bench/java/lang/foreign/JavaLayouts.java index bc395a2310523..56ebaad541035 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/JavaLayouts.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/JavaLayouts.java @@ -26,29 +26,15 @@ import java.lang.foreign.ValueLayout; import java.lang.invoke.VarHandle; -import static java.lang.foreign.ValueLayout.JAVA_FLOAT; -import static java.lang.foreign.ValueLayout.JAVA_INT; -import static java.lang.foreign.ValueLayout.JAVA_LONG; +import static java.lang.foreign.ValueLayout.*; /** * Some useful Java {@link ValueLayout} and associated {@link ValueLayout#arrayElementVarHandle(int...)} var handles. */ public class JavaLayouts { - static final ValueLayout.OfInt JAVA_INT_UNALIGNED = JAVA_INT.withBitAlignment(8); - - static final ValueLayout.OfFloat JAVA_FLOAT_UNALIGNED = JAVA_FLOAT.withBitAlignment(8); - - static final ValueLayout.OfLong JAVA_LONG_UNALIGNED = JAVA_LONG.withBitAlignment(8); static final VarHandle VH_INT_UNALIGNED = JAVA_INT_UNALIGNED.arrayElementVarHandle(); static final VarHandle VH_INT = JAVA_INT.arrayElementVarHandle(); - static final VarHandle VH_FLOAT_UNALIGNED = JAVA_FLOAT_UNALIGNED.arrayElementVarHandle(); - - static final VarHandle VH_FLOAT = JAVA_FLOAT.arrayElementVarHandle(); - - static final VarHandle VH_LONG_UNALIGNED = JAVA_LONG_UNALIGNED.arrayElementVarHandle(); - - static final VarHandle VH_LONG = JAVA_LONG.arrayElementVarHandle(); } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LinkUpcall.java b/test/micro/org/openjdk/bench/java/lang/foreign/LinkUpcall.java index 917370d215712..57b42a0e24a69 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/LinkUpcall.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LinkUpcall.java @@ -35,7 +35,7 @@ import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.util.concurrent.TimeUnit; @@ -64,7 +64,7 @@ public class LinkUpcall extends CLayouts { @Benchmark public MemorySegment link_blank() { - return LINKER.upcallStub(BLANK, BLANK_DESC, MemorySession.openImplicit()); + return LINKER.upcallStub(BLANK, BLANK_DESC, SegmentScope.auto()); } static void blank() {} diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverConstant.java b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverConstant.java index ded0d8ca09fad..a01891894f78e 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverConstant.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverConstant.java @@ -22,7 +22,6 @@ */ package org.openjdk.bench.java.lang.foreign; -import java.lang.foreign.MemorySession; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.CompilerControl; @@ -37,11 +36,12 @@ import sun.misc.Unsafe; import java.lang.foreign.MemorySegment; +import java.lang.foreign.SegmentScope; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.concurrent.TimeUnit; -import static java.lang.foreign.ValueLayout.JAVA_INT; +import static java.lang.foreign.ValueLayout.*; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -69,7 +69,7 @@ public class LoopOverConstant extends JavaLayouts { //setup native memory segment - static final MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE, MemorySession.openImplicit()); + static final MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE, SegmentScope.auto()); static { for (int i = 0; i < ELEM_SIZE; i++) { diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNew.java b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNew.java index c1598522516ff..05be20222fa42 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNew.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNew.java @@ -22,10 +22,12 @@ */ package org.openjdk.bench.java.lang.foreign; +import java.lang.foreign.Arena; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; +import java.lang.foreign.SegmentScope; import java.lang.foreign.SegmentAllocator; -import java.lang.foreign.MemorySession; + import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -42,7 +44,7 @@ import java.nio.ByteOrder; import java.util.concurrent.TimeUnit; -import static java.lang.foreign.ValueLayout.JAVA_INT; +import static java.lang.foreign.ValueLayout.*; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -58,12 +60,12 @@ public class LoopOverNew extends JavaLayouts { static final int CARRIER_SIZE = (int)JAVA_INT.byteSize(); static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE; static final MemoryLayout ALLOC_LAYOUT = MemoryLayout.sequenceLayout(ELEM_SIZE, JAVA_INT); - final MemorySession session = MemorySession.openConfined(); - final SegmentAllocator recyclingAlloc = SegmentAllocator.prefixAllocator(MemorySegment.allocateNative(ALLOC_LAYOUT, session)); + final Arena arena = Arena.openConfined(); + final SegmentAllocator recyclingAlloc = SegmentAllocator.prefixAllocator(MemorySegment.allocateNative(ALLOC_LAYOUT, arena.scope())); @TearDown public void tearDown() throws Throwable { - session.close(); + arena.close(); } @Benchmark @@ -77,8 +79,8 @@ public void unsafe_loop() { @Benchmark public void segment_loop_confined() { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE, 4, session); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = arena.allocate(ALLOC_SIZE, 4); for (int i = 0; i < ELEM_SIZE; i++) { VH_INT.set(segment, (long) i, i); } @@ -87,8 +89,8 @@ public void segment_loop_confined() { @Benchmark public void segment_loop_shared() { - try (MemorySession session = MemorySession.openShared()) { - MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE, 4, session); + try (Arena arena = Arena.openShared()) { + MemorySegment segment = arena.allocate(ALLOC_SIZE, 4); for (int i = 0; i < ELEM_SIZE; i++) { VH_INT.set(segment, (long) i, i); } @@ -133,7 +135,7 @@ public void buffer_loop_implicit() { @Benchmark public void segment_loop_implicit() { if (gcCount++ == 0) System.gc(); // GC when we overflow - MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE, 4, MemorySession.openImplicit()); + MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE, 4, SegmentScope.auto()); for (int i = 0; i < ELEM_SIZE; i++) { VH_INT.set(segment, (long) i, i); } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNewHeap.java b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNewHeap.java index 5793b42707459..2fd596d8a369c 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNewHeap.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNewHeap.java @@ -40,7 +40,7 @@ import java.nio.IntBuffer; import java.util.concurrent.TimeUnit; -import static java.lang.foreign.ValueLayout.JAVA_INT; +import static java.lang.foreign.ValueLayout.*; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstant.java b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstant.java index a287b1120f6e8..c5b697b8ac86f 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstant.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstant.java @@ -22,8 +22,9 @@ */ package org.openjdk.bench.java.lang.foreign; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; + import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -40,7 +41,7 @@ import java.nio.ByteOrder; import java.util.concurrent.TimeUnit; -import static java.lang.foreign.ValueLayout.JAVA_INT; +import static java.lang.foreign.ValueLayout.*; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -55,6 +56,8 @@ public class LoopOverNonConstant extends JavaLayouts { static final int ELEM_SIZE = 1_000_000; static final int CARRIER_SIZE = (int)JAVA_INT.byteSize(); static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE; + + Arena arena; MemorySegment segment; long unsafe_addr; @@ -66,7 +69,8 @@ public void setup() { for (int i = 0; i < ELEM_SIZE; i++) { unsafe.putInt(unsafe_addr + (i * CARRIER_SIZE) , i); } - segment = MemorySegment.allocateNative(ALLOC_SIZE, MemorySession.openConfined()); + arena = Arena.openConfined(); + segment = MemorySegment.allocateNative(ALLOC_SIZE, arena.scope()); for (int i = 0; i < ELEM_SIZE; i++) { VH_INT.set(segment, (long) i, i); } @@ -78,7 +82,7 @@ public void setup() { @TearDown public void tearDown() { - segment.session().close(); + arena.close(); unsafe.invokeCleaner(byteBuffer); unsafe.freeMemory(unsafe_addr); } @@ -157,24 +161,6 @@ public int segment_loop_instance_unaligned() { return res; } - @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; - } - @Benchmark public int segment_loop_slice() { int sum = 0; diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantFP.java b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantFP.java index 65f818c71b14c..430a3b69d045b 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantFP.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantFP.java @@ -22,8 +22,9 @@ */ package org.openjdk.bench.java.lang.foreign; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; + import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -56,7 +57,7 @@ public class LoopOverNonConstantFP { static final int CARRIER_SIZE = (int)JAVA_DOUBLE.byteSize(); static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE; - MemorySession session; + Arena arena; MemorySegment segmentIn, segmentOut; long unsafe_addrIn, unsafe_addrOut; ByteBuffer byteBufferIn, byteBufferOut; @@ -71,9 +72,9 @@ public void setup() { for (int i = 0; i < ELEM_SIZE; i++) { unsafe.putDouble(unsafe_addrOut + (i * CARRIER_SIZE), i); } - session = MemorySession.openConfined(); - segmentIn = MemorySegment.allocateNative(ALLOC_SIZE, session); - segmentOut = MemorySegment.allocateNative(ALLOC_SIZE, session); + arena = Arena.openConfined(); + segmentIn = MemorySegment.allocateNative(ALLOC_SIZE, arena.scope()); + segmentOut = MemorySegment.allocateNative(ALLOC_SIZE, arena.scope()); for (int i = 0; i < ELEM_SIZE; i++) { segmentIn.setAtIndex(JAVA_DOUBLE, i, i); } @@ -92,7 +93,7 @@ public void setup() { @TearDown public void tearDown() { - session.close(); + arena.close(); unsafe.invokeCleaner(byteBufferIn); unsafe.invokeCleaner(byteBufferOut); unsafe.freeMemory(unsafe_addrIn); diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantHeap.java b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantHeap.java index c49c050cbe878..386fe2691eab3 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantHeap.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantHeap.java @@ -23,7 +23,7 @@ package org.openjdk.bench.java.lang.foreign; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; + import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -37,14 +37,12 @@ import org.openjdk.jmh.annotations.Warmup; import sun.misc.Unsafe; +import java.lang.foreign.SegmentScope; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.concurrent.TimeUnit; -import static java.lang.foreign.ValueLayout.JAVA_BYTE; -import static java.lang.foreign.ValueLayout.JAVA_DOUBLE; -import static java.lang.foreign.ValueLayout.JAVA_FLOAT; -import static java.lang.foreign.ValueLayout.JAVA_INT; +import static java.lang.foreign.ValueLayout.*; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -78,7 +76,7 @@ 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, MemorySession.openConfined()); + MemorySegment s = MemorySegment.allocateNative(ALLOC_SIZE, 1, SegmentScope.auto()); for (int i = 0; i < ALLOC_SIZE; i++) { intB.set(JAVA_BYTE, i, (byte)i); intI.setAtIndex(JAVA_INT, i, i); diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantMapped.java b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantMapped.java index f208586eb97b9..0572223f84e1d 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantMapped.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantMapped.java @@ -22,8 +22,9 @@ */ package org.openjdk.bench.java.lang.foreign; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; + import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -47,7 +48,7 @@ import java.nio.file.StandardOpenOption; import java.util.concurrent.TimeUnit; -import static java.lang.foreign.ValueLayout.JAVA_INT; +import static java.lang.foreign.ValueLayout.*; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -78,6 +79,7 @@ public class LoopOverNonConstantMapped extends JavaLayouts { } FileChannel fileChannel; + Arena arena; MemorySegment segment; long unsafe_addr; @@ -93,14 +95,15 @@ public void setup() throws IOException { ((MappedByteBuffer)byteBuffer).force(); } fileChannel = FileChannel.open(tempPath, StandardOpenOption.READ, StandardOpenOption.WRITE); - segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, ALLOC_SIZE, MemorySession.openConfined()); - unsafe_addr = segment.address().toRawLongValue(); + arena = Arena.openConfined(); + segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, ALLOC_SIZE, arena.scope()); + unsafe_addr = segment.address(); } @TearDown public void tearDown() throws IOException { fileChannel.close(); - segment.session().close(); + arena.close(); unsafe.invokeCleaner(byteBuffer); } @@ -149,15 +152,6 @@ public int segment_loop_instance() { 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; - } - @Benchmark public int segment_loop_slice() { int sum = 0; diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantShared.java b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantShared.java index dd61a3cf3b5ca..f3d47fed5d101 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantShared.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantShared.java @@ -22,8 +22,9 @@ */ package org.openjdk.bench.java.lang.foreign; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; + import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -55,6 +56,7 @@ public class LoopOverNonConstantShared extends JavaLayouts { static final int ELEM_SIZE = 1_000_000; static final int CARRIER_SIZE = (int)JAVA_INT.byteSize(); static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE; + Arena arena; MemorySegment segment; long unsafe_addr; @@ -66,7 +68,8 @@ 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, MemorySession.openConfined()); + arena = Arena.openConfined(); + segment = MemorySegment.allocateNative(ALLOC_SIZE, CARRIER_SIZE, arena.scope()); for (int i = 0; i < ELEM_SIZE; i++) { VH_INT.set(segment, (long) i, i); } @@ -78,7 +81,7 @@ public void setup() { @TearDown public void tearDown() { - segment.session().close(); + arena.close(); unsafe.invokeCleaner(byteBuffer); unsafe.freeMemory(unsafe_addr); } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverOfAddress.java b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverOfAddress.java new file mode 100644 index 0000000000000..2463856fbdfb9 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverOfAddress.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.lang.foreign; + +import java.lang.foreign.MemorySegment; + +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.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; + +import java.lang.foreign.SegmentScope; +import java.util.concurrent.TimeUnit; + +@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 = "--enable-preview") +public class LoopOverOfAddress extends JavaLayouts { + + static final int ITERATIONS = 1_000_000; + + @Benchmark + public long segment_loop_addr() { + long res = 0; + for (int i = 0; i < ITERATIONS; i++) { + res += MemorySegment.ofAddress(i % 100).address(); + } + return res; + } + + @Benchmark + public long segment_loop_addr_size() { + long res = 0; + for (int i = 0; i < ITERATIONS; i++) { + res += MemorySegment.ofAddress(i, i % 100).address(); + } + return res; + } + + @Benchmark + public long segment_loop_addr_size_session() { + long res = 0; + for (int i = 0; i < ITERATIONS; i++) { + res += MemorySegment.ofAddress(i, i % 100, SegmentScope.global()).address(); + } + return res; + } +} diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverPollutedSegments.java b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverPollutedSegments.java index 6ed7ab6bc2968..425a1adce44fc 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverPollutedSegments.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverPollutedSegments.java @@ -22,8 +22,9 @@ */ package org.openjdk.bench.java.lang.foreign; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; + import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -38,7 +39,7 @@ import java.util.concurrent.TimeUnit; -import static java.lang.foreign.ValueLayout.JAVA_INT; +import static java.lang.foreign.ValueLayout.*; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -55,7 +56,7 @@ public class LoopOverPollutedSegments extends JavaLayouts { static final Unsafe unsafe = Utils.unsafe; - MemorySession session; + Arena confinedArena, sharedArena; MemorySegment nativeSegment, nativeSharedSegment, heapSegmentBytes, heapSegmentFloats; byte[] arr; long addr; @@ -67,8 +68,10 @@ public void setup() { unsafe.putInt(addr + (i * 4), i); } arr = new byte[ALLOC_SIZE]; - nativeSegment = MemorySegment.allocateNative(ALLOC_SIZE, 4, session = MemorySession.openConfined()); - nativeSharedSegment = MemorySegment.allocateNative(ALLOC_SIZE, 4, session); + confinedArena = Arena.openConfined(); + sharedArena = Arena.openShared(); + nativeSegment = MemorySegment.allocateNative(ALLOC_SIZE, 4, confinedArena.scope()); + nativeSharedSegment = MemorySegment.allocateNative(ALLOC_SIZE, 4, sharedArena.scope()); heapSegmentBytes = MemorySegment.ofArray(new byte[ALLOC_SIZE]); heapSegmentFloats = MemorySegment.ofArray(new float[ELEM_SIZE]); @@ -92,7 +95,8 @@ public void setup() { @TearDown public void tearDown() { - session.close(); + confinedArena.close(); + sharedArena.close(); heapSegmentBytes = null; heapSegmentFloats = null; arr = null; diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverSlice.java b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverSlice.java index 4c5e79c483aa8..23ac72d0e68ca 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverSlice.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverSlice.java @@ -22,8 +22,9 @@ */ package org.openjdk.bench.java.lang.foreign; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; + import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -56,19 +57,22 @@ public class LoopOverSlice { static final int CARRIER_SIZE = (int)JAVA_INT.byteSize(); static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE; + Arena arena; MemorySegment nativeSegment, heapSegment; IntBuffer nativeBuffer, heapBuffer; @Setup public void setup() { - nativeSegment = MemorySegment.allocateNative(ALLOC_SIZE, MemorySession.openConfined()); + arena = Arena.openConfined(); + nativeSegment = MemorySegment.allocateNative(ALLOC_SIZE, arena.scope()); heapSegment = MemorySegment.ofArray(new int[ELEM_SIZE]); nativeBuffer = ByteBuffer.allocateDirect(ALLOC_SIZE).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer(); heapBuffer = IntBuffer.wrap(new int[ELEM_SIZE]); } @TearDown - public void tearDown() { nativeSegment.session().close(); + public void tearDown() { + arena.close(); } @Benchmark diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/MemorySessionClose.java b/test/micro/org/openjdk/bench/java/lang/foreign/MemorySessionClose.java index e609da7fa7e40..6df2fb0d6c1f8 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/MemorySessionClose.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/MemorySessionClose.java @@ -22,8 +22,9 @@ */ package org.openjdk.bench.java.lang.foreign; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; + import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -36,10 +37,9 @@ import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.annotations.Warmup; +import java.lang.foreign.SegmentScope; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @BenchmarkMode(Mode.AverageTime) @@ -104,27 +104,27 @@ public void tearDown() throws Throwable { @Benchmark public MemorySegment confined_close() { - try (MemorySession session = MemorySession.openConfined()) { - return MemorySegment.allocateNative(ALLOC_SIZE, 4, session); + try (Arena arena = Arena.openConfined()) { + return arena.allocate(ALLOC_SIZE, 4); } } @Benchmark public MemorySegment shared_close() { - try (MemorySession session = MemorySession.openShared()) { - return MemorySegment.allocateNative(ALLOC_SIZE, 4, session); + try (Arena arena = Arena.openShared()) { + return arena.allocate(ALLOC_SIZE, 4); } } @Benchmark public MemorySegment implicit_close() { - return MemorySegment.allocateNative(ALLOC_SIZE, 4, MemorySession.openImplicit()); + return MemorySegment.allocateNative(ALLOC_SIZE, 4, SegmentScope.auto()); } @Benchmark public MemorySegment implicit_close_systemgc() { if (gcCount++ == 0) System.gc(); // GC when we overflow - return MemorySegment.allocateNative(ALLOC_SIZE, 4, MemorySession.openImplicit()); + return MemorySegment.allocateNative(ALLOC_SIZE, 4, SegmentScope.auto()); } // keep diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/ParallelSum.java b/test/micro/org/openjdk/bench/java/lang/foreign/ParallelSum.java index 05128cedbb778..78575b3c45c74 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/ParallelSum.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/ParallelSum.java @@ -23,10 +23,11 @@ package org.openjdk.bench.java.lang.foreign; +import java.lang.foreign.Arena; import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySession; import java.lang.foreign.SequenceLayout; import java.lang.foreign.ValueLayout; + import sun.misc.Unsafe; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -68,6 +69,7 @@ public class ParallelSum extends JavaLayouts { static final Unsafe unsafe = Utils.unsafe; + Arena arena; MemorySegment segment; long address; @@ -77,7 +79,8 @@ public void setup() { for (int i = 0; i < ELEM_SIZE; i++) { unsafe.putInt(address + (i * CARRIER_SIZE), i); } - segment = MemorySegment.allocateNative(ALLOC_SIZE, CARRIER_SIZE, MemorySession.openShared()); + arena = Arena.openShared(); + segment = MemorySegment.allocateNative(ALLOC_SIZE, CARRIER_SIZE, arena.scope()); for (int i = 0; i < ELEM_SIZE; i++) { VH_INT.set(segment, (long) i, i); } @@ -86,7 +89,7 @@ public void setup() { @TearDown public void tearDown() throws Throwable { unsafe.freeMemory(address); - segment.session().close(); + arena.close(); } @Benchmark diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/PointerInvoke.java b/test/micro/org/openjdk/bench/java/lang/foreign/PointerInvoke.java index d7abdb7288532..6f4aa4c17b6cf 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/PointerInvoke.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/PointerInvoke.java @@ -25,16 +25,14 @@ package org.openjdk.bench.java.lang.foreign; -import java.lang.foreign.Addressable; +import java.lang.foreign.Arena; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; + import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; @@ -54,8 +52,8 @@ @Fork(value = 3, jvmArgsAppend = { "--enable-native-access=ALL-UNNAMED", "--enable-preview" }) public class PointerInvoke extends CLayouts { - MemorySession session = MemorySession.openConfined(); - MemorySegment segment = MemorySegment.allocateNative(100, session); + Arena arena = Arena.openConfined(); + MemorySegment segment = MemorySegment.allocateNative(100, arena.scope()); static { System.loadLibrary("Ptr"); @@ -66,35 +64,30 @@ public class PointerInvoke extends CLayouts { static { Linker abi = Linker.nativeLinker(); SymbolLookup loaderLibs = SymbolLookup.loaderLookup(); - F_LONG = abi.downcallHandle(loaderLibs.lookup("func_as_long").get(), + F_LONG = abi.downcallHandle(loaderLibs.find("func_as_long").get(), FunctionDescriptor.of(C_INT, C_LONG_LONG)); - F_PTR = abi.downcallHandle(loaderLibs.lookup("func_as_ptr").get(), + F_PTR = abi.downcallHandle(loaderLibs.find("func_as_ptr").get(), FunctionDescriptor.of(C_INT, C_POINTER)); } @TearDown public void tearDown() { - session.close(); + arena.close(); } @Benchmark public int panama_call_as_long() throws Throwable { - return (int)F_LONG.invokeExact(segment.address().toRawLongValue()); + return (int)F_LONG.invokeExact(segment.address()); } @Benchmark public int panama_call_as_address() throws Throwable { - return (int)F_PTR.invokeExact((Addressable)segment.address()); - } - - @Benchmark - public int panama_call_as_segment() throws Throwable { - return (int)F_PTR.invokeExact((Addressable)segment); + return (int)F_PTR.invokeExact(segment); } @Benchmark public int panama_call_as_new_segment() throws Throwable { - MemorySegment newSegment = MemorySegment.ofAddress(segment.address(), 100, session); - return (int)F_PTR.invokeExact((Addressable)newSegment); + MemorySegment newSegment = MemorySegment.ofAddress(segment.address(), 100, arena.scope()); + return (int)F_PTR.invokeExact(newSegment); } } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/QSort.java b/test/micro/org/openjdk/bench/java/lang/foreign/QSort.java index 8fd2a64a34a7b..0b8b4e9708ee1 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/QSort.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/QSort.java @@ -22,13 +22,11 @@ */ package org.openjdk.bench.java.lang.foreign; -import java.lang.foreign.Addressable; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; + import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -38,6 +36,7 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; +import java.lang.foreign.SegmentScope; import java.lang.foreign.SymbolLookup; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; @@ -56,17 +55,17 @@ public class QSort extends CLayouts { static final Linker abi = Linker.nativeLinker(); static final MethodHandle clib_qsort; - static final Addressable native_compar; - static final Addressable panama_upcall_compar; + static final MemorySegment native_compar; + static final MemorySegment 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 Addressable qsort_addr = abi.defaultLookup().lookup("qsort").get(); + static MemorySegment qsort_addr = abi.defaultLookup().find("qsort").get(); static { - INPUT_SEGMENT = MemorySegment.allocateNative(MemoryLayout.sequenceLayout(INPUT.length, JAVA_INT), MemorySession.global()); + INPUT_SEGMENT = MemorySegment.allocateNative(MemoryLayout.sequenceLayout(INPUT.length, JAVA_INT), SegmentScope.global()); INPUT_SEGMENT.copyFrom(MemorySegment.ofArray(INPUT)); System.loadLibrary("QSortJNI"); @@ -78,13 +77,13 @@ public class QSort extends CLayouts { FunctionDescriptor.ofVoid(C_POINTER, C_LONG_LONG, C_LONG_LONG, C_POINTER) ); System.loadLibrary("QSort"); - native_compar = SymbolLookup.loaderLookup().lookup("compar").orElseThrow(); + native_compar = SymbolLookup.loaderLookup().find("compar").orElseThrow(); panama_upcall_compar = abi.upcallStub( lookup().findStatic(QSort.class, "panama_upcall_compar", - MethodType.methodType(int.class, MemoryAddress.class, MemoryAddress.class)), + MethodType.methodType(int.class, MemorySegment.class, MemorySegment.class)), FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER), - MemorySession.global() + SegmentScope.global() ); } catch (ReflectiveOperationException e) { throw new BootstrapMethodError(e); @@ -103,7 +102,7 @@ interface JNIComparator { @Benchmark public void native_qsort() throws Throwable { - clib_qsort.invokeExact((Addressable)INPUT_SEGMENT, (long) INPUT.length, JAVA_INT.byteSize(), (Addressable)native_compar); + clib_qsort.invokeExact(INPUT_SEGMENT, (long) INPUT.length, JAVA_INT.byteSize(), native_compar); } @Benchmark @@ -118,15 +117,11 @@ public void jni_upcall_qsort_naive() { @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); + clib_qsort.invokeExact(INPUT_SEGMENT, (long) INPUT.length, JAVA_INT.byteSize(), panama_upcall_compar); } - static int panama_upcall_compar(MemoryAddress e0, MemoryAddress e1) { - return Integer.compare(getIntAbsolute(e0), getIntAbsolute(e1)); + static int panama_upcall_compar(MemorySegment e0, MemorySegment e1) { + return Integer.compare(e0.get(JAVA_INT, 0), e1.get(JAVA_INT, 0)); } static int jni_upcall_compar(int j0, int j1) { diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/StrLenTest.java b/test/micro/org/openjdk/bench/java/lang/foreign/StrLenTest.java index 5ec95dce4f93a..cd943a022f4f0 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/StrLenTest.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/StrLenTest.java @@ -25,13 +25,13 @@ package org.openjdk.bench.java.lang.foreign; -import java.lang.foreign.Addressable; +import java.lang.foreign.Arena; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.lang.foreign.SegmentAllocator; + import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -57,10 +57,10 @@ @Fork(value = 3, jvmArgsAppend = { "--enable-native-access=ALL-UNNAMED", "--enable-preview" }) public class StrLenTest extends CLayouts { - MemorySession session = MemorySession.openConfined(); + Arena arena = Arena.openConfined(); SegmentAllocator segmentAllocator; - SegmentAllocator arenaAllocator = SegmentAllocator.newNativeArena(session); + SegmentAllocator arenaAllocator = new RingAllocator(arena.scope()); @Param({"5", "20", "100"}) public int size; @@ -74,19 +74,19 @@ public class StrLenTest extends CLayouts { static { Linker abi = Linker.nativeLinker(); - STRLEN = abi.downcallHandle(abi.defaultLookup().lookup("strlen").get(), + STRLEN = abi.downcallHandle(abi.defaultLookup().find("strlen").get(), FunctionDescriptor.of(C_INT, C_POINTER)); } @Setup public void setup() { str = makeString(size); - segmentAllocator = SegmentAllocator.prefixAllocator(MemorySegment.allocateNative(size + 1, MemorySession.openConfined())); + segmentAllocator = SegmentAllocator.prefixAllocator(MemorySegment.allocateNative(size + 1, arena.scope())); } @TearDown public void tearDown() { - session.close(); + arena.close(); } @Benchmark @@ -96,36 +96,35 @@ public int jni_strlen() throws Throwable { @Benchmark public int panama_strlen() throws Throwable { - try (MemorySession session = MemorySession.openConfined()) { - MemorySegment segment = MemorySegment.allocateNative(str.length() + 1, session); - segment.setUtf8String(0, str); - return (int)STRLEN.invokeExact((Addressable)segment); + try (Arena arena = Arena.openConfined()) { + MemorySegment segment = arena.allocateUtf8String(str); + return (int)STRLEN.invokeExact(segment); } } @Benchmark - public int panama_strlen_arena() throws Throwable { - return (int)STRLEN.invokeExact((Addressable)arenaAllocator.allocateUtf8String(str)); + public int panama_strlen_ring() throws Throwable { + return (int)STRLEN.invokeExact(arenaAllocator.allocateUtf8String(str)); } @Benchmark public int panama_strlen_prefix() throws Throwable { - return (int)STRLEN.invokeExact((Addressable)segmentAllocator.allocateUtf8String(str)); + return (int)STRLEN.invokeExact(segmentAllocator.allocateUtf8String(str)); } @Benchmark public int panama_strlen_unsafe() throws Throwable { - MemoryAddress address = makeStringUnsafe(str); - int res = (int) STRLEN.invokeExact((Addressable)address); + MemorySegment address = makeStringUnsafe(str); + int res = (int) STRLEN.invokeExact(address); freeMemory(address); return res; } - static MemoryAddress makeStringUnsafe(String s) { + static MemorySegment makeStringUnsafe(String s) { byte[] bytes = s.getBytes(); int len = bytes.length; - MemoryAddress address = allocateMemory(len + 1); - MemorySegment str = MemorySegment.ofAddress(address, len + 1, MemorySession.global()); + MemorySegment address = allocateMemory(len + 1); + MemorySegment str = address.asSlice(0, len + 1); str.copyFrom(MemorySegment.ofArray(bytes)); str.set(JAVA_BYTE, len, (byte)0); return address; @@ -143,4 +142,31 @@ static String makeString(int size) { """; return lorem.substring(0, size); } + + static class RingAllocator implements SegmentAllocator { + final MemorySegment segment; + SegmentAllocator current; + long rem; + + public RingAllocator(SegmentScope session) { + this.segment = MemorySegment.allocateNative(1024, session); + reset(); + } + + @Override + public MemorySegment allocate(long byteSize, long byteAlignment) { + if (rem < byteSize) { + reset(); + } + MemorySegment res = current.allocate(byteSize, byteAlignment); + long lastOffset = segment.segmentOffset(res) + res.byteSize(); + rem = segment.byteSize() - lastOffset; + return res; + } + + void reset() { + current = SegmentAllocator.slicingAllocator(segment); + rem = segment.byteSize(); + } + } } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/TestLoadBytes.java b/test/micro/org/openjdk/bench/java/lang/foreign/TestLoadBytes.java index ec487cd75ad80..c1ff656f7803d 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/TestLoadBytes.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/TestLoadBytes.java @@ -24,7 +24,7 @@ package org.openjdk.bench.java.lang.foreign; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; + import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.CompilerControl; @@ -37,6 +37,7 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; +import java.lang.foreign.SegmentScope; import java.nio.ByteBuffer; import java.util.concurrent.TimeUnit; @@ -67,7 +68,7 @@ public void setup() { } srcBufferNative = ByteBuffer.allocateDirect(size); - srcSegmentImplicit = MemorySegment.allocateNative(size, MemorySession.openImplicit()); + srcSegmentImplicit = MemorySegment.allocateNative(size, SegmentScope.auto()); } @Benchmark diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/UnrolledAccess.java b/test/micro/org/openjdk/bench/java/lang/foreign/UnrolledAccess.java index 709b6aec9c5be..5586aa866c6fa 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/UnrolledAccess.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/UnrolledAccess.java @@ -24,6 +24,8 @@ package org.openjdk.bench.java.lang.foreign; import java.lang.foreign.*; +import java.lang.foreign.SegmentScope; +import java.lang.invoke.VarHandle; import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.options.Options; @@ -31,7 +33,7 @@ import sun.misc.Unsafe; import java.util.concurrent.TimeUnit; -import static java.lang.foreign.ValueLayout.JAVA_LONG; +import static java.lang.foreign.ValueLayout.*; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -43,6 +45,10 @@ public class UnrolledAccess extends JavaLayouts { static final Unsafe U = Utils.unsafe; + static final VarHandle VH_LONG_UNALIGNED = JAVA_LONG_UNALIGNED.arrayElementVarHandle(); + + static final VarHandle VH_LONG = JAVA_LONG.arrayElementVarHandle(); + final static int SIZE = 1024; @State(Scope.Benchmark) @@ -61,8 +67,8 @@ public Data() { this.outputArray = new double[SIZE]; this.inputAddress = U.allocateMemory(8 * SIZE); this.outputAddress = U.allocateMemory(8 * SIZE); - this.inputSegment = MemorySegment.ofAddress(MemoryAddress.ofLong(inputAddress), 8*SIZE, MemorySession.global()); - this.outputSegment = MemorySegment.ofAddress(MemoryAddress.ofLong(outputAddress), 8*SIZE, MemorySession.global()); + this.inputSegment = MemorySegment.ofAddress(inputAddress, 8*SIZE, SegmentScope.global()); + this.outputSegment = MemorySegment.ofAddress(outputAddress, 8*SIZE, SegmentScope.global()); } } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/Upcalls.java b/test/micro/org/openjdk/bench/java/lang/foreign/Upcalls.java index f40c3c5916e5e..ed79a30b6e8b8 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/Upcalls.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/Upcalls.java @@ -22,10 +22,10 @@ */ package org.openjdk.bench.java.lang.foreign; -import java.lang.foreign.Addressable; +import java.lang.foreign.MemorySegment; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemorySession; + import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -35,6 +35,7 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; +import java.lang.foreign.SegmentScope; import java.lang.foreign.SymbolLookup; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; @@ -56,10 +57,10 @@ public class Upcalls extends CLayouts { static final MethodHandle args5; static final MethodHandle args10; - static final Addressable cb_blank; - static final Addressable cb_identity; - static final Addressable cb_args5; - static final Addressable cb_args10; + static final MemorySegment cb_blank; + static final MemorySegment cb_identity; + static final MemorySegment cb_args5; + static final MemorySegment cb_args10; static final long cb_blank_jni; static final long cb_identity_jni; @@ -122,15 +123,15 @@ public class Upcalls extends CLayouts { static MethodHandle linkFunc(String name, FunctionDescriptor baseDesc) { return abi.downcallHandle( - SymbolLookup.loaderLookup().lookup(name).orElseThrow(), + SymbolLookup.loaderLookup().find(name).orElseThrow(), baseDesc.appendArgumentLayouts(C_POINTER) ); } - static Addressable makeCB(String name, MethodType mt, FunctionDescriptor fd) throws ReflectiveOperationException { + static MemorySegment makeCB(String name, MethodType mt, FunctionDescriptor fd) throws ReflectiveOperationException { return abi.upcallStub( lookup().findStatic(Upcalls.class, name, mt), - fd, MemorySession.global() + fd, SegmentScope.global() ); } @@ -147,7 +148,7 @@ public void jni_blank() throws Throwable { @Benchmark public void panama_blank() throws Throwable { - blank.invokeExact((Addressable)cb_blank); + blank.invokeExact(cb_blank); } @Benchmark @@ -167,17 +168,17 @@ public void jni_args10() throws Throwable { @Benchmark public int panama_identity() throws Throwable { - return (int) identity.invokeExact(10, (Addressable)cb_identity); + return (int) identity.invokeExact(10, cb_identity); } @Benchmark public void panama_args5() throws Throwable { - args5.invokeExact(1L, 2D, 3L, 4D, 5L, (Addressable)cb_args5); + args5.invokeExact(1L, 2D, 3L, 4D, 5L, cb_args5); } @Benchmark public void panama_args10() throws Throwable { - args10.invokeExact(1L, 2D, 3L, 4D, 5L, 6D, 7L, 8D, 9L, 10D, (Addressable)cb_args10); + args10.invokeExact(1L, 2D, 3L, 4D, 5L, 6D, 7L, 8D, 9L, 10D, cb_args10); } static void blank() {} diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/VaList.java b/test/micro/org/openjdk/bench/java/lang/foreign/VaList.java index 46815f565dd7f..a9205c2251524 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/VaList.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/VaList.java @@ -22,10 +22,10 @@ */ package org.openjdk.bench.java.lang.foreign; -import java.lang.foreign.Addressable; +import java.lang.foreign.Arena; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemorySession; + import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -57,9 +57,10 @@ public class VaList extends CLayouts { static { SymbolLookup loaderLibs = SymbolLookup.loaderLookup(); - MH_ellipsis = linker.downcallHandle(loaderLibs.lookup("ellipsis").get(), - FunctionDescriptor.ofVoid(C_INT).asVariadic(C_INT, C_DOUBLE, C_LONG_LONG)); - MH_vaList = linker.downcallHandle(loaderLibs.lookup("vaList").get(), + MH_ellipsis = linker.downcallHandle(loaderLibs.find("ellipsis").get(), + FunctionDescriptor.ofVoid(C_INT, C_INT, C_DOUBLE, C_LONG_LONG), + Linker.Option.firstVariadicArg(1)); + MH_vaList = linker.downcallHandle(loaderLibs.find("vaList").get(), FunctionDescriptor.ofVoid(C_INT, C_POINTER)); } @@ -71,13 +72,13 @@ public void ellipsis() throws Throwable { @Benchmark public void vaList() throws Throwable { - try (MemorySession session = MemorySession.openConfined()) { + try (Arena arena = Arena.openConfined()) { java.lang.foreign.VaList vaList = java.lang.foreign.VaList.make(b -> b.addVarg(C_INT, 1) .addVarg(C_DOUBLE, 2D) - .addVarg(C_LONG_LONG, 3L), session); + .addVarg(C_LONG_LONG, 3L), arena.scope()); MH_vaList.invokeExact(3, - (Addressable)vaList); + vaList.segment()); } } } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/VarHandleExact.java b/test/micro/org/openjdk/bench/java/lang/foreign/VarHandleExact.java index 574bfdc27a824..e6501eae35fee 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/VarHandleExact.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/VarHandleExact.java @@ -22,8 +22,9 @@ */ package org.openjdk.bench.java.lang.foreign; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; + import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -57,16 +58,18 @@ public class VarHandleExact { exact = generic.withInvokeExactBehavior(); } + Arena arena; MemorySegment data; @Setup public void setup() { - data = MemorySegment.allocateNative(JAVA_INT, MemorySession.openConfined()); + arena = Arena.openConfined(); + data = MemorySegment.allocateNative(JAVA_INT, arena.scope()); } @TearDown public void tearDown() { - data.session().close(); + arena.close(); } @Benchmark diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/pointers/NativeType.java b/test/micro/org/openjdk/bench/java/lang/foreign/pointers/NativeType.java new file mode 100644 index 0000000000000..159464db3907c --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/foreign/pointers/NativeType.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.bench.java.lang.foreign.pointers; + +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.ValueLayout; + +public sealed abstract class NativeType { + + public abstract MemoryLayout layout(); + + public non-sealed static abstract class OfInt extends NativeType { + public abstract ValueLayout.OfInt layout(); + } + public non-sealed static abstract class OfDouble extends NativeType { + public abstract ValueLayout.OfDouble layout(); + } + + private static final ValueLayout.OfAddress UNSAFE_ADDRESS = ValueLayout.ADDRESS.asUnbounded(); + + public final static class OfPointer extends NativeType { + public ValueLayout.OfAddress layout() { + return UNSAFE_ADDRESS; + } + } + + public non-sealed static abstract class OfStruct extends NativeType { + public abstract GroupLayout layout(); + public abstract X make(Pointer ptr); + } + + public static final OfInt C_INT = new OfInt<>() { + @Override + public ValueLayout.OfInt layout() { + return ValueLayout.JAVA_INT; + } + }; + + public static final OfDouble C_DOUBLE = new OfDouble<>() { + @Override + public ValueLayout.OfDouble layout() { + return ValueLayout.JAVA_DOUBLE; + } + }; + + @SuppressWarnings("unchecked") + final static OfPointer C_VOID_PTR = new OfPointer(); + + @SuppressWarnings("unchecked") + public static final OfPointer> C_INT_PTR = NativeType.C_VOID_PTR; + @SuppressWarnings("unchecked") + public static final OfPointer> C_DOUBLE_PTR = NativeType.C_VOID_PTR; + + + + @SuppressWarnings("unchecked") + public static OfPointer> ptr(NativeType type) { + return NativeType.C_VOID_PTR; + } +} diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/pointers/Point.java b/test/micro/org/openjdk/bench/java/lang/foreign/pointers/Point.java new file mode 100644 index 0000000000000..223cdf6665227 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/foreign/pointers/Point.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.bench.java.lang.foreign.pointers; + +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; + +public class Point extends Struct { + + Point(Pointer ptr) { + super(ptr); + } + + int x() { + return ptr.segment.get(NativeType.C_INT.layout(), 0); + } + int y() { + return ptr.segment.get(NativeType.C_INT.layout(), 4); + } + + static Point wrap(MemorySegment segment) { + return new Point(Pointer.wrap(TYPE, segment)); + } + + static final NativeType.OfStruct TYPE = new NativeType.OfStruct() { + static final GroupLayout LAYOUT = MemoryLayout.structLayout( + ValueLayout.JAVA_INT.withName("x"), + ValueLayout.JAVA_INT.withName("y")); + + @Override + public GroupLayout layout() { + return LAYOUT; + } + + @Override + public Point make(Pointer pointer) { + return new Point(pointer); + } + }; +} diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/pointers/Pointer.java b/test/micro/org/openjdk/bench/java/lang/foreign/pointers/Pointer.java new file mode 100644 index 0000000000000..13a96a2ab59e2 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/foreign/pointers/Pointer.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.bench.java.lang.foreign.pointers; + + +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SegmentAllocator; + +public class Pointer { + + final MemorySegment segment; + + Pointer(MemorySegment segment) { + this.segment = segment; + } + + public > int get(Z type, long index) { + return segment.getAtIndex(type.layout(), index); + } + + public > double get(Z type, long index) { + return segment.getAtIndex(type.layout(), index); + } + + public > X get(Z type, long index) { + return type.make(addOffset(index * type.layout().byteSize())); + } + + public Pointer addOffset(long offset) { + return new Pointer<>(segment.asSlice(offset)); + } + + @SuppressWarnings("unchecked") + public > X get(Z type, long index) { + MemorySegment address = segment.getAtIndex(type.layout(), index); + return (X)new Pointer<>(address); + } + + @SuppressWarnings("unchecked") + public X get(NativeType type, long offset) { + if (type instanceof NativeType.OfInt intType) { + return (X) (Object) get(intType, offset); + } else if (type instanceof NativeType.OfDouble doubleType) { + return (X) (Object) get(doubleType, offset); + } else { + throw new UnsupportedOperationException(); + } + } + + public MemorySegment segment() { + return segment; + } + + public static Pointer allocate(NativeType type, SegmentAllocator allocator) { + MemorySegment segment = allocator.allocate(type.layout()); + return new Pointer<>(segment); + } + + public static Pointer allocate(NativeType type, long size, SegmentAllocator allocator) { + MemorySegment segment = allocator.allocateArray(type.layout(), size); + return new Pointer<>(segment); + } + + public static Pointer wrap(NativeType type, MemorySegment segment) { + return new Pointer<>(segment); + } +} diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/pointers/PointerBench.java b/test/micro/org/openjdk/bench/java/lang/foreign/pointers/PointerBench.java new file mode 100644 index 0000000000000..1a1d196c2ae4f --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/foreign/pointers/PointerBench.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.bench.java.lang.foreign.pointers; + +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.Scope; +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 java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 3, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Fork(value = 3, jvmArgsAppend = "--enable-preview") +@State(Scope.Benchmark) +public class PointerBench { + + final Arena arena = Arena.openConfined(); + static final int ELEM_SIZE = 1_000_000; + Pointer intPointer = Pointer.allocate(NativeType.C_INT, ELEM_SIZE, arena); + Pointer> intPointerPointer = Pointer.allocate(NativeType.C_INT_PTR, ELEM_SIZE, arena); + Pointer pointPointer = Pointer.allocate(Point.TYPE, ELEM_SIZE, arena); + MemorySegment intSegment = intPointer.segment(); + MemorySegment intPointerSegment = intPointerPointer.segment(); + MemorySegment pointSegment = pointPointer.segment(); + + public static final ValueLayout.OfAddress UNSAFE_ADDRESS = ValueLayout.ADDRESS.asUnbounded(); + + @Setup + public void setup() { + for (int i = 0 ; i < ELEM_SIZE ; i++) { + intSegment.setAtIndex(ValueLayout.JAVA_INT, i, i); + intPointerSegment.setAtIndex(ValueLayout.ADDRESS, i, intSegment.asSlice(4 * i)); + pointSegment.setAtIndex(ValueLayout.JAVA_INT, (i * 2), i); + pointSegment.setAtIndex(ValueLayout.JAVA_INT, (i * 2) + 1, i); + } + } + + @TearDown + public void teardown() { + arena.close(); + } + + @Benchmark + public int testLoopPointerInt_ptr() { + int sum = 0; + for (int i = 0 ; i < ELEM_SIZE ; i++) { + sum += intPointer.get(NativeType.C_INT, i); + } + return sum; + } + + @Benchmark + public int testLoopPointerPointerInt_ptr() { + int sum = 0; + for (int i = 0 ; i < ELEM_SIZE ; i++) { + sum += intPointerPointer.get(NativeType.C_INT_PTR, i) + .get(NativeType.C_INT, 0); + } + return sum; + } + + @Benchmark + public int testLoopPointerPoint_ptr() { + int sum = 0; + for (int i = 0 ; i < ELEM_SIZE ; i++) { + sum += pointPointer.get(Point.TYPE, i).x(); + } + return sum; + } + + @Benchmark + public int testLoopPointerInt_ptr_generic() { + int sum = 0; + for (int i = 0 ; i < ELEM_SIZE ; i++) { + sum += genericGet(intPointer, NativeType.C_INT, i); + } + return sum; + } + + static Z genericGet(Pointer pz, NativeType type, long offset) { + return pz.get(type, offset); + } + + @Benchmark + public int testLoopPointerInt_segment() { + int sum = 0; + for (int i = 0 ; i < ELEM_SIZE ; i++) { + sum += intSegment.getAtIndex(ValueLayout.JAVA_INT, i); + } + return sum; + } + + @Benchmark + public int testLoopPointerPointerInt_segment() { + int sum = 0; + for (long i = 0 ; i < ELEM_SIZE ; i++) { + var segment = intPointerSegment.getAtIndex(UNSAFE_ADDRESS, i); + sum += segment.get(ValueLayout.JAVA_INT, 0); + } + return sum; + } + + @Benchmark + public int testLoopPointerPoint_segment() { + int sum = 0; + for (int i = 0 ; i < ELEM_SIZE ; i++) { + sum += pointSegment.getAtIndex(ValueLayout.JAVA_INT, i * 2); + } + return sum; + } +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/Scoped.java b/test/micro/org/openjdk/bench/java/lang/foreign/pointers/Struct.java similarity index 67% rename from src/java.base/share/classes/jdk/internal/foreign/Scoped.java rename to test/micro/org/openjdk/bench/java/lang/foreign/pointers/Struct.java index d33557341f361..f9a0c05b377f7 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/Scoped.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/pointers/Struct.java @@ -4,9 +4,7 @@ * * 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. + * 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 @@ -23,16 +21,12 @@ * questions. */ -package jdk.internal.foreign; +package org.openjdk.bench.java.lang.foreign.pointers; -import jdk.internal.vm.annotation.ForceInline; +public abstract class Struct> { + protected final Pointer ptr; -import java.lang.foreign.MemorySession; - -public interface Scoped { - @ForceInline - default MemorySessionImpl sessionImpl() { - return MemorySessionImpl.toSessionImpl(session()); + public Struct(Pointer ptr) { + this.ptr = ptr; } - MemorySession session(); } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/points/support/PanamaPoint.java b/test/micro/org/openjdk/bench/java/lang/foreign/points/support/PanamaPoint.java index 4f5fe751a9855..1ed1d7a899cc2 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/points/support/PanamaPoint.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/points/support/PanamaPoint.java @@ -22,12 +22,12 @@ */ package org.openjdk.bench.java.lang.foreign.points.support; -import java.lang.foreign.Addressable; +import java.lang.foreign.Arena; import java.lang.foreign.Linker; import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; + import org.openjdk.bench.java.lang.foreign.CLayouts; import java.lang.foreign.SymbolLookup; @@ -53,19 +53,21 @@ public class PanamaPoint extends CLayouts implements AutoCloseable { System.loadLibrary("Point"); SymbolLookup loaderLibs = SymbolLookup.loaderLookup(); MH_distance = abi.downcallHandle( - loaderLibs.lookup("distance").get(), + loaderLibs.find("distance").get(), FunctionDescriptor.of(C_DOUBLE, LAYOUT, LAYOUT) ); MH_distance_ptrs = abi.downcallHandle( - loaderLibs.lookup("distance_ptrs").get(), + loaderLibs.find("distance_ptrs").get(), FunctionDescriptor.of(C_DOUBLE, C_POINTER, C_POINTER) ); } + Arena arena; private final MemorySegment segment; public PanamaPoint(int x, int y) { - this.segment = MemorySegment.allocateNative(LAYOUT, MemorySession.openConfined()); + this.arena = Arena.openConfined(); + this.segment = MemorySegment.allocateNative(LAYOUT, arena.scope()); setX(x); setY(y); } @@ -96,7 +98,7 @@ public double distanceTo(PanamaPoint other) { public double distanceToPtrs(PanamaPoint other) { try { - return (double) MH_distance_ptrs.invokeExact((Addressable)segment, (Addressable)other.segment); + return (double) MH_distance_ptrs.invokeExact(segment, other.segment); } catch (Throwable throwable) { throw new InternalError(throwable); } @@ -104,6 +106,6 @@ public double distanceToPtrs(PanamaPoint other) { @Override public void close() { - segment.session().close(); + arena.close(); } } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/MemorySegmentVectorAccess.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/MemorySegmentVectorAccess.java index a73beddfe87e7..4f6c4aa65f217 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/vector/MemorySegmentVectorAccess.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/MemorySegmentVectorAccess.java @@ -26,7 +26,7 @@ package org.openjdk.bench.jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.nio.ByteOrder; import java.util.concurrent.TimeUnit; import jdk.incubator.vector.ByteVector; @@ -69,8 +69,8 @@ public class MemorySegmentVectorAccess { @Setup public void setup() { - nativeIn = MemorySegment.allocateNative(size, MemorySession.openImplicit()); - nativeOut = MemorySegment.allocateNative(size, MemorySession.openImplicit()); + nativeIn = MemorySegment.allocateNative(size, SegmentScope.auto()); + nativeOut = MemorySegment.allocateNative(size, SegmentScope.auto()); byteIn = new byte[size]; byteOut = new byte[size]; 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 d28c860ebe00c..62735fa3b0093 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/vector/TestLoadStoreBytes.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/TestLoadStoreBytes.java @@ -23,9 +23,9 @@ */ package org.openjdk.bench.jdk.incubator.vector; -import java.lang.foreign.MemoryAddress; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; +import java.lang.foreign.SegmentScope; import java.nio.ByteOrder; import java.util.concurrent.TimeUnit; import jdk.incubator.vector.ByteVector; @@ -43,8 +43,6 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; -import static java.lang.foreign.ValueLayout.JAVA_BYTE; - @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -70,18 +68,10 @@ public class TestLoadStoreBytes { private MemorySegment dstSegmentHeap; - - private MemorySession implicitScope; - private MemorySegment srcSegment; private MemorySegment dstSegment; - - private MemoryAddress srcAddress; - - private MemoryAddress dstAddress; - private byte[] a, b, c; @Setup @@ -95,12 +85,8 @@ public void setup() { srcSegmentHeap = MemorySegment.ofArray(new byte[size]); dstSegmentHeap = MemorySegment.ofArray(new byte[size]); - implicitScope = MemorySession.openImplicit(); - srcSegment = MemorySegment.allocateNative(size, SPECIES.vectorByteSize(), implicitScope); - dstSegment = MemorySegment.allocateNative(size, SPECIES.vectorByteSize(), implicitScope); - - srcAddress = srcSegment.address(); - dstAddress = dstSegment.address(); + srcSegment = MemorySegment.allocateNative(size, SPECIES.vectorByteSize(), SegmentScope.auto()); + dstSegment = MemorySegment.allocateNative(size, SPECIES.vectorByteSize(), SegmentScope.auto()); a = new byte[size]; b = new byte[size]; @@ -177,9 +163,9 @@ public void segmentNativeImplicit() { @Benchmark public void segmentNativeConfined() { - try (final var session = MemorySession.openConfined()) { - final var srcSegmentConfined = MemorySegment.ofAddress(srcAddress, size, session); - final var dstSegmentConfined = MemorySegment.ofAddress(dstAddress, size, session); + try (final var arena = Arena.openConfined()) { + final var srcSegmentConfined = MemorySegment.ofAddress(srcSegment.address(), size, arena.scope()); + final var dstSegmentConfined = MemorySegment.ofAddress(dstSegment.address(), size, arena.scope()); for (long i = 0; i < SPECIES.loopBound(srcArray.length); i += SPECIES.length()) { var v = ByteVector.fromMemorySegment(SPECIES, srcSegmentConfined, i, ByteOrder.nativeOrder()); diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/TestLoadStoreShorts.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/TestLoadStoreShorts.java index ef6f2bb4ed410..ecbe5889806a5 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/vector/TestLoadStoreShorts.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/TestLoadStoreShorts.java @@ -23,12 +23,13 @@ */ package org.openjdk.bench.jdk.incubator.vector; +import java.lang.foreign.Arena; +import java.lang.foreign.SegmentScope; import java.nio.ByteOrder; import java.util.concurrent.TimeUnit; -import java.lang.foreign.MemoryAddress; import java.lang.foreign.MemorySegment; -import java.lang.foreign.MemorySession; + import jdk.incubator.vector.ShortVector; import jdk.incubator.vector.VectorOperators; import jdk.incubator.vector.VectorSpecies; @@ -71,18 +72,10 @@ public class TestLoadStoreShorts { private MemorySegment dstSegmentHeap; - - private MemorySession implicitScope; - private MemorySegment srcSegment; private MemorySegment dstSegment; - - private MemoryAddress srcAddress; - - private MemoryAddress dstAddress; - private short[] a, b, c; @Setup @@ -97,12 +90,8 @@ public void setup() { srcSegmentHeap = MemorySegment.ofArray(new byte[size]); dstSegmentHeap = MemorySegment.ofArray(new byte[size]); - implicitScope = MemorySession.openImplicit(); - srcSegment = MemorySegment.allocateNative(size, SPECIES.vectorByteSize(), implicitScope); - dstSegment = MemorySegment.allocateNative(size, SPECIES.vectorByteSize(), implicitScope); - - srcAddress = srcSegment.address(); - dstAddress = dstSegment.address(); + srcSegment = MemorySegment.allocateNative(size, SPECIES.vectorByteSize(), SegmentScope.auto()); + dstSegment = MemorySegment.allocateNative(size, SPECIES.vectorByteSize(), SegmentScope.auto()); this.longSize = longSize; @@ -172,9 +161,9 @@ public void segmentNativeImplicit() { @Benchmark public void segmentNativeConfined() { - try (final var session = MemorySession.openConfined()) { - final var srcSegmentConfined = MemorySegment.ofAddress(srcAddress, size, session); - final var dstSegmentConfined = MemorySegment.ofAddress(dstAddress, size, session); + try (final var arena = Arena.openConfined()) { + final var srcSegmentConfined = MemorySegment.ofAddress(srcSegment.address(), size, arena.scope()); + final var dstSegmentConfined = MemorySegment.ofAddress(dstSegment.address(), size, arena.scope()); for (long i = 0; i < SPECIES.loopBound(srcArray.length); i += SPECIES.length()) { var v = ShortVector.fromMemorySegment(SPECIES, srcSegmentConfined, i, ByteOrder.nativeOrder());