mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8304265: Implementation of Foreign Function and Memory API (Third Preview)
Co-authored-by: Maurizio Cimadamore <mcimadamore@openjdk.org> Co-authored-by: Jorn Vernee <jvernee@openjdk.org> Co-authored-by: Paul Sandoz <psandoz@openjdk.org> Co-authored-by: Feilong Jiang <fjiang@openjdk.org> Co-authored-by: Per Minborg <pminborg@openjdk.org> Reviewed-by: erikj, jvernee, vlivanov, psandoz
This commit is contained in:
parent
41d58533ac
commit
cbccc4c817
267 changed files with 6947 additions and 8029 deletions
130
src/java.base/share/classes/java/lang/foreign/AddressLayout.java
Normal file
130
src/java.base/share/classes/java/lang/foreign/AddressLayout.java
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
package java.lang.foreign;
|
||||
|
||||
import jdk.internal.foreign.layout.ValueLayouts;
|
||||
import jdk.internal.javac.PreviewFeature;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
|
||||
import java.lang.foreign.Linker.Option;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A value layout used to model the address of some region of memory. The carrier associated with an address layout is
|
||||
* {@code MemorySegment.class}. The size and alignment of an address layout are platform dependent
|
||||
* (e.g. on a 64-bit platform, the size and alignment of an address layout are set to 64 bits).
|
||||
* <p>
|
||||
* An address layout may optionally feature a {@linkplain #targetLayout() target layout}. An address layout with
|
||||
* target layout {@code T} can be used to model the address of a region of memory whose layout is {@code T}.
|
||||
* For instance, an address layout with target layout {@link ValueLayout#JAVA_INT} can be used to model the address
|
||||
* of a region of memory that is 4 bytes long. Specifying a target layout can be useful in the following situations:
|
||||
* <ul>
|
||||
* <li>When accessing a memory segment that has been obtained by reading an address from another
|
||||
* memory segment, e.g. using {@link MemorySegment#getAtIndex(AddressLayout, long)};</li>
|
||||
* <li>When creating a downcall method handle, using {@link Linker#downcallHandle(FunctionDescriptor, Option...)};
|
||||
* <li>When creating an upcall stub, using {@link Linker#upcallStub(MethodHandle, FunctionDescriptor, Arena, Option...)}.
|
||||
* </ul>
|
||||
*
|
||||
* @see #ADDRESS
|
||||
* @see #ADDRESS_UNALIGNED
|
||||
* @since 19
|
||||
*/
|
||||
@PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
|
||||
public sealed interface AddressLayout extends ValueLayout permits ValueLayouts.OfAddressImpl {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
AddressLayout withName(String name);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
AddressLayout withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
AddressLayout withBitAlignment(long bitAlignment);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
AddressLayout withOrder(ByteOrder order);
|
||||
|
||||
/**
|
||||
* Returns an address layout with the same carrier, alignment constraint, name and order as this address layout,
|
||||
* but associated with the specified target layout. The returned address layout allows raw addresses to be accessed
|
||||
* as {@linkplain MemorySegment memory segments} whose size is set to the size of the specified layout. Moreover,
|
||||
* if the accessed raw address is not compatible with the alignment constraint in the provided layout,
|
||||
* {@linkplain IllegalArgumentException} will be thrown.
|
||||
* @apiNote
|
||||
* This method can also be used to create an address layout which, when used, creates native memory
|
||||
* segments with maximal size (e.g. {@linkplain Long#MAX_VALUE}). This can be done by using a target sequence
|
||||
* layout with unspecified size, as follows:
|
||||
* {@snippet lang = java:
|
||||
* AddressLayout addressLayout = ...
|
||||
* AddressLayout unboundedLayout = addressLayout.withTargetLayout(
|
||||
* MemoryLayout.sequenceLayout(ValueLayout.JAVA_BYTE));
|
||||
*}
|
||||
* <p>
|
||||
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
|
||||
* 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 target layout.
|
||||
* @return an address layout with same characteristics as this layout, but with the provided target layout.
|
||||
* @throws IllegalCallerException If the caller is in a module that does not have native access enabled.
|
||||
* @see #targetLayout()
|
||||
*/
|
||||
@CallerSensitive
|
||||
AddressLayout withTargetLayout(MemoryLayout layout);
|
||||
|
||||
/**
|
||||
* Returns an address layout with the same carrier, alignment constraint, name and order as this address layout,
|
||||
* but without any specified target layout.
|
||||
* <p>
|
||||
* This can be useful to compare two address layouts that have different target layouts, but are otherwise equal.
|
||||
*
|
||||
* @return an address layout with same characteristics as this layout, but with no target layout.
|
||||
* @see #targetLayout()
|
||||
*/
|
||||
AddressLayout withoutTargetLayout();
|
||||
|
||||
/**
|
||||
* {@return the target layout associated with this address layout (if any)}.
|
||||
*/
|
||||
Optional<MemoryLayout> targetLayout();
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -27,40 +27,105 @@ package java.lang.foreign;
|
|||
|
||||
import jdk.internal.foreign.MemorySessionImpl;
|
||||
import jdk.internal.javac.PreviewFeature;
|
||||
import jdk.internal.ref.CleanerFactory;
|
||||
|
||||
import java.lang.foreign.MemorySegment.Scope;
|
||||
|
||||
/**
|
||||
* An arena controls the lifecycle of memory segments, providing both flexible allocation and timely deallocation.
|
||||
* An arena controls the lifecycle of native memory segments, providing both flexible allocation and timely deallocation.
|
||||
* <p>
|
||||
* 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:
|
||||
* An arena has a {@linkplain MemorySegment.Scope scope} - the <em>arena scope</em>. All the segments allocated
|
||||
* by the arena are associated with the arena scope. As such, the arena determines the temporal bounds
|
||||
* of all the memory segments allocated by it.
|
||||
* <p>
|
||||
* Moreover, an arena also determines whether access to memory segments allocated by it should be
|
||||
* {@linkplain MemorySegment#isAccessibleBy(Thread) restricted} to specific threads.
|
||||
* An arena is a {@link SegmentAllocator} and features several allocation methods that can be used by clients
|
||||
* to obtain native segments.
|
||||
* <p>
|
||||
* The simplest arena is the {@linkplain Arena#global() global arena}. The global arena
|
||||
* features an <em>unbounded lifetime</em>. As such, native segments allocated with the global arena are always
|
||||
* accessible and their backing regions of memory are never deallocated. Moreover, memory segments allocated with the
|
||||
* global arena can be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} from any thread.
|
||||
* {@snippet lang = java:
|
||||
* MemorySegment segment = Arena.global().allocate(100, 1);
|
||||
* ...
|
||||
* // segment is never deallocated!
|
||||
*}
|
||||
* <p>
|
||||
* Alternatively, clients can obtain an {@linkplain Arena#ofAuto() automatic arena}, that is an arena
|
||||
* which features a <em>bounded lifetime</em> that is managed, automatically, by the garbage collector. As such, the regions
|
||||
* of memory backing memory segments allocated with the automatic arena are deallocated at some unspecified time
|
||||
* <em>after</em> the automatic arena (and all the segments allocated by it) become
|
||||
* <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>, as shown below:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* try (Arena arena = Arena.openConfined()) {
|
||||
* MemorySegment segment = MemorySegment.allocateNative(100, arena.scope());
|
||||
* ...
|
||||
* } // memory released here
|
||||
* MemorySegment segment = Arena.ofAuto().allocate(100, 1);
|
||||
* ...
|
||||
* segment = null; // the segment region becomes available for deallocation after this point
|
||||
*}
|
||||
*
|
||||
* 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:
|
||||
* Memory segments allocated with an automatic arena can also be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} from any thread.
|
||||
* <p>
|
||||
* Rather than leaving deallocation in the hands of the Java runtime, clients will often wish to exercise control over
|
||||
* the timing of deallocation for regions of memory that back memory segments. Two kinds of arenas support this,
|
||||
* namely {@linkplain #ofConfined() confined} and {@linkplain #ofShared() shared} arenas. They both feature
|
||||
* bounded lifetimes that are managed manually. For instance, the lifetime of a confined arena starts when the confined
|
||||
* arena is created, and ends when the confined arena is {@linkplain #close() closed}. As a result, the regions of memory
|
||||
* backing memory segments allocated with a confined arena are deallocated when the confined arena is closed.
|
||||
* When this happens, all the segments allocated with the confined arena are invalidated, and subsequent access
|
||||
* operations on these segments will fail {@link IllegalStateException}:
|
||||
*
|
||||
* {@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());
|
||||
* MemorySegment segment = null;
|
||||
* try (Arena arena = Arena.ofConfined()) {
|
||||
* segment = arena.allocate(100);
|
||||
* ...
|
||||
* } // memory released here
|
||||
* } // segment region deallocated here
|
||||
* segment.get(ValueLayout.JAVA_BYTE, 0); // throws IllegalStateException
|
||||
*}
|
||||
*
|
||||
* Memory segments allocated with a {@linkplain #ofConfined() confined arena} can only be accessed (and closed) by the
|
||||
* thread that created the arena. If access to a memory segment from multiple threads is required, clients can allocate
|
||||
* segments in a {@linkplain #ofShared() shared arena} instead.
|
||||
* <p>
|
||||
* The characteristics of the various arenas are summarized in the following table:
|
||||
*
|
||||
* <blockquote><table class="plain">
|
||||
* <caption style="display:none">Arenas characteristics</caption>
|
||||
* <thead>
|
||||
* <tr>
|
||||
* <th scope="col">Kind</th>
|
||||
* <th scope="col">Bounded lifetime</th>
|
||||
* <th scope="col">Explicitly closeable</th>
|
||||
* <th scope="col">Accessible from multiple threads</th>
|
||||
* </tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><th scope="row" style="font-weight:normal">Global</th>
|
||||
* <td style="text-align:center;">No</td>
|
||||
* <td style="text-align:center;">No</td>
|
||||
* <td style="text-align:center;">Yes</td></tr>
|
||||
* <tr><th scope="row" style="font-weight:normal">Automatic</th>
|
||||
* <td style="text-align:center;">Yes</td>
|
||||
* <td style="text-align:center;">No</td>
|
||||
* <td style="text-align:center;">Yes</td></tr>
|
||||
* <tr><th scope="row" style="font-weight:normal">Confined</th>
|
||||
* <td style="text-align:center;">Yes</td>
|
||||
* <td style="text-align:center;">Yes</td>
|
||||
* <td style="text-align:center;">No</td></tr>
|
||||
* <tr><th scope="row" style="font-weight:normal">Shared</th>
|
||||
* <td style="text-align:center;">Yes</td>
|
||||
* <td style="text-align:center;">Yes</td>
|
||||
* <td style="text-align:center;">Yes</td></tr>
|
||||
* </tbody>
|
||||
* </table></blockquote>
|
||||
*
|
||||
* <h2 id = "thread-confinement">Safety and thread-confinement</h2>
|
||||
*
|
||||
* Arenas provide strong temporal safety guarantees: a memory segment allocated by an arena cannot be accessed
|
||||
* <em>after</em> 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
|
||||
* is always created and closed by one thread, and the memory segments allocated by the arena are always
|
||||
* accessed by that same thread, then ensuring correctness is trivial.
|
||||
* <p>
|
||||
* Conversely, if an arena allocates segments that can be accessed by multiple threads, or if the arena can be closed
|
||||
|
@ -70,34 +135,120 @@ import jdk.internal.javac.PreviewFeature;
|
|||
* impact, arenas are divided into <em>thread-confined</em> arenas, and <em>shared</em> arenas.
|
||||
* <p>
|
||||
* 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}
|
||||
* <em>owner thread</em>, typically the thread which initiated the creation operation.
|
||||
* The segments created by a confined arena can only be {@linkplain MemorySegment#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}.
|
||||
* <p>
|
||||
* 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
|
||||
* can be {@linkplain MemorySegment#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.
|
||||
* Moreover, a shared arena can be closed by any thread.
|
||||
*
|
||||
* <h2 id = "custom-arenas">Custom arenas</h2>
|
||||
*
|
||||
* Clients can define custom arenas to implement more efficient allocation strategies, or to have better control over
|
||||
* when (and by whom) an arena can be closed. As an example, the following code defines a <em>slicing arena</em> that behaves
|
||||
* like a confined arena (i.e., single-threaded access), but internally uses a
|
||||
* {@linkplain SegmentAllocator#slicingAllocator(MemorySegment) slicing allocator} to respond to allocation requests.
|
||||
* When the slicing arena is closed, the underlying confined arena is also closed; this will invalidate all segments
|
||||
* allocated with the slicing arena (since the scope of the slicing arena is the same as that of the underlying
|
||||
* confined arena):
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* class SlicingArena implements Arena {
|
||||
* final Arena arena = Arena.ofConfined();
|
||||
* final SegmentAllocator slicingAllocator;
|
||||
*
|
||||
* SlicingArena(long size) {
|
||||
* slicingAllocator = SegmentAllocator.slicingAllocator(arena.allocate(size));
|
||||
* }
|
||||
*
|
||||
* public void allocate(long byteSize, long byteAlignment) {
|
||||
* return slicingAllocator.allocate(byteSize, byteAlignment);
|
||||
* }
|
||||
*
|
||||
* public MemorySegment.Scope scope() {
|
||||
* return arena.scope();
|
||||
* }
|
||||
*
|
||||
* public void close() {
|
||||
* return arena.close();
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* In other words, a slicing arena provides a vastly more efficient and scalable allocation strategy, while still retaining
|
||||
* the timely deallocation guarantee provided by the underlying confined arena:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* try (Arena slicingArena = new SlicingArena(1000)) {
|
||||
* for (int i = 0 ; i < 10 ; i++) {
|
||||
* MemorySegment s = slicingArena.allocateArray(JAVA_INT, 1, 2, 3, 4, 5);
|
||||
* ...
|
||||
* }
|
||||
* } // all memory allocated is released here
|
||||
* }
|
||||
*
|
||||
* @implSpec
|
||||
* Implementations of this interface are thread-safe.
|
||||
*
|
||||
* @see MemorySegment
|
||||
*
|
||||
* @since 20
|
||||
*/
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
||||
public interface Arena extends SegmentAllocator, AutoCloseable {
|
||||
|
||||
/**
|
||||
* Creates a new arena that is managed, automatically, by the garbage collector.
|
||||
* Segments obtained with the returned arena can be
|
||||
* {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by any thread.
|
||||
* Calling {@link #close()} on the returned arena will result in an {@link UnsupportedOperationException}.
|
||||
*
|
||||
* @return a new arena that is managed, automatically, by the garbage collector.
|
||||
*/
|
||||
static Arena ofAuto() {
|
||||
return MemorySessionImpl.createImplicit(CleanerFactory.cleaner()).asArena();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the global arena. Segments obtained with the global arena can be
|
||||
* {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by any thread.
|
||||
* Calling {@link #close()} on the returned arena will result in an {@link UnsupportedOperationException}.
|
||||
*
|
||||
* @return the global arena.
|
||||
*/
|
||||
static Arena global() {
|
||||
class Holder {
|
||||
static final Arena GLOBAL = MemorySessionImpl.GLOBAL.asArena();
|
||||
}
|
||||
return Holder.GLOBAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a new confined arena, owned by the current thread}
|
||||
*/
|
||||
static Arena ofConfined() {
|
||||
return MemorySessionImpl.createConfined(Thread.currentThread()).asArena();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a new shared arena}
|
||||
*/
|
||||
static Arena ofShared() {
|
||||
return MemorySessionImpl.createShared().asArena();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 returned segment is associated with this {@linkplain #scope() 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,
|
||||
* 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:
|
||||
*
|
||||
|
@ -110,57 +261,43 @@ public interface Arena extends SegmentAllocator, AutoCloseable {
|
|||
* @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)
|
||||
* @throws IllegalStateException if this arena has already been {@linkplain #close() closed}.
|
||||
* @throws WrongThreadException if this arena is confined, and this method is called from a thread {@code T}
|
||||
* other than the arena owner thread.
|
||||
*/
|
||||
@Override
|
||||
default MemorySegment allocate(long byteSize, long byteAlignment) {
|
||||
return MemorySegment.allocateNative(byteSize, byteAlignment, scope());
|
||||
return ((MemorySessionImpl)scope()).allocate(byteSize, byteAlignment);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the arena scope}
|
||||
*/
|
||||
SegmentScope scope();
|
||||
Scope scope();
|
||||
|
||||
/**
|
||||
* Closes this arena. If this method completes normally, the arena scope is no longer {@linkplain SegmentScope#isAlive() alive},
|
||||
* Closes this arena. If this method completes normally, the arena scope is no longer {@linkplain Scope#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.
|
||||
* segments obtained from this arena are also released.
|
||||
*
|
||||
* @apiNote This operation is not idempotent; that is, closing an already closed arena <em>always</em> 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()
|
||||
* @implSpec If this method completes normally, then {@code this.scope().isAlive() == false}.
|
||||
* Implementations are allowed to throw {@link UnsupportedOperationException} if an explicit close operation is
|
||||
* not supported.
|
||||
*
|
||||
* @see Scope#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}.
|
||||
* @throws IllegalStateException if a segment associated with this arena is being accessed concurrently, e.g.
|
||||
* by a {@linkplain Linker#downcallHandle(FunctionDescriptor, Linker.Option...) downcall method handle}.
|
||||
* @throws WrongThreadException if this arena is confined, and this method is called from a thread {@code T}
|
||||
* other than the arena owner thread.
|
||||
* @throws UnsupportedOperationException if this arena does not support explicit closure.
|
||||
*/
|
||||
@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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -37,7 +37,7 @@ import jdk.internal.javac.PreviewFeature;
|
|||
* 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}.
|
||||
* {@linkplain Linker#upcallStub(MethodHandle, FunctionDescriptor, Arena, Linker.Option...) upcall stubs}.
|
||||
*
|
||||
* @implSpec
|
||||
* Implementing classes are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -63,5 +63,14 @@ public sealed interface GroupLayout extends MemoryLayout permits StructLayout, U
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
GroupLayout withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
* @throws IllegalArgumentException if {@code bitAlignment} is less than {@code M}, where {@code M} is the maximum alignment
|
||||
* constraint in any of the member layouts associated with this group layout.
|
||||
*/
|
||||
@Override
|
||||
GroupLayout withBitAlignment(long bitAlignment);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -34,8 +34,10 @@ import jdk.internal.reflect.CallerSensitive;
|
|||
import jdk.internal.reflect.Reflection;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
@ -54,46 +56,340 @@ import java.util.stream.Stream;
|
|||
* <li>A linker allows Java code to link against foreign functions, via
|
||||
* {@linkplain #downcallHandle(MemorySegment, FunctionDescriptor, Option...) downcall method handles}; and</li>
|
||||
* <li>A linker allows foreign functions to call Java method handles,
|
||||
* via the generation of {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, SegmentScope) upcall stubs}.</li>
|
||||
* via the generation of {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, Arena, Option...) upcall stubs}.</li>
|
||||
* </ul>
|
||||
* In addition, a linker provides a way to look up foreign functions in libraries that conform to the ABI. Each linker
|
||||
* chooses a set of libraries that are commonly used on the OS and processor combination associated with the ABI.
|
||||
* For example, a linker for Linux/x64 might choose two libraries: {@code libc} and {@code libm}. The functions in these
|
||||
* libraries are exposed via a {@linkplain #defaultLookup() symbol lookup}.
|
||||
* <p>
|
||||
* The {@link #nativeLinker()} method provides a linker for the ABI associated with the OS and processor where the Java runtime
|
||||
* is currently executing. This linker also provides access, via its {@linkplain #defaultLookup() default lookup},
|
||||
* to the native libraries loaded with the Java runtime.
|
||||
*
|
||||
* <h2 id="downcall-method-handles">Downcall method handles</h2>
|
||||
* <h2 id="native-linker">Calling native functions</h2>
|
||||
*
|
||||
* {@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.
|
||||
* <p>
|
||||
* The Java {@linkplain java.lang.invoke.MethodType method type} associated with the returned method handle is
|
||||
* {@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:
|
||||
* <ul>
|
||||
* <li>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.</li>
|
||||
* <li>If the function descriptor's return layout is a group layout, the resulting downcall method handle accepts
|
||||
* an additional leading parameter of type {@link SegmentAllocator}, which is used by the linker runtime to allocate the
|
||||
* memory region associated with the struct returned by the downcall method handle.</li>
|
||||
* </ul>
|
||||
* The {@linkplain #nativeLinker() native linker} can be used to link against functions
|
||||
* defined in C libraries (native functions). Suppose we wish to downcall from Java to the {@code strlen} function
|
||||
* defined in the standard C library:
|
||||
* {@snippet lang = c:
|
||||
* size_t strlen(const char *s);
|
||||
* }
|
||||
* A downcall method handle that exposes {@code strlen} is obtained, using the native linker, as follows:
|
||||
*
|
||||
* <h2 id="upcall-stubs">Upcall stubs</h2>
|
||||
* {@snippet lang = java:
|
||||
* Linker linker = Linker.nativeLinker();
|
||||
* MethodHandle strlen = linker.downcallHandle(
|
||||
* linker.defaultLookup().find("strlen").get(),
|
||||
* FunctionDescriptor.of(JAVA_LONG, ADDRESS)
|
||||
* );
|
||||
* }
|
||||
*
|
||||
* {@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.
|
||||
* Note how the native linker also provides access, via its {@linkplain #defaultLookup() default lookup},
|
||||
* to the native functions defined by the C libraries loaded with the Java runtime. Above, the default lookup
|
||||
* is used to search the address of the {@code strlen} native function. That address is then passed, along with
|
||||
* a <em>platform-dependent description</em> of the signature of the function expressed as a
|
||||
* {@link FunctionDescriptor} (more on that below) to the native linker's
|
||||
* {@link #downcallHandle(MemorySegment, FunctionDescriptor, Option...)} method.
|
||||
* The obtained downcall method handle is then invoked as follows:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* try (Arena arena = Arena.openConfined()) {
|
||||
* MemorySegment str = arena.allocateUtf8String("Hello");
|
||||
* long len = strlen.invoke(str); // 5
|
||||
* }
|
||||
* }
|
||||
* <h3 id="describing-c-sigs">Describing C signatures</h3>
|
||||
*
|
||||
* When interacting with the native linker, clients must provide a platform-dependent description of the signature
|
||||
* of the C function they wish to link against. This description, a {@link FunctionDescriptor function descriptor},
|
||||
* defines the layouts associated with the parameter types and return type (if any) of the C function.
|
||||
* <p>
|
||||
* 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.
|
||||
* Scalar C types such as {@code bool}, {@code int} are modelled as {@linkplain ValueLayout value layouts}
|
||||
* of a suitable carrier. The mapping between a scalar type and its corresponding layout is dependent on the ABI
|
||||
* implemented by the native linker. For instance, the C type {@code long} maps to the layout constant
|
||||
* {@link ValueLayout#JAVA_LONG} on Linux/x64, but maps to the layout constant {@link ValueLayout#JAVA_INT} on
|
||||
* Windows/x64. Similarly, the C type {@code size_t} maps to the layout constant {@link ValueLayout#JAVA_LONG}
|
||||
* on 64-bit platforms, but maps to the layout constant {@link ValueLayout#JAVA_INT} on 32-bit platforms.
|
||||
* <p>
|
||||
* Upcall stubs are modelled by instances of type {@link MemorySegment}; upcall stubs can be passed by reference to other
|
||||
* downcall method handles and, they are released via their associated {@linkplain SegmentScope scope}.
|
||||
* Composite types are modelled as {@linkplain GroupLayout group layouts}. More specifically, a C {@code struct} type
|
||||
* maps to a {@linkplain StructLayout struct layout}, whereas a C {@code union} type maps to a {@link UnionLayout union
|
||||
* layout}. When defining a struct or union layout, clients must pay attention to the size and alignment constraint
|
||||
* of the corresponding composite type definition in C. For instance, padding between two struct fields
|
||||
* must be modelled explicitly, by adding an adequately sized {@linkplain PaddingLayout padding layout} member
|
||||
* to the resulting struct layout.
|
||||
* <p>
|
||||
* Finally, pointer types such as {@code int**} and {@code int(*)(size_t*, size_t*)} are modelled as
|
||||
* {@linkplain AddressLayout address layouts}. When the spatial bounds of the pointer type are known statically,
|
||||
* the address layout can be associated with a {@linkplain AddressLayout#targetLayout() target layout}. For instance,
|
||||
* a pointer that is known to point to a C {@code int[2]} array can be modelled as an address layout whose
|
||||
* target layout is a sequence layout whose element count is 2, and whose element type is {@link ValueLayout#JAVA_INT}.
|
||||
* <p>
|
||||
* The following table shows some examples of how C types are modelled in Linux/x64:
|
||||
*
|
||||
* <blockquote><table class="plain">
|
||||
* <caption style="display:none">Mapping C types</caption>
|
||||
* <thead>
|
||||
* <tr>
|
||||
* <th scope="col">C type</th>
|
||||
* <th scope="col">Layout</th>
|
||||
* <th scope="col">Java type</th>
|
||||
* </tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><th scope="row" style="font-weight:normal">{@code bool}</th>
|
||||
* <td style="text-align:center;">{@link ValueLayout#JAVA_BOOLEAN}</td>
|
||||
* <td style="text-align:center;">{@code boolean}</td>
|
||||
* <tr><th scope="row" style="font-weight:normal">{@code char}</th>
|
||||
* <td style="text-align:center;">{@link ValueLayout#JAVA_BYTE}</td>
|
||||
* <td style="text-align:center;">{@code byte}</td>
|
||||
* <tr><th scope="row" style="font-weight:normal">{@code short}</th>
|
||||
* <td style="text-align:center;">{@link ValueLayout#JAVA_SHORT}</td>
|
||||
* <td style="text-align:center;">{@code short}</td>
|
||||
* <tr><th scope="row" style="font-weight:normal">{@code int}</th>
|
||||
* <td style="text-align:center;">{@link ValueLayout#JAVA_INT}</td>
|
||||
* <td style="text-align:center;">{@code int}</td>
|
||||
* <tr><th scope="row" style="font-weight:normal">{@code long}</th>
|
||||
* <td style="text-align:center;">{@link ValueLayout#JAVA_LONG}</td>
|
||||
* <td style="text-align:center;">{@code long}</td>
|
||||
* <tr><th scope="row" style="font-weight:normal">{@code long long}</th>
|
||||
* <td style="text-align:center;">{@link ValueLayout#JAVA_LONG}</td>
|
||||
* <td style="text-align:center;">{@code long}</td>
|
||||
* <tr><th scope="row" style="font-weight:normal">{@code float}</th>
|
||||
* <td style="text-align:center;">{@link ValueLayout#JAVA_FLOAT}</td>
|
||||
* <td style="text-align:center;">{@code float}</td>
|
||||
* <tr><th scope="row" style="font-weight:normal">{@code double}</th>
|
||||
* <td style="text-align:center;">{@link ValueLayout#JAVA_DOUBLE}</td>
|
||||
* <td style="text-align:center;">{@code double}</td>
|
||||
<tr><th scope="row" style="font-weight:normal">{@code size_t}</th>
|
||||
* <td style="text-align:center;">{@link ValueLayout#JAVA_LONG}</td>
|
||||
* <td style="text-align:center;">{@code long}</td>
|
||||
* <tr><th scope="row" style="font-weight:normal">{@code char*}, {@code int**}, {@code struct Point*}</th>
|
||||
* <td style="text-align:center;">{@link ValueLayout#ADDRESS}</td>
|
||||
* <td style="text-align:center;">{@link MemorySegment}</td>
|
||||
* <tr><th scope="row" style="font-weight:normal">{@code int (*ptr)[10]}</th>
|
||||
* <td style="text-align:left;">
|
||||
* <pre>
|
||||
* ValueLayout.ADDRESS.withTargetLayout(
|
||||
* MemoryLayout.sequenceLayout(10,
|
||||
* ValueLayout.JAVA_INT)
|
||||
* );
|
||||
* </pre>
|
||||
* <td style="text-align:center;">{@link MemorySegment}</td>
|
||||
* <tr><th scope="row" style="font-weight:normal"><code>struct Point { int x; long y; };</code></th>
|
||||
* <td style="text-align:left;">
|
||||
* <pre>
|
||||
* MemoryLayout.structLayout(
|
||||
* ValueLayout.JAVA_INT.withName("x"),
|
||||
* MemoryLayout.paddingLayout(32),
|
||||
* ValueLayout.JAVA_LONG.withName("y")
|
||||
* );
|
||||
* </pre>
|
||||
* </td>
|
||||
* <td style="text-align:center;">{@link MemorySegment}</td>
|
||||
* <tr><th scope="row" style="font-weight:normal"><code>union Choice { float a; int b; }</code></th>
|
||||
* <td style="text-align:left;">
|
||||
* <pre>
|
||||
* MemoryLayout.unionLayout(
|
||||
* ValueLayout.JAVA_FLOAT.withName("a"),
|
||||
* ValueLayout.JAVA_INT.withName("b")
|
||||
* );
|
||||
* </pre>
|
||||
* </td>
|
||||
* <td style="text-align:center;">{@link MemorySegment}</td>
|
||||
* </tbody>
|
||||
* </table></blockquote>
|
||||
*
|
||||
* <h3 id="function-pointers">Function pointers</h3>
|
||||
*
|
||||
* Sometimes, it is useful to pass Java code as a function pointer to some native function; this is achieved by using
|
||||
* an {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, Arena, Option...) upcall stub}. To demonstrate this,
|
||||
* let's consider the following function from the C standard library:
|
||||
*
|
||||
* {@snippet lang = c:
|
||||
* void qsort(void *base, size_t nmemb, size_t size,
|
||||
* int (*compar)(const void *, const void *));
|
||||
* }
|
||||
*
|
||||
* The {@code qsort} function can be used to sort the contents of an array, using a custom comparator function which is
|
||||
* passed as a function pointer (the {@code compar} parameter). To be able to call the {@code qsort} function from Java,
|
||||
* we must first create a downcall method handle for it, as follows:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* Linker linker = Linker.nativeLinker();
|
||||
* MethodHandle qsort = linker.downcallHandle(
|
||||
* linker.defaultLookup().find("qsort").get(),
|
||||
* FunctionDescriptor.ofVoid(ADDRESS, JAVA_LONG, JAVA_LONG, ADDRESS)
|
||||
* );
|
||||
* }
|
||||
*
|
||||
* As before, we use {@link ValueLayout#JAVA_LONG} to map the C type {@code size_t} type, and {@link ValueLayout#ADDRESS}
|
||||
* for both the first pointer parameter (the array pointer) and the last parameter (the function pointer).
|
||||
* <p>
|
||||
* To invoke the {@code qsort} downcall handle obtained above, we need a function pointer to be passed as the last
|
||||
* parameter. That is, we need to create a function pointer out of an existing method handle. First, let's write a
|
||||
* Java method that can compare two int elements passed as pointers (i.e. as {@linkplain MemorySegment memory segments}):
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* class Qsort {
|
||||
* static int qsortCompare(MemorySegment elem1, MemorySegmet elem2) {
|
||||
* return Integer.compare(elem1.get(JAVA_INT, 0), elem2.get(JAVA_INT, 0));
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* Now let's create a method handle for the comparator method defined above:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* FunctionDescriptor comparDesc = FunctionDescriptor.of(JAVA_INT,
|
||||
* ADDRESS.withTargetLayout(JAVA_INT),
|
||||
* ADDRESS.withTargetLayout(JAVA_INT));
|
||||
* MethodHandle comparHandle = MethodHandles.lookup()
|
||||
* .findStatic(Qsort.class, "qsortCompare",
|
||||
* comparDesc.toMethodType());
|
||||
* }
|
||||
*
|
||||
* First, we create a function descriptor for the function pointer type. Since we know that the parameters passed to
|
||||
* the comparator method will be pointers to elements of a C {@code int[]} array, we can specify {@link ValueLayout#JAVA_INT}
|
||||
* as the target layout for the address layouts of both parameters. This will allow the comparator method to access
|
||||
* the contents of the array elements to be compared. We then {@linkplain FunctionDescriptor#toMethodType() turn}
|
||||
* that function descriptor into a suitable {@linkplain java.lang.invoke.MethodType method type} which we then use to look up
|
||||
* the comparator method handle. We can now create an upcall stub which points to that method, and pass it, as a function
|
||||
* pointer, to the {@code qsort} downcall handle, as follows:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* try (Arena arena = Arena.ofConfined()) {
|
||||
* MemorySegment comparFunc = linker.upcallStub(comparHandle, comparDesc, arena);
|
||||
* MemorySegment array = session.allocateArray(0, 9, 3, 4, 6, 5, 1, 8, 2, 7);
|
||||
* qsort.invokeExact(array, 10L, 4L, comparFunc);
|
||||
* int[] sorted = array.toArray(JAVA_INT); // [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* This code creates an off-heap array, copies the contents of a Java array into it, and then passes the array to the
|
||||
* {@code qsort} method handle along with the comparator function we obtained from the native linker. After the invocation, the contents
|
||||
* of the off-heap array will be sorted according to our comparator function, written in Java. We then extract a
|
||||
* new Java array from the segment, which contains the sorted elements.
|
||||
*
|
||||
* <h3 id="by-ref">Functions returning pointers</h3>
|
||||
*
|
||||
* When interacting with native functions, it is common for those functions to allocate a region of memory and return
|
||||
* a pointer to that region. Let's consider the following function from the C standard library:
|
||||
*
|
||||
* {@snippet lang = c:
|
||||
* void *malloc(size_t size);
|
||||
* }
|
||||
*
|
||||
* The {@code malloc} function allocates a region of memory of given size,
|
||||
* and returns a pointer to that region of memory, which is later deallocated using another function from
|
||||
* the C standard library:
|
||||
*
|
||||
* {@snippet lang = c:
|
||||
* void free(void *ptr);
|
||||
* }
|
||||
*
|
||||
* The {@code free} function takes a pointer to a region of memory and deallocates that region. In this section we
|
||||
* will show how to interact with these native functions, with the aim of providing a <em>safe</em> allocation
|
||||
* API (the approach outlined below can of course be generalized to allocation functions other than {@code malloc}
|
||||
* and {@code free}).
|
||||
* <p>
|
||||
* First, we need to create the downcall method handles for {@code malloc} and {@code free}, as follows:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* Linker linker = Linker.nativeLinker();
|
||||
*
|
||||
* MethodHandle malloc = linker.downcallHandle(
|
||||
* linker.defaultLookup().find("malloc").get(),
|
||||
* FunctionDescriptor.of(ADDRESS, JAVA_LONG)
|
||||
* );
|
||||
*
|
||||
* MethodHandle free = linker.downcallHandle(
|
||||
* linker.defaultLookup().find("free").get(),
|
||||
* FunctionDescriptor.ofVoid(ADDRESS)
|
||||
* );
|
||||
* }
|
||||
*
|
||||
* When interacting with a native functions returning a pointer (such as {@code malloc}), the Java runtime has no insight
|
||||
* into the size or the lifetime of the returned pointer. Consider the following code:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* MemorySegment segment = (MemorySegment)malloc.invokeExact(100);
|
||||
* }
|
||||
*
|
||||
* The size of the segment returned by the {@code malloc} downcall method handle is
|
||||
* <a href="MemorySegment.html#wrapping-addresses">zero</a>. Moreover, the scope of the
|
||||
* returned segment is a fresh scope that is always alive. To provide safe access to the segment, we must,
|
||||
* unsafely, resize the segment to the desired size (100, in this case). It might also be desirable to
|
||||
* attach the segment to some existing {@linkplain Arena arena}, so that the lifetime of the region of memory
|
||||
* backing the segment can be managed automatically, as for any other native segment created directly from Java code.
|
||||
* Both these operations are accomplished using the restricted {@link MemorySegment#reinterpret(long, Arena, Consumer)}
|
||||
* method, as follows:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* MemorySegment allocateMemory(long byteSize, Arena arena) {
|
||||
* MemorySegment segment = (MemorySegment)malloc.invokeExact(byteSize); // size = 0, scope = always alive
|
||||
* return segment.reinterpret(byteSize, arena, s -> free.invokeExact(s)); // size = byteSize, scope = arena.scope()
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* The {@code allocateMemory} method defined above accepts two parameters: a size and an arena. The method calls the
|
||||
* {@code malloc} downcall method handle, and unsafely reinterprets the returned segment, by giving it a new size
|
||||
* (the size passed to the {@code allocateMemory} method) and a new scope (the scope of the provided arena).
|
||||
* The method also specifies a <em>cleanup action</em> to be executed when the provided arena is closed. Unsurprisingly,
|
||||
* the cleanup action passes the segment to the {@code free} downcall method handle, to deallocate the underlying
|
||||
* region of memory. We can use the {@code allocateMemory} method as follows:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* try (Arena arena = Arena.ofConfined()) {
|
||||
* MemorySegment segment = allocateMemory(100, arena);
|
||||
* } // 'free' called here
|
||||
* }
|
||||
*
|
||||
* Note how the segment obtained from {@code allocateMemory} acts as any other segment managed by the confined arena. More
|
||||
* specifically, the obtained segment has the desired size, can only be accessed by a single thread (the thread which created
|
||||
* the confined arena), and its lifetime is tied to the surrounding <em>try-with-resources</em> block.
|
||||
*
|
||||
* <h3 id="variadic-funcs">Variadic functions</h3>
|
||||
*
|
||||
* 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 by the native linker. However, it is still possible
|
||||
* to link a variadic function by using a <em>specialized</em> function descriptor, together with a
|
||||
* {@linkplain Linker.Option#firstVariadicArg(int) a linker option} which indicates the position of the first variadic argument
|
||||
* in that specialized descriptor.
|
||||
* <p>
|
||||
* A well-known variadic function is the {@code printf} function, defined in the C standard library:
|
||||
*
|
||||
* {@snippet lang = c:
|
||||
* int printf(const char *format, ...);
|
||||
* }
|
||||
*
|
||||
* This function takes a format string, and a number of additional arguments (the number of such arguments is
|
||||
* dictated by the format string). Consider the following variadic call:
|
||||
*
|
||||
* {@snippet lang = c:
|
||||
* printf("%d plus %d equals %d", 2, 2, 4);
|
||||
* }
|
||||
*
|
||||
* To perform an equivalent call using a downcall method handle we must create a function descriptor which
|
||||
* describes the specialized signature of the C function we want to call. This descriptor must include layouts for any
|
||||
* additional variadic argument we intend to provide. In this case, the specialized signature of the C
|
||||
* function is {@code (char*, int, int, int)} as the format string accepts three integer parameters. Then, we need to use
|
||||
* a linker option to specify the position of the first variadic layout in the provided function descriptor (starting from 0).
|
||||
* In this case, since the first parameter is the format string (a non-variadic argument), the first variadic index
|
||||
* needs to be set to 1, as follows:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* Linker linker = Linker.nativeLinker();
|
||||
* MethodHandle printf = linker.downcallHandle(
|
||||
* linker.defaultLookup().lookup("printf").get(),
|
||||
* FunctionDescriptor.of(JAVA_INT, ADDRESS, JAVA_INT, JAVA_INT, JAVA_INT),
|
||||
* Linker.Option.firstVariadicArg(1) // first int is variadic
|
||||
* );
|
||||
* }
|
||||
*
|
||||
* We can then call the specialized downcall handle as usual:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* try (Arena arena = Arena.ofConfined()) {
|
||||
* int res = (int)printf.invokeExact(arena.allocateUtf8String("%d plus %d equals %d"), 2, 2, 4); //prints "2 plus 2 equals 4"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* <h2 id="safety">Safety considerations</h2>
|
||||
*
|
||||
|
@ -101,21 +397,7 @@ import java.util.stream.Stream;
|
|||
* contain enough signature information (e.g. arity and types of foreign function parameters). As a consequence,
|
||||
* the linker runtime cannot validate linkage requests. When a client interacts with a downcall method handle obtained
|
||||
* through an invalid linkage request (e.g. by specifying a function descriptor featuring too many argument layouts),
|
||||
* the result of such interaction is unspecified and can lead to JVM crashes. On downcall handle invocation,
|
||||
* the linker runtime guarantees the following for any argument {@code A} of type {@link MemorySegment} whose corresponding
|
||||
* layout is {@link ValueLayout#ADDRESS}:
|
||||
* <ul>
|
||||
* <li>The scope of {@code A} is {@linkplain SegmentScope#isAlive() alive}. Otherwise, the invocation throws
|
||||
* {@link IllegalStateException};</li>
|
||||
* <li>The invocation occurs in a thread {@code T} such that {@code A.scope().isAccessibleBy(T) == true}.
|
||||
* Otherwise, the invocation throws {@link WrongThreadException}; and</li>
|
||||
* <li>The scope of {@code A} is {@linkplain SegmentScope#whileAlive(Runnable) kept alive} during the invocation.</li>
|
||||
*</ul>
|
||||
* 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}.
|
||||
* the result of such interaction is unspecified and can lead to JVM crashes.
|
||||
* <p>
|
||||
* 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,
|
||||
|
@ -124,12 +406,6 @@ import java.util.stream.Stream;
|
|||
* 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.
|
||||
* <p>
|
||||
* 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 <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
|
||||
|
@ -143,31 +419,6 @@ public sealed interface Linker permits AbstractLinker {
|
|||
* Returns a linker for the ABI associated with the underlying native platform. The underlying native platform
|
||||
* is the combination of OS and processor where the Java runtime is currently executing.
|
||||
* <p>
|
||||
* When interacting with the returned linker, clients must describe the signature of a foreign function using a
|
||||
* {@link FunctionDescriptor function descriptor} whose argument and return layouts are specified as follows:
|
||||
* <ul>
|
||||
* <li>Scalar types are modelled by a {@linkplain ValueLayout value layout} instance of a suitable carrier. Example
|
||||
* of scalar types in C are {@code int}, {@code long}, {@code size_t}, etc. The mapping between a scalar type
|
||||
* and its corresponding layout is dependent on the ABI of the returned linker;
|
||||
* <li>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 constraint of a composite type definition in C (e.g. using {@code struct} or {@code union}); and</li>
|
||||
* <li>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*)};</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Any layout not listed above is <em>unsupported</em>; function descriptors containing unsupported layouts
|
||||
* will cause an {@link IllegalArgumentException} to be thrown, when used to create a
|
||||
* {@link #downcallHandle(MemorySegment, FunctionDescriptor, Option...) downcall method handle} or an
|
||||
* {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, SegmentScope) upcall stub}.
|
||||
* <p>
|
||||
* 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 {@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}).
|
||||
* <p>
|
||||
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
|
||||
* 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
|
||||
|
@ -178,7 +429,7 @@ public sealed interface Linker permits AbstractLinker {
|
|||
* linker are the native libraries loaded in the process where the Java runtime is currently executing. For example,
|
||||
* on Linux, these libraries typically include {@code libc}, {@code libm} and {@code libdl}.
|
||||
*
|
||||
* @return a linker for the ABI associated with the OS and processor where the Java runtime is currently executing.
|
||||
* @return a linker for the ABI associated with the underlying native platform.
|
||||
* @throws UnsupportedOperationException if the underlying native platform is not supported.
|
||||
* @throws IllegalCallerException If the caller is in a module that does not have native access enabled.
|
||||
*/
|
||||
|
@ -189,11 +440,7 @@ public sealed interface Linker permits AbstractLinker {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a method handle which can be used to call a foreign function with the given signature and address.
|
||||
* <p>
|
||||
* 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 to allocate
|
||||
* structs returned by-value.
|
||||
* Creates a method handle which is used to call a foreign function with the given signature and address.
|
||||
* <p>
|
||||
* Calling this method is equivalent to the following code:
|
||||
* {@snippet lang=java :
|
||||
|
@ -214,17 +461,35 @@ public sealed interface Linker permits AbstractLinker {
|
|||
}
|
||||
|
||||
/**
|
||||
* 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 MemorySegment}, which is used to specify the address of the target function
|
||||
* to be called.
|
||||
* Creates a method handle which is used to call a foreign function with the given signature.
|
||||
* <p>
|
||||
* 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 to allocate structs returned by-value.
|
||||
* The Java {@linkplain java.lang.invoke.MethodType method type} associated with the returned method handle is
|
||||
* {@linkplain FunctionDescriptor#toMethodType() derived} from the argument and return layouts in the function descriptor,
|
||||
* but features an additional leading parameter of type {@link MemorySegment}, from which the address of the target
|
||||
* foreign function is derived. Moreover, if the function descriptor's return layout is a group layout, the resulting
|
||||
* downcall method handle accepts an additional leading parameter of type {@link SegmentAllocator}, which is used by
|
||||
* the linker runtime to allocate the memory region associated with the struct returned by the downcall method handle.
|
||||
* <p>
|
||||
* 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}.
|
||||
* Upon invoking a downcall method handle, the linker runtime will guarantee the following for any argument
|
||||
* {@code A} of type {@link MemorySegment} whose corresponding layout is an {@linkplain AddressLayout address layout}:
|
||||
* <ul>
|
||||
* <li>{@code A.scope().isAlive() == true}. Otherwise, the invocation throws {@link IllegalStateException};</li>
|
||||
* <li>The invocation occurs in a thread {@code T} such that {@code A.isAccessibleBy(T) == true}.
|
||||
* Otherwise, the invocation throws {@link WrongThreadException}; and</li>
|
||||
* <li>{@code A} is kept alive during the invocation. For instance, if {@code A} has been obtained using a
|
||||
* {@linkplain Arena#ofShared()} shared arena}, any attempt to {@linkplain Arena#close() close}
|
||||
* the shared arena while the downcall method handle is executing will result in an {@link IllegalStateException}.</li>
|
||||
*</ul>
|
||||
* <p>
|
||||
* Moreover, if the provided function descriptor's return layout is an {@linkplain AddressLayout address layout},
|
||||
* invoking the returned method handle will return a native segment associated with
|
||||
* a fresh scope that is always alive. Under normal conditions, the size of the returned segment is {@code 0}.
|
||||
* However, if the function descriptor's return layout has a {@linkplain AddressLayout#targetLayout()} {@code T},
|
||||
* then the size of the returned segment is set to {@code T.byteSize()}.
|
||||
* <p>
|
||||
* The returned method handle will throw an {@link IllegalArgumentException} if the {@link MemorySegment}
|
||||
* representing the target address of the foreign function is the {@link MemorySegment#NULL} address.
|
||||
* The returned method handle will additionally throw {@link NullPointerException} if any argument passed to it is {@code null}.
|
||||
*
|
||||
* @param function the function descriptor of the target function.
|
||||
* @param options any linker options.
|
||||
|
@ -237,12 +502,19 @@ public sealed interface Linker permits AbstractLinker {
|
|||
|
||||
/**
|
||||
* 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
|
||||
* arena. Calling such a function pointer from foreign code will result in the execution of the provided
|
||||
* method handle.
|
||||
* <p>
|
||||
* 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 provided arena. As such, the lifetime of the returned upcall stub segment is controlled by the
|
||||
* provided arena. For instance, if the provided arena is a confined arena, the returned
|
||||
* upcall stub segment will be deallocated when the provided confined arena is {@linkplain Arena#close() closed}.
|
||||
* <p>
|
||||
* An upcall stub argument whose corresponding layout is an {@linkplain AddressLayout address layout}
|
||||
* is a native segment associated with a fresh scope that is always alive.
|
||||
* Under normal conditions, the size of this segment argument is {@code 0}.
|
||||
* However, if the address layout has a {@linkplain AddressLayout#targetLayout()} {@code T}, then the size of the
|
||||
* segment argument is set to {@code T.byteSize()}.
|
||||
* <p>
|
||||
* 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
|
||||
|
@ -252,16 +524,17 @@ public sealed interface Linker permits AbstractLinker {
|
|||
*
|
||||
* @param target the target method handle.
|
||||
* @param function the upcall stub function descriptor.
|
||||
* @param scope the scope associated with the returned upcall stub segment.
|
||||
* @param arena the arena associated with the returned upcall stub segment.
|
||||
* @param options any linker options.
|
||||
* @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 <a href="Linker.html#upcall-stubs"><em>inferred type</em></a>.
|
||||
* @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 IllegalStateException if {@code arena.scope().isAlive() == false}
|
||||
* @throws WrongThreadException if {@code arena} is a confined arena, and this method is called from a
|
||||
* thread {@code T}, other than the arena's owner thread.
|
||||
*/
|
||||
MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, SegmentScope scope);
|
||||
MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, Arena arena, Linker.Option... options);
|
||||
|
||||
/**
|
||||
* Returns a symbol lookup for symbols in a set of commonly used libraries.
|
||||
|
@ -285,8 +558,7 @@ public sealed interface Linker permits AbstractLinker {
|
|||
*/
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
||||
sealed interface Option
|
||||
permits LinkerOptions.LinkerOptionImpl,
|
||||
Option.CaptureCallState {
|
||||
permits LinkerOptions.LinkerOptionImpl {
|
||||
|
||||
/**
|
||||
* {@return a linker option used to denote the index of the first variadic argument layout in a
|
||||
|
@ -302,70 +574,91 @@ public sealed interface Linker permits AbstractLinker {
|
|||
* calling a foreign function associated with a downcall method handle,
|
||||
* before it can be overwritten by the Java runtime, or read through conventional means}
|
||||
* <p>
|
||||
* A downcall method handle linked with this option will feature an additional {@link MemorySegment}
|
||||
* parameter directly following the target address, and optional {@link SegmentAllocator} parameters.
|
||||
* This memory segment must be a native segment into which the captured state is written.
|
||||
*
|
||||
* @param capturedState the names of the values to save.
|
||||
* @see CaptureCallState#supported()
|
||||
*/
|
||||
static CaptureCallState captureCallState(String... capturedState) {
|
||||
Set<CapturableState> set = Stream.of(capturedState)
|
||||
.map(CapturableState::forName)
|
||||
.collect(Collectors.toSet());
|
||||
return new LinkerOptions.CaptureCallStateImpl(set);
|
||||
}
|
||||
|
||||
/**
|
||||
* A linker option for saving portions of the execution state immediately
|
||||
* after calling a foreign function associated with a downcall method handle,
|
||||
* before it can be overwritten by the runtime, or read through conventional means.
|
||||
* <p>
|
||||
* Execution state is captured by a downcall method handle on invocation, by writing it
|
||||
* to a native segment provided by the user to the downcall method handle.
|
||||
* For this purpose, a downcall method handle linked with the {@link #captureCallState(String[])}
|
||||
* For this purpose, a downcall method handle linked with this
|
||||
* option will feature an additional {@link MemorySegment} parameter directly
|
||||
* following the target address, and optional {@link SegmentAllocator} parameters.
|
||||
* This parameter represents the native segment into which the captured state is written.
|
||||
* This parameter, called the 'capture state segment', represents the native segment into which
|
||||
* the captured state is written.
|
||||
* <p>
|
||||
* The native segment should have the layout {@linkplain CaptureCallState#layout associated}
|
||||
* with the particular {@code CaptureCallState} instance used to link the downcall handle.
|
||||
* The capture state segment should have the layout returned by {@linkplain #captureStateLayout}.
|
||||
* This layout is a struct layout which has a named field for each captured value.
|
||||
* <p>
|
||||
* Captured state can be retrieved from this native segment by constructing var handles
|
||||
* from the {@linkplain #layout layout} associated with the {@code CaptureCallState} instance.
|
||||
* Captured state can be retrieved from the capture state segment by constructing var handles
|
||||
* from the {@linkplain #captureStateLayout capture state layout}.
|
||||
* <p>
|
||||
* The following example demonstrates the use of this linker option:
|
||||
* {@snippet lang = "java":
|
||||
* MemorySegment targetAddress = ...
|
||||
* CaptureCallState ccs = Linker.Option.captureCallState("errno");
|
||||
* Linker.Option ccs = Linker.Option.captureCallState("errno");
|
||||
* MethodHandle handle = Linker.nativeLinker().downcallHandle(targetAddress, FunctionDescriptor.ofVoid(), ccs);
|
||||
*
|
||||
* VarHandle errnoHandle = ccs.layout().varHandle(PathElement.groupElement("errno"));
|
||||
* try (Arena arena = Arena.openConfined()) {
|
||||
* MemorySegment capturedState = arena.allocate(ccs.layout());
|
||||
* StructLayout capturedStateLayout = Linker.Option.capturedStateLayout();
|
||||
* VarHandle errnoHandle = capturedStateLayout.varHandle(PathElement.groupElement("errno"));
|
||||
* try (Arena arena = Arena.ofConfined()) {
|
||||
* MemorySegment capturedState = arena.allocate(capturedStateLayout);
|
||||
* handle.invoke(capturedState);
|
||||
* int errno = errnoHandle.get(capturedState);
|
||||
* // use errno
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @param capturedState the names of the values to save.
|
||||
* @throws IllegalArgumentException if at least one of the provided {@code capturedState} names
|
||||
* is unsupported on the current platform.
|
||||
* @see #captureStateLayout()
|
||||
*/
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
||||
sealed interface CaptureCallState extends Option
|
||||
permits LinkerOptions.CaptureCallStateImpl {
|
||||
/**
|
||||
* {@return A struct layout that represents the layout of the native segment passed
|
||||
* to a downcall handle linked with this {@code CapturedCallState} instance}
|
||||
*/
|
||||
StructLayout layout();
|
||||
static Option captureCallState(String... capturedState) {
|
||||
Set<CapturableState> set = Stream.of(Objects.requireNonNull(capturedState))
|
||||
.map(Objects::requireNonNull)
|
||||
.map(CapturableState::forName)
|
||||
.collect(Collectors.toSet());
|
||||
return new LinkerOptions.CaptureCallState(set);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the names of the state that can be capture by this implementation}
|
||||
*/
|
||||
static Set<String> supported() {
|
||||
return Arrays.stream(CapturableState.values())
|
||||
.map(CapturableState::stateName)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
/**
|
||||
* {@return A struct layout that represents the layout of the capture state segment that is passed
|
||||
* to a downcall handle linked with {@link #captureCallState(String...)}}.
|
||||
* <p>
|
||||
* The capture state layout is <em>platform dependent</em> but is guaranteed to be
|
||||
* a {@linkplain StructLayout struct layout} containing only {@linkplain ValueLayout value layouts}
|
||||
* and possibly {@linkplain PaddingLayout padding layouts}.
|
||||
* As an example, on Windows, the returned layout might contain three value layouts named:
|
||||
* <ul>
|
||||
* <li>GetLastError</li>
|
||||
* <li>WSAGetLastError</li>
|
||||
* <li>errno</li>
|
||||
* </ul>
|
||||
* The following snipet shows how to obtain the names of the supported captured value layouts:
|
||||
* {@snippet lang = java:
|
||||
* String capturedNames = Linker.Option.captureStateLayout().memberLayouts().stream()
|
||||
* .map(MemoryLayout::name)
|
||||
* .flatMap(Optional::stream)
|
||||
* .map(Objects::toString)
|
||||
* .collect(Collectors.joining(", "));
|
||||
* }
|
||||
*
|
||||
* @see #captureCallState(String...)
|
||||
*/
|
||||
static StructLayout captureStateLayout() {
|
||||
return CapturableState.LAYOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return A linker option used to mark a foreign function as <em>trivial</em>}
|
||||
* <p>
|
||||
* A trivial function is a function that has an extremely short running time
|
||||
* in all cases (similar to calling an empty function), and does not call back into Java (e.g. using an upcall stub).
|
||||
* <p>
|
||||
* Using this linker option is a hint which some implementations may use to apply
|
||||
* optimizations that are only valid for trivial functions.
|
||||
* <p>
|
||||
* Using this linker option when linking non trivial functions is likely to have adverse effects,
|
||||
* such as loss of performance, or JVM crashes.
|
||||
*/
|
||||
static Option isTrivial() {
|
||||
return LinkerOptions.IsTrivial.INSTANCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -28,7 +28,6 @@ package java.lang.foreign;
|
|||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
@ -36,6 +35,7 @@ import java.util.Set;
|
|||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.foreign.LayoutPath;
|
||||
import jdk.internal.foreign.LayoutPath.PathElementImpl.PathKind;
|
||||
import jdk.internal.foreign.Utils;
|
||||
|
@ -44,7 +44,6 @@ 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;
|
||||
|
||||
/**
|
||||
|
@ -200,6 +199,17 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
|||
*/
|
||||
MemoryLayout withName(String name);
|
||||
|
||||
/**
|
||||
* Returns a memory layout of the same type with the same size and alignment constraint as this layout,
|
||||
* but without a name.
|
||||
* <p>
|
||||
* This can be useful to compare two layouts that have different names, but are otherwise equal.
|
||||
*
|
||||
* @return a memory layout without a name.
|
||||
* @see MemoryLayout#name()
|
||||
*/
|
||||
MemoryLayout withoutName();
|
||||
|
||||
/**
|
||||
* Returns the alignment constraint associated with this layout, expressed in bits. Layout alignment defines a power
|
||||
* of two {@code A} which is the bit-wise alignment of the layout. If {@code A <= 8} then {@code A/8} is the number of
|
||||
|
@ -235,10 +245,7 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
|||
* @return the layout alignment constraint, in bytes.
|
||||
* @throws UnsupportedOperationException if {@code bitAlignment()} is not a multiple of 8.
|
||||
*/
|
||||
default long byteAlignment() {
|
||||
return Utils.bitsToBytesOrThrow(bitAlignment(),
|
||||
() -> new UnsupportedOperationException("Cannot compute byte alignment; bit alignment is not a multiple of 8"));
|
||||
}
|
||||
long byteAlignment();
|
||||
|
||||
/**
|
||||
* Returns a memory layout of the same type with the same size and name as this layout,
|
||||
|
@ -259,12 +266,14 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
|||
* @throws IllegalArgumentException if the layout path does not select any layout nested in this layout, or if the
|
||||
* layout path contains one or more path elements that select multiple sequence element indices
|
||||
* (see {@link PathElement#sequenceElement()} and {@link PathElement#sequenceElement(long, long)}).
|
||||
* @throws IllegalArgumentException if the layout path contains one or more dereference path elements
|
||||
* (see {@link PathElement#dereferenceElement()}).
|
||||
* @throws NullPointerException if either {@code elements == null}, or if any of the elements
|
||||
* in {@code elements} is {@code null}.
|
||||
*/
|
||||
default long bitOffset(PathElement... elements) {
|
||||
return computePathOp(LayoutPath.rootPath(this), LayoutPath::offset,
|
||||
EnumSet.of(PathKind.SEQUENCE_ELEMENT, PathKind.SEQUENCE_RANGE), elements);
|
||||
EnumSet.of(PathKind.SEQUENCE_ELEMENT, PathKind.SEQUENCE_RANGE, PathKind.DEREF_ELEMENT), elements);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -293,10 +302,12 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
|||
* specified by the given layout path elements, when supplied with the missing sequence element indices.
|
||||
* @throws IllegalArgumentException if the layout path contains one or more path elements that select
|
||||
* multiple sequence element indices (see {@link PathElement#sequenceElement(long, long)}).
|
||||
* @throws IllegalArgumentException if the layout path contains one or more dereference path elements
|
||||
* (see {@link PathElement#dereferenceElement()}).
|
||||
*/
|
||||
default MethodHandle bitOffsetHandle(PathElement... elements) {
|
||||
return computePathOp(LayoutPath.rootPath(this), LayoutPath::offsetHandle,
|
||||
EnumSet.of(PathKind.SEQUENCE_RANGE), elements);
|
||||
EnumSet.of(PathKind.SEQUENCE_RANGE, PathKind.DEREF_ELEMENT), elements);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -308,12 +319,14 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
|||
* @throws IllegalArgumentException if the layout path does not select any layout nested in this layout, or if the
|
||||
* layout path contains one or more path elements that select multiple sequence element indices
|
||||
* (see {@link PathElement#sequenceElement()} and {@link PathElement#sequenceElement(long, long)}).
|
||||
* @throws IllegalArgumentException if the layout path contains one or more dereference path elements
|
||||
* (see {@link PathElement#dereferenceElement()}).
|
||||
* @throws UnsupportedOperationException if {@code bitOffset(elements)} is not a multiple of 8.
|
||||
* @throws NullPointerException if either {@code elements == null}, or if any of the elements
|
||||
* in {@code elements} is {@code null}.
|
||||
*/
|
||||
default long byteOffset(PathElement... elements) {
|
||||
return Utils.bitsToBytesOrThrow(bitOffset(elements), Utils.BITS_TO_BYTES_THROW_OFFSET);
|
||||
return Utils.bitsToBytes(bitOffset(elements));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -346,10 +359,12 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
|||
* specified by the given layout path elements, when supplied with the missing sequence element indices.
|
||||
* @throws IllegalArgumentException if the layout path contains one or more path elements that select
|
||||
* multiple sequence element indices (see {@link PathElement#sequenceElement(long, long)}).
|
||||
* @throws IllegalArgumentException if the layout path contains one or more dereference path elements
|
||||
* (see {@link PathElement#dereferenceElement()}).
|
||||
*/
|
||||
default MethodHandle byteOffsetHandle(PathElement... elements) {
|
||||
MethodHandle mh = bitOffsetHandle(elements);
|
||||
mh = MethodHandles.filterReturnValue(mh, Utils.MH_BITS_TO_BYTES_OR_THROW_FOR_OFFSET);
|
||||
mh = MethodHandles.filterReturnValue(mh, Utils.BITS_TO_BYTES);
|
||||
return mh;
|
||||
}
|
||||
|
||||
|
@ -379,6 +394,28 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
|||
* <p>
|
||||
* Additionally, the provided dynamic values must conform to some bound which is derived from the layout path, that is,
|
||||
* {@code 0 <= x_i < b_i}, where {@code 1 <= i <= n}, or {@link IndexOutOfBoundsException} is thrown.
|
||||
* <p>
|
||||
* Multiple paths can be chained, by using {@linkplain PathElement#dereferenceElement() dereference path elements}.
|
||||
* A dereference path element allows to obtain a native memory segment whose base address is the address obtained
|
||||
* by following the layout path elements immediately preceding the dereference path element. In other words,
|
||||
* if a layout path contains one or more dereference path elements, the final address accessed by the returned
|
||||
* var handle can be computed as follows:
|
||||
*
|
||||
* <blockquote><pre>{@code
|
||||
* address_1 = base(segment) + offset_1
|
||||
* address_2 = base(segment_1) + offset_2
|
||||
* ...
|
||||
* address_k = base(segment_k-1) + offset_k
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* where {@code k} is the number of dereference path elements in a layout path, {@code segment} is the input segment,
|
||||
* {@code segment_1}, ... {@code segment_k-1} are the segments obtained by dereferencing the address associated with
|
||||
* a given dereference path element (e.g. {@code segment_1} is a native segment whose base address is {@code address_1}),
|
||||
* and {@code offset_1}, {@code offset_2}, ... {@code offset_k} are the offsets computed by evaluating
|
||||
* the path elements after a given dereference operation (these offsets are obtained using the computation described
|
||||
* above). In these more complex access operations, all memory accesses immediately preceding a dereference operation
|
||||
* (e.g. those at addresses {@code address_1}, {@code address_2}, ..., {@code address_k-1} are performed using the
|
||||
* {@link VarHandle.AccessMode#GET} access mode.
|
||||
*
|
||||
* @apiNote the resulting var handle will feature an additional {@code long} access coordinate for every
|
||||
* unspecified sequence access component contained in this layout path. Moreover, the resulting var handle
|
||||
|
@ -388,6 +425,8 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
|||
* @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}).
|
||||
* @throws IllegalArgumentException if the layout path in {@code elements} contains a {@linkplain PathElement#dereferenceElement()
|
||||
* dereference path element} for an address layout that has no {@linkplain AddressLayout#targetLayout() target layout}.
|
||||
* @see MethodHandles#memorySegmentViewVarHandle(ValueLayout)
|
||||
*/
|
||||
default VarHandle varHandle(PathElement... elements) {
|
||||
|
@ -432,6 +471,8 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
|||
* @param elements the layout path elements.
|
||||
* @return a method handle which can be used to create a slice of the selected layout element, given a segment.
|
||||
* @throws UnsupportedOperationException if the size of the selected layout in bits is not a multiple of 8.
|
||||
* @throws IllegalArgumentException if the layout path contains one or more dereference path elements
|
||||
* (see {@link PathElement#dereferenceElement()}).
|
||||
*/
|
||||
default MethodHandle sliceHandle(PathElement... elements) {
|
||||
return computePathOp(LayoutPath.rootPath(this), LayoutPath::sliceHandle,
|
||||
|
@ -446,10 +487,12 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
|||
* @throws IllegalArgumentException if the layout path does not select any layout nested in this layout,
|
||||
* or if the layout path contains one or more path elements that select one or more sequence element indices
|
||||
* (see {@link PathElement#sequenceElement(long)} and {@link PathElement#sequenceElement(long, long)}).
|
||||
* @throws IllegalArgumentException if the layout path contains one or more dereference path elements
|
||||
* (see {@link PathElement#dereferenceElement()}).
|
||||
*/
|
||||
default MemoryLayout select(PathElement... elements) {
|
||||
return computePathOp(LayoutPath.rootPath(this), LayoutPath::layout,
|
||||
EnumSet.of(PathKind.SEQUENCE_ELEMENT_INDEX, PathKind.SEQUENCE_RANGE), elements);
|
||||
EnumSet.of(PathKind.SEQUENCE_ELEMENT_INDEX, PathKind.SEQUENCE_RANGE, PathKind.DEREF_ELEMENT), elements);
|
||||
}
|
||||
|
||||
private static <Z> Z computePathOp(LayoutPath path, Function<LayoutPath, Z> finalizer,
|
||||
|
@ -489,6 +532,7 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
|||
*
|
||||
* @implSpec in case multiple group elements with a matching name exist, the path element returned by this
|
||||
* method will select the first one; that is, the group element with the lowest offset from current path is selected.
|
||||
* In such cases, using {@link #groupElement(long)} might be preferable.
|
||||
*
|
||||
* @param name the name of the group element to be selected.
|
||||
* @return a path element which selects the group element with the given name.
|
||||
|
@ -499,6 +543,23 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
|||
path -> path.groupElement(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a path element which selects a member layout with the given index in a group layout.
|
||||
* The path element returned by this method does not alter the number of free dimensions of any path
|
||||
* that is combined with such element.
|
||||
*
|
||||
* @param index the index of the group element to be selected.
|
||||
* @return a path element which selects the group element with the given index.
|
||||
* @throws IllegalArgumentException if {@code index < 0}.
|
||||
*/
|
||||
static PathElement groupElement(long index) {
|
||||
if (index < 0) {
|
||||
throw new IllegalArgumentException("Index < 0");
|
||||
}
|
||||
return new LayoutPath.PathElementImpl(PathKind.GROUP_ELEMENT,
|
||||
path -> path.groupElement(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a path element which selects the element layout at the specified position in a sequence layout.
|
||||
* The path element returned by this method does not alter the number of free dimensions of any path
|
||||
|
@ -578,6 +639,21 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
|||
return new LayoutPath.PathElementImpl(PathKind.SEQUENCE_ELEMENT,
|
||||
LayoutPath::sequenceElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a path element which dereferences an address layout as its
|
||||
* {@linkplain AddressLayout#targetLayout() target layout} (where set).
|
||||
* The path element returned by this method does not alter the number of free dimensions of any path
|
||||
* that is combined with such element. Using this path layout to dereference an address layout
|
||||
* that has no target layout results in an {@link IllegalArgumentException} (e.g. when
|
||||
* a var handle is {@linkplain #varHandle(PathElement...) obtained}).
|
||||
*
|
||||
* @return a path element which dereferences an address layout.
|
||||
*/
|
||||
static PathElement dereferenceElement() {
|
||||
return new LayoutPath.PathElementImpl(PathKind.DEREF_ELEMENT,
|
||||
LayoutPath::derefElement);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -611,60 +687,14 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
|||
String toString();
|
||||
|
||||
/**
|
||||
* Creates a padding layout with the given size.
|
||||
* Creates a padding layout with the given bitSize and a bit-alignment of eight.
|
||||
*
|
||||
* @param size the padding size in bits.
|
||||
* @param bitSize the padding size in bits.
|
||||
* @return the new selector layout.
|
||||
* @throws IllegalArgumentException if {@code size <= 0}.
|
||||
* @throws IllegalArgumentException if {@code bitSize <= 0} or {@code bitSize % 8 != 0}
|
||||
*/
|
||||
static PaddingLayout paddingLayout(long size) {
|
||||
MemoryLayoutUtil.checkSize(size);
|
||||
return PaddingLayoutImpl.of(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a value layout of given Java carrier and byte order. The type of resulting value layout is determined
|
||||
* by the carrier provided:
|
||||
* <ul>
|
||||
* <li>{@link ValueLayout.OfBoolean}, for {@code boolean.class}</li>
|
||||
* <li>{@link ValueLayout.OfByte}, for {@code byte.class}</li>
|
||||
* <li>{@link ValueLayout.OfShort}, for {@code short.class}</li>
|
||||
* <li>{@link ValueLayout.OfChar}, for {@code char.class}</li>
|
||||
* <li>{@link ValueLayout.OfInt}, for {@code int.class}</li>
|
||||
* <li>{@link ValueLayout.OfFloat}, for {@code float.class}</li>
|
||||
* <li>{@link ValueLayout.OfLong}, for {@code long.class}</li>
|
||||
* <li>{@link ValueLayout.OfDouble}, for {@code double.class}</li>
|
||||
* <li>{@link ValueLayout.OfAddress}, for {@code MemorySegment.class}</li>
|
||||
* </ul>
|
||||
* @param carrier the value layout carrier.
|
||||
* @param order the value layout's byte order.
|
||||
* @return a value layout with the given Java carrier and byte-order.
|
||||
* @throws IllegalArgumentException if the carrier type is not supported.
|
||||
*/
|
||||
static ValueLayout valueLayout(Class<?> carrier, ByteOrder order) {
|
||||
Objects.requireNonNull(carrier);
|
||||
Objects.requireNonNull(order);
|
||||
if (carrier == boolean.class) {
|
||||
return ValueLayouts.OfBooleanImpl.of(order);
|
||||
} else if (carrier == char.class) {
|
||||
return ValueLayouts.OfCharImpl.of(order);
|
||||
} else if (carrier == byte.class) {
|
||||
return ValueLayouts.OfByteImpl.of(order);
|
||||
} else if (carrier == short.class) {
|
||||
return ValueLayouts.OfShortImpl.of(order);
|
||||
} else if (carrier == int.class) {
|
||||
return ValueLayouts.OfIntImpl.of(order);
|
||||
} else if (carrier == float.class) {
|
||||
return ValueLayouts.OfFloatImpl.of(order);
|
||||
} else if (carrier == long.class) {
|
||||
return ValueLayouts.OfLongImpl.of(order);
|
||||
} else if (carrier == double.class) {
|
||||
return ValueLayouts.OfDoubleImpl.of(order);
|
||||
} else if (carrier == MemorySegment.class) {
|
||||
return ValueLayouts.OfAddressImpl.of(order);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported carrier: " + carrier.getName());
|
||||
}
|
||||
static PaddingLayout paddingLayout(long bitSize) {
|
||||
return PaddingLayoutImpl.of(MemoryLayoutUtil.requireBitSizeValid(bitSize, false));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -674,10 +704,12 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
|||
* @param elementLayout the sequence element layout.
|
||||
* @return the new sequence layout with the given element layout and size.
|
||||
* @throws IllegalArgumentException if {@code elementCount } is negative.
|
||||
* @throws IllegalArgumentException if {@code elementLayout.bitAlignment() > elementLayout.bitSize()}.
|
||||
*/
|
||||
static SequenceLayout sequenceLayout(long elementCount, MemoryLayout elementLayout) {
|
||||
MemoryLayoutUtil.checkSize(elementCount, true);
|
||||
MemoryLayoutUtil.requireNonNegative(elementCount);
|
||||
Objects.requireNonNull(elementLayout);
|
||||
Utils.checkElementAlignment(elementLayout, "Element layout alignment greater than its size");
|
||||
return wrapOverflow(() ->
|
||||
SequenceLayoutImpl.of(elementCount, elementLayout));
|
||||
}
|
||||
|
@ -693,6 +725,7 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
|||
*
|
||||
* @param elementLayout the sequence element layout.
|
||||
* @return a new sequence layout with the given element layout and maximum element count.
|
||||
* @throws IllegalArgumentException if {@code elementLayout.bitAlignment() > elementLayout.bitSize()}.
|
||||
*/
|
||||
static SequenceLayout sequenceLayout(MemoryLayout elementLayout) {
|
||||
Objects.requireNonNull(elementLayout);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -50,5 +50,12 @@ public sealed interface PaddingLayout extends MemoryLayout permits PaddingLayout
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
PaddingLayout withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
PaddingLayout withBitAlignment(long bitAlignment);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -32,7 +32,6 @@ import java.nio.charset.StandardCharsets;
|
|||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import jdk.internal.foreign.AbstractMemorySegmentImpl;
|
||||
import jdk.internal.foreign.MemorySessionImpl;
|
||||
import jdk.internal.foreign.SlicingAllocator;
|
||||
import jdk.internal.foreign.Utils;
|
||||
import jdk.internal.javac.PreviewFeature;
|
||||
|
@ -46,8 +45,6 @@ import jdk.internal.javac.PreviewFeature;
|
|||
* <p>
|
||||
* This interface also defines factories for commonly used allocators:
|
||||
* <ul>
|
||||
* <li>{@link #nativeAllocator(SegmentScope)} obtains a simple allocator which can
|
||||
* be used to allocate native segments;</li>
|
||||
* <li>{@link #slicingAllocator(MemorySegment)} obtains an efficient slicing allocator, where memory
|
||||
* is allocated by repeatedly slicing the provided memory segment;</li>
|
||||
* <li>{@link #prefixAllocator(MemorySegment)} obtains an allocator which wraps a segment (either on-heap or off-heap)
|
||||
|
@ -201,7 +198,7 @@ public interface SegmentAllocator {
|
|||
* @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, MemorySegment value) {
|
||||
default MemorySegment allocate(AddressLayout layout, MemorySegment value) {
|
||||
Objects.requireNonNull(value);
|
||||
Objects.requireNonNull(layout);
|
||||
MemorySegment segment = allocate(layout);
|
||||
|
@ -363,8 +360,8 @@ public interface SegmentAllocator {
|
|||
|
||||
/**
|
||||
* Returns a segment allocator which responds to allocation requests by recycling a single segment. Each
|
||||
* new allocation request will return a new slice starting at the segment offset {@code 0} (alignment
|
||||
* constraints are ignored by this allocator), hence the name <em>prefix allocator</em>.
|
||||
* new allocation request will return a new slice starting at the segment offset {@code 0}, hence the name
|
||||
* <em>prefix allocator</em>.
|
||||
* Equivalent to (but likely more efficient than) the following code:
|
||||
* {@snippet lang=java :
|
||||
* MemorySegment segment = ...
|
||||
|
@ -384,31 +381,4 @@ public interface SegmentAllocator {
|
|||
static SegmentAllocator prefixAllocator(MemorySegment segment) {
|
||||
return (AbstractMemorySegmentImpl)Objects.requireNonNull(segment);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* <p>
|
||||
* 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}.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* The off-heap region of memory backing a native segment obtained by the returned allocator is initialized to zero.
|
||||
* <p>
|
||||
* This is equivalent to the following code:
|
||||
* {@snippet lang = java:
|
||||
* SegmentAllocator nativeAllocator = (byteSize, byteAlignment) ->
|
||||
* MemorySegment.allocateNative(byteSize, byteAlignment, scope);
|
||||
* }
|
||||
* @param scope the scope associated with the segments returned by the native allocator.
|
||||
* @return a simple allocator used to allocate native segments.
|
||||
*/
|
||||
static SegmentAllocator nativeAllocator(SegmentScope scope) {
|
||||
Objects.requireNonNull(scope);
|
||||
return (MemorySessionImpl)scope;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,132 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
* <p>
|
||||
* 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
|
||||
* <a href="Arena.html#thread-confinement">restricted to specific threads</a>.
|
||||
* <p>
|
||||
* 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!
|
||||
*}
|
||||
* <p>
|
||||
* 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 <em>after</em> they become
|
||||
* <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>, 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.
|
||||
* <p>
|
||||
* 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);
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -128,9 +128,22 @@ public sealed interface SequenceLayout extends MemoryLayout permits SequenceLayo
|
|||
*/
|
||||
SequenceLayout flatten();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
SequenceLayout withName(String name);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
MemoryLayout withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
* @throws IllegalArgumentException if {@code bitAlignment < elementLayout().bitAlignment()}.
|
||||
*/
|
||||
SequenceLayout withBitAlignment(long bitAlignment);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -39,9 +39,22 @@ import jdk.internal.javac.PreviewFeature;
|
|||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
||||
public sealed interface StructLayout extends GroupLayout permits StructLayoutImpl {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
StructLayout withName(String name);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
StructLayout withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
StructLayout withBitAlignment(long bitAlignment);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -52,21 +52,23 @@ import java.util.function.BiFunction;
|
|||
* <ul>
|
||||
* <li>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.</li>
|
||||
* <li>It can be passed to an existing {@linkplain Linker#downcallHandle(FunctionDescriptor, Linker.Option...) downcall method handle}, as an argument to the underlying foreign function.</li>
|
||||
* <li>It can be {@linkplain MemorySegment#set(ValueLayout.OfAddress, long, MemorySegment) stored} inside another memory segment.</li>
|
||||
* <li>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).</li>
|
||||
* <li>It can be {@linkplain MemorySegment#set(AddressLayout, long, MemorySegment) stored} inside another memory segment.</li>
|
||||
* <li>It can be used to access the region of memory backing a global variable (this requires
|
||||
* {@link MemorySegment#reinterpret(long)} () resizing} the segment first).</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2 id="obtaining">Obtaining a symbol lookup</h2>
|
||||
*
|
||||
* The factory methods {@link #libraryLookup(String, SegmentScope)} and {@link #libraryLookup(Path, SegmentScope)}
|
||||
* The factory methods {@link #libraryLookup(String, Arena)} and {@link #libraryLookup(Path, Arena)}
|
||||
* 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 <em>library lookup</em>, is associated
|
||||
* with a {@linkplain SegmentScope scope}; when the scope becomes not {@link SegmentScope#isAlive()}, the library is unloaded:
|
||||
* The library is loaded if not already loaded. The symbol lookup, which is known as a <em>library lookup</em>, and its
|
||||
* lifetime is controlled by an {@linkplain Arena arena}. For instance, if the provided arena is a
|
||||
* confined arena, the library associated with the symbol lookup is unloaded when the confined arena
|
||||
* is {@linkplain Arena#close()}:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* try (Arena arena = Arena.openConfined()) {
|
||||
* SymbolLookup libGL = SymbolLookup.libraryLookup("libGL.so", arena.scope()); // libGL.so loaded here
|
||||
* try (Arena arena = Arena.ofConfined()) {
|
||||
* SymbolLookup libGL = SymbolLookup.libraryLookup("libGL.so", arena); // libGL.so loaded here
|
||||
* MemorySegment glGetString = libGL.find("glGetString").orElseThrow();
|
||||
* ...
|
||||
* } // libGL.so unloaded here
|
||||
|
@ -92,7 +94,7 @@ import java.util.function.BiFunction;
|
|||
* that were loaded in the course of creating a library lookup:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* libraryLookup("libGL.so", scope).find("glGetString").isPresent(); // true
|
||||
* libraryLookup("libGL.so", arena).find("glGetString").isPresent(); // true
|
||||
* loaderLookup().find("glGetString").isPresent(); // false
|
||||
*}
|
||||
*
|
||||
|
@ -101,7 +103,7 @@ import java.util.function.BiFunction;
|
|||
*
|
||||
* {@snippet lang = java:
|
||||
* System.loadLibrary("GL"); // libGL.so loaded here
|
||||
* libraryLookup("libGL.so", scope).find("glGetString").isPresent(); // true
|
||||
* libraryLookup("libGL.so", arena).find("glGetString").isPresent(); // true
|
||||
*}
|
||||
*
|
||||
* <p>
|
||||
|
@ -139,9 +141,10 @@ public interface SymbolLookup {
|
|||
* <p>
|
||||
* Libraries associated with a class loader are unloaded when the class loader becomes
|
||||
* <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>. The symbol lookup
|
||||
* 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.
|
||||
* returned by this method is associated with a fresh {@linkplain MemorySegment.Scope scope} 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, or any of the segments
|
||||
* obtained by it, is reachable.
|
||||
* <p>
|
||||
* In cases where this method is called from a context where there is no caller frame on the stack
|
||||
* (e.g. when called directly from a JNI attached thread), the caller's class loader defaults to the
|
||||
|
@ -158,9 +161,13 @@ public interface SymbolLookup {
|
|||
ClassLoader loader = caller != null ?
|
||||
caller.getClassLoader() :
|
||||
ClassLoader.getSystemClassLoader();
|
||||
SegmentScope loaderScope = (loader == null || loader instanceof BuiltinClassLoader) ?
|
||||
SegmentScope.global() : // builtin loaders never go away
|
||||
MemorySessionImpl.heapSession(loader);
|
||||
Arena loaderArena;// builtin loaders never go away
|
||||
if ((loader == null || loader instanceof BuiltinClassLoader)) {
|
||||
loaderArena = Arena.global();
|
||||
} else {
|
||||
MemorySessionImpl session = MemorySessionImpl.heapSession(loader);
|
||||
loaderArena = session.asArena();
|
||||
}
|
||||
return name -> {
|
||||
Objects.requireNonNull(name);
|
||||
JavaLangAccess javaLangAccess = SharedSecrets.getJavaLangAccess();
|
||||
|
@ -168,14 +175,17 @@ public interface SymbolLookup {
|
|||
long addr = javaLangAccess.findNative(loader, name);
|
||||
return addr == 0L ?
|
||||
Optional.empty() :
|
||||
Optional.of(MemorySegment.ofAddress(addr, 0L, loaderScope));
|
||||
Optional.of(MemorySegment.ofAddress(addr)
|
||||
.reinterpret(loaderArena, null));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 scope becomes
|
||||
* not {@linkplain SegmentScope#isAlive() alive}, if no other library lookup is still using it.
|
||||
* The lifetime of the returned library lookup is controlled by the provided arena.
|
||||
* For instance, if the provided arena is a confined arena, the library
|
||||
* associated with the returned lookup will be unloaded when the provided confined arena is
|
||||
* {@linkplain Arena#close() closed}.
|
||||
* @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,21 +196,26 @@ public interface SymbolLookup {
|
|||
* 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 scope the scope associated with symbols obtained from the returned lookup.
|
||||
* @param arena the arena 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 IllegalStateException if {@code arena.scope().isAlive() == false}
|
||||
* @throws WrongThreadException if {@code arena} is a confined arena, and this method is called from a
|
||||
* thread {@code T}, other than the arena's owner thread.
|
||||
* @throws IllegalArgumentException if {@code name} does not identify a valid library.
|
||||
* @throws IllegalCallerException If the caller is in a module that does not have native access enabled.
|
||||
*/
|
||||
@CallerSensitive
|
||||
static SymbolLookup libraryLookup(String name, SegmentScope scope) {
|
||||
static SymbolLookup libraryLookup(String name, Arena arena) {
|
||||
Reflection.ensureNativeAccess(Reflection.getCallerClass(), SymbolLookup.class, "libraryLookup");
|
||||
return libraryLookup(name, RawNativeLibraries::load, scope);
|
||||
return libraryLookup(name, RawNativeLibraries::load, arena);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 scope becomes
|
||||
* not {@linkplain SegmentScope#isAlive() alive}, if no other library lookup is still using it.
|
||||
* in that library. The lifetime of the returned library lookup is controlled by the provided arena.
|
||||
* For instance, if the provided arena is a confined arena, the library
|
||||
* associated with the returned lookup will be unloaded when the provided confined arena is
|
||||
* {@linkplain Arena#close() closed}.
|
||||
* <p>
|
||||
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
|
||||
* Restricted methods are unsafe, and, if used incorrectly, their use might crash
|
||||
|
@ -210,20 +225,23 @@ public interface SymbolLookup {
|
|||
* @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 scope the scope associated with symbols obtained from the returned lookup.
|
||||
* @param arena the arena 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 IllegalStateException if {@code arena.scope().isAlive() == false}
|
||||
* @throws WrongThreadException if {@code arena} is a confined arena, and this method is called from a
|
||||
* thread {@code T}, other than the arena's owner thread.
|
||||
* @throws IllegalArgumentException if {@code path} does not point to a valid library.
|
||||
* @throws IllegalCallerException If the caller is in a module that does not have native access enabled.
|
||||
*/
|
||||
@CallerSensitive
|
||||
static SymbolLookup libraryLookup(Path path, SegmentScope scope) {
|
||||
static SymbolLookup libraryLookup(Path path, Arena arena) {
|
||||
Reflection.ensureNativeAccess(Reflection.getCallerClass(), SymbolLookup.class, "libraryLookup");
|
||||
return libraryLookup(path, RawNativeLibraries::load, scope);
|
||||
return libraryLookup(path, RawNativeLibraries::load, arena);
|
||||
}
|
||||
|
||||
private static <Z> SymbolLookup libraryLookup(Z libDesc, BiFunction<RawNativeLibraries, Z, NativeLibrary> loadLibraryFunc, SegmentScope libScope) {
|
||||
private static <Z> SymbolLookup libraryLookup(Z libDesc, BiFunction<RawNativeLibraries, Z, NativeLibrary> loadLibraryFunc, Arena libArena) {
|
||||
Objects.requireNonNull(libDesc);
|
||||
Objects.requireNonNull(libScope);
|
||||
Objects.requireNonNull(libArena);
|
||||
// attempt to load native library from path or name
|
||||
RawNativeLibraries nativeLibraries = RawNativeLibraries.newInstance(MethodHandles.lookup());
|
||||
NativeLibrary library = loadLibraryFunc.apply(nativeLibraries, libDesc);
|
||||
|
@ -231,7 +249,7 @@ public interface SymbolLookup {
|
|||
throw new IllegalArgumentException("Cannot open library: " + libDesc);
|
||||
}
|
||||
// register hook to unload library when 'libScope' becomes not alive
|
||||
((MemorySessionImpl) libScope).addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() {
|
||||
MemorySessionImpl.toMemorySession(libArena).addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() {
|
||||
@Override
|
||||
public void cleanup() {
|
||||
nativeLibraries.unload(library);
|
||||
|
@ -242,7 +260,8 @@ public interface SymbolLookup {
|
|||
long addr = library.find(name);
|
||||
return addr == 0L ?
|
||||
Optional.empty() :
|
||||
Optional.of(MemorySegment.ofAddress(addr, 0, libScope));
|
||||
Optional.of(MemorySegment.ofAddress(addr)
|
||||
.reinterpret(libArena, null));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -49,5 +49,12 @@ public sealed interface UnionLayout extends GroupLayout permits UnionLayoutImpl
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
UnionLayout withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
UnionLayout withBitAlignment(long bitAlignment);
|
||||
}
|
||||
|
|
|
@ -1,352 +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.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64VaList;
|
||||
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64VaList;
|
||||
import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64VaList;
|
||||
import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64VaList;
|
||||
import jdk.internal.foreign.abi.x64.sysv.SysVVaList;
|
||||
import jdk.internal.foreign.abi.x64.windows.WinVaList;
|
||||
import jdk.internal.javac.PreviewFeature;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
|
||||
/**
|
||||
* Helper class to create and manipulate variable argument lists, similar in functionality to a C {@code va_list}.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* The contents of a foreign memory segment modelling a variable argument list can be accessed by <em>unsafely</em> 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.
|
||||
* <p>
|
||||
* 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',
|
||||
* which erases integral types by way of integer promotion (see C99 standard 6.3.1.1 - item 2),
|
||||
* and which erases all {@code float} arguments to {@code double}.
|
||||
* <p>
|
||||
* As such, this interface only supports reading {@code int}, {@code double},
|
||||
* and any other type that fits into a {@code long}.
|
||||
* <h2 id="safety">Safety considerations</h2>
|
||||
* Accessing a value through a variable argument list using the wrong memory layout will result in undefined behavior.
|
||||
* For instance, if a variable argument list currently points at a C {@code int} value, then accessing it using
|
||||
* {@link #nextVarg(ValueLayout.OfLong)} is illegal. Similarly, accessing the variable argument list with
|
||||
* {@link #skip(MemoryLayout...)}, and providing a layout other than {@link ValueLayout.OfInt} is illegal.
|
||||
* Any such illegal accesses might not be detected by the implementation, and can corrupt the variable argument list,
|
||||
* so that the behavior of subsequent accesses is also undefined.
|
||||
* <p>
|
||||
* It is possible for clients to access elements outside the spatial bounds of a variable argument list.
|
||||
* Variable argument list implementations will try to detect out-of-bounds reads on a best-effort basis.
|
||||
* <p>
|
||||
* Whether this detection succeeds depends on the factory method used to create the variable argument list:
|
||||
* <ul>
|
||||
* <li>Variable argument lists created <em>safely</em>, using {@link #make(Consumer, SegmentScope)} are capable of detecting out-of-bounds reads;</li>
|
||||
* <li>Variable argument lists created <em>unsafely</em>, using {@link #ofAddress(long, SegmentScope)} are not capable of detecting out-of-bounds reads</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* This class is not thread safe, and all accesses should occur within a single thread
|
||||
* (regardless of the scope used to obtain the variable arity list).
|
||||
*
|
||||
* @since 19
|
||||
*/
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
||||
public sealed interface VaList permits WinVaList, SysVVaList, LinuxAArch64VaList, MacOsAArch64VaList, WindowsAArch64VaList, LinuxRISCV64VaList, SharedUtils.EmptyVaList {
|
||||
|
||||
/**
|
||||
* Reads the next value as an {@code int} and advances this variable argument list's position. The behavior of this
|
||||
* method is equivalent to the C {@code va_arg} function.
|
||||
*
|
||||
* @param layout the layout of the value to be read.
|
||||
* @return the {@code int} value read from this variable argument list.
|
||||
* @throws IllegalStateException if the scope associated with this variable argument list 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 <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
int nextVarg(ValueLayout.OfInt layout);
|
||||
|
||||
/**
|
||||
* Reads the next value as a {@code long} and advances this variable argument list's position. The behavior of this
|
||||
* method is equivalent to the C {@code va_arg} function.
|
||||
*
|
||||
* @param layout the layout of the value to be read.
|
||||
* @return the {@code long} value read from this variable argument list.
|
||||
* @throws IllegalStateException if the scope associated with this variable argument list 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 <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
long nextVarg(ValueLayout.OfLong layout);
|
||||
|
||||
/**
|
||||
* Reads the next value as a {@code double} and advances this variable argument list's position. The behavior of this
|
||||
* method is equivalent to the C {@code va_arg} function.
|
||||
*
|
||||
* @param layout the layout of the value
|
||||
* @return the {@code double} value read from this variable argument list.
|
||||
* @throws IllegalStateException if the scope associated with this variable argument list 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 <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
double nextVarg(ValueLayout.OfDouble layout);
|
||||
|
||||
/**
|
||||
* 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 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 <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
MemorySegment nextVarg(ValueLayout.OfAddress layout);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* <p>
|
||||
* 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
|
||||
* calls to {@code nextVarg} for each of the layouts in {@code L_1, L_2, ... L_n}.
|
||||
* <p>
|
||||
* The memory segment returned by this method will be allocated using the given {@link SegmentAllocator}.
|
||||
*
|
||||
* @param layout the layout of the value to be read.
|
||||
* @param allocator the allocator to be used to create a segment where the contents of the variable argument list
|
||||
* will be copied.
|
||||
* @return the {@code MemorySegment} value read from this variable argument list.
|
||||
* @throws IllegalStateException if the scope associated with this variable argument list 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 <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator);
|
||||
|
||||
/**
|
||||
* Skips a number of elements with the given memory layouts, and advances this variable argument list's position.
|
||||
*
|
||||
* @param layouts the layouts of the values to be skipped.
|
||||
* @throws IllegalStateException if the scope associated with this variable argument list 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 <a href=VaList.html#safety>out-of-bounds</a> 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 scope as this variable argument list. The behavior of this method is equivalent to the C
|
||||
* {@code va_copy} function.
|
||||
* <p>
|
||||
* Copying is useful to traverse the variable argument list elements, starting from the current position,
|
||||
* without affecting the state of the original variable argument list, essentially allowing the elements to be
|
||||
* traversed multiple times.
|
||||
*
|
||||
* @return a copy of this variable argument list.
|
||||
* @throws IllegalStateException if the scope associated with this variable argument list 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();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
MemorySegment segment();
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
* <p>
|
||||
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
|
||||
* 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 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 the caller is in a module that does not have native access enabled.
|
||||
*/
|
||||
@CallerSensitive
|
||||
static VaList ofAddress(long address, SegmentScope scope) {
|
||||
Reflection.ensureNativeAccess(Reflection.getCallerClass(), VaList.class, "ofAddress");
|
||||
Objects.requireNonNull(scope);
|
||||
return SharedUtils.newVaListOfAddress(address, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a variable argument list using a builder (see {@link Builder}), with the given
|
||||
* 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}.
|
||||
* <p>
|
||||
* Note that when there are no elements added to the created va list,
|
||||
* this method will return the same as {@link #empty()}.
|
||||
*
|
||||
* @implNote variable argument lists created using this method can detect <a href=VaList.html#safety>out-of-bounds</a> reads.
|
||||
*
|
||||
* @param actions a consumer for a builder (see {@link Builder}) which can be used to specify the elements
|
||||
* of the underlying variable argument list.
|
||||
* @param scope 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 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<Builder> actions, SegmentScope scope) {
|
||||
Objects.requireNonNull(actions);
|
||||
Objects.requireNonNull(scope);
|
||||
return SharedUtils.newVaList(actions, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
static VaList empty() {
|
||||
return SharedUtils.emptyVaList();
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder used to construct a {@linkplain VaList variable argument list}.
|
||||
*
|
||||
* @since 19
|
||||
*/
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
||||
sealed interface Builder permits WinVaList.Builder, SysVVaList.Builder, LinuxAArch64VaList.Builder, MacOsAArch64VaList.Builder, WindowsAArch64VaList.Builder, LinuxRISCV64VaList.Builder {
|
||||
|
||||
/**
|
||||
* Writes an {@code int} value to the variable argument list being constructed.
|
||||
*
|
||||
* @param layout the layout of the value to be written.
|
||||
* @param value the {@code int} value to be written.
|
||||
* @return this builder.
|
||||
*/
|
||||
Builder addVarg(ValueLayout.OfInt layout, int value);
|
||||
|
||||
/**
|
||||
* Writes a {@code long} value to the variable argument list being constructed.
|
||||
*
|
||||
* @param layout the layout of the value to be written.
|
||||
* @param value the {@code long} value to be written.
|
||||
* @return this builder.
|
||||
*/
|
||||
Builder addVarg(ValueLayout.OfLong layout, long value);
|
||||
|
||||
/**
|
||||
* Writes a {@code double} value to the variable argument list being constructed.
|
||||
*
|
||||
* @param layout the layout of the value to be written.
|
||||
* @param value the {@code double} value to be written.
|
||||
* @return this builder.
|
||||
*/
|
||||
Builder addVarg(ValueLayout.OfDouble layout, double value);
|
||||
|
||||
/**
|
||||
* Writes 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 segment the segment whose {@linkplain MemorySegment#address() address} is to be written.
|
||||
* @return this builder.
|
||||
*/
|
||||
Builder addVarg(ValueLayout.OfAddress layout, MemorySegment segment);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return this builder.
|
||||
*/
|
||||
Builder addVarg(GroupLayout layout, MemorySegment value);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -31,7 +31,6 @@ import java.nio.ByteOrder;
|
|||
|
||||
import jdk.internal.foreign.layout.ValueLayouts;
|
||||
import jdk.internal.javac.PreviewFeature;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
|
||||
/**
|
||||
* A layout that models values of basic data types. Examples of values modelled by a value layout are
|
||||
|
@ -49,10 +48,13 @@ import jdk.internal.reflect.CallerSensitive;
|
|||
*
|
||||
* @implSpec implementing classes and subclasses are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
|
||||
*
|
||||
* @sealedGraph
|
||||
* @since 19
|
||||
*/
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
||||
public sealed interface ValueLayout extends MemoryLayout {
|
||||
public sealed interface ValueLayout extends MemoryLayout permits
|
||||
ValueLayout.OfBoolean, ValueLayout.OfByte, ValueLayout.OfChar, ValueLayout.OfShort, ValueLayout.OfInt,
|
||||
ValueLayout.OfFloat, ValueLayout.OfLong, ValueLayout.OfDouble, AddressLayout {
|
||||
|
||||
/**
|
||||
* {@return the value's byte order}
|
||||
|
@ -68,6 +70,12 @@ public sealed interface ValueLayout extends MemoryLayout {
|
|||
*/
|
||||
ValueLayout withOrder(ByteOrder order);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
ValueLayout withoutName();
|
||||
|
||||
/**
|
||||
* Creates a <em>strided</em> 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
|
||||
|
@ -141,9 +149,9 @@ public sealed interface ValueLayout extends MemoryLayout {
|
|||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
|
||||
ValueLayout withBitAlignment(long bitAlignment);
|
||||
|
||||
/**
|
||||
|
@ -165,6 +173,13 @@ public sealed interface ValueLayout extends MemoryLayout {
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfBoolean withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfBoolean withBitAlignment(long bitAlignment);
|
||||
|
||||
/**
|
||||
|
@ -194,6 +209,13 @@ public sealed interface ValueLayout extends MemoryLayout {
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfByte withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfByte withBitAlignment(long bitAlignment);
|
||||
|
||||
/**
|
||||
|
@ -224,6 +246,13 @@ public sealed interface ValueLayout extends MemoryLayout {
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfChar withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfChar withBitAlignment(long bitAlignment);
|
||||
|
||||
/**
|
||||
|
@ -254,6 +283,13 @@ public sealed interface ValueLayout extends MemoryLayout {
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfShort withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfShort withBitAlignment(long bitAlignment);
|
||||
|
||||
/**
|
||||
|
@ -284,6 +320,13 @@ public sealed interface ValueLayout extends MemoryLayout {
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfInt withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfInt withBitAlignment(long bitAlignment);
|
||||
|
||||
/**
|
||||
|
@ -310,6 +353,12 @@ public sealed interface ValueLayout extends MemoryLayout {
|
|||
@Override
|
||||
OfFloat withName(String name);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfFloat withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@ -344,6 +393,13 @@ public sealed interface ValueLayout extends MemoryLayout {
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfLong withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfLong withBitAlignment(long bitAlignment);
|
||||
|
||||
/**
|
||||
|
@ -374,6 +430,13 @@ public sealed interface ValueLayout extends MemoryLayout {
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfDouble withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfDouble withBitAlignment(long bitAlignment);
|
||||
|
||||
/**
|
||||
|
@ -384,146 +447,57 @@ public sealed interface ValueLayout extends MemoryLayout {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* A value layout whose carrier is {@code MemorySegment.class}.
|
||||
*
|
||||
* @see #ADDRESS
|
||||
* @see #ADDRESS_UNALIGNED
|
||||
* @since 19
|
||||
*/
|
||||
@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 <em>unbounded</em> 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.
|
||||
* <p>
|
||||
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
|
||||
* 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.
|
||||
* @throws IllegalCallerException If the caller is in a module that does not have native access enabled.
|
||||
* @see #isUnbounded()
|
||||
*/
|
||||
@CallerSensitive
|
||||
OfAddress asUnbounded();
|
||||
|
||||
/**
|
||||
* {@return {@code true}, if this address layout is an {@linkplain #asUnbounded() unbounded address layout}}.
|
||||
*/
|
||||
boolean isUnbounded();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}, byte order set to {@link ByteOrder#nativeOrder()}.
|
||||
* Equivalent to the following code:
|
||||
* {@snippet lang=java :
|
||||
* MemoryLayout.valueLayout(MemorySegment.class, ByteOrder.nativeOrder());
|
||||
* }
|
||||
*/
|
||||
OfAddress ADDRESS = ValueLayouts.OfAddressImpl.of(ByteOrder.nativeOrder());
|
||||
AddressLayout 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());
|
||||
* }
|
||||
*/
|
||||
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());
|
||||
* }
|
||||
*/
|
||||
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());
|
||||
* }
|
||||
*/
|
||||
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());
|
||||
* }
|
||||
*/
|
||||
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());
|
||||
* }
|
||||
*/
|
||||
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());
|
||||
* }
|
||||
*/
|
||||
OfLong JAVA_LONG = ValueLayouts.OfLongImpl.of(ByteOrder.nativeOrder());
|
||||
|
||||
/**
|
||||
* A value layout constant whose size is the same as that of a Java {@code float},
|
||||
* bit alignment set to 32, and byte order set to {@link ByteOrder#nativeOrder()}.
|
||||
* Equivalent to the following code:
|
||||
* {@snippet lang=java :
|
||||
* MemoryLayout.valueLayout(float.class, 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());
|
||||
* }
|
||||
*/
|
||||
OfDouble JAVA_DOUBLE = ValueLayouts.OfDoubleImpl.of(ByteOrder.nativeOrder());
|
||||
|
||||
|
@ -537,7 +511,7 @@ public sealed interface ValueLayout extends MemoryLayout {
|
|||
* @apiNote Care should be taken when using unaligned value layouts as they may induce
|
||||
* performance and portability issues.
|
||||
*/
|
||||
OfAddress ADDRESS_UNALIGNED = ADDRESS.withBitAlignment(8);
|
||||
AddressLayout ADDRESS_UNALIGNED = ADDRESS.withBitAlignment(8);
|
||||
|
||||
/**
|
||||
* An unaligned value layout constant whose size is the same as that of a Java {@code char}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -31,77 +31,48 @@
|
|||
*
|
||||
* <p>
|
||||
* The main abstraction introduced to support foreign memory access is {@link java.lang.foreign.MemorySegment}, which
|
||||
* 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 <a href=MemorySegment.html#segment-deref>access memory segments</a>
|
||||
* using {@linkplain java.lang.foreign.MemoryLayout#varHandle(java.lang.foreign.MemoryLayout.PathElement...) var handles},
|
||||
* models a contiguous region of memory, residing either inside or outside the Java heap. Memory segments are
|
||||
* typically allocated using an {@link java.lang.foreign.Arena}, which controls the lifetime of the regions of memory
|
||||
* backing the segments it allocates. 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
|
||||
* <a href=MemorySegment.html#segment-deref>access memory segments</a> using
|
||||
* {@linkplain java.lang.foreign.MemoryLayout#varHandle(java.lang.foreign.MemoryLayout.PathElement...) var handles},
|
||||
* which can be computed using <a href="MemoryLayout.html#layout-paths"><em>layout paths</em></a>.
|
||||
*
|
||||
* 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, SegmentScope.auto());
|
||||
* for (int i = 0 ; i < 10 ; i++) {
|
||||
* segment.setAtIndex(ValueLayout.JAVA_INT, i, i);
|
||||
* }
|
||||
*}
|
||||
*
|
||||
* This code creates a <em>native</em> 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 <em>after</em> the segment becomes <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>.
|
||||
* 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.
|
||||
* <p>
|
||||
* 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) 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}.
|
||||
*
|
||||
* <h3 id="deallocation">Deterministic deallocation</h3>
|
||||
*
|
||||
* When writing code that manipulates memory segments, especially if backed by memory which resides outside the Java heap, it is
|
||||
* 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 <a href="../../../java/lang/ref/package.html#reachability">unreachable</a> is not optimal.
|
||||
* 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 (Arena arena = Arena.openConfined()) {
|
||||
* try (Arena arena = Arena.ofConfined()) {
|
||||
* 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 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 <em>try-with-resources</em> 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 <cite>The Java Language Specification</cite>.
|
||||
*
|
||||
* <h3 id="safety">Safety</h3>
|
||||
*
|
||||
* This API provides strong safety guarantees when it comes to memory access. First, when dereferencing a memory segment,
|
||||
* This code creates a <em>native</em> 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 native segment is allocated using a {@linkplain java.lang.foreign.Arena#ofConfined() confined arena}.
|
||||
* As such, access to the native segment is restricted to the current thread (the thread that created the arena).
|
||||
* Moreover, when the arena is closed, the native segment is invalidated, and its backing region of memory is
|
||||
* deallocated. Note the use of the <em>try-with-resources</em> 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 <cite>The Java Language Specification</cite>.
|
||||
* <p>
|
||||
* Memory segments provide strong safety guarantees when it comes to memory access. First, when accessing a memory segment,
|
||||
* the access coordinates are validated (upon access), to make sure that access does not occur at any address which resides
|
||||
* <em>outside</em> the boundaries of the memory segment used by the access operation. We call this guarantee <em>spatial safety</em>;
|
||||
* 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 <cite>The Java Language Specification</cite>.
|
||||
* <p>
|
||||
* 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 <em>temporal safety</em>. Together, spatial and temporal safety ensure that each memory access
|
||||
* operation either succeeds - and accesses a valid location within the region of memory backing the memory segment - or fails.
|
||||
* Additionally, to prevent a region of memory from being accessed <em>after</em> it has been deallocated
|
||||
* (i.e. <em>use-after-free</em>), a segment is also validated (upon access) to make sure that the arena from which it
|
||||
* has been obtained has not been closed. We call this guarantee <em>temporal safety</em>.
|
||||
* <p>
|
||||
* Together, spatial and temporal safety ensure that each memory access operation either succeeds - and accesses a valid
|
||||
* location within the region of memory backing the memory segment - or fails.
|
||||
*
|
||||
* <h2 id="ffa">Foreign function access</h2>
|
||||
* The key abstractions introduced to support foreign function access are {@link java.lang.foreign.SymbolLookup},
|
||||
|
@ -111,7 +82,7 @@
|
|||
* so that clients can perform foreign function calls directly in Java, without the need for intermediate layers of C/C++
|
||||
* code (as is the case with the <a href="{@docRoot}/../specs/jni/index.html">Java Native Interface (JNI)</a>).
|
||||
* <p>
|
||||
* For example, to compute the length of a string using the C standard library function {@code strlen} on a Linux x64 platform,
|
||||
* 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:
|
||||
|
@ -122,90 +93,39 @@
|
|||
* FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS)
|
||||
* );
|
||||
*
|
||||
* try (Arena arena = Arena.openConfined()) {
|
||||
* try (Arena arena = Arena.ofConfined()) {
|
||||
* MemorySegment cString = arena.allocateUtf8String("Hello");
|
||||
* long len = (long)strlen.invoke(cString); // 5
|
||||
* long len = (long)strlen.invokeExact(cString); // 5
|
||||
* }
|
||||
*}
|
||||
*
|
||||
* Here, we obtain a {@linkplain java.lang.foreign.Linker#nativeLinker() native linker} and we use it
|
||||
* to {@linkplain java.lang.foreign.SymbolLookup#find(java.lang.String) look up} the {@code strlen} symbol in the
|
||||
* standard C library; a <em>downcall method handle</em> targeting said symbol is subsequently
|
||||
* to {@linkplain java.lang.foreign.SymbolLookup#find(java.lang.String) look up} the {@code strlen} function in the
|
||||
* standard C library; a <em>downcall method handle</em> targeting said function is subsequently
|
||||
* {@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...)})
|
||||
* the method handle invocation (here performed using {@link java.lang.invoke.MethodHandle#invokeExact(java.lang.Object...)})
|
||||
* into a foreign function call, according to the rules specified by the ABI of the underlying platform.
|
||||
* The {@link java.lang.foreign.Arena} class also provides many useful methods for
|
||||
* interacting with foreign code, such as
|
||||
* {@linkplain java.lang.foreign.SegmentAllocator#allocateUtf8String(java.lang.String) converting} Java strings into
|
||||
* zero-terminated, UTF-8 strings, as demonstrated in the above example.
|
||||
*
|
||||
* <h3 id="upcalls">Upcalls</h3>
|
||||
* 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 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(MemorySegment addr1, MemorySegment addr2) {
|
||||
* return addr1.get(ValueLayout.JAVA_INT, 0) -
|
||||
* addr2.get(ValueLayout.JAVA_INT, 0);
|
||||
*
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* 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.asUnbounded(),
|
||||
* ValueLayout.ADDRESS.asUnbounded());
|
||||
* MethodHandle intCompareHandle = MethodHandles.lookup().findStatic(IntComparator.class,
|
||||
* "intCompare",
|
||||
* 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.FunctionDescriptor#toMethodType() derive} a method type
|
||||
* that can be used to look up the method handle for {@code IntComparator.intCompare}.
|
||||
* <p>
|
||||
* 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:
|
||||
* 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, 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.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.
|
||||
*
|
||||
* <h2 id="restricted">Restricted methods</h2>
|
||||
* Some methods in this package are considered <em>restricted</em>. 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(long, long, SegmentScope)}
|
||||
* can be used to create a fresh segment with the given spatial bounds out of a native address.
|
||||
* the restricted method {@link java.lang.foreign.MemorySegment#reinterpret(long)} ()}
|
||||
* can be used to create a fresh segment with the same address and temporal bounds,
|
||||
* but with the provided size. This can be useful to resize memory segments obtained when interacting with native functions.
|
||||
* <p>
|
||||
* 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.
|
||||
* or memory corruption when the bound Java API element is accessed. For instance, incorrectly resizing a native
|
||||
* memory sgement using {@link java.lang.foreign.MemorySegment#reinterpret(long)} can lead to a JVM crash, or, worse,
|
||||
* lead to silent memory corruption when attempting to access the resized 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.
|
||||
* <p>
|
||||
* 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
|
||||
|
|
|
@ -815,7 +815,9 @@ public abstract sealed class Buffer
|
|||
|
||||
@Override
|
||||
public ByteBuffer newMappedByteBuffer(UnmapperProxy unmapperProxy, long address, int cap, Object obj, MemorySegment segment) {
|
||||
return new DirectByteBuffer(address, cap, obj, unmapperProxy.fileDescriptor(), unmapperProxy.isSync(), segment);
|
||||
return unmapperProxy == null
|
||||
? new DirectByteBuffer(address, cap, obj, segment)
|
||||
: new DirectByteBuffer(address, cap, obj, unmapperProxy.fileDescriptor(), unmapperProxy.isSync(), segment);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
package java.nio.channels;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.spi.AbstractInterruptibleChannel;
|
||||
|
@ -1004,11 +1004,15 @@ public abstract class FileChannel
|
|||
|
||||
/**
|
||||
* Maps a region of this channel's file into a new mapped memory segment,
|
||||
* with the given offset, size and memory session.
|
||||
* with the given offset, size and arena.
|
||||
* The {@linkplain MemorySegment#address() address} of the returned memory
|
||||
* segment is the starting address of the mapped off-heap region backing
|
||||
* the segment.
|
||||
*
|
||||
* <p>
|
||||
* The lifetime of the returned segment is controlled by the provided arena.
|
||||
* For instance, if the provided arena is a closeable arena,
|
||||
* the returned segment will be unmapped when the provided closeable arena
|
||||
* is {@linkplain Arena#close() closed}.
|
||||
* <p> If the specified mapping mode is
|
||||
* {@linkplain FileChannel.MapMode#READ_ONLY READ_ONLY}, the resulting
|
||||
* segment will be read-only (see {@link MemorySegment#isReadOnly()}).
|
||||
|
@ -1049,8 +1053,8 @@ public abstract class FileChannel
|
|||
* The size (in bytes) of the mapped memory backing the memory
|
||||
* segment.
|
||||
*
|
||||
* @param session
|
||||
* The segment memory session.
|
||||
* @param arena
|
||||
* The segment arena.
|
||||
*
|
||||
* @return A new mapped memory segment.
|
||||
*
|
||||
|
@ -1059,13 +1063,11 @@ public abstract class FileChannel
|
|||
* {@code offset + size} overflows the range of {@code long}.
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
* If the {@code session} is not
|
||||
* {@linkplain SegmentScope#isAlive() alive}.
|
||||
* If {@code arena.isAlive() == false}.
|
||||
*
|
||||
* @throws WrongThreadException
|
||||
* If this method is called from a thread other than the thread
|
||||
* {@linkplain SegmentScope#isAccessibleBy(Thread) owning} the
|
||||
* {@code session}.
|
||||
* If {@code arena} is a confined scoped arena, and this method is called from a
|
||||
* thread {@code T}, other than the scoped arena's owner thread.
|
||||
*
|
||||
* @throws NonReadableChannelException
|
||||
* If the {@code mode} is {@link MapMode#READ_ONLY READ_ONLY} or
|
||||
|
@ -1087,7 +1089,7 @@ public abstract class FileChannel
|
|||
* @since 19
|
||||
*/
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
||||
public MemorySegment map(MapMode mode, long offset, long size, SegmentScope session)
|
||||
public MemorySegment map(MapMode mode, long offset, long size, Arena arena)
|
||||
throws IOException
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue