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:
Per Minborg 2023-04-27 09:00:58 +00:00
parent 41d58533ac
commit cbccc4c817
267 changed files with 6947 additions and 8029 deletions

View 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();
}

View file

@ -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();
}
}

View file

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

View file

@ -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);
}

View file

@ -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;
}
}
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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));
};
}
}

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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}

View file

@ -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

View file

@ -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

View file

@ -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();