8295044: Implementation of Foreign Function and Memory API (Second Preview)

Co-authored-by: Jorn Vernee <jvernee@openjdk.org>
Co-authored-by: Per Minborg <pminborg@openjdk.org>
Co-authored-by: Maurizio Cimadamore <mcimadamore@openjdk.org>
Reviewed-by: jvernee, pminborg, psandoz, alanb, sundar
This commit is contained in:
Maurizio Cimadamore 2022-12-05 13:49:53 +00:00
parent bd381886e0
commit 73baadceb6
252 changed files with 9221 additions and 7889 deletions

View file

@ -52,10 +52,12 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.internal.javac.PreviewFeature;
import jdk.internal.loader.BuiltinClassLoader;
import jdk.internal.loader.BootLoader;
import jdk.internal.loader.ClassLoaders;
import jdk.internal.misc.CDS;
import jdk.internal.module.ModuleBootstrap;
import jdk.internal.module.ModuleLoaderMap;
import jdk.internal.module.ServicesCatalog;
import jdk.internal.module.Resources;
@ -256,25 +258,77 @@ public final class Module implements AnnotatedElement {
/**
* Update this module to allow access to restricted methods.
*/
Module implAddEnableNativeAccess() {
synchronized Module implAddEnableNativeAccess() {
enableNativeAccess = true;
return this;
}
/**
* Update all unnamed modules to allow access to restricted methods.
* Returns {@code true} if this module can access
* <a href="foreign/package-summary.html#restricted"><em>restricted</em></a> methods.
*
* @since 20
*
* @return {@code true} if this module can access <em>restricted</em> methods.
*/
static void implAddEnableNativeAccessAllUnnamed() {
ALL_UNNAMED_MODULE.enableNativeAccess = true;
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public boolean isNativeAccessEnabled() {
Module target = moduleForNativeAccess();
synchronized(target) {
return target.enableNativeAccess;
}
}
// Returns the Module object that holds the enableNativeAccess
// flag for this module.
private Module moduleForNativeAccess() {
return isNamed() ? this : ALL_UNNAMED_MODULE;
}
// This is invoked from Reflection.ensureNativeAccess
void ensureNativeAccess(Class<?> owner, String methodName) {
// The target module whose enableNativeAccess flag is ensured
Module target = moduleForNativeAccess();
// racy read of the enable native access flag
boolean isNativeAccessEnabled = target.enableNativeAccess;
if (!isNativeAccessEnabled) {
synchronized (target) {
// safe read of the enableNativeAccess of the target module
isNativeAccessEnabled = target.enableNativeAccess;
// check again with the safely read flag
if (isNativeAccessEnabled) {
// another thread beat us to it - nothing to do
return;
} else if (ModuleBootstrap.hasEnableNativeAccessFlag()) {
throw new IllegalCallerException("Illegal native access from: " + this);
} else {
// warn and set flag, so that only one warning is reported per module
String cls = owner.getName();
String mtd = cls + "::" + methodName;
String mod = isNamed() ? "module " + getName() : "the unnamed module";
String modflag = isNamed() ? getName() : "ALL-UNNAMED";
System.err.printf("""
WARNING: A restricted method in %s has been called
WARNING: %s has been called by %s
WARNING: Use --enable-native-access=%s to avoid a warning for this module
%n""", cls, mtd, mod, modflag);
// set the flag
target.enableNativeAccess = true;
}
}
}
}
/**
* Returns true if module m can access restricted methods.
* Update all unnamed modules to allow access to restricted methods.
*/
boolean implIsEnableNativeAccess() {
return isNamed() ?
enableNativeAccess :
ALL_UNNAMED_MODULE.enableNativeAccess;
static void implAddEnableNativeAccessToAllUnnamed() {
synchronized (ALL_UNNAMED_MODULE) {
ALL_UNNAMED_MODULE.enableNativeAccess = true;
}
}
// --

View file

@ -44,15 +44,17 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.internal.javac.PreviewFeature;
import jdk.internal.loader.ClassLoaderValue;
import jdk.internal.loader.Loader;
import jdk.internal.loader.LoaderPool;
import jdk.internal.module.ServicesCatalog;
import jdk.internal.misc.CDS;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import jdk.internal.vm.annotation.Stable;
import sun.security.util.SecurityConstants;
/**
* A layer of modules in the Java virtual machine.
*
@ -297,6 +299,39 @@ public final class ModuleLayer {
source.implAddOpens(pn, target);
return this;
}
/**
* Enables native access for a module in the layer if the caller's module
* has native access.
*
* <p> This method is <a href="foreign/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 target
* The module to update
*
* @return This controller
*
* @throws IllegalArgumentException
* If {@code target} is not in the module layer
*
* @throws IllegalCallerException
* If the caller is in a module that does not have native access enabled
*
* @since 20
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
@CallerSensitive
public Controller enableNativeAccess(Module target) {
ensureInLayer(target);
Reflection.ensureNativeAccess(Reflection.getCallerClass(), Module.class,
"enableNativeAccess");
target.implAddEnableNativeAccess();
return this;
}
}

View file

@ -2437,11 +2437,11 @@ public final class System {
public Module addEnableNativeAccess(Module m) {
return m.implAddEnableNativeAccess();
}
public void addEnableNativeAccessAllUnnamed() {
Module.implAddEnableNativeAccessAllUnnamed();
public void addEnableNativeAccessToAllUnnamed() {
Module.implAddEnableNativeAccessToAllUnnamed();
}
public boolean isEnableNativeAccess(Module m) {
return m.implIsEnableNativeAccess();
public void ensureNativeAccess(Module m, Class<?> owner, String methodName) {
m.ensureNativeAccess(owner, methodName);
}
public ServicesCatalog getServicesCatalog(ModuleLayer layer) {
return layer.getServicesCatalog();

View file

@ -1,167 +0,0 @@
/*
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package java.lang.foreign;
import java.util.Objects;
import java.util.Optional;
import jdk.internal.foreign.Utils;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Stable;
abstract non-sealed class AbstractLayout implements MemoryLayout {
private final long size;
final long alignment;
private final Optional<String> name;
@Stable
long cachedSize;
public AbstractLayout(long size, long alignment, Optional<String> name) {
this.size = size;
this.alignment = alignment;
this.name = name;
}
@Override
public AbstractLayout withName(String name) {
Objects.requireNonNull(name);
return dup(alignment, Optional.of(name));
}
@Override
public final Optional<String> name() {
return name;
}
abstract AbstractLayout dup(long alignment, Optional<String> name);
@Override
public AbstractLayout withBitAlignment(long alignmentBits) {
checkAlignment(alignmentBits);
return dup(alignmentBits, name);
}
void checkAlignment(long alignmentBitCount) {
if (((alignmentBitCount & (alignmentBitCount - 1)) != 0L) || //alignment must be a power of two
(alignmentBitCount < 8)) { //alignment must be greater than 8
throw new IllegalArgumentException("Invalid alignment: " + alignmentBitCount);
}
}
static void checkSize(long size) {
checkSize(size, false);
}
static void checkSize(long size, boolean includeZero) {
if (size < 0 || (!includeZero && size == 0)) {
throw new IllegalArgumentException("Invalid size for layout: " + size);
}
}
@Override
public final long bitAlignment() {
return alignment;
}
@Override
@ForceInline
public long byteSize() {
if (cachedSize == 0) {
cachedSize = Utils.bitsToBytesOrThrow(bitSize(),
() -> new UnsupportedOperationException("Cannot compute byte size; bit size is not a multiple of 8"));
}
return cachedSize;
}
@Override
public long bitSize() {
return size;
}
String decorateLayoutString(String s) {
if (name().isPresent()) {
s = String.format("%s(%s)", s, name().get());
}
if (!hasNaturalAlignment()) {
s = alignment + "%" + s;
}
return s;
}
boolean hasNaturalAlignment() {
return size == alignment;
}
@Override
public boolean isPadding() {
return this instanceof PaddingLayout;
}
// the following methods have to copy the same Javadoc as in MemoryLayout, or subclasses will just show
// the Object methods javadoc
/**
* {@return the hash code value for this layout}
*/
@Override
public int hashCode() {
return Objects.hash(name, size, alignment);
}
/**
* Compares the specified object with this layout for equality. Returns {@code true} if and only if the specified
* object is also a layout, and it is equal to this layout. Two layouts are considered equal if they are of
* the same kind, have the same size, name and alignment constraints. Furthermore, depending on the layout kind, additional
* conditions must be satisfied:
* <ul>
* <li>two value layouts are considered equal if they have the same {@linkplain ValueLayout#order() order},
* and {@linkplain ValueLayout#carrier() carrier}</li>
* <li>two sequence layouts are considered equal if they have the same element count (see {@link SequenceLayout#elementCount()}), and
* if their element layouts (see {@link SequenceLayout#elementLayout()}) are also equal</li>
* <li>two group layouts are considered equal if they are of the same kind (see {@link GroupLayout#isStruct()},
* {@link GroupLayout#isUnion()}) and if their member layouts (see {@link GroupLayout#memberLayouts()}) are also equal</li>
* </ul>
*
* @param other the object to be compared for equality with this layout.
* @return {@code true} if the specified object is equal to this layout.
*/
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
return other instanceof AbstractLayout otherLayout &&
name.equals(otherLayout.name) &&
size == otherLayout.size &&
alignment == otherLayout.alignment;
}
/**
* {@return the string representation of this layout}
*/
public abstract String toString();
}

View file

@ -1,47 +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 java.lang.foreign;
import jdk.internal.javac.PreviewFeature;
/**
* An object that may be projected down to a {@linkplain #address() memory address}.
* Examples of addressable types are {@link MemorySegment}, {@link MemoryAddress} and {@link VaList}.
* <p>
* The {@link Addressable} type is used by a {@linkplain Linker linker} to model the types of
* {@linkplain Linker#downcallHandle(FunctionDescriptor) downcall handle} parameters that must be passed <em>by reference</em>
* (e.g. memory addresses, variable argument lists and upcall stubs).
*
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public sealed interface Addressable permits MemorySegment, MemoryAddress, VaList {
/**
* {@return the {@linkplain MemoryAddress memory address} associated with this addressable}
*/
MemoryAddress address();
}

View file

@ -0,0 +1,166 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.foreign;
import jdk.internal.foreign.MemorySessionImpl;
import jdk.internal.javac.PreviewFeature;
/**
* An arena controls the lifecycle of memory segments, providing both flexible allocation and timely deallocation.
* <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:
*
* {@snippet lang = java:
* try (Arena arena = Arena.openConfined()) {
* MemorySegment segment = MemorySegment.allocateNative(100, arena.scope());
* ...
* } // memory released here
*}
*
* Furthermore, an arena is a {@link SegmentAllocator}. All the segments {@linkplain #allocate(long, long) allocated} by the
* arena are associated with the arena scope. This makes arenas extremely useful when interacting with foreign code, as shown below:
*
* {@snippet lang = java:
* try (Arena arena = Arena.openConfined()) {
* MemorySegment nativeArray = arena.allocateArray(ValueLayout.JAVA_INT, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
* MemorySegment nativeString = arena.allocateUtf8String("Hello!");
* MemorySegment upcallStub = linker.upcallStub(handle, desc, arena.scope());
* ...
* } // memory released here
*}
*
* <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
* 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
* by a thread other than the accessing thread, then ensuring correctness is much more complex. For example, a segment
* allocated with the arena might be accessed <em>while</em> another thread attempts, concurrently, to close the arena.
* To provide the strong temporal safety guarantee without forcing every client, even simple ones, to incur a performance
* impact, arenas are divided into <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}
* 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
* multiple threads need to access the same memory segment concurrently (e.g. in the case of parallel processing).
* Moreover, a shared arena {@linkplain #isCloseableBy(Thread) can be closed} by any thread.
*
* @since 20
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public interface Arena extends SegmentAllocator, AutoCloseable {
/**
* Returns a native memory segment with the given size (in bytes) and alignment constraint (in bytes).
* The returned segment is associated with the arena scope.
* The segment's {@link MemorySegment#address() address} is the starting address of the
* allocated off-heap memory region backing the segment, and the address is
* aligned according the provided alignment constraint.
*
* @implSpec
* The default implementation of this method is equivalent to the following code:
* {@snippet lang = java:
* MemorySegment.allocateNative(bytesSize, byteAlignment, scope());
*}
* More generally implementations of this method must return a native segment featuring the requested size,
* and that is compatible with the provided alignment constraint. Furthermore, for any two segments
* {@code S1, S2} returned by this method, the following invariant must hold:
*
* {@snippet lang = java:
* S1.overlappingSlice(S2).isEmpty() == true
*}
*
* @param byteSize the size (in bytes) of the off-heap memory block backing the native memory segment.
* @param byteAlignment the alignment constraint (in bytes) of the off-heap region of memory backing the native memory segment.
* @return a new native memory segment.
* @throws IllegalArgumentException if {@code bytesSize < 0}, {@code alignmentBytes <= 0}, or if {@code alignmentBytes}
* is not a power of 2.
* @throws IllegalStateException if the arena has already been {@linkplain #close() closed}.
* @throws WrongThreadException if this method is called from a thread {@code T},
* such that {@code scope().isAccessibleBy(T) == false}.
* @see MemorySegment#allocateNative(long, long, SegmentScope)
*/
@Override
default MemorySegment allocate(long byteSize, long byteAlignment) {
return MemorySegment.allocateNative(byteSize, byteAlignment, scope());
}
/**
* {@return the arena scope}
*/
SegmentScope scope();
/**
* Closes this arena. If this method completes normally, the arena scope is no longer {@linkplain SegmentScope#isAlive() alive},
* and all the memory segments associated with it can no longer be accessed. Furthermore, any off-heap region of memory backing the
* segments associated with that scope are also released.
*
* @apiNote This operation is not idempotent; that is, closing an already closed arena <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()
*
* @throws IllegalStateException if the arena has already been closed.
* @throws IllegalStateException if the arena scope is {@linkplain SegmentScope#whileAlive(Runnable) kept alive}.
* @throws WrongThreadException if this method is called from a thread {@code T},
* such that {@code isCloseableBy(T) == false}.
*/
@Override
void close();
/**
* {@return {@code true} if the provided thread can close this arena}
* @param thread the thread to be tested.
*/
boolean isCloseableBy(Thread thread);
/**
* {@return a new confined arena, owned by the current thread}
*/
static Arena openConfined() {
return MemorySessionImpl.createConfined(Thread.currentThread()).asArena();
}
/**
* {@return a new shared arena}
*/
static Arena openShared() {
return MemorySessionImpl.createShared().asArena();
}
}

View file

