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

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
@ -25,11 +25,7 @@
package jdk.internal.foreign;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentAllocator;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.ValueLayout;
import java.lang.foreign.*;
import java.lang.reflect.Array;
import java.nio.Buffer;
import java.nio.ByteBuffer;
@ -47,11 +43,14 @@ import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.foreign.UnmapperProxy;
import jdk.internal.misc.ScopedMemoryAccess;
import jdk.internal.misc.Unsafe;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import jdk.internal.util.ArraysSupport;
import jdk.internal.util.Preconditions;
import jdk.internal.vm.annotation.ForceInline;
@ -77,16 +76,16 @@ public abstract sealed class AbstractMemorySegmentImpl
final long length;
final boolean readOnly;
final SegmentScope scope;
final MemorySessionImpl scope;
@ForceInline
AbstractMemorySegmentImpl(long length, boolean readOnly, SegmentScope scope) {
AbstractMemorySegmentImpl(long length, boolean readOnly, MemorySessionImpl scope) {
this.length = length;
this.readOnly = readOnly;
this.scope = scope;
}
abstract AbstractMemorySegmentImpl dup(long offset, long size, boolean readOnly, SegmentScope scope);
abstract AbstractMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySessionImpl scope);
abstract ByteBuffer makeByteBuffer();
@ -112,6 +111,50 @@ public abstract sealed class AbstractMemorySegmentImpl
return asSliceNoCheck(offset, length - offset);
}
@Override
public MemorySegment asSlice(long offset, long newSize, long byteAlignment) {
checkBounds(offset, newSize);
if (!isAlignedForElement(offset, byteAlignment)) {
throw new IllegalArgumentException("Target offset incompatible with alignment constraints");
}
return asSliceNoCheck(offset, newSize);
}
@Override
@CallerSensitive
public final MemorySegment reinterpret(long newSize, Arena arena, Consumer<MemorySegment> cleanup) {
Objects.requireNonNull(arena);
return reinterpretInternal(Reflection.getCallerClass(), newSize,
MemorySessionImpl.toMemorySession(arena), null);
}
@Override
@CallerSensitive
public final MemorySegment reinterpret(long newSize) {
return reinterpretInternal(Reflection.getCallerClass(), newSize, scope, null);
}
@Override
@CallerSensitive
public final MemorySegment reinterpret(Arena arena, Consumer<MemorySegment> cleanup) {
Objects.requireNonNull(arena);
return reinterpretInternal(Reflection.getCallerClass(), byteSize(),
MemorySessionImpl.toMemorySession(arena), cleanup);
}
public MemorySegment reinterpretInternal(Class<?> callerClass, long newSize, Scope scope, Consumer<MemorySegment> cleanup) {
Reflection.ensureNativeAccess(callerClass, MemorySegment.class, "reinterpret");
if (newSize < 0) {
throw new IllegalArgumentException("newSize < 0");
}
if (!isNative()) throw new UnsupportedOperationException("Not a native segment");
Runnable action = cleanup != null ?
() -> cleanup.accept(NativeMemorySegmentImpl.makeNativeSegmentUnchecked(address(), newSize)) :
null;
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(address(), newSize,
(MemorySessionImpl)scope, action);
}
private AbstractMemorySegmentImpl asSliceNoCheck(long offset, long newSize) {
return dup(offset, newSize, readOnly, scope);
}
@ -148,7 +191,7 @@ public abstract sealed class AbstractMemorySegmentImpl
@Override
public MemorySegment allocate(long byteSize, long byteAlignment) {
Utils.checkAllocationSizeAndAlign(byteSize, byteAlignment);
return asSlice(0, byteSize);
return asSlice(0, byteSize, byteAlignment);
}
/**
@ -325,7 +368,12 @@ public abstract sealed class AbstractMemorySegmentImpl
@ForceInline
public final boolean isAlignedForElement(long offset, MemoryLayout layout) {
return (((unsafeGetOffset() + offset) | maxAlignMask()) & (layout.byteAlignment() - 1)) == 0;
return isAlignedForElement(offset, layout.byteAlignment());
}
@ForceInline
public final boolean isAlignedForElement(long offset, long byteAlignment) {
return (((unsafeGetOffset() + offset) | maxAlignMask()) & (byteAlignment - 1)) == 0;
}
private int checkArraySize(String typeName, int elemSize) {
@ -358,13 +406,18 @@ public abstract sealed class AbstractMemorySegmentImpl
}
@Override
public SegmentScope scope() {
public Scope scope() {
return scope;
}
@Override
public boolean isAccessibleBy(Thread thread) {
return sessionImpl().isAccessibleBy(thread);
}
@ForceInline
public final MemorySessionImpl sessionImpl() {
return (MemorySessionImpl)scope;
return scope;
}
private IndexOutOfBoundsException outOfBoundException(long offset, long length) {
@ -450,7 +503,7 @@ public abstract sealed class AbstractMemorySegmentImpl
@Override
public String toString() {
return "MemorySegment{ array: " + array() + " address:" + address() + " limit: " + length + " }";
return "MemorySegment{ heapBase: " + heapBase() + " address:" + address() + " limit: " + length + " }";
}
@Override
@ -481,29 +534,29 @@ public abstract sealed class AbstractMemorySegmentImpl
int size = limit - pos;
AbstractMemorySegmentImpl bufferSegment = (AbstractMemorySegmentImpl) NIO_ACCESS.bufferSegment(bb);
final SegmentScope bufferScope;
boolean readOnly = bb.isReadOnly();
int scaleFactor = getScaleFactor(bb);
final MemorySessionImpl bufferScope;
if (bufferSegment != null) {
bufferScope = bufferSegment.scope;
} else {
bufferScope = MemorySessionImpl.heapSession(bb);
}
boolean readOnly = bb.isReadOnly();
int scaleFactor = getScaleFactor(bb);
if (base != null) {
if (base instanceof byte[]) {
return new HeapMemorySegmentImpl.OfByte(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly);
return new HeapMemorySegmentImpl.OfByte(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly, bufferScope);
} else if (base instanceof short[]) {
return new HeapMemorySegmentImpl.OfShort(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly);
return new HeapMemorySegmentImpl.OfShort(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly, bufferScope);
} else if (base instanceof char[]) {
return new HeapMemorySegmentImpl.OfChar(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly);
return new HeapMemorySegmentImpl.OfChar(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly, bufferScope);
} else if (base instanceof int[]) {
return new HeapMemorySegmentImpl.OfInt(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly);
return new HeapMemorySegmentImpl.OfInt(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly, bufferScope);
} else if (base instanceof float[]) {
return new HeapMemorySegmentImpl.OfFloat(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly);
return new HeapMemorySegmentImpl.OfFloat(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly, bufferScope);
} else if (base instanceof long[]) {
return new HeapMemorySegmentImpl.OfLong(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly);
return new HeapMemorySegmentImpl.OfLong(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly, bufferScope);
} else if (base instanceof double[]) {
return new HeapMemorySegmentImpl.OfDouble(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly);
return new HeapMemorySegmentImpl.OfDouble(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly, bufferScope);
} else {
throw new AssertionError("Cannot get here");
}

View file

@ -25,10 +25,13 @@
*/
package jdk.internal.foreign;
import jdk.internal.foreign.abi.fallback.FallbackLinker;
import jdk.internal.vm.ForeignLinkerSupport;
import jdk.internal.util.OperatingSystem;
import jdk.internal.util.StaticProperty;
import static java.lang.foreign.ValueLayout.ADDRESS;
import static sun.security.action.GetPropertyAction.privilegedGetProperty;
public enum CABI {
SYS_V,
@ -36,51 +39,52 @@ public enum CABI {
LINUX_AARCH_64,
MAC_OS_AARCH_64,
WIN_AARCH_64,
LINUX_RISCV_64;
LINUX_RISCV_64,
FALLBACK,
UNSUPPORTED;
private static final CABI ABI;
private static final String ARCH;
private static final long ADDRESS_SIZE;
private static final CABI CURRENT = computeCurrent();
static {
ARCH = StaticProperty.osArch();
ADDRESS_SIZE = ADDRESS.bitSize();
// might be running in a 32-bit VM on a 64-bit platform.
// addressSize will be correctly 32
if ((ARCH.equals("amd64") || ARCH.equals("x86_64")) && ADDRESS_SIZE == 64) {
if (OperatingSystem.isWindows()) {
ABI = WIN_64;
} else {
ABI = SYS_V;
}
} else if (ARCH.equals("aarch64")) {
if (OperatingSystem.isMacOS()) {
ABI = MAC_OS_AARCH_64;
} else if (OperatingSystem.isWindows()) {
ABI = WIN_AARCH_64;
} else {
// The Linux ABI follows the standard AAPCS ABI
ABI = LINUX_AARCH_64;
}
} else if (ARCH.equals("riscv64")) {
if (OperatingSystem.isLinux()) {
ABI = LINUX_RISCV_64;
} else {
// unsupported
ABI = null;
}
} else {
// unsupported
ABI = null;
private static CABI computeCurrent() {
String abi = privilegedGetProperty("jdk.internal.foreign.CABI");
if (abi != null) {
return CABI.valueOf(abi);
}
if (ForeignLinkerSupport.isSupported()) {
// figure out the ABI based on the platform
String arch = StaticProperty.osArch();
long addressSize = ADDRESS.bitSize();
// might be running in a 32-bit VM on a 64-bit platform.
// addressSize will be correctly 32
if ((arch.equals("amd64") || arch.equals("x86_64")) && addressSize == 64) {
if (OperatingSystem.isWindows()) {
return WIN_64;
} else {
return SYS_V;
}
} else if (arch.equals("aarch64")) {
if (OperatingSystem.isMacOS()) {
return MAC_OS_AARCH_64;
} else if (OperatingSystem.isWindows()) {
return WIN_AARCH_64;
} else {
// The Linux ABI follows the standard AAPCS ABI
return LINUX_AARCH_64;
}
} else if (arch.equals("riscv64")) {
if (OperatingSystem.isLinux()) {
return LINUX_RISCV_64;
}
}
} else if (FallbackLinker.isSupported()) {
return FALLBACK; // fallback linker
}
return UNSUPPORTED;
}
public static CABI current() {
if (ABI == null) {
throw new UnsupportedOperationException(
"Unsupported os, arch, or address size: " + OperatingSystem.current() +
", " + ARCH + ", " + ADDRESS_SIZE);
}
return ABI;
return CURRENT;
}
}

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
@ -27,7 +27,6 @@
package jdk.internal.foreign;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentScope;
import java.nio.ByteBuffer;
import java.util.Objects;
import java.util.Optional;
@ -46,7 +45,7 @@ import jdk.internal.vm.annotation.ForceInline;
* the field type storing the 'base' coordinate is just Object; similarly, all the constructor in the subclasses
* accept an Object 'base' parameter instead of a sharper type (e.g. {@code byte[]}). This is deliberate, as
* using sharper types would require use of type-conversions, which in turn would inhibit some C2 optimizations,
* such as the elimination of store barriers in methods like {@link HeapMemorySegmentImpl#dup(long, long, boolean, SegmentScope)}.
* such as the elimination of store barriers in methods like {@link HeapMemorySegmentImpl#dup(long, long, boolean, MemorySessionImpl)}.
*/
public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl {
@ -62,13 +61,15 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
final Object base;
@Override
public Optional<Object> array() {
return Optional.of(base);
public Optional<Object> heapBase() {
return readOnly ?
Optional.empty() :
Optional.of(base);
}
@ForceInline
HeapMemorySegmentImpl(long offset, Object base, long length, boolean readOnly) {
super(length, readOnly, SegmentScope.global());
HeapMemorySegmentImpl(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) {
super(length, readOnly, session);
this.offset = offset;
this.base = base;
}
@ -79,28 +80,28 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
}
@Override
abstract HeapMemorySegmentImpl dup(long offset, long size, boolean readOnly, SegmentScope scope);
abstract HeapMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySessionImpl scope);
@Override
ByteBuffer makeByteBuffer() {
if (!(base instanceof byte[])) {
if (!(base instanceof byte[] baseByte)) {
throw new UnsupportedOperationException("Not an address to an heap-allocated byte array");
}
JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
return nioAccess.newHeapByteBuffer((byte[])base, (int)offset - BYTE_ARR_BASE, (int) byteSize(), null);
return nioAccess.newHeapByteBuffer(baseByte, (int)offset - BYTE_ARR_BASE, (int) byteSize(), null);
}
// factories
public static final class OfByte extends HeapMemorySegmentImpl {
OfByte(long offset, Object base, long length, boolean readOnly) {
super(offset, base, length, readOnly);
OfByte(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) {
super(offset, base, length, readOnly, session);
}
@Override
OfByte dup(long offset, long size, boolean readOnly, SegmentScope scope) {
return new OfByte(this.offset + offset, base, size, readOnly);
OfByte dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) {
return new OfByte(this.offset + offset, base, size, readOnly, scope);
}
@Override
@ -111,7 +112,8 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
public static MemorySegment fromArray(byte[] arr) {
Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_BYTE_INDEX_SCALE;
return new OfByte(Unsafe.ARRAY_BYTE_BASE_OFFSET, arr, byteSize, false);
return new OfByte(Unsafe.ARRAY_BYTE_BASE_OFFSET, arr, byteSize, false,
MemorySessionImpl.heapSession(arr));
}
@Override
@ -127,13 +129,13 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
public static final class OfChar extends HeapMemorySegmentImpl {
OfChar(long offset, Object base, long length, boolean readOnly) {
super(offset, base, length, readOnly);
OfChar(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) {
super(offset, base, length, readOnly, session);
}
@Override
OfChar dup(long offset, long size, boolean readOnly, SegmentScope scope) {
return new OfChar(this.offset + offset, base, size, readOnly);
OfChar dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) {
return new OfChar(this.offset + offset, base, size, readOnly, scope);
}
@Override
@ -144,7 +146,8 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
public static MemorySegment fromArray(char[] arr) {
Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_CHAR_INDEX_SCALE;
return new OfChar(Unsafe.ARRAY_CHAR_BASE_OFFSET, arr, byteSize, false);
return new OfChar(Unsafe.ARRAY_CHAR_BASE_OFFSET, arr, byteSize, false,
MemorySessionImpl.heapSession(arr));
}
@Override
@ -160,13 +163,13 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
public static final class OfShort extends HeapMemorySegmentImpl {
OfShort(long offset, Object base, long length, boolean readOnly) {
super(offset, base, length, readOnly);
OfShort(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) {
super(offset, base, length, readOnly, session);
}
@Override
OfShort dup(long offset, long size, boolean readOnly, SegmentScope scope) {
return new OfShort(this.offset + offset, base, size, readOnly);
OfShort dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) {
return new OfShort(this.offset + offset, base, size, readOnly, scope);
}
@Override
@ -177,7 +180,8 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
public static MemorySegment fromArray(short[] arr) {
Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_SHORT_INDEX_SCALE;
return new OfShort(Unsafe.ARRAY_SHORT_BASE_OFFSET, arr, byteSize, false);
return new OfShort(Unsafe.ARRAY_SHORT_BASE_OFFSET, arr, byteSize, false,
MemorySessionImpl.heapSession(arr));
}
@Override
@ -193,13 +197,13 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
public static final class OfInt extends HeapMemorySegmentImpl {
OfInt(long offset, Object base, long length, boolean readOnly) {
super(offset, base, length, readOnly);
OfInt(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) {
super(offset, base, length, readOnly, session);
}
@Override
OfInt dup(long offset, long size, boolean readOnly, SegmentScope scope) {
return new OfInt(this.offset + offset, base, size, readOnly);
OfInt dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) {
return new OfInt(this.offset + offset, base, size, readOnly, scope);
}
@Override
@ -210,7 +214,8 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
public static MemorySegment fromArray(int[] arr) {
Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_INT_INDEX_SCALE;
return new OfInt(Unsafe.ARRAY_INT_BASE_OFFSET, arr, byteSize, false);
return new OfInt(Unsafe.ARRAY_INT_BASE_OFFSET, arr, byteSize, false,
MemorySessionImpl.heapSession(arr));
}
@Override
@ -226,13 +231,13 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
public static final class OfLong extends HeapMemorySegmentImpl {
OfLong(long offset, Object base, long length, boolean readOnly) {
super(offset, base, length, readOnly);
OfLong(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) {
super(offset, base, length, readOnly, session);
}
@Override
OfLong dup(long offset, long size, boolean readOnly, SegmentScope scope) {
return new OfLong(this.offset + offset, base, size, readOnly);
OfLong dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) {
return new OfLong(this.offset + offset, base, size, readOnly, scope);
}
@Override
@ -243,7 +248,8 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
public static MemorySegment fromArray(long[] arr) {
Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_LONG_INDEX_SCALE;
return new OfLong(Unsafe.ARRAY_LONG_BASE_OFFSET, arr, byteSize, false);
return new OfLong(Unsafe.ARRAY_LONG_BASE_OFFSET, arr, byteSize, false,
MemorySessionImpl.heapSession(arr));
}
@Override
@ -259,13 +265,13 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
public static final class OfFloat extends HeapMemorySegmentImpl {
OfFloat(long offset, Object base, long length, boolean readOnly) {
super(offset, base, length, readOnly);
OfFloat(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) {
super(offset, base, length, readOnly, session);
}
@Override
OfFloat dup(long offset, long size, boolean readOnly, SegmentScope scope) {
return new OfFloat(this.offset + offset, base, size, readOnly);
OfFloat dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) {
return new OfFloat(this.offset + offset, base, size, readOnly, scope);
}
@Override
@ -276,7 +282,8 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
public static MemorySegment fromArray(float[] arr) {
Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_FLOAT_INDEX_SCALE;
return new OfFloat(Unsafe.ARRAY_FLOAT_BASE_OFFSET, arr, byteSize, false);
return new OfFloat(Unsafe.ARRAY_FLOAT_BASE_OFFSET, arr, byteSize, false,
MemorySessionImpl.heapSession(arr));
}
@Override
@ -292,13 +299,13 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
public static final class OfDouble extends HeapMemorySegmentImpl {
OfDouble(long offset, Object base, long length, boolean readOnly) {
super(offset, base, length, readOnly);
OfDouble(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) {
super(offset, base, length, readOnly, session);
}
@Override
OfDouble dup(long offset, long size, boolean readOnly, SegmentScope scope) {
return new OfDouble(this.offset + offset, base, size, readOnly);
OfDouble dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) {
return new OfDouble(this.offset + offset, base, size, readOnly, scope);
}
@Override
@ -309,7 +316,8 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
public static MemorySegment fromArray(double[] arr) {
Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE;
return new OfDouble(Unsafe.ARRAY_DOUBLE_BASE_OFFSET, arr, byteSize, false);
return new OfDouble(Unsafe.ARRAY_DOUBLE_BASE_OFFSET, arr, byteSize, false,
MemorySessionImpl.heapSession(arr));
}
@Override

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
@ -27,6 +27,7 @@ package jdk.internal.foreign;
import jdk.internal.vm.annotation.ForceInline;
import java.lang.foreign.AddressLayout;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
@ -53,9 +54,11 @@ public class LayoutPath {
private static final long[] EMPTY_STRIDES = new long[0];
private static final long[] EMPTY_BOUNDS = new long[0];
private static final MethodHandle[] EMPTY_DEREF_HANDLES = new MethodHandle[0];
private static final MethodHandle MH_ADD_SCALED_OFFSET;
private static final MethodHandle MH_SLICE;
private static final MethodHandle MH_SEGMENT_RESIZE;
static {
try {
@ -64,6 +67,8 @@ public class LayoutPath {
MethodType.methodType(long.class, long.class, long.class, long.class, long.class));
MH_SLICE = lookup.findVirtual(MemorySegment.class, "asSlice",
MethodType.methodType(MemorySegment.class, long.class, long.class));
MH_SEGMENT_RESIZE = lookup.findStatic(LayoutPath.class, "resizeSegment",
MethodType.methodType(MemorySegment.class, MemorySegment.class, MemoryLayout.class));
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
@ -75,12 +80,14 @@ public class LayoutPath {
private final long[] strides;
private final long[] bounds;
private final MethodHandle[] derefAdapters;
private LayoutPath(MemoryLayout layout, long offset, long[] strides, long[] bounds, LayoutPath enclosing) {
private LayoutPath(MemoryLayout layout, long offset, long[] strides, long[] bounds, MethodHandle[] derefAdapters, LayoutPath enclosing) {
this.layout = layout;
this.offset = offset;
this.strides = strides;
this.bounds = bounds;
this.derefAdapters = derefAdapters;
this.enclosing = enclosing;
}
@ -90,7 +97,7 @@ public class LayoutPath {
check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
SequenceLayout seq = (SequenceLayout)layout;
MemoryLayout elem = seq.elementLayout();
return LayoutPath.nestedPath(elem, offset, addStride(elem.bitSize()), addBound(seq.elementCount()), this);
return LayoutPath.nestedPath(elem, offset, addStride(elem.bitSize()), addBound(seq.elementCount()), derefAdapters, this);
}
public LayoutPath sequenceElement(long start, long step) {
@ -104,7 +111,7 @@ public class LayoutPath {
start + 1;
long maxIndex = Math.ceilDiv(nelems, Math.abs(step));
return LayoutPath.nestedPath(elem, offset + (start * elemSize),
addStride(elemSize * step), addBound(maxIndex), this);
addStride(elemSize * step), addBound(maxIndex), derefAdapters, this);
}
public LayoutPath sequenceElement(long index) {
@ -113,7 +120,7 @@ public class LayoutPath {
checkSequenceBounds(seq, index);
long elemSize = seq.elementLayout().bitSize();
long elemOffset = elemSize * index;
return LayoutPath.nestedPath(seq.elementLayout(), offset + elemOffset, strides, bounds, this);
return LayoutPath.nestedPath(seq.elementLayout(), offset + elemOffset, strides, bounds, derefAdapters,this);
}
public LayoutPath groupElement(String name) {
@ -134,7 +141,41 @@ public class LayoutPath {
if (elem == null) {
throw badLayoutPath("cannot resolve '" + name + "' in layout " + layout);
}
return LayoutPath.nestedPath(elem, this.offset + offset, strides, bounds, this);
return LayoutPath.nestedPath(elem, this.offset + offset, strides, bounds, derefAdapters, this);
}
public LayoutPath groupElement(long index) {
check(GroupLayout.class, "attempting to select a group element from a non-group layout");
GroupLayout g = (GroupLayout)layout;
long elemSize = g.memberLayouts().size();
long offset = 0;
MemoryLayout elem = null;
for (int i = 0; i <= index; i++) {
if (i == elemSize) {
throw badLayoutPath("cannot resolve element " + index + " in layout " + layout);
}
elem = g.memberLayouts().get(i);
if (g instanceof StructLayout && i < index) {
offset += elem.bitSize();
}
}
return LayoutPath.nestedPath(elem, this.offset + offset, strides, bounds, derefAdapters, this);
}
public LayoutPath derefElement() {
if (!(layout instanceof AddressLayout addressLayout) ||
addressLayout.targetLayout().isEmpty()) {
throw badLayoutPath("Cannot dereference layout: " + layout);
}
MemoryLayout derefLayout = addressLayout.targetLayout().get();
MethodHandle handle = dereferenceHandle(false).toMethodHandle(VarHandle.AccessMode.GET);
handle = MethodHandles.filterReturnValue(handle,
MethodHandles.insertArguments(MH_SEGMENT_RESIZE, 1, derefLayout));
return derefPath(derefLayout, handle, this);
}
private static MemorySegment resizeSegment(MemorySegment segment, MemoryLayout layout) {
return Utils.longToAddress(segment.address(), layout.byteSize(), layout.byteAlignment());
}
// Layout path projections
@ -144,22 +185,31 @@ public class LayoutPath {
}
public VarHandle dereferenceHandle() {
return dereferenceHandle(true);
}
public VarHandle dereferenceHandle(boolean adapt) {
if (!(layout instanceof ValueLayout valueLayout)) {
throw new IllegalArgumentException("Path does not select a value layout");
}
checkAlignment(this);
VarHandle handle = Utils.makeSegmentViewVarHandle(valueLayout);
for (int i = strides.length - 1; i >= 0; i--) {
MethodHandle collector = MethodHandles.insertArguments(MH_ADD_SCALED_OFFSET, 2,
Utils.bitsToBytesOrThrow(strides[i], IllegalArgumentException::new),
Utils.bitsToBytes(strides[i]),
bounds[i]);
// (J, ...) -> J to (J, J, ...) -> J
// i.e. new coord is prefixed. Last coord will correspond to innermost layout
handle = MethodHandles.collectCoordinates(handle, 1, collector);
}
handle = MethodHandles.insertCoordinates(handle, 1,
Utils.bitsToBytesOrThrow(offset, IllegalArgumentException::new));
Utils.bitsToBytes(offset));
if (adapt) {
for (int i = derefAdapters.length; i > 0; i--) {
handle = MethodHandles.collectCoordinates(handle, 0, derefAdapters[i - 1]);
}
}
return handle;
}
@ -182,13 +232,8 @@ public class LayoutPath {
}
public MethodHandle sliceHandle() {
if (strides.length == 0) {
// trigger checks eagerly
Utils.bitsToBytesOrThrow(offset, Utils.BITS_TO_BYTES_THROW_OFFSET);
}
MethodHandle offsetHandle = offsetHandle(); // bit offset
offsetHandle = MethodHandles.filterReturnValue(offsetHandle, Utils.MH_BITS_TO_BYTES_OR_THROW_FOR_OFFSET); // byte offset
offsetHandle = MethodHandles.filterReturnValue(offsetHandle, Utils.BITS_TO_BYTES); // byte offset
MethodHandle sliceHandle = MH_SLICE; // (MS, long, long) -> MS
sliceHandle = MethodHandles.insertArguments(sliceHandle, 2, layout.byteSize()); // (MS, long) -> MS
@ -204,11 +249,17 @@ public class LayoutPath {
// Layout path construction
public static LayoutPath rootPath(MemoryLayout layout) {
return new LayoutPath(layout, 0L, EMPTY_STRIDES, EMPTY_BOUNDS, null);
return new LayoutPath(layout, 0L, EMPTY_STRIDES, EMPTY_BOUNDS, EMPTY_DEREF_HANDLES, null);
}
private static LayoutPath nestedPath(MemoryLayout layout, long offset, long[] strides, long[] bounds, LayoutPath encl) {
return new LayoutPath(layout, offset, strides, bounds, encl);
private static LayoutPath nestedPath(MemoryLayout layout, long offset, long[] strides, long[] bounds, MethodHandle[] derefAdapters, LayoutPath encl) {
return new LayoutPath(layout, offset, strides, bounds, derefAdapters, encl);
}
private static LayoutPath derefPath(MemoryLayout layout, MethodHandle handle, LayoutPath encl) {
MethodHandle[] handles = Arrays.copyOf(encl.derefAdapters, encl.derefAdapters.length + 1);
handles[encl.derefAdapters.length] = handle;
return new LayoutPath(layout, 0L, EMPTY_STRIDES, EMPTY_BOUNDS, handles, null);
}
// Helper methods
@ -229,26 +280,6 @@ public class LayoutPath {
return new IllegalArgumentException("Bad layout path: " + cause);
}
private static void checkAlignment(LayoutPath path) {
MemoryLayout layout = path.layout;
long alignment = layout.bitAlignment();
if (!Utils.isAligned(path.offset, alignment)) {
throw new UnsupportedOperationException("Invalid alignment requirements for layout " + layout);
}
for (long stride : path.strides) {
if (!Utils.isAligned(stride, alignment)) {
throw new UnsupportedOperationException("Alignment requirements for layout " + layout + " do not match stride " + stride);
}
}
LayoutPath encl = path.enclosing;
if (encl != null) {
if (encl.layout.bitAlignment() < alignment) {
throw new UnsupportedOperationException("Alignment requirements for layout " + layout + " do not match those for enclosing layout " + encl.layout);
}
checkAlignment(encl);
}
}
private long[] addStride(long stride) {
long[] newStrides = Arrays.copyOf(strides, strides.length + 1);
newStrides[strides.length] = stride;
@ -271,7 +302,8 @@ public class LayoutPath {
SEQUENCE_ELEMENT("unbound sequence element"),
SEQUENCE_ELEMENT_INDEX("bound sequence element"),
SEQUENCE_RANGE("sequence range"),
GROUP_ELEMENT("group element");
GROUP_ELEMENT("group element"),
DEREF_ELEMENT("dereference element");
final String description;

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
@ -25,8 +25,6 @@
package jdk.internal.foreign;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentScope;
import java.nio.ByteBuffer;
import jdk.internal.access.foreign.UnmapperProxy;
import jdk.internal.misc.ScopedMemoryAccess;
@ -37,13 +35,13 @@ import jdk.internal.misc.ScopedMemoryAccess;
* memory mapped segment, such as the file descriptor associated with the mapping. This information is crucial
* in order to correctly reconstruct a byte buffer object from the segment (see {@link #makeByteBuffer()}).
*/
public sealed class MappedMemorySegmentImpl extends NativeMemorySegmentImpl {
public final class MappedMemorySegmentImpl extends NativeMemorySegmentImpl {
private final UnmapperProxy unmapper;
static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess();
public MappedMemorySegmentImpl(long min, UnmapperProxy unmapper, long length, boolean readOnly, SegmentScope scope) {
public MappedMemorySegmentImpl(long min, UnmapperProxy unmapper, long length, boolean readOnly, MemorySessionImpl scope) {
super(min, length, readOnly, scope);
this.unmapper = unmapper;
}
@ -55,7 +53,7 @@ public sealed class MappedMemorySegmentImpl extends NativeMemorySegmentImpl {
}
@Override
MappedMemorySegmentImpl dup(long offset, long size, boolean readOnly, SegmentScope scope) {
MappedMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) {
return new MappedMemorySegmentImpl(min + offset, unmapper, size, readOnly, scope);
}
@ -73,50 +71,26 @@ public sealed class MappedMemorySegmentImpl extends NativeMemorySegmentImpl {
// support for mapped segments
public MemorySegment segment() {
return MappedMemorySegmentImpl.this;
}
public void load() {
SCOPED_MEMORY_ACCESS.load(sessionImpl(), min, unmapper.isSync(), length);
if (unmapper != null) {
SCOPED_MEMORY_ACCESS.load(sessionImpl(), min, unmapper.isSync(), length);
}
}
public void unload() {
SCOPED_MEMORY_ACCESS.unload(sessionImpl(), min, unmapper.isSync(), length);
if (unmapper != null) {
SCOPED_MEMORY_ACCESS.unload(sessionImpl(), min, unmapper.isSync(), length);
}
}
public boolean isLoaded() {
return SCOPED_MEMORY_ACCESS.isLoaded(sessionImpl(), min, unmapper.isSync(), length);
return unmapper == null || SCOPED_MEMORY_ACCESS.isLoaded(sessionImpl(), min, unmapper.isSync(), length);
}
public void force() {
SCOPED_MEMORY_ACCESS.force(sessionImpl(), unmapper.fileDescriptor(), min, unmapper.isSync(), 0, length);
}
public static final class EmptyMappedMemorySegmentImpl extends MappedMemorySegmentImpl {
public EmptyMappedMemorySegmentImpl(boolean readOnly, MemorySessionImpl session) {
super(0, null, 0, readOnly, session);
}
@Override
public void load() {
// do nothing
}
@Override
public void unload() {
// do nothing
}
@Override
public boolean isLoaded() {
return true;
}
@Override
public void force() {
// do nothing
if (unmapper != null) {
SCOPED_MEMORY_ACCESS.force(sessionImpl(), unmapper.fileDescriptor(), min, unmapper.isSync(), 0, length);
}
}
}

View file

@ -26,10 +26,9 @@
package jdk.internal.foreign;
import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.SegmentAllocator;
import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment.Scope;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.ref.Cleaner;
@ -51,7 +50,7 @@ import jdk.internal.vm.annotation.ForceInline;
* access is possible when a session is being closed (see {@link jdk.internal.misc.ScopedMemoryAccess}).
*/
public abstract sealed class MemorySessionImpl
implements SegmentScope, SegmentAllocator
implements Scope
permits ConfinedSession, GlobalSession, SharedSession {
static final int OPEN = 0;
static final int CLOSING = -1;
@ -80,7 +79,7 @@ public abstract sealed class MemorySessionImpl
public Arena asArena() {
return new Arena() {
@Override
public SegmentScope scope() {
public Scope scope() {
return MemorySessionImpl.this;
}
@ -88,16 +87,20 @@ public abstract sealed class MemorySessionImpl
public void close() {
MemorySessionImpl.this.close();
}
@Override
public boolean isCloseableBy(Thread thread) {
Objects.requireNonNull(thread);
return ownerThread() == null || // shared
ownerThread() == thread;
}
};
}
@ForceInline
public static final MemorySessionImpl toMemorySession(Arena arena) {
return (MemorySessionImpl) arena.scope();
}
public final boolean isCloseableBy(Thread thread) {
Objects.requireNonNull(thread);
return isCloseable() &&
(owner == null || owner == thread);
}
public void addCloseAction(Runnable runnable) {
Objects.requireNonNull(runnable);
addInternal(ResourceList.ResourceCleanup.ofRunnable(runnable));
@ -150,7 +153,6 @@ public abstract sealed class MemorySessionImpl
return new ImplicitSession(cleaner);
}
@Override
public MemorySegment allocate(long byteSize, long byteAlignment) {
Utils.checkAllocationSizeAndAlign(byteSize, byteAlignment);
return NativeMemorySegmentImpl.makeNativeSegment(byteSize, byteAlignment, this);
@ -160,7 +162,6 @@ public abstract sealed class MemorySessionImpl
public abstract void acquire0();
@Override
public void whileAlive(Runnable action) {
Objects.requireNonNull(action);
acquire0();
@ -175,12 +176,6 @@ public abstract sealed class MemorySessionImpl
return owner;
}
public static boolean sameOwnerThread(SegmentScope scope1, SegmentScope scope2) {
return ((MemorySessionImpl) scope1).ownerThread() ==
((MemorySessionImpl) scope2).ownerThread();
}
@Override
public final boolean isAccessibleBy(Thread thread) {
Objects.requireNonNull(thread);
return owner == null || owner == thread;
@ -225,6 +220,10 @@ public abstract sealed class MemorySessionImpl
}
}
public static final void checkValidState(MemorySegment segment) {
((AbstractMemorySegmentImpl)segment).sessionImpl().checkValidState();
}
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();

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
@ -27,7 +27,6 @@
package jdk.internal.foreign;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentScope;
import java.nio.ByteBuffer;
import java.util.Optional;
@ -52,9 +51,24 @@ public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl pe
final long min;
@ForceInline
NativeMemorySegmentImpl(long min, long length, boolean readOnly, SegmentScope scope) {
NativeMemorySegmentImpl(long min, long length, boolean readOnly, MemorySessionImpl scope) {
super(length, readOnly, scope);
this.min = min;
this.min = (Unsafe.getUnsafe().addressSize() == 4)
// On 32-bit systems, normalize the upper unused 32-bits to zero
? min & 0x0000_0000_FFFF_FFFFL
// On 64-bit systems, all the bits are used
: min;
}
/**
* This constructor should only be used when initializing {@link MemorySegment#NULL}. Note: because of the memory
* segment class hierarchy, it is possible to end up in a situation where this constructor is called
* when the static fields in this class are not yet initialized.
*/
@ForceInline
public NativeMemorySegmentImpl() {
super(0L, false, new GlobalSession(null));
this.min = 0L;
}
@Override
@ -63,13 +77,13 @@ public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl pe
}
@Override
public Optional<Object> array() {
public Optional<Object> heapBase() {
return Optional.empty();
}
@ForceInline
@Override
NativeMemorySegmentImpl dup(long offset, long size, boolean readOnly, SegmentScope scope) {
NativeMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) {
return new NativeMemorySegmentImpl(min + offset, size, readOnly, scope);
}
@ -101,8 +115,7 @@ public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl pe
// factories
public static MemorySegment makeNativeSegment(long byteSize, long byteAlignment, SegmentScope scope) {
MemorySessionImpl sessionImpl = (MemorySessionImpl) scope;
public static MemorySegment makeNativeSegment(long byteSize, long byteAlignment, MemorySessionImpl sessionImpl) {
sessionImpl.checkValidState();
if (VM.isDirectMemoryPageAligned()) {
byteAlignment = Math.max(byteAlignment, NIO_ACCESS.pageSize());
@ -119,7 +132,7 @@ public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl pe
}
long alignedBuf = Utils.alignUp(buf, byteAlignment);
AbstractMemorySegmentImpl segment = new NativeMemorySegmentImpl(buf, alignedSize,
false, scope);
false, sessionImpl);
sessionImpl.addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() {
@Override
public void cleanup() {
@ -138,25 +151,23 @@ public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl pe
// associated with MemorySegment::ofAddress.
@ForceInline
public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, SegmentScope scope, Runnable action) {
MemorySessionImpl sessionImpl = (MemorySessionImpl) scope;
public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, MemorySessionImpl sessionImpl, Runnable action) {
if (action == null) {
sessionImpl.checkValidState();
} else {
sessionImpl.addCloseAction(action);
}
return new NativeMemorySegmentImpl(min, byteSize, false, scope);
return new NativeMemorySegmentImpl(min, byteSize, false, sessionImpl);
}
@ForceInline
public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, SegmentScope scope) {
MemorySessionImpl sessionImpl = (MemorySessionImpl) scope;
public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, MemorySessionImpl sessionImpl) {
sessionImpl.checkValidState();
return new NativeMemorySegmentImpl(min, byteSize, false, scope);
return new NativeMemorySegmentImpl(min, byteSize, false, sessionImpl);
}
@ForceInline
public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize) {
return new NativeMemorySegmentImpl(min, byteSize, false, SegmentScope.global());
return new NativeMemorySegmentImpl(min, byteSize, false, new GlobalSession(null));
}
}

View file

@ -1,269 +0,0 @@
/*
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package jdk.internal.foreign;
import java.lang.foreign.ValueLayout;
public final class PlatformLayouts {
private PlatformLayouts() {
//just the one
}
/**
* This class defines layout constants modelling standard primitive types supported by the x64 SystemV ABI.
*/
public static final class SysV {
private SysV() {
//just the one
}
/**
* The {@code bool} native type.
*/
public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN;
/**
* The {@code char} native type.
*/
public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE;
/**
* The {@code short} native type.
*/
public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT.withBitAlignment(16);
/**
* The {@code int} native type.
*/
public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT.withBitAlignment(32);
/**
* The {@code long} native type.
*/
public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64);
/**
* The {@code long long} native type.
*/
public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64);
/**
* The {@code float} native type.
*/
public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT.withBitAlignment(32);
/**
* The {@code double} native type.
*/
public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE.withBitAlignment(64);
/**
* The {@code T*} native type.
*/
public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64).asUnbounded();
/**
* The {@code va_list} native type, as it is passed to a function.
*/
public static final ValueLayout.OfAddress C_VA_LIST = SysV.C_POINTER;
}
/**
* This class defines layout constants modelling standard primitive types supported by the x64 Windows ABI.
*/
public static final class Win64 {
private Win64() {
//just the one
}
/**
* The {@code bool} native type.
*/
public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN;
/**
* The {@code char} native type.
*/
public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE;
/**
* The {@code short} native type.
*/
public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT.withBitAlignment(16);
/**
* The {@code int} native type.
*/
public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT.withBitAlignment(32);
/**
* The {@code long} native type.
*/
public static final ValueLayout.OfInt C_LONG = ValueLayout.JAVA_INT.withBitAlignment(32);
/**
* The {@code long long} native type.
*/
public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64);
/**
* The {@code float} native type.
*/
public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT.withBitAlignment(32);
/**
* The {@code double} native type.
*/
public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE.withBitAlignment(64);
/**
* The {@code T*} native type.
*/
public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64).asUnbounded();
/**
* The {@code va_list} native type, as it is passed to a function.
*/
public static final ValueLayout.OfAddress C_VA_LIST = Win64.C_POINTER;
}
/**
* This class defines layout constants modelling standard primitive types supported by the AArch64 ABI.
*/
public static final class AArch64 {
private AArch64() {
//just the one
}
/**
* The {@code bool} native type.
*/
public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN;
/**
* The {@code char} native type.
*/
public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE;
/**
* The {@code short} native type.
*/
public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT.withBitAlignment(16);
/**
* The {@code int} native type.
*/
public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT.withBitAlignment(32);
/**
* The {@code long} native type.
*/
public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64);
/**
* The {@code long long} native type.
*/
public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64);
/**
* The {@code float} native type.
*/
public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT.withBitAlignment(32);
/**
* The {@code double} native type.
*/
public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE.withBitAlignment(64);
/**
* The {@code T*} native type.
*/
public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64).asUnbounded();
/**
* The {@code va_list} native type, as it is passed to a function.
*/
public static final ValueLayout.OfAddress C_VA_LIST = AArch64.C_POINTER;
}
public static final class RISCV64 {
private RISCV64() {
//just the one
}
/**
* The {@code bool} native type.
*/
public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN;
/**
* The {@code char} native type.
*/
public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE;
/**
* The {@code short} native type.
*/
public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT.withBitAlignment(16);
/**
* The {@code int} native type.
*/
public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT.withBitAlignment(32);
/**
* The {@code long} native type.
*/
public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64);
/**
* The {@code long long} native type.
*/
public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64);
/**
* The {@code float} native type.
*/
public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT.withBitAlignment(32);
/**
* The {@code double} native type.
*/
public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE.withBitAlignment(64);
/**
* The {@code T*} native type.
*/
public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64).asUnbounded();
/**
* The {@code va_list} native type, as it is passed to a function.
*/
public static final ValueLayout.OfAddress C_VA_LIST = RISCV64.C_POINTER;
}
}

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
@ -43,14 +43,14 @@ public final class SlicingAllocator implements SegmentAllocator {
MemorySegment trySlice(long byteSize, long byteAlignment) {
long min = segment.address();
long start = Utils.alignUp(min + sp, byteAlignment) - min;
MemorySegment slice = segment.asSlice(start, byteSize);
MemorySegment slice = segment.asSlice(start, byteSize, byteAlignment);
sp = start + byteSize;
return slice;
}
@Override
public MemorySegment allocate(long byteSize, long byteAlignment) {
Utils.checkAllocationSizeAndAlign(byteSize, byteAlignment, maxAlign);
Utils.checkAllocationSizeAndAlign(byteSize, byteAlignment);
// try to slice from current segment first...
return trySlice(byteSize, byteAlignment);
}

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
@ -25,9 +25,7 @@
package jdk.internal.foreign;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.SymbolLookup;
import java.lang.foreign.*;
import java.lang.invoke.MethodHandles;
import java.nio.file.Files;
import java.nio.file.Path;
@ -47,7 +45,10 @@ public final class SystemLookup implements SymbolLookup {
private static final SystemLookup INSTANCE = new SystemLookup();
/* A fallback lookup, used when creation of system lookup fails. */
private static final SymbolLookup FALLBACK_LOOKUP = name -> Optional.empty();
private static final SymbolLookup FALLBACK_LOOKUP = name -> {
Objects.requireNonNull(name);
return Optional.empty();
};
/*
* On POSIX systems, dlsym will allow us to lookup symbol in library dependencies; the same trick doesn't work
@ -57,10 +58,11 @@ public final class SystemLookup implements SymbolLookup {
private static SymbolLookup makeSystemLookup() {
try {
return switch (CABI.current()) {
case SYS_V, LINUX_AARCH_64, MAC_OS_AARCH_64, LINUX_RISCV_64 -> libLookup(libs -> libs.load(jdkLibraryPath("syslookup")));
case WIN_64, WIN_AARCH_64 -> makeWindowsLookup(); // out of line to workaround javac crash
};
if (Utils.IS_WINDOWS) {
return makeWindowsLookup();
} else {
return libLookup(libs -> libs.load(jdkLibraryPath("syslookup")));
}
} catch (Throwable ex) {
// This can happen in the event of a library loading failure - e.g. if one of the libraries the
// system lookup depends on cannot be loaded for some reason. In such extreme cases, rather than
@ -84,15 +86,17 @@ public final class SystemLookup implements SymbolLookup {
SymbolLookup fallbackLibLookup =
libLookup(libs -> libs.load(jdkLibraryPath("syslookup")));
int numSymbols = WindowsFallbackSymbols.values().length;
MemorySegment funcs = MemorySegment.ofAddress(fallbackLibLookup.find("funcs").orElseThrow().address(),
ADDRESS.byteSize() * numSymbols, SegmentScope.global());
MemorySegment funcs = fallbackLibLookup.find("funcs").orElseThrow()
.reinterpret(WindowsFallbackSymbols.LAYOUT.byteSize());
Function<String, Optional<MemorySegment>> fallbackLookup = name -> Optional.ofNullable(WindowsFallbackSymbols.valueOfOrNull(name))
.map(symbol -> MemorySegment.ofAddress(funcs.getAtIndex(ADDRESS, symbol.ordinal()).address(), 0L, SegmentScope.global()));
.map(symbol -> funcs.getAtIndex(ADDRESS, symbol.ordinal()));
final SymbolLookup finalLookup = lookup;
lookup = name -> finalLookup.find(name).or(() -> fallbackLookup.apply(name));
lookup = name -> {
Objects.requireNonNull(name);
return finalLookup.find(name).or(() -> fallbackLookup.apply(name));
};
}
return lookup;
@ -106,7 +110,7 @@ public final class SystemLookup implements SymbolLookup {
long addr = lib.lookup(name);
return addr == 0 ?
Optional.empty() :
Optional.of(MemorySegment.ofAddress(addr, 0, SegmentScope.global()));
Optional.of(MemorySegment.ofAddress(addr));
} catch (NoSuchMethodException e) {
return Optional.empty();
}
@ -118,10 +122,7 @@ public final class SystemLookup implements SymbolLookup {
*/
private static Path jdkLibraryPath(String name) {
Path javahome = Path.of(GetPropertyAction.privilegedGetProperty("java.home"));
String lib = switch (CABI.current()) {
case SYS_V, LINUX_AARCH_64, MAC_OS_AARCH_64, LINUX_RISCV_64 -> "lib";
case WIN_64, WIN_AARCH_64 -> "bin";
};
String lib = Utils.IS_WINDOWS ? "bin" : "lib";
String libname = System.mapLibraryName(name);
return javahome.resolve(lib).resolve(libname);
}
@ -202,5 +203,8 @@ public final class SystemLookup implements SymbolLookup {
return null;
}
}
static final SequenceLayout LAYOUT = MemoryLayout.sequenceLayout(
values().length, ADDRESS);
}
}

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
@ -26,6 +26,7 @@
package jdk.internal.foreign;
import java.lang.foreign.AddressLayout;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentAllocator;
@ -39,27 +40,30 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import jdk.internal.access.SharedSecrets;
import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.vm.annotation.ForceInline;
import sun.invoke.util.Wrapper;
import static java.lang.foreign.ValueLayout.JAVA_BYTE;
import static sun.security.action.GetPropertyAction.privilegedGetProperty;
/**
* This class contains misc helper functions to support creation of memory segments.
*/
public final class Utils {
public static final boolean IS_WINDOWS = privilegedGetProperty("os.name").startsWith("Windows");
// Suppresses default constructor, ensuring non-instantiability.
private Utils() {}
private static final MethodHandle BYTE_TO_BOOL;
private static final MethodHandle BOOL_TO_BYTE;
private static final MethodHandle ADDRESS_TO_LONG;
private static final MethodHandle LONG_TO_ADDRESS_SAFE;
private static final MethodHandle LONG_TO_ADDRESS_UNSAFE;
public static final MethodHandle MH_BITS_TO_BYTES_OR_THROW_FOR_OFFSET;
public static final Supplier<RuntimeException> BITS_TO_BYTES_THROW_OFFSET
= () -> new UnsupportedOperationException("Cannot compute byte offset; bit offset is not a multiple of 8");
private static final MethodHandle LONG_TO_ADDRESS;
public static final MethodHandle BITS_TO_BYTES;
static {
try {
@ -70,15 +74,10 @@ public final class Utils {
MethodType.methodType(byte.class, boolean.class));
ADDRESS_TO_LONG = lookup.findStatic(SharedUtils.class, "unboxSegment",
MethodType.methodType(long.class, MemorySegment.class));
LONG_TO_ADDRESS_SAFE = lookup.findStatic(Utils.class, "longToAddressSafe",
MethodType.methodType(MemorySegment.class, long.class));
LONG_TO_ADDRESS_UNSAFE = lookup.findStatic(Utils.class, "longToAddressUnsafe",
MethodType.methodType(MemorySegment.class, long.class));
MH_BITS_TO_BYTES_OR_THROW_FOR_OFFSET = MethodHandles.insertArguments(
lookup.findStatic(Utils.class, "bitsToBytesOrThrow",
MethodType.methodType(long.class, long.class, Supplier.class)),
1,
BITS_TO_BYTES_THROW_OFFSET);
LONG_TO_ADDRESS = lookup.findStatic(Utils.class, "longToAddress",
MethodType.methodType(MemorySegment.class, long.class, long.class, long.class));
BITS_TO_BYTES = lookup.findStatic(Utils.class, "bitsToBytes",
MethodType.methodType(long.class, long.class));
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
@ -93,28 +92,25 @@ public final class Utils {
return ms.asSlice(alignUp(offset, alignment) - offset);
}
public static long bitsToBytesOrThrow(long bits, Supplier<RuntimeException> exFactory) {
if (Utils.isAligned(bits, 8)) {
return bits / 8;
} else {
throw exFactory.get();
}
public static long bitsToBytes(long bits) {
assert Utils.isAligned(bits, 8);
return bits / Byte.SIZE;
}
public static VarHandle makeSegmentViewVarHandle(ValueLayout layout) {
class VarHandleCache {
private static final Map<ValueLayout, VarHandle> handleMap = new ConcurrentHashMap<>();
final class VarHandleCache {
private static final Map<ValueLayout, VarHandle> HANDLE_MAP = new ConcurrentHashMap<>();
static VarHandle put(ValueLayout layout, VarHandle handle) {
VarHandle prev = handleMap.putIfAbsent(layout, handle);
VarHandle prev = HANDLE_MAP.putIfAbsent(layout, handle);
return prev != null ? prev : handle;
}
}
Class<?> baseCarrier = layout.carrier();
if (layout.carrier() == MemorySegment.class) {
baseCarrier = switch ((int) ValueLayout.ADDRESS.byteSize()) {
case 8 -> long.class;
case 4 -> int.class;
case Long.BYTES -> long.class;
case Integer.BYTES -> int.class;
default -> throw new UnsupportedOperationException("Unsupported address layout");
};
} else if (layout.carrier() == boolean.class) {
@ -126,11 +122,12 @@ public final class Utils {
if (layout.carrier() == boolean.class) {
handle = MethodHandles.filterValue(handle, BOOL_TO_BYTE, BYTE_TO_BOOL);
} else if (layout instanceof ValueLayout.OfAddress addressLayout) {
} else if (layout instanceof AddressLayout addressLayout) {
handle = MethodHandles.filterValue(handle,
MethodHandles.explicitCastArguments(ADDRESS_TO_LONG, MethodType.methodType(baseCarrier, MemorySegment.class)),
MethodHandles.explicitCastArguments(addressLayout.isUnbounded() ?
LONG_TO_ADDRESS_UNSAFE : LONG_TO_ADDRESS_SAFE, MethodType.methodType(MemorySegment.class, baseCarrier)));
MethodHandles.explicitCastArguments(MethodHandles.insertArguments(LONG_TO_ADDRESS, 1,
pointeeByteSize(addressLayout), pointeeByteAlign(addressLayout)),
MethodType.methodType(MemorySegment.class, baseCarrier)));
}
return VarHandleCache.put(layout, handle);
}
@ -144,13 +141,19 @@ public final class Utils {
}
@ForceInline
private static MemorySegment longToAddressSafe(long addr) {
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, 0);
public static MemorySegment longToAddress(long addr, long size, long align) {
if (!isAligned(addr, align)) {
throw new IllegalArgumentException("Invalid alignment constraint for address: " + addr);
}
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, size);
}
@ForceInline
private static MemorySegment longToAddressUnsafe(long addr) {
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, Long.MAX_VALUE);
public static MemorySegment longToAddress(long addr, long size, long align, MemorySessionImpl scope) {
if (!isAligned(addr, align)) {
throw new IllegalArgumentException("Invalid alignment constraint for address: " + addr);
}
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, size, scope);
}
public static void copy(MemorySegment addr, byte[] bytes) {
@ -172,24 +175,21 @@ public final class Utils {
@ForceInline
public static void checkElementAlignment(MemoryLayout layout, String msg) {
if (layout.bitAlignment() > layout.bitSize()) {
if (layout.byteAlignment() > layout.byteSize()) {
throw new IllegalArgumentException(msg);
}
}
public static long pointeeSize(MemoryLayout layout) {
if (layout instanceof ValueLayout.OfAddress addressLayout) {
return addressLayout.isUnbounded() ? Long.MAX_VALUE : 0L;
} else {
throw new UnsupportedOperationException();
}
public static long pointeeByteSize(AddressLayout addressLayout) {
return addressLayout.targetLayout()
.map(MemoryLayout::byteSize)
.orElse(0L);
}
public static void checkAllocationSizeAndAlign(long byteSize, long byteAlignment, long maxAlignment) {
checkAllocationSizeAndAlign(byteSize, byteAlignment);
if (maxAlignment != 0 && byteAlignment > maxAlignment) {
throw new IllegalArgumentException("Invalid alignment constraint : " + byteAlignment + " > " + maxAlignment);
}
public static long pointeeByteAlign(AddressLayout addressLayout) {
return addressLayout.targetLayout()
.map(MemoryLayout::byteAlignment)
.orElse(1L);
}
public static void checkAllocationSizeAndAlign(long byteSize, long byteAlignment) {

View file

@ -28,6 +28,7 @@ import jdk.internal.foreign.SystemLookup;
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker;
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker;
import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64Linker;
import jdk.internal.foreign.abi.fallback.FallbackLinker;
import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64Linker;
import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
@ -35,7 +36,7 @@ import jdk.internal.foreign.layout.AbstractLayout;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.Arena;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.Linker;
import java.lang.foreign.MemorySegment;
@ -46,15 +47,16 @@ import java.util.Objects;
public abstract sealed class AbstractLinker implements Linker permits LinuxAArch64Linker, MacOsAArch64Linker,
SysVx64Linker, WindowsAArch64Linker,
Windowsx64Linker, LinuxRISCV64Linker {
Windowsx64Linker, LinuxRISCV64Linker,
FallbackLinker {
public interface UpcallStubFactory {
MemorySegment makeStub(MethodHandle target, SegmentScope arena);
MemorySegment makeStub(MethodHandle target, Arena arena);
}
private record LinkRequest(FunctionDescriptor descriptor, LinkerOptions options) {}
private final SoftReferenceCache<LinkRequest, MethodHandle> DOWNCALL_CACHE = new SoftReferenceCache<>();
private final SoftReferenceCache<FunctionDescriptor, UpcallStubFactory> UPCALL_CACHE = new SoftReferenceCache<>();
private final SoftReferenceCache<LinkRequest, UpcallStubFactory> UPCALL_CACHE = new SoftReferenceCache<>();
@Override
public MethodHandle downcallHandle(FunctionDescriptor function, Option... options) {
@ -74,23 +76,25 @@ public abstract sealed class AbstractLinker implements Linker permits LinuxAArch
protected abstract MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function, LinkerOptions options);
@Override
public MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, SegmentScope scope) {
Objects.requireNonNull(scope);
public MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, Arena arena, Linker.Option... options) {
Objects.requireNonNull(arena);
Objects.requireNonNull(target);
Objects.requireNonNull(function);
checkHasNaturalAlignment(function);
SharedUtils.checkExceptions(target);
LinkerOptions optionSet = LinkerOptions.forUpcall(function, options);
MethodType type = function.toMethodType();
if (!type.equals(target.type())) {
throw new IllegalArgumentException("Wrong method handle type: " + target.type());
}
UpcallStubFactory factory = UPCALL_CACHE.get(function, f -> arrangeUpcall(type, f));
return factory.makeStub(target, scope);
UpcallStubFactory factory = UPCALL_CACHE.get(new LinkRequest(function, optionSet), linkRequest ->
arrangeUpcall(type, linkRequest.descriptor(), linkRequest.options()));
return factory.makeStub(target, arena);
}
protected abstract UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function);
protected abstract UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options);
@Override
public SystemLookup defaultLookup() {

View file

@ -24,14 +24,11 @@
*/
package jdk.internal.foreign.abi;
import jdk.internal.foreign.NativeMemorySegmentImpl;
import jdk.internal.foreign.Utils;
import jdk.internal.foreign.abi.BindingInterpreter.LoadFunc;
import jdk.internal.foreign.abi.BindingInterpreter.StoreFunc;
import java.lang.foreign.Arena;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.SegmentAllocator;
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@ -199,103 +196,10 @@ import static java.lang.foreign.ValueLayout.JAVA_SHORT_UNALIGNED;
*/
public sealed interface Binding {
/**
* A binding context is used as an helper to carry out evaluation of certain bindings; for instance,
* it helps {@link Allocate} bindings, by providing the {@link SegmentAllocator} that should be used for
* the allocation operation, or {@link BoxAddress} bindings, by providing the {@link SegmentScope} that
* should be used to create an unsafe struct from a memory address.
*/
class Context implements AutoCloseable {
private final SegmentAllocator allocator;
private final SegmentScope scope;
private Context(SegmentAllocator allocator, SegmentScope scope) {
this.allocator = allocator;
this.scope = scope;
}
public SegmentAllocator allocator() {
return allocator;
}
public SegmentScope scope() {
return scope;
}
@Override
public void close() {
throw new UnsupportedOperationException();
}
/**
* Create a binding context from given native scope.
*/
public static Context ofBoundedAllocator(long size) {
Arena arena = Arena.openConfined();
return new Context(SegmentAllocator.slicingAllocator(MemorySegment.allocateNative(size, arena.scope())), arena.scope()) {
@Override
public void close() {
arena.close();
}
};
}
/**
* Create a binding context from given segment allocator. The resulting context will throw when
* the context's scope is accessed.
*/
public static Context ofAllocator(SegmentAllocator allocator) {
return new Context(allocator, null) {
@Override
public SegmentScope scope() {
throw new UnsupportedOperationException();
}
};
}
/**
* Create a binding context from given scope. The resulting context will throw when
* the context's allocator is accessed.
*/
public static Context ofScope() {
Arena arena = Arena.openConfined();
return new Context(null, arena.scope()) {
@Override
public SegmentAllocator allocator() { throw new UnsupportedOperationException(); }
@Override
public void close() {
arena.close();
}
};
}
/**
* Dummy binding context. Throws exceptions when attempting to access scope, return a throwing allocator, and has
* an idempotent {@link #close()}.
*/
public static final Context DUMMY = new Context(null, null) {
@Override
public SegmentAllocator allocator() {
return SharedUtils.THROWING_ALLOCATOR;
}
@Override
public SegmentScope scope() {
throw new UnsupportedOperationException();
}
@Override
public void close() {
// do nothing
}
};
}
void verify(Deque<Class<?>> stack);
void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
BindingInterpreter.LoadFunc loadFunc, Context context);
void interpret(Deque<Object> stack, StoreFunc storeFunc,
LoadFunc loadFunc, SegmentAllocator allocator);
private static void checkType(Class<?> type) {
if (!type.isPrimitive() || type == void.class)
@ -352,16 +256,16 @@ public sealed interface Binding {
return new Allocate(layout.byteSize(), layout.byteAlignment());
}
static BoxAddress boxAddressRaw(long size) {
return new BoxAddress(size, false);
static BoxAddress boxAddressRaw(long size, long align) {
return new BoxAddress(size, align, false);
}
static BoxAddress boxAddress(MemoryLayout layout) {
return new BoxAddress(layout.byteSize(), true);
return new BoxAddress(layout.byteSize(), layout.byteAlignment(), true);
}
static BoxAddress boxAddress(long byteSize) {
return new BoxAddress(byteSize, true);
return new BoxAddress(byteSize, 1, true);
}
static UnboxAddress unboxAddress() {
@ -463,8 +367,8 @@ public sealed interface Binding {
return this;
}
public Binding.Builder boxAddressRaw(long size) {
bindings.add(Binding.boxAddressRaw(size));
public Binding.Builder boxAddressRaw(long size, long align) {
bindings.add(Binding.boxAddressRaw(size, align));
return this;
}
@ -508,8 +412,8 @@ public sealed interface Binding {
}
@Override
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
BindingInterpreter.LoadFunc loadFunc, Context context) {
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
LoadFunc loadFunc, SegmentAllocator allocator) {
storeFunc.store(storage(), type(), stack.pop());
}
}
@ -527,8 +431,8 @@ public sealed interface Binding {
}
@Override
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
BindingInterpreter.LoadFunc loadFunc, Context context) {
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
LoadFunc loadFunc, SegmentAllocator allocator) {
stack.push(loadFunc.load(storage(), type()));
}
}
@ -555,8 +459,8 @@ public sealed interface Binding {
}
@Override
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
BindingInterpreter.LoadFunc loadFunc, Context context) {
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
LoadFunc loadFunc, SegmentAllocator allocator) {
Object value = stack.pop();
MemorySegment writeAddress = (MemorySegment) stack.pop();
if (SharedUtils.isPowerOfTwo(byteWidth())) {
@ -612,8 +516,8 @@ public sealed interface Binding {
}
@Override
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
BindingInterpreter.LoadFunc loadFunc, Context context) {
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
LoadFunc loadFunc, SegmentAllocator allocator) {
MemorySegment readAddress = (MemorySegment) stack.pop();
if (SharedUtils.isPowerOfTwo(byteWidth())) {
// exact size match
@ -657,8 +561,8 @@ public sealed interface Binding {
* and pushes the new buffer onto the operand stack
*/
record Copy(long size, long alignment) implements Binding {
private static MemorySegment copyBuffer(MemorySegment operand, long size, long alignment, Context context) {
return context.allocator().allocate(size, alignment)
private static MemorySegment copyBuffer(MemorySegment operand, long size, long alignment, SegmentAllocator allocator) {
return allocator.allocate(size, alignment)
.copyFrom(operand.asSlice(0, size));
}
@ -670,10 +574,10 @@ public sealed interface Binding {
}
@Override
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
BindingInterpreter.LoadFunc loadFunc, Context context) {
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
LoadFunc loadFunc, SegmentAllocator allocator) {
MemorySegment operand = (MemorySegment) stack.pop();
MemorySegment copy = copyBuffer(operand, size, alignment, context);
MemorySegment copy = copyBuffer(operand, size, alignment, allocator);
stack.push(copy);
}
}
@ -683,8 +587,8 @@ public sealed interface Binding {
* Creates a new MemorySegment with the give [size] and [alignment], and pushes it onto the operand stack.
*/
record Allocate(long size, long alignment) implements Binding {
private static MemorySegment allocateBuffer(long size, long alignment, Context context) {
return context.allocator().allocate(size, alignment);
private static MemorySegment allocateBuffer(long size, long alignment, SegmentAllocator allocator) {
return allocator.allocate(size, alignment);
}
@Override
@ -693,9 +597,9 @@ public sealed interface Binding {
}
@Override
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
BindingInterpreter.LoadFunc loadFunc, Context context) {
stack.push(allocateBuffer(size, alignment, context));
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
LoadFunc loadFunc, SegmentAllocator allocator) {
stack.push(allocateBuffer(size, alignment, allocator));
}
}
@ -715,9 +619,9 @@ public sealed interface Binding {
}
@Override
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
BindingInterpreter.LoadFunc loadFunc, Context context) {
stack.push(((MemorySegment)stack.pop()).address());
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
LoadFunc loadFunc, SegmentAllocator allocator) {
stack.push(SharedUtils.unboxSegment((MemorySegment)stack.pop()));
}
}
@ -726,7 +630,7 @@ public sealed interface Binding {
* Pops a 'long' from the operand stack, converts it to a 'MemorySegment', with the given size and memory scope
* (either the context scope, or the global scope), and pushes that onto the operand stack.
*/
record BoxAddress(long size, boolean needsScope) implements Binding {
record BoxAddress(long size, long align, boolean needsScope) implements Binding {
@Override
public void verify(Deque<Class<?>> stack) {
@ -736,11 +640,13 @@ public sealed interface Binding {
}
@Override
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
BindingInterpreter.LoadFunc loadFunc, Context context) {
SegmentScope scope = needsScope ?
context.scope() : SegmentScope.global();
stack.push(NativeMemorySegmentImpl.makeNativeSegmentUnchecked((long) stack.pop(), size, scope));
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
LoadFunc loadFunc, SegmentAllocator allocator) {
MemorySegment segment = Utils.longToAddress((long) stack.pop(), size, align);
if (needsScope) {
segment = segment.reinterpret((Arena) allocator, null);
}
stack.push(segment);
}
}
@ -758,8 +664,8 @@ public sealed interface Binding {
}
@Override
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
BindingInterpreter.LoadFunc loadFunc, Context context) {
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
LoadFunc loadFunc, SegmentAllocator allocator) {
stack.push(stack.peekLast());
}
}
@ -773,8 +679,8 @@ public sealed interface Binding {
enum Cast implements Binding {
INT_TO_BOOLEAN(int.class, boolean.class) {
@Override
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
BindingInterpreter.LoadFunc loadFunc, Context context) {
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
LoadFunc loadFunc, SegmentAllocator allocator) {
// implement least significant byte non-zero test
int arg = (int) stack.pop();
boolean result = Utils.byteToBoolean((byte) arg);
@ -813,8 +719,8 @@ public sealed interface Binding {
}
@Override
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
BindingInterpreter.LoadFunc loadFunc, Context context) {
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
LoadFunc loadFunc, SegmentAllocator allocator) {
Object arg = stack.pop();
MethodHandle converter = MethodHandles.explicitCastArguments(MethodHandles.identity(toType),
MethodType.methodType(toType, fromType));

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 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
@ -24,25 +24,26 @@
*/
package jdk.internal.foreign.abi;
import java.lang.foreign.SegmentAllocator;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
public class BindingInterpreter {
static void unbox(Object arg, List<Binding> bindings, StoreFunc storeFunc, Binding.Context context) {
static void unbox(Object arg, List<Binding> bindings, StoreFunc storeFunc, SegmentAllocator allocator) {
Deque<Object> stack = new ArrayDeque<>();
stack.push(arg);
for (Binding b : bindings) {
b.interpret(stack, storeFunc, null, context);
b.interpret(stack, storeFunc, null, allocator);
}
}
static Object box(List<Binding> bindings, LoadFunc loadFunc, Binding.Context context) {
static Object box(List<Binding> bindings, LoadFunc loadFunc, SegmentAllocator allocator) {
Deque<Object> stack = new ArrayDeque<>();
for (Binding b : bindings) {
b.interpret(stack, null, loadFunc, context);
b.interpret(stack, null, loadFunc, allocator);
}
return stack.pop();
}

View file

@ -26,7 +26,6 @@ package jdk.internal.foreign.abi;
import jdk.internal.foreign.AbstractMemorySegmentImpl;
import jdk.internal.foreign.MemorySessionImpl;
import jdk.internal.foreign.NativeMemorySegmentImpl;
import jdk.internal.foreign.Utils;
import jdk.internal.foreign.abi.Binding.Allocate;
import jdk.internal.foreign.abi.Binding.BoxAddress;
@ -53,12 +52,7 @@ import sun.security.action.GetPropertyAction;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.constant.ConstantDescs;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.SegmentAllocator;
import java.lang.foreign.ValueLayout;
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@ -88,17 +82,16 @@ public class BindingSpecializer {
private static final String VOID_DESC = methodType(void.class).descriptorString();
private static final String BINDING_CONTEXT_DESC = Binding.Context.class.descriptorString();
private static final String OF_BOUNDED_ALLOCATOR_DESC = methodType(Binding.Context.class, long.class).descriptorString();
private static final String OF_SCOPE_DESC = methodType(Binding.Context.class).descriptorString();
private static final String ALLOCATOR_DESC = methodType(SegmentAllocator.class).descriptorString();
private static final String SCOPE_DESC = methodType(SegmentScope.class).descriptorString();
private static final String ARENA_DESC = Arena.class.descriptorString();
private static final String NEW_BOUNDED_ARENA_DESC = methodType(Arena.class, long.class).descriptorString();
private static final String NEW_EMPTY_ARENA_DESC = methodType(Arena.class).descriptorString();
private static final String SCOPE_DESC = methodType(MemorySegment.Scope.class).descriptorString();
private static final String SESSION_IMPL_DESC = methodType(MemorySessionImpl.class).descriptorString();
private static final String CLOSE_DESC = VOID_DESC;
private static final String UNBOX_SEGMENT_DESC = methodType(long.class, MemorySegment.class).descriptorString();
private static final String COPY_DESC = methodType(void.class, MemorySegment.class, long.class, MemorySegment.class, long.class, long.class).descriptorString();
private static final String OF_LONG_DESC = methodType(MemorySegment.class, long.class, long.class).descriptorString();
private static final String OF_LONG_UNCHECKED_DESC = methodType(MemorySegment.class, long.class, long.class, SegmentScope.class).descriptorString();
private static final String LONG_TO_ADDRESS_NO_SCOPE_DESC = methodType(MemorySegment.class, long.class, long.class, long.class).descriptorString();
private static final String LONG_TO_ADDRESS_SCOPE_DESC = methodType(MemorySegment.class, long.class, long.class, long.class, MemorySessionImpl.class).descriptorString();
private static final String ALLOCATE_DESC = methodType(MemorySegment.class, long.class, long.class).descriptorString();
private static final String HANDLE_UNCAUGHT_EXCEPTION_DESC = methodType(void.class, Throwable.class).descriptorString();
private static final String METHOD_HANDLES_INTRN = Type.getInternalName(MethodHandles.class);
@ -165,7 +158,8 @@ public class BindingSpecializer {
byte[] bytes = specializeHelper(leafHandle.type(), callerMethodType, callingSequence, abi);
try {
MethodHandles.Lookup definedClassLookup = MethodHandles.lookup().defineHiddenClassWithClassData(bytes, leafHandle, false);
MethodHandles.Lookup definedClassLookup = MethodHandles.lookup()
.defineHiddenClassWithClassData(bytes, leafHandle, false);
return definedClassLookup.findStatic(definedClassLookup.lookupClass(), METHOD_NAME, callerMethodType);
} catch (IllegalAccessException | NoSuchMethodException e) {
throw new InternalError("Should not happen", e);
@ -294,11 +288,11 @@ public class BindingSpecializer {
// create a Binding.Context for this call
if (callingSequence.allocationSize() != 0) {
emitConst(callingSequence.allocationSize());
emitInvokeStatic(Binding.Context.class, "ofBoundedAllocator", OF_BOUNDED_ALLOCATOR_DESC);
emitInvokeStatic(SharedUtils.class, "newBoundedArena", NEW_BOUNDED_ARENA_DESC);
} else if (callingSequence.forUpcall() && needsSession()) {
emitInvokeStatic(Binding.Context.class, "ofScope", OF_SCOPE_DESC);
emitInvokeStatic(SharedUtils.class, "newEmptyArena", NEW_EMPTY_ARENA_DESC);
} else {
emitGetStatic(Binding.Context.class, "DUMMY", BINDING_CONTEXT_DESC);
emitGetStatic(SharedUtils.class, "DUMMY_ARENA", ARENA_DESC);
}
contextIdx = newLocal(Object.class);
emitStore(Object.class, contextIdx);
@ -422,13 +416,13 @@ public class BindingSpecializer {
if (callingSequence.forDowncall()) {
mv.visitInsn(ATHROW);
} else {
emitInvokeStatic(SharedUtils.class, "handleUncaughtException", HANDLE_UNCAUGHT_EXCEPTION_DESC);
if (callerMethodType.returnType() != void.class) {
emitConstZero(callerMethodType.returnType());
emitReturn(callerMethodType.returnType());
} else {
mv.visitInsn(RETURN);
}
emitInvokeStatic(SharedUtils.class, "handleUncaughtException", HANDLE_UNCAUGHT_EXCEPTION_DESC);
if (callerMethodType.returnType() != void.class) {
emitConstZero(callerMethodType.returnType());
emitReturn(callerMethodType.returnType());
} else {
mv.visitInsn(RETURN);
}
}
mv.visitTryCatchBlock(tryStart, tryEnd, catchStart, null);
@ -454,7 +448,7 @@ public class BindingSpecializer {
.get(paramIndex - offset);
// is this an address layout?
return paramLayout instanceof ValueLayout.OfAddress;
return paramLayout instanceof AddressLayout;
}
private void emitCleanup() {
@ -563,29 +557,32 @@ public class BindingSpecializer {
private void emitLoadInternalSession() {
assert contextIdx != -1;
emitLoad(Object.class, contextIdx);
emitInvokeVirtual(Binding.Context.class, "scope", SCOPE_DESC);
emitCheckCast(Arena.class);
emitInvokeInterface(Arena.class, "scope", SCOPE_DESC);
emitCheckCast(MemorySessionImpl.class);
}
private void emitLoadInternalAllocator() {
assert contextIdx != -1;
emitLoad(Object.class, contextIdx);
emitInvokeVirtual(Binding.Context.class, "allocator", ALLOCATOR_DESC);
}
private void emitCloseContext() {
assert contextIdx != -1;
emitLoad(Object.class, contextIdx);
emitInvokeVirtual(Binding.Context.class, "close", CLOSE_DESC);
emitCheckCast(Arena.class);
emitInvokeInterface(Arena.class, "close", CLOSE_DESC);
}
private void emitBoxAddress(BoxAddress boxAddress) {
popType(long.class);
emitConst(boxAddress.size());
emitConst(boxAddress.align());
if (needsSession()) {
emitLoadInternalSession();
emitInvokeStatic(NativeMemorySegmentImpl.class, "makeNativeSegmentUnchecked", OF_LONG_UNCHECKED_DESC);
emitInvokeStatic(Utils.class, "longToAddress", LONG_TO_ADDRESS_SCOPE_DESC);
} else {
emitInvokeStatic(NativeMemorySegmentImpl.class, "makeNativeSegmentUnchecked", OF_LONG_DESC);
emitInvokeStatic(Utils.class, "longToAddress", LONG_TO_ADDRESS_NO_SCOPE_DESC);
}
pushType(MemorySegment.class);
}
@ -934,7 +931,7 @@ public class BindingSpecializer {
} else if (type == double.class) {
return ValueLayout.OfDouble.class;
} else if (type == MemorySegment.class) {
return ValueLayout.OfAddress.class;
return AddressLayout.class;
} else {
throw new IllegalStateException("Unknown type: " + type);
}

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
@ -191,6 +191,10 @@ public class CallingSequence {
.reduce(0, (a, b) -> a | b);
}
public boolean needsTransition() {
return !linkerOptions.isTrivial();
}
public int numLeadingParams() {
return 2 + (linkerOptions.hasCapturedCallState() ? 1 : 0); // 2 for addr, allocator
}

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
@ -24,34 +24,49 @@
*/
package jdk.internal.foreign.abi;
import jdk.internal.foreign.Utils;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.StructLayout;
import java.lang.foreign.ValueLayout;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.lang.foreign.ValueLayout.JAVA_INT;
import static sun.security.action.GetPropertyAction.privilegedGetProperty;
public enum CapturableState {
GET_LAST_ERROR ("GetLastError", JAVA_INT, 1 << 0),
WSA_GET_LAST_ERROR("WSAGetLastError", JAVA_INT, 1 << 1),
ERRNO ("errno", JAVA_INT, 1 << 2);
GET_LAST_ERROR ("GetLastError", JAVA_INT, 1 << 0, Utils.IS_WINDOWS),
WSA_GET_LAST_ERROR("WSAGetLastError", JAVA_INT, 1 << 1, Utils.IS_WINDOWS),
ERRNO ("errno", JAVA_INT, 1 << 2, true);
public static final StructLayout LAYOUT = MemoryLayout.structLayout(
supportedStates().map(CapturableState::layout).toArray(MemoryLayout[]::new));
private final String stateName;
private final ValueLayout layout;
private final int mask;
private final boolean isSupported;
CapturableState(String stateName, ValueLayout layout, int mask) {
CapturableState(String stateName, ValueLayout layout, int mask, boolean isSupported) {
this.stateName = stateName;
this.layout = layout.withName(stateName);
this.mask = mask;
this.isSupported = isSupported;
}
private static Stream<CapturableState> supportedStates() {
return Stream.of(values()).filter(CapturableState::isSupported);
}
public static CapturableState forName(String name) {
return Stream.of(values())
.filter(stl -> stl.stateName().equals(name))
.filter(CapturableState::isSupported)
.findAny()
.orElseThrow(() -> new IllegalArgumentException(
"Unknown name: " + name +", must be one of: "
+ Stream.of(CapturableState.values())
+ supportedStates()
.map(CapturableState::stateName)
.collect(Collectors.joining(", "))));
}
@ -67,4 +82,8 @@ public enum CapturableState {
public int mask() {
return mask;
}
public boolean isSupported() {
return isSupported;
}
}

View file

@ -28,6 +28,7 @@ import jdk.internal.access.JavaLangInvokeAccess;
import jdk.internal.access.SharedSecrets;
import sun.security.action.GetPropertyAction;
import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentAllocator;
import java.lang.invoke.MethodHandle;
@ -50,7 +51,6 @@ public class DowncallLinker {
private static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess();
private static final MethodHandle MH_INVOKE_INTERP_BINDINGS;
private static final MethodHandle MH_CHECK_SYMBOL;
private static final MethodHandle EMPTY_OBJECT_ARRAY_HANDLE = MethodHandles.constant(Object[].class, new Object[0]);
static {
@ -58,8 +58,6 @@ public class DowncallLinker {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MH_INVOKE_INTERP_BINDINGS = lookup.findVirtual(DowncallLinker.class, "invokeInterpBindings",
methodType(Object.class, SegmentAllocator.class, Object[].class, InvocationData.class));
MH_CHECK_SYMBOL = lookup.findStatic(SharedUtils.class, "checkSymbol",
methodType(void.class, MemorySegment.class));
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
@ -86,7 +84,8 @@ public class DowncallLinker {
toStorageArray(retMoves),
leafType,
callingSequence.needsReturnBuffer(),
callingSequence.capturedStateMask()
callingSequence.capturedStateMask(),
callingSequence.needsTransition()
);
MethodHandle handle = JLIA.nativeMethodHandle(nep);
@ -111,7 +110,7 @@ public class DowncallLinker {
assert handle.type().parameterType(0) == SegmentAllocator.class;
assert handle.type().parameterType(1) == MemorySegment.class;
handle = foldArguments(handle, 1, MH_CHECK_SYMBOL);
handle = foldArguments(handle, 1, SharedUtils.MH_CHECK_SYMBOL);
handle = SharedUtils.swapArguments(handle, 0, 1); // normalize parameter order
@ -150,10 +149,10 @@ public class DowncallLinker {
private record InvocationData(MethodHandle leaf, Map<VMStorage, Integer> argIndexMap, Map<VMStorage, Integer> retIndexMap) {}
Object invokeInterpBindings(SegmentAllocator allocator, Object[] args, InvocationData invData) throws Throwable {
Binding.Context unboxContext = callingSequence.allocationSize() != 0
? Binding.Context.ofBoundedAllocator(callingSequence.allocationSize())
: Binding.Context.DUMMY;
try (unboxContext) {
Arena unboxArena = callingSequence.allocationSize() != 0
? SharedUtils.newBoundedArena(callingSequence.allocationSize())
: SharedUtils.DUMMY_ARENA;
try (unboxArena) {
MemorySegment returnBuffer = null;
// do argument processing, get Object[] as result
@ -161,7 +160,7 @@ public class DowncallLinker {
if (callingSequence.needsReturnBuffer()) {
// we supply the return buffer (argument array does not contain it)
Object[] prefixedArgs = new Object[args.length + 1];
returnBuffer = unboxContext.allocator().allocate(callingSequence.returnBufferSize());
returnBuffer = unboxArena.allocate(callingSequence.returnBufferSize());
prefixedArgs[0] = returnBuffer;
System.arraycopy(args, 0, prefixedArgs, 1, args.length);
args = prefixedArgs;
@ -169,7 +168,7 @@ public class DowncallLinker {
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
BindingInterpreter.unbox(arg, callingSequence.argumentBindings(i),
(storage, type, value) -> leafArgs[invData.argIndexMap.get(storage)] = value, unboxContext);
(storage, type, value) -> leafArgs[invData.argIndexMap.get(storage)] = value, unboxArena);
}
// call leaf
@ -190,10 +189,10 @@ public class DowncallLinker {
retBufReadOffset += abi.arch.typeSize(storage.type());
return result1;
}
}, Binding.Context.ofAllocator(allocator));
}, allocator);
} else {
return BindingInterpreter.box(callingSequence.returnBindings(), (storage, type) -> o,
Binding.Context.ofAllocator(allocator));
allocator);
}
}
}

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
@ -26,13 +26,11 @@ package jdk.internal.foreign.abi;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.Linker;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.StructLayout;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.stream.Stream;
public class LinkerOptions {
@ -45,14 +43,23 @@ public class LinkerOptions {
}
public static LinkerOptions forDowncall(FunctionDescriptor desc, Linker.Option... options) {
Map<Class<?>, LinkerOptionImpl> optionMap = new HashMap<>();
return forShared(LinkerOptionImpl::validateForDowncall, desc, options);
}
public static LinkerOptions forUpcall(FunctionDescriptor desc, Linker.Option[] options) {
return forShared(LinkerOptionImpl::validateForUpcall, desc, options);
}
private static LinkerOptions forShared(BiConsumer<LinkerOptionImpl, FunctionDescriptor> validator,
FunctionDescriptor desc, Linker.Option... options) {
Map<Class<?>, LinkerOptionImpl> optionMap = new HashMap<>();
for (Linker.Option option : options) {
if (optionMap.containsKey(option.getClass())) {
throw new IllegalArgumentException("Duplicate option: " + option);
}
LinkerOptionImpl opImpl = (LinkerOptionImpl) option;
opImpl.validateForDowncall(desc);
validator.accept(opImpl, desc);
optionMap.put(option.getClass(), opImpl);
}
@ -73,11 +80,11 @@ public class LinkerOptions {
}
public boolean hasCapturedCallState() {
return getOption(CaptureCallStateImpl.class) != null;
return getOption(CaptureCallState.class) != null;
}
public Stream<CapturableState> capturedCallState() {
CaptureCallStateImpl stl = getOption(CaptureCallStateImpl.class);
CaptureCallState stl = getOption(CaptureCallState.class);
return stl == null ? Stream.empty() : stl.saved().stream();
}
@ -86,6 +93,11 @@ public class LinkerOptions {
return fva != null;
}
public boolean isTrivial() {
IsTrivial it = getOption(IsTrivial.class);
return it != null;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@ -99,11 +111,14 @@ public class LinkerOptions {
}
public sealed interface LinkerOptionImpl extends Linker.Option
permits FirstVariadicArg,
CaptureCallStateImpl {
permits CaptureCallState, FirstVariadicArg, IsTrivial {
default void validateForDowncall(FunctionDescriptor descriptor) {
throw new IllegalArgumentException("Not supported for downcall: " + this);
}
default void validateForUpcall(FunctionDescriptor descriptor) {
throw new IllegalArgumentException("Not supported for upcall: " + this);
}
}
public record FirstVariadicArg(int index) implements LinkerOptionImpl {
@ -115,22 +130,19 @@ public class LinkerOptions {
}
}
public record CaptureCallStateImpl(Set<CapturableState> saved) implements LinkerOptionImpl, Linker.Option.CaptureCallState {
public record CaptureCallState(Set<CapturableState> saved) implements LinkerOptionImpl {
@Override
public void validateForDowncall(FunctionDescriptor descriptor) {
// done during construction
}
@Override
public StructLayout layout() {
return MemoryLayout.structLayout(
saved.stream()
.sorted(Comparator.comparingInt(CapturableState::ordinal))
.map(CapturableState::layout)
.toArray(MemoryLayout[]::new)
);
}
}
public record IsTrivial() implements LinkerOptionImpl {
public static IsTrivial INSTANCE = new IsTrivial();
@Override
public void validateForDowncall(FunctionDescriptor descriptor) {
// always allowed
}
}
}

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
@ -47,7 +47,8 @@ public class NativeEntryPoint {
private static final SoftReferenceCache<CacheKey, NativeEntryPoint> NEP_CACHE = new SoftReferenceCache<>();
private record CacheKey(MethodType methodType, ABIDescriptor abi,
List<VMStorage> argMoves, List<VMStorage> retMoves,
boolean needsReturnBuffer, int capturedStateMask) {}
boolean needsReturnBuffer, int capturedStateMask,
boolean needsTransition) {}
private NativeEntryPoint(MethodType methodType, long downcallStubAddress) {
this.methodType = methodType;
@ -58,15 +59,18 @@ public class NativeEntryPoint {
VMStorage[] argMoves, VMStorage[] returnMoves,
MethodType methodType,
boolean needsReturnBuffer,
int capturedStateMask) {
int capturedStateMask,
boolean needsTransition) {
if (returnMoves.length > 1 != needsReturnBuffer) {
throw new AssertionError("Multiple register return, but needsReturnBuffer was false");
}
checkType(methodType, needsReturnBuffer, capturedStateMask);
CacheKey key = new CacheKey(methodType, abi, Arrays.asList(argMoves), Arrays.asList(returnMoves), needsReturnBuffer, capturedStateMask);
CacheKey key = new CacheKey(methodType, abi, Arrays.asList(argMoves), Arrays.asList(returnMoves),
needsReturnBuffer, capturedStateMask, needsTransition);
return NEP_CACHE.get(key, k -> {
long downcallStub = makeDowncallStub(methodType, abi, argMoves, returnMoves, needsReturnBuffer, capturedStateMask);
long downcallStub = makeDowncallStub(methodType, abi, argMoves, returnMoves, needsReturnBuffer,
capturedStateMask, needsTransition);
NativeEntryPoint nep = new NativeEntryPoint(methodType, downcallStub);
CLEANER.register(nep, () -> freeDowncallStub(downcallStub));
return nep;
@ -87,7 +91,8 @@ public class NativeEntryPoint {
private static native long makeDowncallStub(MethodType methodType, ABIDescriptor abi,
VMStorage[] encArgMoves, VMStorage[] encRetMoves,
boolean needsReturnBuffer,
int capturedStateMask);
int capturedStateMask,
boolean needsTransition);
private static native boolean freeDowncallStub0(long downcallStub);
private static void freeDowncallStub(long downcallStub) {

View file

@ -32,19 +32,21 @@ import jdk.internal.foreign.abi.AbstractLinker.UpcallStubFactory;
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker;
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker;
import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64Linker;
import jdk.internal.foreign.abi.fallback.FallbackLinker;
import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64Linker;
import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
import jdk.internal.vm.annotation.ForceInline;
import java.lang.foreign.AddressLayout;
import java.lang.foreign.Arena;
import java.lang.foreign.Linker;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.MemorySegment.Scope;
import java.lang.foreign.SegmentAllocator;
import java.lang.foreign.VaList;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@ -55,9 +57,7 @@ import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@ -76,6 +76,28 @@ public final class SharedUtils {
private static final MethodHandle MH_ALLOC_BUFFER;
private static final MethodHandle MH_BUFFER_COPY;
private static final MethodHandle MH_REACHABILITY_FENCE;
public static final MethodHandle MH_CHECK_SYMBOL;
public static final AddressLayout C_POINTER = ADDRESS
.withBitAlignment(64)
.withTargetLayout(MemoryLayout.sequenceLayout(JAVA_BYTE));
public static final Arena DUMMY_ARENA = new Arena() {
@Override
public Scope scope() {
throw new UnsupportedOperationException();
}
@Override
public MemorySegment allocate(long byteSize) {
throw new UnsupportedOperationException();
}
@Override
public void close() {
// do nothing
}
};
static {
try {
@ -86,6 +108,8 @@ public final class SharedUtils {
methodType(MemorySegment.class, MemorySegment.class, MemorySegment.class));
MH_REACHABILITY_FENCE = lookup.findStatic(Reference.class, "reachabilityFence",
methodType(void.class, Object.class));
MH_CHECK_SYMBOL = lookup.findStatic(SharedUtils.class, "checkSymbol",
methodType(void.class, MemorySegment.class));
} catch (ReflectiveOperationException e) {
throw new BootstrapMethodError(e);
}
@ -214,6 +238,8 @@ public final class SharedUtils {
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.getInstance();
case WIN_AARCH_64 -> WindowsAArch64Linker.getInstance();
case LINUX_RISCV_64 -> LinuxRISCV64Linker.getInstance();
case FALLBACK -> FallbackLinker.getInstance();
case UNSUPPORTED -> throw new UnsupportedOperationException("Platform does not support native linker");
};
}
@ -265,7 +291,7 @@ public final class SharedUtils {
}
static MethodHandle swapArguments(MethodHandle mh, int firstArg, int secondArg) {
public static MethodHandle swapArguments(MethodHandle mh, int firstArg, int secondArg) {
MethodType mtype = mh.type();
int[] perms = new int[mtype.parameterCount()];
MethodType swappedType = MethodType.methodType(mtype.returnType());
@ -283,10 +309,14 @@ public final class SharedUtils {
return MH_REACHABILITY_FENCE.asType(MethodType.methodType(void.class, type));
}
static void handleUncaughtException(Throwable t) {
public static void handleUncaughtException(Throwable t) {
if (t != null) {
t.printStackTrace();
JLA.exit(1);
try {
t.printStackTrace();
System.err.println("Unrecoverable uncaught exception encountered. The VM will now exit");
} finally {
JLA.exit(1);
}
}
}
@ -319,39 +349,6 @@ public final class SharedUtils {
throw new IllegalArgumentException("Symbol is NULL: " + symbol);
}
public static VaList newVaList(Consumer<VaList.Builder> actions, SegmentScope scope) {
return switch (CABI.current()) {
case WIN_64 -> Windowsx64Linker.newVaList(actions, scope);
case SYS_V -> SysVx64Linker.newVaList(actions, scope);
case LINUX_AARCH_64 -> LinuxAArch64Linker.newVaList(actions, scope);
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.newVaList(actions, scope);
case LINUX_RISCV_64 -> LinuxRISCV64Linker.newVaList(actions, scope);
case WIN_AARCH_64 -> WindowsAArch64Linker.newVaList(actions, scope);
};
}
public static VaList newVaListOfAddress(long address, SegmentScope scope) {
return switch (CABI.current()) {
case WIN_64 -> Windowsx64Linker.newVaListOfAddress(address, scope);
case SYS_V -> SysVx64Linker.newVaListOfAddress(address, scope);
case LINUX_AARCH_64 -> LinuxAArch64Linker.newVaListOfAddress(address, scope);
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.newVaListOfAddress(address, scope);
case LINUX_RISCV_64 -> LinuxRISCV64Linker.newVaListOfAddress(address, scope);
case WIN_AARCH_64 -> WindowsAArch64Linker.newVaListOfAddress(address, scope);
};
}
public static VaList emptyVaList() {
return switch (CABI.current()) {
case WIN_64 -> Windowsx64Linker.emptyVaList();
case SYS_V -> SysVx64Linker.emptyVaList();
case LINUX_AARCH_64 -> LinuxAArch64Linker.emptyVaList();
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.emptyVaList();
case LINUX_RISCV_64 -> LinuxRISCV64Linker.emptyVaList();
case WIN_AARCH_64 -> WindowsAArch64Linker.emptyVaList();
};
}
static void checkType(Class<?> actualType, Class<?> expectedType) {
if (expectedType != actualType) {
throw new IllegalArgumentException(
@ -369,8 +366,47 @@ public final class SharedUtils {
: chunkOffset;
}
public static NoSuchElementException newVaListNSEE(MemoryLayout layout) {
return new NoSuchElementException("No such element: " + layout);
public static Arena newBoundedArena(long size) {
return new Arena() {
final Arena arena = Arena.ofConfined();
final SegmentAllocator slicingAllocator = SegmentAllocator.slicingAllocator(arena.allocate(size));
@Override
public Scope scope() {
return arena.scope();
}
@Override
public void close() {
arena.close();
}
@Override
public MemorySegment allocate(long byteSize, long byteAlignment) {
return slicingAllocator.allocate(byteSize, byteAlignment);
}
};
}
public static Arena newEmptyArena() {
return new Arena() {
final Arena arena = Arena.ofConfined();
@Override
public Scope scope() {
return arena.scope();
}
@Override
public void close() {
arena.close();
}
@Override
public MemorySegment allocate(long byteSize, long byteAlignment) {
throw new UnsupportedOperationException();
}
};
}
public static final class SimpleVaArg {
@ -387,59 +423,6 @@ public final class SharedUtils {
}
}
public static final class EmptyVaList implements VaList {
private final MemorySegment address;
public EmptyVaList(MemorySegment address) {
this.address = address;
}
private static UnsupportedOperationException uoe() {
return new UnsupportedOperationException("Empty VaList");
}
@Override
public int nextVarg(ValueLayout.OfInt layout) {
throw uoe();
}
@Override
public long nextVarg(ValueLayout.OfLong layout) {
throw uoe();
}
@Override
public double nextVarg(ValueLayout.OfDouble layout) {
throw uoe();
}
@Override
public MemorySegment nextVarg(ValueLayout.OfAddress layout) {
throw uoe();
}
@Override
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
throw uoe();
}
@Override
public void skip(MemoryLayout... layouts) {
throw uoe();
}
@Override
public VaList copy() {
return this;
}
@Override
public MemorySegment segment() {
return address;
}
}
static void writeOverSized(MemorySegment ptr, Class<?> type, Object o) {
// use VH_LONG for integers to zero out the whole register in the process
if (type == long.class) {

View file

@ -28,8 +28,8 @@ package jdk.internal.foreign.abi;
import jdk.internal.foreign.abi.AbstractLinker.UpcallStubFactory;
import sun.security.action.GetPropertyAction;
import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentScope;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@ -141,9 +141,9 @@ public class UpcallLinker {
ABIDescriptor abi) {}
private static Object invokeInterpBindings(MethodHandle leaf, Object[] lowLevelArgs, InvocationData invData) throws Throwable {
Binding.Context allocator = invData.callingSequence.allocationSize() != 0
? Binding.Context.ofBoundedAllocator(invData.callingSequence.allocationSize())
: Binding.Context.ofScope();
Arena allocator = invData.callingSequence.allocationSize() != 0
? SharedUtils.newBoundedArena(invData.callingSequence.allocationSize())
: SharedUtils.newEmptyArena();
try (allocator) {
/// Invoke interpreter, got array of high-level arguments back
Object[] highLevelArgs = new Object[invData.callingSequence.calleeMethodType().parameterCount()];

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,7 +25,7 @@
package jdk.internal.foreign.abi;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.Arena;
import jdk.internal.foreign.MemorySessionImpl;
@ -50,13 +50,13 @@ public final class UpcallStubs {
registerNatives();
}
static MemorySegment makeUpcall(long entry, SegmentScope scope) {
((MemorySessionImpl) scope).addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() {
static MemorySegment makeUpcall(long entry, Arena arena) {
MemorySessionImpl.toMemorySession(arena).addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() {
@Override
public void cleanup() {
freeUpcallStub(entry);
}
});
return MemorySegment.ofAddress(entry, 0, scope);
return MemorySegment.ofAddress(entry).reinterpret(arena, null);
}
}

View file

@ -30,7 +30,7 @@ import jdk.internal.foreign.abi.Architecture;
import jdk.internal.foreign.abi.StubLocations;
import jdk.internal.foreign.abi.VMStorage;
public class AArch64Architecture implements Architecture {
public final class AArch64Architecture implements Architecture {
public static final Architecture INSTANCE = new AArch64Architecture();
private static final short REG64_MASK = 0b0000_0000_0000_0001;
@ -39,6 +39,9 @@ public class AArch64Architecture implements Architecture {
private static final int INTEGER_REG_SIZE = 8;
private static final int VECTOR_REG_SIZE = 16;
// Suppresses default constructor, ensuring non-instantiability.
private AArch64Architecture() {}
@Override
public boolean isStackType(int cls) {
return cls == StorageType.STACK;

View file

@ -25,6 +25,7 @@
*/
package jdk.internal.foreign.abi.aarch64;
import java.lang.foreign.AddressLayout;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemoryLayout;
@ -44,14 +45,12 @@ import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64CallArranger;
import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64CallArranger;
import jdk.internal.foreign.Utils;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.List;
import java.util.Optional;
import static jdk.internal.foreign.PlatformLayouts.*;
import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.*;
import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.Regs.*;
@ -82,7 +81,7 @@ public abstract class CallArranger {
//
// Although the AAPCS64 says r0-7 and v0-7 are all valid return
// registers, it's not possible to generate a C function that uses
// r2-7 and v4-7 so they are omitted here.
// r2-7 and v4-7 so, they are omitted here.
protected static final ABIDescriptor C = abiFor(
new VMStorage[] { r0, r1, r2, r3, r4, r5, r6, r7, INDIRECT_RESULT},
new VMStorage[] { v0, v1, v2, v3, v4, v5, v6, v7 },
@ -158,7 +157,7 @@ public abstract class CallArranger {
boolean returnInMemory = isInMemoryReturn(cDesc.returnLayout());
if (returnInMemory) {
csb.addArgumentBindings(MemorySegment.class, AArch64.C_POINTER,
csb.addArgumentBindings(MemorySegment.class, SharedUtils.C_POINTER,
argCalc.getIndirectBindings());
} else if (cDesc.returnLayout().isPresent()) {
Class<?> carrier = mt.returnType();
@ -190,8 +189,9 @@ public abstract class CallArranger {
return handle;
}
public UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc) {
Bindings bindings = getBindings(mt, cDesc, true);
public UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc,
LinkerOptions options) {
Bindings bindings = getBindings(mt, cDesc, true, options);
final boolean dropReturn = true; /* drop return, since we don't have bindings for it */
return SharedUtils.arrangeUpcallHelper(mt, bindings.isInMemoryReturn, dropReturn, abiDescriptor(),
bindings.callingSequence);
@ -429,7 +429,7 @@ public abstract class CallArranger {
assert carrier == MemorySegment.class;
bindings.copy(layout)
.unboxAddress();
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, AArch64.C_POINTER);
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, SharedUtils.C_POINTER);
bindings.vmStore(storage, long.class);
}
case POINTER -> {
@ -464,7 +464,7 @@ public abstract class CallArranger {
List<Binding> getIndirectBindings() {
return Binding.builder()
.vmLoad(INDIRECT_RESULT, long.class)
.boxAddressRaw(Long.MAX_VALUE)
.boxAddressRaw(Long.MAX_VALUE, 1)
.build();
}
@ -480,22 +480,25 @@ public abstract class CallArranger {
StorageCalculator.StructStorage[] structStorages
= storageCalculator.structStorages((GroupLayout) layout, forHFA);
for (StorageCalculator.StructStorage structStorage : structStorages) {
for (StorageCalculator.StructStorage(
long offset, Class<?> ca, int byteWidth, VMStorage storage
) : structStorages) {
bindings.dup();
bindings.vmLoad(structStorage.storage(), structStorage.carrier())
.bufferStore(structStorage.offset(), structStorage.carrier(), structStorage.byteWidth());
bindings.vmLoad(storage, ca)
.bufferStore(offset, ca, byteWidth);
}
}
case STRUCT_REFERENCE -> {
assert carrier == MemorySegment.class;
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, AArch64.C_POINTER);
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, SharedUtils.C_POINTER);
bindings.vmLoad(storage, long.class)
.boxAddress(layout);
}
case POINTER -> {
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, (ValueLayout) layout);
AddressLayout addressLayout = (AddressLayout) layout;
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, addressLayout);
bindings.vmLoad(storage, long.class)
.boxAddressRaw(Utils.pointeeSize(layout));
.boxAddressRaw(Utils.pointeeByteSize(addressLayout), Utils.pointeeByteAlign(addressLayout));
}
case INTEGER -> {
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, (ValueLayout) layout);

View file

@ -29,12 +29,9 @@ import jdk.internal.foreign.abi.AbstractLinker;
import jdk.internal.foreign.abi.LinkerOptions;
import jdk.internal.foreign.abi.aarch64.CallArranger;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.VaList;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.function.Consumer;
/**
* ABI implementation based on ARM document "Procedure Call Standard for
@ -60,21 +57,7 @@ public final class LinuxAArch64Linker extends AbstractLinker {
}
@Override
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function) {
return CallArranger.LINUX.arrangeUpcall(targetType, function);
}
public static VaList newVaList(Consumer<VaList.Builder> actions, SegmentScope scope) {
LinuxAArch64VaList.Builder builder = LinuxAArch64VaList.builder(scope);
actions.accept(builder);
return builder.build();
}
public static VaList newVaListOfAddress(long address, SegmentScope scope) {
return LinuxAArch64VaList.ofAddress(address, scope);
}
public static VaList emptyVaList() {
return LinuxAArch64VaList.empty();
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) {
return CallArranger.LINUX.arrangeUpcall(targetType, function, options);
}
}

View file

@ -1,568 +0,0 @@
/*
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2021, Arm Limited. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.foreign.abi.aarch64.linux;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.ValueLayout;
import java.lang.foreign.VaList;
import java.lang.foreign.SegmentAllocator;
import jdk.internal.foreign.abi.aarch64.TypeClass;
import jdk.internal.foreign.MemorySessionImpl;
import jdk.internal.foreign.Utils;
import jdk.internal.foreign.abi.SharedUtils;
import java.lang.invoke.VarHandle;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static jdk.internal.foreign.PlatformLayouts.AArch64;
import static java.lang.foreign.MemoryLayout.PathElement.groupElement;
import static jdk.internal.foreign.abi.SharedUtils.SimpleVaArg;
import static jdk.internal.foreign.abi.SharedUtils.THROWING_ALLOCATOR;
import static jdk.internal.foreign.abi.aarch64.CallArranger.MAX_REGISTER_ARGUMENTS;
/**
* Standard va_list implementation as defined by AAPCS document and used on
* Linux. Variadic parameters may be passed in registers or on the stack.
*/
public non-sealed class LinuxAArch64VaList implements VaList {
// See AAPCS Appendix B "Variable Argument Lists" for definition of
// va_list on AArch64.
//
// typedef struct __va_list {
// void *__stack; // next stack param
// void *__gr_top; // end of GP arg reg save area
// void *__vr_top; // end of FP/SIMD arg reg save area
// int __gr_offs; // offset from __gr_top to next GP register arg
// int __vr_offs; // offset from __vr_top to next FP/SIMD register arg
// } va_list;
static final GroupLayout LAYOUT = MemoryLayout.structLayout(
AArch64.C_POINTER.withName("__stack"),
AArch64.C_POINTER.withName("__gr_top"),
AArch64.C_POINTER.withName("__vr_top"),
AArch64.C_INT.withName("__gr_offs"),
AArch64.C_INT.withName("__vr_offs")
).withName("__va_list");
private static final long STACK_SLOT_SIZE = 8;
private static final MemoryLayout GP_REG
= MemoryLayout.paddingLayout(64).withBitAlignment(64);
private static final MemoryLayout FP_REG
= MemoryLayout.paddingLayout(128).withBitAlignment(128);
private static final MemoryLayout LAYOUT_GP_REGS
= MemoryLayout.sequenceLayout(MAX_REGISTER_ARGUMENTS, GP_REG);
private static final MemoryLayout LAYOUT_FP_REGS
= MemoryLayout.sequenceLayout(MAX_REGISTER_ARGUMENTS, FP_REG);
private static final int GP_SLOT_SIZE = (int) GP_REG.byteSize();
private static final int FP_SLOT_SIZE = (int) FP_REG.byteSize();
private static final int MAX_GP_OFFSET = (int) LAYOUT_GP_REGS.byteSize();
private static final int MAX_FP_OFFSET = (int) LAYOUT_FP_REGS.byteSize();
private static final VarHandle VH_stack = LAYOUT.varHandle(groupElement("__stack"));
private static final VarHandle VH_gr_top = LAYOUT.varHandle(groupElement("__gr_top"));
private static final VarHandle VH_vr_top = LAYOUT.varHandle(groupElement("__vr_top"));
private static final VarHandle VH_gr_offs
= LAYOUT.varHandle(groupElement("__gr_offs"));
private static final VarHandle VH_vr_offs
= LAYOUT.varHandle(groupElement("__vr_offs"));
private static final VaList EMPTY
= new SharedUtils.EmptyVaList(emptyListAddress());
private final MemorySegment segment;
private MemorySegment stack;
private final MemorySegment gpRegsArea;
private final long gpLimit;
private final MemorySegment fpRegsArea;
private final long fpLimit;
private LinuxAArch64VaList(MemorySegment segment, MemorySegment stack,
MemorySegment gpRegsArea, long gpLimit, MemorySegment fpRegsArea, long fpLimit) {
this.segment = segment;
this.stack = stack;
this.gpRegsArea = gpRegsArea;
this.gpLimit = gpLimit;
this.fpRegsArea = fpRegsArea;
this.fpLimit = fpLimit;
}
private static LinuxAArch64VaList readFromAddress(long address, SegmentScope scope) {
MemorySegment segment = MemorySegment.ofAddress(address, LAYOUT.byteSize(), scope);
MemorySegment stack = stackPtr(segment); // size unknown
MemorySegment gpRegsArea = MemorySegment.ofAddress(grTop(segment).address() - MAX_GP_OFFSET, MAX_GP_OFFSET, scope);
MemorySegment fpRegsArea = MemorySegment.ofAddress(vrTop(segment).address() - MAX_FP_OFFSET, MAX_FP_OFFSET, scope);
return new LinuxAArch64VaList(segment, stack, gpRegsArea, MAX_GP_OFFSET, fpRegsArea, MAX_FP_OFFSET);
}
private static MemorySegment emptyListAddress() {
MemorySegment ms = MemorySegment.allocateNative(LAYOUT, SegmentScope.global());
VH_stack.set(ms, MemorySegment.NULL);
VH_gr_top.set(ms, MemorySegment.NULL);
VH_vr_top.set(ms, MemorySegment.NULL);
VH_gr_offs.set(ms, 0);
VH_vr_offs.set(ms, 0);
return ms.asSlice(0, 0);
}
public static VaList empty() {
return EMPTY;
}
private MemorySegment grTop() {
return grTop(segment);
}
private static MemorySegment grTop(MemorySegment segment) {
return (MemorySegment) VH_gr_top.get(segment);
}
private MemorySegment vrTop() {
return vrTop(segment);
}
private static MemorySegment vrTop(MemorySegment segment) {
return (MemorySegment) VH_vr_top.get(segment);
}
private int grOffs() {
final int offs = (int) VH_gr_offs.get(segment);
assert offs <= 0;
return offs;
}
private int vrOffs() {
final int offs = (int) VH_vr_offs.get(segment);
assert offs <= 0;
return offs;
}
private static MemorySegment stackPtr(MemorySegment segment) {
return (MemorySegment) VH_stack.get(segment);
}
private MemorySegment stackPtr() {
return stackPtr(segment);
}
private void setStack(MemorySegment newStack) {
stack = newStack;
VH_stack.set(segment, stack);
}
private void consumeGPSlots(int num) {
final int old = (int) VH_gr_offs.get(segment);
VH_gr_offs.set(segment, old + num * GP_SLOT_SIZE);
}
private void consumeFPSlots(int num) {
final int old = (int) VH_vr_offs.get(segment);
VH_vr_offs.set(segment, old + num * FP_SLOT_SIZE);
}
private long currentGPOffset() {
// Offset from start of GP register segment. __gr_top points to the top
// (highest address) of the GP registers area. __gr_offs is the negative
// offset of next saved register from the top.
return gpRegsArea.byteSize() + grOffs();
}
private long currentFPOffset() {
// Offset from start of FP register segment. __vr_top points to the top
// (highest address) of the FP registers area. __vr_offs is the negative
// offset of next saved register from the top.
return fpRegsArea.byteSize() + vrOffs();
}
private long preAlignOffset(MemoryLayout layout) {
long alignmentOffset = 0;
if (layout.byteAlignment() > STACK_SLOT_SIZE) {
long addr = stack.address();
alignmentOffset = Utils.alignUp(addr, 16) - addr;
}
return alignmentOffset;
}
private void preAlignStack(MemoryLayout layout) {
setStack(stack.asSlice(preAlignOffset(layout)));
}
private void postAlignStack(MemoryLayout layout) {
setStack(stack.asSlice(Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE)));
}
@Override
public int nextVarg(ValueLayout.OfInt layout) {
return (int) read(layout);
}
@Override
public long nextVarg(ValueLayout.OfLong layout) {
return (long) read(layout);
}
@Override
public double nextVarg(ValueLayout.OfDouble layout) {
return (double) read(layout);
}
@Override
public MemorySegment nextVarg(ValueLayout.OfAddress layout) {
return (MemorySegment) read(layout);
}
@Override
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(allocator);
return (MemorySegment) read( layout, allocator);
}
private Object read(MemoryLayout layout) {
return read(layout, THROWING_ALLOCATOR);
}
private Object read(MemoryLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(layout);
TypeClass typeClass = TypeClass.classifyLayout(layout);
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass, layout)) {
checkStackElement(layout);
preAlignStack(layout);
return switch (typeClass) {
case STRUCT_REGISTER, STRUCT_HFA, STRUCT_REFERENCE -> {
MemorySegment slice = stack.asSlice(0, layout.byteSize());
MemorySegment seg = allocator.allocate(layout);
seg.copyFrom(slice);
postAlignStack(layout);
yield seg;
}
case POINTER, INTEGER, FLOAT -> {
VarHandle reader = layout.varHandle();
MemorySegment slice = stack.asSlice(0, layout.byteSize());
Object res = reader.get(slice);
postAlignStack(layout);
yield res;
}
};
} else {
return switch (typeClass) {
case STRUCT_REGISTER -> {
checkGPElement(layout, numSlots(layout));
// Struct is passed packed in integer registers.
MemorySegment value = allocator.allocate(layout);
long offset = 0;
while (offset < layout.byteSize()) {
final long copy = Math.min(layout.byteSize() - offset, 8);
MemorySegment.copy(gpRegsArea, currentGPOffset(), value, offset, copy);
consumeGPSlots(1);
offset += copy;
}
yield value;
}
case STRUCT_HFA -> {
checkFPElement(layout, numSlots(layout));
// Struct is passed with each element in a separate floating
// point register.
MemorySegment value = allocator.allocate(layout);
GroupLayout group = (GroupLayout)layout;
long offset = 0;
for (MemoryLayout elem : group.memberLayouts()) {
assert elem.byteSize() <= 8;
final long copy = elem.byteSize();
MemorySegment.copy(fpRegsArea, currentFPOffset(), value, offset, copy);
consumeFPSlots(1);
offset += copy;
}
yield value;
}
case STRUCT_REFERENCE -> {
checkGPElement(layout, 1);
// Struct is passed indirectly via a pointer in an integer register.
VarHandle ptrReader = AArch64.C_POINTER.varHandle();
MemorySegment ptr = (MemorySegment) ptrReader.get(
gpRegsArea.asSlice(currentGPOffset()));
consumeGPSlots(1);
MemorySegment slice = MemorySegment.ofAddress(ptr.address(), layout.byteSize(), segment.scope());
MemorySegment seg = allocator.allocate(layout);
seg.copyFrom(slice);
yield seg;
}
case POINTER, INTEGER -> {
checkGPElement(layout, 1);
VarHandle reader = layout.varHandle();
Object res = reader.get(gpRegsArea.asSlice(currentGPOffset()));
consumeGPSlots(1);
yield res;
}
case FLOAT -> {
checkFPElement(layout, 1);
VarHandle reader = layout.varHandle();
Object res = reader.get(fpRegsArea.asSlice(currentFPOffset()));
consumeFPSlots(1);
yield res;
}
};
}
}
private void checkGPElement(MemoryLayout layout, long slots) {
if ((grOffs() + MAX_GP_OFFSET) + (slots * GP_SLOT_SIZE) > gpLimit) {
throw SharedUtils.newVaListNSEE(layout);
}
}
private void checkFPElement(MemoryLayout layout, long slots) {
if ((vrOffs() + MAX_FP_OFFSET) + (slots * FP_SLOT_SIZE) > fpLimit) {
throw SharedUtils.newVaListNSEE(layout);
}
}
private void checkStackElement(MemoryLayout layout) {
if (preAlignOffset(layout) + layout.byteSize() > stack.byteSize()) {
throw SharedUtils.newVaListNSEE(layout);
}
}
@Override
public void skip(MemoryLayout... layouts) {
Objects.requireNonNull(layouts);
((MemorySessionImpl) segment.scope()).checkValidState();
for (MemoryLayout layout : layouts) {
Objects.requireNonNull(layout);
TypeClass typeClass = TypeClass.classifyLayout(layout);
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass, layout)) {
checkStackElement(layout);
preAlignStack(layout);
postAlignStack(layout);
} else if (typeClass == TypeClass.FLOAT || typeClass == TypeClass.STRUCT_HFA) {
long slots = numSlots(layout);
checkFPElement(layout, slots);
consumeFPSlots((int) slots);
} else if (typeClass == TypeClass.STRUCT_REFERENCE) {
checkGPElement(layout, 1);
consumeGPSlots(1);
} else {
long slots = numSlots(layout);
checkGPElement(layout, slots);
consumeGPSlots((int) slots);
}
}
}
static LinuxAArch64VaList.Builder builder(SegmentScope scope) {
return new LinuxAArch64VaList.Builder(scope);
}
public static VaList ofAddress(long address, SegmentScope scope) {
return readFromAddress(address, scope);
}
@Override
public VaList copy() {
MemorySegment copy = MemorySegment.allocateNative(LAYOUT, segment.scope());
copy.copyFrom(segment);
return new LinuxAArch64VaList(copy, stack, gpRegsArea, gpLimit, fpRegsArea, fpLimit);
}
@Override
public MemorySegment segment() {
// make sure that returned segment cannot be accessed
return segment.asSlice(0, 0);
}
private static long numSlots(MemoryLayout layout) {
return Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE) / STACK_SLOT_SIZE;
}
private static boolean isRegOverflow(long currentGPOffset, long currentFPOffset,
TypeClass typeClass, MemoryLayout layout) {
if (typeClass == TypeClass.FLOAT || typeClass == TypeClass.STRUCT_HFA) {
return currentFPOffset > MAX_FP_OFFSET - numSlots(layout) * FP_SLOT_SIZE;
} else if (typeClass == TypeClass.STRUCT_REFERENCE) {
return currentGPOffset > MAX_GP_OFFSET - GP_SLOT_SIZE;
} else {
return currentGPOffset > MAX_GP_OFFSET - numSlots(layout) * GP_SLOT_SIZE;
}
}
@Override
public String toString() {
return "LinuxAArch64VaList{"
+ "__stack=" + stackPtr()
+ ", __gr_top=" + grTop()
+ ", __vr_top=" + vrTop()
+ ", __gr_offs=" + grOffs()
+ ", __vr_offs=" + vrOffs()
+ '}';
}
public static non-sealed class Builder implements VaList.Builder {
private final SegmentScope scope;
private final MemorySegment gpRegs;
private final MemorySegment fpRegs;
private long currentGPOffset = 0;
private long currentFPOffset = 0;
private final List<SimpleVaArg> stackArgs = new ArrayList<>();
Builder(SegmentScope scope) {
this.scope = scope;
this.gpRegs = MemorySegment.allocateNative(LAYOUT_GP_REGS, scope);
this.fpRegs = MemorySegment.allocateNative(LAYOUT_FP_REGS, scope);
}
@Override
public Builder addVarg(ValueLayout.OfInt layout, int value) {
return arg(layout, value);
}
@Override
public Builder addVarg(ValueLayout.OfLong layout, long value) {
return arg(layout, value);
}
@Override
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
return arg(layout, value);
}
@Override
public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) {
return arg(layout, value);
}
@Override
public Builder addVarg(GroupLayout layout, MemorySegment value) {
return arg(layout, value);
}
private Builder arg(MemoryLayout layout, Object value) {
Objects.requireNonNull(layout);
Objects.requireNonNull(value);
TypeClass typeClass = TypeClass.classifyLayout(layout);
if (isRegOverflow(currentGPOffset, currentFPOffset, typeClass, layout)) {
stackArgs.add(new SimpleVaArg(layout, value));
} else {
switch (typeClass) {
case STRUCT_REGISTER -> {
// Struct is passed packed in integer registers.
MemorySegment valueSegment = (MemorySegment) value;
long offset = 0;
while (offset < layout.byteSize()) {
final long copy = Math.min(layout.byteSize() - offset, 8);
MemorySegment.copy(valueSegment, offset, gpRegs, currentGPOffset, copy);
currentGPOffset += GP_SLOT_SIZE;
offset += copy;
}
}
case STRUCT_HFA -> {
// Struct is passed with each element in a separate floating
// point register.
MemorySegment valueSegment = (MemorySegment) value;
GroupLayout group = (GroupLayout)layout;
long offset = 0;
for (MemoryLayout elem : group.memberLayouts()) {
assert elem.byteSize() <= 8;
final long copy = elem.byteSize();
MemorySegment.copy(valueSegment, offset, fpRegs, currentFPOffset, copy);
currentFPOffset += FP_SLOT_SIZE;
offset += copy;
}
}
case STRUCT_REFERENCE -> {
// Struct is passed indirectly via a pointer in an integer register.
MemorySegment valueSegment = (MemorySegment) value;
VarHandle writer = AArch64.C_POINTER.varHandle();
writer.set(gpRegs.asSlice(currentGPOffset),
valueSegment);
currentGPOffset += GP_SLOT_SIZE;
}
case POINTER, INTEGER -> {
VarHandle writer = layout.varHandle();
writer.set(gpRegs.asSlice(currentGPOffset), value);
currentGPOffset += GP_SLOT_SIZE;
}
case FLOAT -> {
VarHandle writer = layout.varHandle();
writer.set(fpRegs.asSlice(currentFPOffset), value);
currentFPOffset += FP_SLOT_SIZE;
}
}
}
return this;
}
private boolean isEmpty() {
return currentGPOffset == 0 && currentFPOffset == 0 && stackArgs.isEmpty();
}
public VaList build() {
if (isEmpty()) {
return EMPTY;
}
MemorySegment vaListSegment = MemorySegment.allocateNative(LAYOUT, scope);
MemorySegment stackArgsSegment;
if (!stackArgs.isEmpty()) {
long stackArgsSize = stackArgs.stream()
.reduce(0L, (acc, e) -> acc + Utils.alignUp(e.layout.byteSize(), STACK_SLOT_SIZE), Long::sum);
stackArgsSegment = MemorySegment.allocateNative(stackArgsSize, 16, scope);
MemorySegment writeCursor = stackArgsSegment;
for (SimpleVaArg arg : stackArgs) {
final long alignedSize = Utils.alignUp(arg.layout.byteSize(), STACK_SLOT_SIZE);
writeCursor = Utils.alignUp(writeCursor, alignedSize);
VarHandle writer = arg.varHandle();
writer.set(writeCursor, arg.value);
writeCursor = writeCursor.asSlice(alignedSize);
}
} else {
stackArgsSegment = MemorySegment.NULL;
}
VH_gr_top.set(vaListSegment, gpRegs.asSlice(gpRegs.byteSize()));
VH_vr_top.set(vaListSegment, fpRegs.asSlice(fpRegs.byteSize()));
VH_stack.set(vaListSegment, stackArgsSegment);
VH_gr_offs.set(vaListSegment, -MAX_GP_OFFSET);
VH_vr_offs.set(vaListSegment, -MAX_FP_OFFSET);
assert MemorySessionImpl.sameOwnerThread(gpRegs.scope(), vaListSegment.scope());
assert MemorySessionImpl.sameOwnerThread(fpRegs.scope(), vaListSegment.scope());
return new LinuxAArch64VaList(vaListSegment, stackArgsSegment, gpRegs, currentGPOffset, fpRegs, currentFPOffset);
}
}
}

View file

@ -30,11 +30,8 @@ import jdk.internal.foreign.abi.LinkerOptions;
import jdk.internal.foreign.abi.aarch64.CallArranger;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.VaList;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.function.Consumer;
/**
* ABI implementation for macOS on Apple silicon. Based on AAPCS with
@ -60,21 +57,7 @@ public final class MacOsAArch64Linker extends AbstractLinker {
}
@Override
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function) {
return CallArranger.MACOS.arrangeUpcall(targetType, function);
}
public static VaList newVaList(Consumer<VaList.Builder> actions, SegmentScope scope) {
MacOsAArch64VaList.Builder builder = MacOsAArch64VaList.builder(scope);
actions.accept(builder);
return builder.build();
}
public static VaList newVaListOfAddress(long address, SegmentScope scope) {
return MacOsAArch64VaList.ofAddress(address, scope);
}
public static VaList emptyVaList() {
return MacOsAArch64VaList.empty();
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) {
return CallArranger.MACOS.arrangeUpcall(targetType, function, options);
}
}

View file

@ -1,257 +0,0 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, Arm Limited. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.foreign.abi.aarch64.macos;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.SegmentAllocator;
import java.lang.foreign.VaList;
import java.lang.foreign.ValueLayout;
import jdk.internal.foreign.abi.aarch64.TypeClass;
import jdk.internal.foreign.MemorySessionImpl;
import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.foreign.abi.SharedUtils.SimpleVaArg;
import java.lang.invoke.VarHandle;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static jdk.internal.foreign.PlatformLayouts.AArch64.C_POINTER;
import static jdk.internal.foreign.abi.SharedUtils.alignUp;
/**
* Simplified va_list implementation used on macOS where all variadic
* parameters are passed on the stack and the type of va_list decays to
* char* instead of the structure defined in the AAPCS.
*/
public non-sealed class MacOsAArch64VaList implements VaList {
private static final long VA_SLOT_SIZE_BYTES = 8;
private static final VarHandle VH_address = C_POINTER.varHandle();
private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemorySegment.NULL);
private MemorySegment segment;
private MacOsAArch64VaList(MemorySegment segment) {
this.segment = segment;
}
public static VaList empty() {
return EMPTY;
}
@Override
public int nextVarg(ValueLayout.OfInt layout) {
return (int) read(layout);
}
@Override
public long nextVarg(ValueLayout.OfLong layout) {
return (long) read(layout);
}
@Override
public double nextVarg(ValueLayout.OfDouble layout) {
return (double) read(layout);
}
@Override
public MemorySegment nextVarg(ValueLayout.OfAddress layout) {
return (MemorySegment) read(layout);
}
@Override
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(allocator);
return (MemorySegment) read(layout, allocator);
}
private Object read(MemoryLayout layout) {
return read(layout, SharedUtils.THROWING_ALLOCATOR);
}
private Object read(MemoryLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(layout);
Object res;
if (layout instanceof GroupLayout) {
TypeClass typeClass = TypeClass.classifyLayout(layout);
res = switch (typeClass) {
case STRUCT_REFERENCE -> {
checkElement(layout, VA_SLOT_SIZE_BYTES);
MemorySegment structAddr = (MemorySegment) VH_address.get(segment);
MemorySegment struct = MemorySegment.ofAddress(structAddr.address(), layout.byteSize(), segment.scope());
MemorySegment seg = allocator.allocate(layout);
seg.copyFrom(struct);
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
yield seg;
}
case STRUCT_REGISTER, STRUCT_HFA -> {
long size = alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES);
checkElement(layout, size);
MemorySegment struct = allocator.allocate(layout)
.copyFrom(segment.asSlice(0, layout.byteSize()));
segment = segment.asSlice(size);
yield struct;
}
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
};
} else {
checkElement(layout, VA_SLOT_SIZE_BYTES);
VarHandle reader = layout.varHandle();
res = reader.get(segment);
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
}
return res;
}
private static long sizeOf(MemoryLayout layout) {
return switch (TypeClass.classifyLayout(layout)) {
case STRUCT_REGISTER, STRUCT_HFA -> alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES);
default -> VA_SLOT_SIZE_BYTES;
};
}
@Override
public void skip(MemoryLayout... layouts) {
Objects.requireNonNull(layouts);
((MemorySessionImpl) segment.scope()).checkValidState();
for (MemoryLayout layout : layouts) {
Objects.requireNonNull(layout);
long size = sizeOf(layout);
checkElement(layout, size);
segment = segment.asSlice(size);
}
}
private void checkElement(MemoryLayout layout, long size) {
if (segment.byteSize() < size) {
throw SharedUtils.newVaListNSEE(layout);
}
}
static MacOsAArch64VaList ofAddress(long address, SegmentScope session) {
MemorySegment segment = MemorySegment.ofAddress(address, Long.MAX_VALUE, session);
return new MacOsAArch64VaList(segment);
}
static Builder builder(SegmentScope session) {
return new Builder(session);
}
@Override
public VaList copy() {
((MemorySessionImpl) segment.scope()).checkValidState();
return new MacOsAArch64VaList(segment);
}
@Override
public MemorySegment segment() {
// make sure that returned segment cannot be accessed
return segment.asSlice(0, 0);
}
public static non-sealed class Builder implements VaList.Builder {
private final SegmentScope session;
private final List<SimpleVaArg> args = new ArrayList<>();
public Builder(SegmentScope session) {
((MemorySessionImpl) session).checkValidState();
this.session = session;
}
private Builder arg(MemoryLayout layout, Object value) {
Objects.requireNonNull(layout);
Objects.requireNonNull(value);
args.add(new SimpleVaArg(layout, value));
return this;
}
@Override
public Builder addVarg(ValueLayout.OfInt layout, int value) {
return arg(layout, value);
}
@Override
public Builder addVarg(ValueLayout.OfLong layout, long value) {
return arg(layout, value);
}
@Override
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
return arg(layout, value);
}
@Override
public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) {
return arg(layout, value);
}
@Override
public Builder addVarg(GroupLayout layout, MemorySegment value) {
return arg(layout, value);
}
public VaList build() {
if (args.isEmpty()) {
return EMPTY;
}
long allocationSize = args.stream().reduce(0L, (acc, e) -> acc + sizeOf(e.layout), Long::sum);
MemorySegment segment = MemorySegment.allocateNative(allocationSize, session);
MemorySegment cursor = segment;
for (SimpleVaArg arg : args) {
if (arg.layout instanceof GroupLayout) {
MemorySegment msArg = ((MemorySegment) arg.value);
TypeClass typeClass = TypeClass.classifyLayout(arg.layout);
switch (typeClass) {
case STRUCT_REFERENCE -> {
MemorySegment copy = MemorySegment.allocateNative(arg.layout, session);
copy.copyFrom(msArg); // by-value
VH_address.set(cursor, copy);
cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES);
}
case STRUCT_REGISTER, STRUCT_HFA ->
cursor.copyFrom(msArg.asSlice(0, arg.layout.byteSize()))
.asSlice(alignUp(arg.layout.byteSize(), VA_SLOT_SIZE_BYTES));
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
}
} else {
VarHandle writer = arg.varHandle();
writer.set(cursor, arg.value);
cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES);
}
}
return new MacOsAArch64VaList(segment);
}
}
}

View file

@ -31,11 +31,8 @@ import jdk.internal.foreign.abi.LinkerOptions;
import jdk.internal.foreign.abi.aarch64.CallArranger;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.VaList;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.function.Consumer;
/**
* ABI implementation for Windows/AArch64. Based on AAPCS with
@ -57,22 +54,7 @@ public final class WindowsAArch64Linker extends AbstractLinker {
}
@Override
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function) {
return CallArranger.WINDOWS.arrangeUpcall(targetType, function);
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) {
return CallArranger.WINDOWS.arrangeUpcall(targetType, function, options);
}
public static VaList newVaList(Consumer<VaList.Builder> actions, SegmentScope scope) {
WindowsAArch64VaList.Builder builder = WindowsAArch64VaList.builder(scope);
actions.accept(builder);
return builder.build();
}
public static VaList newVaListOfAddress(long address, SegmentScope scope) {
return WindowsAArch64VaList.ofAddress(address, scope);
}
public static VaList emptyVaList() {
return WindowsAArch64VaList.empty();
}
}

View file

@ -1,261 +0,0 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, Arm Limited. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.foreign.abi.aarch64.windows;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.SegmentAllocator;
import java.lang.foreign.VaList;
import java.lang.foreign.ValueLayout;
import jdk.internal.foreign.abi.aarch64.TypeClass;
import jdk.internal.foreign.MemorySessionImpl;
import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.foreign.abi.SharedUtils.SimpleVaArg;
import java.lang.invoke.VarHandle;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static jdk.internal.foreign.PlatformLayouts.AArch64.C_POINTER;
import static jdk.internal.foreign.abi.SharedUtils.alignUp;
// see vadefs.h (VC header) for the ARM64 va_arg impl
//
// typedef char* va_list;
//
// #define __crt_va_arg(ap, t) \
// ((sizeof(t) > (2 * sizeof(__int64))) \
// ? **(t**)((ap += sizeof(__int64)) - sizeof(__int64)) \
// : *(t*)((ap += _SLOTSIZEOF(t) + _APALIGN(t,ap)) - _SLOTSIZEOF(t)))
//
public non-sealed class WindowsAArch64VaList implements VaList {
private static final long VA_SLOT_SIZE_BYTES = 8;
private static final VarHandle VH_address = C_POINTER.varHandle();
private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemorySegment.NULL);
private MemorySegment segment;
private WindowsAArch64VaList(MemorySegment segment) {
this.segment = segment;
}
public static VaList empty() {
return EMPTY;
}
@Override
public int nextVarg(ValueLayout.OfInt layout) {
return (int) read(layout);
}
@Override
public long nextVarg(ValueLayout.OfLong layout) {
return (long) read(layout);
}
@Override
public double nextVarg(ValueLayout.OfDouble layout) {
return (double) read(layout);
}
@Override
public MemorySegment nextVarg(ValueLayout.OfAddress layout) {
return (MemorySegment) read(layout);
}
@Override
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(allocator);
return (MemorySegment) read(layout, allocator);
}
private Object read(MemoryLayout layout) {
return read(layout, SharedUtils.THROWING_ALLOCATOR);
}
private Object read(MemoryLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(layout);
Object res;
if (layout instanceof GroupLayout) {
TypeClass typeClass = TypeClass.classifyLayout(layout);
res = switch (typeClass) {
case STRUCT_REFERENCE -> {
checkElement(layout, VA_SLOT_SIZE_BYTES);
MemorySegment structAddr = (MemorySegment) VH_address.get(segment);
MemorySegment struct = MemorySegment.ofAddress(structAddr.address(), layout.byteSize(), segment.scope());
MemorySegment seg = allocator.allocate(layout);
seg.copyFrom(struct);
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
yield seg;
}
case STRUCT_REGISTER, STRUCT_HFA -> {
long size = alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES);
checkElement(layout, size);
MemorySegment struct = allocator.allocate(layout)
.copyFrom(segment.asSlice(0, layout.byteSize()));
segment = segment.asSlice(size);
yield struct;
}
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
};
} else {
checkElement(layout, VA_SLOT_SIZE_BYTES);
VarHandle reader = layout.varHandle();
res = reader.get(segment);
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
}
return res;
}
private static long sizeOf(MemoryLayout layout) {
return switch (TypeClass.classifyLayout(layout)) {
case STRUCT_REGISTER, STRUCT_HFA -> alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES);
default -> VA_SLOT_SIZE_BYTES;
};
}
@Override
public void skip(MemoryLayout... layouts) {
Objects.requireNonNull(layouts);
((MemorySessionImpl) segment.scope()).checkValidState();
for (MemoryLayout layout : layouts) {
Objects.requireNonNull(layout);
long size = sizeOf(layout);
checkElement(layout, size);
segment = segment.asSlice(size);
}
}
private void checkElement(MemoryLayout layout, long size) {
if (segment.byteSize() < size) {
throw SharedUtils.newVaListNSEE(layout);
}
}
static WindowsAArch64VaList ofAddress(long address, SegmentScope session) {
MemorySegment segment = MemorySegment.ofAddress(address, Long.MAX_VALUE, session);
return new WindowsAArch64VaList(segment);
}
static Builder builder(SegmentScope session) {
return new Builder(session);
}
@Override
public VaList copy() {
((MemorySessionImpl) segment.scope()).checkValidState();
return new WindowsAArch64VaList(segment);
}
@Override
public MemorySegment segment() {
// make sure that returned segment cannot be accessed
return segment.asSlice(0, 0);
}
public static non-sealed class Builder implements VaList.Builder {
private final SegmentScope session;
private final List<SimpleVaArg> args = new ArrayList<>();
public Builder(SegmentScope session) {
((MemorySessionImpl) session).checkValidState();
this.session = session;
}
private Builder arg(MemoryLayout layout, Object value) {
Objects.requireNonNull(layout);
Objects.requireNonNull(value);
args.add(new SimpleVaArg(layout, value));
return this;
}
@Override
public Builder addVarg(ValueLayout.OfInt layout, int value) {
return arg(layout, value);
}
@Override
public Builder addVarg(ValueLayout.OfLong layout, long value) {
return arg(layout, value);
}
@Override
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
return arg(layout, value);
}
@Override
public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) {
return arg(layout, value);
}
@Override
public Builder addVarg(GroupLayout layout, MemorySegment value) {
return arg(layout, value);
}
public VaList build() {
if (args.isEmpty()) {
return EMPTY;
}
long allocationSize = args.stream().reduce(0L, (acc, e) -> acc + sizeOf(e.layout), Long::sum);
MemorySegment segment = MemorySegment.allocateNative(allocationSize, session);
MemorySegment cursor = segment;
for (SimpleVaArg arg : args) {
if (arg.layout instanceof GroupLayout) {
MemorySegment msArg = ((MemorySegment) arg.value);
TypeClass typeClass = TypeClass.classifyLayout(arg.layout);
switch (typeClass) {
case STRUCT_REFERENCE -> {
MemorySegment copy = MemorySegment.allocateNative(arg.layout, session);
copy.copyFrom(msArg); // by-value
VH_address.set(cursor, copy);
cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES);
}
case STRUCT_REGISTER, STRUCT_HFA ->
cursor = cursor.copyFrom(msArg.asSlice(0, arg.layout.byteSize()))
.asSlice(alignUp(arg.layout.byteSize(), VA_SLOT_SIZE_BYTES));
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
}
} else {
VarHandle writer = arg.varHandle();
writer.set(cursor, arg.value);
cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES);
}
}
return new WindowsAArch64VaList(segment);
}
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.foreign.abi.fallback;
/**
* enum which maps the {@code ffi_abi} enum
*/
enum FFIABI {
DEFAULT(LibFallback.defaultABI());
private final int value;
FFIABI(int abi) {
this.value = abi;
}
int value() {
return value;
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.foreign.abi.fallback;
/**
* See doc: <a href="https://github.com/libffi/libffi/blob/7611bb4cfe90884b55ad225e0166136a1d2cf22b/doc/libffi.texi#L159"></a>
* <p>
* typedef enum {
* FFI_OK = 0,
* FFI_BAD_TYPEDEF,
* FFI_BAD_ABI,
* FFI_BAD_ARGTYPE
* } ffi_status;
*/
enum FFIStatus {
FFI_OK,
FFI_BAD_TYPEDEF,
FFI_BAD_ABI,
FFI_BAD_ARGTYPE;
static FFIStatus of(int code) {
return switch (code) {
case 0 -> FFI_OK;
case 1 -> FFI_BAD_TYPEDEF;
case 2 -> FFI_BAD_ABI;
case 3 -> FFI_BAD_ARGTYPE;
default -> throw new IllegalArgumentException("Unknown status code: " + code);
};
}
}

View file

@ -0,0 +1,146 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.foreign.abi.fallback;
import jdk.internal.foreign.Utils;
import java.lang.foreign.Arena;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.PaddingLayout;
import java.lang.foreign.SequenceLayout;
import java.lang.foreign.StructLayout;
import java.lang.foreign.UnionLayout;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.VarHandle;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
import static java.lang.foreign.ValueLayout.ADDRESS;
import static java.lang.foreign.ValueLayout.JAVA_INT;
import static java.lang.foreign.ValueLayout.JAVA_LONG;
import static java.lang.foreign.ValueLayout.JAVA_SHORT;
/**
* typedef struct _ffi_type
* {
* size_t size;
* unsigned short alignment;
* unsigned short type;
* struct _ffi_type **elements;
* } ffi_type;
*/
class FFIType {
private static final ValueLayout SIZE_T = switch ((int) ADDRESS.bitSize()) {
case 64 -> JAVA_LONG;
case 32 -> JAVA_INT;
default -> throw new IllegalStateException("Address size not supported: " + ADDRESS.byteSize());
};
private static final ValueLayout UNSIGNED_SHORT = JAVA_SHORT;
private static final StructLayout LAYOUT = Utils.computePaddedStructLayout(
SIZE_T, UNSIGNED_SHORT, UNSIGNED_SHORT.withName("type"), ADDRESS.withName("elements"));
private static final VarHandle VH_TYPE = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("type"));
private static final VarHandle VH_ELEMENTS = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("elements"));
private static final VarHandle VH_SIZE_T_ARRAY = SIZE_T.arrayElementVarHandle();
private static MemorySegment make(List<MemoryLayout> elements, FFIABI abi, Arena scope) {
MemorySegment elementsSeg = scope.allocate((elements.size() + 1) * ADDRESS.byteSize());
int i = 0;
for (; i < elements.size(); i++) {
MemoryLayout elementLayout = elements.get(i);
MemorySegment elementType = toFFIType(elementLayout, abi, scope);
elementsSeg.setAtIndex(ADDRESS, i, elementType);
}
// elements array is null-terminated
elementsSeg.setAtIndex(ADDRESS, i, MemorySegment.NULL);
MemorySegment ffiType = scope.allocate(LAYOUT);
VH_TYPE.set(ffiType, LibFallback.structTag());
VH_ELEMENTS.set(ffiType, elementsSeg);
return ffiType;
}
private static final Map<Class<?>, MemorySegment> CARRIER_TO_TYPE = Map.of(
boolean.class, LibFallback.uint8Type(),
byte.class, LibFallback.sint8Type(),
short.class, LibFallback.sint16Type(),
char.class, LibFallback.uint16Type(),
int.class, LibFallback.sint32Type(),
long.class, LibFallback.sint64Type(),
float.class, LibFallback.floatType(),
double.class, LibFallback.doubleType(),
MemorySegment.class, LibFallback.pointerType()
);
static MemorySegment toFFIType(MemoryLayout layout, FFIABI abi, Arena scope) {
if (layout instanceof GroupLayout grpl) {
if (grpl instanceof StructLayout strl) {
// libffi doesn't want our padding
List<MemoryLayout> filteredLayouts = strl.memberLayouts().stream()
.filter(Predicate.not(PaddingLayout.class::isInstance))
.toList();
MemorySegment structType = make(filteredLayouts, abi, scope);
verifyStructType(strl, filteredLayouts, structType, abi);
return structType;
}
assert grpl instanceof UnionLayout;
// JDK-8301800
throw new IllegalArgumentException("Fallback linker does not support by-value unions: " + grpl);
} else if (layout instanceof SequenceLayout sl) {
List<MemoryLayout> elements = Collections.nCopies(Math.toIntExact(sl.elementCount()), sl.elementLayout());
return make(elements, abi, scope);
}
return Objects.requireNonNull(CARRIER_TO_TYPE.get(((ValueLayout) layout).carrier()));
}
// verify layout against what libffi sets
private static void verifyStructType(StructLayout structLayout, List<MemoryLayout> filteredLayouts, MemorySegment structType,
FFIABI abi) {
try (Arena verifyArena = Arena.ofConfined()) {
MemorySegment offsetsOut = verifyArena.allocate(SIZE_T.byteSize() * filteredLayouts.size());
LibFallback.getStructOffsets(structType, offsetsOut, abi);
long expectedOffset = 0;
int offsetIdx = 0;
for (MemoryLayout element : structLayout.memberLayouts()) {
if (!(element instanceof PaddingLayout)) {
long ffiOffset = (long) VH_SIZE_T_ARRAY.get(offsetsOut, offsetIdx++);
if (ffiOffset != expectedOffset) {
throw new IllegalArgumentException("Invalid group layout." +
" Offset of '" + element.name().orElse("<unnamed>")
+ "': " + expectedOffset + " != " + ffiOffset);
}
}
expectedOffset += element.byteSize();
}
}
}
}

View file

@ -0,0 +1,272 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.foreign.abi.fallback;
import jdk.internal.foreign.AbstractMemorySegmentImpl;
import jdk.internal.foreign.MemorySessionImpl;
import jdk.internal.foreign.abi.AbstractLinker;
import jdk.internal.foreign.abi.CapturableState;
import jdk.internal.foreign.abi.LinkerOptions;
import jdk.internal.foreign.abi.SharedUtils;
import java.lang.foreign.AddressLayout;
import java.lang.foreign.Arena;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentAllocator;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.ref.Reference;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import static java.lang.foreign.ValueLayout.ADDRESS;
import static java.lang.foreign.ValueLayout.JAVA_LONG;
import static java.lang.invoke.MethodHandles.foldArguments;
public final class FallbackLinker extends AbstractLinker {
private static final MethodHandle MH_DO_DOWNCALL;
private static final MethodHandle MH_DO_UPCALL;
static {
try {
MH_DO_DOWNCALL = MethodHandles.lookup().findStatic(FallbackLinker.class, "doDowncall",
MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class, FallbackLinker.DowncallData.class));
MH_DO_UPCALL = MethodHandles.lookup().findStatic(FallbackLinker.class, "doUpcall",
MethodType.methodType(void.class, MethodHandle.class, MemorySegment.class, MemorySegment.class, UpcallData.class));
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}
public static FallbackLinker getInstance() {
class Holder {
static final FallbackLinker INSTANCE = new FallbackLinker();
}
return Holder.INSTANCE;
}
public static boolean isSupported() {
return LibFallback.SUPPORTED;
}
@Override
protected MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function, LinkerOptions options) {
MemorySegment cif = makeCif(inferredMethodType, function, FFIABI.DEFAULT, Arena.ofAuto());
int capturedStateMask = options.capturedCallState()
.mapToInt(CapturableState::mask)
.reduce(0, (a, b) -> a | b);
DowncallData invData = new DowncallData(cif, function.returnLayout().orElse(null),
function.argumentLayouts(), capturedStateMask);
MethodHandle target = MethodHandles.insertArguments(MH_DO_DOWNCALL, 2, invData);
int leadingArguments = 1; // address
MethodType type = inferredMethodType.insertParameterTypes(0, SegmentAllocator.class, MemorySegment.class);
if (capturedStateMask != 0) {
leadingArguments++;
type = type.insertParameterTypes(2, MemorySegment.class);
}
target = target.asCollector(1, Object[].class, inferredMethodType.parameterCount() + leadingArguments);
target = target.asType(type);
target = foldArguments(target, 1, SharedUtils.MH_CHECK_SYMBOL);
target = SharedUtils.swapArguments(target, 0, 1); // normalize parameter order
return target;
}
@Override
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) {
MemorySegment cif = makeCif(targetType, function, FFIABI.DEFAULT, Arena.ofAuto());
UpcallData invData = new UpcallData(function.returnLayout().orElse(null), function.argumentLayouts(), cif);
MethodHandle doUpcallMH = MethodHandles.insertArguments(MH_DO_UPCALL, 3, invData);
return (target, scope) -> {
target = MethodHandles.insertArguments(doUpcallMH, 0, target);
return LibFallback.createClosure(cif, target, scope);
};
}
private static MemorySegment makeCif(MethodType methodType, FunctionDescriptor function, FFIABI abi, Arena scope) {
MemorySegment argTypes = scope.allocate(function.argumentLayouts().size() * ADDRESS.byteSize());
List<MemoryLayout> argLayouts = function.argumentLayouts();
for (int i = 0; i < argLayouts.size(); i++) {
MemoryLayout layout = argLayouts.get(i);
argTypes.setAtIndex(ADDRESS, i, FFIType.toFFIType(layout, abi, scope));
}
MemorySegment returnType = methodType.returnType() != void.class
? FFIType.toFFIType(function.returnLayout().orElseThrow(), abi, scope)
: LibFallback.voidType();
return LibFallback.prepCif(returnType, argLayouts.size(), argTypes, abi, scope);
}
private record DowncallData(MemorySegment cif, MemoryLayout returnLayout, List<MemoryLayout> argLayouts,
int capturedStateMask) {}
private static Object doDowncall(SegmentAllocator returnAllocator, Object[] args, DowncallData invData) {
List<MemorySessionImpl> acquiredSessions = new ArrayList<>();
try (Arena arena = Arena.ofConfined()) {
int argStart = 0;
MemorySegment target = (MemorySegment) args[argStart++];
MemorySessionImpl targetImpl = ((AbstractMemorySegmentImpl) target).sessionImpl();
targetImpl.acquire0();
acquiredSessions.add(targetImpl);
MemorySegment capturedState = null;
if (invData.capturedStateMask() != 0) {
capturedState = (MemorySegment) args[argStart++];
MemorySessionImpl capturedStateImpl = ((AbstractMemorySegmentImpl) capturedState).sessionImpl();
capturedStateImpl.acquire0();
acquiredSessions.add(capturedStateImpl);
}
List<MemoryLayout> argLayouts = invData.argLayouts();
MemorySegment argPtrs = arena.allocate(argLayouts.size() * ADDRESS.byteSize());
for (int i = 0; i < argLayouts.size(); i++) {
Object arg = args[argStart + i];
MemoryLayout layout = argLayouts.get(i);
MemorySegment argSeg = arena.allocate(layout);
writeValue(arg, layout, argSeg, addr -> {
MemorySessionImpl sessionImpl = ((AbstractMemorySegmentImpl) addr).sessionImpl();
sessionImpl.acquire0();
acquiredSessions.add(sessionImpl);
});
argPtrs.setAtIndex(ADDRESS, i, argSeg);
}
MemorySegment retSeg = null;
if (invData.returnLayout() != null) {
retSeg = (invData.returnLayout() instanceof GroupLayout ? returnAllocator : arena).allocate(invData.returnLayout);
}
LibFallback.doDowncall(invData.cif, target, retSeg, argPtrs, capturedState, invData.capturedStateMask());
Reference.reachabilityFence(invData.cif());
return readValue(retSeg, invData.returnLayout());
} finally {
for (MemorySessionImpl session : acquiredSessions) {
session.release0();
}
}
}
// note that cif is not used, but we store it here to keep it alive
private record UpcallData(MemoryLayout returnLayout, List<MemoryLayout> argLayouts, MemorySegment cif) {}
private static void doUpcall(MethodHandle target, MemorySegment retPtr, MemorySegment argPtrs, UpcallData data) throws Throwable {
List<MemoryLayout> argLayouts = data.argLayouts();
int numArgs = argLayouts.size();
MemoryLayout retLayout = data.returnLayout();
try (Arena upcallArena = Arena.ofConfined()) {
MemorySegment argsSeg = argPtrs.reinterpret(numArgs * ADDRESS.byteSize(), upcallArena, null);
MemorySegment retSeg = retLayout != null
? retPtr.reinterpret(retLayout.byteSize(), upcallArena, null)
: null;
Object[] args = new Object[numArgs];
for (int i = 0; i < numArgs; i++) {
MemoryLayout argLayout = argLayouts.get(i);
MemorySegment argPtr = argsSeg.getAtIndex(ADDRESS, i)
.reinterpret(argLayout.byteSize(), upcallArena, null);
args[i] = readValue(argPtr, argLayout);
}
Object result = target.invokeWithArguments(args);
writeValue(result, data.returnLayout(), retSeg);
}
}
// where
private static void writeValue(Object arg, MemoryLayout layout, MemorySegment argSeg) {
writeValue(arg, layout, argSeg, addr -> {});
}
private static void writeValue(Object arg, MemoryLayout layout, MemorySegment argSeg,
Consumer<MemorySegment> acquireCallback) {
if (layout instanceof ValueLayout.OfBoolean bl) {
argSeg.set(bl, 0, (Boolean) arg);
} else if (layout instanceof ValueLayout.OfByte bl) {
argSeg.set(bl, 0, (Byte) arg);
} else if (layout instanceof ValueLayout.OfShort sl) {
argSeg.set(sl, 0, (Short) arg);
} else if (layout instanceof ValueLayout.OfChar cl) {
argSeg.set(cl, 0, (Character) arg);
} else if (layout instanceof ValueLayout.OfInt il) {
argSeg.set(il, 0, (Integer) arg);
} else if (layout instanceof ValueLayout.OfLong ll) {
argSeg.set(ll, 0, (Long) arg);
} else if (layout instanceof ValueLayout.OfFloat fl) {
argSeg.set(fl, 0, (Float) arg);
} else if (layout instanceof ValueLayout.OfDouble dl) {
argSeg.set(dl, 0, (Double) arg);
} else if (layout instanceof AddressLayout al) {
MemorySegment addrArg = (MemorySegment) arg;
acquireCallback.accept(addrArg);
argSeg.set(al, 0, addrArg);
} else if (layout instanceof GroupLayout) {
argSeg.copyFrom((MemorySegment) arg); // by-value struct
} else {
assert layout == null;
}
}
private static Object readValue(MemorySegment seg, MemoryLayout layout) {
if (layout instanceof ValueLayout.OfBoolean bl) {
return seg.get(bl, 0);
} else if (layout instanceof ValueLayout.OfByte bl) {
return seg.get(bl, 0);
} else if (layout instanceof ValueLayout.OfShort sl) {
return seg.get(sl, 0);
} else if (layout instanceof ValueLayout.OfChar cl) {
return seg.get(cl, 0);
} else if (layout instanceof ValueLayout.OfInt il) {
return seg.get(il, 0);
} else if (layout instanceof ValueLayout.OfLong ll) {
return seg.get(ll, 0);
} else if (layout instanceof ValueLayout.OfFloat fl) {
return seg.get(fl, 0);
} else if (layout instanceof ValueLayout.OfDouble dl) {
return seg.get(dl, 0);
} else if (layout instanceof AddressLayout al) {
return seg.get(al, 0);
} else if (layout instanceof GroupLayout) {
return seg;
}
assert layout == null;
return null;
}
}

View file

@ -0,0 +1,219 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.foreign.abi.fallback;
import jdk.internal.foreign.abi.SharedUtils;
import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
final class LibFallback {
private LibFallback() {}
static final boolean SUPPORTED = tryLoadLibrary();
private static boolean tryLoadLibrary() {
try {
System.loadLibrary("fallbackLinker");
} catch (UnsatisfiedLinkError ule) {
return false;
}
init();
return true;
}
static int defaultABI() { return NativeConstants.DEFAULT_ABI; }
static MemorySegment uint8Type() { return NativeConstants.UINT8_TYPE; }
static MemorySegment sint8Type() { return NativeConstants.SINT8_TYPE; }
static MemorySegment uint16Type() { return NativeConstants.UINT16_TYPE; }
static MemorySegment sint16Type() { return NativeConstants.SINT16_TYPE; }
static MemorySegment sint32Type() { return NativeConstants.SINT32_TYPE; }
static MemorySegment sint64Type() { return NativeConstants.SINT64_TYPE; }
static MemorySegment floatType() { return NativeConstants.FLOAT_TYPE; }
static MemorySegment doubleType() { return NativeConstants.DOUBLE_TYPE; }
static MemorySegment pointerType() { return NativeConstants.POINTER_TYPE; }
static MemorySegment voidType() { return NativeConstants.VOID_TYPE; }
static short structTag() { return NativeConstants.STRUCT_TAG; }
private static final MethodType UPCALL_TARGET_TYPE = MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class);
/**
* Do a libffi based downcall. This method wraps the {@code ffi_call} function
*
* @param cif a pointer to a {@code ffi_cif} struct
* @param target the address of the target function
* @param retPtr a pointer to a buffer into which the return value shall be written, or {@code null} if the target
* function does not return a value
* @param argPtrs a pointer to an array of pointers, which each point to an argument value
* @param capturedState a pointer to a buffer into which captured state is written, or {@code null} if no state is
* to be captured
* @param capturedStateMask the bit mask indicating which state to capture
*
* @see jdk.internal.foreign.abi.CapturableState
*/
static void doDowncall(MemorySegment cif, MemorySegment target, MemorySegment retPtr, MemorySegment argPtrs,
MemorySegment capturedState, int capturedStateMask) {
doDowncall(cif.address(), target.address(),
retPtr == null ? 0 : retPtr.address(), argPtrs.address(),
capturedState == null ? 0 : capturedState.address(), capturedStateMask);
}
/**
* Wrapper for {@code ffi_prep_cif}
*
* @param returnType a pointer to an @{code ffi_type} describing the return type
* @param numArgs the number of arguments
* @param paramTypes a pointer to an array of pointers, which each point to an {@code ffi_type} describing a
* parameter type
* @param abi the abi to be used
* @param scope the scope into which to allocate the returned {@code ffi_cif} struct
* @return a pointer to a prepared {@code ffi_cif} struct
*
* @throws IllegalStateException if the call to {@code ffi_prep_cif} returns a non-zero status code
*/
static MemorySegment prepCif(MemorySegment returnType, int numArgs, MemorySegment paramTypes, FFIABI abi,
Arena scope) throws IllegalStateException {
MemorySegment cif = scope.allocate(NativeConstants.SIZEOF_CIF);
checkStatus(ffi_prep_cif(cif.address(), abi.value(), numArgs, returnType.address(), paramTypes.address()));
return cif;
}
/**
* Create an upcallStub-style closure. This method wraps the {@code ffi_closure_alloc}
* and {@code ffi_prep_closure_loc} functions.
* <p>
* The closure will end up calling into {@link #doUpcall(long, long, MethodHandle)}
* <p>
* The target method handle should have the type {@code (MemorySegment, MemorySegment) -> void}. The first
* argument is a pointer to the buffer into which the native return value should be written. The second argument
* is a pointer to an array of pointers, which each point to a native argument value.
*
* @param cif a pointer to a {@code ffi_cif} struct
* @param target a method handle that points to the target function
* @param arena the scope to which to attach the created upcall stub
* @return the created upcall stub
*
* @throws IllegalStateException if the call to {@code ffi_prep_closure_loc} returns a non-zero status code
* @throws IllegalArgumentException if {@code target} does not have the right type
*/
static MemorySegment createClosure(MemorySegment cif, MethodHandle target, Arena arena)
throws IllegalStateException, IllegalArgumentException {
if (target.type() != UPCALL_TARGET_TYPE) {
throw new IllegalArgumentException("Target handle has wrong type: " + target.type() + " != " + UPCALL_TARGET_TYPE);
}
long[] ptrs = new long[3];
checkStatus(createClosure(cif.address(), target, ptrs));
long closurePtr = ptrs[0];
long execPtr = ptrs[1];
long globalTarget = ptrs[2];
return MemorySegment.ofAddress(execPtr).reinterpret(arena, unused -> freeClosure(closurePtr, globalTarget));
}
// the target function for a closure call
private static void doUpcall(long retPtr, long argPtrs, MethodHandle target) {
try {
target.invokeExact(MemorySegment.ofAddress(retPtr), MemorySegment.ofAddress(argPtrs));
} catch (Throwable t) {
SharedUtils.handleUncaughtException(t);
}
}
/**
* Wrapper for {@code ffi_get_struct_offsets}
*
* @param structType a pointer to an {@code ffi_type} representing a struct
* @param offsetsOut a pointer to an array of {@code size_t}, with one element for each element of the struct.
* This is an 'out' parameter that will be filled in by this call
* @param abi the abi to be used
*
* @throws IllegalStateException if the call to {@code ffi_get_struct_offsets} returns a non-zero status code
*/
static void getStructOffsets(MemorySegment structType, MemorySegment offsetsOut, FFIABI abi)
throws IllegalStateException {
checkStatus(ffi_get_struct_offsets(abi.value(), structType.address(), offsetsOut.address()));
}
private static void checkStatus(int code) {
FFIStatus status = FFIStatus.of(code);
if (status != FFIStatus.FFI_OK) {
throw new IllegalStateException("libffi call failed with status: " + status);
}
}
private static native void init();
private static native long sizeofCif();
private static native int createClosure(long cif, Object userData, long[] ptrs);
private static native void freeClosure(long closureAddress, long globalTarget);
private static native void doDowncall(long cif, long fn, long rvalue, long avalues, long capturedState, int capturedStateMask);
private static native int ffi_prep_cif(long cif, int abi, int nargs, long rtype, long atypes);
private static native int ffi_get_struct_offsets(int abi, long type, long offsets);
private static native int ffi_default_abi();
private static native short ffi_type_struct();
private static native long ffi_type_void();
private static native long ffi_type_uint8();
private static native long ffi_type_sint8();
private static native long ffi_type_uint16();
private static native long ffi_type_sint16();
private static native long ffi_type_uint32();
private static native long ffi_type_sint32();
private static native long ffi_type_uint64();
private static native long ffi_type_sint64();
private static native long ffi_type_float();
private static native long ffi_type_double();
private static native long ffi_type_pointer();
// put these in a separate class to avoid an UnsatisfiedLinkError
// when LibFallback is initialized but the library is not present
private static final class NativeConstants {
private NativeConstants() {}
static final int DEFAULT_ABI = ffi_default_abi();
static final MemorySegment UINT8_TYPE = MemorySegment.ofAddress(ffi_type_uint8());
static final MemorySegment SINT8_TYPE = MemorySegment.ofAddress(ffi_type_sint8());
static final MemorySegment UINT16_TYPE = MemorySegment.ofAddress(ffi_type_uint16());
static final MemorySegment SINT16_TYPE = MemorySegment.ofAddress(ffi_type_sint16());
static final MemorySegment SINT32_TYPE = MemorySegment.ofAddress(ffi_type_sint32());
static final MemorySegment SINT64_TYPE = MemorySegment.ofAddress(ffi_type_sint64());
static final MemorySegment FLOAT_TYPE = MemorySegment.ofAddress(ffi_type_float());
static final MemorySegment DOUBLE_TYPE = MemorySegment.ofAddress(ffi_type_double());
static final MemorySegment POINTER_TYPE = MemorySegment.ofAddress(ffi_type_pointer());
static final MemorySegment VOID_TYPE = MemorySegment.ofAddress(ffi_type_void());
static final short STRUCT_TAG = ffi_type_struct();
static final long SIZEOF_CIF = sizeofCif();
}
}

View file

@ -32,7 +32,7 @@ import jdk.internal.foreign.abi.StubLocations;
import jdk.internal.foreign.abi.VMStorage;
import jdk.internal.foreign.abi.riscv64.linux.TypeClass;
public class RISCV64Architecture implements Architecture {
public final class RISCV64Architecture implements Architecture {
public static final Architecture INSTANCE = new RISCV64Architecture();
private static final short REG64_MASK = 0b0000_0000_0000_0001;
@ -41,6 +41,9 @@ public class RISCV64Architecture implements Architecture {
private static final int INTEGER_REG_SIZE = 8; // bytes
private static final int FLOAT_REG_SIZE = 8;
// Suppresses default constructor, ensuring non-instantiability.
private RISCV64Architecture() {}
@Override
public boolean isStackType(int cls) {
return cls == StorageType.STACK;

View file

@ -26,6 +26,7 @@
package jdk.internal.foreign.abi.riscv64.linux;
import java.lang.foreign.AddressLayout;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemoryLayout;
@ -42,7 +43,7 @@ import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.foreign.abi.VMStorage;
import jdk.internal.foreign.Utils;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.List;
@ -52,7 +53,6 @@ import java.util.Optional;
import static jdk.internal.foreign.abi.riscv64.linux.TypeClass.*;
import static jdk.internal.foreign.abi.riscv64.RISCV64Architecture.*;
import static jdk.internal.foreign.abi.riscv64.RISCV64Architecture.Regs.*;
import static jdk.internal.foreign.PlatformLayouts.*;
/**
* For the RISCV64 C ABI specifically, this class uses CallingSequenceBuilder
@ -91,7 +91,7 @@ public class LinuxRISCV64CallArranger {
boolean returnInMemory = isInMemoryReturn(cDesc.returnLayout());
if (returnInMemory) {
Class<?> carrier = MemorySegment.class;
MemoryLayout layout = RISCV64.C_POINTER;
MemoryLayout layout = SharedUtils.C_POINTER;
csb.addArgumentBindings(carrier, layout, argCalc.getBindings(carrier, layout, false));
} else if (cDesc.returnLayout().isPresent()) {
Class<?> carrier = mt.returnType();
@ -121,8 +121,8 @@ public class LinuxRISCV64CallArranger {
return handle;
}
public static UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc) {
Bindings bindings = getBindings(mt, cDesc, true);
public static UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc, LinkerOptions options) {
Bindings bindings = getBindings(mt, cDesc, true, options);
final boolean dropReturn = true; /* drop return, since we don't have bindings for it */
return SharedUtils.arrangeUpcallHelper(mt, bindings.isInMemoryReturn, dropReturn, CLinux,
bindings.callingSequence);
@ -391,9 +391,10 @@ public class LinuxRISCV64CallArranger {
bindings.vmLoad(storage, carrier);
}
case POINTER -> {
AddressLayout addressLayout = (AddressLayout) layout;
VMStorage storage = storageCalculator.getStorage(StorageType.INTEGER);
bindings.vmLoad(storage, long.class)
.boxAddressRaw(Utils.pointeeSize(layout));
.boxAddressRaw(Utils.pointeeByteSize(addressLayout), Utils.pointeeByteAlign(addressLayout));
}
case STRUCT_REGISTER_X -> {
assert carrier == MemorySegment.class;

View file

@ -29,13 +29,9 @@ package jdk.internal.foreign.abi.riscv64.linux;
import jdk.internal.foreign.abi.AbstractLinker;
import jdk.internal.foreign.abi.LinkerOptions;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.VaList;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.function.Consumer;
public final class LinuxRISCV64Linker extends AbstractLinker {
@ -57,21 +53,7 @@ public final class LinuxRISCV64Linker extends AbstractLinker {
}
@Override
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function) {
return LinuxRISCV64CallArranger.arrangeUpcall(targetType, function);
}
public static VaList newVaList(Consumer<VaList.Builder> actions, SegmentScope scope) {
LinuxRISCV64VaList.Builder builder = LinuxRISCV64VaList.builder(scope);
actions.accept(builder);
return builder.build();
}
public static VaList newVaListOfAddress(long address, SegmentScope scope) {
return LinuxRISCV64VaList.ofAddress(address, scope);
}
public static VaList emptyVaList() {
return LinuxRISCV64VaList.empty();
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) {
return LinuxRISCV64CallArranger.arrangeUpcall(targetType, function, options);
}
}

View file

@ -1,302 +0,0 @@
/*
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, Institute of Software, Chinese Academy of Sciences.
* All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package jdk.internal.foreign.abi.riscv64.linux;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.SegmentAllocator;
import java.lang.foreign.ValueLayout;
import java.lang.foreign.VaList;
import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.foreign.MemorySessionImpl;
import jdk.internal.foreign.Utils;
import java.lang.invoke.VarHandle;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static java.lang.foreign.ValueLayout.ADDRESS;
import static jdk.internal.foreign.abi.SharedUtils.SimpleVaArg;
import static jdk.internal.foreign.abi.SharedUtils.THROWING_ALLOCATOR;
/**
* Standard va_list implementation as defined by RISC-V ABI document and used on Linux.
* In the base integer calling convention, variadic arguments are passed in the same
* manner as named arguments, with one exception. Variadic arguments with 2 * XLEN-bit
* alignment and size at most 2 * XLEN bits are passed in an aligned register pair
* (i.e., the first register in the pair is even-numbered), or on the stack by value
* if none is available. After a variadic argument has been passed on the stack, all
* future arguments will also be passed on the stack (i.e. the last argument register
* may be left unused due to the aligned register pair rule).
*/
public non-sealed class LinuxRISCV64VaList implements VaList {
// The va_list type is void* on RISCV64.
// See https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc#cc-type-representations
private final MemorySegment segment;
private long offset;
private static final long STACK_SLOT_SIZE = 8;
private static final VaList EMPTY
= new SharedUtils.EmptyVaList(MemorySegment.NULL);
public static VaList empty() {
return EMPTY;
}
public LinuxRISCV64VaList(MemorySegment segment, long offset) {
this.segment = segment;
this.offset = offset;
}
private static LinuxRISCV64VaList readFromAddress(long address, SegmentScope scope) {
MemorySegment segment = MemorySegment.ofAddress(address, Long.MAX_VALUE, scope); // size unknown
return new LinuxRISCV64VaList(segment, 0);
}
@Override
public int nextVarg(ValueLayout.OfInt layout) {
return (int) read(layout);
}
@Override
public long nextVarg(ValueLayout.OfLong layout) {
return (long) read(layout);
}
@Override
public double nextVarg(ValueLayout.OfDouble layout) {
return (double) read(layout);
}
@Override
public MemorySegment nextVarg(ValueLayout.OfAddress layout) {
return (MemorySegment) read(layout);
}
@Override
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(allocator);
return (MemorySegment) read(layout, allocator);
}
private Object read(MemoryLayout layout) {
return read(layout, THROWING_ALLOCATOR);
}
private Object read(MemoryLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(layout);
TypeClass typeClass = TypeClass.classifyLayout(layout);
preAlignStack(layout);
return switch (typeClass) {
case INTEGER, FLOAT, POINTER -> {
checkStackElement(layout);
VarHandle reader = layout.varHandle();
MemorySegment slice = segment.asSlice(offset, layout.byteSize());
Object res = reader.get(slice);
postAlignStack(layout);
yield res;
}
case STRUCT_REGISTER_X, STRUCT_REGISTER_F, STRUCT_REGISTER_XF -> {
checkStackElement(layout);
// Struct is passed indirectly via a pointer in an integer register.
MemorySegment slice = segment.asSlice(offset, layout.byteSize());
MemorySegment seg = allocator.allocate(layout);
seg.copyFrom(slice);
postAlignStack(layout);
yield seg;
}
case STRUCT_REFERENCE -> {
checkStackElement(ADDRESS);
VarHandle addrReader = ADDRESS.varHandle();
MemorySegment slice = segment.asSlice(offset, ADDRESS.byteSize());
MemorySegment addr = (MemorySegment) addrReader.get(slice);
MemorySegment seg = allocator.allocate(layout);
seg.copyFrom(MemorySegment.ofAddress(addr.address(), layout.byteSize(), segment.scope()));
postAlignStack(ADDRESS);
yield seg;
}
};
}
private void checkStackElement(MemoryLayout layout) {
if (Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE) > segment.byteSize()) {
throw SharedUtils.newVaListNSEE(layout);
}
}
private void preAlignStack(MemoryLayout layout) {
if (layout.byteSize() <= 16 && layout.byteAlignment() == 16) {
offset = Utils.alignUp(offset, 16);
} else {
offset = Utils.alignUp(offset, STACK_SLOT_SIZE);
}
}
private void postAlignStack(MemoryLayout layout) {
if (layout.byteSize() <= 16 && layout.byteAlignment() == 16) {
offset += 16;
} else {
offset += Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE);
}
}
@Override
public void skip(MemoryLayout... layouts) {
Objects.requireNonNull(layouts);
((MemorySessionImpl) segment.scope()).checkValidState();
for (MemoryLayout layout : layouts) {
Objects.requireNonNull(layout);
preAlignStack(layout);
postAlignStack(layout);
}
}
static LinuxRISCV64VaList.Builder builder(SegmentScope scope) {
return new LinuxRISCV64VaList.Builder(scope);
}
public static VaList ofAddress(long address, SegmentScope scope) {
return readFromAddress(address, scope);
}
@Override
public VaList copy() {
MemorySessionImpl sessionImpl = (MemorySessionImpl) segment.scope();
sessionImpl.checkValidState();
return new LinuxRISCV64VaList(segment, offset);
}
@Override
public MemorySegment segment() {
// make sure that returned segment cannot be accessed
return segment.asSlice(0, 0);
}
public long address() {
return segment.address() + offset;
}
@Override
public String toString() {
return "LinuxRISCV64VaList{" + "seg: " + address() + ", " + "offset: " + offset + '}';
}
public static non-sealed class Builder implements VaList.Builder {
private final SegmentScope scope;
private final List<SimpleVaArg> stackArgs = new ArrayList<>();
Builder(SegmentScope scope) {
this.scope = scope;
}
@Override
public Builder addVarg(ValueLayout.OfInt layout, int value) {
return arg(layout, value);
}
@Override
public Builder addVarg(ValueLayout.OfLong layout, long value) {
return arg(layout, value);
}
@Override
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
return arg(layout, value);
}
@Override
public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) {
return arg(layout, value);
}
@Override
public Builder addVarg(GroupLayout layout, MemorySegment value) {
return arg(layout, value);
}
private Builder arg(MemoryLayout layout, Object value) {
Objects.requireNonNull(layout);
Objects.requireNonNull(value);
stackArgs.add(new SimpleVaArg(layout, value));
return this;
}
boolean isEmpty() {
return stackArgs.isEmpty();
}
public VaList build() {
if (isEmpty()) {
return EMPTY;
}
long stackArgsSize = 0;
for (SimpleVaArg arg : stackArgs) {
MemoryLayout layout = arg.layout;
long elementSize = TypeClass.classifyLayout(layout) == TypeClass.STRUCT_REFERENCE ?
ADDRESS.byteSize() : layout.byteSize();
// arguments with 2 * XLEN-bit alignment and size at most 2 * XLEN bits
// are saved on memory aligned with 2 * XLEN (XLEN=64 for RISCV64).
if (layout.byteSize() <= 16 && layout.byteAlignment() == 16) {
stackArgsSize = Utils.alignUp(stackArgsSize, 16);
elementSize = 16;
}
stackArgsSize += Utils.alignUp(elementSize, STACK_SLOT_SIZE);
}
MemorySegment argsSegment = MemorySegment.allocateNative(stackArgsSize, 16, scope);
MemorySegment writeCursor = argsSegment;
for (SimpleVaArg arg : stackArgs) {
MemoryLayout layout;
Object value = arg.value;
if (TypeClass.classifyLayout(arg.layout) == TypeClass.STRUCT_REFERENCE) {
layout = ADDRESS;
} else {
layout = arg.layout;
}
long alignedSize = Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE);
if (layout.byteSize() <= 16 && layout.byteAlignment() == 16) {
writeCursor = Utils.alignUp(writeCursor, 16);
alignedSize = 16;
}
if (layout instanceof GroupLayout) {
writeCursor.copyFrom((MemorySegment) value);
} else {
VarHandle writer = layout.varHandle();
writer.set(writeCursor, value);
}
writeCursor = writeCursor.asSlice(alignedSize);
}
return new LinuxRISCV64VaList(argsSegment, 0L);
}
}
}

View file

@ -31,7 +31,7 @@ import jdk.internal.foreign.abi.VMStorage;
import java.util.stream.IntStream;
public class X86_64Architecture implements Architecture {
public final class X86_64Architecture implements Architecture {
public static final Architecture INSTANCE = new X86_64Architecture();
private static final short REG8_H_MASK = 0b0000_0000_0000_0010;
@ -48,6 +48,9 @@ public class X86_64Architecture implements Architecture {
private static final int VECTOR_REG_SIZE = 16; // size of XMM register
private static final int X87_REG_SIZE = 16;
// Suppresses default constructor, ensuring non-instantiability.
private X86_64Architecture() {}
@Override
public boolean isStackType(int cls) {
return cls == StorageType.STACK;
@ -154,7 +157,9 @@ public class X86_64Architecture implements Architecture {
new VMStorage[][] {
outputIntRegs,
outputVectorRegs,
IntStream.range(0, numX87Outputs).mapToObj(X86_64Architecture::x87Storage).toArray(VMStorage[]::new)
IntStream.range(0, numX87Outputs)
.mapToObj(X86_64Architecture::x87Storage)
.toArray(VMStorage[]::new)
},
new VMStorage[][] {
volatileIntRegs,

View file

@ -38,18 +38,18 @@ import jdk.internal.foreign.abi.UpcallLinker;
import jdk.internal.foreign.abi.VMStorage;
import jdk.internal.foreign.abi.x64.X86_64Architecture;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.AddressLayout;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.List;
import java.util.Optional;
import static jdk.internal.foreign.PlatformLayouts.SysV;
import static jdk.internal.foreign.abi.Binding.vmStore;
import static jdk.internal.foreign.abi.x64.X86_64Architecture.*;
import static jdk.internal.foreign.abi.x64.X86_64Architecture.Regs.*;
@ -65,6 +65,11 @@ public class CallArranger {
private static final int MAX_INTEGER_ARGUMENT_REGISTERS = 6;
private static final int MAX_VECTOR_ARGUMENT_REGISTERS = 8;
/**
* The {@code long} native type.
*/
public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG;
private static final ABIDescriptor CSysV = X86_64Architecture.abiFor(
new VMStorage[] { rdi, rsi, rdx, rcx, r8, r9, rax },
new VMStorage[] { xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7 },
@ -97,7 +102,7 @@ public class CallArranger {
boolean returnInMemory = isInMemoryReturn(cDesc.returnLayout());
if (returnInMemory) {
Class<?> carrier = MemorySegment.class;
MemoryLayout layout = SysV.C_POINTER;
MemoryLayout layout = SharedUtils.C_POINTER;
csb.addArgumentBindings(carrier, layout, argCalc.getBindings(carrier, layout));
} else if (cDesc.returnLayout().isPresent()) {
Class<?> carrier = mt.returnType();
@ -113,7 +118,7 @@ public class CallArranger {
if (!forUpcall) {
//add extra binding for number of used vector registers (used for variadic calls)
csb.addArgumentBindings(long.class, SysV.C_LONG,
csb.addArgumentBindings(long.class, C_LONG,
List.of(vmStore(rax, long.class)));
}
@ -133,8 +138,8 @@ public class CallArranger {
return handle;
}
public static UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc) {
Bindings bindings = getBindings(mt, cDesc, true);
public static UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc, LinkerOptions options) {
Bindings bindings = getBindings(mt, cDesc, true, options);
final boolean dropReturn = true; /* drop return, since we don't have bindings for it */
return SharedUtils.arrangeUpcallHelper(mt, bindings.isInMemoryReturn, dropReturn, CSysV,
bindings.callingSequence);
@ -314,9 +319,10 @@ public class CallArranger {
}
}
case POINTER -> {
AddressLayout addressLayout = (AddressLayout) layout;
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER);
bindings.vmLoad(storage, long.class)
.boxAddressRaw(Utils.pointeeSize(layout));
.boxAddressRaw(Utils.pointeeByteSize(addressLayout), Utils.pointeeByteAlign(addressLayout));
}
case INTEGER -> {
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER);

View file

@ -1,479 +0,0 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package jdk.internal.foreign.abi.x64.sysv;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.SegmentAllocator;
import java.lang.foreign.VaList;
import java.lang.foreign.ValueLayout;
import jdk.internal.foreign.MemorySessionImpl;
import jdk.internal.foreign.Utils;
import jdk.internal.foreign.abi.SharedUtils;
import java.lang.invoke.VarHandle;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static jdk.internal.foreign.PlatformLayouts.SysV;
import static java.lang.foreign.MemoryLayout.PathElement.groupElement;
import static jdk.internal.foreign.abi.SharedUtils.SimpleVaArg;
import static jdk.internal.foreign.abi.SharedUtils.THROWING_ALLOCATOR;
// See https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf "3.5.7 Variable Argument Lists"
public non-sealed class SysVVaList implements VaList {
// struct typedef __va_list_tag __va_list_tag {
// unsigned int gp_offset; /* 0 4 */
// unsigned int fp_offset; /* 4 4 */
// void * overflow_arg_area; /* 8 8 */
// void * reg_save_area; /* 16 8 */
//
// /* size: 24, cachelines: 1, members: 4 */
// /* last cacheline: 24 bytes */
// };
static final GroupLayout LAYOUT = MemoryLayout.structLayout(
SysV.C_INT.withName("gp_offset"),
SysV.C_INT.withName("fp_offset"),
SysV.C_POINTER.withName("overflow_arg_area"),
SysV.C_POINTER.withName("reg_save_area")
).withName("__va_list_tag");
private static final long STACK_SLOT_SIZE = 8;
private static final MemoryLayout GP_REG = MemoryLayout.paddingLayout(64).withBitAlignment(64);
private static final MemoryLayout FP_REG = MemoryLayout.paddingLayout(128).withBitAlignment(128);
private static final GroupLayout LAYOUT_REG_SAVE_AREA = MemoryLayout.structLayout(
GP_REG.withName("%rdi"),
GP_REG.withName("%rsi"),
GP_REG.withName("%rdx"),
GP_REG.withName("%rcx"),
GP_REG.withName("%r8"),
GP_REG.withName("%r9"),
FP_REG.withName("%xmm0"),
FP_REG.withName("%xmm1"),
FP_REG.withName("%xmm2"),
FP_REG.withName("%xmm3"),
FP_REG.withName("%xmm4"),
FP_REG.withName("%xmm5"),
FP_REG.withName("%xmm6"),
FP_REG.withName("%xmm7")
// specification and implementation differ as to whether the following are part of a reg save area
// Let's go with the implementation, since then it actually works :)
// FP_REG.withName("%xmm8"),
// FP_REG.withName("%xmm9"),
// FP_REG.withName("%xmm10"),
// FP_REG.withName("%xmm11"),
// FP_REG.withName("%xmm12"),
// FP_REG.withName("%xmm13"),
// FP_REG.withName("%xmm14"),
// FP_REG.withName("%xmm15")
);
private static final long FP_OFFSET = LAYOUT_REG_SAVE_AREA.byteOffset(groupElement("%xmm0"));
private static final int GP_SLOT_SIZE = (int) GP_REG.byteSize();
private static final int FP_SLOT_SIZE = (int) FP_REG.byteSize();
private static final int MAX_GP_OFFSET = (int) FP_OFFSET; // 6 regs used
private static final int MAX_FP_OFFSET = (int) LAYOUT_REG_SAVE_AREA.byteSize(); // 8 16 byte regs
private static final VarHandle VH_fp_offset = LAYOUT.varHandle(groupElement("fp_offset"));
private static final VarHandle VH_gp_offset = LAYOUT.varHandle(groupElement("gp_offset"));
private static final VarHandle VH_overflow_arg_area = LAYOUT.varHandle(groupElement("overflow_arg_area"));
private static final VarHandle VH_reg_save_area = LAYOUT.varHandle(groupElement("reg_save_area"));
private static final VaList EMPTY = new SharedUtils.EmptyVaList(emptyListAddress());
private final MemorySegment segment;
private MemorySegment overflowArgArea;
private final MemorySegment regSaveArea;
private final long gpLimit;
private final long fpLimit;
private SysVVaList(MemorySegment segment,
MemorySegment overflowArgArea,
MemorySegment regSaveArea, long gpLimit, long fpLimit) {
this.segment = segment;
this.overflowArgArea = overflowArgArea;
this.regSaveArea = regSaveArea;
this.gpLimit = gpLimit;
this.fpLimit = fpLimit;
}
private static SysVVaList readFromAddress(long address, SegmentScope scope) {
MemorySegment segment = MemorySegment.ofAddress(address, LAYOUT.byteSize(), scope);
MemorySegment regSaveArea = getRegSaveArea(segment);
MemorySegment overflowArgArea = getArgOverflowArea(segment);
return new SysVVaList(segment, overflowArgArea, regSaveArea, MAX_GP_OFFSET, MAX_FP_OFFSET);
}
private static MemorySegment emptyListAddress() {
MemorySegment base = MemorySegment.allocateNative(LAYOUT, SegmentScope.global());
VH_gp_offset.set(base, MAX_GP_OFFSET);
VH_fp_offset.set(base, MAX_FP_OFFSET);
VH_overflow_arg_area.set(base, MemorySegment.NULL);
VH_reg_save_area.set(base, MemorySegment.NULL);
return base.asSlice(0, 0);
}
public static VaList empty() {
return EMPTY;
}
private int currentGPOffset() {
return (int) VH_gp_offset.get(segment);
}
private void currentGPOffset(int i) {
VH_gp_offset.set(segment, i);
}
private int currentFPOffset() {
return (int) VH_fp_offset.get(segment);
}
private void currentFPOffset(int i) {
VH_fp_offset.set(segment, i);
}
private static MemorySegment getRegSaveArea(MemorySegment segment) {
return ((MemorySegment)VH_reg_save_area.get(segment))
.asSlice(0, LAYOUT_REG_SAVE_AREA.byteSize());
}
private static MemorySegment getArgOverflowArea(MemorySegment segment) {
return (MemorySegment)VH_overflow_arg_area.get(segment); // size unknown
}
private long preAlignOffset(MemoryLayout layout) {
long alignmentOffset = 0;
if (layout.byteAlignment() > STACK_SLOT_SIZE) {
long addr = overflowArgArea.address();
alignmentOffset = Utils.alignUp(addr, 16) - addr;
}
return alignmentOffset;
}
private void setOverflowArgArea(MemorySegment newSegment) {
overflowArgArea = newSegment;
VH_overflow_arg_area.set(segment, overflowArgArea);
}
private void preAlignStack(MemoryLayout layout) {
setOverflowArgArea(overflowArgArea.asSlice(preAlignOffset(layout)));
}
private void postAlignStack(MemoryLayout layout) {
setOverflowArgArea(overflowArgArea.asSlice(Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE)));
}
@Override
public int nextVarg(ValueLayout.OfInt layout) {
return (int) read(layout);
}
@Override
public long nextVarg(ValueLayout.OfLong layout) {
return (long) read(layout);
}
@Override
public double nextVarg(ValueLayout.OfDouble layout) {
return (double) read(layout);
}
@Override
public MemorySegment nextVarg(ValueLayout.OfAddress layout) {
return (MemorySegment) read(layout);
}
@Override
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(allocator);
return (MemorySegment) read(layout, allocator);
}
private Object read(MemoryLayout layout) {
return read(layout, THROWING_ALLOCATOR);
}
private Object read(MemoryLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(layout);
TypeClass typeClass = TypeClass.classifyLayout(layout);
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass)
|| typeClass.inMemory()) {
checkStackElement(layout);
preAlignStack(layout);
return switch (typeClass.kind()) {
case STRUCT -> {
MemorySegment slice = overflowArgArea.asSlice(0, layout.byteSize());
MemorySegment seg = allocator.allocate(layout);
seg.copyFrom(slice);
postAlignStack(layout);
yield seg;
}
case POINTER, INTEGER, FLOAT -> {
VarHandle reader = layout.varHandle();
MemorySegment slice = overflowArgArea.asSlice(0, layout.byteSize());
Object res = reader.get(slice);
postAlignStack(layout);
yield res;
}
};
} else {
checkRegSaveAreaElement(layout, typeClass);
return switch (typeClass.kind()) {
case STRUCT -> {
MemorySegment value = allocator.allocate(layout);
int classIdx = 0;
long offset = 0;
while (offset < layout.byteSize()) {
final long copy = Math.min(layout.byteSize() - offset, 8);
boolean isSSE = typeClass.classes.get(classIdx++) == ArgumentClassImpl.SSE;
if (isSSE) {
MemorySegment.copy(regSaveArea, currentFPOffset(), value, offset, copy);
currentFPOffset(currentFPOffset() + FP_SLOT_SIZE);
} else {
MemorySegment.copy(regSaveArea, currentGPOffset(), value, offset, copy);
currentGPOffset(currentGPOffset() + GP_SLOT_SIZE);
}
offset += copy;
}
yield value;
}
case POINTER, INTEGER -> {
VarHandle reader = layout.varHandle();
Object res = reader.get(regSaveArea.asSlice(currentGPOffset()));
currentGPOffset(currentGPOffset() + GP_SLOT_SIZE);
yield res;
}
case FLOAT -> {
VarHandle reader = layout.varHandle();
Object res = reader.get(regSaveArea.asSlice(currentFPOffset()));
currentFPOffset(currentFPOffset() + FP_SLOT_SIZE);
yield res;
}
};
}
}
private void checkRegSaveAreaElement(MemoryLayout layout, TypeClass typeClass) {
long gpSize = typeClass.nIntegerRegs() * GP_SLOT_SIZE;
long fpSize = typeClass.nVectorRegs() * FP_SLOT_SIZE;
if (currentGPOffset() + gpSize > gpLimit
|| currentFPOffset() + fpSize > fpLimit) {
throw SharedUtils.newVaListNSEE(layout);
}
}
private void checkStackElement(MemoryLayout layout) {
long offset = preAlignOffset(layout);
if (offset + layout.byteSize() > overflowArgArea.byteSize()) {
throw SharedUtils.newVaListNSEE(layout);
}
}
@Override
public void skip(MemoryLayout... layouts) {
Objects.requireNonNull(layouts);
((MemorySessionImpl) segment.scope()).checkValidState();
for (MemoryLayout layout : layouts) {
Objects.requireNonNull(layout);
TypeClass typeClass = TypeClass.classifyLayout(layout);
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass)) {
checkStackElement(layout);
preAlignStack(layout);
postAlignStack(layout);
} else {
checkRegSaveAreaElement(layout, typeClass);
currentGPOffset(currentGPOffset() + (((int) typeClass.nIntegerRegs()) * GP_SLOT_SIZE));
currentFPOffset(currentFPOffset() + (((int) typeClass.nVectorRegs()) * FP_SLOT_SIZE));
}
}
}
static SysVVaList.Builder builder(SegmentScope scope) {
return new SysVVaList.Builder(scope);
}
public static VaList ofAddress(long address, SegmentScope scope) {
return readFromAddress(address, scope);
}
@Override
public VaList copy() {
MemorySegment copy = MemorySegment.allocateNative(LAYOUT, segment.scope());
copy.copyFrom(segment);
return new SysVVaList(copy, overflowArgArea, regSaveArea, gpLimit, fpLimit);
}
@Override
public MemorySegment segment() {
// make sure that returned segment cannot be accessed
return segment.asSlice(0, 0);
}
private static boolean isRegOverflow(long currentGPOffset, long currentFPOffset, TypeClass typeClass) {
return currentGPOffset > MAX_GP_OFFSET - typeClass.nIntegerRegs() * GP_SLOT_SIZE
|| currentFPOffset > MAX_FP_OFFSET - typeClass.nVectorRegs() * FP_SLOT_SIZE;
}
@Override
public String toString() {
return "SysVVaList{"
+ "gp_offset=" + currentGPOffset()
+ ", fp_offset=" + currentFPOffset()
+ ", overflow_arg_area=" + overflowArgArea
+ ", reg_save_area=" + regSaveArea
+ '}';
}
public static non-sealed class Builder implements VaList.Builder {
private final SegmentScope scope;
private final MemorySegment reg_save_area;
private long currentGPOffset = 0;
private long currentFPOffset = FP_OFFSET;
private final List<SimpleVaArg> stackArgs = new ArrayList<>();
public Builder(SegmentScope scope) {
this.scope = scope;
this.reg_save_area = MemorySegment.allocateNative(LAYOUT_REG_SAVE_AREA, scope);
}
@Override
public Builder addVarg(ValueLayout.OfInt layout, int value) {
return arg(layout, value);
}
@Override
public Builder addVarg(ValueLayout.OfLong layout, long value) {
return arg(layout, value);
}
@Override
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
return arg(layout, value);
}
@Override
public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) {
return arg(layout, value);
}
@Override
public Builder addVarg(GroupLayout layout, MemorySegment value) {
return arg(layout, value);
}
private Builder arg(MemoryLayout layout, Object value) {
Objects.requireNonNull(layout);
Objects.requireNonNull(value);
TypeClass typeClass = TypeClass.classifyLayout(layout);
if (isRegOverflow(currentGPOffset, currentFPOffset, typeClass)
|| typeClass.inMemory()) {
// stack it!
stackArgs.add(new SimpleVaArg(layout, value));
} else {
switch (typeClass.kind()) {
case STRUCT -> {
MemorySegment valueSegment = (MemorySegment) value;
int classIdx = 0;
long offset = 0;
while (offset < layout.byteSize()) {
final long copy = Math.min(layout.byteSize() - offset, 8);
boolean isSSE = typeClass.classes.get(classIdx++) == ArgumentClassImpl.SSE;
if (isSSE) {
MemorySegment.copy(valueSegment, offset, reg_save_area, currentFPOffset, copy);
currentFPOffset += FP_SLOT_SIZE;
} else {
MemorySegment.copy(valueSegment, offset, reg_save_area, currentGPOffset, copy);
currentGPOffset += GP_SLOT_SIZE;
}
offset += copy;
}
}
case POINTER, INTEGER -> {
VarHandle writer = layout.varHandle();
writer.set(reg_save_area.asSlice(currentGPOffset), value);
currentGPOffset += GP_SLOT_SIZE;
}
case FLOAT -> {
VarHandle writer = layout.varHandle();
writer.set(reg_save_area.asSlice(currentFPOffset), value);
currentFPOffset += FP_SLOT_SIZE;
}
}
}
return this;
}
private boolean isEmpty() {
return currentGPOffset == 0 && currentFPOffset == FP_OFFSET && stackArgs.isEmpty();
}
public VaList build() {
if (isEmpty()) {
return EMPTY;
}
MemorySegment vaListSegment = MemorySegment.allocateNative(LAYOUT, scope);
MemorySegment stackArgsSegment;
if (!stackArgs.isEmpty()) {
long stackArgsSize = stackArgs.stream().reduce(0L,
(acc, e) -> acc + Utils.alignUp(e.layout.byteSize(), STACK_SLOT_SIZE), Long::sum);
stackArgsSegment = MemorySegment.allocateNative(stackArgsSize, 16, scope);
MemorySegment writeCursor = stackArgsSegment;
for (SimpleVaArg arg : stackArgs) {
if (arg.layout.byteSize() > 8) {
writeCursor = Utils.alignUp(writeCursor, Math.min(16, arg.layout.byteSize()));
}
if (arg.layout instanceof GroupLayout) {
writeCursor.copyFrom((MemorySegment) arg.value);
} else {
VarHandle writer = arg.varHandle();
writer.set(writeCursor, arg.value);
}
writeCursor = writeCursor.asSlice(Utils.alignUp(arg.layout.byteSize(), STACK_SLOT_SIZE));
}
} else {
stackArgsSegment = MemorySegment.NULL;
}
VH_fp_offset.set(vaListSegment, (int) FP_OFFSET);
VH_overflow_arg_area.set(vaListSegment, stackArgsSegment);
VH_reg_save_area.set(vaListSegment, reg_save_area);
assert MemorySessionImpl.sameOwnerThread(reg_save_area.scope(), vaListSegment.scope());
return new SysVVaList(vaListSegment, stackArgsSegment, reg_save_area, currentGPOffset, currentFPOffset);
}
}
}

View file

@ -28,12 +28,9 @@ package jdk.internal.foreign.abi.x64.sysv;
import jdk.internal.foreign.abi.AbstractLinker;
import jdk.internal.foreign.abi.LinkerOptions;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.VaList;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.function.Consumer;
/**
* ABI implementation based on System V ABI AMD64 supplement v.0.99.6
@ -58,21 +55,7 @@ public final class SysVx64Linker extends AbstractLinker {
}
@Override
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function) {
return CallArranger.arrangeUpcall(targetType, function);
}
public static VaList newVaList(Consumer<VaList.Builder> actions, SegmentScope scope) {
SysVVaList.Builder builder = SysVVaList.builder(scope);
actions.accept(builder);
return builder.build();
}
public static VaList newVaListOfAddress(long address, SegmentScope scope) {
return SysVVaList.ofAddress(address, scope);
}
public static VaList emptyVaList() {
return SysVVaList.empty();
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) {
return CallArranger.arrangeUpcall(targetType, function, options);
}
}

View file

@ -37,17 +37,17 @@ import jdk.internal.foreign.abi.UpcallLinker;
import jdk.internal.foreign.abi.VMStorage;
import jdk.internal.foreign.abi.x64.X86_64Architecture;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.AddressLayout;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.List;
import java.util.Optional;
import static jdk.internal.foreign.PlatformLayouts.Win64;
import static jdk.internal.foreign.abi.x64.X86_64Architecture.*;
import static jdk.internal.foreign.abi.x64.X86_64Architecture.Regs.*;
@ -104,7 +104,7 @@ public class CallArranger {
boolean returnInMemory = isInMemoryReturn(cDesc.returnLayout());
if (returnInMemory) {
Class<?> carrier = MemorySegment.class;
MemoryLayout layout = Win64.C_POINTER;
MemoryLayout layout = SharedUtils.C_POINTER;
csb.addArgumentBindings(carrier, layout, false);
if (forUpcall) {
csb.setReturnBindings(carrier, layout);
@ -132,8 +132,8 @@ public class CallArranger {
return handle;
}
public static UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc) {
Bindings bindings = getBindings(mt, cDesc, true);
public static UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc, LinkerOptions options) {
Bindings bindings = getBindings(mt, cDesc, true, options);
final boolean dropReturn = false; /* need the return value as well */
return SharedUtils.arrangeUpcallHelper(mt, bindings.isInMemoryReturn, dropReturn, CWindows,
bindings.callingSequence);
@ -265,9 +265,10 @@ public class CallArranger {
.boxAddress(layout);
}
case POINTER -> {
AddressLayout addressLayout = (AddressLayout) layout;
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER);
bindings.vmLoad(storage, long.class)
.boxAddressRaw(Utils.pointeeSize(layout));
.boxAddressRaw(Utils.pointeeByteSize(addressLayout), Utils.pointeeByteAlign(addressLayout));
}
case INTEGER -> {
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER);

View file

@ -1,246 +0,0 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package jdk.internal.foreign.abi.x64.windows;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.SegmentAllocator;
import java.lang.foreign.VaList;
import java.lang.foreign.ValueLayout;
import jdk.internal.foreign.MemorySessionImpl;
import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.foreign.abi.SharedUtils.SimpleVaArg;
import java.lang.invoke.VarHandle;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static jdk.internal.foreign.PlatformLayouts.Win64.C_POINTER;
// see vadefs.h (VC header)
//
// in short
// -> va_list is just a pointer to a buffer with 64 bit entries.
// -> non-power-of-two-sized, or larger than 64 bit types passed by reference.
// -> other types passed in 64 bit slots by normal function calling convention.
//
// X64 va_arg impl:
//
// typedef char* va_list;
//
// #define __crt_va_arg(ap, t) \
// ((sizeof(t) > sizeof(__int64) || (sizeof(t) & (sizeof(t) - 1)) != 0) \
// ? **(t**)((ap += sizeof(__int64)) - sizeof(__int64)) \
// : *(t* )((ap += sizeof(__int64)) - sizeof(__int64)))
//
public non-sealed class WinVaList implements VaList {
private static final long VA_SLOT_SIZE_BYTES = 8;
private static final VarHandle VH_address = C_POINTER.varHandle();
private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemorySegment.NULL);
private MemorySegment segment;
private WinVaList(MemorySegment segment) {
this.segment = segment;
}
public static final VaList empty() {
return EMPTY;
}
@Override
public int nextVarg(ValueLayout.OfInt layout) {
return (int) read(layout);
}
@Override
public long nextVarg(ValueLayout.OfLong layout) {
return (long) read(layout);
}
@Override
public double nextVarg(ValueLayout.OfDouble layout) {
return (double) read(layout);
}
@Override
public MemorySegment nextVarg(ValueLayout.OfAddress layout) {
return (MemorySegment) read(layout);
}
@Override
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(allocator);
return (MemorySegment) read(layout, allocator);
}
private Object read(MemoryLayout layout) {
return read(layout, SharedUtils.THROWING_ALLOCATOR);
}
private Object read(MemoryLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(layout);
Object res;
checkElement(layout);
if (layout instanceof GroupLayout) {
TypeClass typeClass = TypeClass.typeClassFor(layout, false);
res = switch (typeClass) {
case STRUCT_REFERENCE -> {
MemorySegment structAddr = (MemorySegment) VH_address.get(segment);
MemorySegment struct = MemorySegment.ofAddress(structAddr.address(), layout.byteSize(), segment.scope());
MemorySegment seg = allocator.allocate(layout);
seg.copyFrom(struct);
yield seg;
}
case STRUCT_REGISTER ->
allocator.allocate(layout).copyFrom(segment.asSlice(0, layout.byteSize()));
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
};
} else {
VarHandle reader = layout.varHandle();
res = reader.get(segment);
}
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
return res;
}
private void checkElement(MemoryLayout layout) {
if (segment.byteSize() < VA_SLOT_SIZE_BYTES) {
throw SharedUtils.newVaListNSEE(layout);
}
}
@Override
public void skip(MemoryLayout... layouts) {
Objects.requireNonNull(layouts);
((MemorySessionImpl) segment.scope()).checkValidState();
for (MemoryLayout layout : layouts) {
Objects.requireNonNull(layout);
checkElement(layout);
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
}
}
static WinVaList ofAddress(long address, SegmentScope scope) {
return new WinVaList(MemorySegment.ofAddress(address, Long.MAX_VALUE, scope));
}
static Builder builder(SegmentScope scope) {
return new Builder(scope);
}
@Override
public VaList copy() {
((MemorySessionImpl) segment.scope()).checkValidState();
return new WinVaList(segment);
}
@Override
public MemorySegment segment() {
// make sure that returned segment cannot be accessed
return segment.asSlice(0, 0);
}
public static non-sealed class Builder implements VaList.Builder {
private final SegmentScope scope;
private final List<SimpleVaArg> args = new ArrayList<>();
public Builder(SegmentScope scope) {
((MemorySessionImpl) scope).checkValidState();
this.scope = scope;
}
private Builder arg(MemoryLayout layout, Object value) {
Objects.requireNonNull(layout);
Objects.requireNonNull(value);
args.add(new SimpleVaArg(layout, value));
return this;
}
@Override
public Builder addVarg(ValueLayout.OfInt layout, int value) {
return arg(layout, value);
}
@Override
public Builder addVarg(ValueLayout.OfLong layout, long value) {
return arg(layout, value);
}
@Override
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
return arg(layout, value);
}
@Override
public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) {
return arg(layout, value);
}
@Override
public Builder addVarg(GroupLayout layout, MemorySegment value) {
return arg(layout, value);
}
public VaList build() {
if (args.isEmpty()) {
return EMPTY;
}
MemorySegment segment = MemorySegment.allocateNative(VA_SLOT_SIZE_BYTES * args.size(), scope);
MemorySegment cursor = segment;
for (SimpleVaArg arg : args) {
if (arg.layout instanceof GroupLayout) {
MemorySegment msArg = ((MemorySegment) arg.value);
TypeClass typeClass = TypeClass.typeClassFor(arg.layout, false);
switch (typeClass) {
case STRUCT_REFERENCE -> {
MemorySegment copy = MemorySegment.allocateNative(arg.layout, scope);
copy.copyFrom(msArg); // by-value
VH_address.set(cursor, copy);
}
case STRUCT_REGISTER ->
cursor.copyFrom(msArg.asSlice(0, VA_SLOT_SIZE_BYTES));
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
}
} else {
VarHandle writer = arg.varHandle();
writer.set(cursor, arg.value);
}
cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES);
}
return new WinVaList(segment);
}
}
}

View file

@ -28,12 +28,8 @@ import jdk.internal.foreign.abi.AbstractLinker;
import jdk.internal.foreign.abi.LinkerOptions;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.VaList;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.function.Consumer;
/**
* ABI implementation based on Windows ABI AMD64 supplement v.0.99.6
@ -58,21 +54,7 @@ public final class Windowsx64Linker extends AbstractLinker {
}
@Override
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function) {
return CallArranger.arrangeUpcall(targetType, function);
}
public static VaList newVaList(Consumer<VaList.Builder> actions, SegmentScope scope) {
WinVaList.Builder builder = WinVaList.builder(scope);
actions.accept(builder);
return builder.build();
}
public static VaList newVaListOfAddress(long address, SegmentScope scope) {
return WinVaList.ofAddress(address, scope);
}
public static VaList emptyVaList() {
return WinVaList.empty();
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) {
return CallArranger.arrangeUpcall(targetType, function, options);
}
}

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,15 +49,13 @@ public sealed abstract class AbstractGroupLayout<L extends AbstractGroupLayout<L
private final Kind kind;
private final List<MemoryLayout> elements;
final long minBitAlignment;
AbstractGroupLayout(Kind kind, List<MemoryLayout> elements) {
this(kind, elements, kind.alignof(elements), Optional.empty());
}
AbstractGroupLayout(Kind kind, List<MemoryLayout> elements, long bitAlignment, Optional<String> name) {
super(kind.sizeof(elements), bitAlignment, name); // Subclassing creates toctou problems here
AbstractGroupLayout(Kind kind, List<MemoryLayout> elements, long bitSize, long bitAlignment, long minBitAlignment, Optional<String> name) {
super(bitSize, bitAlignment, name); // Subclassing creates toctou problems here
this.kind = kind;
this.elements = List.copyOf(elements);
this.minBitAlignment = minBitAlignment;
}
/**
@ -83,20 +81,24 @@ public sealed abstract class AbstractGroupLayout<L extends AbstractGroupLayout<L
.collect(Collectors.joining(kind.delimTag, "[", "]")));
}
@Override
public L withBitAlignment(long bitAlignment) {
if (bitAlignment < minBitAlignment) {
throw new IllegalArgumentException("Invalid alignment constraint");
}
return super.withBitAlignment(bitAlignment);
}
/**
* {@inheritDoc}
*/
@Override
public final boolean equals(Object other) {
if (this == other) {
return true;
}
if (!super.equals(other)) {
return false;
}
return other instanceof AbstractGroupLayout<?> otherGroup &&
kind == otherGroup.kind &&
elements.equals(otherGroup.elements);
return this == other ||
other instanceof AbstractGroupLayout<?> otherGroup &&
super.equals(other) &&
kind == otherGroup.kind &&
elements.equals(otherGroup.elements);
}
/**
@ -109,7 +111,7 @@ public sealed abstract class AbstractGroupLayout<L extends AbstractGroupLayout<L
@Override
public final boolean hasNaturalAlignment() {
return bitAlignment() == kind.alignof(elements);
return bitAlignment() == minBitAlignment;
}
/**
@ -119,33 +121,16 @@ public sealed abstract class AbstractGroupLayout<L extends AbstractGroupLayout<L
/**
* A 'struct' kind.
*/
STRUCT("", Math::addExact),
STRUCT(""),
/**
* A 'union' kind.
*/
UNION("|", Math::max);
UNION("|");
final String delimTag;
final LongBinaryOperator sizeOp;
Kind(String delimTag, LongBinaryOperator sizeOp) {
Kind(String delimTag) {
this.delimTag = delimTag;
this.sizeOp = sizeOp;
}
long sizeof(List<MemoryLayout> elems) {
long size = 0;
for (MemoryLayout elem : elems) {
size = sizeOp.applyAsLong(size, elem.bitSize());
}
return size;
}
long alignof(List<MemoryLayout> elems) {
return elems.stream()
.mapToLong(MemoryLayout::bitAlignment)
.max() // max alignment in case we have member layouts
.orElse(1); // or minimal alignment if no member layout is given
}
}
}

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
@ -25,10 +25,6 @@
*/
package jdk.internal.foreign.layout;
import jdk.internal.foreign.Utils;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Stable;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.SequenceLayout;
@ -41,51 +37,50 @@ import java.util.Optional;
public abstract sealed class AbstractLayout<L extends AbstractLayout<L> & MemoryLayout>
permits AbstractGroupLayout, PaddingLayoutImpl, SequenceLayoutImpl, ValueLayouts.AbstractValueLayout {
private final long bitSize;
private final long bitAlignment;
private final long byteSize;
private final long byteAlignment;
private final Optional<String> name;
@Stable
private long byteSize;
AbstractLayout(long bitSize, long bitAlignment, Optional<String> name) {
this.bitSize = bitSize;
this.bitAlignment = bitAlignment;
this.name = name;
this.byteSize = MemoryLayoutUtil.requireBitSizeValid(bitSize, true) / 8;
this.byteAlignment = requirePowerOfTwoAndGreaterOrEqualToEight(bitAlignment) / 8;
this.name = Objects.requireNonNull(name);
}
public final L withName(String name) {
Objects.requireNonNull(name);
return dup(bitAlignment, Optional.of(name));
return dup(bitAlignment(), Optional.of(name));
}
public final L withoutName() {
return dup(bitAlignment(), Optional.empty());
}
public final Optional<String> name() {
return name;
}
public final L withBitAlignment(long bitAlignment) {
checkAlignment(bitAlignment);
public L withBitAlignment(long bitAlignment) {
return dup(bitAlignment, name);
}
public final long bitAlignment() {
return bitAlignment;
return byteAlignment * 8;
}
public final long byteAlignment() {
return byteAlignment;
}
@ForceInline
public final long byteSize() {
if (byteSize == 0) {
byteSize = Utils.bitsToBytesOrThrow(bitSize(),
() -> new UnsupportedOperationException("Cannot compute byte size; bit size is not a multiple of 8"));
}
return byteSize;
}
public final long bitSize() {
return bitSize;
return byteSize * 8;
}
public boolean hasNaturalAlignment() {
return bitSize == bitAlignment;
return byteSize == byteAlignment;
}
// the following methods have to copy the same Javadoc as in MemoryLayout, or subclasses will just show
@ -96,7 +91,7 @@ public abstract sealed class AbstractLayout<L extends AbstractLayout<L> & Memory
*/
@Override
public int hashCode() {
return Objects.hash(name, bitSize, bitAlignment);
return Objects.hash(name, byteSize, byteAlignment);
}
/**
@ -118,39 +113,36 @@ public abstract sealed class AbstractLayout<L extends AbstractLayout<L> & Memory
*/
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
return other instanceof AbstractLayout<?> otherLayout &&
name.equals(otherLayout.name) &&
bitSize == otherLayout.bitSize &&
bitAlignment == otherLayout.bitAlignment;
byteSize == otherLayout.byteSize &&
byteAlignment == otherLayout.byteAlignment;
}
/**
* {@return the string representation of this layout}
*/
@Override
public abstract String toString();
abstract L dup(long alignment, Optional<String> name);
abstract L dup(long bitAlignment, Optional<String> name);
String decorateLayoutString(String s) {
if (name().isPresent()) {
s = String.format("%s(%s)", s, name().get());
}
if (!hasNaturalAlignment()) {
s = bitAlignment + "%" + s;
s = bitAlignment() + "%" + s;
}
return s;
}
private static void checkAlignment(long alignmentBitCount) {
if (((alignmentBitCount & (alignmentBitCount - 1)) != 0L) || //alignment must be a power of two
(alignmentBitCount < 8)) { //alignment must be greater than 8
throw new IllegalArgumentException("Invalid alignment: " + alignmentBitCount);
private static long requirePowerOfTwoAndGreaterOrEqualToEight(long value) {
if (((value & (value - 1)) != 0L) || // value must be a power of two
(value < 8)) { // value must be greater or equal to 8
throw new IllegalArgumentException("Invalid alignment: " + value);
}
return 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
@ -30,14 +30,18 @@ public final class MemoryLayoutUtil {
private MemoryLayoutUtil() {
}
public static void checkSize(long size) {
checkSize(size, false);
public static long requireNonNegative(long value) {
if (value < 0) {
throw new IllegalArgumentException("The provided value was negative: " + value);
}
return value;
}
public static void checkSize(long size, boolean includeZero) {
if (size < 0 || (!includeZero && size == 0)) {
throw new IllegalArgumentException("Invalid size for layout: " + size);
public static long requireBitSizeValid(long bitSize, boolean allowZero) {
if ((bitSize == 0 && !allowZero) || bitSize < 0 || bitSize % 8 != 0) {
throw new IllegalArgumentException("Invalid bitSize: " + bitSize);
}
return bitSize;
}
}

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
@ -32,7 +32,7 @@ import java.util.Optional;
public final class PaddingLayoutImpl extends AbstractLayout<PaddingLayoutImpl> implements PaddingLayout {
private PaddingLayoutImpl(long bitSize) {
this(bitSize, 1, Optional.empty());
this(bitSize, 8, Optional.empty());
}
private PaddingLayoutImpl(long bitSize, long bitAlignment, Optional<String> name) {
@ -46,16 +46,10 @@ public final class PaddingLayoutImpl extends AbstractLayout<PaddingLayoutImpl> i
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!super.equals(other)) {
return false;
}
if (!(other instanceof PaddingLayoutImpl p)) {
return false;
}
return bitSize() == p.bitSize();
return this == other ||
other instanceof PaddingLayoutImpl otherPadding &&
super.equals(other) &&
bitSize() == otherPadding.bitSize();
}
@Override

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
@ -68,7 +68,6 @@ public final class SequenceLayoutImpl extends AbstractLayout<SequenceLayoutImpl>
* @throws IllegalArgumentException if {@code elementCount < 0}.
*/
public SequenceLayout withElementCount(long elementCount) {
MemoryLayoutUtil.checkSize(elementCount, true);
return new SequenceLayoutImpl(elementCount, elementLayout, bitAlignment(), name());
}
@ -177,21 +176,18 @@ public final class SequenceLayoutImpl extends AbstractLayout<SequenceLayoutImpl>
@Override
public String toString() {
boolean max = (Long.MAX_VALUE / elementLayout.bitSize()) == elemCount;
return decorateLayoutString(String.format("[%s:%s]",
elemCount, elementLayout));
max ? "*" : elemCount, elementLayout));
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!super.equals(other)) {
return false;
}
return other instanceof SequenceLayoutImpl otherSeq &&
elemCount == otherSeq.elemCount &&
elementLayout.equals(otherSeq.elementLayout);
return this == other ||
other instanceof SequenceLayoutImpl otherSeq &&
super.equals(other) &&
elemCount == otherSeq.elemCount &&
elementLayout.equals(otherSeq.elementLayout);
}
@Override
@ -204,6 +200,14 @@ public final class SequenceLayoutImpl extends AbstractLayout<SequenceLayoutImpl>
return new SequenceLayoutImpl(elementCount(), elementLayout, bitAlignment, name);
}
@Override
public SequenceLayoutImpl withBitAlignment(long bitAlignment) {
if (bitAlignment < elementLayout.bitAlignment()) {
throw new IllegalArgumentException("Invalid alignment constraint");
}
return super.withBitAlignment(bitAlignment);
}
@Override
public boolean hasNaturalAlignment() {
return bitAlignment() == elementLayout.bitAlignment();

View file

@ -32,21 +32,26 @@ import java.util.Optional;
public final class StructLayoutImpl extends AbstractGroupLayout<StructLayoutImpl> implements StructLayout {
private StructLayoutImpl(List<MemoryLayout> elements) {
super(Kind.STRUCT, elements);
}
private StructLayoutImpl(List<MemoryLayout> elements, long bitAlignment, Optional<String> name) {
super(Kind.STRUCT, elements, bitAlignment, name);
private StructLayoutImpl(List<MemoryLayout> elements, long bitSize, long bitAlignment, long minBitAlignment, Optional<String> name) {
super(Kind.STRUCT, elements, bitSize, bitAlignment, minBitAlignment, name);
}
@Override
StructLayoutImpl dup(long bitAlignment, Optional<String> name) {
return new StructLayoutImpl(memberLayouts(), bitAlignment, name);
return new StructLayoutImpl(memberLayouts(), bitSize(), bitAlignment, minBitAlignment, name);
}
public static StructLayout of(List<MemoryLayout> elements) {
return new StructLayoutImpl(elements);
long size = 0;
long align = 8;
for (MemoryLayout elem : elements) {
if (size % elem.bitAlignment() != 0) {
throw new IllegalArgumentException("Invalid alignment constraint for member layout: " + elem);
}
size = Math.addExact(size, elem.bitSize());
align = Math.max(align, elem.bitAlignment());
}
return new StructLayoutImpl(elements, size, align, align, Optional.empty());
}
}

View file

@ -32,21 +32,23 @@ import java.util.Optional;
public final class UnionLayoutImpl extends AbstractGroupLayout<UnionLayoutImpl> implements UnionLayout {
private UnionLayoutImpl(List<MemoryLayout> elements) {
super(Kind.UNION, elements);
}
private UnionLayoutImpl(List<MemoryLayout> elements, long bitAlignment, Optional<String> name) {
super(Kind.UNION, elements, bitAlignment, name);
private UnionLayoutImpl(List<MemoryLayout> elements, long bitSize, long bitAlignment, long minBitAlignment, Optional<String> name) {
super(Kind.UNION, elements, bitSize, bitAlignment, minBitAlignment, name);
}
@Override
UnionLayoutImpl dup(long bitAlignment, Optional<String> name) {
return new UnionLayoutImpl(memberLayouts(), bitAlignment, name);
return new UnionLayoutImpl(memberLayouts(), bitSize(), bitAlignment, minBitAlignment, name);
}
public static UnionLayout of(List<MemoryLayout> elements) {
return new UnionLayoutImpl(elements);
long size = 0;
long align = 8;
for (MemoryLayout elem : elements) {
size = Math.max(size, elem.bitSize());
align = Math.max(align, elem.bitAlignment());
}
return new UnionLayoutImpl(elements, size, align, align, Optional.empty());
}
}

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
@ -35,6 +35,7 @@ import sun.invoke.util.Wrapper;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.AddressLayout;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
@ -58,8 +59,8 @@ import java.util.Optional;
*/
public final class ValueLayouts {
private ValueLayouts() {
}
// Suppresses default constructor, ensuring non-instantiability.
private ValueLayouts() {}
abstract sealed static class AbstractValueLayout<V extends AbstractValueLayout<V> & ValueLayout> extends AbstractLayout<V> {
@ -70,15 +71,11 @@ public final class ValueLayouts {
@Stable
private VarHandle handle;
AbstractValueLayout(Class<?> carrier, ByteOrder order, long bitSize) {
this(carrier, order, bitSize, bitSize, Optional.empty());
}
AbstractValueLayout(Class<?> carrier, ByteOrder order, long bitSize, long bitAlignment, Optional<String> name) {
super(bitSize, bitAlignment, name);
this.carrier = carrier;
this.order = order;
checkCarrierSize(carrier, bitSize);
assertCarrierSize(carrier, bitSize);
}
/**
@ -95,11 +92,14 @@ public final class ValueLayouts {
* @param order the desired byte order.
* @return a value layout with the given byte order.
*/
abstract V withOrder(ByteOrder order);
public final V withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return dup(order, bitAlignment(), name());
}
@Override
public final String toString() {
char descriptor = carrier == MemorySegment.class ? 'A' : carrier.descriptorString().charAt(0);
public String toString() {
char descriptor = carrier.descriptorString().charAt(0);
if (order == ByteOrder.LITTLE_ENDIAN) {
descriptor = Character.toLowerCase(descriptor);
}
@ -108,15 +108,11 @@ public final class ValueLayouts {
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!super.equals(other)) {
return false;
}
return other instanceof AbstractValueLayout<?> otherValue &&
carrier.equals(otherValue.carrier) &&
order.equals(otherValue.order);
return this == other ||
other instanceof AbstractValueLayout<?> otherValue &&
super.equals(other) &&
carrier.equals(otherValue.carrier) &&
order.equals(otherValue.order);
}
public final VarHandle arrayElementVarHandle(int... shape) {
@ -147,24 +143,24 @@ public final class ValueLayouts {
}
@Override
abstract V dup(long bitAlignment, Optional<String> name);
final V dup(long bitAlignment, Optional<String> name) {
return dup(order(), bitAlignment, name);
}
static void checkCarrierSize(Class<?> carrier, long size) {
if (!isValidCarrier(carrier)) {
throw new IllegalArgumentException("Invalid carrier: " + carrier.getName());
}
if (carrier == MemorySegment.class && size != ADDRESS_SIZE_BITS) {
throw new IllegalArgumentException("Address size mismatch: " + ADDRESS_SIZE_BITS + " != " + size);
}
if (carrier.isPrimitive()) {
int expectedSize = carrier == boolean.class ? 8 : Wrapper.forPrimitiveType(carrier).bitWidth();
if (size != expectedSize) {
throw new IllegalArgumentException("Carrier size mismatch: " + carrier.getName() + " != " + size);
}
}
abstract V dup(ByteOrder order, long bitAlignment, Optional<String> name);
static void assertCarrierSize(Class<?> carrier, long bitSize) {
assert isValidCarrier(carrier);
assert carrier != MemorySegment.class
// MemorySegment bitSize must always equal ADDRESS_SIZE_BITS
|| bitSize == ADDRESS_SIZE_BITS;
assert !carrier.isPrimitive() ||
// Primitive class bitSize must always correspond
bitSize == (carrier == boolean.class ? 8 : Wrapper.forPrimitiveType(carrier).bitWidth());
}
static boolean isValidCarrier(Class<?> carrier) {
// void.class is not valid
return carrier == boolean.class
|| carrier == byte.class
|| carrier == short.class
@ -176,7 +172,6 @@ public final class ValueLayouts {
|| carrier == MemorySegment.class;
}
@ForceInline
public final VarHandle accessHandle() {
if (handle == null) {
@ -194,264 +189,236 @@ public final class ValueLayouts {
public static final class OfBooleanImpl extends AbstractValueLayout<OfBooleanImpl> implements ValueLayout.OfBoolean {
private OfBooleanImpl(ByteOrder order) {
super(boolean.class, order, 8);
}
private OfBooleanImpl(ByteOrder order, long bitAlignment, Optional<String> name) {
super(boolean.class, order, 8, bitAlignment, name);
super(boolean.class, order, Byte.SIZE, bitAlignment, name);
}
@Override
OfBooleanImpl dup(long bitAlignment, Optional<String> name) {
return new OfBooleanImpl(order(), bitAlignment, name);
}
@Override
public OfBooleanImpl withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfBooleanImpl(order, bitAlignment(), name());
OfBooleanImpl dup(ByteOrder order, long bitAlignment, Optional<String> name) {
return new OfBooleanImpl(order, bitAlignment, name);
}
public static OfBoolean of(ByteOrder order) {
return new OfBooleanImpl(order);
return new OfBooleanImpl(order, Byte.SIZE, Optional.empty());
}
}
public static final class OfByteImpl extends AbstractValueLayout<OfByteImpl> implements ValueLayout.OfByte {
private OfByteImpl(ByteOrder order) {
super(byte.class, order, 8);
}
private OfByteImpl(ByteOrder order, long bitAlignment, Optional<String> name) {
super(byte.class, order, 8, bitAlignment, name);
super(byte.class, order, Byte.SIZE, bitAlignment, name);
}
@Override
OfByteImpl dup(long bitAlignment, Optional<String> name) {
return new OfByteImpl(order(), bitAlignment, name);
}
@Override
public OfByteImpl withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfByteImpl(order, bitAlignment(), name());
OfByteImpl dup(ByteOrder order, long bitAlignment, Optional<String> name) {
return new OfByteImpl(order, bitAlignment, name);
}
public static OfByte of(ByteOrder order) {
return new OfByteImpl(order);
return new OfByteImpl(order, Byte.SIZE, Optional.empty());
}
}
public static final class OfCharImpl extends AbstractValueLayout<OfCharImpl> implements ValueLayout.OfChar {
private OfCharImpl(ByteOrder order) {
super(char.class, order, 16);
}
private OfCharImpl(ByteOrder order, long bitAlignment, Optional<String> name) {
super(char.class, order, 16, bitAlignment, name);
super(char.class, order, Character.SIZE, bitAlignment, name);
}
@Override
OfCharImpl dup(long bitAlignment, Optional<String> name) {
return new OfCharImpl(order(), bitAlignment, name);
}
@Override
public OfCharImpl withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfCharImpl(order, bitAlignment(), name());
OfCharImpl dup(ByteOrder order, long bitAlignment, Optional<String> name) {
return new OfCharImpl(order, bitAlignment, name);
}
public static OfChar of(ByteOrder order) {
return new OfCharImpl(order);
return new OfCharImpl(order, Character.SIZE, Optional.empty());
}
}
public static final class OfShortImpl extends AbstractValueLayout<OfShortImpl> implements ValueLayout.OfShort {
private OfShortImpl(ByteOrder order) {
super(short.class, order, 16);
}
private OfShortImpl(ByteOrder order, long bitAlignment, Optional<String> name) {
super(short.class, order, 16, bitAlignment, name);
super(short.class, order, Short.SIZE, bitAlignment, name);
}
@Override
OfShortImpl dup(long bitAlignment, Optional<String> name) {
return new OfShortImpl(order(), bitAlignment, name);
}
@Override
public OfShortImpl withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfShortImpl(order, bitAlignment(), name());
OfShortImpl dup(ByteOrder order, long bitAlignment, Optional<String> name) {
return new OfShortImpl(order, bitAlignment, name);
}
public static OfShort of(ByteOrder order) {
return new OfShortImpl(order);
return new OfShortImpl(order, Short.SIZE, Optional.empty());
}
}
public static final class OfIntImpl extends AbstractValueLayout<OfIntImpl> implements ValueLayout.OfInt {
private OfIntImpl(ByteOrder order) {
super(int.class, order, 32);
}
private OfIntImpl(ByteOrder order, long bitAlignment, Optional<String> name) {
super(int.class, order, 32, bitAlignment, name);
super(int.class, order, Integer.SIZE, bitAlignment, name);
}
@Override
OfIntImpl dup(long bitAlignment, Optional<String> name) {
return new OfIntImpl(order(), bitAlignment, name);
}
@Override
public OfIntImpl withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfIntImpl(order, bitAlignment(), name());
OfIntImpl dup(ByteOrder order, long bitAlignment, Optional<String> name) {
return new OfIntImpl(order, bitAlignment, name);
}
public static OfInt of(ByteOrder order) {
return new OfIntImpl(order);
return new OfIntImpl(order, Integer.SIZE, Optional.empty());
}
}
public static final class OfFloatImpl extends AbstractValueLayout<OfFloatImpl> implements ValueLayout.OfFloat {
private OfFloatImpl(ByteOrder order) {
super(float.class, order, 32);
}
private OfFloatImpl(ByteOrder order, long bitAlignment, Optional<String> name) {
super(float.class, order, 32, bitAlignment, name);
super(float.class, order, Float.SIZE, bitAlignment, name);
}
@Override
OfFloatImpl dup(long bitAlignment, Optional<String> name) {
return new OfFloatImpl(order(), bitAlignment, name);
}
@Override
public OfFloatImpl withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfFloatImpl(order, bitAlignment(), name());
OfFloatImpl dup(ByteOrder order, long bitAlignment, Optional<String> name) {
return new OfFloatImpl(order, bitAlignment, name);
}
public static OfFloat of(ByteOrder order) {
return new OfFloatImpl(order);
return new OfFloatImpl(order, Float.SIZE, Optional.empty());
}
}
public static final class OfLongImpl extends AbstractValueLayout<OfLongImpl> implements ValueLayout.OfLong {
private OfLongImpl(ByteOrder order) {
super(long.class, order, 64);
}
private OfLongImpl(ByteOrder order, long bitAlignment, Optional<String> name) {
super(long.class, order, 64, bitAlignment, name);
super(long.class, order, Long.SIZE, bitAlignment, name);
}
@Override
OfLongImpl dup(long bitAlignment, Optional<String> name) {
return new OfLongImpl(order(), bitAlignment, name);
}
@Override
public OfLongImpl withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfLongImpl(order, bitAlignment(), name());
OfLongImpl dup(ByteOrder order, long bitAlignment, Optional<String> name) {
return new OfLongImpl(order, bitAlignment, name);
}
public static OfLong of(ByteOrder order) {
return new OfLongImpl(order);
return new OfLongImpl(order, Long.SIZE, Optional.empty());
}
}
public static final class OfDoubleImpl extends AbstractValueLayout<OfDoubleImpl> implements ValueLayout.OfDouble {
private OfDoubleImpl(ByteOrder order) {
super(double.class, order, 64);
}
private OfDoubleImpl(ByteOrder order, long bitAlignment, Optional<String> name) {
super(double.class, order, 64, bitAlignment, name);
super(double.class, order, Double.SIZE, bitAlignment, name);
}
@Override
OfDoubleImpl dup(long bitAlignment, Optional<String> name) {
return new OfDoubleImpl(order(), bitAlignment, name);
}
@Override
public OfDoubleImpl withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfDoubleImpl(order, bitAlignment(), name());
OfDoubleImpl dup(ByteOrder order, long bitAlignment, Optional<String> name) {
return new OfDoubleImpl(order, bitAlignment, name);
}
public static OfDouble of(ByteOrder order) {
return new OfDoubleImpl(order);
return new OfDoubleImpl(order, Double.SIZE, Optional.empty());
}
}
public static final class OfAddressImpl extends AbstractValueLayout<OfAddressImpl> implements ValueLayout.OfAddress {
public static final class OfAddressImpl extends AbstractValueLayout<OfAddressImpl> implements AddressLayout {
private final boolean isUnbounded;
private final MemoryLayout targetLayout;
private OfAddressImpl(ByteOrder order) {
super(MemorySegment.class, order, ADDRESS_SIZE_BITS);
this.isUnbounded = false; // safe
}
private OfAddressImpl(ByteOrder order, long size, long bitAlignment, boolean isUnbounded, Optional<String> name) {
super(MemorySegment.class, order, size, bitAlignment, name);
this.isUnbounded = isUnbounded;
private OfAddressImpl(ByteOrder order, long bitSize, long bitAlignment, MemoryLayout targetLayout, Optional<String> name) {
super(MemorySegment.class, order, bitSize, bitAlignment, name);
this.targetLayout = targetLayout;
}
@Override
OfAddressImpl dup(long alignment, Optional<String> name) {
return new OfAddressImpl(order(), bitSize(), alignment, isUnbounded, name);
}
@Override
public OfAddressImpl withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfAddressImpl(order, bitSize(), bitAlignment(), isUnbounded, name());
OfAddressImpl dup(ByteOrder order, long bitAlignment, Optional<String> name) {
return new OfAddressImpl(order, bitSize(), bitAlignment,targetLayout, name);
}
@Override
public boolean equals(Object other) {
return super.equals(other) &&
((OfAddressImpl) other).isUnbounded == this.isUnbounded;
Objects.equals(((OfAddressImpl)other).targetLayout, this.targetLayout);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), isUnbounded);
return Objects.hash(super.hashCode(), targetLayout);
}
@Override
@CallerSensitive
public OfAddress asUnbounded() {
Reflection.ensureNativeAccess(Reflection.getCallerClass(), OfAddress.class, "asUnbounded");
return new OfAddressImpl(order(), bitSize(), bitAlignment(), true, name());
public AddressLayout withTargetLayout(MemoryLayout layout) {
Reflection.ensureNativeAccess(Reflection.getCallerClass(), AddressLayout.class, "withTargetLayout");
Objects.requireNonNull(layout);
return new OfAddressImpl(order(), bitSize(), bitAlignment(), layout, name());
}
@Override
public boolean isUnbounded() {
return isUnbounded;
public AddressLayout withoutTargetLayout() {
return new OfAddressImpl(order(), bitSize(), bitAlignment(), null, name());
}
public static OfAddress of(ByteOrder order) {
return new OfAddressImpl(order);
@Override
public Optional<MemoryLayout> targetLayout() {
return Optional.ofNullable(targetLayout);
}
public static AddressLayout of(ByteOrder order) {
return new OfAddressImpl(order, ADDRESS_SIZE_BITS, ADDRESS_SIZE_BITS, null, Optional.empty());
}
@Override
public String toString() {
char descriptor = 'A';
if (order() == ByteOrder.LITTLE_ENDIAN) {
descriptor = Character.toLowerCase(descriptor);
}
String str = decorateLayoutString(String.format("%s%d", descriptor, bitSize()));
if (targetLayout != null) {
str += ":" + targetLayout;
}
return str;
}
}
/**
* 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.
*/
public 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());
}
}
}

View file

@ -70,7 +70,7 @@ public @interface PreviewFeature {
RECORD_PATTERNS,
// not used
VIRTUAL_THREADS,
@JEP(number=434, title="Foreign Function & Memory API", status="Second Preview")
@JEP(number=442, title="Foreign Function & Memory API", status="Third Preview")
FOREIGN,
/**
* A key for testing.

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.vm;
/**
* Defines a static method to test if the VM has support for the foreign java.lang.foreign.Linker.
*/
public final class ForeignLinkerSupport {
private static final boolean SUPPORTED = isSupported0();
private ForeignLinkerSupport() {
}
/**
* Return true if the VM has support for the foreign Linker.
*/
public static boolean isSupported() {
return SUPPORTED;
}
private static native boolean isSupported0();
}

View file

@ -30,7 +30,7 @@ import java.io.FileDescriptor;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentScope;
import java.lang.foreign.Arena;
import java.lang.ref.Cleaner.Cleanable;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
@ -1206,12 +1206,12 @@ public class FileChannelImpl
@Override
public MemorySegment map(MapMode mode, long offset, long size,
SegmentScope session)
Arena arena)
throws IOException
{
Objects.requireNonNull(mode,"Mode is null");
Objects.requireNonNull(session, "Session is null");
MemorySessionImpl sessionImpl = (MemorySessionImpl) session;
Objects.requireNonNull(arena, "Arena is null");
MemorySessionImpl sessionImpl = MemorySessionImpl.toMemorySession(arena);
sessionImpl.checkValidState();
if (offset < 0)
throw new IllegalArgumentException("Requested bytes offset must be >= 0.");
@ -1228,7 +1228,7 @@ public class FileChannelImpl
if (unmapper != null) {
AbstractMemorySegmentImpl segment =
new MappedMemorySegmentImpl(unmapper.address(), unmapper, size,
readOnly, session);
readOnly, sessionImpl);
MemorySessionImpl.ResourceList.ResourceCleanup resource =
new MemorySessionImpl.ResourceList.ResourceCleanup() {
@Override
@ -1239,7 +1239,7 @@ public class FileChannelImpl
sessionImpl.addOrCleanupIfFail(resource);
return segment;
} else {
return new MappedMemorySegmentImpl.EmptyMappedMemorySegmentImpl(readOnly, sessionImpl);
return new MappedMemorySegmentImpl(0, null, 0, readOnly, sessionImpl);
}
}