@ -25,101 +25,38 @@
package java.lang.foreign;
import java.lang.invoke.MethodHandle;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.lang.invoke.MethodType;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import java.util.List;
import jdk.internal.foreign.FunctionDescriptorImpl;
import jdk.internal.javac.PreviewFeature;
/**
* A function descriptor is made up of zero or more argument layouts and zero or one return layout. A function descriptor
* is used to model the signature of foreign functions when creating
* {@linkplain Linker#downcallHandle(Addressable, FunctionDescriptor) downcall method handles} or
* {@linkplain Linker#upcallStub(MethodHandle, FunctionDescriptor, MemorySession) upcall stubs}.
* A function descriptor models the signature of foreign functions. A function descriptor is made up of zero or more
* argument layouts and zero or one return layout. A function descriptor is typically used when creating
* {@linkplain Linker#downcallHandle(MemorySegment, FunctionDescriptor, Linker.Option...) downcall method handles} or
* {@linkplain Linker#upcallStub(MethodHandle, FunctionDescriptor, SegmentScope) upcall stubs}.
*
* @implSpec
* This class is immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
* Implementing classes are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
*
* @see MemoryLayout
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public sealed class FunctionDescriptor permits FunctionDescriptor.VariadicFunction {
private final MemoryLayout resLayout;
private final List<MemoryLayout> argLayouts;
private FunctionDescriptor(MemoryLayout resLayout, List<MemoryLayout> argLayouts) {
this.resLayout = resLayout;
this.argLayouts = argLayouts;
}
public sealed interface FunctionDescriptor permits FunctionDescriptorImpl {
/**
* {@return the return layout (if any) associated with this function descriptor}
*/
public Optional<MemoryLayout> returnLayout() {
return Optional.ofNullable(resLayout);
}
Optional<MemoryLayout> returnLayout();
/**
* {@return the argument layouts associated with this function descriptor (as an immutable list)}.
*/
public List<MemoryLayout> argumentLayouts() {
return Collections.unmodifiableList(argLayouts);
}
/**
* Creates a function descriptor with the given return and argument layouts.
* @param resLayout the return layout.
* @param argLayouts the argument layouts.
* @return the new function descriptor.
*/
public static FunctionDescriptor of(MemoryLayout resLayout, MemoryLayout... argLayouts) {
Objects.requireNonNull(resLayout);
Objects.requireNonNull(argLayouts);
Arrays.stream(argLayouts).forEach(Objects::requireNonNull);
return new FunctionDescriptor(resLayout, List.of(argLayouts));
}
/**
* Creates a function descriptor with the given argument layouts and no return layout.
* @param argLayouts the argument layouts.
* @return the new function descriptor.
*/
public static FunctionDescriptor ofVoid(MemoryLayout... argLayouts) {
Objects.requireNonNull(argLayouts);
Arrays.stream(argLayouts).forEach(Objects::requireNonNull);
return new FunctionDescriptor(null, List.of(argLayouts));
}
/**
* Creates a specialized variadic function descriptor, by appending given variadic layouts to this
* function descriptor argument layouts. The resulting function descriptor can report the position
* of the {@linkplain #firstVariadicArgumentIndex() first variadic argument}, and cannot be altered
* in any way: for instance, calling {@link #changeReturnLayout(MemoryLayout)} on the resulting descriptor
* will throw an {@link UnsupportedOperationException}.
* @param variadicLayouts the variadic argument layouts to be appended to this descriptor argument layouts.
* @return a variadic function descriptor, or this descriptor if {@code variadicLayouts.length == 0}.
*/
public FunctionDescriptor asVariadic(MemoryLayout... variadicLayouts) {
Objects.requireNonNull(variadicLayouts);
Arrays.stream(variadicLayouts).forEach(Objects::requireNonNull);
return variadicLayouts.length == 0 ? this : new VariadicFunction(this, variadicLayouts);
}
/**
* The index of the first variadic argument layout (where defined).
* @return The index of the first variadic argument layout, or {@code -1} if this is not a
* {@linkplain #asVariadic(MemoryLayout...) variadic} layout.
*/
public int firstVariadicArgumentIndex() {
return -1;
}
List<MemoryLayout> argumentLayouts();
/**
* Returns a function descriptor with the given argument layouts appended to the argument layout array
@ -127,9 +64,7 @@ public sealed class FunctionDescriptor permits FunctionDescriptor.VariadicFuncti
* @param addedLayouts the argument layouts to append.
* @return the new function descriptor.
*/
public FunctionDescriptor appendArgumentLayouts(MemoryLayout... addedLayouts) {
return insertArgumentLayouts(argLayouts.size(), addedLayouts);
}
FunctionDescriptor appendArgumentLayouts(MemoryLayout... addedLayouts);
/**
* Returns a function descriptor with the given argument layouts inserted at the given index, into the argument
@ -139,110 +74,57 @@ public sealed class FunctionDescriptor permits FunctionDescriptor.VariadicFuncti
* @return the new function descriptor.
* @throws IllegalArgumentException if {@code index < 0 || index > argumentLayouts().size()}.
*/
public FunctionDescriptor insertArgumentLayouts(int index, MemoryLayout... addedLayouts) {
if (index < 0 || index > argLayouts.size())
throw new IllegalArgumentException("Index out of bounds: " + index);
List<MemoryLayout> added = List.of(addedLayouts); // null check on array and its elements
List<MemoryLayout> newLayouts = new ArrayList<>(argLayouts.size() + addedLayouts.length);
newLayouts.addAll(argLayouts.subList(0, index));
newLayouts.addAll(added);
newLayouts.addAll(argLayouts.subList(index, argLayouts.size()));
return new FunctionDescriptor(resLayout, newLayouts);
}
FunctionDescriptor insertArgumentLayouts(int index, MemoryLayout... addedLayouts);
/**
* Returns a function descriptor with the given memory layout as the new return layout.
* @param newReturn the new return layout.
* @return the new function descriptor.
*/
public FunctionDescriptor changeReturnLayout(MemoryLayout newReturn) {
Objects.requireNonNull(newReturn);
return new FunctionDescriptor(newReturn, argLayouts);
}
FunctionDescriptor changeReturnLayout(MemoryLayout newReturn);
/**
* Returns a function descriptor with the return layout dropped. This is useful to model functions
* which return no values.
* @return the new function descriptor.
*/
public FunctionDescriptor dropReturnLayout() {
return new FunctionDescriptor(null, argLayouts);
}
FunctionDescriptor dropReturnLayout();
/**
* {@return the string representation of this function descriptor}
*/
@Override
public String toString() {
return String.format("(%s)%s",
IntStream.range(0, argLayouts.size())
.mapToObj(i -> (i == firstVariadicArgumentIndex() ?
"..." : "") + argLayouts.get(i))
.collect(Collectors.joining()),
returnLayout().map(Object::toString).orElse("v"));
}
/**
* Compares the specified object with this function descriptor for equality. Returns {@code true} if and only if the specified
* object is also a function descriptor, and all the following conditions are met:
* Returns the method type consisting of the carrier types of the layouts in this function descriptor.
* <p>
* The carrier type of a layout is determined as follows:
* <ul>
* <li>the two function descriptors have equals return layouts (see {@link MemoryLayout#equals(Object)}), or both have no return layout;</li>
* <li>the two function descriptors have argument layouts that are pair-wise {@linkplain MemoryLayout#equals(Object) equal}; and</li>
* <li>the two function descriptors have the same leading {@linkplain #firstVariadicArgumentIndex() variadic argument index}</li>
* <li>If the layout is a {@link ValueLayout} the carrier type is determined through {@link ValueLayout#carrier()}.</li>
* <li>If the layout is a {@link GroupLayout} the carrier type is {@link MemorySegment}.</li>
* <li>If the layout is a {@link PaddingLayout}, or {@link SequenceLayout} an {@link IllegalArgumentException} is thrown.</li>
* </ul>
*
* @param other the object to be compared for equality with this function descriptor.
* @return {@code true} if the specified object is equal to this function descriptor.
* @return the method type consisting of the carrier types of the layouts in this function descriptor
* @throws IllegalArgumentException if one or more layouts in the function descriptor can not be mapped to carrier
* types (e.g. if they are sequence layouts or padding layouts).
*/
@Override
public boolean equals(Object other) {
return other instanceof FunctionDescriptor f &&
Objects.equals(resLayout, f.resLayout) &&
Objects.equals(argLayouts, f.argLayouts) &&
firstVariadicArgumentIndex() == f.firstVariadicArgumentIndex();
MethodType toMethodType();
/**
* Creates a function descriptor with the given return and argument layouts.
* @param resLayout the return layout.
* @param argLayouts the argument layouts.
* @return the new function descriptor.
*/
static FunctionDescriptor of(MemoryLayout resLayout, MemoryLayout... argLayouts) {
Objects.requireNonNull(resLayout);
// Null checks are implicit in List.of(argLayouts)
return FunctionDescriptorImpl.of(resLayout, List.of(argLayouts));
}
/**
* {@return the hash code value for this function descriptor}
* Creates a function descriptor with the given argument layouts and no return layout.
* @param argLayouts the argument layouts.
* @return the new function descriptor.
*/
@Override
public int hashCode() {
return Objects.hash(argLayouts, resLayout, firstVariadicArgumentIndex());
}
static final class VariadicFunction extends FunctionDescriptor {
private final int firstVariadicIndex;
public VariadicFunction(FunctionDescriptor descriptor, MemoryLayout... argLayouts) {
super(descriptor.returnLayout().orElse(null),
Stream.concat(descriptor.argumentLayouts().stream(), Stream.of(argLayouts)).toList());
this.firstVariadicIndex = descriptor.argumentLayouts().size();
}
@Override
public int firstVariadicArgumentIndex() {
return firstVariadicIndex;
}
@Override
public FunctionDescriptor appendArgumentLayouts(MemoryLayout... addedLayouts) {
throw new UnsupportedOperationException();
}
@Override
public FunctionDescriptor insertArgumentLayouts(int index, MemoryLayout... addedLayouts) {
throw new UnsupportedOperationException();
}
@Override
public FunctionDescriptor changeReturnLayout(MemoryLayout newReturn) {
throw new UnsupportedOperationException();
}
@Override
public FunctionDescriptor dropReturnLayout() {
throw new UnsupportedOperationException();
}
static FunctionDescriptor ofVoid(MemoryLayout... argLayouts) {
// Null checks are implicit in List.of(argLayouts)
return FunctionDescriptorImpl.ofVoid(List.of(argLayouts));
}
}

View file

@ -25,19 +25,14 @@
*/
package java.lang.foreign;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.LongBinaryOperator;
import java.util.stream.Collectors;
import jdk.internal.javac.PreviewFeature;
/**
* A compound layout that aggregates multiple <em>member layouts</em>. There are two ways in which member layouts
* can be combined: if member layouts are laid out one after the other, the resulting group layout is said to be a <em>struct</em>
* can be combined: if member layouts are laid out one after the other, the resulting group layout is said to be a <em>struct layout</em>
* (see {@link MemoryLayout#structLayout(MemoryLayout...)}); conversely, if all member layouts are laid out at the same starting offset,
* the resulting group layout is said to be a <em>union</em> (see {@link MemoryLayout#unionLayout(MemoryLayout...)}).
* the resulting group layout is said to be a <em>union layout</em> (see {@link MemoryLayout#unionLayout(MemoryLayout...)}).
*
* @implSpec
* This class is immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
@ -45,55 +40,7 @@ import jdk.internal.javac.PreviewFeature;
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public final class GroupLayout extends AbstractLayout implements MemoryLayout {
/**
* The group kind.
*/
enum Kind {
/**
* A 'struct' kind.
*/
STRUCT("", Math::addExact),
/**
* A 'union' kind.
*/
UNION("|", Math::max);
final String delimTag;
final LongBinaryOperator sizeOp;
Kind(String delimTag, LongBinaryOperator sizeOp) {
this.delimTag = delimTag;
this.sizeOp = sizeOp;
}
long sizeof(List<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
}
}
private final Kind kind;
private final List<MemoryLayout> elements;
GroupLayout(Kind kind, List<MemoryLayout> elements) {
this(kind, elements, kind.alignof(elements), Optional.empty());
}
GroupLayout(Kind kind, List<MemoryLayout> elements, long alignment, Optional<String> name) {
super(kind.sizeof(elements), alignment, name);
this.kind = kind;
this.elements = elements;
}
public sealed interface GroupLayout extends MemoryLayout permits StructLayout, UnionLayout {
/**
* Returns the member layouts associated with this group.
@ -104,84 +51,17 @@ public final class GroupLayout extends AbstractLayout implements MemoryLayout {
*
* @return the member layouts associated with this group.
*/
public List<MemoryLayout> memberLayouts() {
return Collections.unmodifiableList(elements);
}
List<MemoryLayout> memberLayouts();
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return decorateLayoutString(elements.stream()
.map(Object::toString)
.collect(Collectors.joining(kind.delimTag, "[", "]")));
}
/**
* {@return {@code true}, if this group layout is a struct layout}
*/
public boolean isStruct() {
return kind == Kind.STRUCT;
}
/**
* {@return {@code true}, if this group layout is a union layout}
*/
public boolean isUnion() {
return kind == Kind.UNION;
}
GroupLayout withName(String name);
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!super.equals(other)) {
return false;
}
return other instanceof GroupLayout otherGroup &&
kind == otherGroup.kind &&
elements.equals(otherGroup.elements);
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), kind, elements);
}
@Override
GroupLayout dup(long alignment, Optional<String> name) {
return new GroupLayout(kind, elements, alignment, name);
}
@Override
boolean hasNaturalAlignment() {
return alignment == kind.alignof(elements);
}
//hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
//but that causes issues with javadoc, see JDK-8224052
/**
* {@inheritDoc}
*/
@Override
public GroupLayout withName(String name) {
return (GroupLayout)super.withName(name);
}
/**
* {@inheritDoc}
*/
@Override
public GroupLayout withBitAlignment(long alignmentBits) {
return (GroupLayout)super.withBitAlignment(alignmentBits);
}
GroupLayout withBitAlignment(long bitAlignment);
}

View file

@ -26,13 +26,13 @@
package java.lang.foreign;
import jdk.internal.foreign.abi.AbstractLinker;
import jdk.internal.foreign.abi.LinkerOptions;
import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.javac.PreviewFeature;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
/**
* A linker provides access to foreign functions from Java code, and access to Java code from foreign functions.
@ -47,9 +47,9 @@ import java.lang.invoke.MethodType;
* in the JVM and foreign functions in the library. In particular:
* <ul>
* <li>A linker allows Java code to link against foreign functions, via
* {@linkplain #downcallHandle(Addressable, FunctionDescriptor) downcall method handles}; and</li>
* {@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, MemorySession) upcall stubs}.</li>
* via the generation of {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, SegmentScope) 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.
@ -62,29 +62,16 @@ import java.lang.invoke.MethodType;
*
* <h2 id="downcall-method-handles">Downcall method handles</h2>
*
* {@linkplain #downcallHandle(FunctionDescriptor) Linking a foreign function} is a process which requires a function descriptor,
* {@linkplain #downcallHandle(FunctionDescriptor, Option...) Linking a foreign function} is a process which requires a function descriptor,
* a set of memory layouts which, together, specify the signature of the foreign function to be linked, and returns,
* when complete, a downcall method handle, that is, a method handle that can be used to invoke the target foreign function.
* <p>
* The Java {@linkplain java.lang.invoke.MethodType method type} associated with the returned method handle is
* {@linkplain #downcallType(FunctionDescriptor) derived} from the argument and return layouts in the function descriptor.
* More specifically, given each layout {@code L} in the function descriptor, a corresponding carrier {@code C} is inferred,
* as described below:
* {@linkplain FunctionDescriptor#toMethodType() derived} from the argument and return layouts in the function descriptor.
* The downcall method handle type, might then be decorated by additional leading parameters, in the given order if both are present:
* <ul>
* <li>if {@code L} is a {@link ValueLayout} with carrier {@code E} then there are two cases:
* <ul>
* <li>if {@code L} occurs in a parameter position and {@code E} is {@code MemoryAddress.class},
* then {@code C = Addressable.class};</li>
* <li>otherwise, {@code C = E};
* </ul></li>
* <li>or, if {@code L} is a {@link GroupLayout}, then {@code C} is set to {@code MemorySegment.class}</li>
* </ul>
* <p>
* The downcall method handle type, derived as above, might 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) without specifying a target address},
* the downcall method handle type features a leading parameter of type {@link Addressable}, from which the
* <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
@ -93,26 +80,15 @@ import java.lang.invoke.MethodType;
*
* <h2 id="upcall-stubs">Upcall stubs</h2>
*
* {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, MemorySession) Creating an upcall stub} requires a method
* {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, SegmentScope) Creating an upcall stub} requires a method
* handle and a function descriptor; in this case, the set of memory layouts in the function descriptor
* specify the signature of the function pointer associated with the upcall stub.
* <p>
* The type of the provided method handle has to {@linkplain #upcallType(FunctionDescriptor) match} the Java
* {@linkplain java.lang.invoke.MethodType method type} associated with the upcall stub, which is derived from the argument
* and return layouts in the function descriptor. More specifically, given each layout {@code L} in the function descriptor,
* a corresponding carrier {@code C} is inferred, as described below:
* <ul>
* <li>If {@code L} is a {@link ValueLayout} with carrier {@code E} then there are two cases:
* <ul>
* <li>If {@code L} occurs in a return position and {@code E} is {@code MemoryAddress.class},
* then {@code C = Addressable.class};</li>
* <li>Otherwise, {@code C = E};
* </ul></li>
* <li>Or, if {@code L} is a {@link GroupLayout}, then {@code C} is set to {@code MemorySegment.class}</li>
* </ul>
* 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.
* <p>
* Upcall stubs are modelled by instances of type {@link MemorySegment}; upcall stubs can be passed by reference to other
* downcall method handles (as {@link MemorySegment} implements the {@link Addressable} interface) and,
* when no longer required, they can be {@linkplain MemorySession#close() released}, via their associated {@linkplain MemorySession session}.
* downcall method handles and, they are released via their associated {@linkplain SegmentScope scope}.
*
* <h2 id="safety">Safety considerations</h2>
*
@ -121,23 +97,34 @@ import java.lang.invoke.MethodType;
* the linker runtime cannot validate linkage requests. When a client interacts with a downcall method handle obtained
* through an invalid linkage request (e.g. by specifying a function descriptor featuring too many argument layouts),
* the result of such interaction is unspecified and can lead to JVM crashes. On downcall handle invocation,
* the linker runtime guarantees the following for any argument that is a memory resource {@code R} (of type {@link MemorySegment}
* or {@link VaList}):
* the linker runtime guarantees the following for any argument {@code A} of type {@link MemorySegment} whose corresponding
* layout is {@link ValueLayout#ADDRESS}:
* <ul>
* <li>The memory session of {@code R} is {@linkplain MemorySession#isAlive() alive}. Otherwise, the invocation throws
* <li>The scope of {@code A} is {@linkplain SegmentScope#isAlive() alive}. Otherwise, the invocation throws
* {@link IllegalStateException};</li>
* <li>The invocation occurs in same thread as the one {@linkplain MemorySession#ownerThread() owning} the memory session of {@code R},
* if said session is confined. Otherwise, the invocation throws {@link WrongThreadException}; and</li>
* <li>The memory session of {@code R} is {@linkplain MemorySession#whileAlive(Runnable) kept alive} (and cannot be closed) during the invocation.</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}.
* <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,
* if the foreign code casts the function pointer associated with an upcall stub to a type
* that is incompatible with the provided function descriptor. Moreover, if the target method
* handle associated with an upcall stub returns a {@linkplain MemoryAddress memory address}, clients must ensure
* handle associated with an upcall stub returns a {@linkplain MemorySegment memory segment}, clients must ensure
* that this address cannot become invalid after the upcall completes. This can lead to unspecified behavior,
* and even JVM crashes, since an upcall is typically executed in the context of a downcall method handle invocation.
* <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>.
@ -159,22 +146,22 @@ public sealed interface Linker permits AbstractLinker {
* 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 constraints 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 MemoryAddress}.
* 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(Addressable, FunctionDescriptor) downcall method handle} or an
* {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, MemorySession) upcall stub}.
* {@link #downcallHandle(MemorySegment, FunctionDescriptor, Option...) downcall method handle} or an
* {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, SegmentScope) upcall stub}.
* <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 a {@linkplain FunctionDescriptor#asVariadic(MemoryLayout...) <em>variadic</em>}
* function descriptor, in which the specialized signature of a given variable arity callsite is described in full.
* Alternatively, where the foreign library allows it, clients might be able to interact with variadic functions by
* passing a trailing parameter of type {@link VaList} (e.g. as in {@code vsprintf}).
* variadic function by using {@linkplain Linker.Option#firstVariadicArg(int) a linker option} to indicate
* the start of the list of variadic arguments, together with a specialized function descriptor describing a
* given variable arity callsite. Alternatively, where the foreign library allows it, clients might be able to
* interact with variadic functions by passing a trailing parameter of type {@link VaList} (e.g. as in {@code vsprintf}).
* <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
@ -199,55 +186,60 @@ public sealed interface Linker permits AbstractLinker {
}
/**
* Creates a method handle which can be used to call a target foreign function with the given signature and address.
* Creates a method handle which can be used to call a foreign function with the given signature and address.
* <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 runtime
* to allocate structs returned by-value.
* an additional prefix parameter, of type {@link SegmentAllocator}, which will be used by the linker to allocate
* structs returned by-value.
* <p>
* Calling this method is equivalent to the following code:
* {@snippet lang=java :
* linker.downcallHandle(function).bindTo(symbol);
* }
*
* @param symbol the address of the target function.
* @param symbol the address of the target function.
* @param function the function descriptor of the target function.
* @param options any linker options.
* @return a downcall method handle. The method handle type is <a href="Linker.html#downcall-method-handles"><em>inferred</em></a>
* @throws IllegalArgumentException if the provided function descriptor is not supported by this linker.
* or if the symbol is {@link MemoryAddress#NULL}
* or if the symbol is {@link MemorySegment#NULL}
* @throws IllegalArgumentException if an invalid combination of linker options is given.
*/
default MethodHandle downcallHandle(Addressable symbol, FunctionDescriptor function) {
default MethodHandle downcallHandle(MemorySegment symbol, FunctionDescriptor function, Option... options) {
SharedUtils.checkSymbol(symbol);
return downcallHandle(function).bindTo(symbol);
return downcallHandle(function, options).bindTo(symbol);
}
/**
* Creates a method handle which can be used to call a target foreign function with the given signature.
* Creates a method handle which can be used to call a foreign function with the given signature.
* The resulting method handle features a prefix parameter (as the first parameter) corresponding to the foreign function
* entry point, of type {@link Addressable}, which is used to specify the address of the target function
* entry point, of type {@link MemorySegment}, which is used to specify the address of the target function
* to be called.
* <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 runtime to allocate structs returned by-value.
* which will be used by the linker to allocate structs returned by-value.
* <p>
* The returned method handle will throw an {@link IllegalArgumentException} if the {@link Addressable} parameter passed to it is
* associated with the {@link MemoryAddress#NULL} address, or a {@link NullPointerException} if that parameter is {@code null}.
* The returned method handle will throw an {@link IllegalArgumentException} if the {@link MemorySegment} parameter passed to it is
* associated with the {@link MemorySegment#NULL} address, or a {@link NullPointerException} if that parameter is {@code null}.
*
* @param function the function descriptor of the target function.
* @param options any linker options.
* @return a downcall method handle. The method handle type is <a href="Linker.html#downcall-method-handles"><em>inferred</em></a>
* from the provided function descriptor.
* @throws IllegalArgumentException if the provided function descriptor is not supported by this linker.
* @throws IllegalArgumentException if an invalid combination of linker options is given.
*/
MethodHandle downcallHandle(FunctionDescriptor function);
MethodHandle downcallHandle(FunctionDescriptor function, Option... options);
/**
* Creates a stub which can be passed to other foreign functions as a function pointer, with the given
* memory session. Calling such a function pointer from foreign code will result in the execution of the provided
* Creates a stub which can be passed to other foreign functions as a function pointer, associated with the given
* scope. Calling such a function pointer from foreign code will result in the execution of the provided
* method handle.
* <p>
* The returned memory segment's base address points to the newly allocated upcall stub, and is associated with
* the provided memory session. When such session is closed, the corresponding upcall stub will be deallocated.
* The returned memory segment's address points to the newly allocated upcall stub, and is associated with
* the provided scope. As such, the corresponding upcall stub will be deallocated
* when the scope becomes not {@linkplain SegmentScope#isAlive() alive}.
* <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
@ -257,16 +249,16 @@ public sealed interface Linker permits AbstractLinker {
*
* @param target the target method handle.
* @param function the upcall stub function descriptor.
* @param session the upcall stub memory session.
* @return a zero-length segment whose base address is the address of the upcall stub.
* @param scope the scope associated with the returned upcall stub segment.
* @return a zero-length segment whose address is the address of the upcall stub.
* @throws IllegalArgumentException if the provided function descriptor is not supported by this linker.
* @throws IllegalArgumentException if it is determined that the target method handle can throw an exception, or if the target method handle
* has a type that does not match the upcall stub <a href="Linker.html#upcall-stubs"><em>inferred type</em></a>.
* @throws IllegalStateException if {@code session} is not {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread
* {@linkplain MemorySession#ownerThread() owning} {@code session}.
* @throws IllegalStateException if {@code scope} is not {@linkplain SegmentScope#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread {@code T},
* such that {@code scope.isAccessibleBy(T) == false}.
*/
MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, MemorySession session);
MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, SegmentScope scope);
/**
* Returns a symbol lookup for symbols in a set of commonly used libraries.
@ -284,22 +276,21 @@ public sealed interface Linker permits AbstractLinker {
SymbolLookup defaultLookup();
/**
* {@return the downcall method handle {@linkplain MethodType type} associated with the given function descriptor}
* @param functionDescriptor a function descriptor.
* @throws IllegalArgumentException if one or more layouts in the function descriptor are not supported
* (e.g. if they are sequence layouts or padding layouts).
* A linker option is used to indicate additional linking requirements to the linker,
* besides what is described by a function descriptor.
* @since 20
*/
static MethodType downcallType(FunctionDescriptor functionDescriptor) {
return SharedUtils.inferMethodType(functionDescriptor, false);
}
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
sealed interface Option
permits LinkerOptions.FirstVariadicArg {
/**
* {@return the method handle {@linkplain MethodType type} associated with an upcall stub with the given function descriptor}
* @param functionDescriptor a function descriptor.
* @throws IllegalArgumentException if one or more layouts in the function descriptor are not supported
* (e.g. if they are sequence layouts or padding layouts).
*/
static MethodType upcallType(FunctionDescriptor functionDescriptor) {
return SharedUtils.inferMethodType(functionDescriptor, true);
/**
* {@return a linker option used to denote the index of the first variadic argument layout in a
* foreign function call}
* @param index the index of the first variadic argument in a downcall handle linkage request.
*/
static Option firstVariadicArg(int index) {
return new LinkerOptions.FirstVariadicArg(index);
}
}
}

View file

@ -1,854 +0,0 @@
/*
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package java.lang.foreign;
import java.nio.ByteOrder;
import jdk.internal.foreign.MemoryAddressImpl;
import jdk.internal.javac.PreviewFeature;
import jdk.internal.reflect.CallerSensitive;
import java.lang.invoke.MethodHandle;
/**
* A memory address models a reference into a memory location. Memory addresses are typically obtained in one of the following ways:
* <ul>
* <li>By calling {@link Addressable#address()} on an instance of type {@link Addressable} (e.g. a memory segment);</li>
* <li>By invoking a {@linkplain Linker#downcallHandle(FunctionDescriptor) downcall method handle} which returns a pointer;</li>
* <li>By reading an address from memory, e.g. via {@link MemorySegment#get(ValueLayout.OfAddress, long)}; and</li>
* <li>By the invocation of an {@linkplain Linker#upcallStub(MethodHandle, FunctionDescriptor, MemorySession) upcall stub} which accepts a pointer.</li>
* </ul>
* A memory address is backed by a raw machine pointer, expressed as a {@linkplain #toRawLongValue() long value}.
*
* <h2 id="dereferencing">Dereferencing memory addresses</h2>
*
* A memory address can be read or written using various methods provided in this class (e.g. {@link #get(ValueLayout.OfInt, long)}).
* Each dereference method takes a {@linkplain ValueLayout value layout}, which specifies the size,
* alignment constraints, byte order as well as the Java type associated with the dereference operation, and an offset.
* For instance, to read an int from a segment, using {@linkplain ByteOrder#nativeOrder() default endianness}, the following code can be used:
* {@snippet lang=java :
* MemoryAddress address = ...
* int value = address.get(ValueLayout.JAVA_INT, 0);
* }
*
* If the value to be read is stored in memory using {@link ByteOrder#BIG_ENDIAN big-endian} encoding, the dereference operation
* can be expressed as follows:
* {@snippet lang=java :
* MemoryAddress address = ...
* int value = address.get(ValueLayout.JAVA_INT.withOrder(BIG_ENDIAN), 0);
* }
*
* All the dereference methods in this class are <a href="package-summary.html#restricted"><em>restricted</em></a>: since
* a memory address does not feature temporal nor spatial bounds, the runtime has no way to check the correctness
* of the memory dereference operation.
*
* @implSpec
* Implementations of this interface are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
*
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public sealed interface MemoryAddress extends Addressable permits MemoryAddressImpl {
/**
* {@return the raw long value associated with this memory address}
*/
long toRawLongValue();
/**
* Returns a memory address at given offset from this address.
* @param offset specified offset (in bytes), relative to this address, which should be used to create the new address.
* Might be negative.
* @return a memory address with the given offset from current one.
*/
MemoryAddress addOffset(long offset);
/**
* Reads a UTF-8 encoded, null-terminated string from this address at the given offset.
* <p>
* This method always replaces malformed-input and unmappable-character
* sequences with this charset's default replacement string. The {@link
* java.nio.charset.CharsetDecoder} class should be used when more control
* over the decoding process is required.
* <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 offset offset in bytes (relative to this address). Might be negative.
* The final address of this read operation can be expressed as {@code toRawLongValue() + offset}.
* @return a Java string constructed from the bytes read from the given starting address ({@code toRawLongValue() + offset})
* up to (but not including) the first {@code '\0'} terminator character (assuming one is found).
* @throws IllegalArgumentException if the size of the UTF-8 string is greater than the largest string supported by the platform.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
String getUtf8String(long offset);
/**
* Writes the given string to this address at the given offset, converting it to a null-terminated byte sequence using UTF-8 encoding.
* <p>
* This method always replaces malformed-input and unmappable-character
* sequences with this charset's default replacement string. The {@link
* java.nio.charset.CharsetDecoder} class should be used when more control
* over the decoding process is required.
* <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 offset offset in bytes (relative to this address). Might be negative.
* The final address of this read operation can be expressed as {@code toRawLongValue() + offset}.
* @param str the Java string to be written at this address.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
void setUtf8String(long offset, String str);
/**
* Compares the specified object with this address for equality. Returns {@code true} if and only if the specified
* object is also an address, and it refers to the same memory location as this address.
*
* @param that the object to be compared for equality with this address.
* @return {@code true} if the specified object is equal to this address.
*/
@Override
boolean equals(Object that);
/**
* {@return the hash code value for this address}
*/
@Override
int hashCode();
/**
* The memory address instance modelling the {@code NULL} address.
*/
MemoryAddress NULL = new MemoryAddressImpl(0L);
/**
* Creates a memory address from the given long value.
* @param value the long value representing a raw address.
* @return a memory address with the given raw long value.
*/
static MemoryAddress ofLong(long value) {
return value == 0 ?
NULL :
new MemoryAddressImpl(value);
}
/**
* Reads a byte from this address at the given offset, with the given layout.
* <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 layout of the memory region to be read.
* @param offset offset in bytes (relative to this address). Might be negative.
* The final address of this read operation can be expressed as {@code toRawLongValue() + offset}.
* @return a byte value read from this address.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
byte get(ValueLayout.OfByte layout, long offset);
/**
* Writes a byte into this address at the given offset, with the given layout.
* <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 layout of the memory region to be written.
* @param offset offset in bytes (relative to this address). Might be negative.
* The final address of this write operation can be expressed as {@code toRawLongValue() + offset}.
* @param value the byte value to be written.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
void set(ValueLayout.OfByte layout, long offset, byte value);
/**
* Reads a boolean from this address at the given offset, with the given layout.
* <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 layout of the memory region to be read.
* @param offset offset in bytes (relative to this address). Might be negative.
* The final address of this read operation can be expressed as {@code toRawLongValue() + offset}.
* @return a boolean value read from this address.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
boolean get(ValueLayout.OfBoolean layout, long offset);
/**
* Writes a boolean into this address at the given offset, with the given layout.
* <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 layout of the memory region to be written.
* @param offset offset in bytes (relative to this address). Might be negative.
* The final address of this write operation can be expressed as {@code toRawLongValue() + offset}.
* @param value the boolean value to be written.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
void set(ValueLayout.OfBoolean layout, long offset, boolean value);
/**
* Reads a char from this address at the given offset, with the given layout.
* <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 layout of the memory region to be read.
* @param offset offset in bytes (relative to this address). Might be negative.
* The final address of this read operation can be expressed as {@code toRawLongValue() + offset}.
* @return a char value read from this address.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
char get(ValueLayout.OfChar layout, long offset);
/**
* Writes a char into this address at the given offset, with the given layout.
* <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 layout of the memory region to be written.
* @param offset offset in bytes (relative to this address). Might be negative.
* The final address of this write operation can be expressed as {@code toRawLongValue() + offset}.
* @param value the char value to be written.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
void set(ValueLayout.OfChar layout, long offset, char value);
/**
* Reads a short from this address at the given offset, with the given layout.
* <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 layout of the memory region to be read.
* @param offset offset in bytes (relative to this address). Might be negative.
* The final address of this read operation can be expressed as {@code toRawLongValue() + offset}.
* @return a short value read from this address.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
short get(ValueLayout.OfShort layout, long offset);
/**
* Writes a short into this address at the given offset, with the given layout.
* <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 layout of the memory region to be written.
* @param offset offset in bytes (relative to this address). Might be negative.
* The final address of this write operation can be expressed as {@code toRawLongValue() + offset}.
* @param value the short value to be written.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
void set(ValueLayout.OfShort layout, long offset, short value);
/**
* Reads an int from this address at the given offset, with the given layout.
* <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 layout of the memory region to be read.
* @param offset offset in bytes (relative to this address). Might be negative.
* The final address of this read operation can be expressed as {@code toRawLongValue() + offset}.
* @return an int value read from this address.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
int get(ValueLayout.OfInt layout, long offset);
/**
* Writes an int into this address at the given offset, with the given layout.
* <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 layout of the memory region to be written.
* @param offset offset in bytes (relative to this address). Might be negative.
* The final address of this write operation can be expressed as {@code toRawLongValue() + offset}.
* @param value the int value to be written.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
void set(ValueLayout.OfInt layout, long offset, int value);
/**
* Reads a float from this address at the given offset, with the given layout.
* <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 layout of the memory region to be read.
* @param offset offset in bytes (relative to this address). Might be negative.
* The final address of this read operation can be expressed as {@code toRawLongValue() + offset}.
* @return a float value read from this address.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
float get(ValueLayout.OfFloat layout, long offset);
/**
* Writes a float into this address at the given offset, with the given layout.
* <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 layout of the memory region to be written.
* @param offset offset in bytes (relative to this address). Might be negative.
* The final address of this write operation can be expressed as {@code toRawLongValue() + offset}.
* @param value the float value to be written.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
void set(ValueLayout.OfFloat layout, long offset, float value);
/**
* Reads a long from this address at the given offset, with the given layout.
* <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 layout of the memory region to be read.
* @param offset offset in bytes (relative to this address). Might be negative.
* The final address of this read operation can be expressed as {@code toRawLongValue() + offset}.
* @return a long value read from this address.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
long get(ValueLayout.OfLong layout, long offset);
/**
* Writes a long into this address at the given offset, with the given layout.
* <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 layout of the memory region to be written.
* @param offset offset in bytes (relative to this address). Might be negative.
* The final address of this write operation can be expressed as {@code toRawLongValue() + offset}.
* @param value the long value to be written.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
void set(ValueLayout.OfLong layout, long offset, long value);
/**
* Reads a double from this address at the given offset, with the given layout.
* <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 layout of the memory region to be read.
* @param offset offset in bytes (relative to this address). Might be negative.
* The final address of this read operation can be expressed as {@code toRawLongValue() + offset}.
* @return a double value read from this address.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
double get(ValueLayout.OfDouble layout, long offset);
/**
* Writes a double into this address at the given offset, with the given layout.
* <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 layout of the memory region to be written.
* @param offset offset in bytes (relative to this address). Might be negative.
* The final address of this write operation can be expressed as {@code toRawLongValue() + offset}.
* @param value the double value to be written.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
void set(ValueLayout.OfDouble layout, long offset, double value);
/**
* Reads an address from this address at the given offset, with the given layout.
* <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 layout of the memory region to be read.
* @param offset offset in bytes (relative to this address). Might be negative.
* The final address of this read operation can be expressed as {@code toRawLongValue() + offset}.
* @return an address value read from this address.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
MemoryAddress get(ValueLayout.OfAddress layout, long offset);
/**
* Writes an address into this address at the given offset, with the given layout.
* <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 layout of the memory region to be written.
* @param offset offset in bytes (relative to this address). Might be negative.
* The final address of this write operation can be expressed as {@code toRawLongValue() + offset}.
* @param value the address value to be written.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
void set(ValueLayout.OfAddress layout, long offset, Addressable value);
/**
* Reads a char from this address at the given index, scaled by the given layout size.
* <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 layout of the memory region to be read.
* @param index index in bytes (relative to this address). Might be negative.
* The final address of this read operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
* @return a char value read from this address.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout,
* or if the layout alignment is greater than its size.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
char getAtIndex(ValueLayout.OfChar layout, long index);
/**
* Writes a char into this address at the given index, scaled by the given layout size.
* <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 layout of the memory region to be written.
* @param index index in bytes (relative to this address). Might be negative.
* The final address of this write operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
* @param value the char value to be written.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout,
* or if the layout alignment is greater than its size.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
void setAtIndex(ValueLayout.OfChar layout, long index, char value);
/**
* Reads a short from this address at the given index, scaled by the given layout size.
* <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 layout of the memory region to be read.
* @param index index in bytes (relative to this address). Might be negative.
* The final address of this read operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
* @return a short value read from this address.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout,
* or if the layout alignment is greater than its size.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
short getAtIndex(ValueLayout.OfShort layout, long index);
/**
* Writes a short into this address at the given index, scaled by the given layout size.
* <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 layout of the memory region to be written.
* @param index index in bytes (relative to this address). Might be negative.
* The final address of this write operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
* @param value the short value to be written.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout,
* or if the layout alignment is greater than its size.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
void setAtIndex(ValueLayout.OfShort layout, long index, short value);
/**
* Reads an int from this address at the given index, scaled by the given layout size.
* <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 layout of the memory region to be read.
* @param index index in bytes (relative to this address). Might be negative.
* The final address of this read operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
* @return an int value read from this address.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout,
* or if the layout alignment is greater than its size.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
int getAtIndex(ValueLayout.OfInt layout, long index);
/**
* Writes an int into this address at the given index, scaled by the given layout size.
* <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 layout of the memory region to be written.
* @param index index in bytes (relative to this address). Might be negative.
* The final address of this write operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
* @param value the int value to be written.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout,
* or if the layout alignment is greater than its size.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
void setAtIndex(ValueLayout.OfInt layout, long index, int value);
/**
* Reads a float from this address at the given index, scaled by the given layout size.
* <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 layout of the memory region to be read.
* @param index index in bytes (relative to this address). Might be negative.
* The final address of this read operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
* @return a float value read from this address.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout,
* or if the layout alignment is greater than its size.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
float getAtIndex(ValueLayout.OfFloat layout, long index);
/**
* Writes a float into this address at the given index, scaled by the given layout size.
* <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 layout of the memory region to be written.
* @param index index in bytes (relative to this address). Might be negative.
* The final address of this write operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
* @param value the float value to be written.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout,
* or if the layout alignment is greater than its size.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
void setAtIndex(ValueLayout.OfFloat layout, long index, float value);
/**
* Reads a long from this address at the given index, scaled by the given layout size.
* <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 layout of the memory region to be read.
* @param index index in bytes (relative to this address). Might be negative.
* The final address of this read operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
* @return a long value read from this address.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout,
* or if the layout alignment is greater than its size.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
long getAtIndex(ValueLayout.OfLong layout, long index);
/**
* Writes a long into this address at the given index, scaled by the given layout size.
* <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 layout of the memory region to be written.
* @param index index in bytes (relative to this address). Might be negative.
* The final address of this write operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
* @param value the long value to be written.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout,
* or if the layout alignment is greater than its size.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
void setAtIndex(ValueLayout.OfLong layout, long index, long value);
/**
* Reads a double from this address at the given index, scaled by the given layout size.
* <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 layout of the memory region to be read.
* @param index index in bytes (relative to this address). Might be negative.
* The final address of this read operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
* @return a double value read from this address.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout,
* or if the layout alignment is greater than its size.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
double getAtIndex(ValueLayout.OfDouble layout, long index);
/**
* Writes a double into this address at the given index, scaled by the given layout size.
* <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 layout of the memory region to be written.
* @param index index in bytes (relative to this address). Might be negative.
* The final address of this write operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
* @param value the double value to be written.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout,
* or if the layout alignment is greater than its size.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
void setAtIndex(ValueLayout.OfDouble layout, long index, double value);
/**
* Reads an address from this address at the given index, scaled by the given layout size.
* <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 layout of the memory region to be read.
* @param index index in bytes (relative to this address). Might be negative.
* The final address of this read operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
* @return an address value read from this address.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout,
* or if the layout alignment is greater than its size.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
MemoryAddress getAtIndex(ValueLayout.OfAddress layout, long index);
/**
* Writes an address into this address at the given index, scaled by the given layout size.
* <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 layout of the memory region to be written.
* @param index index in bytes (relative to this address). Might be negative.
* The final address of this write operation can be expressed as {@code toRawLongValue() + (index * layout.byteSize())}.
* @param value the address value to be written.
* @throws IllegalArgumentException if the dereference operation is
* <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraints</a> in the provided layout,
* or if the layout alignment is greater than its size.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
void setAtIndex(ValueLayout.OfAddress layout, long index, Addressable value);
}

View file

@ -35,15 +35,20 @@ import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.internal.foreign.LayoutPath;
import jdk.internal.foreign.LayoutPath.PathElementImpl.PathKind;
import jdk.internal.foreign.Utils;
import jdk.internal.foreign.layout.MemoryLayoutUtil;
import jdk.internal.foreign.layout.PaddingLayoutImpl;
import jdk.internal.foreign.layout.SequenceLayoutImpl;
import jdk.internal.foreign.layout.StructLayoutImpl;
import jdk.internal.foreign.layout.UnionLayoutImpl;
import jdk.internal.foreign.layout.ValueLayouts;
import jdk.internal.javac.PreviewFeature;
/**
* A memory layout can be used to describe the contents of a memory segment.
* A memory layout describes the contents of a memory segment.
* There are two leaves in the layout hierarchy, <em>value layouts</em>, which are used to represent values of given size and kind (see
* {@link ValueLayout}) and <em>padding layouts</em> which are used, as the name suggests, to represent a portion of a memory
* segment whose contents should be ignored, and which are primarily present for alignment reasons (see {@link MemoryLayout#paddingLayout(long)}).
@ -162,10 +167,11 @@ import jdk.internal.javac.PreviewFeature;
* @implSpec
* Implementations of this interface 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 MemoryLayout permits AbstractLayout, SequenceLayout, GroupLayout, PaddingLayout, ValueLayout {
public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, PaddingLayout, ValueLayout {
/**
* {@return the layout size, in bits}
@ -185,7 +191,7 @@ public sealed interface MemoryLayout permits AbstractLayout, SequenceLayout, Gro
Optional<String> name();
/**
* Returns a memory layout with the same size and alignment constraints as this layout,
* Returns a memory layout of the same type with the same size and alignment constraint as this layout,
* but with the specified name.
*
* @param name the layout name.
@ -235,11 +241,11 @@ public sealed interface MemoryLayout permits AbstractLayout, SequenceLayout, Gro
}
/**
* Returns a memory layout with the same size and name as this layout,
* but with the specified alignment constraints (in bits).
* Returns a memory layout of the same type with the same size and name as this layout,
* but with the specified alignment constraint (in bits).
*
* @param bitAlignment the layout alignment constraint, expressed in bits.
* @return a memory layout with the given alignment constraints.
* @return a memory layout with the given alignment constraint.
* @throws IllegalArgumentException if {@code bitAlignment} is not a power of two, or if it's less than 8.
*/
MemoryLayout withBitAlignment(long bitAlignment);
@ -307,7 +313,7 @@ public sealed interface MemoryLayout permits AbstractLayout, SequenceLayout, Gro
* in {@code elements} is {@code null}.
*/
default long byteOffset(PathElement... elements) {
return Utils.bitsToBytesOrThrow(bitOffset(elements), Utils.bitsToBytesThrowOffset);
return Utils.bitsToBytesOrThrow(bitOffset(elements), Utils.BITS_TO_BYTES_THROW_OFFSET);
}
/**
@ -343,23 +349,24 @@ public sealed interface MemoryLayout permits AbstractLayout, SequenceLayout, Gro
*/
default MethodHandle byteOffsetHandle(PathElement... elements) {
MethodHandle mh = bitOffsetHandle(elements);
mh = MethodHandles.filterReturnValue(mh, Utils.MH_bitsToBytesOrThrowForOffset);
mh = MethodHandles.filterReturnValue(mh, Utils.MH_BITS_TO_BYTES_OR_THROW_FOR_OFFSET);
return mh;
}
/**
* Creates an access var handle that can be used to dereference memory at the layout selected by the given layout path,
* Creates a var handle that can be used to access a memory segment at the layout selected by the given layout path,
* where the path is considered rooted in this layout.
* <p>
* The final memory location accessed by the returned var handle can be computed as follows:
* The final address accessed by the returned var handle can be computed as follows:
*
* <blockquote><pre>{@code
* address = base + offset
* address = base(segment) + offset
* }</pre></blockquote>
*
* where {@code base} denotes the base address expressed by the {@link MemorySegment} access coordinate
* (see {@link MemorySegment#address()} and {@link MemoryAddress#toRawLongValue()}) and {@code offset}
* can be expressed in the following form:
* Where {@code base(segment)} denotes a function that returns the physical base address of the accessed
* memory segment. For native segments, this function just returns the native segment's
* {@linkplain MemorySegment#address() address}. For heap segments, this function is more complex, as the address
* of heap segments is virtualized. The {@code offset} coordinate can be expressed in the following form:
*
* <blockquote><pre>{@code
* offset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
@ -378,8 +385,8 @@ public sealed interface MemoryLayout permits AbstractLayout, SequenceLayout, Gro
* features certain <em>access mode restrictions</em>, which are common to all memory segment view handles.
*
* @param elements the layout path elements.
* @return a var handle which can be used to dereference memory at the (possibly nested) layout selected by the layout path in {@code elements}.
* @throws UnsupportedOperationException if the layout path has one or more elements with incompatible alignment constraints.
* @return a var handle which can be used to access a memory segment at the (possibly nested) layout selected by the layout path in {@code elements}.
* @throws UnsupportedOperationException if the layout path has one or more elements with incompatible alignment constraint.
* @throws IllegalArgumentException if the layout path in {@code elements} does not select a value layout (see {@link ValueLayout}).
* @see MethodHandles#memorySegmentViewVarHandle(ValueLayout)
*/
@ -458,11 +465,6 @@ public sealed interface MemoryLayout permits AbstractLayout, SequenceLayout, Gro
return finalizer.apply(path);
}
/**
* {@return true, if this layout is a padding layout}
*/
boolean isPadding();
/**
* An element in a <a href="MemoryLayout.html#layout-paths"><em>layout path</em></a>. There
* are two kinds of path elements: <em>group path elements</em> and <em>sequence path elements</em>. Group
@ -581,15 +583,15 @@ public sealed interface MemoryLayout permits AbstractLayout, SequenceLayout, Gro
/**
* Compares the specified object with this layout for equality. Returns {@code true} if and only if the specified
* object is also a layout, and it is equal to this layout. Two layouts are considered equal if they are of
* the same kind, have the same size, name and alignment constraints. Furthermore, depending on the layout kind, additional
* the same kind, have the same size, name and alignment constraint. Furthermore, depending on the layout kind, additional
* conditions must be satisfied:
* <ul>
* <li>two value layouts are considered equal if they have the same {@linkplain ValueLayout#order() order},
* and {@linkplain ValueLayout#carrier() carrier}</li>
* <li>two sequence layouts are considered equal if they have the same element count (see {@link SequenceLayout#elementCount()}), and
* if their element layouts (see {@link SequenceLayout#elementLayout()}) are also equal</li>
* <li>two group layouts are considered equal if they are of the same kind (see {@link GroupLayout#isStruct()},
* {@link GroupLayout#isUnion()}) and if their member layouts (see {@link GroupLayout#memberLayouts()}) are also equal</li>
* <li>two group layouts are considered equal if they are of the same type (see {@link StructLayout},
* {@link UnionLayout}) and if their member layouts (see {@link GroupLayout#memberLayouts()}) are also equal</li>
* </ul>
*
* @param other the object to be compared for equality with this layout.
@ -615,9 +617,9 @@ public sealed interface MemoryLayout permits AbstractLayout, SequenceLayout, Gro
* @return the new selector layout.
* @throws IllegalArgumentException if {@code size <= 0}.
*/
static MemoryLayout paddingLayout(long size) {
AbstractLayout.checkSize(size);
return new PaddingLayout(size);
static PaddingLayout paddingLayout(long size) {
MemoryLayoutUtil.checkSize(size);
return PaddingLayoutImpl.of(size);
}
/**
@ -632,7 +634,7 @@ public sealed interface MemoryLayout permits AbstractLayout, SequenceLayout, Gro
* <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 MemoryAddress.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.
@ -643,54 +645,58 @@ public sealed interface MemoryLayout permits AbstractLayout, SequenceLayout, Gro
Objects.requireNonNull(carrier);
Objects.requireNonNull(order);
if (carrier == boolean.class) {
return new ValueLayout.OfBoolean(order);
return ValueLayouts.OfBooleanImpl.of(order);
} else if (carrier == char.class) {
return new ValueLayout.OfChar(order);
return ValueLayouts.OfCharImpl.of(order);
} else if (carrier == byte.class) {
return new ValueLayout.OfByte(order);
return ValueLayouts.OfByteImpl.of(order);
} else if (carrier == short.class) {
return new ValueLayout.OfShort(order);
return ValueLayouts.OfShortImpl.of(order);
} else if (carrier == int.class) {
return new ValueLayout.OfInt(order);
return ValueLayouts.OfIntImpl.of(order);
} else if (carrier == float.class) {
return new ValueLayout.OfFloat(order);
return ValueLayouts.OfFloatImpl.of(order);
} else if (carrier == long.class) {
return new ValueLayout.OfLong(order);
return ValueLayouts.OfLongImpl.of(order);
} else if (carrier == double.class) {
return new ValueLayout.OfDouble(order);
} else if (carrier == MemoryAddress.class) {
return new ValueLayout.OfAddress(order);
return ValueLayouts.OfDoubleImpl.of(order);
} else if (carrier == MemorySegment.class) {
return ValueLayouts.OfAddressImpl.of(order);
} else {
throw new IllegalArgumentException("Unsupported carrier: " + carrier.getName());
}
}
/**
* Creates a sequence layout with the given element layout and element count. If the element count has the
* special value {@code -1}, the element count is inferred to be the biggest possible count such that
* the sequence layout size does not overflow, using the following formula:
* Creates a sequence layout with the given element layout and element count.
*
* <blockquote><pre>{@code
* inferredElementCount = Long.MAX_VALUE / elementLayout.bitSize();
* }</pre></blockquote>
*
* @param elementCount the sequence element count; if set to {@code -1}, the sequence element count is inferred.
* @param elementCount the sequence element count.
* @param elementLayout the sequence element layout.
* @return the new sequence layout with the given element layout and size.
* @throws IllegalArgumentException if {@code elementCount < -1}.
* @throws IllegalArgumentException if {@code elementCount != -1} and the computation {@code elementCount * elementLayout.bitSize()} overflows.
* @throws IllegalArgumentException if {@code elementCount } is negative.
*/
static SequenceLayout sequenceLayout(long elementCount, MemoryLayout elementLayout) {
if (elementCount == -1) {
// inferred element count
long inferredElementCount = Long.MAX_VALUE / elementLayout.bitSize();
return new SequenceLayout(inferredElementCount, elementLayout);
} else {
// explicit element count
AbstractLayout.checkSize(elementCount, true);
return wrapOverflow(() ->
new SequenceLayout(elementCount, Objects.requireNonNull(elementLayout)));
}
MemoryLayoutUtil.checkSize(elementCount, true);
Objects.requireNonNull(elementLayout);
return wrapOverflow(() ->
SequenceLayoutImpl.of(elementCount, elementLayout));
}
/**
* Creates a sequence layout with the given element layout and the maximum element
* count such that it does not overflow a {@code long}.
*
* This is equivalent to the following code:
* {@snippet lang = java:
* sequenceLayout(Long.MAX_VALUE / elementLayout.bitSize(), elementLayout);
* }
*
* @param elementLayout the sequence element layout.
* @return a new sequence layout with the given element layout and maximum element count.
*/
static SequenceLayout sequenceLayout(MemoryLayout elementLayout) {
Objects.requireNonNull(elementLayout);
return sequenceLayout(Long.MAX_VALUE / elementLayout.bitSize(), elementLayout);
}
/**
@ -701,13 +707,12 @@ public sealed interface MemoryLayout permits AbstractLayout, SequenceLayout, Gro
* @throws IllegalArgumentException if the sum of the {@linkplain #bitSize() bit sizes} of the member layouts
* overflows.
*/
static GroupLayout structLayout(MemoryLayout... elements) {
static StructLayout structLayout(MemoryLayout... elements) {
Objects.requireNonNull(elements);
return wrapOverflow(() ->
new GroupLayout(GroupLayout.Kind.STRUCT,
Stream.of(elements)
.map(Objects::requireNonNull)
.collect(Collectors.toList())));
StructLayoutImpl.of(Stream.of(elements)
.map(Objects::requireNonNull)
.toList()));
}
/**
@ -716,12 +721,11 @@ public sealed interface MemoryLayout permits AbstractLayout, SequenceLayout, Gro
* @param elements The member layouts of the union layout.
* @return a union layout with the given member layouts.
*/
static GroupLayout unionLayout(MemoryLayout... elements) {
static UnionLayout unionLayout(MemoryLayout... elements) {
Objects.requireNonNull(elements);
return new GroupLayout(GroupLayout.Kind.UNION,
Stream.of(elements)
.map(Objects::requireNonNull)
.collect(Collectors.toList()));
return UnionLayoutImpl.of(Stream.of(elements)
.map(Objects::requireNonNull)
.toList());
}
private static <L extends MemoryLayout> L wrapOverflow(Supplier<L> layoutSupplier) {

View file

@ -1,285 +0,0 @@
/*
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.foreign;
import java.lang.ref.Cleaner;
import java.util.Objects;
import jdk.internal.foreign.MemorySessionImpl;
import jdk.internal.javac.PreviewFeature;
/**
* A memory session manages the lifecycle of one or more resources. Resources (e.g. {@link MemorySegment}) associated
* with a memory session can only be accessed while the memory session is {@linkplain #isAlive() alive},
* and by the {@linkplain #ownerThread() thread} associated with the memory session (if any).
* <p>
* Memory sessions can be closed. When a memory session is closed, it is no longer {@linkplain #isAlive() alive},
* and subsequent operations on resources associated with that session (e.g. attempting to access a {@link MemorySegment} instance)
* will fail with {@link IllegalStateException}.
* <p>
* A memory session is associated with one or more {@linkplain #addCloseAction(Runnable) close actions}. Close actions
* can be used to specify the cleanup code that must run when a given resource (or set of resources) is no longer in use.
* When a memory session is closed, the {@linkplain #addCloseAction(Runnable) close actions}
* associated with that session are executed (in unspecified order). For instance, closing the memory session associated with
* one or more {@linkplain MemorySegment#allocateNative(long, long, MemorySession) native memory segments} results in releasing
* the off-heap memory associated with said segments.
* <p>
* The {@linkplain #global() global session} is a memory session that cannot be closed.
* As a result, resources associated with the global session are never released. Examples of resources associated with
* the global memory session are {@linkplain MemorySegment#ofArray(int[]) heap segments}.
*
* <h2 id = "thread-confinement">Thread confinement</h2>
*
* Memory sessions can be divided into two categories: <em>thread-confined</em> memory sessions, and <em>shared</em>
* memory sessions.
* <p>
* Confined memory sessions, support strong thread-confinement guarantees. Upon creation,
* they are assigned an {@linkplain #ownerThread() owner thread}, typically the thread which initiated the creation operation.
* After creating a confined memory session, only the owner thread will be allowed to directly manipulate the resources
* associated with this memory session. Any attempt to perform resource access from a thread other than the
* owner thread will fail with {@link WrongThreadException}.
* <p>
* Shared memory sessions, on the other hand, have no owner thread; as such, resources associated with shared memory sessions
* can be accessed by multiple threads. This might be useful when multiple threads need to access the same resource concurrently
* (e.g. in the case of parallel processing).
*
* <h2 id="closeable">Closeable memory sessions</h2>
*
* When a session is associated with off-heap resources, it is often desirable for said resources to be released in a timely fashion,
* rather than waiting for the session to be deemed <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>
* by the garbage collector. In this scenario, a client might consider using a {@linkplain #isCloseable() <em>closeable</em>} memory session.
* Closeable memory sessions are memory sessions that can be {@linkplain MemorySession#close() closed} deterministically, as demonstrated
* in the following example:
*
* {@snippet lang=java :
* try (MemorySession session = MemorySession.openConfined()) {
* MemorySegment segment1 = MemorySegment.allocateNative(100);
* MemorySegment segment1 = MemorySegment.allocateNative(200);
* ...
* } // all memory released here
* }
*
* The above code creates a confined, closeable session. Then it allocates two segments associated with that session.
* When the session is {@linkplain #close() closed} (above, this is done implicitly, using the <em>try-with-resources construct</em>),
* all memory allocated within the session will be released
* <p>
* Closeable memory sessions, while powerful, must be used with caution. Closeable memory sessions must be closed
* when no longer in use, either explicitly (by calling the {@link #close} method), or implicitly (by wrapping the use of
* a closeable memory session in a <em>try-with-resources construct</em>). A failure to do so might result in memory leaks.
* To mitigate this problem, closeable memory sessions can be associated with a {@link Cleaner} instance,
* so that they are also closed automatically, once the session instance becomes <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>.
* This can be useful to allow for predictable, deterministic resource deallocation, while still preventing accidental
* native memory leaks. In case a client closes a memory session managed by a cleaner, no further action will be taken when
* the session becomes unreachable; that is, {@linkplain #addCloseAction(Runnable) close actions} associated with a
* memory session, whether managed or not, are called <em>exactly once</em>.
*
* <h2 id="non-closeable">Non-closeable views</h2>
*
* There are situations in which it might not be desirable for a memory session to be reachable from one or
* more resources associated with it. For instance, an API might create a private memory session, and allocate
* a memory segment, and then expose one or more slices of this segment to its clients. Since the API's memory session
* would be reachable from the slices (using the {@link MemorySegment#session()} accessor), it might be possible for
* clients to compromise the API (e.g. by closing the session prematurely). To avoid leaking private memory sessions
* to untrusted clients, an API can instead return segments based on a non-closeable view of the session it created, as follows:
*
* {@snippet lang=java :
* MemorySession session = MemorySession.openConfined();
* MemorySession nonCloseableSession = session.asNonCloseable();
* MemorySegment segment = MemorySegment.allocateNative(100, nonCloseableSession);
* segment.session().close(); // throws
* session.close(); //ok
* }
*
* In other words, only the owner of the original {@code session} object can close the session. External clients can only
* access the non-closeable session, and have no access to the underlying API session.
*
* @implSpec
* Implementations of this interface are thread-safe.
*
* @see MemorySegment
* @see SymbolLookup
* @see Linker
* @see VaList
*
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public sealed interface MemorySession extends AutoCloseable, SegmentAllocator permits MemorySessionImpl, MemorySessionImpl.NonCloseableView {
/**
* {@return {@code true}, if this memory session is alive}
*/
boolean isAlive();
/**
* {@return {@code true}, if this session is a closeable memory session}.
*/
boolean isCloseable();
/**
* {@return the owner thread associated with this memory session, or {@code null} if this session is shared
* across multiple threads}
*/
Thread ownerThread();
/**
* Runs a critical action while this memory session is kept alive.
* @param action the action to be run.
*/
void whileAlive(Runnable action);
/**
* Adds a custom cleanup action which will be executed when the memory session is closed.
* The order in which custom cleanup actions are invoked once the memory session is closed is unspecified.
* @apiNote The provided action should not keep a strong reference to this memory session, so that implicitly
* closed sessions can be handled correctly by a {@link Cleaner} instance.
* @param runnable the custom cleanup action to be associated with this memory session.
* @throws IllegalStateException if this memory session is not {@linkplain #isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread
* {@linkplain #ownerThread() owning} this memory session.
*/
void addCloseAction(Runnable runnable);
/**
* Closes this memory session. If this operation completes without exceptions, this session
* will be marked as <em>not alive</em>, the {@linkplain #addCloseAction(Runnable) close actions} associated
* with this session will be executed, and all the resources associated with this session will be released.
*
* @apiNote This operation is not idempotent; that is, closing an already closed memory session <em>always</em> results in an
* exception being thrown. This reflects a deliberate design choice: memory session state transitions should be
* manifest in the client code; a failure in any of these transitions reveals a bug in the underlying application
* logic.
*
* @see MemorySession#isAlive()
*
* @throws IllegalStateException if this memory session is not {@linkplain #isAlive() alive}.
* @throws IllegalStateException if this session is {@linkplain #whileAlive(Runnable) kept alive} by another client.
* @throws WrongThreadException if this method is called from a thread other than the thread
* {@linkplain #ownerThread() owning} this memory session.
* @throws UnsupportedOperationException if this memory session is not {@linkplain #isCloseable() closeable}.
*/
void close();
/**
* Returns a non-closeable view of this memory session. If this session is {@linkplain #isCloseable() non-closeable},
* this session is returned. Otherwise, this method returns a non-closeable view of this memory session.
* @apiNote a non-closeable view of a memory session {@code S} keeps {@code S} reachable. As such, {@code S}
* cannot be closed implicitly (e.g. by a {@link Cleaner}) as long as one or more non-closeable views of {@code S}
* are reachable.
* @return a non-closeable view of this memory session.
*/
MemorySession asNonCloseable();
/**
* Compares the specified object with this memory session for equality. Returns {@code true} if and only if the specified
* object is also a memory session, and it refers to the same memory session as this memory session.
* {@linkplain #asNonCloseable() A non-closeable view} {@code V} of a memory session {@code S} is considered
* equal to {@code S}.
*
* @param that the object to be compared for equality with this memory session.
* @return {@code true} if the specified object is equal to this memory session.
*/
@Override
boolean equals(Object that);
/**
* {@return the hash code value for this memory session}
*/
@Override
int hashCode();
/**
* Allocates a native segment, using this session. Equivalent to the following code:
* {@snippet lang=java :
* MemorySegment.allocateNative(size, align, this);
* }
*
* @throws IllegalStateException if this memory session is not {@linkplain #isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread
* {@linkplain #ownerThread() owning} this memory session.
* @return a new native segment, associated with this session.
*/
@Override
default MemorySegment allocate(long bytesSize, long bytesAlignment) {
return MemorySegment.allocateNative(bytesSize, bytesAlignment, this);
}
/**
* Creates a closeable confined memory session.
* @return a new closeable confined memory session.
*/
static MemorySession openConfined() {
return MemorySessionImpl.createConfined(Thread.currentThread(), null);
}
/**
* Creates a closeable confined memory session, managed by the provided cleaner instance.
* @param cleaner the cleaner to be associated with the returned memory session.
* @return a new closeable confined memory session, managed by {@code cleaner}.
*/
static MemorySession openConfined(Cleaner cleaner) {
Objects.requireNonNull(cleaner);
return MemorySessionImpl.createConfined(Thread.currentThread(), cleaner);
}
/**
* Creates a closeable shared memory session.
* @return a new closeable shared memory session.
*/
static MemorySession openShared() {
return MemorySessionImpl.createShared(null);
}
/**
* Creates a closeable shared memory session, managed by the provided cleaner instance.
* @param cleaner the cleaner to be associated with the returned memory session.
* @return a new closeable shared memory session, managed by {@code cleaner}.
*/
static MemorySession openShared(Cleaner cleaner) {
Objects.requireNonNull(cleaner);
return MemorySessionImpl.createShared(cleaner);
}
/**
* Creates a non-closeable shared memory session, managed by a private {@link Cleaner} instance.
* Equivalent to (but likely more efficient than) the following code:
* {@snippet lang=java :
* openShared(Cleaner.create()).asNonCloseable();
* }
* @return a non-closeable shared memory session, managed by a private {@link Cleaner} instance.
*/
static MemorySession openImplicit() {
return MemorySessionImpl.createImplicit();
}
/**
* Returns the global memory session.
* @return the global memory session.
*/
static MemorySession global() {
return MemorySessionImpl.GLOBAL;
}
}

View file

@ -25,76 +25,30 @@
*/
package java.lang.foreign;
import java.util.Objects;
import java.util.Optional;
import jdk.internal.foreign.layout.PaddingLayoutImpl;
import jdk.internal.javac.PreviewFeature;
/**
* A padding layout. A padding layout specifies the size of extra space which is typically not accessed by applications,
* and is typically used for aligning member layouts around word boundaries.
*
* @implSpec
* This class is immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
* Implementing classes are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
*
* @since 20
*/
/* package-private */ final class PaddingLayout extends AbstractLayout implements MemoryLayout {
PaddingLayout(long size) {
this(size, 1, Optional.empty());
}
PaddingLayout(long size, long alignment, Optional<String> name) {
super(size, alignment, name);
}
@Override
public String toString() {
return decorateLayoutString("x" + bitSize());
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!super.equals(other)) {
return false;
}
if (!(other instanceof PaddingLayout p)) {
return false;
}
return bitSize() == p.bitSize();
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), bitSize());
}
@Override
PaddingLayout dup(long alignment, Optional<String> name) {
return new PaddingLayout(bitSize(), alignment, name);
}
@Override
public boolean hasNaturalAlignment() {
return true;
}
//hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
//but that causes issues with javadoc, see JDK-8224052
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public sealed interface PaddingLayout extends MemoryLayout permits PaddingLayoutImpl {
/**
* {@inheritDoc}
*/
@Override
public PaddingLayout withName(String name) {
return (PaddingLayout)super.withName(name);
}
PaddingLayout withName(String name);
/**
* {@inheritDoc}
*/
@Override
public PaddingLayout withBitAlignment(long alignmentBits) {
return (PaddingLayout)super.withBitAlignment(alignmentBits);
}
PaddingLayout withBitAlignment(long bitAlignment);
}

View file

@ -32,7 +32,8 @@ import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.function.Function;
import jdk.internal.foreign.AbstractMemorySegmentImpl;
import jdk.internal.foreign.ArenaAllocator;
import jdk.internal.foreign.MemorySessionImpl;
import jdk.internal.foreign.SlicingAllocator;
import jdk.internal.foreign.Utils;
import jdk.internal.javac.PreviewFeature;
@ -45,17 +46,17 @@ import jdk.internal.javac.PreviewFeature;
* <p>
* This interface also defines factories for commonly used allocators:
* <ul>
* <li>{@link #newNativeArena(MemorySession)} creates a more efficient arena-style allocator, where off-heap memory
* is allocated in bigger blocks, which are then sliced accordingly to fit allocation requests;</li>
* <li>{@link #implicitAllocator()} obtains an allocator which allocates native memory segment in independent,
* {@linkplain MemorySession#openImplicit() implicit memory sessions}; and</li>
* <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)
* and recycles its content upon each new allocation request.</li>
* </ul>
* <p>
* Passing a segment allocator to an API can be especially useful in circumstances where a client wants to communicate <em>where</em>
* the results of a certain operation (performed by the API) should be stored, as a memory segment. For instance,
* {@linkplain Linker#downcallHandle(FunctionDescriptor) downcall method handles} can accept an additional
* {@linkplain Linker#downcallHandle(FunctionDescriptor, Linker.Option...) downcall method handles} can accept an additional
* {@link SegmentAllocator} parameter if the underlying foreign function is known to return a struct by-value. Effectively,
* the allocator parameter tells the linker runtime where to store the return value of the foreign function.
*/
@ -80,7 +81,7 @@ public interface SegmentAllocator {
* @implSpec the default implementation for this method copies the contents of the provided Java string
* into a new memory segment obtained by calling {@code this.allocate(str.length() + 1)}.
* @param str the Java string to be converted into a C string.
* @return a new native memory segment containing the converted C string.
* @return a new native segment containing the converted C string.
*/
default MemorySegment allocateUtf8String(String str) {
Objects.requireNonNull(str);
@ -200,11 +201,11 @@ 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, Addressable value) {
default MemorySegment allocate(ValueLayout.OfAddress layout, MemorySegment value) {
Objects.requireNonNull(value);
Objects.requireNonNull(layout);
MemorySegment segment = allocate(layout);
layout.varHandle().set(segment, value.address());
layout.varHandle().set(segment, value);
return segment;
}
@ -287,10 +288,8 @@ public interface SegmentAllocator {
private <Z> MemorySegment copyArrayWithSwapIfNeeded(Z array, ValueLayout elementLayout,
Function<Z, MemorySegment> heapSegmentFactory) {
Objects.requireNonNull(array);
Objects.requireNonNull(elementLayout);
int size = Array.getLength(array);
MemorySegment addr = allocateArray(elementLayout, size);
int size = Array.getLength(Objects.requireNonNull(array));
MemorySegment addr = allocateArray(Objects.requireNonNull(elementLayout), size);
if (size > 0) {
MemorySegment.copy(heapSegmentFactory.apply(array), elementLayout, 0,
addr, elementLayout.withOrder(ByteOrder.nativeOrder()), 0, size);
@ -327,105 +326,39 @@ public interface SegmentAllocator {
/**
* Allocates a memory segment with the given size.
* @implSpec the default implementation for this method calls {@code this.allocate(bytesSize, 1)}.
* @param bytesSize the size (in bytes) of the block of memory to be allocated.
* @implSpec the default implementation for this method calls {@code this.allocate(byteSize, 1)}.
* @param byteSize the size (in bytes) of the block of memory to be allocated.
* @return a segment for the newly allocated memory block.
* @throws IllegalArgumentException if {@code bytesSize < 0}
* @throws IllegalArgumentException if {@code byteSize < 0}
*/
default MemorySegment allocate(long bytesSize) {
return allocate(bytesSize, 1);
default MemorySegment allocate(long byteSize) {
return allocate(byteSize, 1);
}
/**
* Allocates a memory segment with the given size and alignment constraints.
* @param bytesSize the size (in bytes) of the block of memory to be allocated.
* @param bytesAlignment the alignment (in bytes) of the block of memory to be allocated.
* Allocates a memory segment with the given size and alignment constraint.
* @param byteSize the size (in bytes) of the block of memory to be allocated.
* @param byteAlignment the alignment (in bytes) of the block of memory to be allocated.
* @return a segment for the newly allocated memory block.
* @throws IllegalArgumentException if {@code bytesSize < 0}, {@code alignmentBytes <= 0},
* @throws IllegalArgumentException if {@code byteSize < 0}, {@code byteAlignment <= 0},
* or if {@code alignmentBytes} is not a power of 2.
*/
MemorySegment allocate(long bytesSize, long bytesAlignment);
MemorySegment allocate(long byteSize, long byteAlignment);
/**
* Creates an unbounded arena-based allocator used to allocate native memory segments.
* The returned allocator features a predefined block size and maximum arena size, and the segments it allocates
* are associated with the provided memory session. Equivalent to the following code:
* {@snippet lang=java :
* SegmentAllocator.newNativeArena(Long.MAX_VALUE, predefinedBlockSize, session);
* }
*
* @param session the memory session associated with the segments allocated by the arena-based allocator.
* @return a new unbounded arena-based allocator
* @throws IllegalStateException if {@code session} is not {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread
* {@linkplain MemorySession#ownerThread() owning} {@code session}.
*/
static SegmentAllocator newNativeArena(MemorySession session) {
return newNativeArena(Long.MAX_VALUE, ArenaAllocator.DEFAULT_BLOCK_SIZE, session);
}
/**
* Creates an arena-based allocator used to allocate native memory segments.
* The returned allocator features a block size set to the specified arena size, and the native segments
* it allocates are associated with the provided memory session. Equivalent to the following code:
* {@snippet lang=java :
* SegmentAllocator.newNativeArena(arenaSize, arenaSize, session);
* }
*
* @param arenaSize the size (in bytes) of the allocation arena.
* @param session the memory session associated with the segments allocated by the arena-based allocator.
* @return a new unbounded arena-based allocator
* @throws IllegalArgumentException if {@code arenaSize <= 0}.
* @throws IllegalStateException if {@code session} is not {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread
* {@linkplain MemorySession#ownerThread() owning} {@code session}.
*/
static SegmentAllocator newNativeArena(long arenaSize, MemorySession session) {
return newNativeArena(arenaSize, arenaSize, session);
}
/**
* Creates an arena-based allocator used to allocate native memory segments. The returned allocator features
* the given block size {@code B} and the given arena size {@code A}, and the native segments
* it allocates are associated with the provided memory session.
* Returns a segment allocator which responds to allocation requests by returning consecutive slices
* obtained from the provided segment. Each new allocation request will return a new slice starting at the
* current offset (modulo additional padding to satisfy alignment constraint), with given size.
* <p>
* The allocator arena is first initialized by {@linkplain MemorySegment#allocateNative(long, MemorySession) allocating} a
* native memory segment {@code S} of size {@code B}. The allocator then responds to allocation requests in one of the following ways:
* <ul>
* <li>if the size of the allocation requests is smaller than the size of {@code S}, and {@code S} has a <em>free</em>
* slice {@code S'} which fits that allocation request, return that {@code S'}.
* <li>if the size of the allocation requests is smaller than the size of {@code S}, and {@code S} has no <em>free</em>
* slices which fits that allocation request, allocate a new segment {@code S'}, with size {@code B},
* and set {@code S = S'}; the allocator then tries to respond to the same allocation request again.
* <li>if the size of the allocation requests is bigger than the size of {@code S}, allocate a new segment {@code S'},
* which has a sufficient size to satisfy the allocation request, and return {@code S'}.
* </ul>
* <p>
* This segment allocator can be useful when clients want to perform multiple allocation requests while avoiding the
* cost associated with allocating a new off-heap memory region upon each allocation request.
* <p>
* The returned allocator might throw an {@link OutOfMemoryError} if the total memory allocated with this allocator
* exceeds the arena size {@code A}, or the system capacity. Furthermore, the returned allocator is not thread safe.
* Concurrent allocation needs to be guarded with synchronization primitives.
* When the returned allocator cannot satisfy an allocation request, e.g. because a slice of the provided
* segment with the requested size cannot be found, an {@link IndexOutOfBoundsException} is thrown.
*
* @param arenaSize the size (in bytes) of the allocation arena.
* @param blockSize the block size associated with the arena-based allocator.
* @param session the memory session associated with the segments returned by the arena-based allocator.
* @return a new unbounded arena-based allocator
* @throws IllegalArgumentException if {@code blockSize <= 0}, if {@code arenaSize <= 0} or if {@code arenaSize < blockSize}.
* @throws IllegalStateException if {@code session} is not {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread
* {@linkplain MemorySession#ownerThread() owning} {@code session}.
* @param segment the segment which the returned allocator should slice from.
* @return a new slicing allocator
*/
static SegmentAllocator newNativeArena(long arenaSize, long blockSize, MemorySession session) {
Objects.requireNonNull(session);
if (blockSize <= 0) {
throw new IllegalArgumentException("Invalid block size: " + blockSize);
}
if (arenaSize <= 0 || arenaSize < blockSize) {
throw new IllegalArgumentException("Invalid arena size: " + arenaSize);
}
return new ArenaAllocator(blockSize, arenaSize, session);
static SegmentAllocator slicingAllocator(MemorySegment segment) {
Objects.requireNonNull(segment);
return new SlicingAllocator(segment);
}
/**
@ -449,24 +382,33 @@ public interface SegmentAllocator {
* @return an allocator which recycles an existing segment upon each new allocation request.
*/
static SegmentAllocator prefixAllocator(MemorySegment segment) {
Objects.requireNonNull(segment);
return (AbstractMemorySegmentImpl)segment;
return (AbstractMemorySegmentImpl)Objects.requireNonNull(segment);
}
/**
* Returns an allocator which allocates native segments in independent {@linkplain MemorySession#openImplicit() implicit memory sessions}.
* Equivalent to (but likely more efficient than) the following code:
* {@snippet lang=java :
* SegmentAllocator implicitAllocator = (size, align) -> MemorySegment.allocateNative(size, align, MemorySession.openImplicit());
* Simple allocator used to allocate native segments. The returned allocator responds to an allocation request by
* returning a native segment backed by a fresh off-heap region of memory, with given byte size and alignment constraint.
* <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);
* }
*
* @return an allocator which allocates native segments in independent {@linkplain MemorySession#openImplicit() implicit memory sessions}.
* @param scope the scope associated with the segments returned by the native allocator.
* @return a simple allocator used to allocate native segments.
*/
static SegmentAllocator implicitAllocator() {
class Holder {
static final SegmentAllocator IMPLICIT_ALLOCATOR = (size, align) ->
MemorySegment.allocateNative(size, align, MemorySession.openImplicit());
}
return Holder.IMPLICIT_ALLOCATOR;
static SegmentAllocator nativeAllocator(SegmentScope scope) {
Objects.requireNonNull(scope);
return (MemorySessionImpl)scope;
}
}

View file

@ -0,0 +1,132 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package java.lang.foreign;
import jdk.internal.foreign.MemorySessionImpl;
import jdk.internal.javac.PreviewFeature;
import jdk.internal.ref.CleanerFactory;
/**
* A segment scope controls access to memory segments.
* <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

@ -25,9 +25,7 @@
*/
package java.lang.foreign;
import java.util.Objects;
import java.util.Optional;
import jdk.internal.foreign.layout.SequenceLayoutImpl;
import jdk.internal.javac.PreviewFeature;
/**
@ -55,46 +53,27 @@ import jdk.internal.javac.PreviewFeature;
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public final class SequenceLayout extends AbstractLayout implements MemoryLayout {
public sealed interface SequenceLayout extends MemoryLayout permits SequenceLayoutImpl {
private final long elemCount;
private final MemoryLayout elementLayout;
SequenceLayout(long elemCount, MemoryLayout elementLayout) {
this(elemCount, elementLayout, elementLayout.bitAlignment(), Optional.empty());
}
SequenceLayout(long elemCount, MemoryLayout elementLayout, long alignment, Optional<String> name) {
super(Math.multiplyExact(elemCount, elementLayout.bitSize()), alignment, name);
this.elemCount = elemCount;
this.elementLayout = elementLayout;
}
/**
* {@return the element layout associated with this sequence layout}
*/
public MemoryLayout elementLayout() {
return elementLayout;
}
MemoryLayout elementLayout();
/**
* {@return the element count of this sequence layout}
*/
public long elementCount() {
return elemCount;
}
long elementCount();
/**
* Returns a sequence layout with the same element layout, alignment constraints and name as this sequence layout,
* Returns a sequence layout with the same element layout, alignment constraint and name as this sequence layout,
* but with the specified element count.
* @param elementCount the new element count.
* @return a sequence layout with the given element count.
* @throws IllegalArgumentException if {@code elementCount < 0}.
*/
public SequenceLayout withElementCount(long elementCount) {
AbstractLayout.checkSize(elementCount, true);
return new SequenceLayout(elementCount, elementLayout, alignment, name());
}
SequenceLayout withElementCount(long elementCount);
/**
* Re-arrange the elements in this sequence layout into a multi-dimensional sequence layout.
@ -129,47 +108,7 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
* multiplying the element counts does not yield the same element count as the flattened projection of this
* sequence layout.
*/
public SequenceLayout reshape(long... elementCounts) {
Objects.requireNonNull(elementCounts);
if (elementCounts.length == 0) {
throw new IllegalArgumentException();
}
SequenceLayout flat = flatten();
long expectedCount = flat.elementCount();
long actualCount = 1;
int inferPosition = -1;
for (int i = 0 ; i < elementCounts.length ; i++) {
if (elementCounts[i] == -1) {
if (inferPosition == -1) {
inferPosition = i;
} else {
throw new IllegalArgumentException("Too many unspecified element counts");
}
} else if (elementCounts[i] <= 0) {
throw new IllegalArgumentException("Invalid element count: " + elementCounts[i]);
} else {
actualCount = elementCounts[i] * actualCount;
}
}
// infer an unspecified element count (if any)
if (inferPosition != -1) {
long inferredCount = expectedCount / actualCount;
elementCounts[inferPosition] = inferredCount;
actualCount = actualCount * inferredCount;
}
if (actualCount != expectedCount) {
throw new IllegalArgumentException("Element counts do not match expected size: " + expectedCount);
}
MemoryLayout res = flat.elementLayout();
for (int i = elementCounts.length - 1 ; i >= 0 ; i--) {
res = MemoryLayout.sequenceLayout(elementCounts[i], res);
}
return (SequenceLayout)res;
}
SequenceLayout reshape(long... elementCounts);
/**
* Returns a flattened sequence layout. The element layout of the returned sequence layout
@ -187,66 +126,11 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
* @return a sequence layout with the same size as this layout (but, possibly, with different
* element count), whose element layout is not a sequence layout.
*/
public SequenceLayout flatten() {
long count = elementCount();
MemoryLayout elemLayout = elementLayout();
while (elemLayout instanceof SequenceLayout elemSeq) {
count = count * elemSeq.elementCount();
elemLayout = elemSeq.elementLayout();
}
return MemoryLayout.sequenceLayout(count, elemLayout);
}
SequenceLayout flatten();
@Override
public String toString() {
return decorateLayoutString(String.format("[%s:%s]",
elemCount, elementLayout));
}
SequenceLayout withName(String name);
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!super.equals(other)) {
return false;
}
return other instanceof SequenceLayout otherSeq &&
elemCount == otherSeq.elemCount &&
elementLayout.equals(otherSeq.elementLayout);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), elemCount, elementLayout);
}
@Override
SequenceLayout dup(long alignment, Optional<String> name) {
return new SequenceLayout(elementCount(), elementLayout, alignment, name);
}
@Override
boolean hasNaturalAlignment() {
return alignment == elementLayout.bitAlignment();
}
//hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
//but that causes issues with javadoc, see JDK-8224052
/**
* {@inheritDoc}
*/
@Override
public SequenceLayout withName(String name) {
return (SequenceLayout)super.withName(name);
}
/**
* {@inheritDoc}
*/
@Override
public SequenceLayout withBitAlignment(long alignmentBits) {
return (SequenceLayout)super.withBitAlignment(alignmentBits);
}
SequenceLayout withBitAlignment(long bitAlignment);
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package java.lang.foreign;
import jdk.internal.foreign.layout.StructLayoutImpl;
import jdk.internal.javac.PreviewFeature;
/**
* A group layout whose member layouts are laid out one after the other.
*
* @implSpec
* Implementing classes are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
*
* @since 20
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public sealed interface StructLayout extends GroupLayout permits StructLayoutImpl {
@Override
StructLayout withName(String name);
@Override
StructLayout withBitAlignment(long bitAlignment);
}

View file

@ -42,35 +42,35 @@ import java.util.Optional;
import java.util.function.BiFunction;
/**
* A <em>symbol lookup</em> is an object that may be used to retrieve the address of a symbol in one or more libraries.
* A <em>symbol lookup</em> retrieves the address of a symbol in one or more libraries.
* A symbol is a named entity, such as a function or a global variable.
* <p>
* A symbol lookup is created with respect to a particular library (or libraries). Subsequently, the {@link SymbolLookup#lookup(String)}
* A symbol lookup is created with respect to a particular library (or libraries). Subsequently, the {@link SymbolLookup#find(String)}
* method takes the name of a symbol and returns the address of the symbol in that library.
* <p>
* The address of a symbol is modelled as a zero-length {@linkplain MemorySegment memory segment}. The segment can be used in different ways:
* <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 base address.</li>
* <li>It can be passed to an existing {@linkplain Linker#downcallHandle(FunctionDescriptor) downcall method handle}, as an argument to the underlying foreign function.</li>
* <li>It can be {@linkplain MemorySegment#set(ValueLayout.OfAddress, long, Addressable) stored} inside another memory segment.</li>
* <li>It can be used to dereference memory associated with a global variable (this might require
* {@link MemorySegment#ofAddress(MemoryAddress, long, MemorySession) resizing} the segment first).</li>
* <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>
* </ul>
*
* <h2 id="obtaining">Obtaining a symbol lookup</h2>
*
* The factory methods {@link #libraryLookup(String, MemorySession)} and {@link #libraryLookup(Path, MemorySession)}
* The factory methods {@link #libraryLookup(String, SegmentScope)} and {@link #libraryLookup(Path, SegmentScope)}
* create a symbol lookup for a library known to the operating system. The library is specified by either its name or a path.
* The library is loaded if not already loaded. The symbol lookup, which is known as a <em>library lookup</em>, is associated
* with a {@linkplain MemorySession memory session}; when the session is {@linkplain MemorySession#close() closed}, the library is unloaded:
* with a {@linkplain SegmentScope scope}; when the scope becomes not {@link SegmentScope#isAlive()}, the library is unloaded:
*
* {@snippet lang=java :
* try (MemorySession session = MemorySession.openConfined()) {
* SymbolLookup libGL = SymbolLookup.libraryLookup("libGL.so"); // libGL.so loaded here
* MemorySegment glGetString = libGL.lookup("glGetString").orElseThrow();
* {@snippet lang = java:
* try (Arena arena = Arena.openConfined()) {
* SymbolLookup libGL = SymbolLookup.libraryLookup("libGL.so", arena.scope()); // libGL.so loaded here
* MemorySegment glGetString = libGL.find("glGetString").orElseThrow();
* ...
* } // libGL.so unloaded here
* }
*}
* <p>
* If a library was previously loaded through JNI, i.e., by {@link System#load(String)}
* or {@link System#loadLibrary(String)}, then the library was also associated with a particular class loader. The factory
@ -80,7 +80,7 @@ import java.util.function.BiFunction;
* System.loadLibrary("GL"); // libGL.so loaded here
* ...
* SymbolLookup libGL = SymbolLookup.loaderLookup();
* MemorySegment glGetString = libGL.lookup("glGetString").orElseThrow();
* MemorySegment glGetString = libGL.find("glGetString").orElseThrow();
* }
*
* This symbol lookup, which is known as a <em>loader lookup</em>, is dynamic with respect to the libraries associated
@ -91,18 +91,18 @@ import java.util.function.BiFunction;
* by {@link System#load(String)} or {@link System#loadLibrary(String)}. A loader lookup does not expose symbols in libraries
* that were loaded in the course of creating a library lookup:
*
* {@snippet lang=java :
* libraryLookup("libGL.so", session).lookup("glGetString").isPresent(); // true
* loaderLookup().lookup("glGetString").isPresent(); // false
* }
* {@snippet lang = java:
* libraryLookup("libGL.so", scope).find("glGetString").isPresent(); // true
* loaderLookup().find("glGetString").isPresent(); // false
*}
*
* Note also that a library lookup for library {@code L} exposes symbols in {@code L} even if {@code L} was previously loaded
* through JNI (the association with a class loader is immaterial to the library lookup):
*
* {@snippet lang=java :
* {@snippet lang = java:
* System.loadLibrary("GL"); // libGL.so loaded here
* libraryLookup("libGL.so", session).lookup("glGetString").isPresent(); // true
* }
* libraryLookup("libGL.so", scope).find("glGetString").isPresent(); // true
*}
*
* <p>
* Finally, each {@link Linker} provides a symbol lookup for libraries that are commonly used on the OS and processor
@ -110,11 +110,11 @@ import java.util.function.BiFunction;
* helps clients to quickly find addresses of well-known symbols. For example, a {@link Linker} for Linux/x64 might choose to
* expose symbols in {@code libc} through the default lookup:
*
* {@snippet lang=java :
* {@snippet lang = java:
* Linker nativeLinker = Linker.nativeLinker();
* SymbolLookup stdlib = nativeLinker.defaultLookup();
* MemorySegment malloc = stdlib.lookup("malloc").orElseThrow();
* }
* MemorySegment malloc = stdlib.find("malloc").orElseThrow();
*}
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
@FunctionalInterface
@ -123,9 +123,9 @@ public interface SymbolLookup {
/**
* Returns the address of the symbol with the given name.
* @param name the symbol name.
* @return a zero-length memory segment whose base address indicates the address of the symbol, if found.
* @return a zero-length memory segment whose address indicates the address of the symbol, if found.
*/
Optional<MemorySegment> lookup(String name);
Optional<MemorySegment> find(String name);
/**
* Returns a symbol lookup for symbols in the libraries associated with the caller's class loader.
@ -139,8 +139,8 @@ public interface SymbolLookup {
* <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 {@linkplain MemorySession#asNonCloseable() non-closeable}, shared memory
* session which keeps the caller's class loader reachable. Therefore, libraries associated with the caller's class
* returned by this method is backed by a scope that is always alive and which keeps the caller's
* class loader reachable. Therefore, libraries associated with the caller's class
* loader are kept loaded (and their symbols available) as long as a loader lookup for that class loader is reachable.
* <p>
* In cases where this method is called from a context where there is no caller frame on the stack
@ -158,24 +158,24 @@ public interface SymbolLookup {
ClassLoader loader = caller != null ?
caller.getClassLoader() :
ClassLoader.getSystemClassLoader();
MemorySession loaderSession = (loader == null || loader instanceof BuiltinClassLoader) ?
MemorySession.global() : // builtin loaders never go away
SegmentScope loaderScope = (loader == null || loader instanceof BuiltinClassLoader) ?
SegmentScope.global() : // builtin loaders never go away
MemorySessionImpl.heapSession(loader);
return name -> {
Objects.requireNonNull(name);
JavaLangAccess javaLangAccess = SharedSecrets.getJavaLangAccess();
// note: ClassLoader::findNative supports a null loader
MemoryAddress addr = MemoryAddress.ofLong(javaLangAccess.findNative(loader, name));
return addr == MemoryAddress.NULL ?
long addr = javaLangAccess.findNative(loader, name);
return addr == 0L ?
Optional.empty() :
Optional.of(MemorySegment.ofAddress(addr, 0L, loaderSession));
Optional.of(MemorySegment.ofAddress(addr, 0L, loaderScope));
};
}
/**
* Loads a library with the given name (if not already loaded) and creates a symbol lookup for symbols in that library.
* The library will be unloaded when the provided memory session is {@linkplain MemorySession#close() closed},
* if no other library lookup is still using it.
* The library will be unloaded when the provided scope becomes
* not {@linkplain SegmentScope#isAlive() alive}, if no other library lookup is still using it.
* @implNote The process of resolving a library name is OS-specific. For instance, in a POSIX-compliant OS,
* the library name is resolved according to the specification of the {@code dlopen} function for that OS.
* In Windows, the library name is resolved according to the specification of the {@code LoadLibrary} function.
@ -186,7 +186,7 @@ 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 session the memory session which controls the library lifecycle.
* @param scope the scope associated with symbols obtained from the returned lookup.
* @return a new symbol lookup suitable to find symbols in a library with the given name.
* @throws IllegalArgumentException if {@code name} does not identify a valid library.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
@ -194,15 +194,15 @@ public interface SymbolLookup {
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
static SymbolLookup libraryLookup(String name, MemorySession session) {
static SymbolLookup libraryLookup(String name, SegmentScope scope) {
Reflection.ensureNativeAccess(Reflection.getCallerClass(), SymbolLookup.class, "libraryLookup");
return libraryLookup(name, RawNativeLibraries::load, session);
return libraryLookup(name, RawNativeLibraries::load, scope);
}
/**
* Loads a library from the given path (if not already loaded) and creates a symbol lookup for symbols
* in that library. The library will be unloaded when the provided memory session is {@linkplain MemorySession#close() closed},
* if no other library lookup is still using it.
* in that library. The library will be unloaded when the provided scope becomes
* not {@linkplain SegmentScope#isAlive() alive}, if no other library lookup is still using it.
* <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
@ -212,7 +212,7 @@ 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 session the memory session which controls the library lifecycle.
* @param scope the scope associated with symbols obtained from the returned lookup.
* @return a new symbol lookup suitable to find symbols in a library with the given path.
* @throws IllegalArgumentException if {@code path} does not point to a valid library.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
@ -220,22 +220,22 @@ public interface SymbolLookup {
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
static SymbolLookup libraryLookup(Path path, MemorySession session) {
static SymbolLookup libraryLookup(Path path, SegmentScope scope) {
Reflection.ensureNativeAccess(Reflection.getCallerClass(), SymbolLookup.class, "libraryLookup");
return libraryLookup(path, RawNativeLibraries::load, session);
return libraryLookup(path, RawNativeLibraries::load, scope);
}
private static <Z> SymbolLookup libraryLookup(Z libDesc, BiFunction<RawNativeLibraries, Z, NativeLibrary> loadLibraryFunc, MemorySession session) {
private static <Z> SymbolLookup libraryLookup(Z libDesc, BiFunction<RawNativeLibraries, Z, NativeLibrary> loadLibraryFunc, SegmentScope libScope) {
Objects.requireNonNull(libDesc);
Objects.requireNonNull(session);
Objects.requireNonNull(libScope);
// attempt to load native library from path or name
RawNativeLibraries nativeLibraries = RawNativeLibraries.newInstance(MethodHandles.lookup());
NativeLibrary library = loadLibraryFunc.apply(nativeLibraries, libDesc);
if (library == null) {
throw new IllegalArgumentException("Cannot open library: " + libDesc);
}
// register hook to unload library when session is closed
MemorySessionImpl.toSessionImpl(session).addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() {
// register hook to unload library when 'libScope' becomes not alive
((MemorySessionImpl) libScope).addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() {
@Override
public void cleanup() {
nativeLibraries.unload(library);
@ -243,10 +243,10 @@ public interface SymbolLookup {
});
return name -> {
Objects.requireNonNull(name);
MemoryAddress addr = MemoryAddress.ofLong(library.find(name));
return addr == MemoryAddress.NULL
? Optional.empty() :
Optional.of(MemorySegment.ofAddress(addr, 0L, session));
long addr = library.find(name);
return addr == 0L ?
Optional.empty() :
Optional.of(MemorySegment.ofAddress(addr, 0, libScope));
};
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package java.lang.foreign;
import jdk.internal.foreign.layout.UnionLayoutImpl;
import jdk.internal.javac.PreviewFeature;
/**
* A group layout whose member layouts are laid out at the same starting offset.
*
* @implSpec
* Implementing classes are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
*
* @since 20
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public sealed interface UnionLayout extends GroupLayout permits UnionLayoutImpl {
/**
* {@inheritDoc}
*/
@Override
UnionLayout withName(String name);
/**
* {@inheritDoc}
*/
@Override
UnionLayout withBitAlignment(long bitAlignment);
}

View file

@ -39,10 +39,40 @@ import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
/**
* A variable argument list, similar in functionality to a C {@code va_list}.
* Helper class to create and manipulate variable argument lists, similar in functionality to a C {@code va_list}.
* <p>
* A variable argument list is a stateful cursor used to iterate over a set of arguments. A variable argument list
* can be passed by reference e.g. to a {@linkplain Linker#downcallHandle(FunctionDescriptor) downcall method handle}.
* A variable argument list can be created using the {@link #make(Consumer, SegmentScope)} factory, as follows:
* {@snippet lang = java:
* VaList vaList = VaList.make(builder ->
* builder.addVarg(C_INT, 42)
* .addVarg(C_DOUBLE, 3.8d));
*}
* Once created, clients can obtain the platform-dependent {@linkplain #segment() memory segment} associated with a variable
* argument list, which can then be passed to {@linkplain Linker#downcallHandle(FunctionDescriptor, Linker.Option...) downcall method handles}
* targeting native functions using the C {@code va_list} type.
* <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',
@ -64,22 +94,17 @@ import jdk.internal.reflect.Reflection;
* <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, MemorySession)} are capable of detecting out-of-bounds reads;</li>
* <li>Variable argument lists created <em>unsafely</em>, using {@link #ofAddress(MemoryAddress, MemorySession)} are not capable of detecting out-of-bounds reads</li>
* <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 memory session associated with the variable arity list).
* (regardless of the scope used to obtain the variable arity list).
*
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
sealed public interface VaList extends Addressable permits WinVaList, SysVVaList, LinuxAArch64VaList, MacOsAArch64VaList, SharedUtils.EmptyVaList {
/**
* {@return the memory session associated with this variable argument list}
*/
MemorySession session();
public sealed interface VaList permits WinVaList, SysVVaList, LinuxAArch64VaList, MacOsAArch64VaList, SharedUtils.EmptyVaList {
/**
* Reads the next value as an {@code int} and advances this variable argument list's position. The behavior of this
@ -87,10 +112,10 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
*
* @param layout the layout of the value to be read.
* @return the {@code int} value read from this variable argument list.
* @throws IllegalStateException if the {@linkplain #session() session} associated with this variable argument list is not
* {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread owning
* the {@linkplain #session() session} associated with this variable argument list.
* @throws IllegalStateException if the scope associated with this variable argument list is not
* {@linkplain SegmentScope#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread {@code T},
* such that {@code segment().scope().isAccessibleBy(T) == false}.
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
*/
int nextVarg(ValueLayout.OfInt layout);
@ -101,10 +126,10 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
*
* @param layout the layout of the value to be read.
* @return the {@code long} value read from this variable argument list.
* @throws IllegalStateException if the {@linkplain #session() session} associated with this variable argument list is not
* {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread owning
* the {@linkplain #session() session} associated with this variable argument list.
* @throws IllegalStateException if the scope associated with this variable argument list is not
* {@linkplain SegmentScope#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread {@code T},
* such that {@code segment().scope().isAccessibleBy(T) == false}.
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
*/
long nextVarg(ValueLayout.OfLong layout);
@ -115,32 +140,37 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
*
* @param layout the layout of the value
* @return the {@code double} value read from this variable argument list.
* @throws IllegalStateException if the {@linkplain #session() session} associated with this variable argument list is not
* {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread owning
* the {@linkplain #session() session} associated with this variable argument list.
* @throws IllegalStateException if the scope associated with this variable argument list is not
* {@linkplain SegmentScope#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread {@code T},
* such that {@code segment().scope().isAccessibleBy(T) == false}.
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
*/
double nextVarg(ValueLayout.OfDouble layout);
/**
* Reads the next value as a {@code MemoryAddress} and advances this variable argument list's position. The behavior of this
* method is equivalent to the C {@code va_arg} function.
* Reads the next address value, wraps it into a native segment, and advances this variable argument list's position.
* The behavior of this method is equivalent to the C {@code va_arg} function. The returned segment's base
* {@linkplain MemorySegment#address()} is set to the value read from the variable argument list, and the segment
* is associated with the {@linkplain SegmentScope#global() global scope}. Under normal conditions, the size of the returned
* segment is {@code 0}. However, if the provided layout is an {@linkplain ValueLayout.OfAddress#asUnbounded() unbounded}
* address layout, then the size of the returned segment is {@code Long.MAX_VALUE}.
*
* @param layout the layout of the value to be read.
* @return the {@code MemoryAddress} value read from this variable argument list.
* @throws IllegalStateException if the {@linkplain #session() session} associated with this variable argument list is not
* {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread owning
* the {@linkplain #session() session} associated with this variable argument list.
* @return a native segment whose {@linkplain MemorySegment#address() address} is the value read from
* this variable argument list.
* @throws IllegalStateException if the scope associated with this variable argument list is not
* {@linkplain SegmentScope#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread {@code T},
* such that {@code segment().scope().isAccessibleBy(T) == false}.
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
*/
MemoryAddress nextVarg(ValueLayout.OfAddress layout);
MemorySegment nextVarg(ValueLayout.OfAddress layout);
/**
* Reads the next value as a {@code MemorySegment}, and advances this variable argument list's position. The behavior of this
* method is equivalent to the C {@code va_arg} function. The provided group layout must correspond to a C struct or union
* type.
* Reads the next composite value into a new {@code MemorySegment}, allocated with the provided allocator,
* and advances this variable argument list's position. The behavior of this method is equivalent to the C
* {@code va_arg} function. The provided group layout must correspond to a C struct or union type.
* <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
@ -152,10 +182,10 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
* @param allocator the allocator to be used to create a segment where the contents of the variable argument list
* will be copied.
* @return the {@code MemorySegment} value read from this variable argument list.
* @throws IllegalStateException if the {@linkplain #session() session} associated with this variable argument list is not
* {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread owning
* the {@linkplain #session() session} associated with this variable argument list.
* @throws IllegalStateException if the scope associated with this variable argument list is not
* {@linkplain SegmentScope#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread {@code T},
* such that {@code segment().scope().isAccessibleBy(T) == false}.
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
*/
MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator);
@ -164,17 +194,17 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
* Skips a number of elements with the given memory layouts, and advances this variable argument list's position.
*
* @param layouts the layouts of the values to be skipped.
* @throws IllegalStateException if the {@linkplain #session() session} associated with this variable argument list is not
* {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread owning
* the {@linkplain #session() session} associated with this variable argument list.
* @throws IllegalStateException if the scope associated with this variable argument list is not
* {@linkplain SegmentScope#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread {@code T},
* such that {@code segment().scope().isAccessibleBy(T) == false}.
* @throws NoSuchElementException if an <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 memory session as this variable argument list. The behavior of this method is equivalent to the C
* with the same scope as this variable argument list. The behavior of this method is equivalent to the C
* {@code va_copy} function.
* <p>
* Copying is useful to traverse the variable argument list elements, starting from the current position,
@ -182,59 +212,57 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
* traversed multiple times.
*
* @return a copy of this variable argument list.
* @throws IllegalStateException if the {@linkplain #session() session} associated with this variable argument list is not
* {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread owning
* the {@linkplain #session() session} associated with this variable argument list.
* @throws IllegalStateException if the scope associated with this variable argument list is not
* {@linkplain SegmentScope#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread {@code T},
* such that {@code segment().scope().isAccessibleBy(T) == false}.
*/
VaList copy();
/**
* {@return the {@linkplain MemoryAddress memory address} associated with this variable argument list}
* @throws IllegalStateException if the {@linkplain #session() session} associated with this variable argument list is not
* {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread owning
* the {@linkplain #session() session} associated with this variable argument list.
* Returns a zero-length {@linkplain MemorySegment memory segment} associated with this variable argument list.
* The contents of the returned memory segment are platform-dependent. Whether and how the contents of
* the returned segment are updated when iterating the contents of a variable argument list is also
* platform-dependent.
* @return a zero-length {@linkplain MemorySegment memory segment} associated with this variable argument list.
*/
@Override
MemoryAddress address();
MemorySegment segment();
/**
* Creates a variable argument list from a memory address pointing to an existing variable argument list,
* with the given memory session.
* Creates a variable argument list from the give address value and scope. The address is typically obtained
* by calling {@link MemorySegment#address()} on a foreign memory segment instance. The provided scope determines
* the lifecycle of the returned variable argument list: the returned variable argument list will no longer be accessible,
* and its associated off-heap memory region will be deallocated when the scope becomes not
* {@linkplain SegmentScope#isAlive() alive}.
* <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.
*
* @implNote variable argument lists created using this method can not detect <a href=VaList.html#safety>out-of-bounds</a> reads.
*
* @param address a memory address pointing to an existing variable argument list.
* @param session the memory session to be associated with the returned variable argument list.
* @return a new variable argument list backed by the memory region at {@code address}.
* @throws IllegalStateException if {@code session} is not {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread
* {@linkplain MemorySession#ownerThread() owning} {@code session}.
* @param address the address of the variable argument list.
* @param scope the scope associated with the returned variable argument list.
* @return a new variable argument list backed by an off-heap region of memory starting at the given address value.
* @throws IllegalStateException if {@code scope} is not {@linkplain SegmentScope#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread {@code T},
* such that {@code scope.isAccessibleBy(T) == false}.
* @throws UnsupportedOperationException if the underlying native platform is not supported.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is specified, but does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
static VaList ofAddress(MemoryAddress address, MemorySession session) {
static VaList ofAddress(long address, SegmentScope scope) {
Reflection.ensureNativeAccess(Reflection.getCallerClass(), VaList.class, "ofAddress");
Objects.requireNonNull(address);
Objects.requireNonNull(session);
return SharedUtils.newVaListOfAddress(address, session);
Objects.requireNonNull(scope);
return SharedUtils.newVaListOfAddress(address, scope);
}
/**
* Creates a variable argument list using a builder (see {@link Builder}), with the given
* memory session.
* <p>
* If this method needs to allocate memory, such memory will be managed by the given
* memory session, and will be released when the memory session is {@linkplain MemorySession#close closed}.
* scope. The provided scope determines the lifecycle of the returned variable argument list: the
* returned variable argument list will no longer be accessible, and its associated off-heap memory region will be
* deallocated when the scope becomes not {@linkplain SegmentScope#isAlive() alive}.
* <p>
* Note that when there are no elements added to the created va list,
* this method will return the same as {@link #empty()}.
@ -243,23 +271,23 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
*
* @param actions a consumer for a builder (see {@link Builder}) which can be used to specify the elements
* of the underlying variable argument list.
* @param session the memory session to be associated with the new variable arity list.
* @param scope the scope to be associated with the new variable arity list.
* @return a new variable argument list.
* @throws UnsupportedOperationException if the underlying native platform is not supported.
* @throws IllegalStateException if {@code session} is not {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread
* {@linkplain MemorySession#ownerThread() owning} {@code session}.
* @throws IllegalStateException if {@code scope} is not {@linkplain SegmentScope#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread {@code T},
* such that {@code scope.isAccessibleBy(T) == false}.
*/
static VaList make(Consumer<Builder> actions, MemorySession session) {
static VaList make(Consumer<Builder> actions, SegmentScope scope) {
Objects.requireNonNull(actions);
Objects.requireNonNull(session);
return SharedUtils.newVaList(actions, session);
Objects.requireNonNull(scope);
return SharedUtils.newVaList(actions, scope);
}
/**
* Returns an empty variable argument list, associated with the {@linkplain MemorySession#global() global}
* memory session. The resulting variable argument list does not contain any argument, and throws {@link UnsupportedOperationException}
* on all operations, except for {@link VaList#address()}, {@link VaList#copy()} and {@link VaList#session()}.
* Returns an empty variable argument list, associated with the {@linkplain SegmentScope#global() global scope}.
* The resulting variable argument list does not contain any argument, and throws {@link UnsupportedOperationException}
* on all operations, except for {@link VaList#segment()}, {@link VaList#copy()}.
* @return an empty variable argument list.
* @throws UnsupportedOperationException if the underlying native platform is not supported.
*/
@ -303,16 +331,17 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
Builder addVarg(ValueLayout.OfDouble layout, double value);
/**
* Writes an {@code Addressable} value to the variable argument list being constructed.
* Writes the {@linkplain MemorySegment#address() address} of the provided native segment
* to the variable argument list being constructed.
*
* @param layout the layout of the value to be written.
* @param value the {@code Addressable} value to be written.
* @param segment the segment whose {@linkplain MemorySegment#address() address} is to be written.
* @return this builder.
*/
Builder addVarg(ValueLayout.OfAddress layout, Addressable value);
Builder addVarg(ValueLayout.OfAddress layout, MemorySegment segment);
/**
* Writes a {@code MemorySegment} value, with the given layout, to the variable argument list being constructed.
* Writes a {@code MemorySegment}, with the given layout, to the variable argument list being constructed.
*
* @param layout the layout of the value to be written.
* @param value the {@code MemorySegment} whose contents will be copied.

View file

@ -28,102 +28,49 @@ package java.lang.foreign;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import jdk.internal.foreign.Utils;
import jdk.internal.foreign.layout.ValueLayouts;
import jdk.internal.javac.PreviewFeature;
import jdk.internal.misc.Unsafe;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Stable;
import sun.invoke.util.Wrapper;
import jdk.internal.reflect.CallerSensitive;
/**
* A value layout. A value layout is used to model the memory layout associated with values of basic data types, such as <em>integral</em> types
* (either signed or unsigned) and <em>floating-point</em> types. Each value layout has a size, an alignment (in bits),
* A layout that models values of basic data types. Examples of values modelled by a value layout are
* <em>integral</em> values (either signed or unsigned), <em>floating-point</em> values and
* <em>address</em> values.
* <p>
* Each value layout has a size, an alignment (in bits),
* a {@linkplain ByteOrder byte order}, and a <em>carrier</em>, that is, the Java type that should be used when
* {@linkplain MemorySegment#get(OfInt, long) accessing} a memory region using the value layout.
* {@linkplain MemorySegment#get(OfInt, long) accessing} a region of memory using the value layout.
* <p>
* This class defines useful value layout constants for Java primitive types and addresses.
* The layout constants in this class make implicit alignment and byte-ordering assumption: all layout
* constants in this class are byte-aligned, and their byte order is set to the {@linkplain ByteOrder#nativeOrder() platform default},
* thus making it easy to work with other APIs, such as arrays and {@link java.nio.ByteBuffer}.
*
* @implSpec
* This class and its subclasses are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
* @implSpec implementing classes and subclasses are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
*
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public sealed class ValueLayout extends AbstractLayout implements MemoryLayout {
private final Class<?> carrier;
private final ByteOrder order;
private static final int ADDRESS_SIZE_BITS = Unsafe.ADDRESS_SIZE * 8;
ValueLayout(Class<?> carrier, ByteOrder order, long size) {
this(carrier, order, size, size, Optional.empty());
}
ValueLayout(Class<?> carrier, ByteOrder order, long size, long alignment, Optional<String> name) {
super(size, alignment, name);
this.carrier = carrier;
this.order = order;
checkCarrierSize(carrier, size);
}
public sealed interface ValueLayout extends MemoryLayout {
/**
* {@return the value's byte order}
*/
public ByteOrder order() {
return order;
}
ByteOrder order();
/**
* Returns a value layout with the same carrier, alignment constraints and name as this value layout,
* Returns a value layout with the same carrier, alignment constraint and name as this value layout,
* but with the specified byte order.
*
* @param order the desired byte order.
* @return a value layout with the given byte order.
*/
public ValueLayout withOrder(ByteOrder order) {
return new ValueLayout(carrier, Objects.requireNonNull(order), bitSize(), alignment, name());
}
ValueLayout withOrder(ByteOrder order);
/**
* {@inheritDoc}
*/
@Override
public String toString() {
char descriptor = carrier == MemoryAddress.class ? 'A' : carrier.descriptorString().charAt(0);
if (order == ByteOrder.LITTLE_ENDIAN) {
descriptor = Character.toLowerCase(descriptor);
}
return decorateLayoutString(String.format("%s%d", descriptor, bitSize()));
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!super.equals(other)) {
return false;
}
return other instanceof ValueLayout otherValue &&
carrier.equals(otherValue.carrier) &&
order.equals(otherValue.order);
}
/**
* Creates a <em>strided</em> access var handle that can be used to dereference a multi-dimensional array. The
* layout of this array is a sequence layout with {@code shape.length} nested sequence layouts. The element
* Creates a <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
* layout of the sequence layout at depth {@code shape.length} is this value layout.
* As a result, if {@code shape.length == 0}, the array layout will feature only one dimension.
* <p>
@ -138,15 +85,15 @@ public sealed class ValueLayout extends AbstractLayout implements MemoryLayout {
*
* Can be used to access a multi-dimensional array whose layout is as follows:
*
* {@snippet lang=java :
* SequenceLayout arrayLayout = MemoryLayout.sequenceLayout(-1,
* {@snippet lang = java:
* SequenceLayout arrayLayout = MemoryLayout.sequenceLayout(
* MemoryLayout.sequenceLayout(10,
* MemoryLayout.sequenceLayout(20, ValueLayout.JAVA_INT)));
* }
*}
*
* The resulting var handle {@code arrayHandle} will feature 3 coordinates of type {@code long}; each coordinate
* is interpreted as an index into the corresponding sequence layout. If we refer to the var handle coordinates, from left
* to right, as {@code x}, {@code y} and {@code z} respectively, the final offset dereferenced by the var handle can be
* to right, as {@code x}, {@code y} and {@code z} respectively, the final offset accessed by the var handle can be
* computed with the following formula:
*
* <blockquote><pre>{@code
@ -170,7 +117,8 @@ public sealed class ValueLayout extends AbstractLayout implements MemoryLayout {
* as the value for {@code z} is outside its specified bounds.
*
* @param shape the size of each nested array dimension.
* @return a var handle which can be used to dereference a multi-dimensional array, featuring {@code shape.length + 1}
* @return a var handle which can be used to access a memory segment as a multi-dimensional array,
* featuring {@code shape.length + 1}
* {@code long} coordinates.
* @throws IllegalArgumentException if {@code shape[i] < 0}, for at least one index {@code i}.
* @throws UnsupportedOperationException if {@code bitAlignment() > bitSize()}.
@ -178,504 +126,385 @@ public sealed class ValueLayout extends AbstractLayout implements MemoryLayout {
* @see MemoryLayout#varHandle(PathElement...)
* @see SequenceLayout
*/
public VarHandle arrayElementVarHandle(int... shape) {
Objects.requireNonNull(shape);
MemoryLayout layout = this;
List<PathElement> path = new ArrayList<>();
for (int i = shape.length ; i > 0 ; i--) {
int size = shape[i - 1];
if (size < 0) throw new IllegalArgumentException("Invalid shape size: " + size);
layout = MemoryLayout.sequenceLayout(size, layout);
path.add(PathElement.sequenceElement());
}
layout = MemoryLayout.sequenceLayout(-1, layout);
path.add(PathElement.sequenceElement());
return layout.varHandle(path.toArray(new PathElement[0]));
}
VarHandle arrayElementVarHandle(int... shape);
/**
* {@return the carrier associated with this value layout}
*/
public Class<?> carrier() {
return carrier;
}
Class<?> carrier();
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), order, carrier);
}
@Override
ValueLayout dup(long alignment, Optional<String> name) {
return new ValueLayout(carrier, order, bitSize(), alignment, name());
}
//hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
//but that causes issues with javadoc, see JDK-8224052
ValueLayout withName(String name);
/**
* {@inheritDoc}
*/
@Override
public ValueLayout withName(String name) {
return (ValueLayout)super.withName(name);
}
/**
* {@inheritDoc}
*/
@Override
public ValueLayout withBitAlignment(long alignmentBits) {
return (ValueLayout)super.withBitAlignment(alignmentBits);
}
static void checkCarrierSize(Class<?> carrier, long size) {
if (!isValidCarrier(carrier)) {
throw new IllegalArgumentException("Invalid carrier: " + carrier.getName());
}
if (carrier == MemoryAddress.class && size != ADDRESS_SIZE_BITS) {
throw new IllegalArgumentException("Address size mismatch: " + ADDRESS_SIZE_BITS + " != " + size);
}
if (carrier.isPrimitive()) {
int expectedSize = carrier == boolean.class ? 8 : Wrapper.forPrimitiveType(carrier).bitWidth();
if (size != expectedSize) {
throw new IllegalArgumentException("Carrier size mismatch: " + carrier.getName() + " != " + size);
}
}
}
static boolean isValidCarrier(Class<?> carrier) {
return carrier == boolean.class
|| carrier == byte.class
|| carrier == short.class
|| carrier == char.class
|| carrier == int.class
|| carrier == long.class
|| carrier == float.class
|| carrier == double.class
|| carrier == MemoryAddress.class;
}
@Stable
private VarHandle handle;
@ForceInline
VarHandle accessHandle() {
if (handle == null) {
// this store to stable field is safe, because return value of 'makeMemoryAccessVarHandle' has stable identity
handle = Utils.makeSegmentViewVarHandle(this);
}
return handle;
}
ValueLayout withBitAlignment(long bitAlignment);
/**
* A value layout whose carrier is {@code boolean.class}.
*
* @see #JAVA_BOOLEAN
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public static final class OfBoolean extends ValueLayout {
OfBoolean(ByteOrder order) {
super(boolean.class, order, 8);
}
OfBoolean(ByteOrder order, long alignment, Optional<String> name) {
super(boolean.class, order, 8, alignment, name);
}
@PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
sealed interface OfBoolean extends ValueLayout permits ValueLayouts.OfBooleanImpl {
/**
* {@inheritDoc}
*/
@Override
OfBoolean dup(long alignment, Optional<String> name) {
return new OfBoolean(order(), alignment, name);
}
OfBoolean withName(String name);
/**
* {@inheritDoc}
*/
@Override
public OfBoolean withName(String name) {
return (OfBoolean)super.withName(name);
}
OfBoolean withBitAlignment(long bitAlignment);
/**
* {@inheritDoc}
*/
@Override
public OfBoolean withBitAlignment(long alignmentBits) {
return (OfBoolean)super.withBitAlignment(alignmentBits);
}
OfBoolean withOrder(ByteOrder order);
@Override
public OfBoolean withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfBoolean(order, alignment, name());
}
}
/**
* A value layout whose carrier is {@code byte.class}.
*
* @see #JAVA_BYTE
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public static final class OfByte extends ValueLayout {
OfByte(ByteOrder order) {
super(byte.class, order, 8);
}
OfByte(ByteOrder order, long alignment, Optional<String> name) {
super(byte.class, order, 8, alignment, name);
}
@PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
sealed interface OfByte extends ValueLayout permits ValueLayouts.OfByteImpl {
/**
* {@inheritDoc}
*/
@Override
OfByte dup(long alignment, Optional<String> name) {
return new OfByte(order(), alignment, name);
}
OfByte withName(String name);
/**
* {@inheritDoc}
*/
@Override
public OfByte withName(String name) {
return (OfByte)super.withName(name);
}
OfByte withBitAlignment(long bitAlignment);
/**
* {@inheritDoc}
*/
@Override
public OfByte withBitAlignment(long alignmentBits) {
return (OfByte)super.withBitAlignment(alignmentBits);
}
OfByte withOrder(ByteOrder order);
@Override
public OfByte withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfByte(order, alignment, name());
}
}
/**
* A value layout whose carrier is {@code char.class}.
*
* @see #JAVA_CHAR
* @see #JAVA_CHAR_UNALIGNED
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public static final class OfChar extends ValueLayout {
OfChar(ByteOrder order) {
super(char.class, order, 16);
}
OfChar(ByteOrder order, long alignment, Optional<String> name) {
super(char.class, order, 16, alignment, name);
}
@PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
sealed interface OfChar extends ValueLayout permits ValueLayouts.OfCharImpl {
/**
* {@inheritDoc}
*/
@Override
OfChar dup(long alignment, Optional<String> name) {
return new OfChar(order(), alignment, name);
}
OfChar withName(String name);
/**
* {@inheritDoc}
*/
@Override
public OfChar withName(String name) {
return (OfChar)super.withName(name);
}
OfChar withBitAlignment(long bitAlignment);
/**
* {@inheritDoc}
*/
@Override
public OfChar withBitAlignment(long alignmentBits) {
return (OfChar)super.withBitAlignment(alignmentBits);
}
OfChar withOrder(ByteOrder order);
@Override
public OfChar withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfChar(order, alignment, name());
}
}
/**
* A value layout whose carrier is {@code short.class}.
*
* @see #JAVA_SHORT
* @see #JAVA_SHORT_UNALIGNED
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public static final class OfShort extends ValueLayout {
OfShort(ByteOrder order) {
super(short.class, order, 16);
}
OfShort(ByteOrder order, long alignment, Optional<String> name) {
super(short.class, order, 16, alignment, name);
}
@PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
sealed interface OfShort extends ValueLayout permits ValueLayouts.OfShortImpl {
/**
* {@inheritDoc}
*/
@Override
OfShort dup(long alignment, Optional<String> name) {
return new OfShort(order(), alignment, name);
}
OfShort withName(String name);
/**
* {@inheritDoc}
*/
@Override
public OfShort withName(String name) {
return (OfShort)super.withName(name);
}
OfShort withBitAlignment(long bitAlignment);
/**
* {@inheritDoc}
*/
@Override
public OfShort withBitAlignment(long alignmentBits) {
return (OfShort)super.withBitAlignment(alignmentBits);
}
OfShort withOrder(ByteOrder order);
@Override
public OfShort withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfShort(order, alignment, name());
}
}
/**
* A value layout whose carrier is {@code int.class}.
*
* @see #JAVA_INT
* @see #JAVA_INT_UNALIGNED
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public static final class OfInt extends ValueLayout {
OfInt(ByteOrder order) {
super(int.class, order, 32);
}
OfInt(ByteOrder order, long alignment, Optional<String> name) {
super(int.class, order, 32, alignment, name);
}
@PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
sealed interface OfInt extends ValueLayout permits ValueLayouts.OfIntImpl {
/**
* {@inheritDoc}
*/
@Override
OfInt dup(long alignment, Optional<String> name) {
return new OfInt(order(), alignment, name);
}
OfInt withName(String name);
/**
* {@inheritDoc}
*/
@Override
public OfInt withName(String name) {
return (OfInt)super.withName(name);
}
OfInt withBitAlignment(long bitAlignment);
/**
* {@inheritDoc}
*/
@Override
public OfInt withBitAlignment(long alignmentBits) {
return (OfInt)super.withBitAlignment(alignmentBits);
}
OfInt withOrder(ByteOrder order);
@Override
public OfInt withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfInt(order, alignment, name());
}
}
/**
* A value layout whose carrier is {@code float.class}.
*
* @see #JAVA_FLOAT
* @see #JAVA_FLOAT_UNALIGNED
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public static final class OfFloat extends ValueLayout {
OfFloat(ByteOrder order) {
super(float.class, order, 32);
}
OfFloat(ByteOrder order, long alignment, Optional<String> name) {
super(float.class, order, 32, alignment, name);
}
@PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
sealed interface OfFloat extends ValueLayout permits ValueLayouts.OfFloatImpl {
/**
* {@inheritDoc}
*/
@Override
OfFloat dup(long alignment, Optional<String> name) {
return new OfFloat(order(), alignment, name);
}
OfFloat withName(String name);
/**
* {@inheritDoc}
*/
@Override
public OfFloat withName(String name) {
return (OfFloat)super.withName(name);
}
OfFloat withBitAlignment(long bitAlignment);
/**
* {@inheritDoc}
*/
@Override
public OfFloat withBitAlignment(long alignmentBits) {
return (OfFloat)super.withBitAlignment(alignmentBits);
}
OfFloat withOrder(ByteOrder order);
@Override
public OfFloat withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfFloat(order, alignment, name());
}
}
/**
* A value layout whose carrier is {@code long.class}.
*
* @see #JAVA_LONG
* @see #JAVA_LONG_UNALIGNED
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public static final class OfLong extends ValueLayout {
OfLong(ByteOrder order) {
super(long.class, order, 64);
}
OfLong(ByteOrder order, long alignment, Optional<String> name) {
super(long.class, order, 64, alignment, name);
}
@PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
sealed interface OfLong extends ValueLayout permits ValueLayouts.OfLongImpl {
/**
* {@inheritDoc}
*/
@Override
OfLong dup(long alignment, Optional<String> name) {
return new OfLong(order(), alignment, name);
}
OfLong withName(String name);
/**
* {@inheritDoc}
*/
@Override
public OfLong withName(String name) {
return (OfLong)super.withName(name);
}
OfLong withBitAlignment(long bitAlignment);
/**
* {@inheritDoc}
*/
@Override
public OfLong withBitAlignment(long alignmentBits) {
return (OfLong)super.withBitAlignment(alignmentBits);
}
OfLong withOrder(ByteOrder order);
@Override
public OfLong withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfLong(order, alignment, name());
}
}
/**
* A value layout whose carrier is {@code double.class}.
*
* @see #JAVA_DOUBLE
* @see #JAVA_DOUBLE_UNALIGNED
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public static final class OfDouble extends ValueLayout {
OfDouble(ByteOrder order) {
super(double.class, order, 64);
}
OfDouble(ByteOrder order, long alignment, Optional<String> name) {
super(double.class, order, 64, alignment, name);
}
@PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
sealed interface OfDouble extends ValueLayout permits ValueLayouts.OfDoubleImpl {
/**
* {@inheritDoc}
*/
@Override
OfDouble dup(long alignment, Optional<String> name) {
return new OfDouble(order(), alignment, name);
}
OfDouble withName(String name);
/**
* {@inheritDoc}
*/
@Override
public OfDouble withName(String name) {
return (OfDouble)super.withName(name);
}
OfDouble withBitAlignment(long bitAlignment);
/**
* {@inheritDoc}
*/
@Override
public OfDouble withBitAlignment(long alignmentBits) {
return (OfDouble)super.withBitAlignment(alignmentBits);
}
OfDouble withOrder(ByteOrder order);
@Override
public OfDouble withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfDouble(order, alignment, name());
}
}
/**
* A value layout whose carrier is {@code MemoryAddress.class}.
* A value layout whose carrier is {@code MemorySegment.class}.
*
* @see #ADDRESS
* @see #ADDRESS_UNALIGNED
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public static final class OfAddress extends ValueLayout {
OfAddress(ByteOrder order) {
super(MemoryAddress.class, order, ADDRESS_SIZE_BITS);
}
OfAddress(ByteOrder order, long size, long alignment, Optional<String> name) {
super(MemoryAddress.class, order, size, alignment, name);
}
@PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
sealed interface OfAddress extends ValueLayout permits ValueLayouts.OfAddressImpl {
/**
* {@inheritDoc}
*/
@Override
OfAddress dup(long alignment, Optional<String> name) {
return new OfAddress(order(), bitSize(), alignment, name);
}
OfAddress withName(String name);
/**
* {@inheritDoc}
*/
@Override
public OfAddress withName(String name) {
return (OfAddress)super.withName(name);
}
OfAddress withBitAlignment(long bitAlignment);
/**
* {@inheritDoc}
*/
@Override
public OfAddress withBitAlignment(long alignmentBits) {
return (OfAddress)super.withBitAlignment(alignmentBits);
}
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.
* @see #isUnbounded()
*/
@CallerSensitive
OfAddress asUnbounded();
/**
* {@return {@code true}, if this address layout is an {@linkplain #asUnbounded() unbounded address layout}}.
*/
boolean isUnbounded();
@Override
public OfAddress withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfAddress(order, bitSize(), alignment, name());
}
}
/**
* A value layout constant whose size is the same as that of a machine address ({@code size_t}),
* bit alignment set to {@code sizeof(size_t) * 8}, and byte order set to {@link ByteOrder#nativeOrder()}.
* bit alignment set to {@code sizeof(size_t) * 8}, byte order set to {@link ByteOrder#nativeOrder()}.
* Equivalent to the following code:
* {@snippet lang=java :
* MemoryLayout.valueLayout(MemoryAddress.class, ByteOrder.nativeOrder())
* .withBitAlignment(<address size>);
* MemoryLayout.valueLayout(MemorySegment.class, ByteOrder.nativeOrder());
* }
*/
public static final OfAddress ADDRESS = new OfAddress(ByteOrder.nativeOrder())
.withBitAlignment(ValueLayout.ADDRESS_SIZE_BITS);
OfAddress ADDRESS = ValueLayouts.OfAddressImpl.of(ByteOrder.nativeOrder());
/**
* A value layout constant whose size is the same as that of a Java {@code byte},
* bit alignment set to 8, and byte order set to {@link ByteOrder#nativeOrder()}.
* Equivalent to the following code:
* {@snippet lang=java :
* MemoryLayout.valueLayout(byte.class, ByteOrder.nativeOrder()).withBitAlignment(8);
* MemoryLayout.valueLayout(byte.class, ByteOrder.nativeOrder());
* }
*/
public static final OfByte JAVA_BYTE = new OfByte(ByteOrder.nativeOrder()).withBitAlignment(8);
OfByte JAVA_BYTE = ValueLayouts.OfByteImpl.of(ByteOrder.nativeOrder());
/**
* A value layout constant whose size is the same as that of a Java {@code boolean},
* bit alignment set to 8, and byte order set to {@link ByteOrder#nativeOrder()}.
* Equivalent to the following code:
* {@snippet lang=java :
* MemoryLayout.valueLayout(boolean.class, ByteOrder.nativeOrder()).withBitAlignment(8);
* MemoryLayout.valueLayout(boolean.class, ByteOrder.nativeOrder());
* }
*/
public static final OfBoolean JAVA_BOOLEAN = new OfBoolean(ByteOrder.nativeOrder()).withBitAlignment(8);
OfBoolean JAVA_BOOLEAN = ValueLayouts.OfBooleanImpl.of(ByteOrder.nativeOrder());
/**
* A value layout constant whose size is the same as that of a Java {@code char},
* bit alignment set to 16, and byte order set to {@link ByteOrder#nativeOrder()}.
* Equivalent to the following code:
* {@snippet lang=java :
* MemoryLayout.valueLayout(char.class, ByteOrder.nativeOrder()).withBitAlignment(16);
* MemoryLayout.valueLayout(char.class, ByteOrder.nativeOrder());
* }
*/
public static final OfChar JAVA_CHAR = new OfChar(ByteOrder.nativeOrder()).withBitAlignment(16);
OfChar JAVA_CHAR = ValueLayouts.OfCharImpl.of(ByteOrder.nativeOrder());
/**
* A value layout constant whose size is the same as that of a Java {@code short},
* bit alignment set to 16, and byte order set to {@link ByteOrder#nativeOrder()}.
* Equivalent to the following code:
* {@snippet lang=java :
* MemoryLayout.valueLayout(short.class, ByteOrder.nativeOrder()).withBitAlignment(16);
* MemoryLayout.valueLayout(short.class, ByteOrder.nativeOrder());
* }
*/
public static final OfShort JAVA_SHORT = new OfShort(ByteOrder.nativeOrder()).withBitAlignment(16);
OfShort JAVA_SHORT = ValueLayouts.OfShortImpl.of(ByteOrder.nativeOrder());
/**
* A value layout constant whose size is the same as that of a Java {@code int},
* bit alignment set to 32, and byte order set to {@link ByteOrder#nativeOrder()}.
* Equivalent to the following code:
* {@snippet lang=java :
* MemoryLayout.valueLayout(int.class, ByteOrder.nativeOrder()).withBitAlignment(32);
* MemoryLayout.valueLayout(int.class, ByteOrder.nativeOrder());
* }
*/
public static final OfInt JAVA_INT = new OfInt(ByteOrder.nativeOrder()).withBitAlignment(32);
OfInt JAVA_INT = ValueLayouts.OfIntImpl.of(ByteOrder.nativeOrder());
/**
* A value layout constant whose size is the same as that of a Java {@code long},
* bit alignment set to 64, and byte order set to {@link ByteOrder#nativeOrder()}.
* Equivalent to the following code:
* {@snippet lang=java :
* MemoryLayout.valueLayout(long.class, ByteOrder.nativeOrder()).withBitAlignment(64);
* MemoryLayout.valueLayout(long.class, ByteOrder.nativeOrder());
* }
*/
public static final OfLong JAVA_LONG = new OfLong(ByteOrder.nativeOrder())
.withBitAlignment(64);
OfLong JAVA_LONG = ValueLayouts.OfLongImpl.of(ByteOrder.nativeOrder());
/**
* A value layout constant whose size is the same as that of a Java {@code float},
@ -685,15 +514,100 @@ public sealed class ValueLayout extends AbstractLayout implements MemoryLayout {
* MemoryLayout.valueLayout(float.class, ByteOrder.nativeOrder()).withBitAlignment(32);
* }
*/
public static final OfFloat JAVA_FLOAT = new OfFloat(ByteOrder.nativeOrder()).withBitAlignment(32);
OfFloat JAVA_FLOAT = ValueLayouts.OfFloatImpl.of(ByteOrder.nativeOrder());
/**
* A value layout constant whose size is the same as that of a Java {@code double},
* bit alignment set to 64, and byte order set to {@link ByteOrder#nativeOrder()}.
* Equivalent to the following code:
* {@snippet lang=java :
* MemoryLayout.valueLayout(double.class, ByteOrder.nativeOrder()).withBitAlignment(64);
* MemoryLayout.valueLayout(double.class, ByteOrder.nativeOrder());
* }
*/
public static final OfDouble JAVA_DOUBLE = new OfDouble(ByteOrder.nativeOrder()).withBitAlignment(64);
OfDouble JAVA_DOUBLE = ValueLayouts.OfDoubleImpl.of(ByteOrder.nativeOrder());
/**
* An unaligned value layout constant whose size is the same as that of a machine address ({@code size_t}),
* and byte order set to {@link ByteOrder#nativeOrder()}.
* Equivalent to the following code:
* {@snippet lang=java :
* ADDRESS.withBitAlignment(8);
* }
* @apiNote Care should be taken when using unaligned value layouts as they may induce
* performance and portability issues.
*/
OfAddress ADDRESS_UNALIGNED = ADDRESS.withBitAlignment(8);
/**
* An unaligned value layout constant whose size is the same as that of a Java {@code char}
* and byte order set to {@link ByteOrder#nativeOrder()}.
* Equivalent to the following code:
* {@snippet lang=java :
* JAVA_CHAR.withBitAlignment(8);
* }
* @apiNote Care should be taken when using unaligned value layouts as they may induce
* performance and portability issues.
*/
OfChar JAVA_CHAR_UNALIGNED = JAVA_CHAR.withBitAlignment(8);
/**
* An unaligned value layout constant whose size is the same as that of a Java {@code short}
* and byte order set to {@link ByteOrder#nativeOrder()}.
* Equivalent to the following code:
* {@snippet lang=java :
* JAVA_SHORT.withBitAlignment(8);
* }
* @apiNote Care should be taken when using unaligned value layouts as they may induce
* performance and portability issues.
*/
OfShort JAVA_SHORT_UNALIGNED = JAVA_SHORT.withBitAlignment(8);
/**
* An unaligned value layout constant whose size is the same as that of a Java {@code int}
* and byte order set to {@link ByteOrder#nativeOrder()}.
* Equivalent to the following code:
* {@snippet lang=java :
* JAVA_INT.withBitAlignment(8);
* }
* @apiNote Care should be taken when using unaligned value layouts as they may induce
* performance and portability issues.
*/
OfInt JAVA_INT_UNALIGNED = JAVA_INT.withBitAlignment(8);
/**
* An unaligned value layout constant whose size is the same as that of a Java {@code long}
* and byte order set to {@link ByteOrder#nativeOrder()}.
* Equivalent to the following code:
* {@snippet lang=java :
* JAVA_LONG.withBitAlignment(8);
* }
* @apiNote Care should be taken when using unaligned value layouts as they may induce
* performance and portability issues.
*/
OfLong JAVA_LONG_UNALIGNED = JAVA_LONG.withBitAlignment(8);
/**
* An unaligned value layout constant whose size is the same as that of a Java {@code float}
* and byte order set to {@link ByteOrder#nativeOrder()}.
* Equivalent to the following code:
* {@snippet lang=java :
* JAVA_FLOAT.withBitAlignment(8);
* }
* @apiNote Care should be taken when using unaligned value layouts as they may induce
* performance and portability issues.
*/
OfFloat JAVA_FLOAT_UNALIGNED = JAVA_FLOAT.withBitAlignment(8);
/**
* An unaligned value layout constant whose size is the same as that of a Java {@code double}
* and byte order set to {@link ByteOrder#nativeOrder()}.
* Equivalent to the following code:
* {@snippet lang=java :
* JAVA_DOUBLE.withBitAlignment(8);
* }
* @apiNote Care should be taken when using unaligned value layouts as they may induce
* performance and portability issues.
*/
OfDouble JAVA_DOUBLE_UNALIGNED = JAVA_DOUBLE.withBitAlignment(8);
}

View file

@ -31,29 +31,36 @@
*
* <p>
* The main abstraction introduced to support foreign memory access is {@link java.lang.foreign.MemorySegment}, which
* models a contiguous memory region, residing either inside or outside the Java heap. The contents of a memory
* models a contiguous region of memory, residing either inside or outside the Java heap. The contents of a memory
* segment can be described using a {@link java.lang.foreign.MemoryLayout memory layout}, which provides
* basic operations to query sizes, offsets and alignment constraints. Memory layouts also provide
* an alternate, more abstract way, to <a href=MemorySegment.html#segment-deref>dereference memory segments</a>
* using {@linkplain java.lang.foreign.MemoryLayout#varHandle(java.lang.foreign.MemoryLayout.PathElement...) access var handles},
* 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 memory region big enough to hold 10 values of the primitive type {@code int}, and fill it with values
* ranging from {@code 0} to {@code 9}, we can use the following code:
* For example, to allocate an off-heap region of memory big enough to hold 10 values of the primitive type {@code int},
* and fill it with values ranging from {@code 0} to {@code 9}, we can use the following code:
*
* {@snippet lang=java :
* MemorySegment segment = MemorySegment.allocateNative(10 * 4, MemorySession.openImplicit());
* {@snippet lang = java:
* MemorySegment segment = MemorySegment.allocateNative(10 * 4, SegmentScope.auto());
* for (int i = 0 ; i < 10 ; i++) {
* segment.setAtIndex(ValueLayout.JAVA_INT, i, i);
* }
* }
*}
*
* This code creates a <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) dereference method}
* accepts a {@linkplain java.lang.foreign.ValueLayout value layout}, which specifies the size, alignment constraints,
* byte order as well as the Java type ({@code int}, in this case) associated with the dereference operation. More specifically,
* {@linkplain java.lang.foreign.MemorySegment#setAtIndex(ValueLayout.OfInt, long, int) access method}
* accepts a {@linkplain java.lang.foreign.ValueLayout value layout}, which specifies the size, alignment constraint,
* byte order as well as the Java type ({@code int}, in this case) associated with the access operation. More specifically,
* if we view the memory segment as a set of 10 adjacent slots, {@code s[i]}, where {@code 0 <= i < 10},
* where the size of each slot is exactly 4 bytes, the initialization logic above will set each slot
* so that {@code s[i] = i}, again where {@code 0 <= i < 10}.
@ -64,35 +71,37 @@
* often crucial that the resources associated with a memory segment are released when the segment is no longer in use,
* and in a timely fashion. For this reason, there might be cases where waiting for the garbage collector to determine that a segment
* is <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 associated
* with a memory segment. This can be done, using the {@link java.lang.foreign.MemorySession} abstraction, as shown below:
* Clients that operate under these assumptions might want to programmatically release the memory backing a memory segment.
* This can be done, using the {@link java.lang.foreign.Arena} abstraction, as shown below:
*
* {@snippet lang=java :
* try (MemorySession session = MemorySession.openConfined()) {
* MemorySegment segment = MemorySegment.allocateNative(10 * 4, session);
* {@snippet lang = java:
* try (Arena arena = Arena.openConfined()) {
* MemorySegment segment = arena.allocate(10 * 4);
* for (int i = 0 ; i < 10 ; i++) {
* segment.setAtIndex(ValueLayout.JAVA_INT, i, i);
* }
* }
* }
*}
*
* This example is almost identical to the prior one; this time we first create a so called <em>memory session</em>,
* which is used to <em>bind</em> the life-cycle of the segment created immediately afterwards. Note the use of the
* <em>try-with-resources</em> construct: this idiom ensures that all the memory resources associated with the segment will be released
* at the end of the block, according to the semantics described in Section {@jls 14.20.3} of <cite>The Java Language Specification</cite>.
* 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,
* 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 dereference operation. We call this guarantee <em>spatial safety</em>;
* <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 can be closed (see above), segments are also validated (upon access) to make sure that
* the memory session associated with the segment being accessed has not been closed prematurely.
* Since memory segments created with an arena can become invalid (see above), segments are also validated (upon access) to make sure that
* the scope associated with the segment being accessed is still alive.
* We call this guarantee <em>temporal safety</em>. Together, spatial and temporal safety ensure that each memory access
* operation either succeeds - and accesses a valid memory location - or fails.
* operation either succeeds - and accesses a valid location within the region of memory backing the memory segment - or fails.
*
* <h2 id="ffa">Foreign function access</h2>
* The key abstractions introduced to support foreign function access are {@link java.lang.foreign.SymbolLookup},
@ -105,134 +114,118 @@
* For example, to compute the length of a string using the C standard library function {@code strlen} on a Linux x64 platform,
* we can use the following code:
*
* {@snippet lang=java :
* {@snippet lang = java:
* Linker linker = Linker.nativeLinker();
* SymbolLookup stdlib = linker.defaultLookup();
* MethodHandle strlen = linker.downcallHandle(
* stdlib.lookup("strlen").get(),
* stdlib.find("strlen").get(),
* FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS)
* );
*
* try (MemorySession session = MemorySession.openConfined()) {
* MemorySegment cString = MemorySegment.allocateNative(5 + 1, session);
* cString.setUtf8String(0, "Hello");
* try (Arena arena = Arena.openConfined()) {
* MemorySegment cString = arena.allocateUtf8String("Hello");
* long len = (long)strlen.invoke(cString); // 5
* }
* }
*}
*
* Here, we obtain a {@linkplain java.lang.foreign.Linker#nativeLinker() native linker} and we use it
* to {@linkplain java.lang.foreign.SymbolLookup#lookup(java.lang.String) look up} the {@code strlen} symbol in the
* to {@linkplain java.lang.foreign.SymbolLookup#find(java.lang.String) look up} the {@code strlen} symbol in the
* standard C library; a <em>downcall method handle</em> targeting said symbol is subsequently
* {@linkplain java.lang.foreign.Linker#downcallHandle(java.lang.foreign.FunctionDescriptor) obtained}.
* {@linkplain java.lang.foreign.Linker#downcallHandle(FunctionDescriptor, Linker.Option...) obtained}.
* To complete the linking successfully, we must provide a {@link java.lang.foreign.FunctionDescriptor} instance,
* describing the signature of the {@code strlen} function.
* From this information, the linker will uniquely determine the sequence of steps which will turn
* the method handle invocation (here performed using {@link java.lang.invoke.MethodHandle#invoke(java.lang.Object...)})
* into a foreign function call, according to the rules specified by the ABI of the underlying platform.
* The {@link java.lang.foreign.MemorySegment} class also provides many useful methods for
* interacting with foreign code, such as converting Java strings
* {@linkplain java.lang.foreign.MemorySegment#setUtf8String(long, java.lang.String) into} zero-terminated, UTF-8 strings and
* {@linkplain java.lang.foreign.MemorySegment#getUtf8String(long) back}, as demonstrated in the above example.
*
* <h3 id="addresses">Foreign addresses</h3>
*
* When a memory segment is created from Java code, the segment properties (spatial bounds, temporal bounds and confinement)
* are fully known at segment creation. But when interacting with foreign functions, clients will often receive <em>raw</em> pointers.
* Such pointers have no spatial bounds. For example, the C type {@code char*} can refer to a single {@code char} value,
* or an array of {@code char} values, of given size. Nor do said pointers have any notion of temporal bounds or thread-confinement.
* <p>
* Raw pointers are modelled using the {@link java.lang.foreign.MemoryAddress} class. When clients receive a
* memory address instance from a foreign function call, they can perform memory dereference on it directly,
* using one of the many <em>unsafe</em>
* {@linkplain java.lang.foreign.MemoryAddress#get(java.lang.foreign.ValueLayout.OfInt, long) dereference methods}
* provided:
*
* {@snippet lang=java :
* MemoryAddress addr = ... // obtain address from foreign function call
* int x = addr.get(ValueLayout.JAVA_INT, 0);
* }
*
* Alternatively, the client can
* {@linkplain java.lang.foreign.MemorySegment#ofAddress(java.lang.foreign.MemoryAddress, long, java.lang.foreign.MemorySession) create}
* a memory segment <em>unsafely</em>. This allows the client to inject extra knowledge about spatial bounds which might,
* for instance, be available in the documentation of the foreign function which produced the native address.
* Here is how an unsafe segment can be created from a memory address:
*
* {@snippet lang=java :
* MemorySession session = ... // initialize a memory session object
* MemoryAddress addr = ... // obtain address from foreign function call
* MemorySegment segment = MemorySegment.ofAddress(addr, 4, session); // segment is 4 bytes long
* int x = segment.get(ValueLayout.JAVA_INT, 0);
* }
* The {@link java.lang.foreign.Arena} class also provides many useful methods for
* interacting with foreign code, such as
* {@linkplain java.lang.foreign.SegmentAllocator#allocateUtf8String(java.lang.String) converting} Java strings into
* zero-terminated, UTF-8 strings, as demonstrated in the above example.
*
* <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 address, so that Java code can effectively be passed to other foreign functions.
* to a Java method) into a memory segment, so that Java code can effectively be passed to other foreign functions.
* For instance, we can write a method that compares two integer values, as follows:
*
* {@snippet lang=java :
* class IntComparator {
* static int intCompare(MemoryAddress addr1, MemoryAddress addr2) {
* return addr1.get(ValueLayout.JAVA_INT, 0) - addr2.get(ValueLayout.JAVA_INT, 0);
* static int intCompare(MemorySegment addr1, MemorySegment addr2) {
* return addr1.get(ValueLayout.JAVA_INT, 0) -
* addr2.get(ValueLayout.JAVA_INT, 0);
*
* }
* }
* }
*
* The above method dereferences two memory addresses containing an integer value, and performs a simple comparison
* The above method accesses two foreign memory segments containing an integer value, and performs a simple comparison
* by returning the difference between such values. We can then obtain a method handle which targets the above static
* method, as follows:
*
* {@snippet lang=java :
* FunctionDescriptor intCompareDescriptor = FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.ADDRESS);
* {@snippet lang = java:
* FunctionDescriptor intCompareDescriptor = FunctionDescriptor.of(ValueLayout.JAVA_INT,
* ValueLayout.ADDRESS.asUnbounded(),
* ValueLayout.ADDRESS.asUnbounded());
* MethodHandle intCompareHandle = MethodHandles.lookup().findStatic(IntComparator.class,
* "intCompare",
* Linker.upcallType(comparFunction));
* }
* intCompareDescriptor.toMethodType());
*}
*
* As before, we need to create a {@link java.lang.foreign.FunctionDescriptor} instance, this time describing the signature
* of the function pointer we want to create. The descriptor can be used to
* {@linkplain java.lang.foreign.Linker#upcallType(java.lang.foreign.FunctionDescriptor) derive} a method type
* {@linkplain java.lang.foreign.FunctionDescriptor#toMethodType() derive} a method type
* that can be used to look up the method handle for {@code IntComparator.intCompare}.
* <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 :
* MemorySession session = ...
* Addressable comparFunc = Linker.nativeLinker().upcallStub(
* intCompareHandle, intCompareDescriptor, session);
* {@snippet lang = java:
* SegmentScope scope = ...
* MemorySegment comparFunc = Linker.nativeLinker().upcallStub(
* intCompareHandle, intCompareDescriptor, scope);
* );
* }
*}
*
* The {@link java.lang.foreign.FunctionDescriptor} instance created in the previous step is then used to
* {@linkplain java.lang.foreign.Linker#upcallStub(java.lang.invoke.MethodHandle, java.lang.foreign.FunctionDescriptor, java.lang.foreign.MemorySession) create}
* {@linkplain java.lang.foreign.Linker#upcallStub(java.lang.invoke.MethodHandle, FunctionDescriptor, SegmentScope) create}
* a new upcall stub; the layouts in the function descriptors allow the linker to determine the sequence of steps which
* allow foreign code to call the stub for {@code intCompareHandle} according to the rules specified by the ABI of the
* underlying platform.
* The lifecycle of the upcall stub is tied to the {@linkplain java.lang.foreign.MemorySession memory session}
* provided when the upcall stub is created. This same session is made available by the {@link java.lang.foreign.MemorySegment}
* The lifecycle of the upcall stub is tied to the {@linkplain java.lang.foreign.SegmentScope scope}
* provided when the upcall stub is created. This same scope is made available by the {@link java.lang.foreign.MemorySegment}
* instance returned by that method.
*
* <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(MemoryAddress, long, MemorySession)}
* the restricted method {@link java.lang.foreign.MemorySegment#ofAddress(long, long, SegmentScope)}
* can be used to create a fresh segment with the given spatial bounds out of a native address.
* <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(MemoryAddress, long, MemorySession)},
* if the provided spatial bounds are incorrect, a client of the segment returned by that method might crash the VM, or corrupt
* memory when attempting to dereference said segment. For these reasons, it is crucial for code that calls a restricted method
* Binding foreign data and/or functions is generally unsafe and, if done incorrectly, can result in VM crashes,
* or memory corruption when the bound Java API element is accessed. For instance, in the case of
* {@link java.lang.foreign.MemorySegment#ofAddress(long, long, SegmentScope)}, if the provided spatial bounds are
* incorrect, a client of the segment returned by that method might crash the VM, or corrupt
* memory when attempting to access said segment. For these reasons, it is crucial for code that calls a restricted method
* to never pass arguments that might cause incorrect binding of foreign data and/or functions to a Java API.
* <p>
* Access to restricted methods can be controlled using the command line option {@code --enable-native-access=M1,M2, ... Mn},
* where {@code M1}, {@code M2}, {@code ... Mn} are module names (for the unnamed module, the special value {@code ALL-UNNAMED}
* can be used). If this option is specified, access to restricted methods is only granted to the modules listed by that
* option. If this option is not specified, access to restricted methods is enabled for all modules, but
* access to restricted methods will result in runtime warnings.
* Given the potential danger of restricted methods, the Java runtime issues a warning on the standard error stream
* every time a restricted method is invoked. Such warnings can be disabled by granting access to restricted methods
* to selected modules. This can be done either via implementation-specific command line options, or programmatically, e.g. by calling
* {@link java.lang.ModuleLayer.Controller#enableNativeAccess(java.lang.Module)}.
* <p>
* For every class in this package, unless specified otherwise, any method arguments of reference
* type must not be null, and any null argument will elicit a {@code NullPointerException}. This fact is not individually
* documented for methods of this API.
*
* @apiNote Usual memory model guarantees, for example stated in {@jls 6.6} and {@jls 10.4}, do not apply
* when accessing native memory segments as these segments are backed by off-heap regions of memory.
*
* @implNote
* In the reference implementation, access to restricted methods can be granted to specific modules using the command line option
* {@code --enable-native-access=M1,M2, ... Mn}, where {@code M1}, {@code M2}, {@code ... Mn} are module names
* (for the unnamed module, the special value {@code ALL-UNNAMED} can be used). If this option is specified, access to
* restricted methods is only granted to the modules listed by that option. If this option is not specified,
* access to restricted methods is enabled for all modules, but access to restricted methods will result in runtime warnings.
*
*/
package java.lang.foreign;

View file

@ -45,8 +45,8 @@ import sun.security.util.SecurityConstants;
import java.lang.constant.ConstantDescs;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemoryAddress;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.LambdaForm.BasicType;
import java.lang.reflect.Constructor;
@ -7910,21 +7910,21 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
* access modes {@code get} and {@code set} for {@code long} and
* {@code double} on 32-bit platforms.
* <li>atomic update access modes for {@code int}, {@code long},
* {@code float}, {@code double} or {@link MemoryAddress}.
* {@code float}, {@code double} or {@link MemorySegment}.
* (Future major platform releases of the JDK may support additional
* types for certain currently unsupported access modes.)
* <li>numeric atomic update access modes for {@code int}, {@code long} and {@link MemoryAddress}.
* <li>numeric atomic update access modes for {@code int}, {@code long} and {@link MemorySegment}.
* (Future major platform releases of the JDK may support additional
* numeric types for certain currently unsupported access modes.)
* <li>bitwise atomic update access modes for {@code int}, {@code long} and {@link MemoryAddress}.
* <li>bitwise atomic update access modes for {@code int}, {@code long} and {@link MemorySegment}.
* (Future major platform releases of the JDK may support additional
* numeric types for certain currently unsupported access modes.)
* </ul>
*
* If {@code T} is {@code float}, {@code double} or {@link MemoryAddress} then atomic
* If {@code T} is {@code float}, {@code double} or {@link MemorySegment} then atomic
* update access modes compare values using their bitwise representation
* (see {@link Float#floatToRawIntBits},
* {@link Double#doubleToRawLongBits} and {@link MemoryAddress#toRawLongValue()}, respectively).
* {@link Double#doubleToRawLongBits} and {@link MemorySegment#address()}, respectively).
* <p>
* Alternatively, a memory access operation is <em>partially aligned</em> if it occurs at a memory address {@code A}
* which is only compatible with the alignment constraint {@code B}; in such cases, access for anything other than the

View file

@ -52,24 +52,32 @@ import static java.lang.invoke.MethodHandleStatics.newInternalError;
*/
public static MethodHandle make(NativeEntryPoint nep) {
MethodType type = nep.type();
if (!allTypesPrimitive(type))
throw new IllegalArgumentException("Type must only contain primitives: " + type);
if (hasIllegalType(type))
throw new IllegalArgumentException("Illegal type(s) found: " + type);
LambdaForm lform = preparedLambdaForm(type);
return new NativeMethodHandle(type, lform, nep);
}
private static boolean allTypesPrimitive(MethodType type) {
if (!type.returnType().isPrimitive())
return false;
private static boolean hasIllegalType(MethodType type) {
if (isIllegalType(type.returnType()))
return true;
for (Class<?> pType : type.parameterArray()) {
if (!pType.isPrimitive())
return false;
if (isIllegalType(pType))
return true;
}
return true;
return false;
}
private static boolean isIllegalType(Class<?> pType) {
return !(pType == long.class
|| pType == int.class
|| pType == float.class
|| pType == double.class
|| pType == void.class);
}
private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory();

View file

@ -34,7 +34,6 @@ import jdk.internal.util.Preconditions;
import jdk.internal.vm.annotation.ForceInline;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.MemorySession;
import java.nio.ByteBuffer;
import java.nio.ReadOnlyBufferException;
import java.util.List;