8275063: Implementation of Foreign Function & Memory API (Second incubator)

Reviewed-by: erikj, psandoz, jvernee, darcy
This commit is contained in:
Maurizio Cimadamore 2021-11-24 11:51:16 +00:00
parent 17e68caad7
commit 96e36071b6
191 changed files with 9463 additions and 7631 deletions

View file

@ -54,6 +54,7 @@ BUILD_JDK_JTREG_EXECUTABLES_CFLAGS_exeJliLaunchTest := \
-I$(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS)/native/libjli -I$(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS)/native/libjli
BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libAsyncStackWalk := $(LIBCXX) BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libAsyncStackWalk := $(LIBCXX)
BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libAsyncInvokers := $(LIBCXX)
# Platform specific setup # Platform specific setup
ifeq ($(call isTargetOs, windows), true) ifeq ($(call isTargetOs, windows), true)
@ -66,6 +67,7 @@ ifeq ($(call isTargetOs, windows), true)
BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeCallerAccessTest := jvm.lib BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeCallerAccessTest := jvm.lib
BUILD_JDK_JTREG_EXECUTABLES_LIBS_exerevokeall := advapi32.lib BUILD_JDK_JTREG_EXECUTABLES_LIBS_exerevokeall := advapi32.lib
BUILD_JDK_JTREG_LIBRARIES_CFLAGS_libAsyncStackWalk := /EHsc BUILD_JDK_JTREG_LIBRARIES_CFLAGS_libAsyncStackWalk := /EHsc
BUILD_JDK_JTREG_LIBRARIES_CFLAGS_libAsyncInvokers := /EHsc
else else
BUILD_JDK_JTREG_LIBRARIES_LIBS_libstringPlatformChars := -ljava BUILD_JDK_JTREG_LIBRARIES_LIBS_libstringPlatformChars := -ljava
BUILD_JDK_JTREG_LIBRARIES_LIBS_libDirectIO := -ljava BUILD_JDK_JTREG_LIBRARIES_LIBS_libDirectIO := -ljava

View file

@ -351,7 +351,6 @@
/* Foreign API Support */ \ /* Foreign API Support */ \
template(jdk_internal_invoke_NativeEntryPoint, "jdk/internal/invoke/NativeEntryPoint") \ template(jdk_internal_invoke_NativeEntryPoint, "jdk/internal/invoke/NativeEntryPoint") \
template(jdk_internal_invoke_NativeEntryPoint_signature, "Ljdk/internal/invoke/NativeEntryPoint;") \ template(jdk_internal_invoke_NativeEntryPoint_signature, "Ljdk/internal/invoke/NativeEntryPoint;") \
template(jdk_incubator_foreign_MemoryAccess, "jdk/incubator/foreign/MemoryAccess") \
\ \
/* Support for JVMCI */ \ /* Support for JVMCI */ \
JVMCI_VM_SYMBOLS_DO(template, do_alias) \ JVMCI_VM_SYMBOLS_DO(template, do_alias) \

View file

@ -1586,18 +1586,6 @@ bool MethodData::profile_unsafe(const methodHandle& m, int bci) {
return false; return false;
} }
bool MethodData::profile_memory_access(const methodHandle& m, int bci) {
Bytecode_invoke inv(m , bci);
if (inv.is_invokestatic()) {
if (inv.klass() == vmSymbols::jdk_incubator_foreign_MemoryAccess()) {
if (inv.name()->starts_with("get") || inv.name()->starts_with("set")) {
return true;
}
}
}
return false;
}
int MethodData::profile_arguments_flag() { int MethodData::profile_arguments_flag() {
return TypeProfileLevel % 10; return TypeProfileLevel % 10;
} }
@ -1627,10 +1615,6 @@ bool MethodData::profile_arguments_for_invoke(const methodHandle& m, int bci) {
return true; return true;
} }
if (profile_memory_access(m, bci)) {
return true;
}
assert(profile_arguments_jsr292_only(), "inconsistent"); assert(profile_arguments_jsr292_only(), "inconsistent");
return profile_jsr292(m, bci); return profile_jsr292(m, bci);
} }

View file

@ -174,6 +174,7 @@ ProgrammableUpcallHandler::ProgrammableUpcallHandler() {
} }
void ProgrammableUpcallHandler::handle_uncaught_exception(oop exception) { void ProgrammableUpcallHandler::handle_uncaught_exception(oop exception) {
ResourceMark rm;
// Based on CATCH macro // Based on CATCH macro
tty->print_cr("Uncaught exception:"); tty->print_cr("Uncaught exception:");
exception->print(); exception->print();

View file

@ -68,6 +68,7 @@ import jdk.internal.org.objectweb.asm.ModuleVisitor;
import jdk.internal.org.objectweb.asm.Opcodes; import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection; import jdk.internal.reflect.Reflection;
import jdk.internal.vm.annotation.Stable;
import sun.security.util.SecurityConstants; import sun.security.util.SecurityConstants;
/** /**
@ -110,7 +111,8 @@ public final class Module implements AnnotatedElement {
private final ModuleDescriptor descriptor; private final ModuleDescriptor descriptor;
// true, if this module allows restricted native access // true, if this module allows restricted native access
private volatile boolean enableNativeAccess; @Stable
private boolean enableNativeAccess;
/** /**
* Creates a new named Module. The resulting Module will be defined to the * Creates a new named Module. The resulting Module will be defined to the

View file

@ -1667,6 +1667,10 @@ abstract class MethodHandleImpl {
return caller.makeHiddenClassDefiner(name, bytes, Set.of()).defineClassAsLookup(initialize, classData); return caller.makeHiddenClassDefiner(name, bytes, Set.of()).defineClassAsLookup(initialize, classData);
} }
@Override
public Class<?>[] exceptionTypes(MethodHandle handle) {
return VarHandles.exceptionTypes(handle);
}
}); });
} }
@ -2273,15 +2277,16 @@ abstract class MethodHandleImpl {
// Indexes into constant method handles: // Indexes into constant method handles:
static final int static final int
MH_cast = 0, MH_cast = 0,
MH_selectAlternative = 1, MH_selectAlternative = 1,
MH_countedLoopPred = 2, MH_countedLoopPred = 2,
MH_countedLoopStep = 3, MH_countedLoopStep = 3,
MH_initIterator = 4, MH_initIterator = 4,
MH_iteratePred = 5, MH_iteratePred = 5,
MH_iterateNext = 6, MH_iterateNext = 6,
MH_Array_newInstance = 7, MH_Array_newInstance = 7,
MH_LIMIT = 8; MH_VarHandles_handleCheckedExceptions = 8,
MH_LIMIT = 9;
static MethodHandle getConstantHandle(int idx) { static MethodHandle getConstantHandle(int idx) {
MethodHandle handle = HANDLES[idx]; MethodHandle handle = HANDLES[idx];
@ -2331,6 +2336,9 @@ abstract class MethodHandleImpl {
case MH_Array_newInstance: case MH_Array_newInstance:
return IMPL_LOOKUP.findStatic(Array.class, "newInstance", return IMPL_LOOKUP.findStatic(Array.class, "newInstance",
MethodType.methodType(Object.class, Class.class, int.class)); MethodType.methodType(Object.class, Class.class, int.class));
case MH_VarHandles_handleCheckedExceptions:
return IMPL_LOOKUP.findStatic(VarHandles.class, "handleCheckedExceptions",
MethodType.methodType(void.class, Throwable.class));
} }
} catch (ReflectiveOperationException ex) { } catch (ReflectiveOperationException ex) {
throw newInternalError(ex); throw newInternalError(ex);

View file

@ -31,12 +31,9 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
@ -45,8 +42,6 @@ import java.util.stream.Stream;
import static java.lang.invoke.MethodHandleStatics.UNSAFE; import static java.lang.invoke.MethodHandleStatics.UNSAFE;
import static java.lang.invoke.MethodHandleStatics.VAR_HANDLE_IDENTITY_ADAPT; import static java.lang.invoke.MethodHandleStatics.VAR_HANDLE_IDENTITY_ADAPT;
import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException; import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
final class VarHandles { final class VarHandles {
@ -359,13 +354,13 @@ final class VarHandles {
return target; return target;
} }
public static VarHandle filterValue(VarHandle target, MethodHandle filterToTarget, MethodHandle filterFromTarget) { public static VarHandle filterValue(VarHandle target, MethodHandle pFilterToTarget, MethodHandle pFilterFromTarget) {
Objects.requireNonNull(target); Objects.requireNonNull(target);
Objects.requireNonNull(filterToTarget); Objects.requireNonNull(pFilterToTarget);
Objects.requireNonNull(filterFromTarget); Objects.requireNonNull(pFilterFromTarget);
//check that from/to filters do not throw checked exceptions //check that from/to filters do not throw checked exceptions
noCheckedExceptions(filterToTarget); MethodHandle filterToTarget = adaptForCheckedExceptions(pFilterToTarget);
noCheckedExceptions(filterFromTarget); MethodHandle filterFromTarget = adaptForCheckedExceptions(pFilterFromTarget);
List<Class<?>> newCoordinates = new ArrayList<>(); List<Class<?>> newCoordinates = new ArrayList<>();
List<Class<?>> additionalCoordinates = new ArrayList<>(); List<Class<?>> additionalCoordinates = new ArrayList<>();
@ -473,8 +468,9 @@ final class VarHandles {
List<Class<?>> newCoordinates = new ArrayList<>(targetCoordinates); List<Class<?>> newCoordinates = new ArrayList<>(targetCoordinates);
for (int i = 0 ; i < filters.length ; i++) { for (int i = 0 ; i < filters.length ; i++) {
noCheckedExceptions(filters[i]); MethodHandle filter = Objects.requireNonNull(filters[i]);
MethodType filterType = filters[i].type(); filter = adaptForCheckedExceptions(filter);
MethodType filterType = filter.type();
if (filterType.parameterCount() != 1) { if (filterType.parameterCount() != 1) {
throw newIllegalArgumentException("Invalid filter type " + filterType); throw newIllegalArgumentException("Invalid filter type " + filterType);
} else if (newCoordinates.get(pos + i) != filterType.returnType()) { } else if (newCoordinates.get(pos + i) != filterType.returnType()) {
@ -564,10 +560,10 @@ final class VarHandles {
return adjustedType; return adjustedType;
} }
public static VarHandle collectCoordinates(VarHandle target, int pos, MethodHandle filter) { public static VarHandle collectCoordinates(VarHandle target, int pos, MethodHandle pFilter) {
Objects.requireNonNull(target); Objects.requireNonNull(target);
Objects.requireNonNull(filter); Objects.requireNonNull(pFilter);
noCheckedExceptions(filter); MethodHandle filter = adaptForCheckedExceptions(pFilter);
List<Class<?>> targetCoordinates = target.coordinateTypes(); List<Class<?>> targetCoordinates = target.coordinateTypes();
if (pos < 0 || pos >= targetCoordinates.size()) { if (pos < 0 || pos >= targetCoordinates.size()) {
@ -604,42 +600,55 @@ final class VarHandles {
(mode, modeHandle) -> MethodHandles.dropArguments(modeHandle, 1 + pos, valueTypes)); (mode, modeHandle) -> MethodHandles.dropArguments(modeHandle, 1 + pos, valueTypes));
} }
private static void noCheckedExceptions(MethodHandle handle) { private static MethodHandle adaptForCheckedExceptions(MethodHandle target) {
Class<?>[] exceptionTypes = exceptionTypes(target);
if (exceptionTypes != null) { // exceptions known
if (Stream.of(exceptionTypes).anyMatch(VarHandles::isCheckedException)) {
throw newIllegalArgumentException("Cannot adapt a var handle with a method handle which throws checked exceptions");
}
return target; // no adaptation needed
} else {
MethodHandle handler = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_VarHandles_handleCheckedExceptions);
MethodHandle zero = MethodHandles.zero(target.type().returnType()); // dead branch
handler = MethodHandles.collectArguments(zero, 0, handler);
return MethodHandles.catchException(target, Throwable.class, handler);
}
}
static void handleCheckedExceptions(Throwable throwable) throws Throwable {
if (isCheckedException(throwable.getClass())) {
throw new IllegalStateException("Adapter handle threw checked exception", throwable);
}
throw throwable;
}
static Class<?>[] exceptionTypes(MethodHandle handle) {
if (handle instanceof DirectMethodHandle directHandle) { if (handle instanceof DirectMethodHandle directHandle) {
byte refKind = directHandle.member.getReferenceKind(); byte refKind = directHandle.member.getReferenceKind();
MethodHandleInfo info = new InfoFromMemberName( MethodHandleInfo info = new InfoFromMemberName(
MethodHandles.Lookup.IMPL_LOOKUP, MethodHandles.Lookup.IMPL_LOOKUP,
directHandle.member, directHandle.member,
refKind); refKind);
final Class<?>[] exceptionTypes;
if (MethodHandleNatives.refKindIsMethod(refKind)) { if (MethodHandleNatives.refKindIsMethod(refKind)) {
exceptionTypes = info.reflectAs(Method.class, MethodHandles.Lookup.IMPL_LOOKUP) return info.reflectAs(Method.class, MethodHandles.Lookup.IMPL_LOOKUP)
.getExceptionTypes(); .getExceptionTypes();
} else if (MethodHandleNatives.refKindIsField(refKind)) { } else if (MethodHandleNatives.refKindIsField(refKind)) {
exceptionTypes = null; return new Class<?>[0];
} else if (MethodHandleNatives.refKindIsConstructor(refKind)) { } else if (MethodHandleNatives.refKindIsConstructor(refKind)) {
exceptionTypes = info.reflectAs(Constructor.class, MethodHandles.Lookup.IMPL_LOOKUP) return info.reflectAs(Constructor.class, MethodHandles.Lookup.IMPL_LOOKUP)
.getExceptionTypes(); .getExceptionTypes();
} else { } else {
throw new AssertionError("Cannot get here"); throw new AssertionError("Cannot get here");
} }
if (exceptionTypes != null) {
if (Stream.of(exceptionTypes).anyMatch(VarHandles::isCheckedException)) {
throw newIllegalArgumentException("Cannot adapt a var handle with a method handle which throws checked exceptions");
}
}
} else if (handle instanceof DelegatingMethodHandle) { } else if (handle instanceof DelegatingMethodHandle) {
noCheckedExceptions(((DelegatingMethodHandle)handle).getTarget()); return exceptionTypes(((DelegatingMethodHandle)handle).getTarget());
} else { } else if (handle instanceof NativeMethodHandle) {
//bound return new Class<?>[0];
BoundMethodHandle boundHandle = (BoundMethodHandle)handle;
for (int i = 0 ; i < boundHandle.fieldCount() ; i++) {
Object arg = boundHandle.arg(i);
if (arg instanceof MethodHandle){
noCheckedExceptions((MethodHandle) arg);
}
}
} }
assert handle instanceof BoundMethodHandle : "Unexpected handle type: " + handle;
// unknown
return null;
} }
private static boolean isCheckedException(Class<?> clazz) { private static boolean isCheckedException(Class<?> clazz) {

View file

@ -820,7 +820,7 @@ public abstract class Buffer {
} }
@Override @Override
public Scope.Handle acquireScope(Buffer buffer, boolean async) { public Runnable acquireScope(Buffer buffer, boolean async) {
var scope = buffer.scope(); var scope = buffer.scope();
if (scope == null) { if (scope == null) {
return null; return null;
@ -828,7 +828,8 @@ public abstract class Buffer {
if (async && scope.ownerThread() != null) { if (async && scope.ownerThread() != null) {
throw new IllegalStateException("Confined scope not supported"); throw new IllegalStateException("Confined scope not supported");
} }
return scope.acquire(); scope.acquire0();
return scope::release0;
} }
@Override @Override

View file

@ -181,4 +181,11 @@ public interface JavaLangInvokeAccess {
* The given bytes is trusted. * The given bytes is trusted.
*/ */
Lookup defineHiddenClassWithClassData(Lookup caller, String name, byte[] bytes, Object classData, boolean initialize); Lookup defineHiddenClassWithClassData(Lookup caller, String name, byte[] bytes, Object classData, boolean initialize);
/**
* A best-effort method that tries to find any exceptions thrown by the given method handle.
* @param handle the handle to check
* @return an array of exceptions, or {@code null}.
*/
Class<?>[] exceptionTypes(MethodHandle handle);
} }

View file

@ -92,7 +92,7 @@ public interface JavaNioAccess {
* scope handle. Null is returned if the buffer has no scope, or * scope handle. Null is returned if the buffer has no scope, or
* acquiring is not required to guarantee safety. * acquiring is not required to guarantee safety.
*/ */
Scope.Handle acquireScope(Buffer buffer, boolean async); Runnable acquireScope(Buffer buffer, boolean async);
/** /**
* Used by {@code jdk.internal.foreign.MappedMemorySegmentImpl} and byte buffer var handle views. * Used by {@code jdk.internal.foreign.MappedMemorySegmentImpl} and byte buffer var handle views.

View file

@ -103,19 +103,13 @@ public class ScopedMemoryAccess {
*/ */
public interface Scope { public interface Scope {
interface Handle {
Scope scope();
}
void checkValidState(); void checkValidState();
Thread ownerThread(); Thread ownerThread();
boolean isImplicit(); void acquire0();
Handle acquire(); void release0();
void release(Handle handle);
/** /**
* Error thrown when memory access fails because the memory has already been released. * Error thrown when memory access fails because the memory has already been released.

View file

@ -32,6 +32,7 @@ import java.util.Objects;
import java.util.Set; import java.util.Set;
import jdk.internal.access.SharedSecrets; import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.VM; import jdk.internal.misc.VM;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.IntrinsicCandidate; import jdk.internal.vm.annotation.IntrinsicCandidate;
/** Common utility routines used by both java.lang and /** Common utility routines used by both java.lang and
@ -106,6 +107,7 @@ public class Reflection {
} }
} }
@ForceInline
public static void ensureNativeAccess(Class<?> currentClass) { public static void ensureNativeAccess(Class<?> currentClass) {
Module module = currentClass.getModule(); Module module = currentClass.getModule();
if (!SharedSecrets.getJavaLangAccess().isEnableNativeAccess(module)) { if (!SharedSecrets.getJavaLangAccess().isEnableNativeAccess(module)) {

View file

@ -475,15 +475,15 @@ public class IOUtil {
private static final JavaNioAccess NIO_ACCESS = SharedSecrets.getJavaNioAccess(); private static final JavaNioAccess NIO_ACCESS = SharedSecrets.getJavaNioAccess();
static Scope.Handle acquireScope(ByteBuffer bb, boolean async) { static Runnable acquireScope(ByteBuffer bb, boolean async) {
return NIO_ACCESS.acquireScope(bb, async); return NIO_ACCESS.acquireScope(bb, async);
} }
private static void releaseScope(Scope.Handle handle) { private static void releaseScope(Runnable handle) {
if (handle == null) if (handle == null)
return; return;
try { try {
handle.scope().release(handle); handle.run();
} catch (Exception e) { } catch (Exception e) {
throw new IllegalStateException(e); throw new IllegalStateException(e);
} }
@ -535,11 +535,11 @@ public class IOUtil {
} }
} }
static record Releaser(Scope.Handle handle) implements Runnable { static record Releaser(Runnable handle) implements Runnable {
Releaser { Objects.requireNonNull(handle) ; } Releaser { Objects.requireNonNull(handle) ; }
@Override public void run() { releaseScope(handle); } @Override public void run() { releaseScope(handle); }
static Runnable of(Scope.Handle handle) { return new Releaser(handle); } static Runnable of(Runnable handle) { return new Releaser(handle); }
static Runnable ofNullable(Scope.Handle handle) { static Runnable ofNullable(Runnable handle) {
if (handle == null) if (handle == null)
return () -> { }; return () -> { };
return new Releaser(handle); return new Releaser(handle);

View file

@ -25,26 +25,23 @@
*/ */
package jdk.incubator.foreign; package jdk.incubator.foreign;
import jdk.internal.foreign.Utils;
import jdk.internal.vm.annotation.Stable;
import java.lang.constant.ClassDesc; import java.lang.constant.ClassDesc;
import java.lang.constant.Constable;
import java.lang.constant.ConstantDesc; import java.lang.constant.ConstantDesc;
import java.lang.constant.ConstantDescs;
import java.lang.constant.DirectMethodHandleDesc; import java.lang.constant.DirectMethodHandleDesc;
import java.lang.constant.DynamicConstantDesc; import java.lang.constant.DynamicConstantDesc;
import java.lang.constant.MethodHandleDesc; import java.lang.constant.MethodHandleDesc;
import java.lang.constant.MethodTypeDesc; import java.lang.constant.MethodTypeDesc;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalLong; import java.util.OptionalLong;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.lang.constant.ConstantDescs.BSM_GET_STATIC_FINAL; import static java.lang.constant.ConstantDescs.BSM_GET_STATIC_FINAL;
import static java.lang.constant.ConstantDescs.BSM_INVOKE; import static java.lang.constant.ConstantDescs.BSM_INVOKE;
import static java.lang.constant.ConstantDescs.CD_Class;
import static java.lang.constant.ConstantDescs.CD_String; import static java.lang.constant.ConstantDescs.CD_String;
import static java.lang.constant.ConstantDescs.CD_long; import static java.lang.constant.ConstantDescs.CD_long;
@ -52,50 +49,33 @@ abstract non-sealed class AbstractLayout implements MemoryLayout {
private final OptionalLong size; private final OptionalLong size;
final long alignment; final long alignment;
final Map<String, Constable> attributes; private final Optional<String> name;
@Stable
long cachedSize;
public AbstractLayout(OptionalLong size, long alignment, Map<String, Constable> attributes) { public AbstractLayout(OptionalLong size, long alignment, Optional<String> name) {
this.size = size; this.size = size;
this.alignment = alignment; this.alignment = alignment;
this.attributes = Collections.unmodifiableMap(attributes); this.name = name;
} }
@Override @Override
public AbstractLayout withName(String name) { public AbstractLayout withName(String name) {
Objects.requireNonNull(name); Objects.requireNonNull(name);
return withAttribute(LAYOUT_NAME, name); return dup(alignment, Optional.of(name));
} }
@Override @Override
public final Optional<String> name() { public final Optional<String> name() {
return attribute(LAYOUT_NAME).map(String.class::cast); return name;
} }
@Override abstract AbstractLayout dup(long alignment, Optional<String> name);
public Optional<Constable> attribute(String name) {
Objects.requireNonNull(name);
return Optional.ofNullable(attributes.get(name));
}
@Override
public Stream<String> attributes() {
return attributes.keySet().stream();
}
@Override
public AbstractLayout withAttribute(String name, Constable value) {
Objects.requireNonNull(name);
Map<String, Constable> newAttributes = new HashMap<>(attributes);
newAttributes.put(name, value);
return dup(alignment, newAttributes);
}
abstract AbstractLayout dup(long alignment, Map<String, Constable> annos);
@Override @Override
public AbstractLayout withBitAlignment(long alignmentBits) { public AbstractLayout withBitAlignment(long alignmentBits) {
checkAlignment(alignmentBits); checkAlignment(alignmentBits);
return dup(alignmentBits, attributes); return dup(alignmentBits, name);
} }
void checkAlignment(long alignmentBitCount) { void checkAlignment(long alignmentBitCount) {
@ -120,6 +100,15 @@ abstract non-sealed class AbstractLayout implements MemoryLayout {
return alignment; return alignment;
} }
@Override
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 @Override
public boolean hasSize() { public boolean hasSize() {
return size.isPresent(); return size.isPresent();
@ -145,11 +134,6 @@ abstract non-sealed class AbstractLayout implements MemoryLayout {
if (!hasNaturalAlignment()) { if (!hasNaturalAlignment()) {
s = alignment + "%" + s; s = alignment + "%" + s;
} }
if (!attributes.isEmpty()) {
s += attributes.entrySet().stream()
.map(e -> e.getKey() + "=" + e.getValue())
.collect(Collectors.joining(",", "[", "]"));
}
return s; return s;
} }
@ -158,9 +142,9 @@ abstract non-sealed class AbstractLayout implements MemoryLayout {
desc = DynamicConstantDesc.ofNamed(BSM_INVOKE, "withBitAlignment", desc.constantType(), MH_WITH_BIT_ALIGNMENT, desc = DynamicConstantDesc.ofNamed(BSM_INVOKE, "withBitAlignment", desc.constantType(), MH_WITH_BIT_ALIGNMENT,
desc, bitAlignment()); desc, bitAlignment());
} }
for (var e : attributes.entrySet()) { if (name().isPresent()) {
desc = DynamicConstantDesc.ofNamed(BSM_INVOKE, "withAttribute", desc.constantType(), MH_WITH_ATTRIBUTE, desc = DynamicConstantDesc.ofNamed(BSM_INVOKE, "withName", desc.constantType(), MH_WITH_NAME,
desc, e.getKey(), e.getValue().describeConstable().orElseThrow()); desc, name().get().describeConstable().orElseThrow());
} }
return desc; return desc;
@ -177,7 +161,7 @@ abstract non-sealed class AbstractLayout implements MemoryLayout {
@Override @Override
public int hashCode() { public int hashCode() {
return attributes.hashCode() << Long.hashCode(alignment); return name.hashCode() << Long.hashCode(alignment);
} }
@Override @Override
@ -190,7 +174,7 @@ abstract non-sealed class AbstractLayout implements MemoryLayout {
return false; return false;
} }
return Objects.equals(attributes, ((AbstractLayout) other).attributes) && return Objects.equals(name, ((AbstractLayout) other).name) &&
Objects.equals(alignment, ((AbstractLayout) other).alignment); Objects.equals(alignment, ((AbstractLayout) other).alignment);
} }
@ -208,8 +192,6 @@ abstract non-sealed class AbstractLayout implements MemoryLayout {
static final ClassDesc CD_FUNCTION_DESC = FunctionDescriptor.class.describeConstable().get(); static final ClassDesc CD_FUNCTION_DESC = FunctionDescriptor.class.describeConstable().get();
static final ClassDesc CD_Constable = Constable.class.describeConstable().get();
static final ConstantDesc BIG_ENDIAN = DynamicConstantDesc.ofNamed(BSM_GET_STATIC_FINAL, "BIG_ENDIAN", CD_BYTEORDER, CD_BYTEORDER); static final ConstantDesc BIG_ENDIAN = DynamicConstantDesc.ofNamed(BSM_GET_STATIC_FINAL, "BIG_ENDIAN", CD_BYTEORDER, CD_BYTEORDER);
static final ConstantDesc LITTLE_ENDIAN = DynamicConstantDesc.ofNamed(BSM_GET_STATIC_FINAL, "LITTLE_ENDIAN", CD_BYTEORDER, CD_BYTEORDER); static final ConstantDesc LITTLE_ENDIAN = DynamicConstantDesc.ofNamed(BSM_GET_STATIC_FINAL, "LITTLE_ENDIAN", CD_BYTEORDER, CD_BYTEORDER);
@ -217,9 +199,6 @@ abstract non-sealed class AbstractLayout implements MemoryLayout {
static final MethodHandleDesc MH_PADDING = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "paddingLayout", static final MethodHandleDesc MH_PADDING = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "paddingLayout",
MethodTypeDesc.of(CD_MEMORY_LAYOUT, CD_long)); MethodTypeDesc.of(CD_MEMORY_LAYOUT, CD_long));
static final MethodHandleDesc MH_VALUE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "valueLayout",
MethodTypeDesc.of(CD_VALUE_LAYOUT, CD_long, CD_BYTEORDER));
static final MethodHandleDesc MH_SIZED_SEQUENCE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "sequenceLayout", static final MethodHandleDesc MH_SIZED_SEQUENCE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "sequenceLayout",
MethodTypeDesc.of(CD_SEQUENCE_LAYOUT, CD_long, CD_MEMORY_LAYOUT)); MethodTypeDesc.of(CD_SEQUENCE_LAYOUT, CD_long, CD_MEMORY_LAYOUT));
@ -232,6 +211,9 @@ abstract non-sealed class AbstractLayout implements MemoryLayout {
static final MethodHandleDesc MH_UNION = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "unionLayout", static final MethodHandleDesc MH_UNION = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "unionLayout",
MethodTypeDesc.of(CD_GROUP_LAYOUT, CD_MEMORY_LAYOUT.arrayType())); MethodTypeDesc.of(CD_GROUP_LAYOUT, CD_MEMORY_LAYOUT.arrayType()));
static final MethodHandleDesc MH_VALUE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "valueLayout",
MethodTypeDesc.of(CD_VALUE_LAYOUT, CD_Class, CD_BYTEORDER));
static final MethodHandleDesc MH_VOID_FUNCTION = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.STATIC, CD_FUNCTION_DESC, "ofVoid", static final MethodHandleDesc MH_VOID_FUNCTION = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.STATIC, CD_FUNCTION_DESC, "ofVoid",
MethodTypeDesc.of(CD_FUNCTION_DESC, CD_MEMORY_LAYOUT.arrayType())); MethodTypeDesc.of(CD_FUNCTION_DESC, CD_MEMORY_LAYOUT.arrayType()));
@ -241,6 +223,6 @@ abstract non-sealed class AbstractLayout implements MemoryLayout {
static final MethodHandleDesc MH_WITH_BIT_ALIGNMENT = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_VIRTUAL, CD_MEMORY_LAYOUT, "withBitAlignment", static final MethodHandleDesc MH_WITH_BIT_ALIGNMENT = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_VIRTUAL, CD_MEMORY_LAYOUT, "withBitAlignment",
MethodTypeDesc.of(CD_MEMORY_LAYOUT, CD_long)); MethodTypeDesc.of(CD_MEMORY_LAYOUT, CD_long));
static final MethodHandleDesc MH_WITH_ATTRIBUTE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_VIRTUAL, CD_MEMORY_LAYOUT, "withAttribute", static final MethodHandleDesc MH_WITH_NAME = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_VIRTUAL, CD_MEMORY_LAYOUT, "withName",
MethodTypeDesc.of(CD_MEMORY_LAYOUT, CD_String, CD_Constable)); MethodTypeDesc.of(CD_MEMORY_LAYOUT, CD_String));
} }

View file

@ -27,16 +27,21 @@ package jdk.incubator.foreign;
/** /**
* Represents a type which is <em>addressable</em>. An addressable type is one which can be projected down to * Represents a type which is <em>addressable</em>. An addressable type is one which can be projected down to
* a memory address instance (see {@link #address()}). Examples of addressable types are {@link MemorySegment}, * a {@linkplain #address() memory address}. Examples of addressable types are {@link MemorySegment},
* {@link MemoryAddress} and {@link CLinker.VaList}. * {@link MemoryAddress}, {@link VaList} and {@link NativeSymbol}.
* <p>
* The {@link Addressable} type is used by the {@link CLinker C linker} to model the types of
* {@link CLinker#downcallHandle(FunctionDescriptor) downcall handle} parameters that must be passed <em>by reference</em>
* (e.g. memory addresses, va lists and upcall stubs).
* *
* @implSpec * @implSpec
* Implementations of this interface are <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>. * Implementations of this interface are <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
*/ */
public interface Addressable { public sealed interface Addressable permits MemorySegment, MemoryAddress, NativeSymbol, VaList {
/** /**
* Map this object into a {@link MemoryAddress} instance. * Returns the memory address associated with this addressable.
* @return the {@link MemoryAddress} instance associated with this object. * @return The memory address associated with this addressable.
*/ */
MemoryAddress address(); MemoryAddress address();
} }

View file

@ -25,93 +25,123 @@
*/ */
package jdk.incubator.foreign; package jdk.incubator.foreign;
import jdk.internal.foreign.AbstractCLinker;
import jdk.internal.foreign.NativeMemorySegmentImpl;
import jdk.internal.foreign.PlatformLayouts;
import jdk.internal.foreign.SystemLookup; import jdk.internal.foreign.SystemLookup;
import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64VaList; import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker;
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64VaList; import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker;
import jdk.internal.foreign.abi.x64.sysv.SysVVaList; import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
import jdk.internal.foreign.abi.x64.windows.WinVaList; import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection; import jdk.internal.reflect.Reflection;
import java.lang.constant.Constable;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import java.nio.charset.Charset; import java.util.Optional;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.function.Consumer;
import static jdk.internal.foreign.PlatformLayouts.*;
/** /**
* A C linker implements the C Application Binary Interface (ABI) calling conventions. * A C linker implements the C Application Binary Interface (ABI) calling conventions.
* Instances of this interface can be used to link foreign functions in native libraries that * Instances of this interface can be used to link foreign functions in native libraries that
* follow the JVM's target platform C ABI. * follow the JVM's target platform C ABI. A C linker provides two main capabilities: first, it allows Java code
* to <em>link</em> foreign functions into a so called <em>downcall method handle</em>; secondly, it allows
* native code to call Java method handles via the generation of <em>upcall stubs</em>.
* <p> * <p>
* Linking a foreign function is a process which requires two components: a method type, and * On unsupported platforms this class will fail to initialize with an {@link ExceptionInInitializerError}.
* a function descriptor. The method type, consists of a set of <em>carrier</em> types, which, together,
* specify the Java signature which clients must adhere to when calling the underlying foreign function.
* The function descriptor contains a set of memory layouts which, together, specify the foreign function
* signature and classification information (via a custom layout attributes, see {@link TypeKind}), so that linking can take place.
* <p> * <p>
* Clients of this API can build function descriptors using the predefined memory layout constants * Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null}
* (based on a subset of the built-in types provided by the C language), found in this interface; alternatively,
* they can also decorate existing value layouts using the required {@link TypeKind} classification attribute
* (this can be done using the {@link MemoryLayout#withAttribute(String, Constable)} method). A failure to do so might
* result in linkage errors, given that linking requires additional classification information to determine, for instance,
* how arguments should be loaded into registers during a foreign function call.
* <p>
* Implementations of this interface support the following primitive carrier types:
* {@code byte}, {@code short}, {@code char}, {@code int}, {@code long}, {@code float},
* and {@code double}, as well as {@link MemoryAddress} for passing pointers, and
* {@link MemorySegment} for passing structs and unions. Finally, the {@link VaList}
* carrier type can be used to match the native {@code va_list} type.
* <p>
* For the linking process to be successful, some requirements must be satisfied; if {@code M} and {@code F} are
* the method type (obtained after dropping any prefix arguments) and the function descriptor, respectively,
* used during the linking process, then it must be that:
* <ul>
* <li>The arity of {@code M} is the same as that of {@code F};</li>
* <li>If the return type of {@code M} is {@code void}, then {@code F} should have no return layout
* (see {@link FunctionDescriptor#ofVoid(MemoryLayout...)});</li>
* <li>for each pair of carrier type {@code C} and layout {@code L} in {@code M} and {@code F}, respectively,
* where {@code C} and {@code L} refer to the same argument, or to the return value, the following conditions must hold:
* <ul>
* <li>If {@code C} is a primitve type, then {@code L} must be a {@code ValueLayout}, and the size of the layout must match
* that of the carrier type (see {@link Integer#SIZE} and similar fields in other primitive wrapper classes);</li>
* <li>If {@code C} is {@code MemoryAddress.class}, then {@code L} must be a {@code ValueLayout}, and its size must match
* the platform's address size (see {@link MemoryLayouts#ADDRESS}). For this purpose, the {@link CLinker#C_POINTER} layout
* constant can be used;</li>
* <li>If {@code C} is {@code MemorySegment.class}, then {@code L} must be a {@code GroupLayout}</li>
* <li>If {@code C} is {@code VaList.class}, then {@code L} must be {@link CLinker#C_VA_LIST}</li>
* </ul>
* </li>
* </ul>
*
* <p>Variadic functions, declared in C either with a trailing ellipses ({@code ...}) at the end of the formal parameter
* list or with an empty formal parameter list, are not supported directly. It is not possible to create a method handle
* that takes a variable number of arguments, and neither is it possible to create an upcall stub wrapping a method
* handle that accepts a variable number of arguments. However, for downcalls only, it is possible to link a native
* variadic function by using a <em>specialized</em> method type and function descriptor: for each argument that is to be
* passed as a variadic argument, an explicit, additional, carrier type and memory layout must be present in the method type and
* function descriptor objects passed to the linker. Furthermore, as memory layouts corresponding to variadic arguments in
* a function descriptor must contain additional classification information, it is required that
* {@link #asVarArg(MemoryLayout)} is used to create the memory layouts for each parameter corresponding to a variadic
* argument in a specialized function descriptor.
*
* <p>On unsupported platforms this class will fail to initialize with an {@link ExceptionInInitializerError}.
*
* <p> Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null}
* elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown. </p> * elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown. </p>
* *
* <h2><a id = "downcall-method-handles">Downcall method handles</a></h2>
* <p>
* {@linkplain #downcallHandle(FunctionDescriptor) 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 native function.
* The Java {@link 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:
* <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:
* <ul>
* <li>If the downcall method handle is created {@linkplain #downcallHandle(FunctionDescriptor) without specifying a native symbol},
* the downcall method handle type features a leading parameter of type {@link NativeSymbol}, from which the
* address of the target native function can be derived.</li>
* <li>If the function descriptor's return layout is a group layout, the resulting downcall method handle accepts
* an additional leading parameter of type {@link SegmentAllocator}, which is used by the linker runtime to allocate the
* memory region associated with the struct returned by the downcall method handle.</li>
* </ul>
* <p>Variadic functions, declared in C either 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 native
* 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,
* if the foreign library allows it, clients might also be able to interact with variable arity methods
* by passing a trailing parameter of type {@link VaList}.
*
* <h2><a id = "upcall-stubs">Upcall stubs</a></h2>
*
* {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, ResourceScope) 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 match the Java {@link 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>
* Upcall stubs are modelled by instances of type {@link NativeSymbol}; upcall stubs can be passed by reference to other
* downcall method handles (as {@link NativeSymbol} implements the {@link Addressable} interface) and,
* when no longer required, they can be {@link ResourceScope#close() released}, via their {@linkplain NativeSymbol#scope() scope}.
*
* <h2>System lookup</h2>
*
* This class implements the {@link SymbolLookup} interface; as such clients can {@linkplain #lookup(String) lookup} symbols
* in the standard libraries associated with this linker. The set of symbols available for lookup is unspecified,
* as it depends on the platform and on the operating system.
*
* <h2>Safety considerations</h2>
*
* Obtaining downcall method handle is intrinsically unsafe. A symbol in a native library does not, in general,
* contain enough signature information (e.g. arity and types of native function parameters). As a consequence,
* the linker runtime cannot validate linkage requests. When a client interacts with a downcall method handle obtained
* through an invalid linkage request (e.g. by specifying a function descriptor featuring too many argument layouts),
* the result of such interaction is unspecified and can lead to JVM crashes. On downcall handle invocation,
* the linker runtime guarantees the following for any argument that is a memory resource {@code R} (of type {@link MemorySegment},
* {@link NativeSymbol} or {@link VaList}):
* <ul>
* <li>The resource scope of {@code R} is {@linkplain ResourceScope#isAlive() alive}. Otherwise, the invocation throws
* {@link IllegalStateException};</li>
* <li>The invocation occurs in same thread as the one {@link ResourceScope#ownerThread() owning} the resource scope of {@code R},
* if said scope is confined. Otherwise, the invocation throws {@link IllegalStateException}; and</li>
* <li>The scope of {@code R} is {@linkplain ResourceScope#keepAlive(ResourceScope) kept alive} (and cannot be closed) during the invocation.
*</ul>
* <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 native 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 native address}, 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.
*
* @implSpec * @implSpec
* Implementations of this interface are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>. * Implementations of this interface are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
*/ */
public sealed interface CLinker permits AbstractCLinker { public sealed interface CLinker extends SymbolLookup permits Windowsx64Linker, SysVx64Linker, LinuxAArch64Linker, MacOsAArch64Linker {
/** /**
* Returns the C linker for the current platform. * Returns the C linker for the current platform.
@ -127,96 +157,75 @@ public sealed interface CLinker permits AbstractCLinker {
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module. * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/ */
@CallerSensitive @CallerSensitive
static CLinker getInstance() { static CLinker systemCLinker() {
Reflection.ensureNativeAccess(Reflection.getCallerClass()); Reflection.ensureNativeAccess(Reflection.getCallerClass());
return SharedUtils.getSystemLinker(); return SharedUtils.getSystemLinker();
} }
/** /**
* Obtains a system lookup which is suitable to find symbols in the standard C libraries. The set of symbols * Lookup a symbol in the standard libraries associated with this linker.
* available for lookup is unspecified, as it depends on the platform and on the operating system. * The set of symbols available for lookup is unspecified, as it depends on the platform and on the operating system.
* <p> * @return a symbol in the standard libraries associated with this linker.
* 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 a system-specific library lookup which is suitable to find symbols in the standard C libraries.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/ */
@CallerSensitive @Override
static SymbolLookup systemLookup() { default Optional<NativeSymbol> lookup(String name) {
Reflection.ensureNativeAccess(Reflection.getCallerClass()); return SystemLookup.getInstance().lookup(name);
return SystemLookup.getInstance();
} }
/** /**
* Obtains a foreign method handle, with the given type and featuring the given function descriptor, * Obtains a foreign method handle, with the given type and featuring the given function descriptor,
* which can be used to call a target foreign function at the given address. * which can be used to call a target foreign function at the address in the given native symbol.
* <p> * <p>
* If the provided method type's return type is {@code MemorySegment}, then the resulting method handle features * 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 * an additional prefix parameter, of type {@link SegmentAllocator}, which will be used by the linker runtime
* to allocate structs returned by-value. * to allocate structs returned by-value.
* <p>
* Calling this method is equivalent to the following code:
<blockquote><pre>{@code
linker.downcallHandle(function).bindTo(symbol);
}</pre></blockquote>
* *
* @param symbol downcall symbol. * @param symbol downcall symbol.
* @param type the method type.
* @param function the function descriptor. * @param function the function descriptor.
* @return the downcall method handle. * @return the downcall method handle. The method handle type is <a href="CLinker.html#downcall-method-handles"><em>inferred</em></a>
* @throws IllegalArgumentException in the case of a method type and function descriptor mismatch, or if the symbol * @throws IllegalArgumentException if the provided descriptor contains either a sequence or a padding layout,
* is {@link MemoryAddress#NULL} * or if the symbol is {@link MemoryAddress#NULL}
* *
* @see SymbolLookup * @see SymbolLookup
*/ */
MethodHandle downcallHandle(Addressable symbol, MethodType type, FunctionDescriptor function); default MethodHandle downcallHandle(NativeSymbol symbol, FunctionDescriptor function) {
SharedUtils.checkSymbol(symbol);
/** return downcallHandle(function).bindTo(symbol);
* Obtain a foreign method handle, with the given type and featuring the given function descriptor, }
* which can be used to call a target foreign function at the given address.
* <p>
* If the provided method type's return type is {@code MemorySegment}, then the provided allocator will be used by
* the linker runtime to allocate structs returned by-value.
*
* @param symbol downcall symbol.
* @param allocator the segment allocator.
* @param type the method type.
* @param function the function descriptor.
* @return the downcall method handle.
* @throws IllegalArgumentException in the case of a method type and function descriptor mismatch, or if the symbol
* is {@link MemoryAddress#NULL}
*
* @see SymbolLookup
*/
MethodHandle downcallHandle(Addressable symbol, SegmentAllocator allocator, MethodType type, FunctionDescriptor function);
/** /**
* Obtains a foreign method handle, with the given type and featuring the given function descriptor, which can be * Obtains a foreign method handle, with the given type and featuring the given function descriptor, which can be
* used to call a target foreign function at an address. * used to call a target foreign function at the address in a dynamically provided native symbol.
* The resulting method handle features a prefix parameter (as the first parameter) corresponding to the address, of * The resulting method handle features a prefix parameter (as the first parameter) corresponding to the foreign function
* type {@link Addressable}. * entry point, of type {@link NativeSymbol}.
* <p> * <p>
* If the provided method type's return type is {@code MemorySegment}, then the resulting method handle features an * 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}), * 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 runtime to allocate structs returned by-value.
* <p> * <p>
* The returned method handle will throw an {@link IllegalArgumentException} if the target address passed to it is * The returned method handle will throw an {@link IllegalArgumentException} if the native symbol passed to it is
* {@link MemoryAddress#NULL}, or a {@link NullPointerException} if the target address is {@code null}. * associated with the {@link MemoryAddress#NULL} address, or a {@link NullPointerException} if the native symbol is {@code null}.
* *
* @param type the method type.
* @param function the function descriptor. * @param function the function descriptor.
* @return the downcall method handle. * @return the downcall method handle. The method handle type is <a href="CLinker.html#downcall-method-handles"><em>inferred</em></a>
* @throws IllegalArgumentException in the case of a method type and function descriptor mismatch. * from the provided function descriptor.
* @throws IllegalArgumentException if the provided descriptor contains either a sequence or a padding layout.
* *
* @see SymbolLookup * @see SymbolLookup
*/ */
MethodHandle downcallHandle(MethodType type, FunctionDescriptor function); MethodHandle downcallHandle(FunctionDescriptor function);
/** /**
* Allocates a native stub with given scope which can be passed to other foreign functions (as a function pointer); * Allocates a native stub with given scope which can be passed to other foreign functions (as a function pointer);
* calling such a function pointer from native code will result in the execution of the provided method handle. * calling such a function pointer from native code will result in the execution of the provided method handle.
* *
* <p> * <p>
* The returned memory address is associated with the provided scope. When such scope is closed, * The returned function pointer is associated with the provided scope. When such scope is closed,
* the corresponding native stub will be deallocated. * the corresponding native stub will be deallocated.
* <p> * <p>
* The target method handle should not throw any exceptions. If the target method handle does throw an exception, * The target method handle should not throw any exceptions. If the target method handle does throw an exception,
@ -228,563 +237,34 @@ public sealed interface CLinker permits AbstractCLinker {
* @param target the target method handle. * @param target the target method handle.
* @param function the function descriptor. * @param function the function descriptor.
* @param scope the upcall stub scope. * @param scope the upcall stub scope.
* @return the native stub segment. * @return the native stub symbol.
* @throws IllegalArgumentException if the target's method type and the function descriptor mismatch. * @throws IllegalArgumentException if the provided descriptor contains either a sequence or a padding layout,
* or 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="CLinker.html#upcall-stubs"><em>inferred type</em></a>.
* @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other * @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other
* than the thread owning {@code scope}. * than the thread owning {@code scope}.
*/ */
MemoryAddress upcallStub(MethodHandle target, FunctionDescriptor function, ResourceScope scope); NativeSymbol upcallStub(MethodHandle target, FunctionDescriptor function, ResourceScope scope);
/** /**
* The layout for the {@code char} C type * Obtains the downcall method handle {@linkplain MethodType type} associated with a given function descriptor.
* @param functionDescriptor a function descriptor.
* @return the downcall method handle {@linkplain MethodType type} associated with a given 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).
*/ */
ValueLayout C_CHAR = pick(SysV.C_CHAR, Win64.C_CHAR, AArch64.C_CHAR); static MethodType downcallType(FunctionDescriptor functionDescriptor) {
/** return SharedUtils.inferMethodType(functionDescriptor, false);
* The layout for the {@code short} C type
*/
ValueLayout C_SHORT = pick(SysV.C_SHORT, Win64.C_SHORT, AArch64.C_SHORT);
/**
* The layout for the {@code int} C type
*/
ValueLayout C_INT = pick(SysV.C_INT, Win64.C_INT, AArch64.C_INT);
/**
* The layout for the {@code long} C type
*/
ValueLayout C_LONG = pick(SysV.C_LONG, Win64.C_LONG, AArch64.C_LONG);
/**
* The layout for the {@code long long} C type.
*/
ValueLayout C_LONG_LONG = pick(SysV.C_LONG_LONG, Win64.C_LONG_LONG, AArch64.C_LONG_LONG);
/**
* The layout for the {@code float} C type
*/
ValueLayout C_FLOAT = pick(SysV.C_FLOAT, Win64.C_FLOAT, AArch64.C_FLOAT);
/**
* The layout for the {@code double} C type
*/
ValueLayout C_DOUBLE = pick(SysV.C_DOUBLE, Win64.C_DOUBLE, AArch64.C_DOUBLE);
/**
* The {@code T*} native type.
*/
ValueLayout C_POINTER = pick(SysV.C_POINTER, Win64.C_POINTER, AArch64.C_POINTER);
/**
* The layout for the {@code va_list} C type
*/
MemoryLayout C_VA_LIST = pick(SysV.C_VA_LIST, Win64.C_VA_LIST, AArch64.C_VA_LIST);
/**
* Returns a memory layout that is suitable to use as the layout for variadic arguments in a specialized
* function descriptor.
* @param <T> the memory layout type
* @param layout the layout the adapt
* @return a potentially newly created layout with the right attributes
*/
@SuppressWarnings("unchecked")
static <T extends MemoryLayout> T asVarArg(T layout) {
Objects.requireNonNull(layout);
return (T) PlatformLayouts.asVarArg(layout);
} }
/** /**
* Converts a Java string into a UTF-8 encoded, null-terminated C string, * Obtains the method handle {@linkplain MethodType type} associated with an upcall stub with given function descriptor.
* storing the result into a native memory segment allocated using the provided allocator. * @param functionDescriptor a function descriptor.
* <p> * @return the method handle {@linkplain MethodType type} associated with an upcall stub with given function descriptor.
* This method always replaces malformed-input and unmappable-character * @throws IllegalArgumentException if one or more layouts in the function descriptor are not supported
* sequences with this charset's default replacement byte array. The * (e.g. if they are sequence layouts or padding layouts).
* {@link java.nio.charset.CharsetEncoder} class should be used when more
* control over the encoding process is required.
*
* @param str the Java string to be converted into a C string.
* @param allocator the allocator to be used for the native segment allocation.
* @return a new native memory segment containing the converted C string.
*/ */
static MemorySegment toCString(String str, SegmentAllocator allocator) { static MethodType upcallType(FunctionDescriptor functionDescriptor) {
Objects.requireNonNull(str); return SharedUtils.inferMethodType(functionDescriptor, true);
Objects.requireNonNull(allocator);
return toCString(str.getBytes(StandardCharsets.UTF_8), allocator);
}
/**
* Converts a Java string into a UTF-8 encoded, null-terminated C string,
* storing the result into a native memory segment associated with the provided resource scope.
* <p>
* This method always replaces malformed-input and unmappable-character
* sequences with this charset's default replacement byte array. The
* {@link java.nio.charset.CharsetEncoder} class should be used when more
* control over the encoding process is required.
*
* @param str the Java string to be converted into a C string.
* @param scope the resource scope to be associated with the returned segment.
* @return a new native memory segment containing the converted C string.
* @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other
* than the thread owning {@code scope}.
*/
static MemorySegment toCString(String str, ResourceScope scope) {
return toCString(str, SegmentAllocator.ofScope(scope));
}
/**
* Converts a UTF-8 encoded, null-terminated C string stored at given address into a Java string.
* <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 addr the address at which the string is stored.
* @return a Java string with the contents of the null-terminated C string at given address.
* @throws IllegalArgumentException if the size of the native string is greater than the largest string supported by the platform,
* or if {@code addr == MemoryAddress.NULL}.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
static String toJavaString(MemoryAddress addr) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
SharedUtils.checkAddress(addr);
return SharedUtils.toJavaStringInternal(NativeMemorySegmentImpl.EVERYTHING, addr.toRawLongValue());
}
/**
* Converts a UTF-8 encoded, null-terminated C string stored at given address into a Java string.
* <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.
* @param addr the address at which the string is stored.
* @return a Java string with the contents of the null-terminated C string at given address.
* @throws IllegalArgumentException if the size of the native string is greater than the largest string supported by the platform.
* @throws IllegalStateException if the size of the native string is greater than the size of the segment
* associated with {@code addr}, or if {@code addr} is associated with a segment that is <em>not alive</em>.
*/
static String toJavaString(MemorySegment addr) {
Objects.requireNonNull(addr);
return SharedUtils.toJavaStringInternal(addr, 0L);
}
private static void copy(MemorySegment addr, byte[] bytes) {
var heapSegment = MemorySegment.ofArray(bytes);
addr.copyFrom(heapSegment);
MemoryAccess.setByteAtOffset(addr, bytes.length, (byte)0);
}
private static MemorySegment toCString(byte[] bytes, SegmentAllocator allocator) {
MemorySegment addr = allocator.allocate(bytes.length + 1, 1L);
copy(addr, bytes);
return addr;
}
/**
* Allocates memory of given size using malloc.
* <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 size memory size to be allocated
* @return addr memory address of the allocated memory
* @throws OutOfMemoryError if malloc could not allocate the required amount of native memory.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
static MemoryAddress allocateMemory(long size) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
MemoryAddress addr = SharedUtils.allocateMemoryInternal(size);
if (addr.equals(MemoryAddress.NULL)) {
throw new OutOfMemoryError();
} else {
return addr;
}
}
/**
* Frees the memory pointed by the given memory address.
* <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 addr memory address of the native memory to be freed
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* @throws IllegalArgumentException if {@code addr == MemoryAddress.NULL}.
* {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
static void freeMemory(MemoryAddress addr) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
SharedUtils.checkAddress(addr);
SharedUtils.freeMemoryInternal(addr);
}
/**
* An interface that models a C {@code va_list}.
* <p>
* A va list is a stateful cursor used to iterate over a set of variadic arguments.
* <p>
* Per the C specification (see C standard 6.5.2.2 Function calls - item 6),
* arguments to variadic calls are erased by way of 'default argument promotions',
* which erases integral types by way of integer promotion (see C standard 6.3.1.1 - item 2),
* and which erases all {@code float} arguments to {@code double}.
* <p>
* As such, this interface only supports reading {@code int}, {@code double},
* and any other type that fits into a {@code long}.
*
* <p> Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null}
* elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown. </p>
*/
sealed interface VaList extends Addressable permits WinVaList, SysVVaList, LinuxAArch64VaList, MacOsAArch64VaList, SharedUtils.EmptyVaList {
/**
* Reads the next value as an {@code int} and advances this va list's position.
*
* @param layout the layout of the value
* @return the value read as an {@code int}
* @throws IllegalStateException if the resource scope associated with this instance has been closed
* (see {@link #scope()}).
* @throws IllegalArgumentException if the given memory layout is not compatible with {@code int}
*/
int vargAsInt(MemoryLayout layout);
/**
* Reads the next value as a {@code long} and advances this va list's position.
*
* @param layout the layout of the value
* @return the value read as an {@code long}
* @throws IllegalStateException if the resource scope associated with this instance has been closed
* (see {@link #scope()}).
* @throws IllegalArgumentException if the given memory layout is not compatible with {@code long}
*/
long vargAsLong(MemoryLayout layout);
/**
* Reads the next value as a {@code double} and advances this va list's position.
*
* @param layout the layout of the value
* @return the value read as an {@code double}
* @throws IllegalStateException if the resource scope associated with this instance has been closed
* (see {@link #scope()}).
* @throws IllegalArgumentException if the given memory layout is not compatible with {@code double}
*/
double vargAsDouble(MemoryLayout layout);
/**
* Reads the next value as a {@code MemoryAddress} and advances this va list's position.
*
* @param layout the layout of the value
* @return the value read as an {@code MemoryAddress}
* @throws IllegalStateException if the resource scope associated with this instance has been closed
* (see {@link #scope()}).
* @throws IllegalArgumentException if the given memory layout is not compatible with {@code MemoryAddress}
*/
MemoryAddress vargAsAddress(MemoryLayout layout);
/**
* Reads the next value as a {@code MemorySegment}, and advances this va list's position.
* <p>
* The memory segment returned by this method will be allocated using the given {@link SegmentAllocator}.
*
* @param layout the layout of the value
* @param allocator the allocator to be used for the native segment allocation
* @return the value read as an {@code MemorySegment}
* @throws IllegalStateException if the resource scope associated with this instance has been closed
* (see {@link #scope()}).
* @throws IllegalArgumentException if the given memory layout is not compatible with {@code MemorySegment}
*/
MemorySegment vargAsSegment(MemoryLayout layout, SegmentAllocator allocator);
/**
* Reads the next value as a {@code MemorySegment}, and advances this va list's position.
* <p>
* The memory segment returned by this method will be associated with the given {@link ResourceScope}.
*
* @param layout the layout of the value
* @param scope the resource scope to be associated with the returned segment
* @return the value read as an {@code MemorySegment}
* @throws IllegalStateException if the resource scope associated with this instance has been closed
* (see {@link #scope()}).
* @throws IllegalArgumentException if the given memory layout is not compatible with {@code MemorySegment}
* @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other
* than the thread owning {@code scope}.
*/
MemorySegment vargAsSegment(MemoryLayout layout, ResourceScope scope);
/**
* Skips a number of elements with the given memory layouts, and advances this va list's position.
*
* @param layouts the layout of the value
* @throws IllegalStateException if the resource scope associated with this instance has been closed
* (see {@link #scope()}).
*/
void skip(MemoryLayout... layouts);
/**
* Returns the resource scope associated with this instance.
* @return the resource scope associated with this instance.
*/
ResourceScope scope();
/**
* Copies this C {@code va_list} at its current position. Copying is useful to traverse the va list's elements
* starting from the current position, without affecting the state of the original va list, essentially
* allowing the elements to be traversed multiple times.
* <p>
* Any native resource required by the execution of this method will be allocated in the resource scope
* associated with this instance (see {@link #scope()}).
* <p>
* This method only copies the va list cursor itself and not the memory that may be attached to the
* va list which holds its elements. That means that if this va list was created with the
* {@link #make(Consumer, ResourceScope)} method, closing this va list will also release the native memory that holds its
* elements, making the copy unusable.
*
* @return a copy of this C {@code va_list}.
* @throws IllegalStateException if the resource scope associated with this instance has been closed
* (see {@link #scope()}).
*/
VaList copy();
/**
* Returns the memory address of the C {@code va_list} associated with this instance.
* The returned memory address is associated with same resource scope as that associated with this instance.
*
* @return the memory address of the C {@code va_list} associated with this instance.
*/
@Override
MemoryAddress address();
/**
* Constructs a new {@code VaList} instance out of a memory address pointing to an existing C {@code va_list},
* backed by the {@linkplain ResourceScope#globalScope() global} resource scope.
* <p>
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
* Restricted methods are unsafe, and, if used incorrectly, their use might crash
* the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
* restricted methods, and use safe and supported functionalities, where possible.
*
* @param address a memory address pointing to an existing C {@code va_list}.
* @return a new {@code VaList} instance backed by the C {@code va_list} at {@code address}.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or 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) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
return SharedUtils.newVaListOfAddress(address, ResourceScope.globalScope());
}
/**
* Constructs a new {@code VaList} instance out of a memory address pointing to an existing C {@code va_list},
* with given resource scope.
* <p>
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
* Restricted methods are unsafe, and, if used incorrectly, their use might crash
* the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
* restricted methods, and use safe and supported functionalities, where possible.
*
* @param address a memory address pointing to an existing C {@code va_list}.
* @param scope the resource scope to be associated with the returned {@code VaList} instance.
* @return a new {@code VaList} instance backed by the C {@code va_list} at {@code address}.
* @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other
* than the thread owning {@code scope}.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or 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, ResourceScope scope) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
Objects.requireNonNull(address);
Objects.requireNonNull(scope);
return SharedUtils.newVaListOfAddress(address, scope);
}
/**
* Constructs a new {@code VaList} using a builder (see {@link Builder}), associated with a given
* {@linkplain ResourceScope resource scope}.
* <p>
* If this method needs to allocate native memory, such memory will be managed by the given
* {@linkplain ResourceScope resource scope}, and will be released when the resource scope is {@linkplain ResourceScope#close closed}.
* <p>
* Note that when there are no elements added to the created va list,
* this method will return the same as {@link #empty()}.
*
* @param actions a consumer for a builder (see {@link Builder}) which can be used to specify the elements
* of the underlying C {@code va_list}.
* @param scope the scope to be used for the valist allocation.
* @return a new {@code VaList} instance backed by a fresh C {@code va_list}.
* @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other
* than the thread owning {@code scope}.
*/
static VaList make(Consumer<Builder> actions, ResourceScope scope) {
Objects.requireNonNull(actions);
Objects.requireNonNull(scope);
return SharedUtils.newVaList(actions, scope);
}
/**
* Returns an empty C {@code va_list} constant.
* <p>
* The returned {@code VaList} can not be closed.
*
* @return a {@code VaList} modelling an empty C {@code va_list}.
*/
static VaList empty() {
return SharedUtils.emptyVaList();
}
/**
* A builder interface used to construct a C {@code va_list}.
*
* <p> Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null}
* elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown. </p>
*/
sealed interface Builder permits WinVaList.Builder, SysVVaList.Builder, LinuxAArch64VaList.Builder, MacOsAArch64VaList.Builder {
/**
* Adds a native value represented as an {@code int} to the C {@code va_list} being constructed.
*
* @param layout the native layout of the value.
* @param value the value, represented as an {@code int}.
* @return this builder.
* @throws IllegalArgumentException if the given memory layout is not compatible with {@code int}
*/
Builder vargFromInt(ValueLayout layout, int value);
/**
* Adds a native value represented as a {@code long} to the C {@code va_list} being constructed.
*
* @param layout the native layout of the value.
* @param value the value, represented as a {@code long}.
* @return this builder.
* @throws IllegalArgumentException if the given memory layout is not compatible with {@code long}
*/
Builder vargFromLong(ValueLayout layout, long value);
/**
* Adds a native value represented as a {@code double} to the C {@code va_list} being constructed.
*
* @param layout the native layout of the value.
* @param value the value, represented as a {@code double}.
* @return this builder.
* @throws IllegalArgumentException if the given memory layout is not compatible with {@code double}
*/
Builder vargFromDouble(ValueLayout layout, double value);
/**
* Adds a native value represented as a {@code MemoryAddress} to the C {@code va_list} being constructed.
*
* @param layout the native layout of the value.
* @param value the value, represented as a {@code Addressable}.
* @return this builder.
* @throws IllegalArgumentException if the given memory layout is not compatible with {@code MemoryAddress}
*/
Builder vargFromAddress(ValueLayout layout, Addressable value);
/**
* Adds a native value represented as a {@code MemorySegment} to the C {@code va_list} being constructed.
*
* @param layout the native layout of the value.
* @param value the value, represented as a {@code MemorySegment}.
* @return this builder.
* @throws IllegalArgumentException if the given memory layout is not compatible with {@code MemorySegment}
*/
Builder vargFromSegment(GroupLayout layout, MemorySegment value);
}
}
/**
* A C type kind. Each kind corresponds to a particular C language builtin type, and can be attached to
* {@link ValueLayout} instances using the {@link MemoryLayout#withAttribute(String, Constable)} in order
* to obtain a layout which can be classified accordingly by {@link CLinker#downcallHandle(Addressable, MethodType, FunctionDescriptor)}
* and {@link CLinker#upcallStub(MethodHandle, FunctionDescriptor, ResourceScope)}.
*/
enum TypeKind {
/**
* A kind corresponding to the <em>integral</em> C {@code char} type
*/
CHAR(true),
/**
* A kind corresponding to the <em>integral</em> C {@code short} type
*/
SHORT(true),
/**
* A kind corresponding to the <em>integral</em> C {@code int} type
*/
INT(true),
/**
* A kind corresponding to the <em>integral</em> C {@code long} type
*/
LONG(true),
/**
* A kind corresponding to the <em>integral</em> C {@code long long} type
*/
LONG_LONG(true),
/**
* A kind corresponding to the <em>floating-point</em> C {@code float} type
*/
FLOAT(false),
/**
* A kind corresponding to the <em>floating-point</em> C {@code double} type
*/
DOUBLE(false),
/**
* A kind corresponding to the an <em>integral</em> C pointer type
*/
POINTER(false);
private final boolean isIntegral;
TypeKind(boolean isIntegral) {
this.isIntegral = isIntegral;
}
/**
* Is this kind integral?
*
* @return true if this kind is integral
*/
public boolean isIntegral() {
return isIntegral;
}
/**
* Is this kind a floating point type?
*
* @return true if this kind is a floating point type
*/
public boolean isFloat() {
return !isIntegral() && !isPointer();
}
/**
* Is this kind a pointer kind?
*
* @return true if this kind is a pointer kind
*/
public boolean isPointer() {
return this == POINTER;
}
/**
* The layout attribute name associated with this classification kind. Clients can retrieve the type kind
* of a layout using the following code:
* <blockquote><pre>{@code
ValueLayout layout = ...
TypeKind = layout.attribute(TypeKind.ATTR_NAME).orElse(null);
* }</pre></blockquote>
*/
public static final String ATTR_NAME = "abi/kind";
} }
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -30,10 +30,7 @@ import java.lang.constant.ConstantDescs;
import java.lang.constant.DynamicConstantDesc; import java.lang.constant.DynamicConstantDesc;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -46,60 +43,16 @@ import java.util.stream.Stream;
* <p> Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null} * <p> Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null}
* elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown. </p> * elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown. </p>
*/ */
public final class FunctionDescriptor implements Constable { public sealed class FunctionDescriptor implements Constable permits FunctionDescriptor.VariadicFunction {
/**
* The name of the function descriptor attribute (see {@link #attributes()} used to mark trivial functions. The
* attribute value must be a boolean.
*/
public static final String TRIVIAL_ATTRIBUTE_NAME = "abi/trivial";
private final MemoryLayout resLayout; private final MemoryLayout resLayout;
private final MemoryLayout[] argLayouts; private final List<MemoryLayout> argLayouts;
private final Map<String, Constable> attributes;
private FunctionDescriptor(MemoryLayout resLayout, Map<String, Constable> attributes, MemoryLayout... argLayouts) { private FunctionDescriptor(MemoryLayout resLayout, List<MemoryLayout> argLayouts) {
this.resLayout = resLayout; this.resLayout = resLayout;
this.attributes = attributes;
this.argLayouts = argLayouts; this.argLayouts = argLayouts;
} }
/**
* Returns the attribute with the given name (if it exists).
*
* @param name the attribute name.
* @return the attribute with the given name (if it exists).
*/
public Optional<Constable> attribute(String name) {
Objects.requireNonNull(name);
return Optional.ofNullable(attributes.get(name));
}
/**
* Returns a stream of the attribute names associated with this function descriptor.
*
* @return a stream of the attribute names associated with this function descriptor.
*/
public Stream<String> attributes() {
return attributes.keySet().stream();
}
/**
* Returns a new function descriptor which features the same attributes as this descriptor, plus the newly specified attribute.
* If this descriptor already contains an attribute with the same name, the existing attribute value is overwritten in the returned
* descriptor.
*
* @param name the attribute name.
* @param value the attribute value.
* @return a new function descriptor which features the same attributes as this descriptor, plus the newly specified attribute.
*/
public FunctionDescriptor withAttribute(String name, Constable value) {
Objects.requireNonNull(name);
Map<String, Constable> newAttributes = new HashMap<>(attributes);
newAttributes.put(name, value);
return new FunctionDescriptor(resLayout, newAttributes, argLayouts);
}
/** /**
* Returns the return layout associated with this function. * Returns the return layout associated with this function.
* @return the return layout. * @return the return layout.
@ -113,7 +66,7 @@ public final class FunctionDescriptor implements Constable {
* @return the argument layouts. * @return the argument layouts.
*/ */
public List<MemoryLayout> argumentLayouts() { public List<MemoryLayout> argumentLayouts() {
return Arrays.asList(argLayouts); return argLayouts;
} }
/** /**
@ -126,7 +79,7 @@ public final class FunctionDescriptor implements Constable {
Objects.requireNonNull(resLayout); Objects.requireNonNull(resLayout);
Objects.requireNonNull(argLayouts); Objects.requireNonNull(argLayouts);
Arrays.stream(argLayouts).forEach(Objects::requireNonNull); Arrays.stream(argLayouts).forEach(Objects::requireNonNull);
return new FunctionDescriptor(resLayout, Map.of(), argLayouts); return new FunctionDescriptor(resLayout, List.of(argLayouts));
} }
/** /**
@ -137,7 +90,31 @@ public final class FunctionDescriptor implements Constable {
public static FunctionDescriptor ofVoid(MemoryLayout... argLayouts) { public static FunctionDescriptor ofVoid(MemoryLayout... argLayouts) {
Objects.requireNonNull(argLayouts); Objects.requireNonNull(argLayouts);
Arrays.stream(argLayouts).forEach(Objects::requireNonNull); Arrays.stream(argLayouts).forEach(Objects::requireNonNull);
return new FunctionDescriptor(null, Map.of(), argLayouts); return new FunctionDescriptor(null, List.of(argLayouts));
}
/**
* Obtain 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 #withReturnLayout(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 new 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;
} }
/** /**
@ -149,9 +126,8 @@ public final class FunctionDescriptor implements Constable {
public FunctionDescriptor withAppendedArgumentLayouts(MemoryLayout... addedLayouts) { public FunctionDescriptor withAppendedArgumentLayouts(MemoryLayout... addedLayouts) {
Objects.requireNonNull(addedLayouts); Objects.requireNonNull(addedLayouts);
Arrays.stream(addedLayouts).forEach(Objects::requireNonNull); Arrays.stream(addedLayouts).forEach(Objects::requireNonNull);
MemoryLayout[] newLayouts = Arrays.copyOf(argLayouts, argLayouts.length + addedLayouts.length); List<MemoryLayout> newLayouts = Stream.concat(argLayouts.stream(), Stream.of(addedLayouts)).toList();
System.arraycopy(addedLayouts, 0, newLayouts, argLayouts.length, addedLayouts.length); return new FunctionDescriptor(resLayout, newLayouts);
return new FunctionDescriptor(resLayout, attributes, newLayouts);
} }
/** /**
@ -161,7 +137,7 @@ public final class FunctionDescriptor implements Constable {
*/ */
public FunctionDescriptor withReturnLayout(MemoryLayout newReturn) { public FunctionDescriptor withReturnLayout(MemoryLayout newReturn) {
Objects.requireNonNull(newReturn); Objects.requireNonNull(newReturn);
return new FunctionDescriptor(newReturn, attributes, argLayouts); return new FunctionDescriptor(newReturn, argLayouts);
} }
/** /**
@ -169,7 +145,7 @@ public final class FunctionDescriptor implements Constable {
* @return the new function descriptor. * @return the new function descriptor.
*/ */
public FunctionDescriptor withVoidReturnLayout() { public FunctionDescriptor withVoidReturnLayout() {
return new FunctionDescriptor(null, attributes, argLayouts); return new FunctionDescriptor(null, argLayouts);
} }
/** /**
@ -187,7 +163,7 @@ public final class FunctionDescriptor implements Constable {
/** /**
* Compares the specified object with this function descriptor for equality. Returns {@code true} if and only if the specified * 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 of the following conditions are met: * object is also a function descriptor, and all the following conditions are met:
* <ul> * <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 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 equal (see {@link MemoryLayout#equals(Object)}) * <li>the two function descriptors have argument layouts that are pair-wise equal (see {@link MemoryLayout#equals(Object)})
@ -201,11 +177,10 @@ public final class FunctionDescriptor implements Constable {
if (this == other) { if (this == other) {
return true; return true;
} }
if (!(other instanceof FunctionDescriptor)) { if (!(other instanceof FunctionDescriptor f)) {
return false; return false;
} }
FunctionDescriptor f = (FunctionDescriptor) other; return Objects.equals(resLayout, f.resLayout) && Objects.equals(argLayouts, f.argLayouts);
return Objects.equals(resLayout, f.resLayout) && Arrays.equals(argLayouts, f.argLayouts);
} }
/** /**
@ -214,10 +189,18 @@ public final class FunctionDescriptor implements Constable {
*/ */
@Override @Override
public int hashCode() { public int hashCode() {
int hashCode = Arrays.hashCode(argLayouts); int hashCode = Objects.hashCode(argLayouts);
return resLayout == null ? hashCode : resLayout.hashCode() ^ hashCode; return resLayout == null ? hashCode : resLayout.hashCode() ^ hashCode;
} }
/**
* Returns an {@link Optional} containing the nominal descriptor for this
* function descriptor, if one can be constructed, or an empty {@link Optional}
* if one cannot be constructed.
*
* @return An {@link Optional} containing the resulting nominal descriptor,
* or an empty {@link Optional} if one cannot be constructed.
*/
@Override @Override
public Optional<DynamicConstantDesc<FunctionDescriptor>> describeConstable() { public Optional<DynamicConstantDesc<FunctionDescriptor>> describeConstable() {
List<ConstantDesc> constants = new ArrayList<>(); List<ConstantDesc> constants = new ArrayList<>();
@ -231,4 +214,40 @@ public final class FunctionDescriptor implements Constable {
return Optional.of(DynamicConstantDesc.ofNamed( return Optional.of(DynamicConstantDesc.ofNamed(
ConstantDescs.BSM_INVOKE, "function", AbstractLayout.CD_FUNCTION_DESC, constants.toArray(new ConstantDesc[0]))); ConstantDescs.BSM_INVOKE, "function", AbstractLayout.CD_FUNCTION_DESC, constants.toArray(new ConstantDesc[0])));
} }
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 withAppendedArgumentLayouts(MemoryLayout... addedLayouts) {
throw new UnsupportedOperationException();
}
@Override
public FunctionDescriptor withReturnLayout(MemoryLayout newReturn) {
throw new UnsupportedOperationException();
}
@Override
public FunctionDescriptor withVoidReturnLayout() {
throw new UnsupportedOperationException();
}
@Override
public Optional<DynamicConstantDesc<FunctionDescriptor>> describeConstable() {
return Optional.empty();
}
}
} }

View file

@ -25,14 +25,12 @@
*/ */
package jdk.incubator.foreign; package jdk.incubator.foreign;
import java.lang.constant.Constable;
import java.lang.constant.ConstantDesc; import java.lang.constant.ConstantDesc;
import java.lang.constant.ConstantDescs; import java.lang.constant.ConstantDescs;
import java.lang.constant.DynamicConstantDesc; import java.lang.constant.DynamicConstantDesc;
import java.lang.constant.MethodHandleDesc; import java.lang.constant.MethodHandleDesc;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalLong; import java.util.OptionalLong;
@ -40,7 +38,7 @@ import java.util.function.LongBinaryOperator;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* A group layout is used to combine together multiple <em>member layouts</em>. There are two ways in which member layouts * A group layout is used to combine 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</em>
* (see {@link MemoryLayout#structLayout(MemoryLayout...)}); conversely, if all member layouts are laid out at the same starting offset, * (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</em> (see {@link MemoryLayout#unionLayout(MemoryLayout...)}).
@ -105,11 +103,11 @@ public final class GroupLayout extends AbstractLayout implements MemoryLayout {
private final List<MemoryLayout> elements; private final List<MemoryLayout> elements;
GroupLayout(Kind kind, List<MemoryLayout> elements) { GroupLayout(Kind kind, List<MemoryLayout> elements) {
this(kind, elements, kind.alignof(elements), Map.of()); this(kind, elements, kind.alignof(elements), Optional.empty());
} }
GroupLayout(Kind kind, List<MemoryLayout> elements, long alignment, Map<String, Constable> attributes) { GroupLayout(Kind kind, List<MemoryLayout> elements, long alignment, Optional<String> name) {
super(kind.sizeof(elements), alignment, attributes); super(kind.sizeof(elements), alignment, name);
this.kind = kind; this.kind = kind;
this.elements = elements; this.elements = elements;
} }
@ -160,10 +158,9 @@ public final class GroupLayout extends AbstractLayout implements MemoryLayout {
if (!super.equals(other)) { if (!super.equals(other)) {
return false; return false;
} }
if (!(other instanceof GroupLayout)) { if (!(other instanceof GroupLayout g)) {
return false; return false;
} }
GroupLayout g = (GroupLayout)other;
return kind.equals(g.kind) && elements.equals(g.elements); return kind.equals(g.kind) && elements.equals(g.elements);
} }
@ -173,8 +170,8 @@ public final class GroupLayout extends AbstractLayout implements MemoryLayout {
} }
@Override @Override
GroupLayout dup(long alignment, Map<String, Constable> attributes) { GroupLayout dup(long alignment, Optional<String> name) {
return new GroupLayout(kind, elements, alignment, attributes); return new GroupLayout(kind, elements, alignment, name);
} }
@Override @Override
@ -212,12 +209,4 @@ public final class GroupLayout extends AbstractLayout implements MemoryLayout {
public GroupLayout withBitAlignment(long alignmentBits) { public GroupLayout withBitAlignment(long alignmentBits) {
return (GroupLayout)super.withBitAlignment(alignmentBits); return (GroupLayout)super.withBitAlignment(alignmentBits);
} }
/**
* {@inheritDoc}
*/
@Override
public GroupLayout withAttribute(String name, Constable value) {
return (GroupLayout)super.withAttribute(name, value);
}
} }

View file

@ -27,33 +27,45 @@
package jdk.incubator.foreign; package jdk.incubator.foreign;
import jdk.internal.foreign.MemoryAddressImpl; import jdk.internal.foreign.MemoryAddressImpl;
import jdk.internal.ref.CleanerFactory;
import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.CallerSensitive;
import java.lang.ref.Cleaner; import java.nio.ByteOrder;
/** /**
* A memory address models a reference into a memory location. Memory addresses are typically obtained using the * A memory address models a reference into a memory location. Memory addresses are typically obtained in three ways:
* {@link MemorySegment#address()} method, and can refer to either off-heap or on-heap memory. Off-heap memory * <ul>
* addresses are referred to as <em>native</em> memory addresses (see {@link #isNative()}). Native memory addresses * <li>By calling {@link Addressable#address()} on an instance of type {@link Addressable} (e.g. a memory segment);</li>
* allow clients to obtain a raw memory address (expressed as a long value) which can then be used e.g. when interacting * <li>By invoking a {@linkplain CLinker#downcallHandle(FunctionDescriptor) downcall method handle} which returns a pointer;</li>
* with native code. * <li>By reading an address from memory, e.g. via {@link MemorySegment#get(ValueLayout.OfAddress, long)}.</li>
* <p> * </ul>
* Given an address, it is possible to compute its offset relative to a given segment, which can be useful * A memory address is backed by a raw machine pointer, expressed as a {@linkplain #toRawLongValue() long value}.
* when performing memory dereference operations using a memory access var handle (see {@link MemoryHandles}). *
* <p> * <h2>Dereference</h2>
* A memory address is associated with a {@linkplain ResourceScope resource scope}; the resource scope determines the *
* lifecycle of the memory address, and whether the address can be used from multiple threads. Memory addresses * A memory address can be read or written using various methods provided in this class (e.g. {@link #get(ValueLayout.OfInt, long)}).
* obtained from {@linkplain #ofLong(long) numeric values}, or from native code, are associated with the * Each dereference method takes a {@linkplain jdk.incubator.foreign.ValueLayout value layout}, which specifies the size,
* {@linkplain ResourceScope#globalScope() global resource scope}. Memory addresses obtained from segments * alignment constraints, byte order as well as the Java type associated with the dereference operation, and an offset.
* are associated with the same scope as the segment from which they have been obtained. * For instance, to read an int from a segment, using {@link ByteOrder#nativeOrder() default endianness}, the following code can be used:
* <blockquote><pre>{@code
MemoryAddress address = ...
int value = address.get(ValueLayout.JAVA_INT, 0);
* }</pre></blockquote>
*
* 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:
* <blockquote><pre>{@code
MemoryAddress address = ...
int value = address.get(ValueLayout.JAVA_INT.withOrder(BIG_ENDIAN), 0);
* }</pre></blockquote>
*
* 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.
* <p> * <p>
* All implementations of this interface must be <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>; * All implementations of this interface must be <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>;
* programmers should treat instances that are {@linkplain #equals(Object) equal} as interchangeable and should not * programmers should treat instances that are {@linkplain #equals(Object) equal} as interchangeable and should not
* use instances for synchronization, or unpredictable behavior may occur. For example, in a future release, * use instances for synchronization, or unpredictable behavior may occur. For example, in a future release,
* synchronization may fail. The {@code equals} method should be used for comparisons. * synchronization may fail. The {@code equals} method should be used for comparisons.
* <p>
* Non-platform classes should not implement {@linkplain MemoryAddress} directly.
* *
* <p> Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null} * <p> Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null}
* elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown. </p> * elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown. </p>
@ -63,10 +75,11 @@ import java.lang.ref.Cleaner;
*/ */
public sealed interface MemoryAddress extends Addressable permits MemoryAddressImpl { public sealed interface MemoryAddress extends Addressable permits MemoryAddressImpl {
@Override /**
default MemoryAddress address() { * Returns the raw long value associated with this memory address.
return this; * @return The raw long value associated with this memory address.
} */
long toRawLongValue();
/** /**
* Creates a new memory address with given offset (in bytes), which might be negative, from current one. * Creates a new memory address with given offset (in bytes), which might be negative, from current one.
@ -76,127 +89,49 @@ public sealed interface MemoryAddress extends Addressable permits MemoryAddressI
MemoryAddress addOffset(long offset); MemoryAddress addOffset(long offset);
/** /**
* Returns the resource scope associated with this memory address. * Reads a UTF-8 encoded, null-terminated string from this address and offset.
* @return the resource scope associated with this memory address.
*/
ResourceScope scope();
/**
* Returns the offset of this memory address into the given segment. More specifically, if both the segment's
* base address and this address are native addresses, the result is computed as
* {@code this.toRawLongValue() - segment.address().toRawLongValue()}. Otherwise, if both addresses in the form
* {@code (B, O1)}, {@code (B, O2)}, where {@code B} is the same base heap object and {@code O1}, {@code O2}
* are byte offsets (relative to the base object) associated with this address and the segment's base address,
* the result is computed as {@code O1 - O2}.
* <p> * <p>
* If the segment's base address and this address are both heap addresses, but with different base objects, the result is undefined * This method always replaces malformed-input and unmappable-character
* and an exception is thrown. Similarly, if the segment's base address is an heap address (resp. off-heap) and * sequences with this charset's default replacement string. The {@link
* this address is an off-heap (resp. heap) address, the result is undefined and an exception is thrown. * java.nio.charset.CharsetDecoder} class should be used when more control
* Otherwise, the result is a byte offset {@code SO}. If this address falls within the * over the decoding process is required.
* spatial bounds of the given segment, then {@code 0 <= SO < segment.byteSize()}; otherwise, {@code SO < 0 || SO > segment.byteSize()}.
* @return the offset of this memory address into the given segment.
* @param segment the segment relative to which this address offset should be computed
* @throws IllegalArgumentException if {@code segment} is not compatible with this address; this can happen, for instance,
* when {@code segment} models an heap memory region, while this address is a {@linkplain #isNative() native} address.
*/
long segmentOffset(MemorySegment segment);
/**
Returns a new native memory segment with given size and resource scope (replacing the scope already associated
* with this address), and whose base address is this address. This method can be useful when interacting with custom
* native memory sources (e.g. custom allocators), where an address to some
* underlying memory region is typically obtained from native code (often as a plain {@code long} value).
* The returned segment is not read-only (see {@link MemorySegment#isReadOnly()}), and is associated with the
* provided resource scope.
* <p>
* Clients should ensure that the address and bounds refers to a valid region of memory that is accessible for reading and,
* if appropriate, writing; an attempt to access an invalid memory location from Java code will either return an arbitrary value,
* have no visible effect, or cause an unspecified exception to be thrown.
* <p>
* This method is equivalent to the following code:
* <pre>{@code
asSegment(byteSize, null, scope);
* }</pre>
* <p> * <p>
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>. * 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 * 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 * 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. * restricted methods, and use safe and supported functionalities, where possible.
* *
* @param bytesSize the desired size. * @param offset offset in bytes (relative to this address). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}.
* @param scope the native segment scope. * @return a Java string constructed from the bytes read from the given starting address ({@code toRowLongValue() + offset})
* @return a new native memory segment with given base address, size and scope. * up to (but not including) the first {@code '\0'} terminator character (assuming one is found).
* @throws IllegalArgumentException if {@code bytesSize <= 0}. * @throws IllegalArgumentException if the size of the native string is greater than the largest string supported by the platform.
* @throws IllegalStateException if either the scope associated with this address or the provided scope
* have been already closed, or if access occurs from a thread other than the thread owning either
* scopes.
* @throws UnsupportedOperationException if this address is not a {@linkplain #isNative() native} address.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module. * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/ */
@CallerSensitive @CallerSensitive
MemorySegment asSegment(long bytesSize, ResourceScope scope); String getUtf8String(long offset);
/** /**
* Returns a new native memory segment with given size and resource scope (replacing the scope already associated * Writes the given string to this address at given offset, converting it to a null-terminated byte sequence using UTF-8 encoding.
* with this address), and whose base address is this address. This method can be useful when interacting with custom
* native memory sources (e.g. custom allocators), where an address to some
* underlying memory region is typically obtained from native code (often as a plain {@code long} value).
* The returned segment is associated with the provided resource scope.
* <p> * <p>
* Clients should ensure that the address and bounds refers to a valid region of memory that is accessible for reading and, * This method always replaces malformed-input and unmappable-character
* if appropriate, writing; an attempt to access an invalid memory location from Java code will either return an arbitrary value, * sequences with this charset's default replacement string. The {@link
* have no visible effect, or cause an unspecified exception to be thrown. * java.nio.charset.CharsetDecoder} class should be used when more control
* <p> * over the decoding process is required.
* Calling {@link ResourceScope#close()} on the scope associated with the returned segment will result in calling * @param offset offset in bytes (relative to this address). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}.
* the provided cleanup action (if any). * @param str the Java string to be written at this address.
* <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 bytesSize the desired size.
* @param cleanupAction the cleanup action; can be {@code null}.
* @param scope the native segment scope.
* @return a new native memory segment with given base address, size and scope.
* @throws IllegalArgumentException if {@code bytesSize <= 0}.
* @throws IllegalStateException if either the scope associated with this address or the provided scope
* have been already closed, or if access occurs from a thread other than the thread owning either
* scopes.
* @throws UnsupportedOperationException if this address is not a {@linkplain #isNative() native} address.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option * @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or * {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module. * {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/ */
@CallerSensitive @CallerSensitive
MemorySegment asSegment(long bytesSize, Runnable cleanupAction, ResourceScope scope); void setUtf8String(long offset, String str);
/**
* Is this an off-heap memory address?
* @return true, if this is an off-heap memory address.
*/
boolean isNative();
/**
* Returns the raw long value associated with this native memory address.
* @return The raw long value associated with this native memory address.
* @throws UnsupportedOperationException if this memory address is not a {@linkplain #isNative() native} address.
* @throws IllegalStateException if the scope associated with this segment has been already closed,
* or if access occurs from a thread other than the thread owning either segment.
*/
long toRawLongValue();
/** /**
* Compares the specified object with this address for equality. Returns {@code true} if and only if the specified * 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. * object is also an address, and it refers to the same memory location as this address.
* *
* @apiNote two addresses might be considered equal despite their associated resource scopes differ. This
* can happen, for instance, if the same memory address is used to create memory segments with different
* scopes (using {@link #asSegment(long, ResourceScope)}), and the base address of the resulting segments is
* then compared.
*
* @param that the object to be compared for equality with 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. * @return {@code true} if the specified object is equal to this address.
*/ */
@ -211,20 +146,594 @@ public sealed interface MemoryAddress extends Addressable permits MemoryAddressI
int hashCode(); int hashCode();
/** /**
* The native memory address instance modelling the {@code NULL} address, associated * The native memory address instance modelling the {@code NULL} address.
* with the {@linkplain ResourceScope#globalScope() global} resource scope.
*/ */
MemoryAddress NULL = new MemoryAddressImpl(null, 0L); MemoryAddress NULL = new MemoryAddressImpl(0L);
/** /**
* Obtain a native memory address instance from given long address. The returned address is associated * Obtain a native memory address instance from given long address.
* with the {@linkplain ResourceScope#globalScope() global} resource scope.
* @param value the long address. * @param value the long address.
* @return the new memory address instance. * @return the new memory address instance.
*/ */
static MemoryAddress ofLong(long value) { static MemoryAddress ofLong(long value) {
return value == 0 ? return value == 0 ?
NULL : NULL :
new MemoryAddressImpl(null, value); new MemoryAddressImpl(value);
} }
/**
* Reads a byte from this address and offset with 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). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}.
* @return a byte value read from 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 either absent, or 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 to this address instance and offset with 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). The final address of this write operation can be expressed as {@code toRowLongValue() + offset}.
* @param value the byte value to be written.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or 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 and offset with 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). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}.
* @return a boolean value read from 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 either absent, or 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 to this address instance and offset with 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). The final address of this write operation can be expressed as {@code toRowLongValue() + offset}.
* @param value the boolean value to be written.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or 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 and offset with 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). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}.
* @return a char value read from 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 either absent, or 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 to this address instance and offset with 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). The final address of this write operation can be expressed as {@code toRowLongValue() + offset}.
* @param value the char value to be written.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or 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 and offset with 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). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}.
* @return a short value read from 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 either absent, or 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 to this address instance and offset with 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). The final address of this write operation can be expressed as {@code toRowLongValue() + offset}.
* @param value the short value to be written.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or 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 and offset with 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). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}.
* @return an int value read from 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 either absent, or 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 to this address instance and offset with 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). The final address of this write operation can be expressed as {@code toRowLongValue() + offset}.
* @param value the int value to be written.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or 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 and offset with 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). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}.
* @return a float value read from 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 either absent, or 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 to this address instance and offset with 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). The final address of this write operation can be expressed as {@code toRowLongValue() + offset}.
* @param value the float value to be written.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or 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 and offset with 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). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}.
* @return a long value read from 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 either absent, or 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 to this address instance and offset with 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). The final address of this write operation can be expressed as {@code toRowLongValue() + offset}.
* @param value the long value to be written.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or 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 and offset with 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). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}.
* @return a double value read from 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 either absent, or 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 to this address instance and offset with 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). The final address of this write operation can be expressed as {@code toRowLongValue() + offset}.
* @param value the double value to be written.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or 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 and offset with 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). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}.
* @return an address value read from 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 either absent, or 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 to this address instance and offset with 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). The final address of this write operation can be expressed as {@code toRowLongValue() + offset}.
* @param value the address value to be written.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or 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 and index, scaled by 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). The final address of this read operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}.
* @return a char value read from 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 either absent, or 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 to this address instance and index, scaled by 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). The final address of this write operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}.
* @param value the char value to be written.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or 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 and index, scaled by 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). The final address of this read operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}.
* @return a short value read from 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 either absent, or 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 to this address instance and index, scaled by 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). The final address of this write operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}.
* @param value the short value to be written.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or 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 and index, scaled by 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). The final address of this read operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}.
* @return an int value read from 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 either absent, or 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 to this address instance and index, scaled by 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). The final address of this write operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}.
* @param value the int value to be written.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or 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 and index, scaled by 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). The final address of this read operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}.
* @return a float value read from 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 either absent, or 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 to this address instance and index, scaled by 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). The final address of this write operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}.
* @param value the float value to be written.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or 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 and index, scaled by 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). The final address of this read operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}.
* @return a long value read from 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 either absent, or 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 to this address instance and index, scaled by 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). The final address of this write operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}.
* @param value the long value to be written.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or 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 and index, scaled by 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). The final address of this read operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}.
* @return a double value read from 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 either absent, or 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 to this address instance and index, scaled by 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). The final address of this write operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}.
* @param value the double value to be written.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or 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 and index, scaled by 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). The final address of this read operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}.
* @return an address value read from 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 either absent, or 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 to this address instance and index, scaled by 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). The final address of this write operation can be expressed as {@code toRowLongValue() + (index * layout.byteSize())}.
* @param value the address value to be written.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or 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

@ -27,24 +27,21 @@ package jdk.incubator.foreign;
import jdk.internal.access.JavaLangInvokeAccess; import jdk.internal.access.JavaLangInvokeAccess;
import jdk.internal.access.SharedSecrets; import jdk.internal.access.SharedSecrets;
import jdk.internal.foreign.Utils;
import sun.invoke.util.Wrapper; import sun.invoke.util.Wrapper;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle; import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
/** /**
* This class defines several factory methods for constructing and combining memory access var handles. * This class defines several factory methods for constructing and combining memory access var handles.
* To obtain a memory access var handle, clients must start from one of the <em>leaf</em> methods * Memory access var handles can be obtained using {@link MemoryHandles#varHandle(ValueLayout)}. The provided value layout
* (see {@link MemoryHandles#varHandle(Class, ByteOrder)}, * determines the type, as well as the alignment constraint and the byte order associated with the memory access var handle.
* {@link MemoryHandles#varHandle(Class, long, ByteOrder)}). This determines the variable type * <p>
* (all primitive types but {@code void} and {@code boolean} are supported), as well as the alignment constraint and the * The resulting memory access var handle can then be combined in various ways
* byte order associated with a memory access var handle. The resulting memory access var handle can then be combined in various ways
* to emulate different addressing modes. The var handles created by this class feature a <em>mandatory</em> coordinate type * to emulate different addressing modes. The var handles created by this class feature a <em>mandatory</em> coordinate type
* (of type {@link MemorySegment}), and one {@code long} coordinate type, which represents the offset, in bytes, relative * (of type {@link MemorySegment}), and one {@code long} coordinate type, which represents the offset, in bytes, relative
* to the segment, at which dereference should occur. * to the segment, at which dereference should occur.
@ -53,12 +50,12 @@ import java.util.Objects;
* <blockquote><pre>{@code * <blockquote><pre>{@code
GroupLayout seq = MemoryLayout.structLayout( GroupLayout seq = MemoryLayout.structLayout(
MemoryLayout.paddingLayout(32), MemoryLayout.paddingLayout(32),
MemoryLayout.valueLayout(32, ByteOrder.BIG_ENDIAN).withName("value") ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN).withName("value")
); );
* }</pre></blockquote> * }</pre></blockquote>
* To access the member layout named {@code value}, we can construct a memory access var handle as follows: * To access the member layout named {@code value}, we can construct a memory access var handle as follows:
* <blockquote><pre>{@code * <blockquote><pre>{@code
VarHandle handle = MemoryHandles.varHandle(int.class, ByteOrder.BIG_ENDIAN); //(MemorySegment, long) -> int VarHandle handle = MemoryHandles.varHandle(ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN)); //(MemorySegment, long) -> int
handle = MemoryHandles.insertCoordinates(handle, 1, 4); //(MemorySegment) -> int handle = MemoryHandles.insertCoordinates(handle, 1, 4); //(MemorySegment) -> int
* }</pre></blockquote> * }</pre></blockquote>
* *
@ -77,21 +74,21 @@ handle = MemoryHandles.insertCoordinates(handle, 1, 4); //(MemorySegment) -> int
* access modes {@code get} and {@code set} for {@code long} and * access modes {@code get} and {@code set} for {@code long} and
* {@code double} on 32-bit platforms. * {@code double} on 32-bit platforms.
* <li>atomic update access modes for {@code int}, {@code long}, * <li>atomic update access modes for {@code int}, {@code long},
* {@code float} or {@code double}. * {@code float}, {@code double} or {@link MemoryAddress}.
* (Future major platform releases of the JDK may support additional * (Future major platform releases of the JDK may support additional
* types for certain currently unsupported access modes.) * types for certain currently unsupported access modes.)
* <li>numeric atomic update access modes for {@code int} and {@code long}. * <li>numeric atomic update access modes for {@code int}, {@code long} and {@link MemoryAddress}.
* (Future major platform releases of the JDK may support additional * (Future major platform releases of the JDK may support additional
* numeric types for certain currently unsupported access modes.) * numeric types for certain currently unsupported access modes.)
* <li>bitwise atomic update access modes for {@code int} and {@code long}. * <li>bitwise atomic update access modes for {@code int}, {@code long} and {@link MemoryAddress}.
* (Future major platform releases of the JDK may support additional * (Future major platform releases of the JDK may support additional
* numeric types for certain currently unsupported access modes.) * numeric types for certain currently unsupported access modes.)
* </ul> * </ul>
* *
* If {@code T} is {@code float} or {@code double} then atomic * If {@code T} is {@code float}, {@code double} or {@link MemoryAddress} then atomic
* update access modes compare values using their bitwise representation * update access modes compare values using their bitwise representation
* (see {@link Float#floatToRawIntBits} and * (see {@link Float#floatToRawIntBits},
* {@link Double#doubleToRawLongBits}, respectively). * {@link Double#doubleToRawLongBits} and {@link MemoryAddress#toRawLongValue()}, respectively).
* <p> * <p>
* Alternatively, a memory access operation is <em>partially aligned</em> if it occurs at a memory address {@code A} * 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 * which is only compatible with the alignment constraint {@code B}; in such cases, access for anything other than the
@ -109,8 +106,6 @@ public final class MemoryHandles {
//sorry, just the one! //sorry, just the one!
} }
private static final MethodHandle LONG_TO_ADDRESS;
private static final MethodHandle ADDRESS_TO_LONG;
private static final MethodHandle INT_TO_BYTE; private static final MethodHandle INT_TO_BYTE;
private static final MethodHandle BYTE_TO_UNSIGNED_INT; private static final MethodHandle BYTE_TO_UNSIGNED_INT;
private static final MethodHandle INT_TO_SHORT; private static final MethodHandle INT_TO_SHORT;
@ -124,10 +119,6 @@ public final class MemoryHandles {
static { static {
try { try {
LONG_TO_ADDRESS = MethodHandles.lookup().findStatic(MemoryHandles.class, "longToAddress",
MethodType.methodType(MemoryAddress.class, long.class));
ADDRESS_TO_LONG = MethodHandles.lookup().findStatic(MemoryHandles.class, "addressToLong",
MethodType.methodType(long.class, MemoryAddress.class));
INT_TO_BYTE = MethodHandles.explicitCastArguments(MethodHandles.identity(byte.class), INT_TO_BYTE = MethodHandles.explicitCastArguments(MethodHandles.identity(byte.class),
MethodType.methodType(byte.class, int.class)); MethodType.methodType(byte.class, int.class));
BYTE_TO_UNSIGNED_INT = MethodHandles.lookup().findStatic(Byte.class, "toUnsignedInt", BYTE_TO_UNSIGNED_INT = MethodHandles.lookup().findStatic(Byte.class, "toUnsignedInt",
@ -154,34 +145,10 @@ public final class MemoryHandles {
} }
/** /**
* Creates a memory access var handle with the given carrier type and byte order. * Creates a memory access var handle from given value layout. The provided layout will specify the
* * {@linkplain ValueLayout#carrier() carrier type}, the {@linkplain ValueLayout#byteSize() the byte size},
* The returned var handle's type is {@code carrier} and the list of coordinate types is * the {@linkplain ValueLayout#byteAlignment() byte alignment} and the {@linkplain ValueLayout#order() byte order}
* {@code (MemorySegment, long)}, where the {@code long} coordinate type corresponds to byte offset into * associated to the returned var handle.
* a given memory segment. The returned var handle accesses bytes at an offset in a given
* memory segment, composing bytes to or from a value of the type {@code carrier} according to the given endianness;
* the alignment constraint (in bytes) for the resulting memory access var handle is the same as the size (in bytes) of the
* carrier type {@code carrier}.
*
* @apiNote the resulting var handle features certain <a href="#memaccess-mode">access mode restrictions</a>,
* which are common to all memory access var handles.
*
* @param carrier the carrier type. Valid carriers are {@code byte}, {@code short}, {@code char}, {@code int},
* {@code float}, {@code long}, and {@code double}.
* @param byteOrder the required byte order.
* @return the new memory access var handle.
* @throws IllegalArgumentException when an illegal carrier type is used
*/
public static VarHandle varHandle(Class<?> carrier, ByteOrder byteOrder) {
Objects.requireNonNull(carrier);
Objects.requireNonNull(byteOrder);
return varHandle(carrier,
carrierSize(carrier),
byteOrder);
}
/**
* Creates a memory access var handle with the given carrier type, alignment constraint, and byte order.
* *
* The returned var handle's type is {@code carrier} and the list of coordinate types is * The returned var handle's type is {@code carrier} and the list of coordinate types is
* {@code (MemorySegment, long)}, where the {@code long} coordinate type corresponds to byte offset into * {@code (MemorySegment, long)}, where the {@code long} coordinate type corresponds to byte offset into
@ -192,56 +159,13 @@ public final class MemoryHandles {
* @apiNote the resulting var handle features certain <a href="#memaccess-mode">access mode restrictions</a>, * @apiNote the resulting var handle features certain <a href="#memaccess-mode">access mode restrictions</a>,
* which are common to all memory access var handles. * which are common to all memory access var handles.
* *
* @param carrier the carrier type. Valid carriers are {@code byte}, {@code short}, {@code char}, {@code int}, * @param layout the value layout for which a memory access handle is to be obtained.
* {@code float}, {@code long}, and {@code double}.
* @param alignmentBytes the alignment constraint (in bytes). Must be a power of two.
* @param byteOrder the required byte order.
* @return the new memory access var handle. * @return the new memory access var handle.
* @throws IllegalArgumentException if an illegal carrier type is used, or if {@code alignmentBytes} is not a power of two. * @throws IllegalArgumentException if an illegal carrier type is used, or if {@code alignmentBytes} is not a power of two.
*/ */
public static VarHandle varHandle(Class<?> carrier, long alignmentBytes, ByteOrder byteOrder) { public static VarHandle varHandle(ValueLayout layout) {
Objects.requireNonNull(carrier); Objects.requireNonNull(layout);
Objects.requireNonNull(byteOrder); return layout.accessHandle();
checkCarrier(carrier);
if (alignmentBytes <= 0
|| (alignmentBytes & (alignmentBytes - 1)) != 0) { // is power of 2?
throw new IllegalArgumentException("Bad alignment: " + alignmentBytes);
}
return Utils.fixUpVarHandle(JLI.memoryAccessVarHandle(carrier, false, alignmentBytes - 1, byteOrder));
}
/**
* Adapt an existing var handle into a new var handle whose carrier type is {@link MemorySegment}.
* That is, when calling {@link VarHandle#get(Object...)} on the returned var handle,
* the read numeric value will be turned into a memory address (as if by calling {@link MemoryAddress#ofLong(long)});
* similarly, when calling {@link VarHandle#set(Object...)}, the memory address to be set will be converted
* into a numeric value, and then written into memory. The amount of bytes read (resp. written) from (resp. to)
* memory depends on the carrier of the original memory access var handle.
*
* @param target the memory access var handle to be adapted
* @return the adapted var handle.
* @throws IllegalArgumentException if the carrier type of {@code varHandle} is either {@code boolean},
* {@code float}, or {@code double}, or is not a primitive type.
*/
public static VarHandle asAddressVarHandle(VarHandle target) {
Objects.requireNonNull(target);
Class<?> carrier = target.varType();
if (!carrier.isPrimitive() || carrier == boolean.class ||
carrier == float.class || carrier == double.class) {
throw new IllegalArgumentException("Unsupported carrier type: " + carrier.getName());
}
if (carrier != long.class) {
// slow-path, we need to adapt
return filterValue(target,
MethodHandles.explicitCastArguments(ADDRESS_TO_LONG, MethodType.methodType(carrier, MemoryAddress.class)),
MethodHandles.explicitCastArguments(LONG_TO_ADDRESS, MethodType.methodType(MemoryAddress.class, carrier)));
} else {
// fast-path
return filterValue(target, ADDRESS_TO_LONG, LONG_TO_ADDRESS);
}
} }
/** /**
@ -255,7 +179,7 @@ public final class MemoryHandles {
* the case if modeled as a Java {@code short}. This is illustrated in the following example: * the case if modeled as a Java {@code short}. This is illustrated in the following example:
* <blockquote><pre>{@code * <blockquote><pre>{@code
MemorySegment segment = MemorySegment.allocateNative(2, ResourceScope.newImplicitScope()); MemorySegment segment = MemorySegment.allocateNative(2, ResourceScope.newImplicitScope());
VarHandle SHORT_VH = MemoryLayouts.JAVA_SHORT.varHandle(short.class); VarHandle SHORT_VH = ValueLayout.JAVA_SHORT.varHandle();
VarHandle INT_VH = MemoryHandles.asUnsigned(SHORT_VH, int.class); VarHandle INT_VH = MemoryHandles.asUnsigned(SHORT_VH, int.class);
SHORT_VH.set(segment, (short)-1); SHORT_VH.set(segment, (short)-1);
INT_VH.get(segment); // returns 65535 INT_VH.get(segment); // returns 65535
@ -276,7 +200,7 @@ public final class MemoryHandles {
* <p> * <p>
* The returned var handle will feature the variable type {@code adaptedType}, * The returned var handle will feature the variable type {@code adaptedType},
* and the same access coordinates, the same access modes (see {@link * and the same access coordinates, the same access modes (see {@link
* java.lang.invoke.VarHandle.AccessMode}, and the same atomic access * java.lang.invoke.VarHandle.AccessMode}), and the same atomic access
* guarantees, as those featured by the {@code target} var handle. * guarantees, as those featured by the {@code target} var handle.
* *
* @param target the memory access var handle to be adapted * @param target the memory access var handle to be adapted
@ -284,7 +208,7 @@ public final class MemoryHandles {
* @return the adapted var handle. * @return the adapted var handle.
* @throws IllegalArgumentException if the carrier type of {@code target} * @throws IllegalArgumentException if the carrier type of {@code target}
* is not one of {@code byte}, {@code short}, or {@code int}; if {@code * is not one of {@code byte}, {@code short}, or {@code int}; if {@code
* adaptedType} is not one of {@code int}, or {@code long}; if the bitwidth * adaptedType} is not one of {@code int}, or {@code long}; if the bit width
* of the {@code adaptedType} is not greater than that of the {@code target} * of the {@code adaptedType} is not greater than that of the {@code target}
* carrier type. * carrier type.
* *
@ -324,12 +248,15 @@ public final class MemoryHandles {
* is processed using the second filter and returned to the caller. More advanced access mode types, such as * is processed using the second filter and returned to the caller. More advanced access mode types, such as
* {@link java.lang.invoke.VarHandle.AccessMode#COMPARE_AND_EXCHANGE} might apply both filters at the same time. * {@link java.lang.invoke.VarHandle.AccessMode#COMPARE_AND_EXCHANGE} might apply both filters at the same time.
* <p> * <p>
* For the boxing and unboxing filters to be well formed, their types must be of the form {@code (A... , S) -> T} and * For the boxing and unboxing filters to be well-formed, their types must be of the form {@code (A... , S) -> T} and
* {@code (A... , T) -> S}, respectively, where {@code T} is the type of the target var handle. If this is the case, * {@code (A... , T) -> S}, respectively, where {@code T} is the type of the target var handle. If this is the case,
* the resulting var handle will have type {@code S} and will feature the additional coordinates {@code A...} (which * the resulting var handle will have type {@code S} and will feature the additional coordinates {@code A...} (which
* will be appended to the coordinates of the target var handle). * will be appended to the coordinates of the target var handle).
* <p> * <p>
* The resulting var handle will feature the same access modes (see {@link java.lang.invoke.VarHandle.AccessMode} and * If the boxing and unboxing filters throw any checked exceptions when invoked, the resulting var handle will
* throw an {@link IllegalStateException}.
* <p>
* The resulting var handle will feature the same access modes (see {@link java.lang.invoke.VarHandle.AccessMode}) and
* atomic access guarantees as those featured by the target var handle. * atomic access guarantees as those featured by the target var handle.
* *
* @param target the target var handle * @param target the target var handle
@ -338,7 +265,7 @@ public final class MemoryHandles {
* @return an adapter var handle which accepts a new type, performing the provided boxing/unboxing conversions. * @return an adapter var handle which accepts a new type, performing the provided boxing/unboxing conversions.
* @throws IllegalArgumentException if {@code filterFromTarget} and {@code filterToTarget} are not well-formed, that is, they have types * @throws IllegalArgumentException if {@code filterFromTarget} and {@code filterToTarget} are not well-formed, that is, they have types
* other than {@code (A... , S) -> T} and {@code (A... , T) -> S}, respectively, where {@code T} is the type of the target var handle, * other than {@code (A... , S) -> T} and {@code (A... , T) -> S}, respectively, where {@code T} is the type of the target var handle,
* or if either {@code filterFromTarget} or {@code filterToTarget} throws any checked exceptions. * or if it's determined that either {@code filterFromTarget} or {@code filterToTarget} throws any checked exceptions.
*/ */
public static VarHandle filterValue(VarHandle target, MethodHandle filterToTarget, MethodHandle filterFromTarget) { public static VarHandle filterValue(VarHandle target, MethodHandle filterToTarget, MethodHandle filterFromTarget) {
return JLI.filterValue(target, filterToTarget, filterFromTarget); return JLI.filterValue(target, filterToTarget, filterFromTarget);
@ -353,9 +280,12 @@ public final class MemoryHandles {
* parameter types of the unary filter functions), and then passed (along with any coordinate that was left unaltered * parameter types of the unary filter functions), and then passed (along with any coordinate that was left unaltered
* by the adaptation) to the target var handle. * by the adaptation) to the target var handle.
* <p> * <p>
* For the coordinate filters to be well formed, their types must be of the form {@code S1 -> T1, S2 -> T1 ... Sn -> Tn}, * For the coordinate filters to be well-formed, their types must be of the form {@code S1 -> T1, S2 -> T1 ... Sn -> Tn},
* where {@code T1, T2 ... Tn} are the coordinate types starting at position {@code pos} of the target var handle. * where {@code T1, T2 ... Tn} are the coordinate types starting at position {@code pos} of the target var handle.
* <p> * <p>
* If any of the filters throws a checked exception when invoked, the resulting var handle will
* throw an {@link IllegalStateException}.
* <p>
* The resulting var handle will feature the same access modes (see {@link java.lang.invoke.VarHandle.AccessMode}) and * The resulting var handle will feature the same access modes (see {@link java.lang.invoke.VarHandle.AccessMode}) and
* atomic access guarantees as those featured by the target var handle. * atomic access guarantees as those featured by the target var handle.
* *
@ -368,7 +298,7 @@ public final class MemoryHandles {
* other than {@code S1 -> T1, S2 -> T2, ... Sn -> Tn} where {@code T1, T2 ... Tn} are the coordinate types starting * other than {@code S1 -> T1, S2 -> T2, ... Sn -> Tn} where {@code T1, T2 ... Tn} are the coordinate types starting
* at position {@code pos} of the target var handle, if {@code pos} is not between 0 and the target var handle coordinate arity, inclusive, * at position {@code pos} of the target var handle, if {@code pos} is not between 0 and the target var handle coordinate arity, inclusive,
* or if more filters are provided than the actual number of coordinate types available starting at {@code pos}, * or if more filters are provided than the actual number of coordinate types available starting at {@code pos},
* or if any of the filters throws any checked exceptions. * or if it's determined that any of the filters throws any checked exceptions.
*/ */
public static VarHandle filterCoordinates(VarHandle target, int pos, MethodHandle... filters) { public static VarHandle filterCoordinates(VarHandle target, int pos, MethodHandle... filters) {
return JLI.filterCoordinates(target, pos, filters); return JLI.filterCoordinates(target, pos, filters);
@ -382,7 +312,7 @@ public final class MemoryHandles {
* When calling e.g. {@link VarHandle#get(Object...)} on the resulting var handle, incoming coordinate values * When calling e.g. {@link VarHandle#get(Object...)} on the resulting var handle, incoming coordinate values
* are joined with bound coordinate values, and then passed to the target var handle. * are joined with bound coordinate values, and then passed to the target var handle.
* <p> * <p>
* For the bound coordinates to be well formed, their types must be {@code T1, T2 ... Tn }, * For the bound coordinates to be well-formed, their types must be {@code T1, T2 ... Tn },
* where {@code T1, T2 ... Tn} are the coordinate types starting at position {@code pos} of the target var handle. * where {@code T1, T2 ... Tn} are the coordinate types starting at position {@code pos} of the target var handle.
* <p> * <p>
* The resulting var handle will feature the same access modes (see {@link java.lang.invoke.VarHandle.AccessMode}) and * The resulting var handle will feature the same access modes (see {@link java.lang.invoke.VarHandle.AccessMode}) and
@ -409,7 +339,7 @@ public final class MemoryHandles {
* <p> * <p>
* The given array controls the reordering. * The given array controls the reordering.
* Call {@code #I} the number of incoming coordinates (the value * Call {@code #I} the number of incoming coordinates (the value
* {@code newCoordinates.size()}, and call {@code #O} the number * {@code newCoordinates.size()}), and call {@code #O} the number
* of outgoing coordinates (the number of coordinates associated with the target var handle). * of outgoing coordinates (the number of coordinates associated with the target var handle).
* Then the length of the reordering array must be {@code #O}, * Then the length of the reordering array must be {@code #O},
* and each element must be a non-negative number less than {@code #I}. * and each element must be a non-negative number less than {@code #I}.
@ -444,7 +374,7 @@ public final class MemoryHandles {
} }
/** /**
* Adapts a target var handle handle by pre-processing * Adapts a target var handle by pre-processing
* a sub-sequence of its coordinate values with a filter (a method handle). * a sub-sequence of its coordinate values with a filter (a method handle).
* The pre-processed coordinates are replaced by the result (if any) of the * The pre-processed coordinates are replaced by the result (if any) of the
* filter function and the target var handle is then called on the modified (usually shortened) * filter function and the target var handle is then called on the modified (usually shortened)
@ -464,6 +394,9 @@ public final class MemoryHandles {
* coordinate type of the target var handle at position {@code pos}, and that target var handle * coordinate type of the target var handle at position {@code pos}, and that target var handle
* coordinate is supplied by the return value of the filter. * coordinate is supplied by the return value of the filter.
* <p> * <p>
* If any of the filters throws a checked exception when invoked, the resulting var handle will
* throw an {@link IllegalStateException}.
* <p>
* The resulting var handle will feature the same access modes (see {@link java.lang.invoke.VarHandle.AccessMode}) and * The resulting var handle will feature the same access modes (see {@link java.lang.invoke.VarHandle.AccessMode}) and
* atomic access guarantees as those featured by the target var handle. * atomic access guarantees as those featured by the target var handle.
* *
@ -476,7 +409,7 @@ public final class MemoryHandles {
* is void, or it is not the same as the {@code pos} coordinate of the target var handle, * is void, or it is not the same as the {@code pos} coordinate of the target var handle,
* if {@code pos} is not between 0 and the target var handle coordinate arity, inclusive, * if {@code pos} is not between 0 and the target var handle coordinate arity, inclusive,
* if the resulting var handle's type would have <a href="MethodHandle.html#maxarity">too many coordinates</a>, * if the resulting var handle's type would have <a href="MethodHandle.html#maxarity">too many coordinates</a>,
* or if {@code filter} throws any checked exceptions. * or if it's determined that {@code filter} throws any checked exceptions.
*/ */
public static VarHandle collectCoordinates(VarHandle target, int pos, MethodHandle filter) { public static VarHandle collectCoordinates(VarHandle target, int pos, MethodHandle filter) {
return JLI.collectCoordinates(target, pos, filter); return JLI.collectCoordinates(target, pos, filter);
@ -505,24 +438,6 @@ public final class MemoryHandles {
return JLI.dropCoordinates(target, pos, valueTypes); return JLI.dropCoordinates(target, pos, valueTypes);
} }
private static void checkAddressFirstCoordinate(VarHandle handle) {
if (handle.coordinateTypes().size() < 1 ||
handle.coordinateTypes().get(0) != MemorySegment.class) {
throw new IllegalArgumentException("Expected var handle with leading coordinate of type MemorySegment");
}
}
private static void checkCarrier(Class<?> carrier) {
if (!carrier.isPrimitive() || carrier == void.class || carrier == boolean.class) {
throw new IllegalArgumentException("Illegal carrier: " + carrier.getSimpleName());
}
}
private static long carrierSize(Class<?> carrier) {
long bitsAlignment = Math.max(8, Wrapper.forPrimitiveType(carrier).bitWidth());
return Utils.bitsToBytesOrThrow(bitsAlignment, IllegalStateException::new);
}
private static void checkWidenable(Class<?> carrier) { private static void checkWidenable(Class<?> carrier) {
if (!(carrier == byte.class || carrier == short.class || carrier == int.class)) { if (!(carrier == byte.class || carrier == short.class || carrier == int.class)) {
throw new IllegalArgumentException("illegal carrier:" + carrier.getSimpleName()); throw new IllegalArgumentException("illegal carrier:" + carrier.getSimpleName());
@ -541,12 +456,4 @@ public final class MemoryHandles {
target.getSimpleName() + " is not wider than: " + carrier.getSimpleName()); target.getSimpleName() + " is not wider than: " + carrier.getSimpleName());
} }
} }
private static MemoryAddress longToAddress(long value) {
return MemoryAddress.ofLong(value);
}
private static long addressToLong(MemoryAddress value) {
return value.toRawLongValue();
}
} }

View file

@ -46,11 +46,11 @@ import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
/** /**
* A memory layout can be used to describe the contents of a memory segment in a <em>language neutral</em> fashion. * A memory layout can be used to describe 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 * 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 * {@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)}). * segment whose contents should be ignored, and which are primarily present for alignment reasons (see {@link MemoryLayout#paddingLayout(long)}).
* Some common value layout constants are defined in the {@link MemoryLayouts} class. * Some common value layout constants are defined in the {@link ValueLayout} class.
* <p> * <p>
* More complex layouts can be derived from simpler ones: a <em>sequence layout</em> denotes a repetition of one or more * More complex layouts can be derived from simpler ones: a <em>sequence layout</em> denotes a repetition of one or more
* element layout (see {@link SequenceLayout}); a <em>group layout</em> denotes an aggregation of (typically) heterogeneous * element layout (see {@link SequenceLayout}); a <em>group layout</em> denotes an aggregation of (typically) heterogeneous
@ -70,9 +70,9 @@ import java.util.stream.Stream;
* <blockquote><pre>{@code * <blockquote><pre>{@code
SequenceLayout taggedValues = MemoryLayout.sequenceLayout(5, SequenceLayout taggedValues = MemoryLayout.sequenceLayout(5,
MemoryLayout.structLayout( MemoryLayout.structLayout(
MemoryLayout.valueLayout(8, ByteOrder.nativeOrder()).withName("kind"), ValueLayout.JAVA_BYTE.withName("kind"),
MemoryLayout.paddingLayout(24), MemoryLayout.paddingLayout(24),
MemoryLayout.valueLayout(32, ByteOrder.nativeOrder()).withName("value") ValueLayout.JAVA_INT.withName("value")
) )
).withName("TaggedValues"); ).withName("TaggedValues");
* }</pre></blockquote> * }</pre></blockquote>
@ -81,8 +81,6 @@ SequenceLayout taggedValues = MemoryLayout.sequenceLayout(5,
* programmers should treat instances that are {@linkplain #equals(Object) equal} as interchangeable and should not * programmers should treat instances that are {@linkplain #equals(Object) equal} as interchangeable and should not
* use instances for synchronization, or unpredictable behavior may occur. For example, in a future release, * use instances for synchronization, or unpredictable behavior may occur. For example, in a future release,
* synchronization may fail. The {@code equals} method should be used for comparisons. * synchronization may fail. The {@code equals} method should be used for comparisons.
* <p>
* Non-platform classes should not implement {@linkplain MemoryLayout} directly.
* *
* <p> Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null} * <p> Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null}
* elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown. </p> * elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown. </p>
@ -122,11 +120,11 @@ SequenceLayout taggedValues = MemoryLayout.sequenceLayout(5,
* at a layout nested within the root layout - this is the layout <em>selected</em> by the layout path. * at a layout nested within the root layout - this is the layout <em>selected</em> by the layout path.
* Layout paths are typically expressed as a sequence of one or more {@link PathElement} instances. * Layout paths are typically expressed as a sequence of one or more {@link PathElement} instances.
* <p> * <p>
* Layout paths are for example useful in order to obtain offsets of arbitrarily nested layouts inside another layout * Layout paths are for example useful in order to obtain {@linkplain MemoryLayout#bitOffset(PathElement...) offsets} of
* (see {@link MemoryLayout#bitOffset(PathElement...)}), to quickly obtain a memory access handle corresponding to the selected * arbitrarily nested layouts inside another layout, to quickly obtain a {@linkplain #varHandle(PathElement...) memory access handle}
* layout (see {@link MemoryLayout#varHandle(Class, PathElement...)}), to select an arbitrarily nested layout inside * corresponding to the selected layout, to {@linkplain #select(PathElement...) select} an arbitrarily nested layout inside
* another layout (see {@link MemoryLayout#select(PathElement...)}, or to transform a nested layout element inside * another layout, or to {@link #map(UnaryOperator, PathElement...) transform} a nested layout element inside
* another layout (see {@link MemoryLayout#map(UnaryOperator, PathElement...)}). * another layout.
* <p> * <p>
* Such <em>layout paths</em> can be constructed programmatically using the methods in this class. * Such <em>layout paths</em> can be constructed programmatically using the methods in this class.
* For instance, given the {@code taggedValues} layout instance constructed as above, we can obtain the offset, * For instance, given the {@code taggedValues} layout instance constructed as above, we can obtain the offset,
@ -152,7 +150,7 @@ MemoryLayout taggedValuesWithHole = taggedValues.map(l -> MemoryLayout.paddingLa
* <blockquote><pre>{@code * <blockquote><pre>{@code
MemoryLayout taggedValuesWithHole = MemoryLayout.sequenceLayout(5, MemoryLayout taggedValuesWithHole = MemoryLayout.sequenceLayout(5,
MemoryLayout.structLayout( MemoryLayout.structLayout(
MemoryLayout.valueLayout(8, ByteOrder.nativeOrder()).withName("kind"), ValueLayout.JAVA_BYTE.withName("kind"),
MemoryLayout.paddingLayout(32), MemoryLayout.paddingLayout(32),
MemoryLayout.paddingLayout(32) MemoryLayout.paddingLayout(32)
)); ));
@ -164,8 +162,7 @@ MemoryLayout taggedValuesWithHole = MemoryLayout.sequenceLayout(5,
* This is important when obtaining memory access var handle from layouts, as in the following code: * This is important when obtaining memory access var handle from layouts, as in the following code:
* *
* <blockquote><pre>{@code * <blockquote><pre>{@code
VarHandle valueHandle = taggedValues.varHandle(int.class, VarHandle valueHandle = taggedValues.varHandle(PathElement.sequenceElement(),
PathElement.sequenceElement(),
PathElement.groupElement("value")); PathElement.groupElement("value"));
* }</pre></blockquote> * }</pre></blockquote>
* *
@ -189,9 +186,7 @@ long offset2 = (long) offsetHandle.invokeExact(2L); // 16
* *
* <h2>Layout attributes</h2> * <h2>Layout attributes</h2>
* *
* Layouts can be optionally associated with one or more <em>attributes</em>. A layout attribute forms a <em>name/value</em> * Layouts can be optionally associated with a <em>name</em>. A layout name can be referred to when
* pair, where the name is a {@link String} and the value is a {@link Constable}. The most common form of layout attribute
* is the <em>layout name</em> (see {@link #LAYOUT_NAME}), a custom name that can be associated with memory layouts and that can be referred to when
* constructing <a href="MemoryLayout.html#layout-paths"><em>layout paths</em></a>. * constructing <a href="MemoryLayout.html#layout-paths"><em>layout paths</em></a>.
* *
* @implSpec * @implSpec
@ -236,18 +231,10 @@ public sealed interface MemoryLayout extends Constable permits AbstractLayout, S
* @throws UnsupportedOperationException if the layout is, or contains, a sequence layout with unspecified size (see {@link SequenceLayout}), * @throws UnsupportedOperationException if the layout is, or contains, a sequence layout with unspecified size (see {@link SequenceLayout}),
* or if {@code bitSize()} is not a multiple of 8. * or if {@code bitSize()} is not a multiple of 8.
*/ */
default long byteSize() { long byteSize();
return Utils.bitsToBytesOrThrow(bitSize(),
() -> new UnsupportedOperationException("Cannot compute byte size; bit size is not a multiple of 8"));
}
/** /**
* Return the <em>name</em> (if any) associated with this layout. * Return the <em>name</em> (if any) associated with this layout.
* <p>
* This is equivalent to the following code:
* <blockquote><pre>{@code
attribute(LAYOUT_NAME).map(String.class::cast);
* }</pre></blockquote>
* *
* @return the layout <em>name</em> (if any). * @return the layout <em>name</em> (if any).
* @see MemoryLayout#withName(String) * @see MemoryLayout#withName(String)
@ -256,11 +243,6 @@ public sealed interface MemoryLayout extends Constable permits AbstractLayout, S
/** /**
* Creates a new layout which features the desired layout <em>name</em>. * Creates a new layout which features the desired layout <em>name</em>.
* <p>
* This is equivalent to the following code:
* <blockquote><pre>{@code
withAttribute(LAYOUT_NAME, name);
* }</pre></blockquote>
* *
* @param name the layout name. * @param name the layout name.
* @return a new layout which is the same as this layout, except for the <em>name</em> associated with it. * @return a new layout which is the same as this layout, except for the <em>name</em> associated with it.
@ -313,36 +295,10 @@ public sealed interface MemoryLayout extends Constable permits AbstractLayout, S
* *
* @param bitAlignment the layout alignment constraint, expressed in bits. * @param bitAlignment the layout alignment constraint, expressed in bits.
* @return a new layout which is the same as this layout, except for the alignment constraint associated with it. * @return a new layout which is the same as this layout, except for the alignment constraint associated with it.
* @throws IllegalArgumentException if {@code bitAlignment} is not a power of two, or if it's less than than 8. * @throws IllegalArgumentException if {@code bitAlignment} is not a power of two, or if it's less than 8.
*/ */
MemoryLayout withBitAlignment(long bitAlignment); MemoryLayout withBitAlignment(long bitAlignment);
/**
* Returns the attribute with the given name (if it exists).
*
* @param name the attribute name
* @return the attribute with the given name (if it exists).
*/
Optional<Constable> attribute(String name);
/**
* Returns a new memory layout which features the same attributes as this layout, plus the newly specified attribute.
* If this layout already contains an attribute with the same name, the existing attribute value is overwritten in the returned
* layout.
*
* @param name the attribute name.
* @param value the attribute value.
* @return a new memory layout which features the same attributes as this layout, plus the newly specified attribute.
*/
MemoryLayout withAttribute(String name, Constable value);
/**
* Returns a stream of the attribute names associated with this layout.
*
* @return a stream of the attribute names associated with this layout.
*/
Stream<String> attributes();
/** /**
* Computes the offset, in bits, of the layout selected by a given layout path, where the path is considered rooted in this * Computes the offset, in bits, of the layout selected by a given layout path, where the path is considered rooted in this
* layout. * layout.
@ -366,7 +322,7 @@ public sealed interface MemoryLayout extends Constable permits AbstractLayout, S
* by a given layout path, where the path is considered rooted in this layout. * by a given layout path, where the path is considered rooted in this layout.
* *
* <p>The returned method handle has a return type of {@code long}, and features as many {@code long} * <p>The returned method handle has a return type of {@code long}, and features as many {@code long}
* parameter types as there are free dimensions in the provided layout path (see {@link PathElement#sequenceElement()}, * parameter types as there are free dimensions in the provided layout path (see {@link PathElement#sequenceElement()}),
* where the order of the parameters corresponds to the order of the path elements. * where the order of the parameters corresponds to the order of the path elements.
* The returned method handle can be used to compute a layout offset similar to {@link #bitOffset(PathElement...)}, * The returned method handle can be used to compute a layout offset similar to {@link #bitOffset(PathElement...)},
* but where some sequence indices are specified only when invoking the method handle. * but where some sequence indices are specified only when invoking the method handle.
@ -417,7 +373,7 @@ public sealed interface MemoryLayout extends Constable permits AbstractLayout, S
* by a given layout path, where the path is considered rooted in this layout. * by a given layout path, where the path is considered rooted in this layout.
* *
* <p>The returned method handle has a return type of {@code long}, and features as many {@code long} * <p>The returned method handle has a return type of {@code long}, and features as many {@code long}
* parameter types as there are free dimensions in the provided layout path (see {@link PathElement#sequenceElement()}, * parameter types as there are free dimensions in the provided layout path (see {@link PathElement#sequenceElement()}),
* where the order of the parameters corresponds to the order of the path elements. * where the order of the parameters corresponds to the order of the path elements.
* The returned method handle can be used to compute a layout offset similar to {@link #byteOffset(PathElement...)}, * The returned method handle can be used to compute a layout offset similar to {@link #byteOffset(PathElement...)},
* but where some sequence indices are specified only when invoking the method handle. * but where some sequence indices are specified only when invoking the method handle.
@ -477,18 +433,14 @@ public sealed interface MemoryLayout extends Constable permits AbstractLayout, S
* unspecified sequence access component contained in this layout path. Moreover, the resulting var handle * unspecified sequence access component contained in this layout path. Moreover, the resulting var handle
* features certain <a href="MemoryHandles.html#memaccess-mode">access mode restrictions</a>, which are common to all memory access var handles. * features certain <a href="MemoryHandles.html#memaccess-mode">access mode restrictions</a>, which are common to all memory access var handles.
* *
* @param carrier the var handle carrier type.
* @param elements the layout path elements. * @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}. * @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, * @throws UnsupportedOperationException if the layout path has one or more elements with incompatible alignment constraints,
* or if one of the layouts traversed by the layout path has unspecified size. * or if one of the layouts traversed by the layout path has unspecified size.
* @throws IllegalArgumentException if the carrier does not represent a primitive type, if the carrier is {@code void}, * @throws IllegalArgumentException if the layout path in {@code elements} does not select a value layout (see {@link ValueLayout}).
* {@code boolean}, or if the layout path in {@code elements} does not select a value layout (see {@link ValueLayout}),
* or if the selected value layout has a size that that does not match that of the specified carrier type.
*/ */
default VarHandle varHandle(Class<?> carrier, PathElement... elements) { default VarHandle varHandle(PathElement... elements) {
Objects.requireNonNull(carrier); return computePathOp(LayoutPath.rootPath(this, MemoryLayout::bitSize), LayoutPath::dereferenceHandle,
return computePathOp(LayoutPath.rootPath(this, MemoryLayout::bitSize), path -> path.dereferenceHandle(carrier),
Set.of(), elements); Set.of(), elements);
} }
@ -498,7 +450,7 @@ public sealed interface MemoryLayout extends Constable permits AbstractLayout, S
* *
* <p>The returned method handle has a return type of {@code MemorySegment}, features a {@code MemorySegment} * <p>The returned method handle has a return type of {@code MemorySegment}, features a {@code MemorySegment}
* parameter as leading parameter representing the segment to be sliced, and features as many trailing {@code long} * parameter as leading parameter representing the segment to be sliced, and features as many trailing {@code long}
* parameter types as there are free dimensions in the provided layout path (see {@link PathElement#sequenceElement()}, * parameter types as there are free dimensions in the provided layout path (see {@link PathElement#sequenceElement()}),
* where the order of the parameters corresponds to the order of the path elements. * where the order of the parameters corresponds to the order of the path elements.
* The returned method handle can be used to create a slice similar to using {@link MemorySegment#asSlice(long, long)}, * The returned method handle can be used to create a slice similar to using {@link MemorySegment#asSlice(long, long)},
* but where the offset argument is dynamically compute based on indices specified when invoking the method handle. * but where the offset argument is dynamically compute based on indices specified when invoking the method handle.
@ -515,7 +467,7 @@ public sealed interface MemoryLayout extends Constable permits AbstractLayout, S
* and {@code s_0}, {@code s_1}, ... {@code s_n} are <em>static</em> stride constants which are derived from * and {@code s_0}, {@code s_1}, ... {@code s_n} are <em>static</em> stride constants which are derived from
* the layout path. * the layout path.
* *
* <p>After the offset is computed, the returned segment is create as if by calling: * <p>After the offset is computed, the returned segment is created as if by calling:
* <blockquote><pre>{@code * <blockquote><pre>{@code
segment.asSlice(offset, layout.byteSize()); segment.asSlice(offset, layout.byteSize());
* }</pre></blockquote> * }</pre></blockquote>
@ -582,7 +534,7 @@ public sealed interface MemoryLayout extends Constable permits AbstractLayout, S
} }
/** /**
* Is this a padding layout (e.g. a layout created from {@link #paddingLayout(long)}) ? * Is this a {@linkplain #paddingLayout(long) padding layout} ?
* @return true, if this layout is a padding layout. * @return true, if this layout is a padding layout.
*/ */
boolean isPadding(); boolean isPadding();
@ -595,8 +547,6 @@ public sealed interface MemoryLayout extends Constable permits AbstractLayout, S
* of sequence element layout can be <em>explicit</em> (see {@link PathElement#sequenceElement(long)}) or * of sequence element layout can be <em>explicit</em> (see {@link PathElement#sequenceElement(long)}) or
* <em>implicit</em> (see {@link PathElement#sequenceElement()}). When a path uses one or more implicit * <em>implicit</em> (see {@link PathElement#sequenceElement()}). When a path uses one or more implicit
* sequence path elements, it acquires additional <em>free dimensions</em>. * sequence path elements, it acquires additional <em>free dimensions</em>.
* <p>
* Non-platform classes should not implement {@linkplain PathElement} directly.
* *
* <p> Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null} * <p> Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null}
* elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown. </p> * elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown. </p>
@ -612,7 +562,7 @@ public sealed interface MemoryLayout extends Constable permits AbstractLayout, S
* that is combined with such element. * that is combined with such element.
* *
* @implSpec in case multiple group elements with a matching name exist, the path element returned by this * @implSpec in case multiple group elements with a matching name exist, the path element returned by this
* method will select the first one; that is, the group element with lowest offset from current path is selected. * method will select the first one; that is, the group element with the lowest offset from current path is selected.
* *
* @param name the name of the group element to be selected. * @param name the name of the group element to be selected.
* @return a path element which selects the group element with given name. * @return a path element which selects the group element with given name.
@ -728,17 +678,48 @@ E * (S + I * F)
} }
/** /**
* Create a value layout of given byte order and size. * Creates a value layout of given Java carrier and byte order. The type of resulting value layout is determined
* * by the carrier provided:
* @param size the value layout size. * <ul>
* <li>{@link ValueLayout.OfBoolean}, for {@code boolean.class}</li>
* <li>{@link ValueLayout.OfByte}, for {@code byte.class}</li>
* <li>{@link ValueLayout.OfShort}, for {@code short.class}</li>
* <li>{@link ValueLayout.OfChar}, for {@code char.class}</li>
* <li>{@link ValueLayout.OfInt}, for {@code int.class}</li>
* <li>{@link ValueLayout.OfFloat}, for {@code float.class}</li>
* <li>{@link ValueLayout.OfLong}, for {@code long.class}</li>
* <li>{@link ValueLayout.OfDouble}, for {@code double.class}</li>
* <li>{@link ValueLayout.OfAddress}, for {@code MemoryAddress.class}</li>
* </ul>
* @param carrier the value layout carrier.
* @param order the value layout's byte order. * @param order the value layout's byte order.
* @return a new value layout. * @return a new value layout.
* @throws IllegalArgumentException if {@code size <= 0}. * @throws IllegalArgumentException if the carrier type is not supported.
*/ */
static ValueLayout valueLayout(long size, ByteOrder order) { static ValueLayout valueLayout(Class<?> carrier, ByteOrder order) {
Objects.requireNonNull(carrier);
Objects.requireNonNull(order); Objects.requireNonNull(order);
AbstractLayout.checkSize(size); if (carrier == boolean.class) {
return new ValueLayout(order, size); return new ValueLayout.OfBoolean(order);
} else if (carrier == char.class) {
return new ValueLayout.OfChar(order);
} else if (carrier == byte.class) {
return new ValueLayout.OfByte(order);
} else if (carrier == short.class) {
return new ValueLayout.OfShort(order);
} else if (carrier == int.class) {
return new ValueLayout.OfInt(order);
} else if (carrier == float.class) {
return new ValueLayout.OfFloat(order);
} else if (carrier == long.class) {
return new ValueLayout.OfLong(order);
} else if (carrier == double.class) {
return new ValueLayout.OfDouble(order);
} else if (carrier == MemoryAddress.class) {
return new ValueLayout.OfAddress(order);
} else {
throw new IllegalArgumentException("Unsupported carrier: " + carrier.getName());
}
} }
/** /**
@ -792,9 +773,4 @@ E * (S + I * F)
.map(Objects::requireNonNull) .map(Objects::requireNonNull)
.collect(Collectors.toList())); .collect(Collectors.toList()));
} }
/**
* Attribute name used to specify the <em>name</em> property of a memory layout (see {@link #name()} and {@link #withName(String)}).
*/
String LAYOUT_NAME = "layout/name";
} }

View file

@ -1,157 +0,0 @@
/*
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package jdk.incubator.foreign;
import jdk.internal.misc.Unsafe;
import java.nio.ByteOrder;
/**
* This class defines useful layout constants. Some of the constants defined in this class are explicit in both
* size and byte order (see {@link #BITS_64_BE}), and can therefore be used to explicitly and unambiguously specify the
* contents of a memory segment. Other constants make implicit byte order assumptions (see
* {@link #JAVA_INT}); as such, these constants make it easy to work with other serialization-centric APIs,
* such as {@link java.nio.ByteBuffer}.
*/
public final class MemoryLayouts {
private MemoryLayouts() {
//just the one, please
}
/**
* A value layout constant with size of one byte, and byte order set to {@link ByteOrder#LITTLE_ENDIAN}.
*/
public static final ValueLayout BITS_8_LE = MemoryLayout.valueLayout(8, ByteOrder.LITTLE_ENDIAN);
/**
* A value layout constant with size of two bytes, and byte order set to {@link ByteOrder#LITTLE_ENDIAN}.
*/
public static final ValueLayout BITS_16_LE = MemoryLayout.valueLayout(16, ByteOrder.LITTLE_ENDIAN);
/**
* A value layout constant with size of four bytes, and byte order set to {@link ByteOrder#LITTLE_ENDIAN}.
*/
public static final ValueLayout BITS_32_LE = MemoryLayout.valueLayout(32, ByteOrder.LITTLE_ENDIAN);
/**
* A value layout constant with size of eight bytes, and byte order set to {@link ByteOrder#LITTLE_ENDIAN}.
*/
public static final ValueLayout BITS_64_LE = MemoryLayout.valueLayout(64, ByteOrder.LITTLE_ENDIAN);
/**
* A value layout constant with size of one byte, and byte order set to {@link ByteOrder#BIG_ENDIAN}.
*/
public static final ValueLayout BITS_8_BE = MemoryLayout.valueLayout(8, ByteOrder.BIG_ENDIAN);
/**
* A value layout constant with size of two bytes, and byte order set to {@link ByteOrder#BIG_ENDIAN}.
*/
public static final ValueLayout BITS_16_BE = MemoryLayout.valueLayout(16, ByteOrder.BIG_ENDIAN);
/**
* A value layout constant with size of four bytes, and byte order set to {@link ByteOrder#BIG_ENDIAN}.
*/
public static final ValueLayout BITS_32_BE = MemoryLayout.valueLayout(32, ByteOrder.BIG_ENDIAN);
/**
* A value layout constant with size of eight bytes, and byte order set to {@link ByteOrder#BIG_ENDIAN}.
*/
public static final ValueLayout BITS_64_BE = MemoryLayout.valueLayout(64, ByteOrder.BIG_ENDIAN);
/**
* A padding layout constant with size of one byte.
*/
public static final MemoryLayout PAD_8 = MemoryLayout.paddingLayout(8);
/**
* A padding layout constant with size of two bytes.
*/
public static final MemoryLayout PAD_16 = MemoryLayout.paddingLayout(16);
/**
* A padding layout constant with size of four bytes.
*/
public static final MemoryLayout PAD_32 = MemoryLayout.paddingLayout(32);
/**
* A padding layout constant with size of eight bytes.
*/
public static final MemoryLayout PAD_64 = MemoryLayout.paddingLayout(64);
/**
* A value layout constant whose size is the same as that of a machine address (e.g. {@code size_t}), and byte order set to {@link ByteOrder#nativeOrder()}.
*/
public static final ValueLayout ADDRESS = MemoryLayout.valueLayout(Unsafe.ADDRESS_SIZE * 8, ByteOrder.nativeOrder());
/**
* A value layout constant whose size is the same as that of a Java {@code byte}, and byte order set to {@link ByteOrder#nativeOrder()}.
*/
public static final ValueLayout JAVA_BYTE = MemoryLayout.valueLayout(8, ByteOrder.nativeOrder());
/**
* A value layout constant whose size is the same as that of a Java {@code char}, and byte order set to {@link ByteOrder#nativeOrder()}.
*/
public static final ValueLayout JAVA_CHAR = MemoryLayout.valueLayout(16, ByteOrder.nativeOrder());
/**
* A value layout constant whose size is the same as that of a Java {@code short}, and byte order set to {@link ByteOrder#nativeOrder()}.
*/
public static final ValueLayout JAVA_SHORT = MemoryLayout.valueLayout(16, ByteOrder.nativeOrder());
/**
* A value layout constant whose size is the same as that of a Java {@code int}, and byte order set to {@link ByteOrder#nativeOrder()}.
*/
public static final ValueLayout JAVA_INT = MemoryLayout.valueLayout(32, ByteOrder.nativeOrder());
/**
* A value layout constant whose size is the same as that of a Java {@code long}, and byte order set to {@link ByteOrder#nativeOrder()}.
* The alignment of this layout (see {@link MemoryLayout#byteAlignment()} is platform-dependent, so that the following
* invariant holds:
* <blockquote><pre>{@code
MemoryLayouts.JAVA_LONG.byteAlignment() == MemoryLayouts.ADDRESS.byteSize();
* }</pre></blockquote>
*/
public static final ValueLayout JAVA_LONG = MemoryLayout.valueLayout(64, ByteOrder.nativeOrder())
.withBitAlignment(ADDRESS.bitSize());
/**
* A value layout constant whose size is the same as that of a Java {@code float}, and byte order set to {@link ByteOrder#nativeOrder()}.
*/
public static final ValueLayout JAVA_FLOAT = MemoryLayout.valueLayout(32, ByteOrder.nativeOrder());
/**
* A value layout constant whose size is the same as that of a Java {@code double}, and byte order set to {@link ByteOrder#nativeOrder()}.
* The alignment of this layout (see {@link MemoryLayout#byteAlignment()} is platform-dependent, so that the following
* invariant holds:
* <blockquote><pre>{@code
MemoryLayouts.JAVA_DOUBLE.byteAlignment() == MemoryLayouts.ADDRESS.byteSize();
* }</pre></blockquote>
*/
public static final ValueLayout JAVA_DOUBLE = MemoryLayout.valueLayout(64, ByteOrder.nativeOrder())
.withBitAlignment(ADDRESS.bitSize());
}

View file

@ -0,0 +1,89 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package jdk.incubator.foreign;
import jdk.internal.foreign.NativeSymbolImpl;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import java.lang.invoke.MethodHandle;
import java.util.Objects;
/**
* A native symbol models a reference to a location (typically the entry point of a function) in a native library.
* A native symbol has a name, and is associated with a scope, which governs the native symbol's lifecycle.
* This is useful, since the library a native symbol refers to can be <em>unloaded</em>, thus invalidating the native symbol.
* While native symbols are typically obtained using a {@link SymbolLookup#lookup(String) symbol lookup}, it is also possible to obtain an
* <em>anonymous</em> native symbol, in the form of an {@linkplain CLinker#upcallStub(MethodHandle, FunctionDescriptor, ResourceScope) upcall stub},
* that is, a reference to a dynamically-generated native symbol which can be used to call back into Java code.
*/
sealed public interface NativeSymbol extends Addressable permits NativeSymbolImpl {
/**
* Returns the name of this symbol.
* @return the name of this symbol.
*/
String name();
/**
* Returns the resource scope associated with this symbol.
* @return the resource scope associated with this symbol.
*/
ResourceScope scope();
/**
* Returns the memory address associated with this symbol.
* @throws IllegalStateException if the scope associated with this symbol has been closed, or if access occurs from
* a thread other than the thread owning that scope.
* @return The memory address associated with this symbol.
*/
@Override
MemoryAddress address();
/**
* Creates a new symbol from given name, address and scope.
* <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 name the symbol name.
* @param address the symbol address.
* @param scope the symbol scope.
* @return A new symbol from given name, address and scope.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
*/
@CallerSensitive
static NativeSymbol ofAddress(String name, MemoryAddress address, ResourceScope scope) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
Objects.requireNonNull(name);
Objects.requireNonNull(address);
Objects.requireNonNull(scope);
return new NativeSymbolImpl(name, address, scope);
}
}

View file

@ -25,10 +25,8 @@
*/ */
package jdk.incubator.foreign; package jdk.incubator.foreign;
import java.lang.constant.Constable;
import java.lang.constant.ConstantDescs; import java.lang.constant.ConstantDescs;
import java.lang.constant.DynamicConstantDesc; import java.lang.constant.DynamicConstantDesc;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalLong; import java.util.OptionalLong;
@ -53,11 +51,11 @@ import java.util.OptionalLong;
/* package-private */ final class PaddingLayout extends AbstractLayout implements MemoryLayout { /* package-private */ final class PaddingLayout extends AbstractLayout implements MemoryLayout {
PaddingLayout(long size) { PaddingLayout(long size) {
this(size, 1, Map.of()); this(size, 1, Optional.empty());
} }
PaddingLayout(long size, long alignment, Map<String, Constable> attributes) { PaddingLayout(long size, long alignment, Optional<String> name) {
super(OptionalLong.of(size), alignment, attributes); super(OptionalLong.of(size), alignment, name);
} }
@Override @Override
@ -73,10 +71,9 @@ import java.util.OptionalLong;
if (!super.equals(other)) { if (!super.equals(other)) {
return false; return false;
} }
if (!(other instanceof PaddingLayout)) { if (!(other instanceof PaddingLayout p)) {
return false; return false;
} }
PaddingLayout p = (PaddingLayout)other;
return bitSize() == p.bitSize(); return bitSize() == p.bitSize();
} }
@ -86,8 +83,8 @@ import java.util.OptionalLong;
} }
@Override @Override
PaddingLayout dup(long alignment, Map<String, Constable> attributes) { PaddingLayout dup(long alignment, Optional<String> name) {
return new PaddingLayout(bitSize(), alignment, attributes); return new PaddingLayout(bitSize(), alignment, name);
} }
@Override @Override
@ -119,12 +116,4 @@ import java.util.OptionalLong;
public PaddingLayout withBitAlignment(long alignmentBits) { public PaddingLayout withBitAlignment(long alignmentBits) {
return (PaddingLayout)super.withBitAlignment(alignmentBits); return (PaddingLayout)super.withBitAlignment(alignmentBits);
} }
/**
* {@inheritDoc}
*/
@Override
public PaddingLayout withAttribute(String name, Constable value) {
return (PaddingLayout)super.withAttribute(name, value);
}
} }

View file

@ -26,9 +26,11 @@
package jdk.incubator.foreign; package jdk.incubator.foreign;
import jdk.internal.foreign.ResourceScopeImpl; import jdk.internal.foreign.ResourceScopeImpl;
import jdk.internal.ref.CleanerFactory;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.ref.Cleaner; import java.lang.ref.Cleaner;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Objects; import java.util.Objects;
@ -36,109 +38,109 @@ import java.util.Spliterator;
/** /**
* A resource scope manages the lifecycle of one or more resources. Resources (e.g. {@link MemorySegment}) associated * A resource scope manages the lifecycle of one or more resources. Resources (e.g. {@link MemorySegment}) associated
* with a resource scope can only be accessed while the resource scope is <em>alive</em> (see {@link #isAlive()}), * with a resource scope can only be accessed while the resource scope is {@linkplain #isAlive() alive},
* and by the thread associated with the resource scope (if any). * and by the {@linkplain #ownerThread() thread} associated with the resource scope (if any).
* *
* <h2>Explicit resource scopes</h2> * <h2>Deterministic deallocation</h2>
* *
* Resource scopes obtained from {@link #newConfinedScope()}, {@link #newSharedScope()} support <em>deterministic deallocation</em>; * Resource scopes support <em>deterministic deallocation</em>; that is, they can be {@linkplain ResourceScope#close() closed}
* We call these resource scopes <em>explicit scopes</em>. Explicit resource scopes can be closed explicitly (see {@link ResourceScope#close()}). * explicitly. When a resource scope is closed, it is no longer {@link #isAlive() alive}, and subsequent
* When a resource scope is closed, it is no longer <em>alive</em> (see {@link #isAlive()}, and subsequent operations on * operations on resources associated with that scope (e.g. attempting to access a {@link MemorySegment} instance)
* resources associated with that scope (e.g. attempting to access a {@link MemorySegment} instance) will fail with {@link IllegalStateException}. * will fail with {@link IllegalStateException}.
* <p> * <p>
* Closing a resource scope will cause all the cleanup actions associated with that scope (see {@link #addCloseAction(Runnable)}) to be called. * Closing a resource scope will cause all the {@linkplain #addCloseAction(Runnable) close actions} associated with that scope to be called.
* Moreover, closing a resource scope might trigger the releasing of the underlying memory resources associated with said scope; for instance: * Moreover, closing a resource scope might trigger the releasing of the underlying memory resources associated with said scope; for instance:
* <ul> * <ul>
* <li>closing the scope associated with a native memory segment results in <em>freeing</em> the native memory associated with it * <li>closing the scope associated with a {@linkplain MemorySegment#allocateNative(long, long, ResourceScope) native memory segment}
* (see {@link MemorySegment#allocateNative(long, ResourceScope)}, or {@link SegmentAllocator#arenaAllocator(ResourceScope)})</li> * results in <em>freeing</em> the native memory associated with it;</li>
* <li>closing the scope associated with a mapped memory segment results in the backing memory-mapped file to be unmapped * <li>closing the scope associated with a {@linkplain MemorySegment#mapFile(Path, long, long, FileChannel.MapMode, ResourceScope) mapped memory segment}
* (see {@link MemorySegment#mapFile(Path, long, long, FileChannel.MapMode, ResourceScope)})</li> * results in the backing memory-mapped file to be unmapped;</li>
* <li>closing the scope associated with an upcall stub results in releasing the stub * <li>closing the scope associated with an {@linkplain CLinker#upcallStub(MethodHandle, FunctionDescriptor, ResourceScope) upcall stub}
* (see {@link CLinker#upcallStub(MethodHandle, FunctionDescriptor, ResourceScope)}</li> * results in releasing the stub;</li>
* <li>closing the scope associated with a {@linkplain VaList variable arity list} results in releasing the memory
* associated with that variable arity list instance.</li>
* </ul> * </ul>
* <p>
* Sometimes, explicit scopes can be associated with a {@link Cleaner} instance (see {@link #newConfinedScope(Cleaner)} and
* {@link #newSharedScope(Cleaner)}). We call these resource scopes <em>managed</em> resource scopes. A managed resource scope
* is closed automatically once the scope instance becomes <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>.
* <p>
* Managed scopes can be useful to allow for predictable, deterministic resource deallocation, while still prevent accidental native memory leaks.
* In case a managed resource scope is closed explicitly, no further action will be taken when the scope becomes unreachable;
* that is, cleanup actions (see {@link #addCloseAction(Runnable)}) associated with a resource scope, whether managed or not,
* are called <em>exactly once</em>.
* *
* <h2>Implicit resource scopes</h2> * <h2>Implicit deallocation</h2>
*
* Resource scopes can be associated with a {@link Cleaner} instance, so that they are also closed automatically,
* once the scope 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 managed resource scope is closed explicitly, no further action will be taken when
* the scope becomes unreachable; that is, {@linkplain #addCloseAction(Runnable) close actions} associated with a
* resource scope, whether managed or not, are called <em>exactly once</em>.
*
* <h2><a id = "global-scope">Global scope</a></h2>
* *
* Resource scopes obtained from {@link #newImplicitScope()} cannot be closed explicitly. We call these resource scopes
* <em>implicit scopes</em>. Calling {@link #close()} on an implicit resource scope always results in an exception.
* Resources associated with implicit scopes are released once the scope instance becomes
* <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>.
* <p>
* An important implicit resource scope is the so called {@linkplain #globalScope() global scope}; the global scope is * An important implicit resource scope is the so called {@linkplain #globalScope() global scope}; the global scope is
* an implicit scope that is guaranteed to never become <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>. * a resource scope that cannot be closed, either explicitly or implicitly. As a result, the global scope will never
* As a results, the global scope will never attempt to release resources associated with it. Such resources must, where * attempt to release resources associated with it. Examples of resources associated with the global scope are:
* <ul>
* <li>heap segments created from {@linkplain MemorySegment#ofArray(int[]) arrays} or
* {@linkplain MemorySegment#ofByteBuffer(ByteBuffer) buffers};</li>
* <li>variable arity lists {@linkplain VaList#ofAddress(MemoryAddress, ResourceScope) obtained} from raw memory addresses;
* <li>native symbols {@linkplain SymbolLookup#lookup(String) obtained} from a {@linkplain SymbolLookup#loaderLookup() loader lookup},
* or from the {@link CLinker}.</li>
* </ul>
* In other words, the global scope is used to indicate that the lifecycle of one or more resources must, where
* needed, be managed independently by clients. * needed, be managed independently by clients.
* *
* <h2><a id = "thread-confinement">Thread confinement</a></h2> * <h2><a id = "thread-confinement">Thread confinement</a></h2>
* *
* Resource scopes can be further divided into two categories: <em>thread-confined</em> resource scopes, and <em>shared</em> * Resource scopes can be divided into two categories: <em>thread-confined</em> resource scopes, and <em>shared</em>
* resource scopes. * resource scopes.
* <p> * <p>
* Confined resource scopes (see {@link #newConfinedScope()}), support strong thread-confinement guarantees. Upon creation, * {@linkplain #newConfinedScope() Confined resource scopes}, support strong thread-confinement guarantees. Upon creation,
* they are assigned an <em>owner thread</em>, typically the thread which initiated the creation operation (see {@link #ownerThread()}). * they are assigned an {@linkplain #ownerThread() owner thread}, typically the thread which initiated the creation operation.
* After creating a confined resource scope, only the owner thread will be allowed to directly manipulate the resources * After creating a confined resource scope, only the owner thread will be allowed to directly manipulate the resources
* associated with this resource scope. Any attempt to perform resource access from a thread other than the * associated with this resource scope. Any attempt to perform resource access from a thread other than the
* owner thread will result in a runtime failure. * owner thread will result in a runtime failure.
* <p> * <p>
* Shared resource scopes (see {@link #newSharedScope()} and {@link #newImplicitScope()}), on the other hand, have no owner thread; * {@linkplain #newSharedScope() Shared resource scopes}, on the other hand, have no owner thread;
* as such resources associated with this shared resource scopes can be accessed by multiple threads. * as such, resources associated with shared resource scopes 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). * This might be useful when multiple threads need to access the same resource concurrently (e.g. in the case of parallel processing).
* For instance, a client might obtain a {@link Spliterator} from a shared segment, which can then be used to slice the * For instance, a client might obtain a {@link Spliterator} from a segment backed by a shared scope, which can then be used to slice the
* segment and allow multiple threads to work in parallel on disjoint segment slices. The following code can be used to sum * segment and allow multiple threads to work in parallel on disjoint segment slices. The following code can be used to sum
* all int values in a memory segment in parallel: * all int values in a memory segment in parallel:
* *
* <blockquote><pre>{@code * <blockquote><pre>{@code
SequenceLayout SEQUENCE_LAYOUT = MemoryLayout.sequenceLayout(1024, MemoryLayouts.JAVA_INT);
try (ResourceScope scope = ResourceScope.newSharedScope()) { try (ResourceScope scope = ResourceScope.newSharedScope()) {
SequenceLayout SEQUENCE_LAYOUT = MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_INT);
MemorySegment segment = MemorySegment.allocateNative(SEQUENCE_LAYOUT, scope); MemorySegment segment = MemorySegment.allocateNative(SEQUENCE_LAYOUT, scope);
VarHandle VH_int = SEQUENCE_LAYOUT.elementLayout().varHandle(int.class); int sum = segment.elements(ValueLayout.JAVA_INT).parallel()
int sum = StreamSupport.stream(segment.spliterator(SEQUENCE_LAYOUT), true) .mapToInt(s -> s.get(ValueLayout.JAVA_INT, 0))
.mapToInt(s -> (int)VH_int.get(s.address())) .sum();
.sum();
} }
* }</pre></blockquote> * }</pre></blockquote>
* *
* <p> * <p>
* Explicit shared resource scopes, while powerful, must be used with caution: if one or more threads accesses * Shared resource scopes, while powerful, must be used with caution: if one or more threads accesses
* a resource associated with a shared scope while the scope is being closed from another thread, an exception might occur on both * a resource associated with a shared scope while the scope is being closed from another thread, an exception might occur on both
* the accessing and the closing threads. Clients should refrain from attempting to close a shared resource scope repeatedly * the accessing and the closing threads. Clients should refrain from attempting to close a shared resource scope repeatedly
* (e.g. keep calling {@link #close()} until no exception is thrown). Instead, clients of shared resource scopes * (e.g. keep calling {@link #close()} until no exception is thrown). Instead, clients of shared resource scopes
* should always ensure that proper synchronization mechanisms (e.g. using resource scope handles, see below) are put in place * should always ensure that proper synchronization mechanisms (e.g. using temporal dependencies, see below) are put in place
* so that threads closing shared resource scopes can never race against threads accessing resources managed by same scopes. * so that threads closing shared resource scopes can never race against threads accessing resources managed by same scopes.
* *
* <h2>Resource scope handles</h2> * <h2>Temporal dependencies</h2>
* *
* Resource scopes can be made <em>non-closeable</em> by acquiring one or more resource scope <em>handles</em> (see * Resource scopes can depend on each other. More specifically, a scope can feature
* {@link #acquire()}. A resource scope handle can be used to make sure that resources associated with a given resource scope * {@linkplain #keepAlive(ResourceScope) temporal dependencies} on one or more other resource scopes.
* (either explicit or implicit) cannot be released for a certain period of time - e.g. during a critical region of code * Such a resource scope cannot be closed (either implicitly or explicitly) until <em>all</em> the scopes it depends on
* involving one or more resources associated with the scope. For instance, an explicit resource scope can only be closed * have also been closed.
* <em>after</em> all the handles acquired against that scope have been closed (see {@link Handle#close()}). * <p>
* This can be useful when clients need to perform a critical operation on a memory segment, during which they have * This can be useful when clients need to perform a critical operation on a memory segment, during which they have
* to ensure that the segment will not be released; this can be done as follows: * to ensure that the scope associated with that segment will not be closed; this can be done as follows:
* *
* <blockquote><pre>{@code * <blockquote><pre>{@code
MemorySegment segment = ... MemorySegment segment = ...
ResourceScope.Handle segmentHandle = segment.scope().acquire() try (ResourceScope criticalScope = ResourceScope.newConfinedScope()) {
try { criticalScope.keepAlive(segment.scope());
<critical operation on segment> <critical operation on segment>
} finally {
segment.scope().release(segmentHandle);
} }
* }</pre></blockquote> * }</pre></blockquote>
* *
* Acquiring implicit resource scopes is also possible, but it is often unnecessary: since resources associated with * Note that a resource scope does not become <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>
* an implicit scope will only be released when the scope becomes <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>, * until all the scopes it depends on have been closed.
* clients can use e.g. {@link java.lang.ref.Reference#reachabilityFence(Object)} to make sure that resources associated
* with implicit scopes are not released prematurely. That said, the above code snippet works (trivially) for implicit scopes too.
* *
* @implSpec * @implSpec
* Implementations of this interface are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>. * Implementations of this interface are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
@ -158,15 +160,7 @@ public sealed interface ResourceScope extends AutoCloseable permits ResourceScop
Thread ownerThread(); Thread ownerThread();
/** /**
* Is this resource scope an <em>implicit scope</em>? * Closes this resource scope. As a side effect, if this operation completes without exceptions, this scope will be marked
* @return true if this scope is an <em>implicit scope</em>.
* @see #newImplicitScope()
* @see #globalScope()
*/
boolean isImplicit();
/**
* Closes this resource scope. As a side-effect, if this operation completes without exceptions, this scope will be marked
* as <em>not alive</em>, and subsequent operations on resources associated with this scope will fail with {@link IllegalStateException}. * as <em>not alive</em>, and subsequent operations on resources associated with this scope will fail with {@link IllegalStateException}.
* Additionally, upon successful closure, all native resources associated with this resource scope will be released. * Additionally, upon successful closure, all native resources associated with this resource scope will be released.
* *
@ -180,9 +174,9 @@ public sealed interface ResourceScope extends AutoCloseable permits ResourceScop
* <li>this resource scope is not <em>alive</em> * <li>this resource scope is not <em>alive</em>
* <li>this resource scope is confined, and this method is called from a thread other than the thread owning this resource scope</li> * <li>this resource scope is confined, and this method is called from a thread other than the thread owning this resource scope</li>
* <li>this resource scope is shared and a resource associated with this scope is accessed while this method is called</li> * <li>this resource scope is shared and a resource associated with this scope is accessed while this method is called</li>
* <li>one or more handles (see {@link #acquire()}) associated with this resource scope have not been {@linkplain #release(Handle) released}</li> * <li>one or more scopes which {@linkplain #keepAlive(ResourceScope) depend} on this resource scope have not been closed.
* </ul> * </ul>
* @throws UnsupportedOperationException if this resource scope is {@linkplain #isImplicit() implicit}. * @throws UnsupportedOperationException if this resource scope is the {@linkplain #globalScope() global scope}.
*/ */
void close(); void close();
@ -190,61 +184,42 @@ public sealed interface ResourceScope extends AutoCloseable permits ResourceScop
* Add a custom cleanup action which will be executed when the resource scope is closed. * Add a custom cleanup action which will be executed when the resource scope is closed.
* The order in which custom cleanup actions are invoked once the scope is closed is unspecified. * The order in which custom cleanup actions are invoked once the scope is closed is unspecified.
* @param runnable the custom cleanup action to be associated with this scope. * @param runnable the custom cleanup action to be associated with this scope.
* @throws IllegalStateException if this scope has already been closed. * @throws IllegalStateException if this scope has been closed, or if access occurs from
* a thread other than the thread owning this scope.
*/ */
void addCloseAction(Runnable runnable); void addCloseAction(Runnable runnable);
/** /**
* Acquires a resource scope handle associated with this resource scope. An explicit resource scope cannot be * Creates a temporal dependency between this scope and the target scope. As a result, the target scope cannot
* {@linkplain #close() closed} until all the resource scope handles acquired from it have been {@linkplain #release(Handle)} released}. * be {@linkplain #close() closed} <em>before</em> this scope.
* @return a resource scope handle. * @implNote A given scope can support up to {@link Integer#MAX_VALUE} pending keep alive requests.
* @param target the scope that needs to be kept alive.
* @throws IllegalArgumentException if {@code target == this}.
* @throws IllegalStateException if this scope or {@code target} have been closed, or if access occurs from
* a thread other than the thread owning this scope or {@code target}.
*/ */
Handle acquire(); void keepAlive(ResourceScope target);
/** /**
* Release the provided resource scope handle. This method is idempotent, that is, releasing the same handle * Creates a new confined scope.
* multiple times has no effect.
* @param handle the resource scope handle to be released.
* @throws IllegalArgumentException if the provided handle is not associated with this scope.
*/
void release(Handle handle);
/**
* An abstraction modelling a resource scope handle. A resource scope handle is typically {@linkplain #acquire() acquired} by clients
* in order to prevent an explicit resource scope from being closed while executing a certain operation.
* Once obtained, resource scope handles can be {@linkplain #release(Handle)} released}; an explicit resource scope can
* be closed only <em>after</em> all the resource scope handles acquired from it have been released.
*/
sealed interface Handle permits ResourceScopeImpl.HandleImpl {
/**
* Returns the resource scope associated with this handle.
* @return the resource scope associated with this handle.
*/
ResourceScope scope();
}
/**
* Create a new confined scope. The resulting scope is closeable, and is not managed by a {@link Cleaner}.
* @return a new confined scope. * @return a new confined scope.
*/ */
static ResourceScope newConfinedScope() { static ResourceScope newConfinedScope() {
return ResourceScopeImpl.createConfined( null); return ResourceScopeImpl.createConfined(Thread.currentThread(), null);
} }
/** /**
* Create a new confined scope managed by a {@link Cleaner}. * Creates a new confined scope, managed by the provided cleaner instance.
* @param cleaner the cleaner to be associated with the returned scope. * @param cleaner the cleaner to be associated with the returned scope.
* @return a new confined scope, managed by {@code cleaner}. * @return a new confined scope, managed by {@code cleaner}.
* @throws NullPointerException if {@code cleaner == null}.
*/ */
static ResourceScope newConfinedScope(Cleaner cleaner) { static ResourceScope newConfinedScope(Cleaner cleaner) {
Objects.requireNonNull(cleaner); Objects.requireNonNull(cleaner);
return ResourceScopeImpl.createConfined( cleaner); return ResourceScopeImpl.createConfined(Thread.currentThread(), cleaner);
} }
/** /**
* Create a new shared scope. The resulting scope is closeable, and is not managed by a {@link Cleaner}. * Creates a new shared scope.
* @return a new shared scope. * @return a new shared scope.
*/ */
static ResourceScope newSharedScope() { static ResourceScope newSharedScope() {
@ -252,10 +227,9 @@ public sealed interface ResourceScope extends AutoCloseable permits ResourceScop
} }
/** /**
* Create a new shared scope managed by a {@link Cleaner}. * Creates a new shared scope, managed by the provided cleaner instance.
* @param cleaner the cleaner to be associated with the returned scope. * @param cleaner the cleaner to be associated with the returned scope.
* @return a new shared scope, managed by {@code cleaner}. * @return a new shared scope, managed by {@code cleaner}.
* @throws NullPointerException if {@code cleaner == null}.
*/ */
static ResourceScope newSharedScope(Cleaner cleaner) { static ResourceScope newSharedScope(Cleaner cleaner) {
Objects.requireNonNull(cleaner); Objects.requireNonNull(cleaner);
@ -263,21 +237,20 @@ public sealed interface ResourceScope extends AutoCloseable permits ResourceScop
} }
/** /**
* Create a new <em>implicit scope</em>. The implicit scope is a managed, shared, and non-closeable scope which only features * Creates a new shared scope, managed by a private {@link Cleaner} instance. Equivalent to (but likely more efficient than)
* <a href="ResourceScope.html#implicit-closure"><em>implicit closure</em></a>. * the following code:
* Since implicit scopes can only be closed implicitly by the garbage collector, it is recommended that implicit * <pre>{@code
* scopes are only used in cases where deallocation performance is not a critical concern, to avoid unnecessary newSharedScope(Cleaner.create());
* memory pressure. * }</pre>
* * @return a shared scope, managed by a private {@link Cleaner} instance.
* @return a new implicit scope.
*/ */
static ResourceScope newImplicitScope() { static ResourceScope newImplicitScope() {
return ResourceScopeImpl.createImplicitScope(); return newSharedScope(CleanerFactory.cleaner());
} }
/** /**
* Returns an implicit scope which is assumed to be always alive. * Returns the <a href="ResourceScope.html#global-scope"><em>global scope</em></a>.
* @return the global scope. * @return the <a href="ResourceScope.html#global-scope"><em>global scope</em></a>.
*/ */
static ResourceScope globalScope() { static ResourceScope globalScope() {
return ResourceScopeImpl.GLOBAL; return ResourceScopeImpl.GLOBAL;

View file

@ -25,306 +25,277 @@
package jdk.incubator.foreign; package jdk.incubator.foreign;
import jdk.internal.foreign.ArenaAllocator;
import jdk.internal.foreign.AbstractMemorySegmentImpl; import jdk.internal.foreign.AbstractMemorySegmentImpl;
import jdk.internal.foreign.ArenaAllocator;
import jdk.internal.foreign.ResourceScopeImpl; import jdk.internal.foreign.ResourceScopeImpl;
import jdk.internal.foreign.Utils; import jdk.internal.foreign.Utils;
import java.lang.invoke.VarHandle; import java.lang.invoke.VarHandle;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Objects; import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Stream;
/** /**
* This interface models a memory allocator. Clients implementing this interface * This interface models a memory allocator. Clients implementing this interface
* must implement the {@link #allocate(long, long)} method. This interface defines several default methods * must implement the {@link #allocate(long, long)} method. This interface defines several default methods
* which can be useful to create segments from several kinds of Java values such as primitives and arrays. * which can be useful to create segments from several kinds of Java values such as primitives and arrays.
* This interface can be seen as a thin wrapper around the basic capabilities for creating native segments * This interface can be seen as a thin wrapper around the basic capabilities for
* (e.g. {@link MemorySegment#allocateNative(long, long, ResourceScope)}); since {@link SegmentAllocator} is a <em>functional interface</em>, * {@linkplain MemorySegment#allocateNative(long, long, ResourceScope) creating} native segments;
* since {@link SegmentAllocator} is a <em>functional interface</em>,
* clients can easily obtain a native allocator by using either a lambda expression or a method reference. * clients can easily obtain a native allocator by using either a lambda expression or a method reference.
* <p> * <p>
* This interface provides a factory, namely {@link SegmentAllocator#ofScope(ResourceScope)} which can be used to obtain * This interface also defines factories for commonly used allocators:
* a <em>scoped</em> allocator, that is, an allocator which creates segment bound by a given scope. This can be useful * <ul>
* when working inside a <em>try-with-resources</em> construct: * <li>{@link #nativeAllocator(ResourceScope)} creates an allocator which
* * {@linkplain MemorySegment#allocateNative(long, long, ResourceScope) allocates} native segments, backed by a given scope;</li>
* <blockquote><pre>{@code * <li>{@link #newNativeArena(ResourceScope)} creates a more efficient arena-style native allocator, where memory
try (ResourceScope scope = ResourceScope.newConfinedScope()) { * is allocated in bigger blocks, which are then sliced accordingly to fit allocation requests;</li>
SegmentAllocator allocator = SegmentAllocator.ofScope(scope); * <li>{@link #prefixAllocator(MemorySegment)} creates an allocator which wraps a segment (either on-heap or off-heap)
... * and recycles its content upon each new allocation request.</li>
} * </ul>
* }</pre></blockquote> * <p>
* * Passing a segment allocator to an API can be especially useful in circumstances where a client wants to communicate <em>where</em>
* In addition, this interface also defines factories for commonly used allocators; for instance {@link #arenaAllocator(ResourceScope)} * the results of a certain operation (performed by the API) should be stored, as a memory segment. For instance,
* and {@link #arenaAllocator(long, ResourceScope)} are arena-style native allocators. Finally {@link #ofSegment(MemorySegment)} * {@linkplain CLinker#downcallHandle(FunctionDescriptor) downcall method handles} can accept an additional
* returns an allocator which wraps a segment (either on-heap or off-heap) and recycles its content upon each new allocation request. * {@link SegmentAllocator} parameter if the underlying native 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 native function.
*/ */
@FunctionalInterface @FunctionalInterface
public interface SegmentAllocator { public interface SegmentAllocator {
/** /**
* Allocate a block of memory with given layout and initialize it with given byte value. * Converts a Java string into a UTF-8 encoded, null-terminated C string,
* storing the result into a memory segment.
* <p>
* This method always replaces malformed-input and unmappable-character
* sequences with this charset's default replacement byte array. The
* {@link java.nio.charset.CharsetEncoder} class should be used when more
* control over the encoding process is required.
*
* @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.
*/
default MemorySegment allocateUtf8String(String str) {
Objects.requireNonNull(str);
return Utils.toCString(str.getBytes(StandardCharsets.UTF_8), this);
}
/**
* Allocate a memory segment with given layout and initialize it with given byte value.
* @implSpec the default implementation for this method calls {@code this.allocate(layout)}. * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
* @param layout the layout of the block of memory to be allocated. * @param layout the layout of the block of memory to be allocated.
* @param value the value to be set on the newly allocated memory block. * @param value the value to be set on the newly allocated memory block.
* @return a segment for the newly allocated memory block. * @return a segment for the newly allocated memory block.
* @throws IllegalArgumentException if {@code layout.byteSize()} does not conform to the size of a byte value.
*/ */
default MemorySegment allocate(ValueLayout layout, byte value) { default MemorySegment allocate(ValueLayout.OfByte layout, byte value) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
VarHandle handle = layout.varHandle(byte.class); VarHandle handle = layout.varHandle();
MemorySegment addr = allocate(layout); MemorySegment addr = allocate(layout);
handle.set(addr, value); handle.set(addr, value);
return addr; return addr;
} }
/** /**
* Allocate a block of memory with given layout and initialize it with given char value. * Allocate a memory segment with given layout and initialize it with given char value.
* @implSpec the default implementation for this method calls {@code this.allocate(layout)}. * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
* @param layout the layout of the block of memory to be allocated. * @param layout the layout of the block of memory to be allocated.
* @param value the value to be set on the newly allocated memory block. * @param value the value to be set on the newly allocated memory block.
* @return a segment for the newly allocated memory block. * @return a segment for the newly allocated memory block.
* @throws IllegalArgumentException if {@code layout.byteSize()} does not conform to the size of a char value.
*/ */
default MemorySegment allocate(ValueLayout layout, char value) { default MemorySegment allocate(ValueLayout.OfChar layout, char value) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
VarHandle handle = layout.varHandle(char.class); VarHandle handle = layout.varHandle();
MemorySegment addr = allocate(layout); MemorySegment addr = allocate(layout);
handle.set(addr, value); handle.set(addr, value);
return addr; return addr;
} }
/** /**
* Allocate a block of memory with given layout and initialize it with given short value. * Allocate a memory segment with given layout and initialize it with given short value.
* @implSpec the default implementation for this method calls {@code this.allocate(layout)}. * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
* @param layout the layout of the block of memory to be allocated. * @param layout the layout of the block of memory to be allocated.
* @param value the value to be set on the newly allocated memory block. * @param value the value to be set on the newly allocated memory block.
* @return a segment for the newly allocated memory block. * @return a segment for the newly allocated memory block.
* @throws IllegalArgumentException if {@code layout.byteSize()} does not conform to the size of a short value.
*/ */
default MemorySegment allocate(ValueLayout layout, short value) { default MemorySegment allocate(ValueLayout.OfShort layout, short value) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
VarHandle handle = layout.varHandle(short.class); VarHandle handle = layout.varHandle();
MemorySegment addr = allocate(layout); MemorySegment addr = allocate(layout);
handle.set(addr, value); handle.set(addr, value);
return addr; return addr;
} }
/** /**
* Allocate a block of memory with given layout and initialize it with given int value. * Allocate a memory segment with given layout and initialize it with given int value.
* @implSpec the default implementation for this method calls {@code this.allocate(layout)}. * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
* @param layout the layout of the block of memory to be allocated. * @param layout the layout of the block of memory to be allocated.
* @param value the value to be set on the newly allocated memory block. * @param value the value to be set on the newly allocated memory block.
* @return a segment for the newly allocated memory block. * @return a segment for the newly allocated memory block.
* @throws IllegalArgumentException if {@code layout.byteSize()} does not conform to the size of a int value.
*/ */
default MemorySegment allocate(ValueLayout layout, int value) { default MemorySegment allocate(ValueLayout.OfInt layout, int value) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
VarHandle handle = layout.varHandle(int.class); VarHandle handle = layout.varHandle();
MemorySegment addr = allocate(layout); MemorySegment addr = allocate(layout);
handle.set(addr, value); handle.set(addr, value);
return addr; return addr;
} }
/** /**
* Allocate a block of memory with given layout and initialize it with given float value. * Allocate a memory segment with given layout and initialize it with given float value.
* @implSpec the default implementation for this method calls {@code this.allocate(layout)}. * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
* @param layout the layout of the block of memory to be allocated. * @param layout the layout of the block of memory to be allocated.
* @param value the value to be set on the newly allocated memory block. * @param value the value to be set on the newly allocated memory block.
* @return a segment for the newly allocated memory block. * @return a segment for the newly allocated memory block.
* @throws IllegalArgumentException if {@code layout.byteSize()} does not conform to the size of a float value.
*/ */
default MemorySegment allocate(ValueLayout layout, float value) { default MemorySegment allocate(ValueLayout.OfFloat layout, float value) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
VarHandle handle = layout.varHandle(float.class); VarHandle handle = layout.varHandle();
MemorySegment addr = allocate(layout); MemorySegment addr = allocate(layout);
handle.set(addr, value); handle.set(addr, value);
return addr; return addr;
} }
/** /**
* Allocate a block of memory with given layout and initialize it with given long value. * Allocate a memory segment with given layout and initialize it with given long value.
* @implSpec the default implementation for this method calls {@code this.allocate(layout)}. * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
* @param layout the layout of the block of memory to be allocated. * @param layout the layout of the block of memory to be allocated.
* @param value the value to be set on the newly allocated memory block. * @param value the value to be set on the newly allocated memory block.
* @return a segment for the newly allocated memory block. * @return a segment for the newly allocated memory block.
* @throws IllegalArgumentException if {@code layout.byteSize()} does not conform to the size of a long value.
*/ */
default MemorySegment allocate(ValueLayout layout, long value) { default MemorySegment allocate(ValueLayout.OfLong layout, long value) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
VarHandle handle = layout.varHandle(long.class); VarHandle handle = layout.varHandle();
MemorySegment addr = allocate(layout); MemorySegment addr = allocate(layout);
handle.set(addr, value); handle.set(addr, value);
return addr; return addr;
} }
/** /**
* Allocate a block of memory with given layout and initialize it with given double value. * Allocate a memory segment with given layout and initialize it with given double value.
* @implSpec the default implementation for this method calls {@code this.allocate(layout)}. * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
* @param layout the layout of the block of memory to be allocated. * @param layout the layout of the block of memory to be allocated.
* @param value the value to be set on the newly allocated memory block. * @param value the value to be set on the newly allocated memory block.
* @return a segment for the newly allocated memory block. * @return a segment for the newly allocated memory block.
* @throws IllegalArgumentException if {@code layout.byteSize()} does not conform to the size of a double value.
*/ */
default MemorySegment allocate(ValueLayout layout, double value) { default MemorySegment allocate(ValueLayout.OfDouble layout, double value) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
VarHandle handle = layout.varHandle(double.class); VarHandle handle = layout.varHandle();
MemorySegment addr = allocate(layout); MemorySegment addr = allocate(layout);
handle.set(addr, value); handle.set(addr, value);
return addr; return addr;
} }
/** /**
* Allocate a block of memory with given layout and initialize it with given address value * Allocate a memory segment with given layout and initialize it with given address value
* (expressed as an {@link Addressable} instance). * (expressed as an {@link Addressable} instance).
* The address value might be narrowed according to the platform address size (see {@link MemoryLayouts#ADDRESS}). * The address value might be narrowed according to the platform address size (see {@link ValueLayout#ADDRESS}).
* @implSpec the default implementation for this method calls {@code this.allocate(layout)}. * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
* @param layout the layout of the block of memory to be allocated. * @param layout the layout of the block of memory to be allocated.
* @param value the value to be set on the newly allocated memory block. * @param value the value to be set on the newly allocated memory block.
* @return a segment for the newly allocated memory block. * @return a segment for the newly allocated memory block.
* @throws IllegalArgumentException if {@code layout.byteSize() != MemoryLayouts.ADDRESS.byteSize()}.
*/ */
default MemorySegment allocate(ValueLayout layout, Addressable value) { default MemorySegment allocate(ValueLayout.OfAddress layout, Addressable value) {
Objects.requireNonNull(value); Objects.requireNonNull(value);
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
if (MemoryLayouts.ADDRESS.byteSize() != layout.byteSize()) { MemorySegment segment = allocate(layout);
throw new IllegalArgumentException("Layout size mismatch - " + layout.byteSize() + " != " + MemoryLayouts.ADDRESS.byteSize()); layout.varHandle().set(segment, value.address());
} return segment;
return switch ((int)layout.byteSize()) {
case 4 -> allocate(layout, (int)value.address().toRawLongValue());
case 8 -> allocate(layout, value.address().toRawLongValue());
default -> throw new UnsupportedOperationException("Unsupported pointer size"); // should not get here
};
} }
/** /**
* Allocate a block of memory with given layout and initialize it with given byte array. * Allocate a memory segment with given layout and initialize it with given byte array.
* @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}. * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
* @param elementLayout the element layout of the array to be allocated. * @param elementLayout the element layout of the array to be allocated.
* @param array the array to be copied on the newly allocated memory block. * @param array the array to be copied on the newly allocated memory block.
* @return a segment for the newly allocated memory block. * @return a segment for the newly allocated memory block.
* @throws IllegalArgumentException if {@code elementLayout.byteSize()} does not conform to the size of a byte value.
*/ */
default MemorySegment allocateArray(ValueLayout elementLayout, byte[] array) { default MemorySegment allocateArray(ValueLayout.OfByte elementLayout, byte[] array) {
return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray); return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
} }
/** /**
* Allocate a block of memory with given layout and initialize it with given short array. * Allocate a memory segment with given layout and initialize it with given short array.
* @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}. * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
* @param elementLayout the element layout of the array to be allocated. * @param elementLayout the element layout of the array to be allocated.
* @param array the array to be copied on the newly allocated memory block. * @param array the array to be copied on the newly allocated memory block.
* @return a segment for the newly allocated memory block. * @return a segment for the newly allocated memory block.
* @throws IllegalArgumentException if {@code elementLayout.byteSize()} does not conform to the size of a short value.
*/ */
default MemorySegment allocateArray(ValueLayout elementLayout, short[] array) { default MemorySegment allocateArray(ValueLayout.OfShort elementLayout, short[] array) {
return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray); return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
} }
/** /**
* Allocate a block of memory with given layout and initialize it with given char array. * Allocate a memory segment with given layout and initialize it with given char array.
* @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}. * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
* @param elementLayout the element layout of the array to be allocated. * @param elementLayout the element layout of the array to be allocated.
* @param array the array to be copied on the newly allocated memory block. * @param array the array to be copied on the newly allocated memory block.
* @return a segment for the newly allocated memory block. * @return a segment for the newly allocated memory block.
* @throws IllegalArgumentException if {@code elementLayout.byteSize()} does not conform to the size of a char value.
*/ */
default MemorySegment allocateArray(ValueLayout elementLayout, char[] array) { default MemorySegment allocateArray(ValueLayout.OfChar elementLayout, char[] array) {
return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray); return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
} }
/** /**
* Allocate a block of memory with given layout and initialize it with given int array. * Allocate a memory segment with given layout and initialize it with given int array.
* @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}. * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
* @param elementLayout the element layout of the array to be allocated. * @param elementLayout the element layout of the array to be allocated.
* @param array the array to be copied on the newly allocated memory block. * @param array the array to be copied on the newly allocated memory block.
* @return a segment for the newly allocated memory block. * @return a segment for the newly allocated memory block.
* @throws IllegalArgumentException if {@code elementLayout.byteSize()} does not conform to the size of a int value.
*/ */
default MemorySegment allocateArray(ValueLayout elementLayout, int[] array) { default MemorySegment allocateArray(ValueLayout.OfInt elementLayout, int[] array) {
return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray); return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
} }
/** /**
* Allocate a block of memory with given layout and initialize it with given float array. * Allocate a memory segment with given layout and initialize it with given float array.
* @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}. * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
* @param elementLayout the element layout of the array to be allocated. * @param elementLayout the element layout of the array to be allocated.
* @param array the array to be copied on the newly allocated memory block. * @param array the array to be copied on the newly allocated memory block.
* @return a segment for the newly allocated memory block. * @return a segment for the newly allocated memory block.
* @throws IllegalArgumentException if {@code elementLayout.byteSize()} does not conform to the size of a float value.
*/ */
default MemorySegment allocateArray(ValueLayout elementLayout, float[] array) { default MemorySegment allocateArray(ValueLayout.OfFloat elementLayout, float[] array) {
return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray); return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
} }
/** /**
* Allocate a block of memory with given layout and initialize it with given long array. * Allocate a memory segment with given layout and initialize it with given long array.
* @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}. * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
* @param elementLayout the element layout of the array to be allocated. * @param elementLayout the element layout of the array to be allocated.
* @param array the array to be copied on the newly allocated memory block. * @param array the array to be copied on the newly allocated memory block.
* @return a segment for the newly allocated memory block. * @return a segment for the newly allocated memory block.
* @throws IllegalArgumentException if {@code elementLayout.byteSize()} does not conform to the size of a long value.
*/ */
default MemorySegment allocateArray(ValueLayout elementLayout, long[] array) { default MemorySegment allocateArray(ValueLayout.OfLong elementLayout, long[] array) {
return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray); return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
} }
/** /**
* Allocate a block of memory with given layout and initialize it with given double array. * Allocate a memory segment with given layout and initialize it with given double array.
* @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}. * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
* @param elementLayout the element layout of the array to be allocated. * @param elementLayout the element layout of the array to be allocated.
* @param array the array to be copied on the newly allocated memory block. * @param array the array to be copied on the newly allocated memory block.
* @return a segment for the newly allocated memory block. * @return a segment for the newly allocated memory block.
* @throws IllegalArgumentException if {@code elementLayout.byteSize()} does not conform to the size of a double value.
*/ */
default MemorySegment allocateArray(ValueLayout elementLayout, double[] array) { default MemorySegment allocateArray(ValueLayout.OfDouble elementLayout, double[] array) {
return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray); return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
} }
/**
* Allocate a block of memory with given layout and initialize it with given address array.
* The address value of each array element might be narrowed according to the platform address size (see {@link MemoryLayouts#ADDRESS}).
* @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
* @param elementLayout the element layout of the array to be allocated.
* @param array the array to be copied on the newly allocated memory block.
* @return a segment for the newly allocated memory block.
* @throws IllegalArgumentException if {@code layout.byteSize() != MemoryLayouts.ADDRESS.byteSize()}.
*/
default MemorySegment allocateArray(ValueLayout elementLayout, Addressable[] array) {
Objects.requireNonNull(elementLayout);
Objects.requireNonNull(array);
Stream.of(array).forEach(Objects::requireNonNull);
if (MemoryLayouts.ADDRESS.byteSize() != elementLayout.byteSize()) {
throw new IllegalArgumentException("Layout size mismatch - " + elementLayout.byteSize() + " != " + MemoryLayouts.ADDRESS.byteSize());
}
return switch ((int)elementLayout.byteSize()) {
case 4 -> copyArrayWithSwapIfNeeded(Stream.of(array)
.mapToInt(a -> (int)a.address().toRawLongValue()).toArray(),
elementLayout, MemorySegment::ofArray);
case 8 -> copyArrayWithSwapIfNeeded(Stream.of(array)
.mapToLong(a -> a.address().toRawLongValue()).toArray(),
elementLayout, MemorySegment::ofArray);
default -> throw new UnsupportedOperationException("Unsupported pointer size"); // should not get here
};
}
private <Z> MemorySegment copyArrayWithSwapIfNeeded(Z array, ValueLayout elementLayout, private <Z> MemorySegment copyArrayWithSwapIfNeeded(Z array, ValueLayout elementLayout,
Function<Z, MemorySegment> heapSegmentFactory) { Function<Z, MemorySegment> heapSegmentFactory) {
Objects.requireNonNull(array); Objects.requireNonNull(array);
Objects.requireNonNull(elementLayout); Objects.requireNonNull(elementLayout);
Utils.checkPrimitiveCarrierCompat(array.getClass().componentType(), elementLayout); int size = Array.getLength(array);
MemorySegment addr = allocate(MemoryLayout.sequenceLayout(Array.getLength(array), elementLayout)); MemorySegment addr = allocate(MemoryLayout.sequenceLayout(size, elementLayout));
if (elementLayout.byteSize() == 1 || (elementLayout.order() == ByteOrder.nativeOrder())) { MemorySegment.copy(heapSegmentFactory.apply(array), elementLayout, 0,
addr.copyFrom(heapSegmentFactory.apply(array)); addr, elementLayout.withOrder(ByteOrder.nativeOrder()), 0, size);
} else {
((AbstractMemorySegmentImpl)addr).copyFromSwap(heapSegmentFactory.apply(array), elementLayout.byteSize());
}
return addr; return addr;
} }
/** /**
* Allocate a block of memory with given layout. * Allocate a memory segment with given layout.
* @implSpec the default implementation for this method calls {@code this.allocate(layout.byteSize(), layout.byteAlignment())}. * @implSpec the default implementation for this method calls {@code this.allocate(layout.byteSize(), layout.byteAlignment())}.
* @param layout the layout of the block of memory to be allocated. * @param layout the layout of the block of memory to be allocated.
* @return a segment for the newly allocated memory block. * @return a segment for the newly allocated memory block.
@ -335,7 +306,7 @@ public interface SegmentAllocator {
} }
/** /**
* Allocate a block of memory corresponding to an array with given element layout and size. * Allocate a memory segment with given element layout and size.
* @implSpec the default implementation for this method calls {@code this.allocate(MemoryLayout.sequenceLayout(count, elementLayout))}. * @implSpec the default implementation for this method calls {@code this.allocate(MemoryLayout.sequenceLayout(count, elementLayout))}.
* @param elementLayout the array element layout. * @param elementLayout the array element layout.
* @param count the array element count. * @param count the array element count.
@ -347,7 +318,8 @@ public interface SegmentAllocator {
} }
/** /**
* Allocate a block of memory with given size, with default alignment (1-byte aligned). * Allocate a memory segment with given size
* and default alignment constraints (1-byte aligned).
* @implSpec the default implementation for this method calls {@code this.allocate(bytesSize, 1)}. * @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. * @param bytesSize the size (in bytes) of the block of memory to be allocated.
* @return a segment for the newly allocated memory block. * @return a segment for the newly allocated memory block.
@ -357,7 +329,7 @@ public interface SegmentAllocator {
} }
/** /**
* Allocate a block of memory with given size and alignment constraint. * Allocate a memory segment with given size and alignment constraints.
* @param bytesSize the size (in bytes) of the block of memory to be allocated. * @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. * @param bytesAlignment the alignment (in bytes) of the block of memory to be allocated.
* @return a segment for the newly allocated memory block. * @return a segment for the newly allocated memory block.
@ -365,74 +337,91 @@ public interface SegmentAllocator {
MemorySegment allocate(long bytesSize, long bytesAlignment); MemorySegment allocate(long bytesSize, long bytesAlignment);
/** /**
* Returns a native arena-based allocator which allocates a single memory segment, of given size (using malloc), * Returns a native unbounded arena-based allocator, with predefined block size and maximum arena size,
* and then responds to allocation request by returning different slices of that same segment * associated with the provided scope. Equivalent to the following code:
* (until no further allocation is possible). * <blockquote><pre>{@code
* This can be useful when clients want to perform multiple allocation requests while avoiding the cost associated SegmentAllocator.newNativeArena(Long.MAX_VALUE, predefinedBlockSize, scope);
* with allocating a new off-heap memory region upon each allocation request. * }</pre></blockquote>
* <p>
* An allocator associated with a <em>shared</em> resource scope is thread-safe and allocation requests may be
* performed concurrently; conversely, if the arena allocator is associated with a <em>confined</em> resource scope,
* allocation requests can only occur from the thread owning the allocator's resource scope.
* <p>
* The returned allocator might throw an {@link OutOfMemoryError} if an incoming allocation request exceeds
* the allocator capacity.
* *
* @param size the size (in bytes) of the allocation arena. * @param scope the scope associated with the segments returned by the arena-based allocator.
* @param scope the scope associated with the segments returned by this allocator. * @return a new unbounded arena-based allocator
* @return a new bounded arena-based allocator
* @throws IllegalArgumentException if {@code size <= 0}.
* @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other * @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other
* than the thread owning {@code scope}. * than the thread owning {@code scope}.
*/ */
static SegmentAllocator arenaAllocator(long size, ResourceScope scope) { static SegmentAllocator newNativeArena(ResourceScope scope) {
Objects.requireNonNull(scope); return newNativeArena(Long.MAX_VALUE, ArenaAllocator.DEFAULT_BLOCK_SIZE, scope);
return scope.ownerThread() == null ?
new ArenaAllocator.BoundedSharedArenaAllocator(scope, size) :
new ArenaAllocator.BoundedArenaAllocator(scope, size);
} }
/** /**
* Returns a native unbounded arena-based allocator. * Returns a native unbounded arena-based allocator, with block size set to the specified arena size, associated with
* the provided scope, with given arena size. Equivalent to the following code:
* <blockquote><pre>{@code
SegmentAllocator.newNativeArena(arenaSize, arenaSize, scope);
* }</pre></blockquote>
*
* @param arenaSize the size (in bytes) of the allocation arena.
* @param scope the scope associated with the segments returned by the arena-based allocator.
* @return a new unbounded arena-based allocator
* @throws IllegalArgumentException if {@code arenaSize <= 0}.
* @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other
* than the thread owning {@code scope}.
*/
static SegmentAllocator newNativeArena(long arenaSize, ResourceScope scope) {
return newNativeArena(arenaSize, arenaSize, scope);
}
/**
* Returns a native arena-based allocator, associated with the provided scope, with given arena size and block size.
* <p> * <p>
* The returned allocator allocates a memory segment {@code S} of a certain fixed size (using malloc) and then * The returned allocator {@linkplain MemorySegment#allocateNative(long, ResourceScope) allocates} a memory segment
* responds to allocation requests in one of the following ways: * {@code S} of the specified block size and then responds to allocation requests in one of the following ways:
* <ul> * <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> * <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'}. * 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> * <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'} (using malloc), which has same size as {@code S} * slices which fits that allocation request, allocate a new segment {@code S'}, which has same size as {@code S}
* and set {@code S = S'}; the allocator then tries to respond to the same allocation request again. * 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'} * <li>if the size of the allocation requests is bigger than the size of {@code S}, allocate a new segment {@code S'},
* (using malloc), which has a sufficient size to satisfy the allocation request, and return {@code S'}. * which has a sufficient size to satisfy the allocation request, and return {@code S'}.
* </ul> * </ul>
* <p> * <p>
* This segment allocator can be useful when clients want to perform multiple allocation requests while avoiding the * 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. * cost associated with allocating a new off-heap memory region upon each allocation request.
* <p> * <p>
* An allocator associated with a <em>shared</em> resource scope is thread-safe and allocation requests may be * The returned allocator might throw an {@link OutOfMemoryError} if the total memory allocated with this allocator
* performed concurrently; conversely, if the arena allocator is associated with a <em>confined</em> resource scope, * exceeds the arena size, or the system capacity. Furthermore, the returned allocator is not thread safe.
* allocation requests can only occur from the thread owning the allocator's resource scope. * Concurrent allocation needs to be guarded with synchronization primitives.
* <p>
* The returned allocator might throw an {@link OutOfMemoryError} if an incoming allocation request exceeds
* the system capacity.
* *
* @param scope the scope associated with the segments returned by this allocator. * @param arenaSize the size (in bytes) of the allocation arena.
* @param blockSize the block size associated with the arena-based allocator.
* @param scope the scope associated with the segments returned by the arena-based allocator.
* @return a new unbounded 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 scope} has been already closed, or if access occurs from a thread other * @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other
* than the thread owning {@code scope}. * than the thread owning {@code scope}.
*/ */
static SegmentAllocator arenaAllocator(ResourceScope scope) { static SegmentAllocator newNativeArena(long arenaSize, long blockSize, ResourceScope scope) {
Objects.requireNonNull(scope); Objects.requireNonNull(scope);
return scope.ownerThread() == null ? if (blockSize <= 0) {
new ArenaAllocator.UnboundedSharedArenaAllocator(scope) : throw new IllegalArgumentException("Invalid block size: " + blockSize);
new ArenaAllocator.UnboundedArenaAllocator(scope); }
if (arenaSize <= 0 || arenaSize < blockSize) {
throw new IllegalArgumentException("Invalid arena size: " + arenaSize);
}
return new ArenaAllocator(blockSize, arenaSize, scope);
} }
/** /**
* Returns a segment allocator which responds to allocation requests by recycling a single segment; that is, * Returns a segment allocator which responds to allocation requests by recycling a single segment; that is,
* each new allocation request will return a new slice starting at the segment offset {@code 0} (alignment * each new allocation request will return a new slice starting at the segment offset {@code 0} (alignment
* constraints are ignored by this allocator). This can be useful to limit allocation requests in case a client * constraints are ignored by this allocator), hence the name <em>prefix allocator</em>.
* Equivalent to (but likely more efficient than) the following code:
* <blockquote><pre>{@code
MemorySegment segment = ...
SegmentAllocator prefixAllocator = (size, align) -> segment.asSlice(0, size);
* }</pre></blockquote>
* <p>
* This allocator can be useful to limit allocation requests in case a client
* knows that they have fully processed the contents of the allocated segment before the subsequent allocation request * knows that they have fully processed the contents of the allocated segment before the subsequent allocation request
* takes place. * takes place.
* <p> * <p>
@ -442,25 +431,42 @@ public interface SegmentAllocator {
* @param segment the memory segment to be recycled by the returned allocator. * @param segment the memory segment to be recycled by the returned allocator.
* @return an allocator which recycles an existing segment upon each new allocation request. * @return an allocator which recycles an existing segment upon each new allocation request.
*/ */
static SegmentAllocator ofSegment(MemorySegment segment) { static SegmentAllocator prefixAllocator(MemorySegment segment) {
Objects.requireNonNull(segment); Objects.requireNonNull(segment);
return (size, align) -> segment.asSlice(0, size); return (AbstractMemorySegmentImpl)segment;
} }
/** /**
* Returns a native allocator which responds to allocation requests by allocating new segments * Returns a native allocator, associated with the provided scope. Equivalent to (but likely more efficient than)
* bound by the given resource scope, using the {@link MemorySegment#allocateNative(long, long, ResourceScope)} * the following code:
* factory. This code is equivalent (but likely more efficient) to the following:
* <blockquote><pre>{@code * <blockquote><pre>{@code
Resource scope = ... ResourceScope scope = ...
SegmentAllocator scoped = (size, align) -> MemorySegment.allocateNative(size, align, scope); SegmentAllocator nativeAllocator = (size, align) -> MemorySegment.allocateNative(size, align, scope);
* }</pre></blockquote> * }</pre></blockquote>
* *
* @param scope the resource scope associated with the segments created by the returned allocator. * @param scope the scope associated with the returned allocator.
* @return an allocator which allocates new memory segment bound by the provided resource scope. * @return a native allocator, associated with the provided scope.
*/ */
static SegmentAllocator ofScope(ResourceScope scope) { static SegmentAllocator nativeAllocator(ResourceScope scope) {
Objects.requireNonNull(scope); Objects.requireNonNull(scope);
return (ResourceScopeImpl)scope; return (ResourceScopeImpl)scope;
} }
/**
* Returns a native allocator which allocates segments in independent {@linkplain ResourceScope#newImplicitScope() implicit scopes}.
* Equivalent to (but likely more efficient than) the following code:
* <blockquote><pre>{@code
ResourceScope scope = ...
SegmentAllocator implicitAllocator = (size, align) -> MemorySegment.allocateNative(size, align, ResourceScope.newImplicitScope());
* }</pre></blockquote>
*
* @return a native allocator which allocates segments in independent {@linkplain ResourceScope#newImplicitScope() implicit scopes}.
*/
static SegmentAllocator implicitAllocator() {
class Holder {
static final SegmentAllocator IMPLICIT_ALLOCATOR = (size, align) ->
MemorySegment.allocateNative(size, align, ResourceScope.newImplicitScope());
}
return Holder.IMPLICIT_ALLOCATOR;
}
} }

View file

@ -25,31 +25,29 @@
*/ */
package jdk.incubator.foreign; package jdk.incubator.foreign;
import java.lang.constant.Constable;
import java.lang.constant.ConstantDescs; import java.lang.constant.ConstantDescs;
import java.lang.constant.DynamicConstantDesc; import java.lang.constant.DynamicConstantDesc;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalLong; import java.util.OptionalLong;
/** /**
* A sequence layout. A sequence layout is used to denote a repetition of a given layout, also called the sequence layout's <em>element layout</em>. * A sequence layout. A sequence layout is used to denote a repetition of a given layout, also called the sequence layout's <em>element layout</em>.
* The repetition count, where it exists (e.g. for <em>finite</em> sequence layouts) is said to be the the sequence layout's <em>element count</em>. * The repetition count, where it exists (e.g. for <em>finite</em> sequence layouts) is said to be the sequence layout's <em>element count</em>.
* A finite sequence layout can be thought of as a group layout where the sequence layout's element layout is repeated a number of times * A finite sequence layout can be thought of as a group layout where the sequence layout's element layout is repeated a number of times
* that is equal to the sequence layout's element count. In other words this layout: * that is equal to the sequence layout's element count. In other words this layout:
* *
* <pre>{@code * <pre>{@code
MemoryLayout.sequenceLayout(3, MemoryLayout.valueLayout(32, ByteOrder.BIG_ENDIAN)); MemoryLayout.sequenceLayout(3, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN));
* }</pre> * }</pre>
* *
* is equivalent to the following layout: * is equivalent to the following layout:
* *
* <pre>{@code * <pre>{@code
MemoryLayout.structLayout( MemoryLayout.structLayout(
MemoryLayout.valueLayout(32, ByteOrder.BIG_ENDIAN), ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN),
MemoryLayout.valueLayout(32, ByteOrder.BIG_ENDIAN), ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN),
MemoryLayout.valueLayout(32, ByteOrder.BIG_ENDIAN)); ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN));
* }</pre> * }</pre>
* *
* <p> * <p>
@ -72,13 +70,13 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
private final MemoryLayout elementLayout; private final MemoryLayout elementLayout;
SequenceLayout(OptionalLong elemCount, MemoryLayout elementLayout) { SequenceLayout(OptionalLong elemCount, MemoryLayout elementLayout) {
this(elemCount, elementLayout, elementLayout.bitAlignment(), Map.of()); this(elemCount, elementLayout, elementLayout.bitAlignment(), Optional.empty());
} }
SequenceLayout(OptionalLong elemCount, MemoryLayout elementLayout, long alignment, Map<String, Constable> attributes) { SequenceLayout(OptionalLong elemCount, MemoryLayout elementLayout, long alignment, Optional<String> name) {
super(elemCount.isPresent() && AbstractLayout.optSize(elementLayout).isPresent() ? super(elemCount.isPresent() && AbstractLayout.optSize(elementLayout).isPresent() ?
OptionalLong.of(elemCount.getAsLong() * elementLayout.bitSize()) : OptionalLong.of(elemCount.getAsLong() * elementLayout.bitSize()) :
OptionalLong.empty(), alignment, attributes); OptionalLong.empty(), alignment, name);
this.elemCount = elemCount; this.elemCount = elemCount;
this.elementLayout = elementLayout; this.elementLayout = elementLayout;
} }
@ -110,7 +108,7 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
*/ */
public SequenceLayout withElementCount(long elementCount) { public SequenceLayout withElementCount(long elementCount) {
AbstractLayout.checkSize(elementCount, true); AbstractLayout.checkSize(elementCount, true);
return new SequenceLayout(OptionalLong.of(elementCount), elementLayout, alignment, attributes); return new SequenceLayout(OptionalLong.of(elementCount), elementLayout, alignment, name());
} }
/** /**
@ -122,11 +120,11 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
* <p> * <p>
* For instance, given a sequence layout of the kind: * For instance, given a sequence layout of the kind:
* <pre>{@code * <pre>{@code
var seq = MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(3, MemoryLayouts.JAVA_INT)); var seq = MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(3, ValueLayout.JAVA_INT));
* }</pre> * }</pre>
* calling {@code seq.reshape(2, 6)} will yield the following sequence layout: * calling {@code seq.reshape(2, 6)} will yield the following sequence layout:
* <pre>{@code * <pre>{@code
var reshapeSeq = MemoryLayout.sequenceLayout(2, MemoryLayout.sequenceLayout(6, MemoryLayouts.JAVA_INT)); var reshapeSeq = MemoryLayout.sequenceLayout(2, MemoryLayout.sequenceLayout(6, ValueLayout.JAVA_INT));
* }</pre> * }</pre>
* <p> * <p>
* If one of the provided element count is the special value {@code -1}, then the element * If one of the provided element count is the special value {@code -1}, then the element
@ -151,7 +149,7 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
if (elementCounts.length == 0) { if (elementCounts.length == 0) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
if (!elementCount().isPresent()) { if (elementCount().isEmpty()) {
throw new UnsupportedOperationException("Cannot reshape a sequence layout whose element count is unspecified"); throw new UnsupportedOperationException("Cannot reshape a sequence layout whose element count is unspecified");
} }
SequenceLayout flat = flatten(); SequenceLayout flat = flatten();
@ -198,11 +196,11 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
* be dropped and their element counts will be incorporated into that of the returned sequence layout. * be dropped and their element counts will be incorporated into that of the returned sequence layout.
* For instance, given a sequence layout of the kind: * For instance, given a sequence layout of the kind:
* <pre>{@code * <pre>{@code
var seq = MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(3, MemoryLayouts.JAVA_INT)); var seq = MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(3, ValueLayout.JAVA_INT));
* }</pre> * }</pre>
* calling {@code seq.flatten()} will yield the following sequence layout: * calling {@code seq.flatten()} will yield the following sequence layout:
* <pre>{@code * <pre>{@code
var flattenedSeq = MemoryLayout.sequenceLayout(12, MemoryLayouts.JAVA_INT); var flattenedSeq = MemoryLayout.sequenceLayout(12, ValueLayout.JAVA_INT);
* }</pre> * }</pre>
* @return a new sequence layout with the same size as this layout (but, possibly, with different * @return a new sequence layout with the same size as this layout (but, possibly, with different
* element count), whose element layout is not a sequence layout. * element count), whose element layout is not a sequence layout.
@ -210,13 +208,12 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
* flattened, does not have an element count. * flattened, does not have an element count.
*/ */
public SequenceLayout flatten() { public SequenceLayout flatten() {
if (!elementCount().isPresent()) { if (elementCount().isEmpty()) {
throw badUnboundSequenceLayout(); throw badUnboundSequenceLayout();
} }
long count = elementCount().getAsLong(); long count = elementCount().getAsLong();
MemoryLayout elemLayout = elementLayout(); MemoryLayout elemLayout = elementLayout();
while (elemLayout instanceof SequenceLayout) { while (elemLayout instanceof SequenceLayout elemSeq) {
SequenceLayout elemSeq = (SequenceLayout)elemLayout;
count = count * elemSeq.elementCount().orElseThrow(this::badUnboundSequenceLayout); count = count * elemSeq.elementCount().orElseThrow(this::badUnboundSequenceLayout);
elemLayout = elemSeq.elementLayout(); elemLayout = elemSeq.elementLayout();
} }
@ -241,10 +238,9 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
if (!super.equals(other)) { if (!super.equals(other)) {
return false; return false;
} }
if (!(other instanceof SequenceLayout)) { if (!(other instanceof SequenceLayout s)) {
return false; return false;
} }
SequenceLayout s = (SequenceLayout)other;
return elemCount.equals(s.elemCount) && elementLayout.equals(s.elementLayout); return elemCount.equals(s.elemCount) && elementLayout.equals(s.elementLayout);
} }
@ -254,8 +250,8 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
} }
@Override @Override
SequenceLayout dup(long alignment, Map<String, Constable> attributes) { SequenceLayout dup(long alignment, Optional<String> name) {
return new SequenceLayout(elementCount(), elementLayout, alignment, attributes); return new SequenceLayout(elementCount(), elementLayout, alignment, name);
} }
@Override @Override
@ -290,12 +286,4 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
public SequenceLayout withBitAlignment(long alignmentBits) { public SequenceLayout withBitAlignment(long alignmentBits) {
return (SequenceLayout)super.withBitAlignment(alignmentBits); return (SequenceLayout)super.withBitAlignment(alignmentBits);
} }
/**
* {@inheritDoc}
*/
@Override
public SequenceLayout withAttribute(String name, Constable value) {
return (SequenceLayout)super.withAttribute(name, value);
}
} }

View file

@ -26,6 +26,7 @@ package jdk.incubator.foreign;
import jdk.internal.access.JavaLangAccess; import jdk.internal.access.JavaLangAccess;
import jdk.internal.access.SharedSecrets; import jdk.internal.access.SharedSecrets;
import jdk.internal.foreign.ResourceScopeImpl;
import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection; import jdk.internal.reflect.Reflection;
@ -34,11 +35,11 @@ import java.util.Optional;
/** /**
* A symbol lookup. Exposes a lookup operation for searching symbol addresses by name, see {@link SymbolLookup#lookup(String)}. * A symbol lookup. Exposes a lookup operation for searching symbol addresses by name, see {@link SymbolLookup#lookup(String)}.
* A symbol lookup can be used to lookup a symbol in a loaded library. Clients can obtain a {@linkplain #loaderLookup() loader lookup}, * A symbol lookup can be used to look up a symbol in a loaded library. Clients can obtain a {@linkplain #loaderLookup() loader lookup},
* which can be used to search symbols in libraries loaded by the current classloader (e.g. using {@link System#load(String)}, * which can be used to search symbols in libraries loaded by the current classloader (e.g. using {@link System#load(String)},
* or {@link System#loadLibrary(String)}). * or {@link System#loadLibrary(String)}).
* Alternatively, clients can obtain a {@linkplain CLinker#systemLookup() platform-dependent lookup}, to search symbols * Alternatively, clients can search symbols in the standard C library using a {@link CLinker}, which conveniently
* in the standard C library. * implements this interface.
* <p> Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null} * <p> Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null}
* elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown. </p> * elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown. </p>
*/ */
@ -49,13 +50,15 @@ public interface SymbolLookup {
* Looks up a symbol with given name in this lookup. * Looks up a symbol with given name in this lookup.
* *
* @param name the symbol name. * @param name the symbol name.
* @return the memory address associated with the symbol (if any). * @return the lookup symbol (if any).
*/ */
Optional<MemoryAddress> lookup(String name); Optional<NativeSymbol> lookup(String name);
/** /**
* Obtains a symbol lookup suitable to find symbols in native libraries associated with the caller's classloader * Obtains a symbol lookup suitable to find symbols in native libraries associated with the caller's classloader
* (that is, libraries loaded using {@link System#loadLibrary} or {@link System#load}). * (that is, libraries loaded using {@link System#loadLibrary} or {@link System#load}). The returned lookup
* returns native symbols backed by a non-closeable, shared scope which keeps the caller's classloader
* <a href="../../../java/lang/ref/package.html#reachability">reachable</a>.
* <p> * <p>
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>. * 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 * Restricted methods are unsafe, and, if used incorrectly, their use might crash
@ -72,11 +75,13 @@ public interface SymbolLookup {
Class<?> caller = Reflection.getCallerClass(); Class<?> caller = Reflection.getCallerClass();
Reflection.ensureNativeAccess(caller); Reflection.ensureNativeAccess(caller);
ClassLoader loader = Objects.requireNonNull(caller.getClassLoader()); ClassLoader loader = Objects.requireNonNull(caller.getClassLoader());
ResourceScope loaderScope = ResourceScopeImpl.heapScope(loader);
return name -> { return name -> {
Objects.requireNonNull(name); Objects.requireNonNull(name);
JavaLangAccess javaLangAccess = SharedSecrets.getJavaLangAccess(); JavaLangAccess javaLangAccess = SharedSecrets.getJavaLangAccess();
MemoryAddress addr = MemoryAddress.ofLong(javaLangAccess.findNative(loader, name)); MemoryAddress addr = MemoryAddress.ofLong(javaLangAccess.findNative(loader, name));
return addr == MemoryAddress.NULL? Optional.empty() : Optional.of(addr); return addr == MemoryAddress.NULL? Optional.empty() : Optional.of(NativeSymbol.ofAddress(name, addr, loaderScope));
}; };
} }
} }

View file

@ -0,0 +1,262 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package jdk.incubator.foreign;
import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64VaList;
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64VaList;
import jdk.internal.foreign.abi.x64.sysv.SysVVaList;
import jdk.internal.foreign.abi.x64.windows.WinVaList;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import java.util.Objects;
import java.util.function.Consumer;
/**
* An interface that models a variable argument list, 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 CLinker#downcallHandle(FunctionDescriptor) downcall method handle}.
* <p>
* Per the C specification (see C standard 6.5.2.2 Function calls - item 6),
* arguments to variadic calls are erased by way of 'default argument promotions',
* which erases integral types by way of integer promotion (see C standard 6.3.1.1 - item 2),
* and which erases all {@code float} arguments to {@code double}.
* <p>
* As such, this interface only supports reading {@code int}, {@code double},
* and any other type that fits into a {@code long}.
*
* This class is not thread safe, and all accesses should occur within a single thread
* (regardless of the scope associated with the variable arity list).
*
* <p> Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null}
* elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown. </p>
*/
sealed public interface VaList extends Addressable permits WinVaList, SysVVaList, LinuxAArch64VaList, MacOsAArch64VaList, SharedUtils.EmptyVaList {
/**
* Reads the next value as an {@code int} and advances this variable argument list's position.
*
* @param layout the layout of the value to be read.
* @return the {@code int} value read from this variable argument list.
* @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from
* a thread other than the thread owning that scope.
*/
int nextVarg(ValueLayout.OfInt layout);
/**
* Reads the next value as a {@code long} and advances this variable argument list's position.
*
* @param layout the layout of the value to be read.
* @return the {@code long} value read from this variable argument list.
* @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from
* a thread other than the thread owning that scope.
*/
long nextVarg(ValueLayout.OfLong layout);
/**
* Reads the next value as a {@code double} and advances this variable argument list's position.
*
* @param layout the layout of the value
* @return the {@code double} value read from this variable argument list.
* @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from
* a thread other than the thread owning that scope.
*/
double nextVarg(ValueLayout.OfDouble layout);
/**
* Reads the next value as a {@code MemoryAddress} and advances this variable argument list's position.
*
* @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 scope associated with this variable argument list has been closed, or if access occurs from
* a thread other than the thread owning that scope.
*/
MemoryAddress nextVarg(ValueLayout.OfAddress layout);
/**
* Reads the next value as a {@code MemorySegment}, and advances this variable argument list's position.
* <p>
* The memory segment returned by this method will be allocated using the given {@link SegmentAllocator}.
*
* @param layout the layout of the value to be read.
* @param allocator the allocator to be used to create a segment where the contents of the variable argument list
* will be copied.
* @return the {@code MemorySegment} value read from this variable argument list.
* @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from
* a thread other than the thread owning that scope.
*/
MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator);
/**
* Skips a number of elements with the given memory layouts, and advances this variable argument list's position.
*
* @param layouts the layouts of the values to be skipped.
* @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from
* a thread other than the thread owning that scope.
*/
void skip(MemoryLayout... layouts);
/**
* Returns the resource scope associated with this variable argument list.
* @return the resource scope associated with this variable argument list.
*/
ResourceScope scope();
/**
* Copies this variable argument list at its current position into a new variable argument list associated
* with the same scope as this variable argument list. Copying is useful to
* traverse the variable argument list elements, starting from the current position, without affecting the state
* of the original variable argument list, essentially allowing the elements to be traversed multiple times.
*
* @return a copy of this variable argument list.
* @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from
* a thread other than the thread owning that scope.
*/
VaList copy();
/**
* Returns the memory address associated with this variable argument list.
* @throws IllegalStateException if the scope associated with this variable argument list has been closed, or if access occurs from
* a thread other than the thread owning that scope.
* @return The memory address associated with this variable argument list.
*/
@Override
MemoryAddress address();
/**
* Constructs a new variable argument list from a memory address pointing to an existing variable argument list,
* with given resource scope.
* <p>
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
* Restricted methods are unsafe, and, if used incorrectly, their use might crash
* the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
* restricted methods, and use safe and supported functionalities, where possible.
*
* @param address a memory address pointing to an existing variable argument list.
* @param scope the resource scope 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 scope} has been already closed, or if access occurs from a thread other
* than the thread owning {@code scope}.
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
* {@code --enable-native-access} is either absent, or 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, ResourceScope scope) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
Objects.requireNonNull(address);
Objects.requireNonNull(scope);
return SharedUtils.newVaListOfAddress(address, scope);
}
/**
* Constructs a new variable argument list using a builder (see {@link Builder}), with a given resource scope.
* <p>
* If this method needs to allocate native memory, such memory will be managed by the given
* {@linkplain ResourceScope resource scope}, and will be released when the resource scope is {@linkplain ResourceScope#close closed}.
* <p>
* Note that when there are no elements added to the created va list,
* this method will return the same as {@link #empty()}.
*
* @param actions a consumer for a builder (see {@link Builder}) which can be used to specify the elements
* of the underlying variable argument list.
* @param scope scope the scope to be associated with the new variable arity list.
* @return a new variable argument list.
* @throws IllegalStateException if the scope associated with {@code allocator} has been already closed,
* or if access occurs from a thread other than the thread owning that scope.
*/
static VaList make(Consumer<Builder> actions, ResourceScope scope) {
Objects.requireNonNull(actions);
Objects.requireNonNull(scope);
return SharedUtils.newVaList(actions, scope);
}
/**
* Returns an empty variable argument list, associated with the {@linkplain ResourceScope#globalScope() global}
* scope. The resulting variable argument list does not contain any argument, and throws {@link UnsupportedOperationException}
* on all operations, except for {@link #scope()}, {@link #copy()} and {@link #address()}.
* @return an empty variable argument list.
*/
static VaList empty() {
return SharedUtils.emptyVaList();
}
/**
* A builder interface used to construct a variable argument list.
*
* <p> Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null}
* elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown. </p>
*/
sealed interface Builder permits WinVaList.Builder, SysVVaList.Builder, LinuxAArch64VaList.Builder, MacOsAArch64VaList.Builder {
/**
* Writes an {@code int} value to the variable argument list being constructed.
*
* @param layout the layout of the value to be written.
* @param value the {@code int} value to be written.
* @return this builder.
*/
Builder addVarg(ValueLayout.OfInt layout, int value);
/**
* Writes a {@code long} value to the variable argument list being constructed.
*
* @param layout the layout of the value to be written.
* @param value the {@code long} value to be written.
* @return this builder.
*/
Builder addVarg(ValueLayout.OfLong layout, long value);
/**
* Writes a {@code double} value to the variable argument list being constructed.
*
* @param layout the layout of the value to be written.
* @param value the {@code double} value to be written.
* @return this builder.
*/
Builder addVarg(ValueLayout.OfDouble layout, double value);
/**
* Writes an {@code Addressable} value 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.
* @return this builder.
*/
Builder addVarg(ValueLayout.OfAddress layout, Addressable value);
/**
* Writes a {@code MemorySegment} value, with given layout, to the variable argument list being constructed.
*
* @param layout the layout of the value to be written.
* @param value the {@code MemorySegment} whose contents will be copied.
* @return this builder.
*/
Builder addVarg(GroupLayout layout, MemorySegment value);
}
}

View file

@ -25,19 +25,30 @@
*/ */
package jdk.incubator.foreign; package jdk.incubator.foreign;
import java.lang.constant.Constable; import jdk.internal.foreign.Utils;
import jdk.internal.misc.Unsafe;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Stable;
import sun.invoke.util.Wrapper;
import java.lang.constant.ConstantDescs; import java.lang.constant.ConstantDescs;
import java.lang.constant.DynamicConstantDesc; import java.lang.constant.DynamicConstantDesc;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalLong; import java.util.OptionalLong;
/** /**
* 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 * 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 and a byte order (see {@link ByteOrder}). * (either signed or unsigned) and <em>floating-point</em> types. Each value layout has a size, 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.
* <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}.
* <p> * <p>
* This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a> * This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
* class; programmers should treat instances that are * class; programmers should treat instances that are
@ -52,17 +63,22 @@ import java.util.OptionalLong;
* @implSpec * @implSpec
* This class is immutable and thread-safe. * This class is immutable and thread-safe.
*/ */
public final class ValueLayout extends AbstractLayout implements MemoryLayout { public sealed class ValueLayout extends AbstractLayout implements MemoryLayout {
private final Class<?> carrier;
private final ByteOrder order; private final ByteOrder order;
ValueLayout(ByteOrder order, long size) { private static final int ADDRESS_SIZE_BITS = Unsafe.ADDRESS_SIZE * 8;
this(order, size, size, Map.of());
ValueLayout(Class<?> carrier, ByteOrder order, long size) {
this(carrier, order, size, size, Optional.empty());
} }
ValueLayout(ByteOrder order, long size, long alignment, Map<String, Constable> attributes) { ValueLayout(Class<?> carrier, ByteOrder order, long size, long alignment, Optional<String> name) {
super(OptionalLong.of(size), alignment, attributes); super(OptionalLong.of(size), alignment, name);
this.carrier = carrier;
this.order = order; this.order = order;
checkCarrierSize(carrier, size);
} }
/** /**
@ -81,7 +97,7 @@ public final class ValueLayout extends AbstractLayout implements MemoryLayout {
* @return a new value layout with given byte order. * @return a new value layout with given byte order.
*/ */
public ValueLayout withOrder(ByteOrder order) { public ValueLayout withOrder(ByteOrder order) {
return new ValueLayout(Objects.requireNonNull(order), bitSize(), alignment, attributes); return new ValueLayout(carrier, Objects.requireNonNull(order), bitSize(), alignment, name());
} }
@Override @Override
@ -99,29 +115,37 @@ public final class ValueLayout extends AbstractLayout implements MemoryLayout {
if (!super.equals(other)) { if (!super.equals(other)) {
return false; return false;
} }
if (!(other instanceof ValueLayout)) { if (!(other instanceof ValueLayout v)) {
return false; return false;
} }
ValueLayout v = (ValueLayout)other; return carrier.equals(v.carrier) &&
return order.equals(v.order) && order.equals(v.order) &&
bitSize() == v.bitSize() && bitSize() == v.bitSize() &&
alignment == v.alignment; alignment == v.alignment;
} }
/**
* Returns the carrier associated with this value layout.
* @return the carrier associated with this value layout.
*/
public Class<?> carrier() {
return carrier;
}
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(super.hashCode(), order, bitSize(), alignment); return Objects.hash(super.hashCode(), order, bitSize(), alignment);
} }
@Override @Override
ValueLayout dup(long alignment, Map<String, Constable> attributes) { ValueLayout dup(long alignment, Optional<String> name) {
return new ValueLayout(order, bitSize(), alignment, attributes); return new ValueLayout(carrier, order, bitSize(), alignment, name());
} }
@Override @Override
public Optional<DynamicConstantDesc<ValueLayout>> describeConstable() { public Optional<DynamicConstantDesc<ValueLayout>> describeConstable() {
return Optional.of(decorateLayoutConstant(DynamicConstantDesc.ofNamed(ConstantDescs.BSM_INVOKE, "value", return Optional.of(decorateLayoutConstant(DynamicConstantDesc.ofNamed(ConstantDescs.BSM_INVOKE, "value",
CD_VALUE_LAYOUT, MH_VALUE, bitSize(), order == ByteOrder.BIG_ENDIAN ? BIG_ENDIAN : LITTLE_ENDIAN))); CD_VALUE_LAYOUT, MH_VALUE, carrier().describeConstable().get(), order == ByteOrder.BIG_ENDIAN ? BIG_ENDIAN : LITTLE_ENDIAN)));
} }
//hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout //hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
@ -143,11 +167,439 @@ public final class ValueLayout extends AbstractLayout implements MemoryLayout {
return (ValueLayout)super.withBitAlignment(alignmentBits); return (ValueLayout)super.withBitAlignment(alignmentBits);
} }
/** static void checkCarrierSize(Class<?> carrier, long size) {
* {@inheritDoc} if (!isValidCarrier(carrier)) {
*/ throw new IllegalArgumentException("Invalid carrier: " + carrier.getName());
@Override }
public ValueLayout withAttribute(String name, Constable value) { if (carrier == MemoryAddress.class && size != ADDRESS_SIZE_BITS) {
return (ValueLayout)super.withAttribute(name, value); 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.makeMemoryAccessVarHandle(this, false);
}
return handle;
}
/**
* A value layout whose carrier is {@code boolean.class}.
*/
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);
}
@Override
OfBoolean dup(long alignment, Optional<String> name) {
return new OfBoolean(order(), alignment, name);
}
@Override
public OfBoolean withName(String name) {
return (OfBoolean)super.withName(name);
}
@Override
public OfBoolean withBitAlignment(long alignmentBits) {
return (OfBoolean)super.withBitAlignment(alignmentBits);
}
@Override
public OfBoolean withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfBoolean(order, alignment, name());
}
}
/**
* A value layout whose carrier is {@code byte.class}.
*/
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);
}
@Override
OfByte dup(long alignment, Optional<String> name) {
return new OfByte(order(), alignment, name);
}
@Override
public OfByte withName(String name) {
return (OfByte)super.withName(name);
}
@Override
public OfByte withBitAlignment(long alignmentBits) {
return (OfByte)super.withBitAlignment(alignmentBits);
}
@Override
public OfByte withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfByte(order, alignment, name());
}
}
/**
* A value layout whose carrier is {@code char.class}.
*/
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);
}
@Override
OfChar dup(long alignment, Optional<String> name) {
return new OfChar(order(), alignment, name);
}
@Override
public OfChar withName(String name) {
return (OfChar)super.withName(name);
}
@Override
public OfChar withBitAlignment(long alignmentBits) {
return (OfChar)super.withBitAlignment(alignmentBits);
}
@Override
public OfChar withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfChar(order, alignment, name());
}
}
/**
* A value layout whose carrier is {@code short.class}.
*/
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);
}
@Override
OfShort dup(long alignment, Optional<String> name) {
return new OfShort(order(), alignment, name);
}
@Override
public OfShort withName(String name) {
return (OfShort)super.withName(name);
}
@Override
public OfShort withBitAlignment(long alignmentBits) {
return (OfShort)super.withBitAlignment(alignmentBits);
}
@Override
public OfShort withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfShort(order, alignment, name());
}
}
/**
* A value layout whose carrier is {@code int.class}.
*/
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);
}
@Override
OfInt dup(long alignment, Optional<String> name) {
return new OfInt(order(), alignment, name);
}
@Override
public OfInt withName(String name) {
return (OfInt)super.withName(name);
}
@Override
public OfInt withBitAlignment(long alignmentBits) {
return (OfInt)super.withBitAlignment(alignmentBits);
}
@Override
public OfInt withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfInt(order, alignment, name());
}
}
/**
* A value layout whose carrier is {@code float.class}.
*/
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);
}
@Override
OfFloat dup(long alignment, Optional<String> name) {
return new OfFloat(order(), alignment, name);
}
@Override
public OfFloat withName(String name) {
return (OfFloat)super.withName(name);
}
@Override
public OfFloat withBitAlignment(long alignmentBits) {
return (OfFloat)super.withBitAlignment(alignmentBits);
}
@Override
public OfFloat withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfFloat(order, alignment, name());
}
}
/**
* A value layout whose carrier is {@code long.class}.
*/
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);
}
@Override
OfLong dup(long alignment, Optional<String> name) {
return new OfLong(order(), alignment, name);
}
@Override
public OfLong withName(String name) {
return (OfLong)super.withName(name);
}
@Override
public OfLong withBitAlignment(long alignmentBits) {
return (OfLong)super.withBitAlignment(alignmentBits);
}
@Override
public OfLong withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfLong(order, alignment, name());
}
}
/**
* A value layout whose carrier is {@code double.class}.
*/
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);
}
@Override
OfDouble dup(long alignment, Optional<String> name) {
return new OfDouble(order(), alignment, name);
}
@Override
public OfDouble withName(String name) {
return (OfDouble)super.withName(name);
}
@Override
public OfDouble withBitAlignment(long alignmentBits) {
return (OfDouble)super.withBitAlignment(alignmentBits);
}
@Override
public OfDouble withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfDouble(order, alignment, name());
}
}
/**
* A value layout whose carrier is {@code MemoryAddress.class}.
*/
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);
}
@Override
OfAddress dup(long alignment, Optional<String> name) {
return new OfAddress(order(), bitSize(), alignment, name);
}
@Override
public OfAddress withName(String name) {
return (OfAddress)super.withName(name);
}
@Override
public OfAddress withBitAlignment(long alignmentBits) {
return (OfAddress)super.withBitAlignment(alignmentBits);
}
@Override
public OfAddress withOrder(ByteOrder order) {
Objects.requireNonNull(order);
return new OfAddress(order, bitSize(), alignment, name());
}
}
/**
* A value layout constant whose size is the same as that of a machine address (e.g. {@code size_t}),
* bit-alignment set to 8, and byte order set to {@link ByteOrder#nativeOrder()}.
* Equivalent to the following code:
* <blockquote><pre>{@code
MemoryLayout.valueLayout(MemoryAddress.class, ByteOrder.nativeOrder()).withBitAlignment(8);
* }</pre></blockquote>
*/
public static final OfAddress ADDRESS = new OfAddress(ByteOrder.nativeOrder()).withBitAlignment(8);
/**
* 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:
* <blockquote><pre>{@code
MemoryLayout.valueLayout(byte.class, ByteOrder.nativeOrder()).withBitAlignment(8);
* }</pre></blockquote>
*/
public static final OfByte JAVA_BYTE = new OfByte(ByteOrder.nativeOrder()).withBitAlignment(8);
/**
* 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:
* <blockquote><pre>{@code
MemoryLayout.valueLayout(boolean.class, ByteOrder.nativeOrder()).withBitAlignment(8);
* }</pre></blockquote>
*/
public static final OfBoolean JAVA_BOOLEAN = new OfBoolean(ByteOrder.nativeOrder()).withBitAlignment(8);
/**
* A value layout constant whose size is the same as that of a Java {@code char},
* bit-alignment set to 8, and byte order set to {@link ByteOrder#nativeOrder()}.
* Equivalent to the following code:
* <blockquote><pre>{@code
MemoryLayout.valueLayout(char.class, ByteOrder.nativeOrder()).withBitAlignment(8);
* }</pre></blockquote>
*/
public static final OfChar JAVA_CHAR = new OfChar(ByteOrder.nativeOrder()).withBitAlignment(8);
/**
* A value layout constant whose size is the same as that of a Java {@code short},
* bit-alignment set to 8, and byte order set to {@link ByteOrder#nativeOrder()}.
* Equivalent to the following code:
* <blockquote><pre>{@code
MemoryLayout.valueLayout(short.class, ByteOrder.nativeOrder()).withBitAlignment(8);
* }</pre></blockquote>
*/
public static final OfShort JAVA_SHORT = new OfShort(ByteOrder.nativeOrder()).withBitAlignment(8);
/**
* A value layout constant whose size is the same as that of a Java {@code int},
* bit-alignment set to 8, and byte order set to {@link ByteOrder#nativeOrder()}.
* Equivalent to the following code:
* <blockquote><pre>{@code
MemoryLayout.valueLayout(int.class, ByteOrder.nativeOrder()).withBitAlignment(8);
* }</pre></blockquote>
*/
public static final OfInt JAVA_INT = new OfInt(ByteOrder.nativeOrder()).withBitAlignment(8);
/**
* A value layout constant whose size is the same as that of a Java {@code long},
* bit-alignment set to 8, and byte order set to {@link ByteOrder#nativeOrder()}.
* Equivalent to the following code:
* <blockquote><pre>{@code
MemoryLayout.valueLayout(long.class, ByteOrder.nativeOrder()).withBitAlignment(8);
* }</pre></blockquote>
*/
public static final OfLong JAVA_LONG = new OfLong(ByteOrder.nativeOrder())
.withBitAlignment(8);
/**
* A value layout constant whose size is the same as that of a Java {@code float},
* bit-alignment set to 8, and byte order set to {@link ByteOrder#nativeOrder()}.
* Equivalent to the following code:
* <blockquote><pre>{@code
MemoryLayout.valueLayout(float.class, ByteOrder.nativeOrder()).withBitAlignment(8);
* }</pre></blockquote>
*/
public static final OfFloat JAVA_FLOAT = new OfFloat(ByteOrder.nativeOrder()).withBitAlignment(8);
/**
* A value layout constant whose size is the same as that of a Java {@code double},
* bit-alignment set to 8, and byte order set to {@link ByteOrder#nativeOrder()}.
* Equivalent to the following code:
* <blockquote><pre>{@code
MemoryLayout.valueLayout(double.class, ByteOrder.nativeOrder()).withBitAlignment(8);
* }</pre></blockquote>
*/
public static final OfDouble JAVA_DOUBLE = new OfDouble(ByteOrder.nativeOrder()).withBitAlignment(8);
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -30,12 +30,11 @@
* <h2>Foreign memory access</h2> * <h2>Foreign memory access</h2>
* *
* <p> * <p>
* The key abstractions introduced to support foreign memory access are {@link jdk.incubator.foreign.MemorySegment} and {@link jdk.incubator.foreign.MemoryAddress}. * The main abstractions introduced to support foreign memory access is {@link jdk.incubator.foreign.MemorySegment}, which
* The first models a contiguous memory region, which can reside either inside or outside the Java heap; the latter models an address - which also can * models a contiguous memory region, which can reside either inside or outside the Java heap.
* reside either inside or outside the Java heap (and can sometimes be expressed as an offset into a given segment).
* A memory segment represents the main access coordinate of a memory access var handle, which can be obtained * A memory segment represents the main access coordinate of a memory access var handle, which can be obtained
* using the combinator methods defined in the {@link jdk.incubator.foreign.MemoryHandles} class; a set of * using the combinator methods defined in the {@link jdk.incubator.foreign.MemoryHandles} class; a set of
* common dereference operations is provided also by the {@link jdk.incubator.foreign.MemoryAccess} class, which can * common dereference and copy operations is provided also by the {@link jdk.incubator.foreign.MemorySegment} class, which can
* be useful for simple, non-structured access. Finally, the {@link jdk.incubator.foreign.MemoryLayout} class * be useful for simple, non-structured access. Finally, the {@link jdk.incubator.foreign.MemoryLayout} class
* hierarchy enables description of <em>memory layouts</em> and basic operations such as computing the size in bytes of a given * hierarchy enables description of <em>memory layouts</em> and basic operations such as computing the size in bytes of a given
* layout, obtain its alignment requirements, and so on. Memory layouts also provide an alternate, more abstract way, to produce * layout, obtain its alignment requirements, and so on. Memory layouts also provide an alternate, more abstract way, to produce
@ -47,16 +46,18 @@
* <pre>{@code * <pre>{@code
MemorySegment segment = MemorySegment.allocateNative(10 * 4, ResourceScope.newImplicitScope()); MemorySegment segment = MemorySegment.allocateNative(10 * 4, ResourceScope.newImplicitScope());
for (int i = 0 ; i < 10 ; i++) { for (int i = 0 ; i < 10 ; i++) {
MemoryAccess.setIntAtIndex(segment, i, 42); segment.setAtIndex(ValueLayout.JAVA_INT, i, i);
} }
* }</pre> * }</pre>
* *
* Here create a <em>native</em> memory segment, that is, a memory segment backed by * 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}. * off-heap memory; the size of the segment is 40 bytes, enough to store 10 values of the primitive type {@code int}.
* Inside a loop, we then initialize the contents of the memory segment using the * Inside a loop, we then initialize the contents of the memory segment; note how the
* {@link jdk.incubator.foreign.MemoryAccess#setIntAtIndex(jdk.incubator.foreign.MemorySegment, long, int)} helper method; * {@linkplain jdk.incubator.foreign.MemorySegment#setAtIndex(ValueLayout.OfInt, long, int) dereference method}
* more specifically, if we view the memory segment as a set of 10 adjacent slots, * accepts a {@linkplain jdk.incubator.foreign.ValueLayout value layout}, which specifies the size, alignment constraints,
* {@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 * byte order as well as the Java type ({@code int}, in this case) associated with the dereference 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}. * so that {@code s[i] = i}, again where {@code 0 <= i < 10}.
* *
* <h3><a id="deallocation"></a>Deterministic deallocation</h3> * <h3><a id="deallocation"></a>Deterministic deallocation</h3>
@ -72,7 +73,7 @@ for (int i = 0 ; i < 10 ; i++) {
try (ResourceScope scope = ResourceScope.newConfinedScope()) { try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment segment = MemorySegment.allocateNative(10 * 4, scope); MemorySegment segment = MemorySegment.allocateNative(10 * 4, scope);
for (int i = 0 ; i < 10 ; i++) { for (int i = 0 ; i < 10 ; i++) {
MemoryAccess.setIntAtIndex(segment, i, 42); segment.setAtIndex(ValueLayout.JAVA_INT, i, i);
} }
} }
* }</pre> * }</pre>
@ -96,9 +97,10 @@ try (ResourceScope scope = ResourceScope.newConfinedScope()) {
* operation either succeeds - and accesses a valid memory location - or fails. * operation either succeeds - and accesses a valid memory location - or fails.
* *
* <h2>Foreign function access</h2> * <h2>Foreign function access</h2>
* The key abstractions introduced to support foreign function access are {@link jdk.incubator.foreign.SymbolLookup} and {@link jdk.incubator.foreign.CLinker}. * The key abstractions introduced to support foreign function access are {@link jdk.incubator.foreign.SymbolLookup},
* The former is used to lookup symbols inside native libraries; the latter * {@link jdk.incubator.foreign.MemoryAddress} and {@link jdk.incubator.foreign.CLinker}.
* provides linking capabilities which allow to model foreign functions as {@link java.lang.invoke.MethodHandle} instances, * The first is used to lookup symbols inside native libraries; the second is used to model native addresses (more on that later),
* while the third provides linking capabilities which allows modelling foreign functions as {@link java.lang.invoke.MethodHandle} instances,
* so that clients can perform foreign function calls directly in Java, without the need for intermediate layers of native * so that clients can perform foreign function calls directly in Java, without the need for intermediate layers of native
* code (as it's the case with the <a href="{@docRoot}/../specs/jni/index.html">Java Native Interface (JNI)</a>). * code (as it's the case with the <a href="{@docRoot}/../specs/jni/index.html">Java Native Interface (JNI)</a>).
* <p> * <p>
@ -106,30 +108,32 @@ try (ResourceScope scope = ResourceScope.newConfinedScope()) {
* we can use the following code: * we can use the following code:
* *
* <pre>{@code * <pre>{@code
MethodHandle strlen = CLinker.getInstance().downcallHandle( var linker = CLinker.systemCLinker();
CLinker.systemLookup().lookup("strlen").get(), MethodHandle strlen = linker.downcallHandle(
MethodType.methodType(long.class, MemoryAddress.class), linker.lookup("strlen").get(),
FunctionDescriptor.of(CLinker.C_LONG, CLinker.C_POINTER) FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS)
); );
try (var scope = ResourceScope.newConfinedScope()) { try (var scope = ResourceScope.newConfinedScope()) {
var cString = CLinker.toCString("Hello", scope); var cString = MemorySegment.allocateNative(5 + 1, scope);
long len = (long)strlen.invokeExact(cString.address()); // 5 cString.setUtf8String("Hello");
long len = (long)strlen.invoke(cString); // 5
} }
* }</pre> * }</pre>
* *
* Here, we lookup the {@code strlen} symbol in the {@linkplain jdk.incubator.foreign.CLinker#systemLookup() system lookup}. * Here, we obtain a {@linkplain jdk.incubator.foreign.CLinker#systemCLinker() linker instance} and we use it
* Then, we obtain a linker instance (see {@link jdk.incubator.foreign.CLinker#getInstance()}) and we use it to * to {@linkplain jdk.incubator.foreign.CLinker#lookup(java.lang.String) lookup} the {@code strlen} symbol in the
* obtain a method handle which targets the {@code strlen} library symbol. To complete the linking successfully, * standard C library; a <em>downcall method handle</em> targeting said symbol is subsequently
* we must provide (i) a {@link java.lang.invoke.MethodType} instance, describing the type of the resulting method handle * {@linkplain jdk.incubator.foreign.CLinker#downcallHandle(jdk.incubator.foreign.FunctionDescriptor) obtained}.
* and (ii) a {@link jdk.incubator.foreign.FunctionDescriptor} instance, describing the signature of the {@code strlen} * To complete the linking successfully, we must provide a {@link jdk.incubator.foreign.FunctionDescriptor} instance,
* function. From this information, the linker will uniquely determine the sequence of steps which will turn * describing the signature of the {@code strlen} function.
* the method handle invocation (here performed using {@link java.lang.invoke.MethodHandle#invokeExact(java.lang.Object...)}) * From this information, the linker will uniquely determine the sequence of steps which will turn
* into a foreign function call, according to the rules specified by the platform C ABI. The {@link jdk.incubator.foreign.CLinker} * the method handle invocation (here performed using {@link java.lang.invoke.MethodHandle#invoke(java.lang.Object...)})
* class also provides many useful methods for interacting with native code, such as converting Java strings into * into a foreign function call, according to the rules specified by the platform C ABI.
* native strings and viceversa (see {@link jdk.incubator.foreign.CLinker#toCString(java.lang.String, ResourceScope)} and * The {@link jdk.incubator.foreign.MemorySegment} class also provides many useful methods for
* {@link jdk.incubator.foreign.CLinker#toJavaString(jdk.incubator.foreign.MemorySegment)}, respectively), as * interacting with native code, such as converting Java strings
* demonstrated in the above example. * {@linkplain jdk.incubator.foreign.MemorySegment#setUtf8String(long, java.lang.String) into} native strings and
* {@linkplain jdk.incubator.foreign.MemorySegment#getUtf8String(long) back}, as demonstrated in the above example.
* *
* <h3>Foreign addresses</h3> * <h3>Foreign addresses</h3>
* *
@ -138,53 +142,40 @@ try (ResourceScope scope = ResourceScope.newConfinedScope()) {
* such pointers have no spatial bounds (example: does the C type {@code char*} refer to a single {@code char} value, * such pointers have no spatial bounds (example: does the C type {@code char*} refer to a single {@code char} value,
* or an array of {@code char} values, of given size?), no notion of temporal bounds, nor thread-confinement. * or an array of {@code char} values, of given size?), no notion of temporal bounds, nor thread-confinement.
* <p> * <p>
* When clients receive a {@link jdk.incubator.foreign.MemoryAddress} instance from a foreign function call, it might be * Raw pointers are modelled using the {@link jdk.incubator.foreign.MemoryAddress} class. When clients receive a
* necessary to obtain a {@link jdk.incubator.foreign.MemorySegment} instance to dereference the memory pointed to by that address. * memory address instance from a foreign function call, they can perform memory dereference on it directly,
* To do that, clients can proceed in three different ways, described below. * using one of the many <em>unsafe</em>
* <p> * {@linkplain jdk.incubator.foreign.MemoryAddress#get(jdk.incubator.foreign.ValueLayout.OfInt, long) dereference methods}
* First, if the memory address is known to belong to a segment the client already owns, a <em>rebase</em> operation can be performed; * provided:
* in other words, the client can ask the address what its offset relative to a given segment is, and, then, proceed to dereference
* the original segment accordingly, as follows:
* *
* <pre>{@code * <pre>{@code
MemorySegment segment = MemorySegment.allocateNative(100, scope);
... ...
MemoryAddress addr = ... //obtain address from native code MemoryAddress addr = ... //obtain address from native code
int x = MemoryAccess.getIntAtOffset(segment, addr.segmentOffset(segment)); int x = addr.get(ValueLayout.JAVA_INT, 0);
* }</pre> * }</pre>
* *
* Secondly, if the client does <em>not</em> have a segment which contains a given memory address, it can create one <em>unsafely</em>, * Alternatively, the client can
* using the {@link jdk.incubator.foreign.MemoryAddress#asSegment(long, ResourceScope)} factory. This allows the client to * {@linkplain jdk.incubator.foreign.MemorySegment#ofAddress(jdk.incubator.foreign.MemoryAddress, long, jdk.incubator.foreign.ResourceScope) create}
* inject extra knowledge about spatial bounds which might, for instance, be available in the documentation of the foreign function * a memory segment <em>unsafely</em>. This allows the client to inject extra knowledge about spatial bounds which might,
* which produced the native address. Here is how an unsafe segment can be created from a native address: * 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 native address:
* *
* <pre>{@code * <pre>{@code
ResourceScope scope = ... // initialize a resource scope object ResourceScope scope = ... // initialize a resource scope object
MemoryAddress addr = ... //obtain address from native code MemoryAddress addr = ... //obtain address from native code
MemorySegment segment = addr.asSegment(4, scope); // segment is 4 bytes long MemorySegment segment = MemorySegment.ofAddress(addr, 4, scope); // segment is 4 bytes long
int x = MemoryAccess.getInt(segment); int x = segment.get(ValueLayout.JAVA_INT, 0);
* }</pre>
*
* Alternatively, the client can fall back to use the so called <em>everything</em> segment - that is, a primordial segment
* which covers the entire native heap. This segment can be obtained by calling the {@link jdk.incubator.foreign.MemorySegment#globalNativeSegment()}
* method, so that dereference can happen without the need of creating any additional segment instances:
*
* <pre>{@code
MemoryAddress addr = ... //obtain address from native code
int x = MemoryAccess.getIntAtOffset(MemorySegment.globalNativeSegment(), addr.toRawLongValue());
* }</pre> * }</pre>
* *
* <h3>Upcalls</h3> * <h3>Upcalls</h3>
* The {@link jdk.incubator.foreign.CLinker} interface also allows to turn an existing method handle (which might point * The {@link jdk.incubator.foreign.CLinker} interface also allows to turn an existing method handle (which might point
* to a Java method) into a native memory address (see {@link jdk.incubator.foreign.MemoryAddress}), so that Java code * to a Java method) into a memory address, so that Java code can effectively be passed to other foreign functions.
* can effectively be passed to other foreign functions. For instance, we can write a method that compares two * For instance, we can write a method that compares two integer values, as follows:
* integer values, as follows:
* *
* <pre>{@code * <pre>{@code
class IntComparator { class IntComparator {
static int intCompare(MemoryAddress addr1, MemoryAddress addr2) { static int intCompare(MemoryAddress addr1, MemoryAddress addr2) {
return MemoryAccess.getIntAtOffset(MemorySegment.globalNativeSegment(), addr1.toRawLongValue()) - return addr1.get(ValueLayout.JAVA_INT, 0) - addr2.get(ValueLayout.JAVA_INT, 0);
MemoryAccess.getIntAtOffset(MemorySegment.globalNativeSegment(), addr2.toRawLongValue());
} }
} }
* }</pre> * }</pre>
@ -194,39 +185,45 @@ class IntComparator {
* method, as follows: * method, as follows:
* *
* <pre>{@code * <pre>{@code
FunctionDescriptor intCompareDescriptor = FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.ADDRESS);
MethodHandle intCompareHandle = MethodHandles.lookup().findStatic(IntComparator.class, MethodHandle intCompareHandle = MethodHandles.lookup().findStatic(IntComparator.class,
"intCompare", "intCompare",
MethodType.methodType(int.class, MemoryAddress.class, MemoryAddress.class)); CLinker.upcallType(comparFunction));
* }</pre> * }</pre>
* *
* Now that we have a method handle instance, we can link it into a fresh native memory address, using the {@link jdk.incubator.foreign.CLinker} interface, as follows: * As before, we need to create a {@link jdk.incubator.foreign.FunctionDescriptor} instance, this time describing the signature
* of the function pointer we want to create. The descriptor can be used to
* {@linkplain jdk.incubator.foreign.CLinker#upcallType(jdk.incubator.foreign.FunctionDescriptor) derive} a method type
* that can be used to lookup 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 jdk.incubator.foreign.CLinker} interface, as follows:
* *
* <pre>{@code * <pre>{@code
ResourceScope scope = ... ResourceScope scope = ...
MemoryAddress comparFunc = CLinker.getInstance().upcallStub( Addressable comparFunc = CLinker.systemCLinker().upcallStub(
intCompareHandle, intCompareHandle, intCompareDescriptor, scope);
FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER),
scope
); );
* }</pre> * }</pre>
* *
* As before, we need to provide a {@link jdk.incubator.foreign.FunctionDescriptor} instance describing the signature * The {@link jdk.incubator.foreign.FunctionDescriptor} instance created in the previous step is then used to
* of the function pointer we want to create; as before, this, coupled with the method handle type, uniquely determines the * {@linkplain jdk.incubator.foreign.CLinker#upcallStub(java.lang.invoke.MethodHandle, jdk.incubator.foreign.FunctionDescriptor, jdk.incubator.foreign.ResourceScope) create}
* sequence of steps which will allow foreign code to call {@code intCompareHandle} according to the rules specified * a new upcall stub; the layouts in the function descriptors allow the linker to determine the sequence of steps which
* by the platform C ABI. The lifecycle of the memory address returned by * allow foreign code to call the stub for {@code intCompareHandle} according to the rules specified by the platform C ABI.
* {@link jdk.incubator.foreign.CLinker#upcallStub(java.lang.invoke.MethodHandle, jdk.incubator.foreign.FunctionDescriptor, jdk.incubator.foreign.ResourceScope)} * The lifecycle of the upcall stub returned by is tied to the {@linkplain jdk.incubator.foreign.ResourceScope resource scope}
* is tied to the {@linkplain jdk.incubator.foreign.ResourceScope resource scope} parameter passed to that method. * provided when the upcall stub is created. This same scope is made available by the {@link jdk.incubator.foreign.NativeSymbol}
* instance returned by that method.
* *
* <a id="restricted"></a> * <a id="restricted"></a>
* <h2>Restricted methods</h2> * <h2>Restricted methods</h2>
* Some methods in this package are considered <em>restricted</em>. Restricted methods are typically used to bind native * 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 * 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 jdk.incubator.foreign.MemoryAddress#asSegment(long, ResourceScope)} can be used to create * the restricted method {@link MemorySegment#ofAddress(MemoryAddress, long, ResourceScope)}
* a fresh segment with given spatial bounds out of a native address. * can be used to create a fresh segment with given spatial bounds out of a native address.
* <p> * <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. * 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 jdk.incubator.foreign.MemoryAddress#asSegment(long, ResourceScope)}, if the provided * For instance, in the case of {@link MemorySegment#ofAddress(MemoryAddress, long, ResourceScope)},
* spatial bounds are incorrect, a client of the segment returned by that method might crash the VM, or corrupt * 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 * memory when attempting to dereference 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. * to never pass arguments that might cause incorrect binding of foreign data and/or functions to a Java API.
* <p> * <p>

View file

@ -1,56 +0,0 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package jdk.internal.foreign;
import jdk.incubator.foreign.Addressable;
import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.SegmentAllocator;
import jdk.internal.foreign.abi.SharedUtils;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Objects;
public abstract non-sealed class AbstractCLinker implements CLinker {
public final MethodHandle downcallHandle(Addressable symbol, MethodType type, FunctionDescriptor function) {
SharedUtils.checkSymbol(symbol);
return MethodHandles.insertArguments(downcallHandle(type, function), 0, symbol);
}
public final MethodHandle downcallHandle(Addressable symbol, SegmentAllocator allocator, MethodType type, FunctionDescriptor function) {
SharedUtils.checkSymbol(symbol);
Objects.requireNonNull(allocator);
MethodHandle downcall = MethodHandles.insertArguments(downcallHandle(type, function), 0, symbol);
if (type.returnType().equals(MemorySegment.class)) {
downcall = MethodHandles.insertArguments(downcall, 0, allocator);
}
return downcall;
}
}

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -36,6 +36,7 @@ import jdk.internal.vm.annotation.ForceInline;
import sun.security.action.GetPropertyAction; import sun.security.action.GetPropertyAction;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.*; import java.util.*;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
@ -43,6 +44,8 @@ import java.util.function.IntFunction;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE;
/** /**
* This abstract class provides an immutable implementation for the {@code MemorySegment} interface. This class contains information * This abstract class provides an immutable implementation for the {@code MemorySegment} interface. This class contains information
* about the segment's spatial and temporal bounds; each memory segment implementation is associated with an owner thread which is set at creation time. * about the segment's spatial and temporal bounds; each memory segment implementation is associated with an owner thread which is set at creation time.
@ -52,7 +55,7 @@ import java.util.stream.StreamSupport;
* are defined for each memory segment kind, see {@link NativeMemorySegmentImpl}, {@link HeapMemorySegmentImpl} and * are defined for each memory segment kind, see {@link NativeMemorySegmentImpl}, {@link HeapMemorySegmentImpl} and
* {@link MappedMemorySegmentImpl}. * {@link MappedMemorySegmentImpl}.
*/ */
public abstract non-sealed class AbstractMemorySegmentImpl extends MemorySegmentProxy implements MemorySegment { public abstract non-sealed class AbstractMemorySegmentImpl extends MemorySegmentProxy implements MemorySegment, SegmentAllocator, Scoped {
private static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess(); private static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess();
@ -140,24 +143,9 @@ public abstract non-sealed class AbstractMemorySegmentImpl extends MemorySegment
return this; return this;
} }
public void copyFrom(MemorySegment src) { @Override
AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl)Objects.requireNonNull(src); public MemorySegment allocate(long bytesSize, long bytesAlignment) {
long size = that.byteSize(); return asSlice(0, bytesSize);
checkAccess(0, size, false);
that.checkAccess(0, size, true);
SCOPED_MEMORY_ACCESS.copyMemory(scope, that.scope,
that.base(), that.min(),
base(), min(), size);
}
public void copyFromSwap(MemorySegment src, long elemSize) {
AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl)src;
long size = that.byteSize();
checkAccess(0, size, false);
that.checkAccess(0, size, true);
SCOPED_MEMORY_ACCESS.copySwapMemory(scope, that.scope,
that.base(), that.min(),
base(), min(), size, elemSize);
} }
@Override @Override
@ -175,7 +163,7 @@ public abstract non-sealed class AbstractMemorySegmentImpl extends MemorySegment
long i = 0; long i = 0;
if (length > 7) { if (length > 7) {
if (MemoryAccess.getByte(this) != MemoryAccess.getByte(that)) { if (get(JAVA_BYTE, 0) != that.get(JAVA_BYTE, 0)) {
return 0; return 0;
} }
i = vectorizedMismatchLargeForBytes(scope, that.scope, i = vectorizedMismatchLargeForBytes(scope, that.scope,
@ -190,7 +178,7 @@ public abstract non-sealed class AbstractMemorySegmentImpl extends MemorySegment
i = length - remaining; i = length - remaining;
} }
for (; i < length; i++) { for (; i < length; i++) {
if (MemoryAccess.getByteAtOffset(this, i) != MemoryAccess.getByteAtOffset(that, i)) { if (get(JAVA_BYTE, i) != that.get(JAVA_BYTE, i)) {
return i; return i;
} }
} }
@ -230,9 +218,8 @@ public abstract non-sealed class AbstractMemorySegmentImpl extends MemorySegment
} }
@Override @Override
@ForceInline public MemoryAddress address() {
public final MemoryAddress address() { throw new UnsupportedOperationException("Cannot obtain address of on-heap segment");
return new MemoryAddressImpl(this, 0L);
} }
@Override @Override
@ -269,6 +256,33 @@ public abstract non-sealed class AbstractMemorySegmentImpl extends MemorySegment
return false; return false;
} }
@Override
public final MemorySegment asOverlappingSlice(MemorySegment other) {
AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl)Objects.requireNonNull(other);
if (base() == that.base()) { // both either native or heap
final long thisStart = this.min();
final long thatStart = that.min();
final long thisEnd = thisStart + this.byteSize();
final long thatEnd = thatStart + that.byteSize();
if (thisStart < thatEnd && thisEnd > thatStart) { //overlap occurs
long offsetToThat = this.segmentOffset(that);
long newOffset = offsetToThat >= 0 ? offsetToThat : 0;
return asSlice(newOffset, Math.min(this.byteSize() - newOffset, that.byteSize() + offsetToThat));
}
}
return null;
}
@Override
public final long segmentOffset(MemorySegment other) {
AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl) Objects.requireNonNull(other);
if (base() == that.base()) {
return that.min() - this.min();
}
throw new UnsupportedOperationException("Cannot compute offset from native to heap (or vice versa).");
}
@Override @Override
public void load() { public void load() {
throw new UnsupportedOperationException("Not a mapped segment"); throw new UnsupportedOperationException("Not a mapped segment");
@ -290,45 +304,45 @@ public abstract non-sealed class AbstractMemorySegmentImpl extends MemorySegment
} }
@Override @Override
public final byte[] toByteArray() { public final byte[] toArray(ValueLayout.OfByte elementLayout) {
return toArray(byte[].class, 1, byte[]::new, MemorySegment::ofArray); return toArray(byte[].class, elementLayout, byte[]::new, MemorySegment::ofArray);
} }
@Override @Override
public final short[] toShortArray() { public final short[] toArray(ValueLayout.OfShort elementLayout) {
return toArray(short[].class, 2, short[]::new, MemorySegment::ofArray); return toArray(short[].class, elementLayout, short[]::new, MemorySegment::ofArray);
} }
@Override @Override
public final char[] toCharArray() { public final char[] toArray(ValueLayout.OfChar elementLayout) {
return toArray(char[].class, 2, char[]::new, MemorySegment::ofArray); return toArray(char[].class, elementLayout, char[]::new, MemorySegment::ofArray);
} }
@Override @Override
public final int[] toIntArray() { public final int[] toArray(ValueLayout.OfInt elementLayout) {
return toArray(int[].class, 4, int[]::new, MemorySegment::ofArray); return toArray(int[].class, elementLayout, int[]::new, MemorySegment::ofArray);
} }
@Override @Override
public final float[] toFloatArray() { public final float[] toArray(ValueLayout.OfFloat elementLayout) {
return toArray(float[].class, 4, float[]::new, MemorySegment::ofArray); return toArray(float[].class, elementLayout, float[]::new, MemorySegment::ofArray);
} }
@Override @Override
public final long[] toLongArray() { public final long[] toArray(ValueLayout.OfLong elementLayout) {
return toArray(long[].class, 8, long[]::new, MemorySegment::ofArray); return toArray(long[].class, elementLayout, long[]::new, MemorySegment::ofArray);
} }
@Override @Override
public final double[] toDoubleArray() { public final double[] toArray(ValueLayout.OfDouble elementLayout) {
return toArray(double[].class, 8, double[]::new, MemorySegment::ofArray); return toArray(double[].class, elementLayout, double[]::new, MemorySegment::ofArray);
} }
private <Z> Z toArray(Class<Z> arrayClass, int elemSize, IntFunction<Z> arrayFactory, Function<Z, MemorySegment> segmentFactory) { private <Z> Z toArray(Class<Z> arrayClass, ValueLayout elemLayout, IntFunction<Z> arrayFactory, Function<Z, MemorySegment> segmentFactory) {
int size = checkArraySize(arrayClass.getSimpleName(), elemSize); int size = checkArraySize(arrayClass.getSimpleName(), (int)elemLayout.byteSize());
Z arr = arrayFactory.apply(size); Z arr = arrayFactory.apply(size);
MemorySegment arrSegment = segmentFactory.apply(arr); MemorySegment arrSegment = segmentFactory.apply(arr);
arrSegment.copyFrom(this); MemorySegment.copy(this, elemLayout, 0, arrSegment, elemLayout.withOrder(ByteOrder.nativeOrder()), 0, size);
return arr; return arr;
} }
@ -382,11 +396,12 @@ public abstract non-sealed class AbstractMemorySegmentImpl extends MemorySegment
private void checkBounds(long offset, long length) { private void checkBounds(long offset, long length) {
if (isSmall() && if (isSmall() &&
offset < Integer.MAX_VALUE && length < Integer.MAX_VALUE && offset <= Integer.MAX_VALUE && length <= Integer.MAX_VALUE &&
offset > Integer.MIN_VALUE && length > Integer.MIN_VALUE) { offset >= Integer.MIN_VALUE && length >= Integer.MIN_VALUE) {
checkBoundsSmall((int)offset, (int)length); checkBoundsSmall((int)offset, (int)length);
} else { } else if (this != NativeMemorySegmentImpl.EVERYTHING) { // oob not possible for everything segment
if (length < 0 || if (
length < 0 ||
offset < 0 || offset < 0 ||
offset > this.length - length) { // careful of overflow offset > this.length - length) { // careful of overflow
throw outOfBoundException(offset, length); throw outOfBoundException(offset, length);

View file

@ -29,14 +29,23 @@ import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.SegmentAllocator; import jdk.incubator.foreign.SegmentAllocator;
import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.ResourceScope;
public abstract class ArenaAllocator implements SegmentAllocator { public final class ArenaAllocator implements SegmentAllocator {
protected MemorySegment segment; public static final long DEFAULT_BLOCK_SIZE = 4 * 1024;
protected long sp = 0L; private MemorySegment segment;
ArenaAllocator(MemorySegment segment) { private long sp = 0L;
this.segment = segment; private long size = 0;
private final long blockSize;
private final long arenaSize;
private final ResourceScope scope;
public ArenaAllocator(long blockSize, long arenaSize, ResourceScope scope) {
this.blockSize = blockSize;
this.arenaSize = arenaSize;
this.scope = scope;
this.segment = newSegment(blockSize, 1);
} }
MemorySegment trySlice(long bytesSize, long bytesAlignment) { MemorySegment trySlice(long bytesSize, long bytesAlignment) {
@ -51,99 +60,37 @@ public abstract class ArenaAllocator implements SegmentAllocator {
} }
} }
void checkConfinementIfNeeded() { public ResourceScope scope() {
Thread ownerThread = scope().ownerThread(); return scope;
if (ownerThread != null && ownerThread != Thread.currentThread()) {
throw new IllegalStateException("Attempt to allocate outside confinement thread");
}
} }
ResourceScope scope() { private MemorySegment newSegment(long bytesSize, long bytesAlignment) {
return segment.scope(); long allocatedSize = Utils.alignUp(bytesSize, bytesAlignment);
if (size + allocatedSize > arenaSize) {
throw new OutOfMemoryError();
}
size += allocatedSize;
return MemorySegment.allocateNative(bytesSize, bytesAlignment, scope);
} }
public static class UnboundedArenaAllocator extends ArenaAllocator { @Override
public MemorySegment allocate(long bytesSize, long bytesAlignment) {
private static final long DEFAULT_BLOCK_SIZE = 4 * 1024; // try to slice from current segment first...
MemorySegment slice = trySlice(bytesSize, bytesAlignment);
public UnboundedArenaAllocator(ResourceScope scope) { if (slice != null) {
super(MemorySegment.allocateNative(DEFAULT_BLOCK_SIZE, 1, scope)); return slice;
} } else {
long maxPossibleAllocationSize = bytesSize + bytesAlignment - 1;
private MemorySegment newSegment(long size, long align) { if (maxPossibleAllocationSize > blockSize) {
return MemorySegment.allocateNative(size, align, segment.scope()); // too big
} return newSegment(bytesSize, bytesAlignment);
@Override
public MemorySegment allocate(long bytesSize, long bytesAlignment) {
checkConfinementIfNeeded();
// try to slice from current segment first...
MemorySegment slice = trySlice(bytesSize, bytesAlignment);
if (slice != null) {
return slice;
} else { } else {
long maxPossibleAllocationSize = bytesSize + bytesAlignment - 1; // allocate a new segment and slice from there
if (maxPossibleAllocationSize > DEFAULT_BLOCK_SIZE) { sp = 0L;
// too big segment = newSegment(blockSize, 1L);
return newSegment(bytesSize, bytesAlignment); slice = trySlice(bytesSize, bytesAlignment);
} else {
// allocate a new segment and slice from there
sp = 0L;
segment = newSegment(DEFAULT_BLOCK_SIZE, 1L);
return trySlice(bytesSize, bytesAlignment);
}
}
}
}
public static class BoundedArenaAllocator extends ArenaAllocator {
public BoundedArenaAllocator(ResourceScope scope, long size) {
super(MemorySegment.allocateNative(size, 1, scope));
}
@Override
public MemorySegment allocate(long bytesSize, long bytesAlignment) {
checkConfinementIfNeeded();
// try to slice from current segment first...
MemorySegment slice = trySlice(bytesSize, bytesAlignment);
if (slice != null) {
return slice; return slice;
} else {
throw new OutOfMemoryError("Not enough space left to allocate");
} }
} }
} }
public static class BoundedSharedArenaAllocator extends BoundedArenaAllocator {
public BoundedSharedArenaAllocator(ResourceScope scope, long size) {
super(scope, size);
}
@Override
public synchronized MemorySegment allocate(long bytesSize, long bytesAlignment) {
return super.allocate(bytesSize, bytesAlignment);
}
}
public static class UnboundedSharedArenaAllocator implements SegmentAllocator {
final ResourceScope scope;
final ThreadLocal<ArenaAllocator> allocators = new ThreadLocal<>() {
@Override
protected ArenaAllocator initialValue() {
return new UnboundedArenaAllocator(scope);
}
};
public UnboundedSharedArenaAllocator(ResourceScope scope) {
this.scope = scope;
}
@Override
public MemorySegment allocate(long bytesSize, long bytesAlignment) {
return allocators.get().allocate(bytesSize, bytesAlignment);
}
}
} }

View file

@ -25,9 +25,7 @@
*/ */
package jdk.internal.foreign; package jdk.internal.foreign;
import sun.security.action.GetPropertyAction; import static jdk.incubator.foreign.ValueLayout.ADDRESS;
import static jdk.incubator.foreign.MemoryLayouts.ADDRESS;
import static sun.security.action.GetPropertyAction.privilegedGetProperty; import static sun.security.action.GetPropertyAction.privilegedGetProperty;
public enum CABI { public enum CABI {

View file

@ -25,11 +25,11 @@
package jdk.internal.foreign; package jdk.internal.foreign;
import jdk.incubator.foreign.ResourceScope;
import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.ForceInline;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.ref.Cleaner; import java.lang.ref.Cleaner;
import java.lang.ref.Reference;
/** /**
* A confined scope, which features an owner thread. The liveness check features an additional * A confined scope, which features an owner thread. The liveness check features an additional
@ -41,10 +41,21 @@ final class ConfinedScope extends ResourceScopeImpl {
private boolean closed; // = false private boolean closed; // = false
private int lockCount = 0; private int lockCount = 0;
private int asyncReleaseCount = 0;
private final Thread owner; private final Thread owner;
static final VarHandle ASYNC_RELEASE_COUNT;
static {
try {
ASYNC_RELEASE_COUNT = MethodHandles.lookup().findVarHandle(ConfinedScope.class, "asyncReleaseCount", int.class);
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
public ConfinedScope(Thread owner, Cleaner cleaner) { public ConfinedScope(Thread owner, Cleaner cleaner) {
super(cleaner, new ConfinedResourceList()); super(new ConfinedResourceList(), cleaner);
this.owner = owner; this.owner = owner;
} }
@ -64,18 +75,35 @@ final class ConfinedScope extends ResourceScopeImpl {
} }
@Override @Override
public HandleImpl acquire() { @ForceInline
public void acquire0() {
checkValidState(); checkValidState();
if (lockCount == MAX_FORKS) {
throw new IllegalStateException("Scope keep alive limit exceeded");
}
lockCount++; lockCount++;
return new ConfinedHandle(); }
@Override
@ForceInline
public void release0() {
if (Thread.currentThread() == owner) {
lockCount--;
} else {
// It is possible to end up here in two cases: this scope was kept alive by some other confined scope
// which is implicitly released (in which case the release call comes from the cleaner thread). Or,
// this scope might be kept alive by a shared scope, which means the release call can come from any
// thread.
ASYNC_RELEASE_COUNT.getAndAdd(this, 1);
}
} }
void justClose() { void justClose() {
this.checkValidState(); this.checkValidState();
if (lockCount == 0) { if (lockCount == 0 || lockCount - ((int)ASYNC_RELEASE_COUNT.getVolatile(this)) == 0) {
closed = true; closed = true;
} else { } else {
throw new IllegalStateException("Scope is acquired by " + lockCount + " locks"); throw new IllegalStateException("Scope is kept alive by " + lockCount + " scopes");
} }
} }
@ -109,25 +137,4 @@ final class ConfinedScope extends ResourceScopeImpl {
} }
} }
} }
/**
* A confined resource scope handle; no races are possible here.
*/
final class ConfinedHandle implements HandleImpl {
boolean released = false;
@Override
public ResourceScopeImpl scope() {
return ConfinedScope.this;
}
@Override
public void release() {
checkValidState(); // thread check
if (!released) {
released = true;
lockCount--;
}
}
}
} }

View file

@ -28,8 +28,6 @@ package jdk.internal.foreign;
import jdk.incubator.foreign.MemoryHandles; import jdk.incubator.foreign.MemoryHandles;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.internal.access.JavaLangInvokeAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.foreign.MemorySegmentProxy; import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.incubator.foreign.GroupLayout; import jdk.incubator.foreign.GroupLayout;
@ -53,12 +51,10 @@ import java.util.function.UnaryOperator;
* (see {@link #sequenceElement()}, {@link #sequenceElement(long)}, {@link #sequenceElement(long, long)}, {@link #groupElement(String)}). * (see {@link #sequenceElement()}, {@link #sequenceElement(long)}, {@link #sequenceElement(long, long)}, {@link #groupElement(String)}).
* Once a path has been fully constructed, clients can ask for the offset associated with the layout element selected * Once a path has been fully constructed, clients can ask for the offset associated with the layout element selected
* by the path (see {@link #offset}), or obtain a memory access var handle to access the selected layout element * by the path (see {@link #offset}), or obtain a memory access var handle to access the selected layout element
* given an address pointing to a segment associated with the root layout (see {@link #dereferenceHandle(Class)}). * given an address pointing to a segment associated with the root layout (see {@link #dereferenceHandle()}).
*/ */
public class LayoutPath { public class LayoutPath {
private static final JavaLangInvokeAccess JLI = SharedSecrets.getJavaLangInvokeAccess();
private static final MethodHandle ADD_STRIDE; private static final MethodHandle ADD_STRIDE;
private static final MethodHandle MH_ADD_SCALED_OFFSET; private static final MethodHandle MH_ADD_SCALED_OFFSET;
private static final MethodHandle MH_SLICE; private static final MethodHandle MH_SLICE;
@ -156,8 +152,10 @@ public class LayoutPath {
return offset; return offset;
} }
public VarHandle dereferenceHandle(Class<?> carrier) { public VarHandle dereferenceHandle() {
Utils.checkPrimitiveCarrierCompat(carrier, layout); if (!(layout instanceof ValueLayout valueLayout)) {
throw new IllegalArgumentException("Path does not select a value layout");
}
checkAlignment(this); checkAlignment(this);
List<Class<?>> expectedCoordinates = new ArrayList<>(); List<Class<?>> expectedCoordinates = new ArrayList<>();
@ -165,8 +163,7 @@ public class LayoutPath {
perms.addFirst(0); perms.addFirst(0);
expectedCoordinates.add(MemorySegment.class); expectedCoordinates.add(MemorySegment.class);
VarHandle handle = Utils.fixUpVarHandle(JLI.memoryAccessVarHandle(carrier, true, layout.byteAlignment() - 1, VarHandle handle = Utils.makeMemoryAccessVarHandle(valueLayout, true);
((ValueLayout)layout).order()));
for (int i = 0 ; i < strides.length ; i++) { for (int i = 0 ; i < strides.length ; i++) {
expectedCoordinates.add(long.class); expectedCoordinates.add(long.class);
@ -226,15 +223,13 @@ public class LayoutPath {
MemoryLayout newLayout = op.apply(layout); MemoryLayout newLayout = op.apply(layout);
if (enclosing == null) { if (enclosing == null) {
return newLayout; return newLayout;
} else if (enclosing.layout instanceof SequenceLayout) { } else if (enclosing.layout instanceof SequenceLayout seq) {
SequenceLayout seq = (SequenceLayout)enclosing.layout;
if (seq.elementCount().isPresent()) { if (seq.elementCount().isPresent()) {
return enclosing.map(l -> dup(l, MemoryLayout.sequenceLayout(seq.elementCount().getAsLong(), newLayout))); return enclosing.map(l -> dup(l, MemoryLayout.sequenceLayout(seq.elementCount().getAsLong(), newLayout)));
} else { } else {
return enclosing.map(l -> dup(l, MemoryLayout.sequenceLayout(newLayout))); return enclosing.map(l -> dup(l, MemoryLayout.sequenceLayout(newLayout)));
} }
} else if (enclosing.layout instanceof GroupLayout) { } else if (enclosing.layout instanceof GroupLayout g) {
GroupLayout g = (GroupLayout)enclosing.layout;
List<MemoryLayout> newElements = new ArrayList<>(g.memberLayouts()); List<MemoryLayout> newElements = new ArrayList<>(g.memberLayouts());
//if we selected a layout in a group we must have a valid index //if we selected a layout in a group we must have a valid index
newElements.set((int)elementIndex, newLayout); newElements.set((int)elementIndex, newLayout);

View file

@ -25,117 +25,62 @@
*/ */
package jdk.internal.foreign; package jdk.internal.foreign;
import jdk.incubator.foreign.Addressable;
import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.ResourceScope;
import jdk.incubator.foreign.ValueLayout;
import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection; import jdk.internal.reflect.Reflection;
import jdk.incubator.foreign.ResourceScope; import jdk.internal.vm.annotation.ForceInline;
import java.util.Objects;
/** /**
* This class provides an immutable implementation for the {@code MemoryAddress} interface. This class contains information * This class provides an immutable implementation for the {@code MemoryAddress} interface. This class contains information
* about the segment this address is associated with, as well as an offset into such segment. * about the segment this address is associated with, as well as an offset into such segment.
*/ */
public final class MemoryAddressImpl implements MemoryAddress { public final class MemoryAddressImpl implements MemoryAddress, Scoped {
private final AbstractMemorySegmentImpl segment;
private final long offset; private final long offset;
public MemoryAddressImpl(AbstractMemorySegmentImpl segment, long offset) { public MemoryAddressImpl(long offset) {
this.segment = segment;
this.offset = offset; this.offset = offset;
} }
Object base() {
return segment != null ? segment.base() : null;
}
long offset() {
return segment != null ?
segment.min() + offset : offset;
}
// MemoryAddress methods // MemoryAddress methods
@Override
public ResourceScope scope() {
return segment != null ?
segment.scope() : ResourceScope.globalScope();
}
@Override @Override
public MemoryAddress addOffset(long offset) { public MemoryAddress addOffset(long offset) {
return new MemoryAddressImpl(segment, this.offset + offset); return new MemoryAddressImpl(this.offset + offset);
}
@Override
public long segmentOffset(MemorySegment segment) {
Objects.requireNonNull(segment);
AbstractMemorySegmentImpl segmentImpl = (AbstractMemorySegmentImpl)segment;
if (segmentImpl.base() != base()) {
throw new IllegalArgumentException("Incompatible segment: " + segment);
}
return offset() - segmentImpl.min();
}
@Override
public boolean isNative() {
return base() == null;
} }
@Override @Override
public long toRawLongValue() { public long toRawLongValue() {
if (segment != null) { return offset;
if (segment.base() != null) { }
throw new UnsupportedOperationException("Not a native address");
} @Override
segment.checkValidState(); public final MemoryAddress address() {
} return this;
return offset();
} }
// Object methods // Object methods
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(base(), offset()); return (int) toRawLongValue();
} }
@Override @Override
public boolean equals(Object that) { public boolean equals(Object that) {
if (that instanceof MemoryAddressImpl) { return (that instanceof MemoryAddressImpl addressImpl &&
MemoryAddressImpl addr = (MemoryAddressImpl)that; offset == addressImpl.offset);
return Objects.equals(base(), addr.base()) &&
offset() == addr.offset();
} else {
return false;
}
} }
@Override @Override
public String toString() { public String toString() {
return "MemoryAddress{ base: " + base() + " offset=0x" + Long.toHexString(offset()) + " }"; return "MemoryAddress{ offset=0x" + Long.toHexString(offset) + " }";
}
@Override
@CallerSensitive
public final MemorySegment asSegment(long bytesSize, ResourceScope scope) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
return asSegment(bytesSize, null, scope);
}
@Override
@CallerSensitive
public final MemorySegment asSegment(long bytesSize, Runnable cleanupAction, ResourceScope scope) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
Objects.requireNonNull(scope);
if (bytesSize <= 0) {
throw new IllegalArgumentException("Invalid size : " + bytesSize);
}
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(this, bytesSize,
cleanupAction,
(ResourceScopeImpl) scope);
} }
public static MemorySegment ofLongUnchecked(long value) { public static MemorySegment ofLongUnchecked(long value) {
@ -143,10 +88,289 @@ public final class MemoryAddressImpl implements MemoryAddress {
} }
public static MemorySegment ofLongUnchecked(long value, long byteSize, ResourceScopeImpl resourceScope) { public static MemorySegment ofLongUnchecked(long value, long byteSize, ResourceScopeImpl resourceScope) {
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(MemoryAddress.ofLong(value), byteSize, null, resourceScope); return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(MemoryAddress.ofLong(value), byteSize, resourceScope);
} }
public static MemorySegment ofLongUnchecked(long value, long byteSize) { public static MemorySegment ofLongUnchecked(long value, long byteSize) {
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(MemoryAddress.ofLong(value), byteSize, null, ResourceScopeImpl.GLOBAL); return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(MemoryAddress.ofLong(value), byteSize, ResourceScopeImpl.GLOBAL);
}
@Override
public ResourceScope scope() {
return ResourceScopeImpl.GLOBAL;
}
@Override
@CallerSensitive
@ForceInline
public String getUtf8String(long offset) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
SharedUtils.checkAddress(this);
return NativeMemorySegmentImpl.EVERYTHING.getUtf8String(toRawLongValue() + offset);
}
@Override
@CallerSensitive
@ForceInline
public void setUtf8String(long offset, String str) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
SharedUtils.checkAddress(this);
NativeMemorySegmentImpl.EVERYTHING.setUtf8String(toRawLongValue() + offset, str);
}
@Override
@ForceInline
@CallerSensitive
public byte get(ValueLayout.OfByte layout, long offset) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset);
}
@Override
@ForceInline
@CallerSensitive
public void set(ValueLayout.OfByte layout, long offset, byte value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value);
}
@Override
@ForceInline
@CallerSensitive
public boolean get(ValueLayout.OfBoolean layout, long offset) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset);
}
@Override
@ForceInline
@CallerSensitive
public void set(ValueLayout.OfBoolean layout, long offset, boolean value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value);
}
@Override
@ForceInline
@CallerSensitive
public char get(ValueLayout.OfChar layout, long offset) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset);
}
@Override
@ForceInline
@CallerSensitive
public void set(ValueLayout.OfChar layout, long offset, char value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value);
}
@Override
@ForceInline
@CallerSensitive
public short get(ValueLayout.OfShort layout, long offset) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset);
}
@Override
@ForceInline
@CallerSensitive
public void set(ValueLayout.OfShort layout, long offset, short value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value);
}
@Override
@ForceInline
@CallerSensitive
public int get(ValueLayout.OfInt layout, long offset) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset);
}
@Override
@ForceInline
@CallerSensitive
public void set(ValueLayout.OfInt layout, long offset, int value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value);
}
@Override
@ForceInline
@CallerSensitive
public float get(ValueLayout.OfFloat layout, long offset) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset);
}
@Override
@ForceInline
@CallerSensitive
public void set(ValueLayout.OfFloat layout, long offset, float value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value);
}
@Override
@ForceInline
@CallerSensitive
public long get(ValueLayout.OfLong layout, long offset) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset);
}
@Override
@ForceInline
@CallerSensitive
public void set(ValueLayout.OfLong layout, long offset, long value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value);
}
@Override
@ForceInline
@CallerSensitive
public double get(ValueLayout.OfDouble layout, long offset) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset);
}
@Override
@ForceInline
@CallerSensitive
public void set(ValueLayout.OfDouble layout, long offset, double value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value);
}
@Override
@ForceInline
@CallerSensitive
public MemoryAddress get(ValueLayout.OfAddress layout, long offset) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + offset);
}
@Override
@ForceInline
@CallerSensitive
public void set(ValueLayout.OfAddress layout, long offset, Addressable value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + offset, value.address());
}
@Override
@ForceInline
@CallerSensitive
public char getAtIndex(ValueLayout.OfChar layout, long index) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + (index * layout.byteSize()));
}
@Override
@ForceInline
@CallerSensitive
public void setAtIndex(ValueLayout.OfChar layout, long index, char value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value);
}
@Override
@ForceInline
@CallerSensitive
public short getAtIndex(ValueLayout.OfShort layout, long index) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + (index * layout.byteSize()));
}
@Override
@ForceInline
@CallerSensitive
public void setAtIndex(ValueLayout.OfShort layout, long index, short value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value);
}
@Override
@ForceInline
@CallerSensitive
public int getAtIndex(ValueLayout.OfInt layout, long index) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + (index * layout.byteSize()));
}
@Override
@ForceInline
@CallerSensitive
public void setAtIndex(ValueLayout.OfInt layout, long index, int value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value);
}
@Override
@ForceInline
@CallerSensitive
public float getAtIndex(ValueLayout.OfFloat layout, long index) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + (index * layout.byteSize()));
}
@Override
@ForceInline
@CallerSensitive
public void setAtIndex(ValueLayout.OfFloat layout, long index, float value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value);
}
@Override
@ForceInline
@CallerSensitive
public long getAtIndex(ValueLayout.OfLong layout, long index) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + (index * layout.byteSize()));
}
@Override
@ForceInline
@CallerSensitive
public void setAtIndex(ValueLayout.OfLong layout, long index, long value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value);
}
@Override
@ForceInline
@CallerSensitive
public double getAtIndex(ValueLayout.OfDouble layout, long index) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + (index * layout.byteSize()));
}
@Override
@ForceInline
@CallerSensitive
public void setAtIndex(ValueLayout.OfDouble layout, long index, double value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value);
}
@Override
@ForceInline
@CallerSensitive
public MemoryAddress getAtIndex(ValueLayout.OfAddress layout, long index) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + (index * layout.byteSize()));
}
@Override
@ForceInline
@CallerSensitive
public void setAtIndex(ValueLayout.OfAddress layout, long index, Addressable value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value.address());
} }
} }

View file

@ -28,8 +28,6 @@ package jdk.internal.foreign;
import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.ResourceScope;
import jdk.incubator.foreign.SegmentAllocator;
import jdk.internal.misc.Unsafe; import jdk.internal.misc.Unsafe;
import jdk.internal.misc.VM; import jdk.internal.misc.VM;
import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.ForceInline;
@ -43,12 +41,10 @@ import java.nio.ByteBuffer;
*/ */
public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl { public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl {
public static final MemorySegment EVERYTHING = makeNativeSegmentUnchecked(MemoryAddress.NULL, Long.MAX_VALUE, null, ResourceScopeImpl.GLOBAL); public static final MemorySegment EVERYTHING = makeNativeSegmentUnchecked(MemoryAddress.NULL, Long.MAX_VALUE, ResourceScopeImpl.GLOBAL);
private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final Unsafe unsafe = Unsafe.getUnsafe();
public static final SegmentAllocator IMPLICIT_ALLOCATOR = (size, align) -> MemorySegment.allocateNative(size, align, ResourceScope.newImplicitScope());
// The maximum alignment supported by malloc - typically 16 on // The maximum alignment supported by malloc - typically 16 on
// 64-bit platforms and 8 on 32-bit platforms. // 64-bit platforms and 8 on 32-bit platforms.
private static final long MAX_MALLOC_ALIGN = Unsafe.ADDRESS_SIZE == 4 ? 8 : 16; private static final long MAX_MALLOC_ALIGN = Unsafe.ADDRESS_SIZE == 4 ? 8 : 16;
@ -63,6 +59,13 @@ public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl {
this.min = min; this.min = min;
} }
@ForceInline
@Override
public MemoryAddress address() {
checkValidState();
return MemoryAddress.ofLong(unsafeGetOffset());
}
@Override @Override
NativeMemorySegmentImpl dup(long offset, long size, int mask, ResourceScopeImpl scope) { NativeMemorySegmentImpl dup(long offset, long size, int mask, ResourceScopeImpl scope) {
return new NativeMemorySegmentImpl(min + offset, size, mask, scope); return new NativeMemorySegmentImpl(min + offset, size, mask, scope);
@ -123,12 +126,9 @@ public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl {
return segment; return segment;
} }
public static MemorySegment makeNativeSegmentUnchecked(MemoryAddress min, long bytesSize, Runnable cleanupAction, ResourceScopeImpl scope) { public static MemorySegment makeNativeSegmentUnchecked(MemoryAddress min, long bytesSize, ResourceScopeImpl scope) {
scope.checkValidStateSlow(); scope.checkValidStateSlow();
AbstractMemorySegmentImpl segment = new NativeMemorySegmentImpl(min.toRawLongValue(), bytesSize, defaultAccessModes(bytesSize), scope); AbstractMemorySegmentImpl segment = new NativeMemorySegmentImpl(min.toRawLongValue(), bytesSize, defaultAccessModes(bytesSize), scope);
if (cleanupAction != null) {
scope.addCloseAction(cleanupAction);
}
return segment; return segment;
} }
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -18,13 +18,21 @@
* 2 along with this work; if not, write to the Free Software Foundation, * 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
* *
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 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 * or visit www.oracle.com if you need additional information or have any
* questions. * questions.
*
*/ */
package jdk.internal.foreign;
package jdk.internal.foreign.abi; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.NativeSymbol;
import jdk.incubator.foreign.ResourceScope;
public interface UpcallHandler { public record NativeSymbolImpl(String name, MemoryAddress address, ResourceScope scope) implements NativeSymbol, Scoped {
long entryPoint(); @Override
public MemoryAddress address() {
((ResourceScopeImpl)scope).checkValidState();
return address;
}
} }

View file

@ -25,14 +25,9 @@
*/ */
package jdk.internal.foreign; package jdk.internal.foreign;
import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.ValueLayout; import jdk.incubator.foreign.ValueLayout;
import java.nio.ByteOrder;
import static java.nio.ByteOrder.LITTLE_ENDIAN;
public class PlatformLayouts { public class PlatformLayouts {
public static <Z extends MemoryLayout> Z pick(Z sysv, Z win64, Z aarch64) { public static <Z extends MemoryLayout> Z pick(Z sysv, Z win64, Z aarch64) {
return switch (CABI.current()) { return switch (CABI.current()) {
@ -42,59 +37,6 @@ public class PlatformLayouts {
}; };
} }
public static MemoryLayout asVarArg(MemoryLayout ml) {
return switch (CABI.current()) {
case Win64 -> Win64.asVarArg(ml);
case MacOsAArch64 -> AArch64.asVarArg(ml);
default -> ml;
};
}
private static ValueLayout ofChar(ByteOrder order, long bitSize) {
return MemoryLayout.valueLayout(bitSize, order)
.withAttribute(CLinker.TypeKind.ATTR_NAME, CLinker.TypeKind.CHAR);
}
private static ValueLayout ofShort(ByteOrder order, long bitSize) {
return MemoryLayout.valueLayout(bitSize, order)
.withAttribute(CLinker.TypeKind.ATTR_NAME, CLinker.TypeKind.SHORT);
}
private static ValueLayout ofInt(ByteOrder order, long bitSize) {
return MemoryLayout.valueLayout(bitSize, order)
.withAttribute(CLinker.TypeKind.ATTR_NAME, CLinker.TypeKind.INT);
}
private static ValueLayout ofLong(ByteOrder order, long bitSize) {
return MemoryLayout.valueLayout(bitSize, order)
.withAttribute(CLinker.TypeKind.ATTR_NAME, CLinker.TypeKind.LONG);
}
private static ValueLayout ofLongLong(ByteOrder order, long bitSize) {
return MemoryLayout.valueLayout(bitSize, order)
.withAttribute(CLinker.TypeKind.ATTR_NAME, CLinker.TypeKind.LONG_LONG);
}
private static ValueLayout ofFloat(ByteOrder order, long bitSize) {
return MemoryLayout.valueLayout(bitSize, order)
.withAttribute(CLinker.TypeKind.ATTR_NAME, CLinker.TypeKind.FLOAT);
}
private static ValueLayout ofDouble(ByteOrder order, long bitSize) {
return MemoryLayout.valueLayout(bitSize, order)
.withAttribute(CLinker.TypeKind.ATTR_NAME, CLinker.TypeKind.DOUBLE);
}
private static ValueLayout ofPointer(ByteOrder order, long bitSize) {
return MemoryLayout.valueLayout(bitSize, order)
.withAttribute(CLinker.TypeKind.ATTR_NAME, CLinker.TypeKind.POINTER);
}
public static CLinker.TypeKind getKind(MemoryLayout layout) {
return (CLinker.TypeKind)layout.attribute(CLinker.TypeKind.ATTR_NAME).orElseThrow(
() -> new IllegalStateException("Unexpected value layout: could not determine ABI class"));
}
/** /**
* This class defines layout constants modelling standard primitive types supported by the x64 SystemV ABI. * This class defines layout constants modelling standard primitive types supported by the x64 SystemV ABI.
*/ */
@ -103,50 +45,55 @@ public class PlatformLayouts {
//just the one //just the one
} }
/**
* The {@code bool} native type.
*/
public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN;
/** /**
* The {@code char} native type. * The {@code char} native type.
*/ */
public static final ValueLayout C_CHAR = ofChar(LITTLE_ENDIAN, 8); public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE;
/** /**
* The {@code short} native type. * The {@code short} native type.
*/ */
public static final ValueLayout C_SHORT = ofShort(LITTLE_ENDIAN, 16); public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT.withBitAlignment(16);
/** /**
* The {@code int} native type. * The {@code int} native type.
*/ */
public static final ValueLayout C_INT = ofInt(LITTLE_ENDIAN, 32); public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT.withBitAlignment(32);
/** /**
* The {@code long} native type. * The {@code long} native type.
*/ */
public static final ValueLayout C_LONG = ofLong(LITTLE_ENDIAN, 64); public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64);
/** /**
* The {@code long long} native type. * The {@code long long} native type.
*/ */
public static final ValueLayout C_LONG_LONG = ofLongLong(LITTLE_ENDIAN, 64); public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64);
/** /**
* The {@code float} native type. * The {@code float} native type.
*/ */
public static final ValueLayout C_FLOAT = ofFloat(LITTLE_ENDIAN, 32); public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT.withBitAlignment(32);
/** /**
* The {@code double} native type. * The {@code double} native type.
*/ */
public static final ValueLayout C_DOUBLE = ofDouble(LITTLE_ENDIAN, 64); public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE.withBitAlignment(64);
/** /**
* The {@code T*} native type. * The {@code T*} native type.
*/ */
public static final ValueLayout C_POINTER = ofPointer(LITTLE_ENDIAN, 64); public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64);
/** /**
* The {@code va_list} native type, as it is passed to a function. * The {@code va_list} native type, as it is passed to a function.
*/ */
public static final MemoryLayout C_VA_LIST = SysV.C_POINTER; public static final ValueLayout.OfAddress C_VA_LIST = SysV.C_POINTER;
} }
/** /**
@ -159,64 +106,53 @@ public class PlatformLayouts {
} }
/** /**
* The name of the layout attribute (see {@link MemoryLayout#attributes()}) used to mark variadic parameters. The * The {@code bool} native type.
* attribute value must be a boolean.
*/ */
public static final String VARARGS_ATTRIBUTE_NAME = "abi/windows/varargs"; public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN;
/** /**
* The {@code char} native type. * The {@code char} native type.
*/ */
public static final ValueLayout C_CHAR = ofChar(LITTLE_ENDIAN, 8); public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE;
/** /**
* The {@code short} native type. * The {@code short} native type.
*/ */
public static final ValueLayout C_SHORT = ofShort(LITTLE_ENDIAN, 16); public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT.withBitAlignment(16);
/** /**
* The {@code int} native type. * The {@code int} native type.
*/ */
public static final ValueLayout C_INT = ofInt(LITTLE_ENDIAN, 32); public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT.withBitAlignment(32);
/** /**
* The {@code long} native type. * The {@code long} native type.
*/ */
public static final ValueLayout C_LONG = ofLong(LITTLE_ENDIAN, 32); public static final ValueLayout.OfInt C_LONG = ValueLayout.JAVA_INT.withBitAlignment(32);
/** /**
* The {@code long long} native type. * The {@code long long} native type.
*/ */
public static final ValueLayout C_LONG_LONG = ofLongLong(LITTLE_ENDIAN, 64); public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64);
/** /**
* The {@code float} native type. * The {@code float} native type.
*/ */
public static final ValueLayout C_FLOAT = ofFloat(LITTLE_ENDIAN, 32); public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT.withBitAlignment(32);
/** /**
* The {@code double} native type. * The {@code double} native type.
*/ */
public static final ValueLayout C_DOUBLE = ofDouble(LITTLE_ENDIAN, 64); public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE.withBitAlignment(64);
/** /**
* The {@code T*} native type. * The {@code T*} native type.
*/ */
public static final ValueLayout C_POINTER = ofPointer(LITTLE_ENDIAN, 64); public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64);
/** /**
* The {@code va_list} native type, as it is passed to a function. * The {@code va_list} native type, as it is passed to a function.
*/ */
public static final MemoryLayout C_VA_LIST = Win64.C_POINTER; public static final ValueLayout.OfAddress C_VA_LIST = Win64.C_POINTER;
/**
* Return a new memory layout which describes a variadic parameter to be passed to a function.
* @param layout the original parameter layout.
* @return a layout which is the same as {@code layout}, except for the extra attribute {@link #VARARGS_ATTRIBUTE_NAME},
* which is set to {@code true}.
*/
public static MemoryLayout asVarArg(MemoryLayout layout) {
return layout.withAttribute(VARARGS_ATTRIBUTE_NAME, true);
}
} }
/** /**
@ -228,69 +164,54 @@ public class PlatformLayouts {
//just the one //just the one
} }
/**
* The {@code bool} native type.
*/
public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN;
/** /**
* The {@code char} native type. * The {@code char} native type.
*/ */
public static final ValueLayout C_CHAR = ofChar(LITTLE_ENDIAN, 8); public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE;
/** /**
* The {@code short} native type. * The {@code short} native type.
*/ */
public static final ValueLayout C_SHORT = ofShort(LITTLE_ENDIAN, 16); public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT.withBitAlignment(16);
/** /**
* The {@code int} native type. * The {@code int} native type.
*/ */
public static final ValueLayout C_INT = ofInt(LITTLE_ENDIAN, 32); public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT.withBitAlignment(32);
/** /**
* The {@code long} native type. * The {@code long} native type.
*/ */
public static final ValueLayout C_LONG = ofLong(LITTLE_ENDIAN, 64); public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64);
/** /**
* The {@code long long} native type. * The {@code long long} native type.
*/ */
public static final ValueLayout C_LONG_LONG = ofLongLong(LITTLE_ENDIAN, 64); public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64);
/** /**
* The {@code float} native type. * The {@code float} native type.
*/ */
public static final ValueLayout C_FLOAT = ofFloat(LITTLE_ENDIAN, 32); public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT.withBitAlignment(32);
/** /**
* The {@code double} native type. * The {@code double} native type.
*/ */
public static final ValueLayout C_DOUBLE = ofDouble(LITTLE_ENDIAN, 64); public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE.withBitAlignment(64);
/** /**
* The {@code T*} native type. * The {@code T*} native type.
*/ */
public static final ValueLayout C_POINTER = ofPointer(LITTLE_ENDIAN, 64); public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64);
/** /**
* The {@code va_list} native type, as it is passed to a function. * The {@code va_list} native type, as it is passed to a function.
*/ */
public static final MemoryLayout C_VA_LIST = AArch64.C_POINTER; public static final ValueLayout.OfAddress C_VA_LIST = AArch64.C_POINTER;
/**
* The name of the layout attribute (see {@link MemoryLayout#attributes()})
* used to mark variadic parameters on systems such as macOS which pass these
* entirely on the stack. The attribute value must be a boolean.
*/
public final static String STACK_VARARGS_ATTRIBUTE_NAME = "abi/aarch64/stack_varargs";
/**
* Return a new memory layout which describes a variadic parameter to be
* passed to a function. This is only required on platforms such as macOS
* which pass variadic parameters entirely on the stack.
* @param layout the original parameter layout.
* @return a layout which is the same as {@code layout}, except for
* the extra attribute {@link #STACK_VARARGS_ATTRIBUTE_NAME}, which is set
* to {@code true}.
*/
public static MemoryLayout asVarArg(MemoryLayout layout) {
return layout.withAttribute(STACK_VARARGS_ATTRIBUTE_NAME, true);
}
} }
} }

View file

@ -30,7 +30,7 @@ import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.ResourceScope;
import jdk.incubator.foreign.SegmentAllocator; import jdk.incubator.foreign.SegmentAllocator;
import jdk.internal.misc.ScopedMemoryAccess; import jdk.internal.misc.ScopedMemoryAccess;
import jdk.internal.ref.CleanerFactory; import jdk.internal.vm.annotation.ForceInline;
import java.lang.ref.Cleaner; import java.lang.ref.Cleaner;
import java.lang.ref.Reference; import java.lang.ref.Reference;
@ -49,9 +49,12 @@ import java.util.Objects;
* shared scopes use a more sophisticated synchronization mechanism, which guarantees that no concurrent * shared scopes use a more sophisticated synchronization mechanism, which guarantees that no concurrent
* access is possible when a scope is being closed (see {@link jdk.internal.misc.ScopedMemoryAccess}). * access is possible when a scope is being closed (see {@link jdk.internal.misc.ScopedMemoryAccess}).
*/ */
public abstract non-sealed class ResourceScopeImpl implements ResourceScope, ScopedMemoryAccess.Scope, SegmentAllocator { public abstract non-sealed class ResourceScopeImpl implements ResourceScope, SegmentAllocator, ScopedMemoryAccess.Scope {
final ResourceList resourceList; final ResourceList resourceList;
final Cleaner.Cleanable cleanable;
static final int MAX_FORKS = Integer.MAX_VALUE;
@Override @Override
public void addCloseAction(Runnable runnable) { public void addCloseAction(Runnable runnable) {
@ -59,11 +62,6 @@ public abstract non-sealed class ResourceScopeImpl implements ResourceScope, Sco
addInternal(ResourceList.ResourceCleanup.ofRunnable(runnable)); addInternal(ResourceList.ResourceCleanup.ofRunnable(runnable));
} }
@Override
public boolean isImplicit() {
return false;
}
/** /**
* Add a cleanup action. If a failure occurred (because of a add vs. close race), call the cleanup action. * Add a cleanup action. If a failure occurred (because of a add vs. close race), call the cleanup action.
* This semantics is useful when allocating new memory segments, since we first do a malloc/mmap and _then_ * This semantics is useful when allocating new memory segments, since we first do a malloc/mmap and _then_
@ -91,72 +89,38 @@ public abstract non-sealed class ResourceScopeImpl implements ResourceScope, Sco
} }
} }
protected ResourceScopeImpl(Cleaner cleaner, ResourceList resourceList) { protected ResourceScopeImpl(ResourceList resourceList, Cleaner cleaner) {
this.resourceList = resourceList; this.resourceList = resourceList;
if (cleaner != null) { cleanable = (cleaner != null) ?
cleaner.register(this, resourceList); cleaner.register(this, resourceList) : null;
}
}
public static ResourceScopeImpl createImplicitScope() {
return new ImplicitScopeImpl(CleanerFactory.cleaner());
} }
public static ResourceScopeImpl createConfined(Thread thread, Cleaner cleaner) { public static ResourceScopeImpl createConfined(Thread thread, Cleaner cleaner) {
return new ConfinedScope(thread, cleaner); return new ConfinedScope(thread, cleaner);
} }
/**
* Creates a confined memory scope with given attachment and cleanup action. The returned scope
* is assumed to be confined on the current thread.
* @return a confined memory scope
*/
public static ResourceScopeImpl createConfined(Cleaner cleaner) {
return new ConfinedScope(Thread.currentThread(), cleaner);
}
/**
* Creates a shared memory scope with given attachment and cleanup action.
* @return a shared memory scope
*/
public static ResourceScopeImpl createShared(Cleaner cleaner) { public static ResourceScopeImpl createShared(Cleaner cleaner) {
return new SharedScope(cleaner); return new SharedScope(cleaner);
} }
private final void release0(HandleImpl handle) { @Override
try { public MemorySegment allocate(long bytesSize, long bytesAlignment) {
Objects.requireNonNull(handle); return MemorySegment.allocateNative(bytesSize, bytesAlignment, this);
if (handle.scope() != this) { }
throw new IllegalArgumentException("Cannot release an handle acquired from another scope");
} public abstract void release0();
handle.release();
} finally { public abstract void acquire0();
Reference.reachabilityFence(this);
@Override
public void keepAlive(ResourceScope target) {
Objects.requireNonNull(target);
if (target == this) {
throw new IllegalArgumentException("Invalid target scope.");
} }
} ResourceScopeImpl targetImpl = (ResourceScopeImpl)target;
targetImpl.acquire0();
@Override addCloseAction(targetImpl::release0);
public final void release(ResourceScope.Handle handle) {
release0((HandleImpl)handle);
}
@Override
public final void release(ScopedMemoryAccess.Scope.Handle handle) {
release0((HandleImpl)handle);
}
@Override
public abstract HandleImpl acquire();
/**
* Internal interface used to implement resource scope handles.
*/
public non-sealed interface HandleImpl extends ResourceScope.Handle, ScopedMemoryAccess.Scope.Handle {
@Override
ResourceScopeImpl scope();
void release();
} }
/** /**
@ -167,7 +131,11 @@ public abstract non-sealed class ResourceScopeImpl implements ResourceScope, Sco
public void close() { public void close() {
try { try {
justClose(); justClose();
resourceList.cleanup(); if (cleanable != null) {
cleanable.clean();
} else {
resourceList.cleanup();
}
} finally { } finally {
Reference.reachabilityFence(this); Reference.reachabilityFence(this);
} }
@ -215,32 +183,17 @@ public abstract non-sealed class ResourceScopeImpl implements ResourceScope, Sco
} }
/** /**
* Allocates a segment using this scope. Used by {@link SegmentAllocator#ofScope(ResourceScope)}. * The global, always alive, non-closeable, shared scope. Similar to a shared scope, but its {@link #close()} method throws unconditionally.
* Adding new resources to the global scope, does nothing: as the scope can never become not-alive, there is nothing to track.
* Acquiring and or releasing a resource scope similarly does nothing.
*/ */
@Override static class GlobalScopeImpl extends SharedScope {
public MemorySegment allocate(long bytesSize, long bytesAlignment) {
return MemorySegment.allocateNative(bytesSize, bytesAlignment, this);
}
/** final Object ref;
* A non-closeable, shared scope. Similar to a shared scope, but its {@link #close()} method throws unconditionally.
* In addition, non-closeable scopes feature a much simpler scheme for generating resource scope handles, where
* the scope itself also acts as a resource scope handle and is returned by {@link #acquire()}.
*/
static class ImplicitScopeImpl extends SharedScope implements HandleImpl {
public ImplicitScopeImpl(Cleaner cleaner) { public GlobalScopeImpl(Object ref) {
super(cleaner); super(null);
} this.ref = ref;
@Override
public HandleImpl acquire() {
return this;
}
@Override
public boolean isImplicit() {
return true;
} }
@Override @Override
@ -249,27 +202,28 @@ public abstract non-sealed class ResourceScopeImpl implements ResourceScope, Sco
} }
@Override @Override
public void release() { @ForceInline
public void release0() {
// do nothing // do nothing
} }
@Override @Override
public ResourceScopeImpl scope() { @ForceInline
return this; public void acquire0() {
// do nothing
} }
}
/**
* The global, always alive, non-closeable, shared scope. This is like a {@link ImplicitScopeImpl non-closeable scope},
* except that the operation which adds new resources to the global scope does nothing: as the scope can never
* become not-alive, there is nothing to track.
*/
public static final ResourceScopeImpl GLOBAL = new ImplicitScopeImpl( null) {
@Override @Override
void addInternal(ResourceList.ResourceCleanup resource) { void addInternal(ResourceList.ResourceCleanup resource) {
// do nothing // do nothing
} }
}; }
public static final ResourceScopeImpl GLOBAL = new GlobalScopeImpl(null);
public static ResourceScopeImpl heapScope(Object ref) {
return new GlobalScopeImpl(ref);
}
/** /**
* A list of all cleanup actions associated with a resource scope. Cleanup actions are modelled as instances * A list of all cleanup actions associated with a resource scope. Cleanup actions are modelled as instances
@ -296,7 +250,7 @@ public abstract non-sealed class ResourceScopeImpl implements ResourceScope, Sco
} }
} }
public static abstract class ResourceCleanup { public abstract static class ResourceCleanup {
ResourceCleanup next; ResourceCleanup next;
public abstract void cleanup(); public abstract void cleanup();

View file

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

View file

@ -25,14 +25,12 @@
package jdk.internal.foreign; package jdk.internal.foreign;
import jdk.incubator.foreign.ResourceScope;
import jdk.internal.misc.ScopedMemoryAccess; import jdk.internal.misc.ScopedMemoryAccess;
import jdk.internal.vm.annotation.ForceInline;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle; import java.lang.invoke.VarHandle;
import java.lang.ref.Cleaner; import java.lang.ref.Cleaner;
import java.lang.ref.Reference;
import java.util.concurrent.atomic.AtomicBoolean;
/** /**
* A shared scope, which can be shared across multiple threads. Closing a shared scope has to ensure that * A shared scope, which can be shared across multiple threads. Closing a shared scope has to ensure that
@ -50,7 +48,6 @@ class SharedScope extends ResourceScopeImpl {
private static final int ALIVE = 0; private static final int ALIVE = 0;
private static final int CLOSING = -1; private static final int CLOSING = -1;
private static final int CLOSED = -2; private static final int CLOSED = -2;
private static final int MAX_FORKS = Integer.MAX_VALUE;
private int state = ALIVE; private int state = ALIVE;
@ -65,7 +62,7 @@ class SharedScope extends ResourceScopeImpl {
} }
SharedScope(Cleaner cleaner) { SharedScope(Cleaner cleaner) {
super(cleaner, new SharedResourceList()); super(new SharedResourceList(), cleaner);
} }
@Override @Override
@ -81,7 +78,8 @@ class SharedScope extends ResourceScopeImpl {
} }
@Override @Override
public HandleImpl acquire() { @ForceInline
public void acquire0() {
int value; int value;
do { do {
value = (int) STATE.getVolatile(this); value = (int) STATE.getVolatile(this);
@ -90,10 +88,22 @@ class SharedScope extends ResourceScopeImpl {
throw new IllegalStateException("Already closed"); throw new IllegalStateException("Already closed");
} else if (value == MAX_FORKS) { } else if (value == MAX_FORKS) {
//overflow //overflow
throw new IllegalStateException("Segment acquire limit exceeded"); throw new IllegalStateException("Scope keep alive limit exceeded");
} }
} while (!STATE.compareAndSet(this, value, value + 1)); } while (!STATE.compareAndSet(this, value, value + 1));
return new SharedHandle(); }
@Override
@ForceInline
public void release0() {
int value;
do {
value = (int) STATE.getVolatile(this);
if (value <= ALIVE) {
//cannot get here - we can't close segment twice
throw new IllegalStateException("Already closed");
}
} while (!STATE.compareAndSet(this, value, value - 1));
} }
void justClose() { void justClose() {
@ -101,7 +111,7 @@ class SharedScope extends ResourceScopeImpl {
if (prevState < 0) { if (prevState < 0) {
throw new IllegalStateException("Already closed"); throw new IllegalStateException("Already closed");
} else if (prevState != ALIVE) { } else if (prevState != ALIVE) {
throw new IllegalStateException("Scope is acquired by " + prevState + " locks"); throw new IllegalStateException("Scope is kept alive by " + prevState + " scopes");
} }
boolean success = SCOPED_MEMORY_ACCESS.closeScope(this); boolean success = SCOPED_MEMORY_ACCESS.closeScope(this);
STATE.setVolatile(this, success ? CLOSED : ALIVE); STATE.setVolatile(this, success ? CLOSED : ALIVE);
@ -133,13 +143,13 @@ class SharedScope extends ResourceScopeImpl {
@Override @Override
void add(ResourceCleanup cleanup) { void add(ResourceCleanup cleanup) {
while (true) { while (true) {
ResourceCleanup prev = (ResourceCleanup) FST.getAcquire(this); ResourceCleanup prev = (ResourceCleanup) FST.getVolatile(this);
cleanup.next = prev; if (prev == ResourceCleanup.CLOSED_LIST) {
ResourceCleanup newSegment = (ResourceCleanup) FST.compareAndExchangeRelease(this, prev, cleanup);
if (newSegment == ResourceCleanup.CLOSED_LIST) {
// too late // too late
throw new IllegalStateException("Already closed"); throw new IllegalStateException("Already closed");
} else if (newSegment == prev) { }
cleanup.next = prev;
if (FST.compareAndSet(this, prev, cleanup)) {
return; //victory return; //victory
} }
// keep trying // keep trying
@ -155,9 +165,9 @@ class SharedScope extends ResourceScopeImpl {
//ok now we're really closing down //ok now we're really closing down
ResourceCleanup prev = null; ResourceCleanup prev = null;
while (true) { while (true) {
prev = (ResourceCleanup) FST.getAcquire(this); prev = (ResourceCleanup) FST.getVolatile(this);
// no need to check for DUMMY, since only one thread can get here! // no need to check for DUMMY, since only one thread can get here!
if (FST.weakCompareAndSetRelease(this, prev, ResourceCleanup.CLOSED_LIST)) { if (FST.compareAndSet(this, prev, ResourceCleanup.CLOSED_LIST)) {
break; break;
} }
} }
@ -167,30 +177,4 @@ class SharedScope extends ResourceScopeImpl {
} }
} }
} }
/**
* A shared resource scope handle; this implementation has to handle close vs. close races.
*/
class SharedHandle implements HandleImpl {
final AtomicBoolean released = new AtomicBoolean(false);
@Override
public ResourceScopeImpl scope() {
return SharedScope.this;
}
@Override
public void release() {
if (released.compareAndSet(false, true)) {
int value;
do {
value = (int) STATE.getVolatile(jdk.internal.foreign.SharedScope.this);
if (value <= ALIVE) {
//cannot get here - we can't close segment twice
throw new IllegalStateException("Already closed");
}
} while (!STATE.compareAndSet(jdk.internal.foreign.SharedScope.this, value, value - 1));
}
}
}
} }

View file

@ -25,8 +25,8 @@
package jdk.internal.foreign; package jdk.internal.foreign;
import jdk.incubator.foreign.MemoryAccess;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.NativeSymbol;
import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.ResourceScope;
import jdk.incubator.foreign.SymbolLookup; import jdk.incubator.foreign.SymbolLookup;
import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryAddress;
@ -39,13 +39,13 @@ import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.function.Function; import java.util.function.Function;
import static jdk.incubator.foreign.CLinker.C_POINTER; import static jdk.incubator.foreign.ValueLayout.ADDRESS;
public class SystemLookup implements SymbolLookup { public class SystemLookup implements SymbolLookup {
private SystemLookup() { } private SystemLookup() { }
final static SystemLookup INSTANCE = new SystemLookup(); static final SystemLookup INSTANCE = new SystemLookup();
/* /*
* On POSIX systems, dlsym will allow us to lookup symbol in library dependencies; the same trick doesn't work * On POSIX systems, dlsym will allow us to lookup symbol in library dependencies; the same trick doesn't work
@ -71,11 +71,11 @@ public class SystemLookup implements SymbolLookup {
SymbolLookup fallbackLibLookup = libLookup(libs -> libs.loadLibrary("WinFallbackLookup")); SymbolLookup fallbackLibLookup = libLookup(libs -> libs.loadLibrary("WinFallbackLookup"));
int numSymbols = WindowsFallbackSymbols.values().length; int numSymbols = WindowsFallbackSymbols.values().length;
MemorySegment funcs = fallbackLibLookup.lookup("funcs").orElseThrow() MemorySegment funcs = MemorySegment.ofAddress(fallbackLibLookup.lookup("funcs").orElseThrow().address(),
.asSegment(C_POINTER.byteSize() * numSymbols, ResourceScope.newImplicitScope()); ADDRESS.byteSize() * numSymbols, ResourceScope.globalScope());
SymbolLookup fallbackLookup = name -> Optional.ofNullable(WindowsFallbackSymbols.valueOfOrNull(name)) SymbolLookup fallbackLookup = name -> Optional.ofNullable(WindowsFallbackSymbols.valueOfOrNull(name))
.map(symbol -> MemoryAccess.getAddressAtIndex(funcs, symbol.ordinal())); .map(symbol -> NativeSymbol.ofAddress(symbol.name(), funcs.getAtIndex(ADDRESS, symbol.ordinal()), ResourceScope.globalScope()));
final SymbolLookup finalLookup = lookup; final SymbolLookup finalLookup = lookup;
lookup = name -> finalLookup.lookup(name).or(() -> fallbackLookup.lookup(name)); lookup = name -> finalLookup.lookup(name).or(() -> fallbackLookup.lookup(name));
@ -91,7 +91,8 @@ public class SystemLookup implements SymbolLookup {
try { try {
long addr = lib.lookup(name); long addr = lib.lookup(name);
return addr == 0 ? return addr == 0 ?
Optional.empty() : Optional.of(MemoryAddress.ofLong(addr)); Optional.empty() :
Optional.of(NativeSymbol.ofAddress(name, MemoryAddress.ofLong(addr), ResourceScope.globalScope()));
} catch (NoSuchMethodException e) { } catch (NoSuchMethodException e) {
return Optional.empty(); return Optional.empty();
} }
@ -99,7 +100,7 @@ public class SystemLookup implements SymbolLookup {
} }
@Override @Override
public Optional<MemoryAddress> lookup(String name) { public Optional<NativeSymbol> lookup(String name) {
return syslookup.lookup(name); return syslookup.lookup(name);
} }

View file

@ -27,17 +27,19 @@
package jdk.internal.foreign; package jdk.internal.foreign;
import jdk.incubator.foreign.*; import jdk.incubator.foreign.*;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.foreign.MemorySegmentProxy; import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.VM; import jdk.internal.vm.annotation.ForceInline;
import sun.invoke.util.Wrapper;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle; import java.lang.invoke.VarHandle;
import java.util.Optional; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier; import java.util.function.Supplier;
import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE;
import static sun.security.action.GetPropertyAction.*; import static sun.security.action.GetPropertyAction.*;
/** /**
@ -49,6 +51,10 @@ public final class Utils {
= Boolean.parseBoolean(privilegedGetProperty("jdk.internal.foreign.SHOULD_ADAPT_HANDLES", "true")); = Boolean.parseBoolean(privilegedGetProperty("jdk.internal.foreign.SHOULD_ADAPT_HANDLES", "true"));
private static final MethodHandle SEGMENT_FILTER; private static final MethodHandle SEGMENT_FILTER;
private static final MethodHandle BYTE_TO_BOOL;
private static final MethodHandle BOOL_TO_BYTE;
private static final MethodHandle ADDRESS_TO_LONG;
private static final MethodHandle LONG_TO_ADDRESS;
public static final MethodHandle MH_bitsToBytesOrThrowForOffset; public static final MethodHandle MH_bitsToBytesOrThrowForOffset;
public static final Supplier<RuntimeException> bitsToBytesThrowOffset public static final Supplier<RuntimeException> bitsToBytesThrowOffset
@ -59,6 +65,14 @@ public final class Utils {
MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandles.Lookup lookup = MethodHandles.lookup();
SEGMENT_FILTER = lookup.findStatic(Utils.class, "filterSegment", SEGMENT_FILTER = lookup.findStatic(Utils.class, "filterSegment",
MethodType.methodType(MemorySegmentProxy.class, MemorySegment.class)); MethodType.methodType(MemorySegmentProxy.class, MemorySegment.class));
BYTE_TO_BOOL = lookup.findStatic(Utils.class, "byteToBoolean",
MethodType.methodType(boolean.class, byte.class));
BOOL_TO_BYTE = lookup.findStatic(Utils.class, "booleanToByte",
MethodType.methodType(byte.class, boolean.class));
ADDRESS_TO_LONG = lookup.findVirtual(MemoryAddress.class, "toRawLongValue",
MethodType.methodType(long.class));
LONG_TO_ADDRESS = lookup.findStatic(MemoryAddress.class, "ofLong",
MethodType.methodType(MemoryAddress.class, long.class));
MH_bitsToBytesOrThrowForOffset = MethodHandles.insertArguments( MH_bitsToBytesOrThrowForOffset = MethodHandles.insertArguments(
lookup.findStatic(Utils.class, "bitsToBytesOrThrow", lookup.findStatic(Utils.class, "bitsToBytesOrThrow",
MethodType.methodType(long.class, long.class, Supplier.class)), MethodType.methodType(long.class, long.class, Supplier.class)),
@ -91,38 +105,72 @@ public final class Utils {
} }
} }
public static VarHandle fixUpVarHandle(VarHandle handle) { public static VarHandle makeMemoryAccessVarHandle(ValueLayout layout, boolean skipAlignmentCheck) {
class VarHandleCache {
private static final Map<ValueLayout, VarHandle> handleMap = new ConcurrentHashMap<>();
private static final Map<ValueLayout, VarHandle> handleMapNoAlignCheck = new ConcurrentHashMap<>();
static VarHandle put(ValueLayout layout, VarHandle handle, boolean skipAlignmentCheck) {
VarHandle prev = (skipAlignmentCheck ? handleMapNoAlignCheck : handleMap).putIfAbsent(layout, handle);
return prev != null ? prev : handle;
}
}
Class<?> baseCarrier = layout.carrier();
if (layout.carrier() == MemoryAddress.class) {
baseCarrier = switch ((int) ValueLayout.ADDRESS.byteSize()) {
case 8 -> long.class;
case 4 -> int.class;
default -> throw new UnsupportedOperationException("Unsupported address layout");
};
} else if (layout.carrier() == boolean.class) {
baseCarrier = byte.class;
}
VarHandle handle = SharedSecrets.getJavaLangInvokeAccess().memoryAccessVarHandle(baseCarrier, skipAlignmentCheck,
layout.byteAlignment() - 1, layout.order());
// This adaptation is required, otherwise the memory access var handle will have type MemorySegmentProxy, // This adaptation is required, otherwise the memory access var handle will have type MemorySegmentProxy,
// and not MemorySegment (which the user expects), which causes performance issues with asType() adaptations. // and not MemorySegment (which the user expects), which causes performance issues with asType() adaptations.
return SHOULD_ADAPT_HANDLES handle = SHOULD_ADAPT_HANDLES
? MemoryHandles.filterCoordinates(handle, 0, SEGMENT_FILTER) ? MemoryHandles.filterCoordinates(handle, 0, SEGMENT_FILTER)
: handle; : handle;
if (layout.carrier() == boolean.class) {
handle = MemoryHandles.filterValue(handle, BOOL_TO_BYTE, BYTE_TO_BOOL);
} else if (layout.carrier() == MemoryAddress.class) {
handle = MemoryHandles.filterValue(handle,
MethodHandles.explicitCastArguments(ADDRESS_TO_LONG, MethodType.methodType(baseCarrier, MemoryAddress.class)),
MethodHandles.explicitCastArguments(LONG_TO_ADDRESS, MethodType.methodType(MemoryAddress.class, baseCarrier)));
}
return VarHandleCache.put(layout, handle, skipAlignmentCheck);
} }
private static MemorySegmentProxy filterSegment(MemorySegment segment) { private static MemorySegmentProxy filterSegment(MemorySegment segment) {
return (AbstractMemorySegmentImpl)segment; return (AbstractMemorySegmentImpl)segment;
} }
public static void checkPrimitiveCarrierCompat(Class<?> carrier, MemoryLayout layout) { private static boolean byteToBoolean(byte b) {
checkLayoutType(layout, ValueLayout.class); return b != 0;
if (!isValidPrimitiveCarrier(carrier))
throw new IllegalArgumentException("Unsupported carrier: " + carrier);
if (Wrapper.forPrimitiveType(carrier).bitWidth() != layout.bitSize())
throw new IllegalArgumentException("Carrier size mismatch: " + carrier + " != " + layout);
} }
public static boolean isValidPrimitiveCarrier(Class<?> carrier) { private static byte booleanToByte(boolean b) {
return carrier == byte.class return b ? (byte)1 : (byte)0;
|| carrier == short.class
|| carrier == char.class
|| carrier == int.class
|| carrier == long.class
|| carrier == float.class
|| carrier == double.class;
} }
public static void checkLayoutType(MemoryLayout layout, Class<? extends MemoryLayout> layoutType) { public static void copy(MemorySegment addr, byte[] bytes) {
if (!layoutType.isInstance(layout)) var heapSegment = MemorySegment.ofArray(bytes);
throw new IllegalArgumentException("Expected a " + layoutType.getSimpleName() + ": " + layout); addr.copyFrom(heapSegment);
addr.set(JAVA_BYTE, bytes.length, (byte)0);
}
public static MemorySegment toCString(byte[] bytes, SegmentAllocator allocator) {
MemorySegment addr = allocator.allocate(bytes.length + 1, 1L);
copy(addr, bytes);
return addr;
}
@ForceInline
public static long scaleOffset(MemorySegment segment, long index, long size) {
// note: we know size is a small value (as it comes from ValueLayout::byteSize())
return MemorySegmentProxy.multiplyOffsets(index, (int)size, (AbstractMemorySegmentImpl)segment);
} }
} }

View file

@ -24,17 +24,20 @@
*/ */
package jdk.internal.foreign.abi; package jdk.internal.foreign.abi;
import jdk.incubator.foreign.Addressable;
import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryHandles; import jdk.incubator.foreign.MemoryHandles;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.ResourceScope;
import jdk.incubator.foreign.SegmentAllocator; import jdk.incubator.foreign.SegmentAllocator;
import jdk.incubator.foreign.ValueLayout;
import jdk.internal.foreign.MemoryAddressImpl; import jdk.internal.foreign.MemoryAddressImpl;
import jdk.internal.foreign.ResourceScopeImpl; import jdk.internal.foreign.ResourceScopeImpl;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Deque; import java.util.Deque;
import java.util.List; import java.util.List;
@ -204,7 +207,6 @@ import static java.lang.invoke.MethodType.methodType;
public abstract class Binding { public abstract class Binding {
private static final MethodHandle MH_UNBOX_ADDRESS; private static final MethodHandle MH_UNBOX_ADDRESS;
private static final MethodHandle MH_BOX_ADDRESS; private static final MethodHandle MH_BOX_ADDRESS;
private static final MethodHandle MH_BASE_ADDRESS;
private static final MethodHandle MH_COPY_BUFFER; private static final MethodHandle MH_COPY_BUFFER;
private static final MethodHandle MH_ALLOCATE_BUFFER; private static final MethodHandle MH_ALLOCATE_BUFFER;
private static final MethodHandle MH_TO_SEGMENT; private static final MethodHandle MH_TO_SEGMENT;
@ -216,8 +218,6 @@ public abstract class Binding {
methodType(long.class)); methodType(long.class));
MH_BOX_ADDRESS = lookup.findStatic(MemoryAddress.class, "ofLong", MH_BOX_ADDRESS = lookup.findStatic(MemoryAddress.class, "ofLong",
methodType(MemoryAddress.class, long.class)); methodType(MemoryAddress.class, long.class));
MH_BASE_ADDRESS = lookup.findVirtual(MemorySegment.class, "address",
methodType(MemoryAddress.class));
MH_COPY_BUFFER = lookup.findStatic(Binding.Copy.class, "copyBuffer", MH_COPY_BUFFER = lookup.findStatic(Binding.Copy.class, "copyBuffer",
methodType(MemorySegment.class, MemorySegment.class, long.class, long.class, Context.class)); methodType(MemorySegment.class, MemorySegment.class, long.class, long.class, Context.class));
MH_ALLOCATE_BUFFER = lookup.findStatic(Binding.Allocate.class, "allocateBuffer", MH_ALLOCATE_BUFFER = lookup.findStatic(Binding.Allocate.class, "allocateBuffer",
@ -262,7 +262,7 @@ public abstract class Binding {
*/ */
public static Context ofBoundedAllocator(long size) { public static Context ofBoundedAllocator(long size) {
ResourceScope scope = ResourceScope.newConfinedScope(); ResourceScope scope = ResourceScope.newConfinedScope();
return new Context(SegmentAllocator.arenaAllocator(size, scope), scope); return new Context(SegmentAllocator.newNativeArena(size, scope), scope);
} }
/** /**
@ -321,7 +321,6 @@ public abstract class Binding {
ALLOC_BUFFER, ALLOC_BUFFER,
BOX_ADDRESS, BOX_ADDRESS,
UNBOX_ADDRESS, UNBOX_ADDRESS,
BASE_ADDRESS,
TO_SEGMENT, TO_SEGMENT,
DUP DUP
} }
@ -344,7 +343,7 @@ public abstract class Binding {
public abstract MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos); public abstract MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos);
private static void checkType(Class<?> type) { private static void checkType(Class<?> type) {
if (!type.isPrimitive() || type == void.class || type == boolean.class) if (!type.isPrimitive() || type == void.class)
throw new IllegalArgumentException("Illegal type: " + type); throw new IllegalArgumentException("Illegal type: " + type);
} }
@ -388,11 +387,11 @@ public abstract class Binding {
} }
public static UnboxAddress unboxAddress() { public static UnboxAddress unboxAddress() {
return UnboxAddress.INSTANCE; return UnboxAddress.INSTANCE.get(MemoryAddress.class);
} }
public static BaseAddress baseAddress() { public static UnboxAddress unboxAddress(Class<?> carrier) {
return BaseAddress.INSTANCE; return UnboxAddress.INSTANCE.get(carrier);
} }
public static ToSegment toSegment(MemoryLayout layout) { public static ToSegment toSegment(MemoryLayout layout) {
@ -467,8 +466,8 @@ public abstract class Binding {
return this; return this;
} }
public Binding.Builder baseAddress() { public Binding.Builder unboxAddress(Class<?> carrier) {
bindings.add(Binding.baseAddress()); bindings.add(Binding.unboxAddress(carrier));
return this; return this;
} }
@ -487,7 +486,7 @@ public abstract class Binding {
} }
} }
static abstract class Move extends Binding { abstract static class Move extends Binding {
private final VMStorage storage; private final VMStorage storage;
private final Class<?> type; private final Class<?> type;
@ -593,7 +592,7 @@ public abstract class Binding {
} }
} }
private static abstract class Dereference extends Binding { private abstract static class Dereference extends Binding {
private final long offset; private final long offset;
private final Class<?> type; private final Class<?> type;
@ -630,7 +629,8 @@ public abstract class Binding {
// alignment is set to 1 byte here to avoid exceptions for cases where we do super word // alignment is set to 1 byte here to avoid exceptions for cases where we do super word
// copies of e.g. 2 int fields of a struct as a single long, while the struct is only // copies of e.g. 2 int fields of a struct as a single long, while the struct is only
// 4-byte-aligned (since it only contains ints) // 4-byte-aligned (since it only contains ints)
return MemoryHandles.insertCoordinates(MemoryHandles.varHandle(type, 1, ByteOrder.nativeOrder()), 1, offset); ValueLayout layout = MemoryLayout.valueLayout(type(), ByteOrder.nativeOrder()).withBitAlignment(8);
return MemoryHandles.insertCoordinates(MemoryHandles.varHandle(layout), 1, offset);
} }
} }
@ -740,9 +740,8 @@ public abstract class Binding {
private static MemorySegment copyBuffer(MemorySegment operand, long size, long alignment, private static MemorySegment copyBuffer(MemorySegment operand, long size, long alignment,
Context context) { Context context) {
MemorySegment copy = context.allocator().allocate(size, alignment); return context.allocator().allocate(size, alignment)
copy.copyFrom(operand.asSlice(0, size)); .copyFrom(operand.asSlice(0, size));
return copy;
} }
public long size() { public long size() {
@ -875,27 +874,44 @@ public abstract class Binding {
* and pushes that onto the operand stack. * and pushes that onto the operand stack.
*/ */
public static class UnboxAddress extends Binding { public static class UnboxAddress extends Binding {
private static final UnboxAddress INSTANCE = new UnboxAddress();
private UnboxAddress() { static final ClassValue<UnboxAddress> INSTANCE = new ClassValue<>() {
@Override
protected UnboxAddress computeValue(Class<?> type) {
return new UnboxAddress(type);
}
};
final Class<?> carrier;
final MethodHandle toAddress;
private UnboxAddress(Class<?> carrier) {
super(Tag.UNBOX_ADDRESS); super(Tag.UNBOX_ADDRESS);
this.carrier = carrier;
try {
this.toAddress = MethodHandles.lookup().findVirtual(carrier, "address", MethodType.methodType(MemoryAddress.class));
} catch (Throwable ex) {
throw new IllegalArgumentException(ex);
}
} }
@Override @Override
public void verify(Deque<Class<?>> stack) { public void verify(Deque<Class<?>> stack) {
Class<?> actualType = stack.pop(); Class<?> actualType = stack.pop();
SharedUtils.checkType(actualType, MemoryAddress.class); SharedUtils.checkType(actualType, carrier);
stack.push(long.class); stack.push(long.class);
} }
@Override @Override
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc, public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
BindingInterpreter.LoadFunc loadFunc, Context context) { BindingInterpreter.LoadFunc loadFunc, Context context) {
stack.push(((MemoryAddress)stack.pop()).toRawLongValue()); stack.push(((Addressable)stack.pop()).address().toRawLongValue());
} }
@Override @Override
public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) { public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
return filterArguments(specializedHandle, insertPos, MH_UNBOX_ADDRESS); return filterArguments(specializedHandle, insertPos,
MethodHandles.filterReturnValue(toAddress, MH_UNBOX_ADDRESS));
} }
@Override @Override
@ -939,41 +955,6 @@ public abstract class Binding {
} }
} }
/**
* BASE_ADDRESS()
* Pops a MemorySegment from the operand stack, and takes the base address of the segment
* (the MemoryAddress that points to the start), and pushes that onto the operand stack
*/
public static class BaseAddress extends Binding {
private static final BaseAddress INSTANCE = new BaseAddress();
private BaseAddress() {
super(Tag.BASE_ADDRESS);
}
@Override
public void verify(Deque<Class<?>> stack) {
Class<?> actualType = stack.pop();
SharedUtils.checkType(actualType, MemorySegment.class);
stack.push(MemoryAddress.class);
}
@Override
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
BindingInterpreter.LoadFunc loadFunc, Context context) {
stack.push(((MemorySegment) stack.pop()).address());
}
@Override
public MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos) {
return filterArguments(specializedHandle, insertPos, MH_BASE_ADDRESS);
}
@Override
public String toString() {
return "BaseAddress{}";
}
}
/** /**
* TO_SEGMENT([size]) * TO_SEGMENT([size])
* Pops a MemoryAddress from the operand stack, and converts it to a MemorySegment * Pops a MemoryAddress from the operand stack, and converts it to a MemorySegment

View file

@ -24,8 +24,8 @@
*/ */
package jdk.internal.foreign.abi; package jdk.internal.foreign.abi;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.ValueLayout;
import jdk.internal.foreign.MemoryAddressImpl; import jdk.internal.foreign.MemoryAddressImpl;
import java.io.PrintStream; import java.io.PrintStream;
@ -34,7 +34,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
class BufferLayout { class BufferLayout {
static final VarHandle VH_LONG = MemoryLayouts.JAVA_LONG.varHandle(long.class); static final VarHandle VH_LONG = ValueLayout.JAVA_LONG.varHandle();
final long size; final long size;
final long arguments_next_pc; final long arguments_next_pc;

View file

@ -28,7 +28,6 @@ import jdk.incubator.foreign.FunctionDescriptor;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import java.util.List; import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream; import java.util.stream.Stream;
public class CallingSequence { public class CallingSequence {

View file

@ -100,7 +100,6 @@ public class CallingSequenceBuilder {
//ALLOC_BUFFER, //ALLOC_BUFFER,
//BOX_ADDRESS, //BOX_ADDRESS,
UNBOX_ADDRESS, UNBOX_ADDRESS,
BASE_ADDRESS,
//TO_SEGMENT, //TO_SEGMENT,
DUP DUP
); );
@ -129,7 +128,6 @@ public class CallingSequenceBuilder {
ALLOC_BUFFER, ALLOC_BUFFER,
BOX_ADDRESS, BOX_ADDRESS,
//UNBOX_ADDRESS, //UNBOX_ADDRESS,
//BASE_ADDRESS,
TO_SEGMENT, TO_SEGMENT,
DUP DUP
); );

View file

@ -25,11 +25,11 @@
package jdk.internal.foreign.abi; package jdk.internal.foreign.abi;
import jdk.incubator.foreign.Addressable; import jdk.incubator.foreign.Addressable;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.NativeSymbol;
import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.ResourceScope;
import jdk.incubator.foreign.SegmentAllocator; import jdk.incubator.foreign.SegmentAllocator;
import jdk.incubator.foreign.ValueLayout;
import jdk.internal.access.JavaLangInvokeAccess; import jdk.internal.access.JavaLangInvokeAccess;
import jdk.internal.access.SharedSecrets; import jdk.internal.access.SharedSecrets;
import jdk.internal.invoke.NativeEntryPoint; import jdk.internal.invoke.NativeEntryPoint;
@ -40,7 +40,6 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle; import java.lang.invoke.VarHandle;
import java.lang.ref.Reference;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -70,7 +69,7 @@ public class ProgrammableInvoker {
private static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess(); private static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess();
private static final VarHandle VH_LONG = MemoryLayouts.JAVA_LONG.varHandle(long.class); private static final VarHandle VH_LONG = ValueLayout.JAVA_LONG.varHandle();
private static final MethodHandle MH_INVOKE_MOVES; private static final MethodHandle MH_INVOKE_MOVES;
private static final MethodHandle MH_INVOKE_INTERP_BINDINGS; private static final MethodHandle MH_INVOKE_INTERP_BINDINGS;
@ -87,10 +86,10 @@ public class ProgrammableInvoker {
MH_INVOKE_MOVES = lookup.findVirtual(ProgrammableInvoker.class, "invokeMoves", MH_INVOKE_MOVES = lookup.findVirtual(ProgrammableInvoker.class, "invokeMoves",
methodType(Object.class, long.class, Object[].class, Binding.VMStore[].class, Binding.VMLoad[].class)); methodType(Object.class, long.class, Object[].class, Binding.VMStore[].class, Binding.VMLoad[].class));
MH_INVOKE_INTERP_BINDINGS = lookup.findVirtual(ProgrammableInvoker.class, "invokeInterpBindings", MH_INVOKE_INTERP_BINDINGS = lookup.findVirtual(ProgrammableInvoker.class, "invokeInterpBindings",
methodType(Object.class, Addressable.class, SegmentAllocator.class, Object[].class, MethodHandle.class, Map.class, Map.class)); methodType(Object.class, NativeSymbol.class, SegmentAllocator.class, Object[].class, MethodHandle.class, Map.class, Map.class));
MH_WRAP_ALLOCATOR = lookup.findStatic(Binding.Context.class, "ofAllocator", MH_WRAP_ALLOCATOR = lookup.findStatic(Binding.Context.class, "ofAllocator",
methodType(Binding.Context.class, SegmentAllocator.class)); methodType(Binding.Context.class, SegmentAllocator.class));
MH_ADDR_TO_LONG = lookup.findStatic(ProgrammableInvoker.class, "unboxTargetAddress", methodType(long.class, Addressable.class)); MH_ADDR_TO_LONG = lookup.findStatic(ProgrammableInvoker.class, "unboxTargetAddress", methodType(long.class, NativeSymbol.class));
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -172,9 +171,9 @@ public class ProgrammableInvoker {
return handle; return handle;
} }
private static long unboxTargetAddress(Addressable addr) { private static long unboxTargetAddress(NativeSymbol addr) {
MemoryAddress ma = SharedUtils.checkSymbol(addr); SharedUtils.checkSymbol(addr);
return ma.toRawLongValue(); return addr.address().toRawLongValue();
} }
// Funnel from type to Object[] // Funnel from type to Object[]
@ -312,7 +311,7 @@ public class ProgrammableInvoker {
} }
} }
Object invokeInterpBindings(Addressable address, SegmentAllocator allocator, Object[] args, MethodHandle leaf, Object invokeInterpBindings(NativeSymbol symbol, SegmentAllocator allocator, Object[] args, MethodHandle leaf,
Map<VMStorage, Integer> argIndexMap, Map<VMStorage, Integer> argIndexMap,
Map<VMStorage, Integer> retIndexMap) throws Throwable { Map<VMStorage, Integer> retIndexMap) throws Throwable {
Binding.Context unboxContext = bufferCopySize != 0 Binding.Context unboxContext = bufferCopySize != 0
@ -321,21 +320,17 @@ public class ProgrammableInvoker {
try (unboxContext) { try (unboxContext) {
// do argument processing, get Object[] as result // do argument processing, get Object[] as result
Object[] leafArgs = new Object[leaf.type().parameterCount()]; Object[] leafArgs = new Object[leaf.type().parameterCount()];
leafArgs[0] = address; // addr leafArgs[0] = symbol; // symbol
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
Object arg = args[i]; Object arg = args[i];
BindingInterpreter.unbox(arg, callingSequence.argumentBindings(i), BindingInterpreter.unbox(arg, callingSequence.argumentBindings(i),
(storage, type, value) -> { (storage, type, value) -> {
leafArgs[argIndexMap.get(storage) + 1] = value; // +1 to skip addr leafArgs[argIndexMap.get(storage) + 1] = value; // +1 to skip symbol
}, unboxContext); }, unboxContext);
} }
// call leaf // call leaf
Object o = leaf.invokeWithArguments(leafArgs); Object o = leaf.invokeWithArguments(leafArgs);
// make sure arguments are reachable during the call
// technically we only need to do all Addressable parameters here
Reference.reachabilityFence(address);
Reference.reachabilityFence(args);
// return value processing // return value processing
if (o == null) { if (o == null) {

View file

@ -26,10 +26,10 @@
package jdk.internal.foreign.abi; package jdk.internal.foreign.abi;
import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.NativeSymbol;
import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.ResourceScope;
import jdk.incubator.foreign.SegmentAllocator; import jdk.incubator.foreign.ValueLayout;
import jdk.internal.access.JavaLangInvokeAccess; import jdk.internal.access.JavaLangInvokeAccess;
import jdk.internal.access.SharedSecrets; import jdk.internal.access.SharedSecrets;
import jdk.internal.foreign.MemoryAddressImpl; import jdk.internal.foreign.MemoryAddressImpl;
@ -46,6 +46,7 @@ import java.util.Objects;
import java.util.stream.Stream; import java.util.stream.Stream;
import static java.lang.invoke.MethodHandles.dropArguments; import static java.lang.invoke.MethodHandles.dropArguments;
import static java.lang.invoke.MethodHandles.exactInvoker;
import static java.lang.invoke.MethodHandles.filterReturnValue; import static java.lang.invoke.MethodHandles.filterReturnValue;
import static java.lang.invoke.MethodHandles.identity; import static java.lang.invoke.MethodHandles.identity;
import static java.lang.invoke.MethodHandles.insertArguments; import static java.lang.invoke.MethodHandles.insertArguments;
@ -69,7 +70,7 @@ public class ProgrammableUpcallHandler {
private static final JavaLangInvokeAccess JLI = SharedSecrets.getJavaLangInvokeAccess(); private static final JavaLangInvokeAccess JLI = SharedSecrets.getJavaLangInvokeAccess();
private static final VarHandle VH_LONG = MemoryLayouts.JAVA_LONG.varHandle(long.class); private static final VarHandle VH_LONG = ValueLayout.JAVA_LONG.varHandle();
private static final MethodHandle MH_invokeMoves; private static final MethodHandle MH_invokeMoves;
private static final MethodHandle MH_invokeInterpBindings; private static final MethodHandle MH_invokeInterpBindings;
@ -88,7 +89,7 @@ public class ProgrammableUpcallHandler {
} }
} }
public static UpcallHandler make(ABIDescriptor abi, MethodHandle target, CallingSequence callingSequence) { public static NativeSymbol make(ABIDescriptor abi, MethodHandle target, CallingSequence callingSequence, ResourceScope scope) {
Binding.VMLoad[] argMoves = argMoveBindings(callingSequence); Binding.VMLoad[] argMoves = argMoveBindings(callingSequence);
Binding.VMStore[] retMoves = retMoveBindings(callingSequence); Binding.VMStore[] retMoves = retMoveBindings(callingSequence);
@ -134,7 +135,7 @@ public class ProgrammableUpcallHandler {
MethodHandle invokeMoves = insertArguments(MH_invokeMoves, 1, doBindingsErased, argMoves, retMoves, abi, layout); MethodHandle invokeMoves = insertArguments(MH_invokeMoves, 1, doBindingsErased, argMoves, retMoves, abi, layout);
entryPoint = allocateUpcallStub(invokeMoves, abi, layout); entryPoint = allocateUpcallStub(invokeMoves, abi, layout);
} }
return () -> entryPoint; return UpcallStubs.makeUpcall(entryPoint, scope);
} }
private static void checkPrimitive(MethodType type) { private static void checkPrimitive(MethodType type) {

View file

@ -25,40 +25,45 @@
package jdk.internal.foreign.abi; package jdk.internal.foreign.abi;
import jdk.incubator.foreign.Addressable; import jdk.incubator.foreign.Addressable;
import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.FunctionDescriptor; import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.GroupLayout; import jdk.incubator.foreign.GroupLayout;
import jdk.incubator.foreign.MemoryAccess;
import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryHandles;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.NativeSymbol;
import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.ResourceScope;
import jdk.incubator.foreign.SegmentAllocator; import jdk.incubator.foreign.SegmentAllocator;
import jdk.incubator.foreign.SequenceLayout; import jdk.incubator.foreign.SequenceLayout;
import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.VaList;
import jdk.incubator.foreign.ValueLayout; import jdk.incubator.foreign.ValueLayout;
import jdk.internal.access.JavaLangAccess; import jdk.internal.access.JavaLangAccess;
import jdk.internal.access.JavaLangInvokeAccess;
import jdk.internal.access.SharedSecrets; import jdk.internal.access.SharedSecrets;
import jdk.internal.foreign.Scoped;
import jdk.internal.foreign.CABI; import jdk.internal.foreign.CABI;
import jdk.internal.foreign.MemoryAddressImpl; import jdk.internal.foreign.MemoryAddressImpl;
import jdk.internal.foreign.ResourceScopeImpl;
import jdk.internal.foreign.Utils; import jdk.internal.foreign.Utils;
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker; import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker;
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker; import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker;
import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker; import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker; import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
import jdk.internal.vm.annotation.ForceInline;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle; import java.lang.invoke.VarHandle;
import java.lang.ref.Reference; import java.lang.ref.Reference;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
@ -67,17 +72,26 @@ import static java.lang.invoke.MethodHandles.constant;
import static java.lang.invoke.MethodHandles.dropArguments; import static java.lang.invoke.MethodHandles.dropArguments;
import static java.lang.invoke.MethodHandles.dropReturn; import static java.lang.invoke.MethodHandles.dropReturn;
import static java.lang.invoke.MethodHandles.empty; import static java.lang.invoke.MethodHandles.empty;
import static java.lang.invoke.MethodHandles.filterArguments; import static java.lang.invoke.MethodHandles.foldArguments;
import static java.lang.invoke.MethodHandles.identity; import static java.lang.invoke.MethodHandles.identity;
import static java.lang.invoke.MethodHandles.insertArguments; import static java.lang.invoke.MethodHandles.insertArguments;
import static java.lang.invoke.MethodHandles.permuteArguments; import static java.lang.invoke.MethodHandles.permuteArguments;
import static java.lang.invoke.MethodHandles.tryFinally; import static java.lang.invoke.MethodHandles.tryFinally;
import static java.lang.invoke.MethodType.methodType; import static java.lang.invoke.MethodType.methodType;
import static jdk.incubator.foreign.CLinker.*; import static jdk.incubator.foreign.ValueLayout.ADDRESS;
import static jdk.incubator.foreign.ValueLayout.JAVA_BOOLEAN;
import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE;
import static jdk.incubator.foreign.ValueLayout.JAVA_CHAR;
import static jdk.incubator.foreign.ValueLayout.JAVA_DOUBLE;
import static jdk.incubator.foreign.ValueLayout.JAVA_FLOAT;
import static jdk.incubator.foreign.ValueLayout.JAVA_INT;
import static jdk.incubator.foreign.ValueLayout.JAVA_LONG;
import static jdk.incubator.foreign.ValueLayout.JAVA_SHORT;
public class SharedUtils { public class SharedUtils {
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
private static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess();
private static final MethodHandle MH_ALLOC_BUFFER; private static final MethodHandle MH_ALLOC_BUFFER;
private static final MethodHandle MH_BASEADDRESS; private static final MethodHandle MH_BASEADDRESS;
@ -87,6 +101,8 @@ public class SharedUtils {
private static final MethodHandle MH_CLOSE_CONTEXT; private static final MethodHandle MH_CLOSE_CONTEXT;
private static final MethodHandle MH_REACHBILITY_FENCE; private static final MethodHandle MH_REACHBILITY_FENCE;
private static final MethodHandle MH_HANDLE_UNCAUGHT_EXCEPTION; private static final MethodHandle MH_HANDLE_UNCAUGHT_EXCEPTION;
private static final MethodHandle ACQUIRE_MH;
private static final MethodHandle RELEASE_MH;
static { static {
try { try {
@ -107,13 +123,19 @@ public class SharedUtils {
methodType(void.class, Object.class)); methodType(void.class, Object.class));
MH_HANDLE_UNCAUGHT_EXCEPTION = lookup.findStatic(SharedUtils.class, "handleUncaughtException", MH_HANDLE_UNCAUGHT_EXCEPTION = lookup.findStatic(SharedUtils.class, "handleUncaughtException",
methodType(void.class, Throwable.class)); methodType(void.class, Throwable.class));
ACQUIRE_MH = MethodHandles.lookup().findStatic(SharedUtils.class, "acquire",
MethodType.methodType(void.class, Scoped[].class));
RELEASE_MH = MethodHandles.lookup().findStatic(SharedUtils.class, "release",
MethodType.methodType(void.class, Scoped[].class));
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
throw new BootstrapMethodError(e); throw new BootstrapMethodError(e);
} }
} }
// this allocator should be used when no allocation is expected // this allocator should be used when no allocation is expected
public static final SegmentAllocator THROWING_ALLOCATOR = (size, align) -> { throw new IllegalStateException("Cannot get here"); }; public static final SegmentAllocator THROWING_ALLOCATOR = (size, align) -> {
throw new IllegalStateException("Cannot get here");
};
/** /**
* Align the specified type from a given address * Align the specified type from a given address
@ -225,34 +247,6 @@ public class SharedUtils {
return dest; return dest;
} }
public static void checkCompatibleType(Class<?> carrier, MemoryLayout layout, long addressSize) {
if (carrier.isPrimitive()) {
Utils.checkPrimitiveCarrierCompat(carrier, layout);
} else if (carrier == MemoryAddress.class) {
Utils.checkLayoutType(layout, ValueLayout.class);
if (layout.bitSize() != addressSize)
throw new IllegalArgumentException("Address size mismatch: " + addressSize + " != " + layout.bitSize());
} else if (carrier == MemorySegment.class) {
Utils.checkLayoutType(layout, GroupLayout.class);
} else {
throw new IllegalArgumentException("Unsupported carrier: " + carrier);
}
}
public static void checkFunctionTypes(MethodType mt, FunctionDescriptor cDesc, long addressSize) {
if (mt.returnType() == void.class != cDesc.returnLayout().isEmpty())
throw new IllegalArgumentException("Return type mismatch: " + mt + " != " + cDesc);
List<MemoryLayout> argLayouts = cDesc.argumentLayouts();
if (mt.parameterCount() != argLayouts.size())
throw new IllegalArgumentException("Arity mismatch: " + mt + " != " + cDesc);
int paramCount = mt.parameterCount();
for (int i = 0; i < paramCount; i++) {
checkCompatibleType(mt.parameterType(i), argLayouts.get(i), addressSize);
}
cDesc.returnLayout().ifPresent(rl -> checkCompatibleType(mt.returnType(), rl, addressSize));
}
public static Class<?> primitiveCarrierForSize(long size, boolean useFloat) { public static Class<?> primitiveCarrierForSize(long size, boolean useFloat) {
if (useFloat) { if (useFloat) {
if (size == 4) { if (size == 4) {
@ -287,15 +281,14 @@ public class SharedUtils {
public static String toJavaStringInternal(MemorySegment segment, long start) { public static String toJavaStringInternal(MemorySegment segment, long start) {
int len = strlen(segment, start); int len = strlen(segment, start);
byte[] bytes = new byte[len]; byte[] bytes = new byte[len];
MemorySegment.ofArray(bytes) MemorySegment.copy(segment, JAVA_BYTE, start, bytes, 0, len);
.copyFrom(segment.asSlice(start, len));
return new String(bytes, StandardCharsets.UTF_8); return new String(bytes, StandardCharsets.UTF_8);
} }
private static int strlen(MemorySegment segment, long start) { private static int strlen(MemorySegment segment, long start) {
// iterate until overflow (String can only hold a byte[], whose length can be expressed as an int) // iterate until overflow (String can only hold a byte[], whose length can be expressed as an int)
for (int offset = 0; offset >= 0; offset++) { for (int offset = 0; offset >= 0; offset++) {
byte curr = MemoryAccess.getByteAtOffset(segment, start + offset); byte curr = segment.get(JAVA_BYTE, start + offset);
if (curr == 0) { if (curr == 0) {
return offset; return offset;
} }
@ -392,31 +385,21 @@ public class SharedUtils {
insertPos = 1; insertPos = 1;
} else { } else {
closer = identity(specializedHandle.type().returnType()); // (V) -> V closer = identity(specializedHandle.type().returnType()); // (V) -> V
closer = dropArguments(closer, 0, Throwable.class); // (Throwable, V) -> V if (!upcall) {
closer = dropArguments(closer, 0, Throwable.class); // (Throwable, V) -> V
} else {
closer = collectArguments(closer, 0, MH_HANDLE_UNCAUGHT_EXCEPTION); // (Throwable, V) -> V
}
insertPos = 2; insertPos = 2;
} }
// downcalls get the leading Addressable/SegmentAllocator param as well // downcalls get the leading NativeSymbol/SegmentAllocator param as well
if (!upcall) { if (!upcall) {
closer = collectArguments(closer, insertPos++, reachabilityFenceHandle(Addressable.class)); closer = collectArguments(closer, insertPos++, reachabilityFenceHandle(NativeSymbol.class));
closer = dropArguments(closer, insertPos++, SegmentAllocator.class); // (Throwable, V?, Addressable, SegmentAllocator) -> V/void closer = dropArguments(closer, insertPos++, SegmentAllocator.class); // (Throwable, V?, NativeSymbol, SegmentAllocator) -> V/void
} }
closer = collectArguments(closer, insertPos++, MH_CLOSE_CONTEXT); // (Throwable, V?, Addressable?, BindingContext) -> V/void closer = collectArguments(closer, insertPos++, MH_CLOSE_CONTEXT); // (Throwable, V?, NativeSymbol?, BindingContext) -> V/void
if (!upcall) {
// now for each Addressable parameter, add a reachability fence
MethodType specType = specializedHandle.type();
// skip 3 for address, segment allocator, and binding context
for (int i = 3; i < specType.parameterCount(); i++) {
Class<?> param = specType.parameterType(i);
if (Addressable.class.isAssignableFrom(param)) {
closer = collectArguments(closer, insertPos++, reachabilityFenceHandle(param));
} else {
closer = dropArguments(closer, insertPos++, param);
}
}
}
MethodHandle contextFactory; MethodHandle contextFactory;
@ -434,34 +417,171 @@ public class SharedUtils {
return specializedHandle; return specializedHandle;
} }
@ForceInline
@SuppressWarnings("fallthrough")
public static void acquire(Scoped[] args) {
ResourceScope scope4 = null;
ResourceScope scope3 = null;
ResourceScope scope2 = null;
ResourceScope scope1 = null;
ResourceScope scope0 = null;
switch (args.length) {
default:
// slow path, acquire all remaining addressable parameters in isolation
for (int i = 5 ; i < args.length ; i++) {
acquire(args[i].scope());
}
// fast path, acquire only scopes not seen in other parameters
case 5:
scope4 = args[4].scope();
acquire(scope4);
case 4:
scope3 = args[3].scope();
if (scope3 != scope4)
acquire(scope3);
case 3:
scope2 = args[2].scope();
if (scope2 != scope3 && scope2 != scope4)
acquire(scope2);
case 2:
scope1 = args[1].scope();
if (scope1 != scope2 && scope1 != scope3 && scope1 != scope4)
acquire(scope1);
case 1:
scope0 = args[0].scope();
if (scope0 != scope1 && scope0 != scope2 && scope0 != scope3 && scope0 != scope4)
acquire(scope0);
case 0: break;
}
}
@ForceInline
@SuppressWarnings("fallthrough")
public static void release(Scoped[] args) {
ResourceScope scope4 = null;
ResourceScope scope3 = null;
ResourceScope scope2 = null;
ResourceScope scope1 = null;
ResourceScope scope0 = null;
switch (args.length) {
default:
// slow path, release all remaining addressable parameters in isolation
for (int i = 5 ; i < args.length ; i++) {
release(args[i].scope());
}
// fast path, release only scopes not seen in other parameters
case 5:
scope4 = args[4].scope();
release(scope4);
case 4:
scope3 = args[3].scope();
if (scope3 != scope4)
release(scope3);
case 3:
scope2 = args[2].scope();
if (scope2 != scope3 && scope2 != scope4)
release(scope2);
case 2:
scope1 = args[1].scope();
if (scope1 != scope2 && scope1 != scope3 && scope1 != scope4)
release(scope1);
case 1:
scope0 = args[0].scope();
if (scope0 != scope1 && scope0 != scope2 && scope0 != scope3 && scope0 != scope4)
release(scope0);
case 0: break;
}
}
@ForceInline
private static void acquire(ResourceScope scope) {
((ResourceScopeImpl)scope).acquire0();
}
@ForceInline
private static void release(ResourceScope scope) {
((ResourceScopeImpl)scope).release0();
}
/*
* This method adds a try/finally block to a downcall method handle, to make sure that all by-reference
* parameters (including the target address of the native function) are kept alive for the duration of
* the downcall.
*/
public static MethodHandle wrapDowncall(MethodHandle downcallHandle, FunctionDescriptor descriptor) {
boolean hasReturn = descriptor.returnLayout().isPresent();
MethodHandle tryBlock = downcallHandle;
MethodHandle cleanup = hasReturn ?
MethodHandles.identity(downcallHandle.type().returnType()) :
MethodHandles.empty(MethodType.methodType(void.class));
int addressableCount = 0;
List<UnaryOperator<MethodHandle>> adapters = new ArrayList<>();
for (int i = 0 ; i < downcallHandle.type().parameterCount() ; i++) {
Class<?> ptype = downcallHandle.type().parameterType(i);
if (ptype == Addressable.class || ptype == NativeSymbol.class) {
addressableCount++;
} else {
int pos = i;
adapters.add(mh -> dropArguments(mh, pos, ptype));
}
}
if (addressableCount > 0) {
cleanup = dropArguments(cleanup, 0, Throwable.class);
MethodType adapterType = MethodType.methodType(void.class);
for (int i = 0 ; i < addressableCount ; i++) {
adapterType = adapterType.appendParameterTypes(i == 0 ? NativeSymbol.class : Addressable.class);
}
MethodHandle acquireHandle = ACQUIRE_MH.asCollector(Scoped[].class, addressableCount).asType(adapterType);
MethodHandle releaseHandle = RELEASE_MH.asCollector(Scoped[].class, addressableCount).asType(adapterType);
for (UnaryOperator<MethodHandle> adapter : adapters) {
acquireHandle = adapter.apply(acquireHandle);
releaseHandle = adapter.apply(releaseHandle);
}
tryBlock = foldArguments(tryBlock, acquireHandle);
cleanup = collectArguments(cleanup, hasReturn ? 2 : 1, releaseHandle);
return tryFinally(tryBlock, cleanup);
} else {
return downcallHandle;
}
}
public static void checkExceptions(MethodHandle target) {
Class<?>[] exceptions = JLIA.exceptionTypes(target);
if (exceptions != null && exceptions.length != 0) {
throw new IllegalArgumentException("Target handle may throw exceptions: " + Arrays.toString(exceptions));
}
}
// lazy init MH_ALLOC and MH_FREE handles // lazy init MH_ALLOC and MH_FREE handles
private static class AllocHolder { private static class AllocHolder {
private static final CLinker SYS_LINKER = getSystemLinker(); private static final CLinker SYS_LINKER = getSystemLinker();
static final MethodHandle MH_MALLOC = SYS_LINKER.downcallHandle(CLinker.systemLookup().lookup("malloc").get(), static final MethodHandle MH_MALLOC = SYS_LINKER.downcallHandle(CLinker.systemCLinker().lookup("malloc").get(),
MethodType.methodType(MemoryAddress.class, long.class), FunctionDescriptor.of(ADDRESS, JAVA_LONG));
FunctionDescriptor.of(C_POINTER, C_LONG_LONG));
static final MethodHandle MH_FREE = SYS_LINKER.downcallHandle(CLinker.systemLookup().lookup("free").get(), static final MethodHandle MH_FREE = SYS_LINKER.downcallHandle(CLinker.systemCLinker().lookup("free").get(),
MethodType.methodType(void.class, MemoryAddress.class), FunctionDescriptor.ofVoid(ADDRESS));
FunctionDescriptor.ofVoid(C_POINTER));
} }
public static MemoryAddress checkSymbol(Addressable symbol) { public static void checkSymbol(NativeSymbol symbol) {
return checkAddressable(symbol, "Symbol is NULL"); checkAddressable(symbol, "Symbol is NULL");
} }
public static MemoryAddress checkAddress(MemoryAddress address) { public static void checkAddress(MemoryAddress address) {
return checkAddressable(address, "Address is NULL"); checkAddressable(address, "Address is NULL");
} }
private static MemoryAddress checkAddressable(Addressable symbol, String msg) { private static void checkAddressable(Addressable symbol, String msg) {
Objects.requireNonNull(symbol); Objects.requireNonNull(symbol);
MemoryAddress symbolAddr = symbol.address(); if (symbol.address().toRawLongValue() == 0)
if (symbolAddr.equals(MemoryAddress.NULL)) throw new IllegalArgumentException("Symbol is NULL: " + symbol);
throw new IllegalArgumentException("Symbol is NULL: " + symbolAddr);
return symbolAddr;
} }
public static MemoryAddress allocateMemoryInternal(long size) { public static MemoryAddress allocateMemoryInternal(long size) {
@ -474,7 +594,7 @@ public class SharedUtils {
public static void freeMemoryInternal(MemoryAddress addr) { public static void freeMemoryInternal(MemoryAddress addr) {
try { try {
AllocHolder.MH_FREE.invokeExact(addr); AllocHolder.MH_FREE.invokeExact((Addressable)addr);
} catch (Throwable th) { } catch (Throwable th) {
throw new RuntimeException(th); throw new RuntimeException(th);
} }
@ -489,12 +609,6 @@ public class SharedUtils {
}; };
} }
public static VarHandle vhPrimitiveOrAddress(Class<?> carrier, MemoryLayout layout) {
return carrier == MemoryAddress.class
? MemoryHandles.asAddressVarHandle(layout.varHandle(primitiveCarrierForSize(layout.byteSize(), false)))
: layout.varHandle(carrier);
}
public static VaList newVaListOfAddress(MemoryAddress ma, ResourceScope scope) { public static VaList newVaListOfAddress(MemoryAddress ma, ResourceScope scope) {
return switch (CABI.current()) { return switch (CABI.current()) {
case Win64 -> Windowsx64Linker.newVaListOfAddress(ma, scope); case Win64 -> Windowsx64Linker.newVaListOfAddress(ma, scope);
@ -513,34 +627,6 @@ public class SharedUtils {
}; };
} }
public static MethodType convertVaListCarriers(MethodType mt, Class<?> carrier) {
Class<?>[] params = new Class<?>[mt.parameterCount()];
for (int i = 0; i < params.length; i++) {
Class<?> pType = mt.parameterType(i);
params[i] = ((pType == VaList.class) ? carrier : pType);
}
return methodType(mt.returnType(), params);
}
public static MethodHandle unboxVaLists(MethodType type, MethodHandle handle, MethodHandle unboxer) {
for (int i = 0; i < type.parameterCount(); i++) {
if (type.parameterType(i) == VaList.class) {
handle = filterArguments(handle, i + 1, unboxer); // +1 for leading address
}
}
return handle;
}
public static MethodHandle boxVaLists(MethodHandle handle, MethodHandle boxer) {
MethodType type = handle.type();
for (int i = 0; i < type.parameterCount(); i++) {
if (type.parameterType(i) == VaList.class) {
handle = filterArguments(handle, i, boxer);
}
}
return handle;
}
static void checkType(Class<?> actualType, Class<?> expectedType) { static void checkType(Class<?> actualType, Class<?> expectedType) {
if (expectedType != actualType) { if (expectedType != actualType) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
@ -549,9 +635,12 @@ public class SharedUtils {
} }
public static boolean isTrivial(FunctionDescriptor cDesc) { public static boolean isTrivial(FunctionDescriptor cDesc) {
return cDesc.attribute(FunctionDescriptor.TRIVIAL_ATTRIBUTE_NAME) return false; // FIXME: use system property?
.map(Boolean.class::cast) }
.orElse(false);
public static boolean isVarargsIndex(FunctionDescriptor descriptor, int argIndex) {
int firstPos = descriptor.firstVariadicArgumentIndex();
return firstPos != -1 && argIndex >= firstPos;
} }
public static class SimpleVaArg { public static class SimpleVaArg {
@ -566,13 +655,11 @@ public class SharedUtils {
} }
public VarHandle varHandle() { public VarHandle varHandle() {
return carrier == MemoryAddress.class return layout.varHandle();
? MemoryHandles.asAddressVarHandle(layout.varHandle(primitiveCarrierForSize(layout.byteSize(), false)))
: layout.varHandle(carrier);
} }
} }
public static non-sealed class EmptyVaList implements VaList { public static non-sealed class EmptyVaList implements VaList, Scoped {
private final MemoryAddress address; private final MemoryAddress address;
@ -585,32 +672,27 @@ public class SharedUtils {
} }
@Override @Override
public int vargAsInt(MemoryLayout layout) { public int nextVarg(ValueLayout.OfInt layout) {
throw uoe(); throw uoe();
} }
@Override @Override
public long vargAsLong(MemoryLayout layout) { public long nextVarg(ValueLayout.OfLong layout) {
throw uoe(); throw uoe();
} }
@Override @Override
public double vargAsDouble(MemoryLayout layout) { public double nextVarg(ValueLayout.OfDouble layout) {
throw uoe(); throw uoe();
} }
@Override @Override
public MemoryAddress vargAsAddress(MemoryLayout layout) { public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
throw uoe(); throw uoe();
} }
@Override @Override
public MemorySegment vargAsSegment(MemoryLayout layout, SegmentAllocator allocator) { public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
throw uoe();
}
@Override
public MemorySegment vargAsSegment(MemoryLayout layout, ResourceScope scope) {
throw uoe(); throw uoe();
} }
@ -638,19 +720,21 @@ public class SharedUtils {
static void writeOverSized(MemorySegment ptr, Class<?> type, Object o) { static void writeOverSized(MemorySegment ptr, Class<?> type, Object o) {
// use VH_LONG for integers to zero out the whole register in the process // use VH_LONG for integers to zero out the whole register in the process
if (type == long.class) { if (type == long.class) {
MemoryAccess.setLong(ptr, (long) o); ptr.set(JAVA_LONG, 0, (long) o);
} else if (type == int.class) { } else if (type == int.class) {
MemoryAccess.setLong(ptr, (int) o); ptr.set(JAVA_LONG, 0, (int) o);
} else if (type == short.class) { } else if (type == short.class) {
MemoryAccess.setLong(ptr, (short) o); ptr.set(JAVA_LONG, 0, (short) o);
} else if (type == char.class) { } else if (type == char.class) {
MemoryAccess.setLong(ptr, (char) o); ptr.set(JAVA_LONG, 0, (char) o);
} else if (type == byte.class) { } else if (type == byte.class) {
MemoryAccess.setLong(ptr, (byte) o); ptr.set(JAVA_LONG, 0, (byte) o);
} else if (type == float.class) { } else if (type == float.class) {
MemoryAccess.setFloat(ptr, (float) o); ptr.set(JAVA_FLOAT, 0, (float) o);
} else if (type == double.class) { } else if (type == double.class) {
MemoryAccess.setDouble(ptr, (double) o); ptr.set(JAVA_DOUBLE, 0, (double) o);
} else if (type == boolean.class) {
ptr.set(JAVA_BOOLEAN, 0, (boolean) o);
} else { } else {
throw new IllegalArgumentException("Unsupported carrier: " + type); throw new IllegalArgumentException("Unsupported carrier: " + type);
} }
@ -658,19 +742,21 @@ public class SharedUtils {
static void write(MemorySegment ptr, Class<?> type, Object o) { static void write(MemorySegment ptr, Class<?> type, Object o) {
if (type == long.class) { if (type == long.class) {
MemoryAccess.setLong(ptr, (long) o); ptr.set(JAVA_LONG, 0, (long) o);
} else if (type == int.class) { } else if (type == int.class) {
MemoryAccess.setInt(ptr, (int) o); ptr.set(JAVA_INT, 0, (int) o);
} else if (type == short.class) { } else if (type == short.class) {
MemoryAccess.setShort(ptr, (short) o); ptr.set(JAVA_SHORT, 0, (short) o);
} else if (type == char.class) { } else if (type == char.class) {
MemoryAccess.setChar(ptr, (char) o); ptr.set(JAVA_CHAR, 0, (char) o);
} else if (type == byte.class) { } else if (type == byte.class) {
MemoryAccess.setByte(ptr, (byte) o); ptr.set(JAVA_BYTE, 0, (byte) o);
} else if (type == float.class) { } else if (type == float.class) {
MemoryAccess.setFloat(ptr, (float) o); ptr.set(JAVA_FLOAT, 0, (float) o);
} else if (type == double.class) { } else if (type == double.class) {
MemoryAccess.setDouble(ptr, (double) o); ptr.set(JAVA_DOUBLE, 0, (double) o);
} else if (type == boolean.class) {
ptr.set(JAVA_BOOLEAN, 0, (boolean) o);
} else { } else {
throw new IllegalArgumentException("Unsupported carrier: " + type); throw new IllegalArgumentException("Unsupported carrier: " + type);
} }
@ -678,21 +764,43 @@ public class SharedUtils {
static Object read(MemorySegment ptr, Class<?> type) { static Object read(MemorySegment ptr, Class<?> type) {
if (type == long.class) { if (type == long.class) {
return MemoryAccess.getLong(ptr); return ptr.get(JAVA_LONG, 0);
} else if (type == int.class) { } else if (type == int.class) {
return MemoryAccess.getInt(ptr); return ptr.get(JAVA_INT, 0);
} else if (type == short.class) { } else if (type == short.class) {
return MemoryAccess.getShort(ptr); return ptr.get(JAVA_SHORT, 0);
} else if (type == char.class) { } else if (type == char.class) {
return MemoryAccess.getChar(ptr); return ptr.get(JAVA_CHAR, 0);
} else if (type == byte.class) { } else if (type == byte.class) {
return MemoryAccess.getByte(ptr); return ptr.get(JAVA_BYTE, 0);
} else if (type == float.class) { } else if (type == float.class) {
return MemoryAccess.getFloat(ptr); return ptr.get(JAVA_FLOAT, 0);
} else if (type == double.class) { } else if (type == double.class) {
return MemoryAccess.getDouble(ptr); return ptr.get(JAVA_DOUBLE, 0);
} else if (type == boolean.class) {
return ptr.get(JAVA_BOOLEAN, 0);
} else { } else {
throw new IllegalArgumentException("Unsupported carrier: " + type); throw new IllegalArgumentException("Unsupported carrier: " + type);
} }
} }
public static MethodType inferMethodType(FunctionDescriptor descriptor, boolean upcall) {
MethodType type = MethodType.methodType(descriptor.returnLayout().isPresent() ?
carrierFor(descriptor.returnLayout().get(), upcall) : void.class);
for (MemoryLayout argLayout : descriptor.argumentLayouts()) {
type = type.appendParameterTypes(carrierFor(argLayout, !upcall));
}
return type;
}
static Class<?> carrierFor(MemoryLayout layout, boolean forArg) {
if (layout instanceof ValueLayout valueLayout) {
return (forArg && valueLayout.carrier().equals(MemoryAddress.class)) ?
Addressable.class : valueLayout.carrier();
} else if (layout instanceof GroupLayout) {
return MemorySegment.class;
} else {
throw new IllegalArgumentException("Unsupported layout: " + layout);
}
}
} }

View file

@ -25,18 +25,13 @@
package jdk.internal.foreign.abi; package jdk.internal.foreign.abi;
import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.NativeSymbol;
import jdk.incubator.foreign.ResourceScope;
import jdk.internal.foreign.NativeSymbolImpl;
import jdk.internal.foreign.ResourceScopeImpl; import jdk.internal.foreign.ResourceScopeImpl;
import jdk.internal.foreign.NativeMemorySegmentImpl;
public class UpcallStubs { public class UpcallStubs {
public static MemoryAddress upcallAddress(UpcallHandler handler, ResourceScopeImpl scope) {
long stubAddress = handler.entryPoint();
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(MemoryAddress.ofLong(stubAddress), 0,
() -> freeUpcallStub(stubAddress), scope).address();
}
private static void freeUpcallStub(long stubAddress) { private static void freeUpcallStub(long stubAddress) {
if (!freeUpcallStub0(stubAddress)) { if (!freeUpcallStub0(stubAddress)) {
throw new IllegalStateException("Not a stub address: " + stubAddress); throw new IllegalStateException("Not a stub address: " + stubAddress);
@ -52,4 +47,14 @@ public class UpcallStubs {
static { static {
registerNatives(); registerNatives();
} }
static NativeSymbol makeUpcall(long entry, ResourceScope scope) {
((ResourceScopeImpl)scope).addOrCleanupIfFail(new ResourceScopeImpl.ResourceList.ResourceCleanup() {
@Override
public void cleanup() {
freeUpcallStub(entry);
}
});
return new NativeSymbolImpl("upcall:" + Long.toHexString(entry), MemoryAddress.ofLong(entry), scope);
}
} }

View file

@ -30,9 +30,10 @@ import jdk.incubator.foreign.GroupLayout;
import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.NativeSymbol;
import jdk.incubator.foreign.ResourceScope;
import jdk.internal.foreign.Utils; import jdk.internal.foreign.Utils;
import jdk.internal.foreign.abi.CallingSequenceBuilder; import jdk.internal.foreign.abi.CallingSequenceBuilder;
import jdk.internal.foreign.abi.UpcallHandler;
import jdk.internal.foreign.abi.ABIDescriptor; import jdk.internal.foreign.abi.ABIDescriptor;
import jdk.internal.foreign.abi.Binding; import jdk.internal.foreign.abi.Binding;
import jdk.internal.foreign.abi.CallingSequence; import jdk.internal.foreign.abi.CallingSequence;
@ -40,6 +41,8 @@ import jdk.internal.foreign.abi.ProgrammableInvoker;
import jdk.internal.foreign.abi.ProgrammableUpcallHandler; import jdk.internal.foreign.abi.ProgrammableUpcallHandler;
import jdk.internal.foreign.abi.VMStorage; import jdk.internal.foreign.abi.VMStorage;
import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64CallArranger;
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64CallArranger;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
@ -54,8 +57,12 @@ import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.*;
* to translate a C FunctionDescriptor into a CallingSequence2, which can then be turned into a MethodHandle. * to translate a C FunctionDescriptor into a CallingSequence2, which can then be turned into a MethodHandle.
* *
* This includes taking care of synthetic arguments like pointers to return buffers for 'in-memory' returns. * This includes taking care of synthetic arguments like pointers to return buffers for 'in-memory' returns.
*
* There are minor differences between the ABIs implemented on Linux, macOS, and Windows
* which are handled in sub-classes. Clients should access these through the provided
* public constants CallArranger.LINUX and CallArranger.MACOS.
*/ */
public class CallArranger { public abstract class CallArranger {
private static final int STACK_SLOT_SIZE = 8; private static final int STACK_SLOT_SIZE = 8;
public static final int MAX_REGISTER_ARGUMENTS = 8; public static final int MAX_REGISTER_ARGUMENTS = 8;
@ -95,9 +102,20 @@ public class CallArranger {
} }
} }
public static Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall) { public static final CallArranger LINUX = new LinuxAArch64CallArranger();
SharedUtils.checkFunctionTypes(mt, cDesc, AArch64.C_POINTER.bitSize()); public static final CallArranger MACOS = new MacOsAArch64CallArranger();
/**
* Are variadic arguments assigned to registers as in the standard calling
* convention, or always passed on the stack?
*
* @returns true if variadic arguments should be spilled to the stack.
*/
protected abstract boolean varArgsOnStack();
protected CallArranger() {}
public Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall) {
CallingSequenceBuilder csb = new CallingSequenceBuilder(forUpcall); CallingSequenceBuilder csb = new CallingSequenceBuilder(forUpcall);
BindingCalculator argCalc = forUpcall ? new BoxBindingCalculator(true) : new UnboxBindingCalculator(true); BindingCalculator argCalc = forUpcall ? new BoxBindingCalculator(true) : new UnboxBindingCalculator(true);
@ -116,6 +134,9 @@ public class CallArranger {
for (int i = 0; i < mt.parameterCount(); i++) { for (int i = 0; i < mt.parameterCount(); i++) {
Class<?> carrier = mt.parameterType(i); Class<?> carrier = mt.parameterType(i);
MemoryLayout layout = cDesc.argumentLayouts().get(i); MemoryLayout layout = cDesc.argumentLayouts().get(i);
if (varArgsOnStack() && SharedUtils.isVarargsIndex(cDesc, i)) {
argCalc.storageCalculator.adjustForVarArgs();
}
csb.addArgumentBindings(carrier, layout, argCalc.getBindings(carrier, layout)); csb.addArgumentBindings(carrier, layout, argCalc.getBindings(carrier, layout));
} }
@ -124,7 +145,7 @@ public class CallArranger {
return new Bindings(csb.build(), returnInMemory); return new Bindings(csb.build(), returnInMemory);
} }
public static MethodHandle arrangeDowncall(MethodType mt, FunctionDescriptor cDesc) { public MethodHandle arrangeDowncall(MethodType mt, FunctionDescriptor cDesc) {
Bindings bindings = getBindings(mt, cDesc, false); Bindings bindings = getBindings(mt, cDesc, false);
MethodHandle handle = new ProgrammableInvoker(C, bindings.callingSequence).getBoundMethodHandle(); MethodHandle handle = new ProgrammableInvoker(C, bindings.callingSequence).getBoundMethodHandle();
@ -136,14 +157,14 @@ public class CallArranger {
return handle; return handle;
} }
public static UpcallHandler arrangeUpcall(MethodHandle target, MethodType mt, FunctionDescriptor cDesc) { public NativeSymbol arrangeUpcall(MethodHandle target, MethodType mt, FunctionDescriptor cDesc, ResourceScope scope) {
Bindings bindings = getBindings(mt, cDesc, true); Bindings bindings = getBindings(mt, cDesc, true);
if (bindings.isInMemoryReturn) { if (bindings.isInMemoryReturn) {
target = SharedUtils.adaptUpcallForIMR(target, true /* drop return, since we don't have bindings for it */); target = SharedUtils.adaptUpcallForIMR(target, true /* drop return, since we don't have bindings for it */);
} }
return ProgrammableUpcallHandler.make(C, target, bindings.callingSequence); return ProgrammableUpcallHandler.make(C, target, bindings.callingSequence,scope);
} }
private static boolean isInMemoryReturn(Optional<MemoryLayout> returnLayout) { private static boolean isInMemoryReturn(Optional<MemoryLayout> returnLayout) {
@ -208,18 +229,15 @@ public class CallArranger {
return storage[0]; return storage[0];
} }
void adjustForVarArgs(MemoryLayout layout) { void adjustForVarArgs() {
if (layout.attribute(AArch64.STACK_VARARGS_ATTRIBUTE_NAME) // This system passes all variadic parameters on the stack. Ensure
.map(Boolean.class::cast).orElse(false)) { // no further arguments are allocated to registers.
// This system passes all variadic parameters on the stack. Ensure nRegs[StorageClasses.INTEGER] = MAX_REGISTER_ARGUMENTS;
// no further arguments are allocated to registers. nRegs[StorageClasses.VECTOR] = MAX_REGISTER_ARGUMENTS;
nRegs[StorageClasses.INTEGER] = MAX_REGISTER_ARGUMENTS;
nRegs[StorageClasses.VECTOR] = MAX_REGISTER_ARGUMENTS;
}
} }
} }
static abstract class BindingCalculator { abstract static class BindingCalculator {
protected final StorageCalculator storageCalculator; protected final StorageCalculator storageCalculator;
protected BindingCalculator(boolean forArguments) { protected BindingCalculator(boolean forArguments) {
@ -288,7 +306,6 @@ public class CallArranger {
List<Binding> getBindings(Class<?> carrier, MemoryLayout layout) { List<Binding> getBindings(Class<?> carrier, MemoryLayout layout) {
TypeClass argumentClass = TypeClass.classifyLayout(layout); TypeClass argumentClass = TypeClass.classifyLayout(layout);
Binding.Builder bindings = Binding.builder(); Binding.Builder bindings = Binding.builder();
storageCalculator.adjustForVarArgs(layout);
switch (argumentClass) { switch (argumentClass) {
case STRUCT_REGISTER: { case STRUCT_REGISTER: {
assert carrier == MemorySegment.class; assert carrier == MemorySegment.class;
@ -317,8 +334,7 @@ public class CallArranger {
case STRUCT_REFERENCE: { case STRUCT_REFERENCE: {
assert carrier == MemorySegment.class; assert carrier == MemorySegment.class;
bindings.copy(layout) bindings.copy(layout)
.baseAddress() .unboxAddress(MemorySegment.class);
.unboxAddress();
VMStorage storage = storageCalculator.nextStorage( VMStorage storage = storageCalculator.nextStorage(
StorageClasses.INTEGER, AArch64.C_POINTER); StorageClasses.INTEGER, AArch64.C_POINTER);
bindings.vmStore(storage, long.class); bindings.vmStore(storage, long.class);
@ -349,7 +365,7 @@ public class CallArranger {
break; break;
} }
case POINTER: { case POINTER: {
bindings.unboxAddress(); bindings.unboxAddress(carrier);
VMStorage storage = VMStorage storage =
storageCalculator.nextStorage(StorageClasses.INTEGER, layout); storageCalculator.nextStorage(StorageClasses.INTEGER, layout);
bindings.vmStore(storage, long.class); bindings.vmStore(storage, long.class);
@ -391,7 +407,6 @@ public class CallArranger {
List<Binding> getBindings(Class<?> carrier, MemoryLayout layout) { List<Binding> getBindings(Class<?> carrier, MemoryLayout layout) {
TypeClass argumentClass = TypeClass.classifyLayout(layout); TypeClass argumentClass = TypeClass.classifyLayout(layout);
Binding.Builder bindings = Binding.builder(); Binding.Builder bindings = Binding.builder();
assert !layout.attribute(AArch64.STACK_VARARGS_ATTRIBUTE_NAME).isPresent() : "no variadic upcalls";
switch (argumentClass) { switch (argumentClass) {
case STRUCT_REGISTER: { case STRUCT_REGISTER: {
assert carrier == MemorySegment.class; assert carrier == MemorySegment.class;

View file

@ -26,10 +26,10 @@
package jdk.internal.foreign.abi.aarch64; package jdk.internal.foreign.abi.aarch64;
import jdk.incubator.foreign.GroupLayout; import jdk.incubator.foreign.GroupLayout;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.SequenceLayout; import jdk.incubator.foreign.SequenceLayout;
import jdk.incubator.foreign.ValueLayout; import jdk.incubator.foreign.ValueLayout;
import jdk.internal.foreign.PlatformLayouts;
public enum TypeClass { public enum TypeClass {
STRUCT_REGISTER, STRUCT_REGISTER,
@ -42,11 +42,17 @@ public enum TypeClass {
private static final int MAX_AGGREGATE_REGS_SIZE = 2; private static final int MAX_AGGREGATE_REGS_SIZE = 2;
private static TypeClass classifyValueType(ValueLayout type) { private static TypeClass classifyValueType(ValueLayout type) {
return switch (PlatformLayouts.getKind(type)) { Class<?> carrier = type.carrier();
case CHAR, SHORT, INT, LONG, LONG_LONG -> INTEGER; if (carrier == boolean.class || carrier == byte.class || carrier == char.class ||
case POINTER -> POINTER; carrier == short.class || carrier == int.class || carrier == long.class) {
case FLOAT, DOUBLE -> FLOAT; return INTEGER;
}; } else if (carrier == float.class || carrier == double.class) {
return FLOAT;
} else if (carrier == MemoryAddress.class) {
return POINTER;
} else {
throw new IllegalStateException("Cannot get here: " + carrier.getName());
}
} }
static boolean isRegisterAggregate(MemoryLayout type) { static boolean isRegisterAggregate(MemoryLayout type) {

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, Arm Limited. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.foreign.abi.aarch64.linux;
import jdk.internal.foreign.abi.aarch64.*;
/**
* AArch64 CallArranger specialized for Linux ABI.
*/
public class LinuxAArch64CallArranger extends CallArranger {
@Override
protected boolean varArgsOnStack() {
// Variadic arguments are passed as normal arguments
return false;
}
}

View file

@ -25,14 +25,14 @@
*/ */
package jdk.internal.foreign.abi.aarch64.linux; package jdk.internal.foreign.abi.aarch64.linux;
import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.FunctionDescriptor; import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.NativeSymbol;
import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.ResourceScope;
import jdk.internal.foreign.AbstractCLinker; import jdk.incubator.foreign.VaList;
import jdk.internal.foreign.ResourceScopeImpl;
import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.foreign.abi.UpcallStubs;
import jdk.internal.foreign.abi.aarch64.CallArranger; import jdk.internal.foreign.abi.aarch64.CallArranger;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
@ -45,26 +45,11 @@ import java.util.function.Consumer;
* ABI implementation based on ARM document "Procedure Call Standard for * ABI implementation based on ARM document "Procedure Call Standard for
* the ARM 64-bit Architecture". * the ARM 64-bit Architecture".
*/ */
public final class LinuxAArch64Linker extends AbstractCLinker { public final class LinuxAArch64Linker implements CLinker {
private static LinuxAArch64Linker instance; private static LinuxAArch64Linker instance;
static final long ADDRESS_SIZE = 64; // bits static final long ADDRESS_SIZE = 64; // bits
private static final MethodHandle MH_unboxVaList;
private static final MethodHandle MH_boxVaList;
static {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MH_unboxVaList = lookup.findVirtual(VaList.class, "address",
MethodType.methodType(MemoryAddress.class));
MH_boxVaList = MethodHandles.insertArguments(lookup.findStatic(LinuxAArch64Linker.class, "newVaListOfAddress",
MethodType.methodType(VaList.class, MemoryAddress.class, ResourceScope.class)), 1, ResourceScope.globalScope());
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}
public static LinuxAArch64Linker getInstance() { public static LinuxAArch64Linker getInstance() {
if (instance == null) { if (instance == null) {
instance = new LinuxAArch64Linker(); instance = new LinuxAArch64Linker();
@ -73,26 +58,28 @@ public final class LinuxAArch64Linker extends AbstractCLinker {
} }
@Override @Override
public final MethodHandle downcallHandle(MethodType type, FunctionDescriptor function) { public final MethodHandle downcallHandle(FunctionDescriptor function) {
Objects.requireNonNull(type);
Objects.requireNonNull(function); Objects.requireNonNull(function);
MethodType llMt = SharedUtils.convertVaListCarriers(type, LinuxAArch64VaList.CARRIER); MethodType type = SharedUtils.inferMethodType(function, false);
MethodHandle handle = CallArranger.arrangeDowncall(llMt, function); MethodHandle handle = CallArranger.LINUX.arrangeDowncall(type, function);
if (!type.returnType().equals(MemorySegment.class)) { if (!type.returnType().equals(MemorySegment.class)) {
// not returning segment, just insert a throwing allocator // not returning segment, just insert a throwing allocator
handle = MethodHandles.insertArguments(handle, 1, SharedUtils.THROWING_ALLOCATOR); handle = MethodHandles.insertArguments(handle, 1, SharedUtils.THROWING_ALLOCATOR);
} }
handle = SharedUtils.unboxVaLists(type, handle, MH_unboxVaList); return SharedUtils.wrapDowncall(handle, function);
return handle;
} }
@Override @Override
public final MemoryAddress upcallStub(MethodHandle target, FunctionDescriptor function, ResourceScope scope) { public final NativeSymbol upcallStub(MethodHandle target, FunctionDescriptor function, ResourceScope scope) {
Objects.requireNonNull(scope); Objects.requireNonNull(scope);
Objects.requireNonNull(target); Objects.requireNonNull(target);
Objects.requireNonNull(function); Objects.requireNonNull(function);
target = SharedUtils.boxVaLists(target, MH_boxVaList); SharedUtils.checkExceptions(target);
return UpcallStubs.upcallAddress(CallArranger.arrangeUpcall(target, target.type(), function), (ResourceScopeImpl) scope); MethodType type = SharedUtils.inferMethodType(function, true);
if (!type.equals(target.type())) {
throw new IllegalArgumentException("Wrong method handle type: " + target.type());
}
return CallArranger.LINUX.arrangeUpcall(target, target.type(), function, scope);
} }
public static VaList newVaList(Consumer<VaList.Builder> actions, ResourceScope scope) { public static VaList newVaList(Consumer<VaList.Builder> actions, ResourceScope scope) {

View file

@ -26,32 +26,30 @@
package jdk.internal.foreign.abi.aarch64.linux; package jdk.internal.foreign.abi.aarch64.linux;
import jdk.incubator.foreign.*; import jdk.incubator.foreign.*;
import jdk.internal.foreign.ResourceScopeImpl;
import jdk.internal.foreign.Scoped;
import jdk.internal.foreign.Utils; import jdk.internal.foreign.Utils;
import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.foreign.abi.aarch64.*; import jdk.internal.foreign.abi.aarch64.*;
import jdk.internal.misc.Unsafe; import jdk.internal.misc.Unsafe;
import java.lang.invoke.VarHandle; import java.lang.invoke.VarHandle;
import java.lang.ref.Cleaner;
import java.nio.ByteOrder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import static jdk.internal.foreign.PlatformLayouts.AArch64; import static jdk.internal.foreign.PlatformLayouts.AArch64;
import static jdk.incubator.foreign.CLinker.VaList;
import static jdk.incubator.foreign.MemoryLayout.PathElement.groupElement; import static jdk.incubator.foreign.MemoryLayout.PathElement.groupElement;
import static jdk.internal.foreign.abi.SharedUtils.SimpleVaArg; import static jdk.internal.foreign.abi.SharedUtils.SimpleVaArg;
import static jdk.internal.foreign.abi.SharedUtils.THROWING_ALLOCATOR; import static jdk.internal.foreign.abi.SharedUtils.THROWING_ALLOCATOR;
import static jdk.internal.foreign.abi.SharedUtils.checkCompatibleType;
import static jdk.internal.foreign.abi.SharedUtils.vhPrimitiveOrAddress;
import static jdk.internal.foreign.abi.aarch64.CallArranger.MAX_REGISTER_ARGUMENTS; import static jdk.internal.foreign.abi.aarch64.CallArranger.MAX_REGISTER_ARGUMENTS;
/** /**
* Standard va_list implementation as defined by AAPCS document and used on * Standard va_list implementation as defined by AAPCS document and used on
* Linux. Variadic parameters may be passed in registers or on the stack. * Linux. Variadic parameters may be passed in registers or on the stack.
*/ */
public non-sealed class LinuxAArch64VaList implements VaList { public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
private static final Unsafe U = Unsafe.getUnsafe(); private static final Unsafe U = Unsafe.getUnsafe();
static final Class<?> CARRIER = MemoryAddress.class; static final Class<?> CARRIER = MemoryAddress.class;
@ -76,9 +74,9 @@ public non-sealed class LinuxAArch64VaList implements VaList {
).withName("__va_list"); ).withName("__va_list");
private static final MemoryLayout GP_REG private static final MemoryLayout GP_REG
= MemoryLayout.valueLayout(64, ByteOrder.nativeOrder()); = MemoryLayout.paddingLayout(64).withBitAlignment(64);
private static final MemoryLayout FP_REG private static final MemoryLayout FP_REG
= MemoryLayout.valueLayout(128, ByteOrder.nativeOrder()); = MemoryLayout.paddingLayout(128).withBitAlignment(128);
private static final MemoryLayout LAYOUT_GP_REGS private static final MemoryLayout LAYOUT_GP_REGS
= MemoryLayout.sequenceLayout(MAX_REGISTER_ARGUMENTS, GP_REG); = MemoryLayout.sequenceLayout(MAX_REGISTER_ARGUMENTS, GP_REG);
@ -91,18 +89,14 @@ public non-sealed class LinuxAArch64VaList implements VaList {
private static final int MAX_GP_OFFSET = (int) LAYOUT_GP_REGS.byteSize(); private static final int MAX_GP_OFFSET = (int) LAYOUT_GP_REGS.byteSize();
private static final int MAX_FP_OFFSET = (int) LAYOUT_FP_REGS.byteSize(); private static final int MAX_FP_OFFSET = (int) LAYOUT_FP_REGS.byteSize();
private static final VarHandle VH_stack private static final VarHandle VH_stack = LAYOUT.varHandle(groupElement("__stack"));
= MemoryHandles.asAddressVarHandle(LAYOUT.varHandle(long.class, groupElement("__stack"))); private static final VarHandle VH_gr_top = LAYOUT.varHandle(groupElement("__gr_top"));
private static final VarHandle VH_gr_top private static final VarHandle VH_vr_top = LAYOUT.varHandle(groupElement("__vr_top"));
= MemoryHandles.asAddressVarHandle(LAYOUT.varHandle(long.class, groupElement("__gr_top")));
private static final VarHandle VH_vr_top
= MemoryHandles.asAddressVarHandle(LAYOUT.varHandle(long.class, groupElement("__vr_top")));
private static final VarHandle VH_gr_offs private static final VarHandle VH_gr_offs
= LAYOUT.varHandle(int.class, groupElement("__gr_offs")); = LAYOUT.varHandle(groupElement("__gr_offs"));
private static final VarHandle VH_vr_offs private static final VarHandle VH_vr_offs
= LAYOUT.varHandle(int.class, groupElement("__vr_offs")); = LAYOUT.varHandle(groupElement("__vr_offs"));
private static final Cleaner cleaner = Cleaner.create();
private static final VaList EMPTY private static final VaList EMPTY
= new SharedUtils.EmptyVaList(emptyListAddress()); = new SharedUtils.EmptyVaList(emptyListAddress());
@ -117,19 +111,20 @@ public non-sealed class LinuxAArch64VaList implements VaList {
} }
private static LinuxAArch64VaList readFromSegment(MemorySegment segment) { private static LinuxAArch64VaList readFromSegment(MemorySegment segment) {
MemorySegment gpRegsArea = grTop(segment).addOffset(-MAX_GP_OFFSET).asSegment( MemorySegment gpRegsArea = MemorySegment.ofAddress(grTop(segment).addOffset(-MAX_GP_OFFSET),
MAX_GP_OFFSET, segment.scope()); MAX_GP_OFFSET, segment.scope());
MemorySegment fpRegsArea = vrTop(segment).addOffset(-MAX_FP_OFFSET).asSegment( MemorySegment fpRegsArea = MemorySegment.ofAddress(vrTop(segment).addOffset(-MAX_FP_OFFSET),
MAX_FP_OFFSET, segment.scope()); MAX_FP_OFFSET, segment.scope());
return new LinuxAArch64VaList(segment, gpRegsArea, fpRegsArea); return new LinuxAArch64VaList(segment, gpRegsArea, fpRegsArea);
} }
private static MemoryAddress emptyListAddress() { private static MemoryAddress emptyListAddress() {
long ptr = U.allocateMemory(LAYOUT.byteSize()); long ptr = U.allocateMemory(LAYOUT.byteSize());
MemorySegment ms = MemoryAddress.ofLong(ptr).asSegment( ResourceScope scope = ResourceScope.newImplicitScope();
LAYOUT.byteSize(), () -> U.freeMemory(ptr), ResourceScope.newSharedScope()); scope.addCloseAction(() -> U.freeMemory(ptr));
cleaner.register(LinuxAArch64VaList.class, () -> ms.scope().close()); MemorySegment ms = MemorySegment.ofAddress(MemoryAddress.ofLong(ptr),
LAYOUT.byteSize(), scope);
VH_stack.set(ms, MemoryAddress.NULL); VH_stack.set(ms, MemoryAddress.NULL);
VH_gr_top.set(ms, MemoryAddress.NULL); VH_gr_top.set(ms, MemoryAddress.NULL);
VH_vr_top.set(ms, MemoryAddress.NULL); VH_vr_top.set(ms, MemoryAddress.NULL);
@ -215,58 +210,51 @@ public non-sealed class LinuxAArch64VaList implements VaList {
} }
@Override @Override
public int vargAsInt(MemoryLayout layout) { public int nextVarg(ValueLayout.OfInt layout) {
return (int) read(int.class, layout); return (int) read(int.class, layout);
} }
@Override @Override
public long vargAsLong(MemoryLayout layout) { public long nextVarg(ValueLayout.OfLong layout) {
return (long) read(long.class, layout); return (long) read(long.class, layout);
} }
@Override @Override
public double vargAsDouble(MemoryLayout layout) { public double nextVarg(ValueLayout.OfDouble layout) {
return (double) read(double.class, layout); return (double) read(double.class, layout);
} }
@Override @Override
public MemoryAddress vargAsAddress(MemoryLayout layout) { public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
return (MemoryAddress) read(MemoryAddress.class, layout); return (MemoryAddress) read(MemoryAddress.class, layout);
} }
@Override @Override
public MemorySegment vargAsSegment(MemoryLayout layout, SegmentAllocator allocator) { public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(allocator); Objects.requireNonNull(allocator);
return (MemorySegment) read(MemorySegment.class, layout, allocator); return (MemorySegment) read(MemorySegment.class, layout, allocator);
} }
@Override
public MemorySegment vargAsSegment(MemoryLayout layout, ResourceScope scope) {
return vargAsSegment(layout, SegmentAllocator.ofScope(scope));
}
private Object read(Class<?> carrier, MemoryLayout layout) { private Object read(Class<?> carrier, MemoryLayout layout) {
return read(carrier, layout, THROWING_ALLOCATOR); return read(carrier, layout, THROWING_ALLOCATOR);
} }
private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) { private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
checkCompatibleType(carrier, layout, LinuxAArch64Linker.ADDRESS_SIZE);
TypeClass typeClass = TypeClass.classifyLayout(layout); TypeClass typeClass = TypeClass.classifyLayout(layout);
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass, layout)) { if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass, layout)) {
preAlignStack(layout); preAlignStack(layout);
return switch (typeClass) { return switch (typeClass) {
case STRUCT_REGISTER, STRUCT_HFA, STRUCT_REFERENCE -> { case STRUCT_REGISTER, STRUCT_HFA, STRUCT_REFERENCE -> {
MemorySegment slice = stackPtr().asSegment(layout.byteSize(), scope()); MemorySegment slice = MemorySegment.ofAddress(stackPtr(), layout.byteSize(), scope());
MemorySegment seg = allocator.allocate(layout); MemorySegment seg = allocator.allocate(layout);
seg.copyFrom(slice); seg.copyFrom(slice);
postAlignStack(layout); postAlignStack(layout);
yield seg; yield seg;
} }
case POINTER, INTEGER, FLOAT -> { case POINTER, INTEGER, FLOAT -> {
VarHandle reader = vhPrimitiveOrAddress(carrier, layout); VarHandle reader = layout.varHandle();
MemorySegment slice = stackPtr().asSegment(layout.byteSize(), scope()); MemorySegment slice = MemorySegment.ofAddress(stackPtr(), layout.byteSize(), scope());
Object res = reader.get(slice); Object res = reader.get(slice);
postAlignStack(layout); postAlignStack(layout);
yield res; yield res;
@ -280,8 +268,7 @@ public non-sealed class LinuxAArch64VaList implements VaList {
long offset = 0; long offset = 0;
while (offset < layout.byteSize()) { while (offset < layout.byteSize()) {
final long copy = Math.min(layout.byteSize() - offset, 8); final long copy = Math.min(layout.byteSize() - offset, 8);
MemorySegment slice = value.asSlice(offset, copy); MemorySegment.copy(gpRegsArea, currentGPOffset(), value, offset, copy);
slice.copyFrom(gpRegsArea.asSlice(currentGPOffset(), copy));
consumeGPSlots(1); consumeGPSlots(1);
offset += copy; offset += copy;
} }
@ -296,8 +283,7 @@ public non-sealed class LinuxAArch64VaList implements VaList {
for (MemoryLayout elem : group.memberLayouts()) { for (MemoryLayout elem : group.memberLayouts()) {
assert elem.byteSize() <= 8; assert elem.byteSize() <= 8;
final long copy = elem.byteSize(); final long copy = elem.byteSize();
MemorySegment slice = value.asSlice(offset, copy); MemorySegment.copy(fpRegsArea, currentFPOffset(), value, offset, copy);
slice.copyFrom(fpRegsArea.asSlice(currentFPOffset(), copy));
consumeFPSlots(1); consumeFPSlots(1);
offset += copy; offset += copy;
} }
@ -305,25 +291,24 @@ public non-sealed class LinuxAArch64VaList implements VaList {
} }
case STRUCT_REFERENCE -> { case STRUCT_REFERENCE -> {
// Struct is passed indirectly via a pointer in an integer register. // Struct is passed indirectly via a pointer in an integer register.
VarHandle ptrReader VarHandle ptrReader = AArch64.C_POINTER.varHandle();
= SharedUtils.vhPrimitiveOrAddress(MemoryAddress.class, AArch64.C_POINTER);
MemoryAddress ptr = (MemoryAddress) ptrReader.get( MemoryAddress ptr = (MemoryAddress) ptrReader.get(
gpRegsArea.asSlice(currentGPOffset())); gpRegsArea.asSlice(currentGPOffset()));
consumeGPSlots(1); consumeGPSlots(1);
MemorySegment slice = ptr.asSegment(layout.byteSize(), scope()); MemorySegment slice = MemorySegment.ofAddress(ptr, layout.byteSize(), scope());
MemorySegment seg = allocator.allocate(layout); MemorySegment seg = allocator.allocate(layout);
seg.copyFrom(slice); seg.copyFrom(slice);
yield seg; yield seg;
} }
case POINTER, INTEGER -> { case POINTER, INTEGER -> {
VarHandle reader = SharedUtils.vhPrimitiveOrAddress(carrier, layout); VarHandle reader = layout.varHandle();
Object res = reader.get(gpRegsArea.asSlice(currentGPOffset())); Object res = reader.get(gpRegsArea.asSlice(currentGPOffset()));
consumeGPSlots(1); consumeGPSlots(1);
yield res; yield res;
} }
case FLOAT -> { case FLOAT -> {
VarHandle reader = layout.varHandle(carrier); VarHandle reader = layout.varHandle();
Object res = reader.get(fpRegsArea.asSlice(currentFPOffset())); Object res = reader.get(fpRegsArea.asSlice(currentFPOffset()));
consumeFPSlots(1); consumeFPSlots(1);
yield res; yield res;
@ -335,6 +320,7 @@ public non-sealed class LinuxAArch64VaList implements VaList {
@Override @Override
public void skip(MemoryLayout... layouts) { public void skip(MemoryLayout... layouts) {
Objects.requireNonNull(layouts); Objects.requireNonNull(layouts);
((ResourceScopeImpl)segment.scope()).checkValidStateSlow();
for (MemoryLayout layout : layouts) { for (MemoryLayout layout : layouts) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
TypeClass typeClass = TypeClass.classifyLayout(layout); TypeClass typeClass = TypeClass.classifyLayout(layout);
@ -356,7 +342,7 @@ public non-sealed class LinuxAArch64VaList implements VaList {
} }
public static VaList ofAddress(MemoryAddress ma, ResourceScope scope) { public static VaList ofAddress(MemoryAddress ma, ResourceScope scope) {
return readFromSegment(ma.asSegment(LAYOUT.byteSize(), scope)); return readFromSegment(MemorySegment.ofAddress(ma, LAYOUT.byteSize(), scope));
} }
@Override @Override
@ -418,35 +404,33 @@ public non-sealed class LinuxAArch64VaList implements VaList {
} }
@Override @Override
public Builder vargFromInt(ValueLayout layout, int value) { public Builder addVarg(ValueLayout.OfInt layout, int value) {
return arg(int.class, layout, value); return arg(int.class, layout, value);
} }
@Override @Override
public Builder vargFromLong(ValueLayout layout, long value) { public Builder addVarg(ValueLayout.OfLong layout, long value) {
return arg(long.class, layout, value); return arg(long.class, layout, value);
} }
@Override @Override
public Builder vargFromDouble(ValueLayout layout, double value) { public Builder addVarg(ValueLayout.OfDouble layout, double value) {
return arg(double.class, layout, value); return arg(double.class, layout, value);
} }
@Override @Override
public Builder vargFromAddress(ValueLayout layout, Addressable value) { public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) {
return arg(MemoryAddress.class, layout, value.address()); return arg(MemoryAddress.class, layout, value.address());
} }
@Override @Override
public Builder vargFromSegment(GroupLayout layout, MemorySegment value) { public Builder addVarg(GroupLayout layout, MemorySegment value) {
return arg(MemorySegment.class, layout, value); return arg(MemorySegment.class, layout, value);
} }
private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) { private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
Objects.requireNonNull(value); Objects.requireNonNull(value);
checkCompatibleType(carrier, layout, LinuxAArch64Linker.ADDRESS_SIZE);
TypeClass typeClass = TypeClass.classifyLayout(layout); TypeClass typeClass = TypeClass.classifyLayout(layout);
if (isRegOverflow(currentGPOffset, currentFPOffset, typeClass, layout)) { if (isRegOverflow(currentGPOffset, currentFPOffset, typeClass, layout)) {
stackArgs.add(new SimpleVaArg(carrier, layout, value)); stackArgs.add(new SimpleVaArg(carrier, layout, value));
@ -458,8 +442,7 @@ public non-sealed class LinuxAArch64VaList implements VaList {
long offset = 0; long offset = 0;
while (offset < layout.byteSize()) { while (offset < layout.byteSize()) {
final long copy = Math.min(layout.byteSize() - offset, 8); final long copy = Math.min(layout.byteSize() - offset, 8);
MemorySegment slice = valueSegment.asSlice(offset, copy); MemorySegment.copy(valueSegment, offset, gpRegs, currentGPOffset, copy);
gpRegs.asSlice(currentGPOffset, copy).copyFrom(slice);
currentGPOffset += GP_SLOT_SIZE; currentGPOffset += GP_SLOT_SIZE;
offset += copy; offset += copy;
} }
@ -473,8 +456,7 @@ public non-sealed class LinuxAArch64VaList implements VaList {
for (MemoryLayout elem : group.memberLayouts()) { for (MemoryLayout elem : group.memberLayouts()) {
assert elem.byteSize() <= 8; assert elem.byteSize() <= 8;
final long copy = elem.byteSize(); final long copy = elem.byteSize();
MemorySegment slice = valueSegment.asSlice(offset, copy); MemorySegment.copy(valueSegment, offset, fpRegs, currentFPOffset, copy);
fpRegs.asSlice(currentFPOffset, copy).copyFrom(slice);
currentFPOffset += FP_SLOT_SIZE; currentFPOffset += FP_SLOT_SIZE;
offset += copy; offset += copy;
} }
@ -482,20 +464,18 @@ public non-sealed class LinuxAArch64VaList implements VaList {
case STRUCT_REFERENCE -> { case STRUCT_REFERENCE -> {
// Struct is passed indirectly via a pointer in an integer register. // Struct is passed indirectly via a pointer in an integer register.
MemorySegment valueSegment = (MemorySegment) value; MemorySegment valueSegment = (MemorySegment) value;
VarHandle writer VarHandle writer = AArch64.C_POINTER.varHandle();
= SharedUtils.vhPrimitiveOrAddress(MemoryAddress.class,
AArch64.C_POINTER);
writer.set(gpRegs.asSlice(currentGPOffset), writer.set(gpRegs.asSlice(currentGPOffset),
valueSegment.address()); valueSegment.address());
currentGPOffset += GP_SLOT_SIZE; currentGPOffset += GP_SLOT_SIZE;
} }
case POINTER, INTEGER -> { case POINTER, INTEGER -> {
VarHandle writer = SharedUtils.vhPrimitiveOrAddress(carrier, layout); VarHandle writer = layout.varHandle();
writer.set(gpRegs.asSlice(currentGPOffset), value); writer.set(gpRegs.asSlice(currentGPOffset), value);
currentGPOffset += GP_SLOT_SIZE; currentGPOffset += GP_SLOT_SIZE;
} }
case FLOAT -> { case FLOAT -> {
VarHandle writer = layout.varHandle(carrier); VarHandle writer = layout.varHandle();
writer.set(fpRegs.asSlice(currentFPOffset), value); writer.set(fpRegs.asSlice(currentFPOffset), value);
currentFPOffset += FP_SLOT_SIZE; currentFPOffset += FP_SLOT_SIZE;
} }
@ -513,7 +493,7 @@ public non-sealed class LinuxAArch64VaList implements VaList {
return EMPTY; return EMPTY;
} }
SegmentAllocator allocator = SegmentAllocator.arenaAllocator(scope); SegmentAllocator allocator = SegmentAllocator.newNativeArena(scope);
MemorySegment vaListSegment = allocator.allocate(LAYOUT); MemorySegment vaListSegment = allocator.allocate(LAYOUT);
MemoryAddress stackArgsPtr = MemoryAddress.NULL; MemoryAddress stackArgsPtr = MemoryAddress.NULL;
if (!stackArgs.isEmpty()) { if (!stackArgs.isEmpty()) {

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, Arm Limited. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.foreign.abi.aarch64.macos;
import jdk.internal.foreign.abi.aarch64.*;
/**
* AArch64 CallArranger specialized for macOS ABI.
*/
public class MacOsAArch64CallArranger extends CallArranger {
@Override
protected boolean varArgsOnStack() {
// Variadic arguments are always passed on the stack
return true;
}
}

View file

@ -25,16 +25,14 @@
*/ */
package jdk.internal.foreign.abi.aarch64.macos; package jdk.internal.foreign.abi.aarch64.macos;
import jdk.incubator.foreign.Addressable; import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.FunctionDescriptor; import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.NativeSymbol;
import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.ResourceScope;
import jdk.internal.foreign.AbstractCLinker; import jdk.incubator.foreign.VaList;
import jdk.internal.foreign.ResourceScopeImpl;
import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.foreign.abi.UpcallStubs;
import jdk.internal.foreign.abi.aarch64.CallArranger; import jdk.internal.foreign.abi.aarch64.CallArranger;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
@ -43,32 +41,15 @@ import java.lang.invoke.MethodType;
import java.util.Objects; import java.util.Objects;
import java.util.function.Consumer; import java.util.function.Consumer;
import static jdk.internal.foreign.PlatformLayouts.*;
/** /**
* ABI implementation for macOS on Apple silicon. Based on AAPCS with * ABI implementation for macOS on Apple silicon. Based on AAPCS with
* changes to va_list and passing arguments on the stack. * changes to va_list and passing arguments on the stack.
*/ */
public final class MacOsAArch64Linker extends AbstractCLinker { public final class MacOsAArch64Linker implements CLinker {
private static MacOsAArch64Linker instance; private static MacOsAArch64Linker instance;
static final long ADDRESS_SIZE = 64; // bits static final long ADDRESS_SIZE = 64; // bits
private static final MethodHandle MH_unboxVaList;
private static final MethodHandle MH_boxVaList;
static {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MH_unboxVaList = lookup.findVirtual(VaList.class, "address",
MethodType.methodType(MemoryAddress.class));
MH_boxVaList = MethodHandles.insertArguments(lookup.findStatic(MacOsAArch64Linker.class, "newVaListOfAddress",
MethodType.methodType(VaList.class, MemoryAddress.class, ResourceScope.class)), 1, ResourceScope.globalScope());
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}
public static MacOsAArch64Linker getInstance() { public static MacOsAArch64Linker getInstance() {
if (instance == null) { if (instance == null) {
instance = new MacOsAArch64Linker(); instance = new MacOsAArch64Linker();
@ -77,26 +58,27 @@ public final class MacOsAArch64Linker extends AbstractCLinker {
} }
@Override @Override
public final MethodHandle downcallHandle(MethodType type, FunctionDescriptor function) { public final MethodHandle downcallHandle(FunctionDescriptor function) {
Objects.requireNonNull(type);
Objects.requireNonNull(function); Objects.requireNonNull(function);
MethodType llMt = SharedUtils.convertVaListCarriers(type, MacOsAArch64VaList.CARRIER); MethodType type = SharedUtils.inferMethodType(function, false);
MethodHandle handle = CallArranger.arrangeDowncall(llMt, function); MethodHandle handle = CallArranger.MACOS.arrangeDowncall(type, function);
if (!type.returnType().equals(MemorySegment.class)) { if (!type.returnType().equals(MemorySegment.class)) {
// not returning segment, just insert a throwing allocator // not returning segment, just insert a throwing allocator
handle = MethodHandles.insertArguments(handle, 1, SharedUtils.THROWING_ALLOCATOR); handle = MethodHandles.insertArguments(handle, 1, SharedUtils.THROWING_ALLOCATOR);
} }
handle = SharedUtils.unboxVaLists(type, handle, MH_unboxVaList); return SharedUtils.wrapDowncall(handle, function);
return handle;
} }
@Override @Override
public final MemoryAddress upcallStub(MethodHandle target, FunctionDescriptor function, ResourceScope scope) { public final NativeSymbol upcallStub(MethodHandle target, FunctionDescriptor function, ResourceScope scope) {
Objects.requireNonNull(scope); Objects.requireNonNull(scope);
Objects.requireNonNull(target); Objects.requireNonNull(target);
Objects.requireNonNull(function); Objects.requireNonNull(function);
target = SharedUtils.boxVaLists(target, MH_boxVaList); MethodType type = SharedUtils.inferMethodType(function, true);
return UpcallStubs.upcallAddress(CallArranger.arrangeUpcall(target, target.type(), function), (ResourceScopeImpl) scope); if (!type.equals(target.type())) {
throw new IllegalArgumentException("Wrong method handle type: " + target.type());
}
return CallArranger.MACOS.arrangeUpcall(target, target.type(), function, scope);
} }
public static VaList newVaList(Consumer<VaList.Builder> actions, ResourceScope scope) { public static VaList newVaList(Consumer<VaList.Builder> actions, ResourceScope scope) {

View file

@ -26,7 +26,7 @@
package jdk.internal.foreign.abi.aarch64.macos; package jdk.internal.foreign.abi.aarch64.macos;
import jdk.incubator.foreign.*; import jdk.incubator.foreign.*;
import jdk.incubator.foreign.CLinker.VaList; import jdk.internal.foreign.Scoped;
import jdk.internal.foreign.ResourceScopeImpl; import jdk.internal.foreign.ResourceScopeImpl;
import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.foreign.abi.SharedUtils.SimpleVaArg; import jdk.internal.foreign.abi.SharedUtils.SimpleVaArg;
@ -45,10 +45,10 @@ import static jdk.internal.foreign.abi.SharedUtils.alignUp;
* parameters are passed on the stack and the type of va_list decays to * parameters are passed on the stack and the type of va_list decays to
* char* instead of the structure defined in the AAPCS. * char* instead of the structure defined in the AAPCS.
*/ */
public non-sealed class MacOsAArch64VaList implements VaList { public non-sealed class MacOsAArch64VaList implements VaList, Scoped {
public static final Class<?> CARRIER = MemoryAddress.class; public static final Class<?> CARRIER = MemoryAddress.class;
private static final long VA_SLOT_SIZE_BYTES = 8; private static final long VA_SLOT_SIZE_BYTES = 8;
private static final VarHandle VH_address = MemoryHandles.asAddressVarHandle(C_POINTER.varHandle(long.class)); private static final VarHandle VH_address = C_POINTER.varHandle();
private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemoryAddress.NULL); private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemoryAddress.NULL);
@ -65,65 +65,59 @@ public non-sealed class MacOsAArch64VaList implements VaList {
} }
@Override @Override
public int vargAsInt(MemoryLayout layout) { public int nextVarg(ValueLayout.OfInt layout) {
return (int) read(int.class, layout); return (int) read(int.class, layout);
} }
@Override @Override
public long vargAsLong(MemoryLayout layout) { public long nextVarg(ValueLayout.OfLong layout) {
return (long) read(long.class, layout); return (long) read(long.class, layout);
} }
@Override @Override
public double vargAsDouble(MemoryLayout layout) { public double nextVarg(ValueLayout.OfDouble layout) {
return (double) read(double.class, layout); return (double) read(double.class, layout);
} }
@Override @Override
public MemoryAddress vargAsAddress(MemoryLayout layout) { public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
return (MemoryAddress) read(MemoryAddress.class, layout); return (MemoryAddress) read(MemoryAddress.class, layout);
} }
@Override @Override
public MemorySegment vargAsSegment(MemoryLayout layout, SegmentAllocator allocator) { public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(allocator); Objects.requireNonNull(allocator);
return (MemorySegment) read(MemorySegment.class, layout, allocator); return (MemorySegment) read(MemorySegment.class, layout, allocator);
} }
@Override
public MemorySegment vargAsSegment(MemoryLayout layout, ResourceScope scope) {
return vargAsSegment(layout, SegmentAllocator.ofScope(scope));
}
private Object read(Class<?> carrier, MemoryLayout layout) { private Object read(Class<?> carrier, MemoryLayout layout) {
return read(carrier, layout, SharedUtils.THROWING_ALLOCATOR); return read(carrier, layout, SharedUtils.THROWING_ALLOCATOR);
} }
private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) { private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
SharedUtils.checkCompatibleType(carrier, layout, MacOsAArch64Linker.ADDRESS_SIZE);
Object res; Object res;
if (carrier == MemorySegment.class) { if (carrier == MemorySegment.class) {
TypeClass typeClass = TypeClass.classifyLayout(layout); TypeClass typeClass = TypeClass.classifyLayout(layout);
res = switch (typeClass) { res = switch (typeClass) {
case STRUCT_REFERENCE -> { case STRUCT_REFERENCE -> {
MemoryAddress structAddr = (MemoryAddress) VH_address.get(segment); MemoryAddress structAddr = (MemoryAddress) VH_address.get(segment);
MemorySegment struct = structAddr.asSegment(layout.byteSize(), scope()); MemorySegment struct = MemorySegment.ofAddress(structAddr, layout.byteSize(), scope());
MemorySegment seg = allocator.allocate(layout); MemorySegment seg = allocator.allocate(layout);
seg.copyFrom(struct); seg.copyFrom(struct);
segment = segment.asSlice(VA_SLOT_SIZE_BYTES); segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
yield seg; yield seg;
} }
case STRUCT_REGISTER, STRUCT_HFA -> { case STRUCT_REGISTER, STRUCT_HFA -> {
MemorySegment struct = allocator.allocate(layout); MemorySegment struct = allocator.allocate(layout)
struct.copyFrom(segment.asSlice(0L, layout.byteSize())); .copyFrom(segment.asSlice(0, layout.byteSize()));
segment = segment.asSlice(alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES)); segment = segment.asSlice(alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES));
yield struct; yield struct;
} }
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass); default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
}; };
} else { } else {
VarHandle reader = SharedUtils.vhPrimitiveOrAddress(carrier, layout); VarHandle reader = layout.varHandle();
res = reader.get(segment); res = reader.get(segment);
segment = segment.asSlice(VA_SLOT_SIZE_BYTES); segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
} }
@ -133,6 +127,7 @@ public non-sealed class MacOsAArch64VaList implements VaList {
@Override @Override
public void skip(MemoryLayout... layouts) { public void skip(MemoryLayout... layouts) {
Objects.requireNonNull(layouts); Objects.requireNonNull(layouts);
((ResourceScopeImpl)scope).checkValidStateSlow();
for (MemoryLayout layout : layouts) { for (MemoryLayout layout : layouts) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
@ -144,7 +139,7 @@ public non-sealed class MacOsAArch64VaList implements VaList {
} }
static MacOsAArch64VaList ofAddress(MemoryAddress addr, ResourceScope scope) { static MacOsAArch64VaList ofAddress(MemoryAddress addr, ResourceScope scope) {
MemorySegment segment = addr.asSegment(Long.MAX_VALUE, scope); MemorySegment segment = MemorySegment.ofAddress(addr, Long.MAX_VALUE, scope);
return new MacOsAArch64VaList(segment, scope); return new MacOsAArch64VaList(segment, scope);
} }
@ -181,33 +176,32 @@ public non-sealed class MacOsAArch64VaList implements VaList {
private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) { private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
Objects.requireNonNull(value); Objects.requireNonNull(value);
SharedUtils.checkCompatibleType(carrier, layout, MacOsAArch64Linker.ADDRESS_SIZE);
args.add(new SimpleVaArg(carrier, layout, value)); args.add(new SimpleVaArg(carrier, layout, value));
return this; return this;
} }
@Override @Override
public Builder vargFromInt(ValueLayout layout, int value) { public Builder addVarg(ValueLayout.OfInt layout, int value) {
return arg(int.class, layout, value); return arg(int.class, layout, value);
} }
@Override @Override
public Builder vargFromLong(ValueLayout layout, long value) { public Builder addVarg(ValueLayout.OfLong layout, long value) {
return arg(long.class, layout, value); return arg(long.class, layout, value);
} }
@Override @Override
public Builder vargFromDouble(ValueLayout layout, double value) { public Builder addVarg(ValueLayout.OfDouble layout, double value) {
return arg(double.class, layout, value); return arg(double.class, layout, value);
} }
@Override @Override
public Builder vargFromAddress(ValueLayout layout, Addressable value) { public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) {
return arg(MemoryAddress.class, layout, value.address()); return arg(MemoryAddress.class, layout, value.address());
} }
@Override @Override
public Builder vargFromSegment(GroupLayout layout, MemorySegment value) { public Builder addVarg(GroupLayout layout, MemorySegment value) {
return arg(MemorySegment.class, layout, value); return arg(MemorySegment.class, layout, value);
} }
@ -216,7 +210,7 @@ public non-sealed class MacOsAArch64VaList implements VaList {
return EMPTY; return EMPTY;
} }
SegmentAllocator allocator = SegmentAllocator.arenaAllocator(scope); SegmentAllocator allocator = SegmentAllocator.newNativeArena(scope);
// Each argument may occupy up to four slots // Each argument may occupy up to four slots
MemorySegment segment = allocator.allocate(VA_SLOT_SIZE_BYTES * args.size() * 4); MemorySegment segment = allocator.allocate(VA_SLOT_SIZE_BYTES * args.size() * 4);
@ -237,10 +231,9 @@ public non-sealed class MacOsAArch64VaList implements VaList {
VH_address.set(cursor, copy.address()); VH_address.set(cursor, copy.address());
cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES); cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES);
} }
case STRUCT_REGISTER, STRUCT_HFA -> { case STRUCT_REGISTER, STRUCT_HFA ->
cursor.copyFrom(msArg.asSlice(0, arg.layout.byteSize())); cursor.copyFrom(msArg.asSlice(0, arg.layout.byteSize()))
cursor = cursor.asSlice(alignUp(arg.layout.byteSize(), VA_SLOT_SIZE_BYTES)); .asSlice(alignUp(arg.layout.byteSize(), VA_SLOT_SIZE_BYTES));
}
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass); default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
} }
} else { } else {

View file

@ -30,8 +30,9 @@ import jdk.incubator.foreign.GroupLayout;
import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.NativeSymbol;
import jdk.incubator.foreign.ResourceScope;
import jdk.internal.foreign.abi.CallingSequenceBuilder; import jdk.internal.foreign.abi.CallingSequenceBuilder;
import jdk.internal.foreign.abi.UpcallHandler;
import jdk.internal.foreign.abi.ABIDescriptor; import jdk.internal.foreign.abi.ABIDescriptor;
import jdk.internal.foreign.abi.Binding; import jdk.internal.foreign.abi.Binding;
import jdk.internal.foreign.abi.CallingSequence; import jdk.internal.foreign.abi.CallingSequence;
@ -86,8 +87,6 @@ public class CallArranger {
} }
public static Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall) { public static Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall) {
SharedUtils.checkFunctionTypes(mt, cDesc, SysVx64Linker.ADDRESS_SIZE);
CallingSequenceBuilder csb = new CallingSequenceBuilder(forUpcall); CallingSequenceBuilder csb = new CallingSequenceBuilder(forUpcall);
BindingCalculator argCalc = forUpcall ? new BoxBindingCalculator(true) : new UnboxBindingCalculator(true); BindingCalculator argCalc = forUpcall ? new BoxBindingCalculator(true) : new UnboxBindingCalculator(true);
@ -134,14 +133,14 @@ public class CallArranger {
return handle; return handle;
} }
public static UpcallHandler arrangeUpcall(MethodHandle target, MethodType mt, FunctionDescriptor cDesc) { public static NativeSymbol arrangeUpcall(MethodHandle target, MethodType mt, FunctionDescriptor cDesc, ResourceScope scope) {
Bindings bindings = getBindings(mt, cDesc, true); Bindings bindings = getBindings(mt, cDesc, true);
if (bindings.isInMemoryReturn) { if (bindings.isInMemoryReturn) {
target = SharedUtils.adaptUpcallForIMR(target, true /* drop return, since we don't have bindings for it */); target = SharedUtils.adaptUpcallForIMR(target, true /* drop return, since we don't have bindings for it */);
} }
return ProgrammableUpcallHandler.make(CSysV, target, bindings.callingSequence); return ProgrammableUpcallHandler.make(CSysV, target, bindings.callingSequence, scope);
} }
private static boolean isInMemoryReturn(Optional<MemoryLayout> returnLayout) { private static boolean isInMemoryReturn(Optional<MemoryLayout> returnLayout) {
@ -239,7 +238,7 @@ public class CallArranger {
} }
} }
static abstract class BindingCalculator { abstract static class BindingCalculator {
protected final StorageCalculator storageCalculator; protected final StorageCalculator storageCalculator;
protected BindingCalculator(boolean forArguments) { protected BindingCalculator(boolean forArguments) {
@ -280,7 +279,7 @@ public class CallArranger {
break; break;
} }
case POINTER: { case POINTER: {
bindings.unboxAddress(); bindings.unboxAddress(carrier);
VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER); VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER);
bindings.vmStore(storage, long.class); bindings.vmStore(storage, long.class);
break; break;

View file

@ -26,27 +26,25 @@
package jdk.internal.foreign.abi.x64.sysv; package jdk.internal.foreign.abi.x64.sysv;
import jdk.incubator.foreign.*; import jdk.incubator.foreign.*;
import jdk.internal.foreign.ResourceScopeImpl;
import jdk.internal.foreign.Scoped;
import jdk.internal.foreign.Utils; import jdk.internal.foreign.Utils;
import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.misc.Unsafe; import jdk.internal.misc.Unsafe;
import java.lang.invoke.VarHandle; import java.lang.invoke.VarHandle;
import java.lang.ref.Cleaner;
import java.nio.ByteOrder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import static jdk.internal.foreign.PlatformLayouts.SysV; import static jdk.internal.foreign.PlatformLayouts.SysV;
import static jdk.incubator.foreign.CLinker.VaList;
import static jdk.incubator.foreign.MemoryLayout.PathElement.groupElement; import static jdk.incubator.foreign.MemoryLayout.PathElement.groupElement;
import static jdk.internal.foreign.abi.SharedUtils.SimpleVaArg; import static jdk.internal.foreign.abi.SharedUtils.SimpleVaArg;
import static jdk.internal.foreign.abi.SharedUtils.THROWING_ALLOCATOR; import static jdk.internal.foreign.abi.SharedUtils.THROWING_ALLOCATOR;
import static jdk.internal.foreign.abi.SharedUtils.checkCompatibleType;
import static jdk.internal.foreign.abi.SharedUtils.vhPrimitiveOrAddress;
// See https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf "3.5.7 Variable Argument Lists" // See https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf "3.5.7 Variable Argument Lists"
public non-sealed class SysVVaList implements VaList { public non-sealed class SysVVaList implements VaList, Scoped {
private static final Unsafe U = Unsafe.getUnsafe(); private static final Unsafe U = Unsafe.getUnsafe();
static final Class<?> CARRIER = MemoryAddress.class; static final Class<?> CARRIER = MemoryAddress.class;
@ -67,8 +65,8 @@ public non-sealed class SysVVaList implements VaList {
SysV.C_POINTER.withName("reg_save_area") SysV.C_POINTER.withName("reg_save_area")
).withName("__va_list_tag"); ).withName("__va_list_tag");
private static final MemoryLayout GP_REG = MemoryLayout.valueLayout(64, ByteOrder.nativeOrder()); private static final MemoryLayout GP_REG = MemoryLayout.paddingLayout(64).withBitAlignment(64);
private static final MemoryLayout FP_REG = MemoryLayout.valueLayout(128, ByteOrder.nativeOrder()); private static final MemoryLayout FP_REG = MemoryLayout.paddingLayout(128).withBitAlignment(128);
private static final GroupLayout LAYOUT_REG_SAVE_AREA = MemoryLayout.structLayout( private static final GroupLayout LAYOUT_REG_SAVE_AREA = MemoryLayout.structLayout(
GP_REG.withName("%rdi"), GP_REG.withName("%rdi"),
@ -105,14 +103,11 @@ public non-sealed class SysVVaList implements VaList {
private static final int MAX_GP_OFFSET = (int) FP_OFFSET; // 6 regs used private static final int MAX_GP_OFFSET = (int) FP_OFFSET; // 6 regs used
private static final int MAX_FP_OFFSET = (int) LAYOUT_REG_SAVE_AREA.byteSize(); // 8 16 byte regs private static final int MAX_FP_OFFSET = (int) LAYOUT_REG_SAVE_AREA.byteSize(); // 8 16 byte regs
private static final VarHandle VH_fp_offset = LAYOUT.varHandle(int.class, groupElement("fp_offset")); private static final VarHandle VH_fp_offset = LAYOUT.varHandle(groupElement("fp_offset"));
private static final VarHandle VH_gp_offset = LAYOUT.varHandle(int.class, groupElement("gp_offset")); private static final VarHandle VH_gp_offset = LAYOUT.varHandle(groupElement("gp_offset"));
private static final VarHandle VH_overflow_arg_area private static final VarHandle VH_overflow_arg_area = LAYOUT.varHandle(groupElement("overflow_arg_area"));
= MemoryHandles.asAddressVarHandle(LAYOUT.varHandle(long.class, groupElement("overflow_arg_area"))); private static final VarHandle VH_reg_save_area = LAYOUT.varHandle(groupElement("reg_save_area"));
private static final VarHandle VH_reg_save_area
= MemoryHandles.asAddressVarHandle(LAYOUT.varHandle(long.class, groupElement("reg_save_area")));
private static final Cleaner cleaner = Cleaner.create();
private static final VaList EMPTY = new SharedUtils.EmptyVaList(emptyListAddress()); private static final VaList EMPTY = new SharedUtils.EmptyVaList(emptyListAddress());
private final MemorySegment segment; private final MemorySegment segment;
@ -130,9 +125,10 @@ public non-sealed class SysVVaList implements VaList {
private static MemoryAddress emptyListAddress() { private static MemoryAddress emptyListAddress() {
long ptr = U.allocateMemory(LAYOUT.byteSize()); long ptr = U.allocateMemory(LAYOUT.byteSize());
MemorySegment base = MemoryAddress.ofLong(ptr).asSegment( ResourceScope scope = ResourceScope.newImplicitScope();
LAYOUT.byteSize(), () -> U.freeMemory(ptr), ResourceScope.newSharedScope()); scope.addCloseAction(() -> U.freeMemory(ptr));
cleaner.register(SysVVaList.class, () -> base.scope().close()); MemorySegment base = MemorySegment.ofAddress(MemoryAddress.ofLong(ptr),
LAYOUT.byteSize(), scope);
VH_gp_offset.set(base, MAX_GP_OFFSET); VH_gp_offset.set(base, MAX_GP_OFFSET);
VH_fp_offset.set(base, MAX_FP_OFFSET); VH_fp_offset.set(base, MAX_FP_OFFSET);
VH_overflow_arg_area.set(base, MemoryAddress.NULL); VH_overflow_arg_area.set(base, MemoryAddress.NULL);
@ -173,7 +169,7 @@ public non-sealed class SysVVaList implements VaList {
} }
private static MemorySegment getRegSaveArea(MemorySegment segment) { private static MemorySegment getRegSaveArea(MemorySegment segment) {
return ((MemoryAddress)VH_reg_save_area.get(segment)).asSegment( return MemorySegment.ofAddress(((MemoryAddress)VH_reg_save_area.get(segment)),
LAYOUT_REG_SAVE_AREA.byteSize(), segment.scope()); LAYOUT_REG_SAVE_AREA.byteSize(), segment.scope());
} }
@ -188,59 +184,53 @@ public non-sealed class SysVVaList implements VaList {
} }
@Override @Override
public int vargAsInt(MemoryLayout layout) { public int nextVarg(ValueLayout.OfInt layout) {
return (int) read(int.class, layout); return (int) read(int.class, layout);
} }
@Override @Override
public long vargAsLong(MemoryLayout layout) { public long nextVarg(ValueLayout.OfLong layout) {
return (long) read(long.class, layout); return (long) read(long.class, layout);
} }
@Override @Override
public double vargAsDouble(MemoryLayout layout) { public double nextVarg(ValueLayout.OfDouble layout) {
return (double) read(double.class, layout); return (double) read(double.class, layout);
} }
@Override @Override
public MemoryAddress vargAsAddress(MemoryLayout layout) { public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
return (MemoryAddress) read(MemoryAddress.class, layout); return (MemoryAddress) read(MemoryAddress.class, layout);
} }
@Override @Override
public MemorySegment vargAsSegment(MemoryLayout layout, SegmentAllocator allocator) { public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(allocator); Objects.requireNonNull(allocator);
return (MemorySegment) read(MemorySegment.class, layout, allocator); return (MemorySegment) read(MemorySegment.class, layout, allocator);
} }
@Override
public MemorySegment vargAsSegment(MemoryLayout layout, ResourceScope scope) {
return vargAsSegment(layout, SegmentAllocator.ofScope(scope));
}
private Object read(Class<?> carrier, MemoryLayout layout) { private Object read(Class<?> carrier, MemoryLayout layout) {
return read(carrier, layout, THROWING_ALLOCATOR); return read(carrier, layout, THROWING_ALLOCATOR);
} }
private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) { private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
checkCompatibleType(carrier, layout, SysVx64Linker.ADDRESS_SIZE);
TypeClass typeClass = TypeClass.classifyLayout(layout); TypeClass typeClass = TypeClass.classifyLayout(layout);
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass) if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass)
|| typeClass.inMemory()) { || typeClass.inMemory()) {
preAlignStack(layout); preAlignStack(layout);
return switch (typeClass.kind()) { return switch (typeClass.kind()) {
case STRUCT -> { case STRUCT -> {
MemorySegment slice = stackPtr().asSegment(layout.byteSize(), scope()); MemorySegment slice = MemorySegment.ofAddress(stackPtr(), layout.byteSize(), scope());
MemorySegment seg = allocator.allocate(layout); MemorySegment seg = allocator.allocate(layout);
seg.copyFrom(slice); seg.copyFrom(slice);
postAlignStack(layout); postAlignStack(layout);
yield seg; yield seg;
} }
case POINTER, INTEGER, FLOAT -> { case POINTER, INTEGER, FLOAT -> {
VarHandle reader = vhPrimitiveOrAddress(carrier, layout); VarHandle reader = layout.varHandle();
try (ResourceScope localScope = ResourceScope.newConfinedScope()) { try (ResourceScope localScope = ResourceScope.newConfinedScope()) {
MemorySegment slice = stackPtr().asSegment(layout.byteSize(), localScope); MemorySegment slice = MemorySegment.ofAddress(stackPtr(), layout.byteSize(), localScope);
Object res = reader.get(slice); Object res = reader.get(slice);
postAlignStack(layout); postAlignStack(layout);
yield res; yield res;
@ -256,12 +246,11 @@ public non-sealed class SysVVaList implements VaList {
while (offset < layout.byteSize()) { while (offset < layout.byteSize()) {
final long copy = Math.min(layout.byteSize() - offset, 8); final long copy = Math.min(layout.byteSize() - offset, 8);
boolean isSSE = typeClass.classes.get(classIdx++) == ArgumentClassImpl.SSE; boolean isSSE = typeClass.classes.get(classIdx++) == ArgumentClassImpl.SSE;
MemorySegment slice = value.asSlice(offset, copy);
if (isSSE) { if (isSSE) {
slice.copyFrom(regSaveArea.asSlice(currentFPOffset(), copy)); MemorySegment.copy(regSaveArea, currentFPOffset(), value, offset, copy);
currentFPOffset(currentFPOffset() + FP_SLOT_SIZE); currentFPOffset(currentFPOffset() + FP_SLOT_SIZE);
} else { } else {
slice.copyFrom(regSaveArea.asSlice(currentGPOffset(), copy)); MemorySegment.copy(regSaveArea, currentGPOffset(), value, offset, copy);
currentGPOffset(currentGPOffset() + GP_SLOT_SIZE); currentGPOffset(currentGPOffset() + GP_SLOT_SIZE);
} }
offset += copy; offset += copy;
@ -269,13 +258,13 @@ public non-sealed class SysVVaList implements VaList {
yield value; yield value;
} }
case POINTER, INTEGER -> { case POINTER, INTEGER -> {
VarHandle reader = SharedUtils.vhPrimitiveOrAddress(carrier, layout); VarHandle reader = layout.varHandle();
Object res = reader.get(regSaveArea.asSlice(currentGPOffset())); Object res = reader.get(regSaveArea.asSlice(currentGPOffset()));
currentGPOffset(currentGPOffset() + GP_SLOT_SIZE); currentGPOffset(currentGPOffset() + GP_SLOT_SIZE);
yield res; yield res;
} }
case FLOAT -> { case FLOAT -> {
VarHandle reader = layout.varHandle(carrier); VarHandle reader = layout.varHandle();
Object res = reader.get(regSaveArea.asSlice(currentFPOffset())); Object res = reader.get(regSaveArea.asSlice(currentFPOffset()));
currentFPOffset(currentFPOffset() + FP_SLOT_SIZE); currentFPOffset(currentFPOffset() + FP_SLOT_SIZE);
yield res; yield res;
@ -287,6 +276,7 @@ public non-sealed class SysVVaList implements VaList {
@Override @Override
public void skip(MemoryLayout... layouts) { public void skip(MemoryLayout... layouts) {
Objects.requireNonNull(layouts); Objects.requireNonNull(layouts);
((ResourceScopeImpl)segment.scope()).checkValidStateSlow();
for (MemoryLayout layout : layouts) { for (MemoryLayout layout : layouts) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
TypeClass typeClass = TypeClass.classifyLayout(layout); TypeClass typeClass = TypeClass.classifyLayout(layout);
@ -305,7 +295,7 @@ public non-sealed class SysVVaList implements VaList {
} }
public static VaList ofAddress(MemoryAddress ma, ResourceScope scope) { public static VaList ofAddress(MemoryAddress ma, ResourceScope scope) {
return readFromSegment(ma.asSegment(LAYOUT.byteSize(), scope)); return readFromSegment(MemorySegment.ofAddress(ma, LAYOUT.byteSize(), scope));
} }
@Override @Override
@ -353,34 +343,33 @@ public non-sealed class SysVVaList implements VaList {
} }
@Override @Override
public Builder vargFromInt(ValueLayout layout, int value) { public Builder addVarg(ValueLayout.OfInt layout, int value) {
return arg(int.class, layout, value); return arg(int.class, layout, value);
} }
@Override @Override
public Builder vargFromLong(ValueLayout layout, long value) { public Builder addVarg(ValueLayout.OfLong layout, long value) {
return arg(long.class, layout, value); return arg(long.class, layout, value);
} }
@Override @Override
public Builder vargFromDouble(ValueLayout layout, double value) { public Builder addVarg(ValueLayout.OfDouble layout, double value) {
return arg(double.class, layout, value); return arg(double.class, layout, value);
} }
@Override @Override
public Builder vargFromAddress(ValueLayout layout, Addressable value) { public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) {
return arg(MemoryAddress.class, layout, value.address()); return arg(MemoryAddress.class, layout, value.address());
} }
@Override @Override
public Builder vargFromSegment(GroupLayout layout, MemorySegment value) { public Builder addVarg(GroupLayout layout, MemorySegment value) {
return arg(MemorySegment.class, layout, value); return arg(MemorySegment.class, layout, value);
} }
private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) { private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
Objects.requireNonNull(value); Objects.requireNonNull(value);
checkCompatibleType(carrier, layout, SysVx64Linker.ADDRESS_SIZE);
TypeClass typeClass = TypeClass.classifyLayout(layout); TypeClass typeClass = TypeClass.classifyLayout(layout);
if (isRegOverflow(currentGPOffset, currentFPOffset, typeClass) if (isRegOverflow(currentGPOffset, currentFPOffset, typeClass)
|| typeClass.inMemory()) { || typeClass.inMemory()) {
@ -395,24 +384,23 @@ public non-sealed class SysVVaList implements VaList {
while (offset < layout.byteSize()) { while (offset < layout.byteSize()) {
final long copy = Math.min(layout.byteSize() - offset, 8); final long copy = Math.min(layout.byteSize() - offset, 8);
boolean isSSE = typeClass.classes.get(classIdx++) == ArgumentClassImpl.SSE; boolean isSSE = typeClass.classes.get(classIdx++) == ArgumentClassImpl.SSE;
MemorySegment slice = valueSegment.asSlice(offset, copy);
if (isSSE) { if (isSSE) {
reg_save_area.asSlice(currentFPOffset, copy).copyFrom(slice); MemorySegment.copy(valueSegment, offset, reg_save_area, currentFPOffset, copy);
currentFPOffset += FP_SLOT_SIZE; currentFPOffset += FP_SLOT_SIZE;
} else { } else {
reg_save_area.asSlice(currentGPOffset, copy).copyFrom(slice); MemorySegment.copy(valueSegment, offset, reg_save_area, currentGPOffset, copy);
currentGPOffset += GP_SLOT_SIZE; currentGPOffset += GP_SLOT_SIZE;
} }
offset += copy; offset += copy;
} }
} }
case POINTER, INTEGER -> { case POINTER, INTEGER -> {
VarHandle writer = SharedUtils.vhPrimitiveOrAddress(carrier, layout); VarHandle writer = layout.varHandle();
writer.set(reg_save_area.asSlice(currentGPOffset), value); writer.set(reg_save_area.asSlice(currentGPOffset), value);
currentGPOffset += GP_SLOT_SIZE; currentGPOffset += GP_SLOT_SIZE;
} }
case FLOAT -> { case FLOAT -> {
VarHandle writer = layout.varHandle(carrier); VarHandle writer = layout.varHandle();
writer.set(reg_save_area.asSlice(currentFPOffset), value); writer.set(reg_save_area.asSlice(currentFPOffset), value);
currentFPOffset += FP_SLOT_SIZE; currentFPOffset += FP_SLOT_SIZE;
} }
@ -430,7 +418,7 @@ public non-sealed class SysVVaList implements VaList {
return EMPTY; return EMPTY;
} }
SegmentAllocator allocator = SegmentAllocator.arenaAllocator(scope); SegmentAllocator allocator = SegmentAllocator.newNativeArena(scope);
MemorySegment vaListSegment = allocator.allocate(LAYOUT); MemorySegment vaListSegment = allocator.allocate(LAYOUT);
MemoryAddress stackArgsPtr = MemoryAddress.NULL; MemoryAddress stackArgsPtr = MemoryAddress.NULL;
if (!stackArgs.isEmpty()) { if (!stackArgs.isEmpty()) {

View file

@ -25,14 +25,14 @@
package jdk.internal.foreign.abi.x64.sysv; package jdk.internal.foreign.abi.x64.sysv;
import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.FunctionDescriptor; import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.NativeSymbol;
import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.ResourceScope;
import jdk.internal.foreign.AbstractCLinker; import jdk.incubator.foreign.VaList;
import jdk.internal.foreign.ResourceScopeImpl;
import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.foreign.abi.UpcallStubs;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
@ -43,7 +43,7 @@ import java.util.function.Consumer;
/** /**
* ABI implementation based on System V ABI AMD64 supplement v.0.99.6 * ABI implementation based on System V ABI AMD64 supplement v.0.99.6
*/ */
public final class SysVx64Linker extends AbstractCLinker { public final class SysVx64Linker implements CLinker {
public static final int MAX_INTEGER_ARGUMENT_REGISTERS = 6; public static final int MAX_INTEGER_ARGUMENT_REGISTERS = 6;
public static final int MAX_INTEGER_RETURN_REGISTERS = 2; public static final int MAX_INTEGER_RETURN_REGISTERS = 2;
public static final int MAX_VECTOR_ARGUMENT_REGISTERS = 8; public static final int MAX_VECTOR_ARGUMENT_REGISTERS = 8;
@ -54,21 +54,6 @@ public final class SysVx64Linker extends AbstractCLinker {
static final long ADDRESS_SIZE = 64; // bits static final long ADDRESS_SIZE = 64; // bits
private static final MethodHandle MH_unboxVaList;
private static final MethodHandle MH_boxVaList;
static {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MH_unboxVaList = lookup.findVirtual(VaList.class, "address",
MethodType.methodType(MemoryAddress.class));
MH_boxVaList = MethodHandles.insertArguments(lookup.findStatic(SysVx64Linker.class, "newVaListOfAddress",
MethodType.methodType(VaList.class, MemoryAddress.class, ResourceScope.class)), 1, ResourceScope.globalScope());
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}
public static SysVx64Linker getInstance() { public static SysVx64Linker getInstance() {
if (instance == null) { if (instance == null) {
instance = new SysVx64Linker(); instance = new SysVx64Linker();
@ -83,26 +68,28 @@ public final class SysVx64Linker extends AbstractCLinker {
} }
@Override @Override
public final MethodHandle downcallHandle(MethodType type, FunctionDescriptor function) { public final MethodHandle downcallHandle(FunctionDescriptor function) {
Objects.requireNonNull(type);
Objects.requireNonNull(function); Objects.requireNonNull(function);
MethodType llMt = SharedUtils.convertVaListCarriers(type, SysVVaList.CARRIER); MethodType type = SharedUtils.inferMethodType(function, false);
MethodHandle handle = CallArranger.arrangeDowncall(llMt, function); MethodHandle handle = CallArranger.arrangeDowncall(type, function);
if (!type.returnType().equals(MemorySegment.class)) { if (!type.returnType().equals(MemorySegment.class)) {
// not returning segment, just insert a throwing allocator // not returning segment, just insert a throwing allocator
handle = MethodHandles.insertArguments(handle, 1, SharedUtils.THROWING_ALLOCATOR); handle = MethodHandles.insertArguments(handle, 1, SharedUtils.THROWING_ALLOCATOR);
} }
handle = SharedUtils.unboxVaLists(type, handle, MH_unboxVaList); return SharedUtils.wrapDowncall(handle, function);
return handle;
} }
@Override @Override
public final MemoryAddress upcallStub(MethodHandle target, FunctionDescriptor function, ResourceScope scope) { public final NativeSymbol upcallStub(MethodHandle target, FunctionDescriptor function, ResourceScope scope) {
Objects.requireNonNull(scope); Objects.requireNonNull(scope);
Objects.requireNonNull(target); Objects.requireNonNull(target);
Objects.requireNonNull(function); Objects.requireNonNull(function);
target = SharedUtils.boxVaLists(target, MH_boxVaList); SharedUtils.checkExceptions(target);
return UpcallStubs.upcallAddress(CallArranger.arrangeUpcall(target, target.type(), function), (ResourceScopeImpl) scope); MethodType type = SharedUtils.inferMethodType(function, true);
if (!type.equals(target.type())) {
throw new IllegalArgumentException("Wrong method handle type: " + target.type());
}
return CallArranger.arrangeUpcall(target, target.type(), function, scope);
} }
public static VaList newVaListOfAddress(MemoryAddress ma, ResourceScope scope) { public static VaList newVaListOfAddress(MemoryAddress ma, ResourceScope scope) {

View file

@ -25,10 +25,10 @@
package jdk.internal.foreign.abi.x64.sysv; package jdk.internal.foreign.abi.x64.sysv;
import jdk.incubator.foreign.GroupLayout; import jdk.incubator.foreign.GroupLayout;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.SequenceLayout; import jdk.incubator.foreign.SequenceLayout;
import jdk.incubator.foreign.ValueLayout; import jdk.incubator.foreign.ValueLayout;
import jdk.internal.foreign.PlatformLayouts;
import jdk.internal.foreign.Utils; import jdk.internal.foreign.Utils;
import java.util.ArrayList; import java.util.ArrayList;
@ -107,11 +107,17 @@ class TypeClass {
} }
private static ArgumentClassImpl argumentClassFor(MemoryLayout layout) { private static ArgumentClassImpl argumentClassFor(MemoryLayout layout) {
return switch (PlatformLayouts.getKind(layout)) { Class<?> carrier = ((ValueLayout)layout).carrier();
case CHAR, SHORT, INT, LONG, LONG_LONG -> ArgumentClassImpl.INTEGER; if (carrier == boolean.class || carrier == byte.class || carrier == char.class ||
case FLOAT, DOUBLE -> ArgumentClassImpl.SSE; carrier == short.class || carrier == int.class || carrier == long.class) {
case POINTER -> ArgumentClassImpl.POINTER; return ArgumentClassImpl.INTEGER;
}; } else if (carrier == float.class || carrier == double.class) {
return ArgumentClassImpl.SSE;
} else if (carrier == MemoryAddress.class) {
return ArgumentClassImpl.POINTER;
} else {
throw new IllegalStateException("Cannot get here: " + carrier.getName());
}
} }
// TODO: handle zero length arrays // TODO: handle zero length arrays

View file

@ -29,9 +29,10 @@ import jdk.incubator.foreign.GroupLayout;
import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.NativeSymbol;
import jdk.incubator.foreign.ResourceScope;
import jdk.internal.foreign.Utils; import jdk.internal.foreign.Utils;
import jdk.internal.foreign.abi.CallingSequenceBuilder; import jdk.internal.foreign.abi.CallingSequenceBuilder;
import jdk.internal.foreign.abi.UpcallHandler;
import jdk.internal.foreign.abi.ABIDescriptor; import jdk.internal.foreign.abi.ABIDescriptor;
import jdk.internal.foreign.abi.Binding; import jdk.internal.foreign.abi.Binding;
import jdk.internal.foreign.abi.CallingSequence; import jdk.internal.foreign.abi.CallingSequence;
@ -82,8 +83,6 @@ public class CallArranger {
} }
public static Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall) { public static Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall) {
SharedUtils.checkFunctionTypes(mt, cDesc, Windowsx64Linker.ADDRESS_SIZE);
class CallingSequenceBuilderHelper { class CallingSequenceBuilderHelper {
final CallingSequenceBuilder csb = new CallingSequenceBuilder(forUpcall); final CallingSequenceBuilder csb = new CallingSequenceBuilder(forUpcall);
final BindingCalculator argCalc = final BindingCalculator argCalc =
@ -91,12 +90,12 @@ public class CallArranger {
final BindingCalculator retCalc = final BindingCalculator retCalc =
forUpcall ? new UnboxBindingCalculator(false) : new BoxBindingCalculator(false); forUpcall ? new UnboxBindingCalculator(false) : new BoxBindingCalculator(false);
void addArgumentBindings(Class<?> carrier, MemoryLayout layout) { void addArgumentBindings(Class<?> carrier, MemoryLayout layout, boolean isVararg) {
csb.addArgumentBindings(carrier, layout, argCalc.getBindings(carrier, layout)); csb.addArgumentBindings(carrier, layout, argCalc.getBindings(carrier, layout, isVararg));
} }
void setReturnBindings(Class<?> carrier, MemoryLayout layout) { void setReturnBindings(Class<?> carrier, MemoryLayout layout) {
csb.setReturnBindings(carrier, layout, retCalc.getBindings(carrier, layout)); csb.setReturnBindings(carrier, layout, retCalc.getBindings(carrier, layout, false));
} }
} }
var csb = new CallingSequenceBuilderHelper(); var csb = new CallingSequenceBuilderHelper();
@ -105,7 +104,7 @@ public class CallArranger {
if (returnInMemory) { if (returnInMemory) {
Class<?> carrier = MemoryAddress.class; Class<?> carrier = MemoryAddress.class;
MemoryLayout layout = Win64.C_POINTER; MemoryLayout layout = Win64.C_POINTER;
csb.addArgumentBindings(carrier, layout); csb.addArgumentBindings(carrier, layout, false);
if (forUpcall) { if (forUpcall) {
csb.setReturnBindings(carrier, layout); csb.setReturnBindings(carrier, layout);
} }
@ -114,7 +113,7 @@ public class CallArranger {
} }
for (int i = 0; i < mt.parameterCount(); i++) { for (int i = 0; i < mt.parameterCount(); i++) {
csb.addArgumentBindings(mt.parameterType(i), cDesc.argumentLayouts().get(i)); csb.addArgumentBindings(mt.parameterType(i), cDesc.argumentLayouts().get(i), SharedUtils.isVarargsIndex(cDesc, i));
} }
csb.csb.setTrivial(SharedUtils.isTrivial(cDesc)); csb.csb.setTrivial(SharedUtils.isTrivial(cDesc));
@ -134,14 +133,14 @@ public class CallArranger {
return handle; return handle;
} }
public static UpcallHandler arrangeUpcall(MethodHandle target, MethodType mt, FunctionDescriptor cDesc) { public static NativeSymbol arrangeUpcall(MethodHandle target, MethodType mt, FunctionDescriptor cDesc, ResourceScope scope) {
Bindings bindings = getBindings(mt, cDesc, true); Bindings bindings = getBindings(mt, cDesc, true);
if (bindings.isInMemoryReturn) { if (bindings.isInMemoryReturn) {
target = SharedUtils.adaptUpcallForIMR(target, false /* need the return value as well */); target = SharedUtils.adaptUpcallForIMR(target, false /* need the return value as well */);
} }
return ProgrammableUpcallHandler.make(CWindows, target, bindings.callingSequence); return ProgrammableUpcallHandler.make(CWindows, target, bindings.callingSequence, scope);
} }
private static boolean isInMemoryReturn(Optional<MemoryLayout> returnLayout) { private static boolean isInMemoryReturn(Optional<MemoryLayout> returnLayout) {
@ -185,7 +184,7 @@ public class CallArranger {
} }
private interface BindingCalculator { private interface BindingCalculator {
List<Binding> getBindings(Class<?> carrier, MemoryLayout layout); List<Binding> getBindings(Class<?> carrier, MemoryLayout layout, boolean isVararg);
} }
static class UnboxBindingCalculator implements BindingCalculator { static class UnboxBindingCalculator implements BindingCalculator {
@ -196,8 +195,8 @@ public class CallArranger {
} }
@Override @Override
public List<Binding> getBindings(Class<?> carrier, MemoryLayout layout) { public List<Binding> getBindings(Class<?> carrier, MemoryLayout layout, boolean isVararg) {
TypeClass argumentClass = TypeClass.typeClassFor(layout); TypeClass argumentClass = TypeClass.typeClassFor(layout, isVararg);
Binding.Builder bindings = Binding.builder(); Binding.Builder bindings = Binding.builder();
switch (argumentClass) { switch (argumentClass) {
case STRUCT_REGISTER: { case STRUCT_REGISTER: {
@ -211,14 +210,13 @@ public class CallArranger {
case STRUCT_REFERENCE: { case STRUCT_REFERENCE: {
assert carrier == MemorySegment.class; assert carrier == MemorySegment.class;
bindings.copy(layout) bindings.copy(layout)
.baseAddress() .unboxAddress(MemorySegment.class);
.unboxAddress();
VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER, layout); VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER, layout);
bindings.vmStore(storage, long.class); bindings.vmStore(storage, long.class);
break; break;
} }
case POINTER: { case POINTER: {
bindings.unboxAddress(); bindings.unboxAddress(carrier);
VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER, layout); VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER, layout);
bindings.vmStore(storage, long.class); bindings.vmStore(storage, long.class);
break; break;
@ -259,8 +257,8 @@ public class CallArranger {
} }
@Override @Override
public List<Binding> getBindings(Class<?> carrier, MemoryLayout layout) { public List<Binding> getBindings(Class<?> carrier, MemoryLayout layout, boolean isVararg) {
TypeClass argumentClass = TypeClass.typeClassFor(layout); TypeClass argumentClass = TypeClass.typeClassFor(layout, isVararg);
Binding.Builder bindings = Binding.builder(); Binding.Builder bindings = Binding.builder();
switch (argumentClass) { switch (argumentClass) {
case STRUCT_REGISTER: { case STRUCT_REGISTER: {

View file

@ -25,11 +25,9 @@
package jdk.internal.foreign.abi.x64.windows; package jdk.internal.foreign.abi.x64.windows;
import jdk.incubator.foreign.GroupLayout; import jdk.incubator.foreign.GroupLayout;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.ValueLayout; import jdk.incubator.foreign.ValueLayout;
import jdk.internal.foreign.PlatformLayouts;
import static jdk.internal.foreign.PlatformLayouts.Win64.VARARGS_ATTRIBUTE_NAME;
enum TypeClass { enum TypeClass {
STRUCT_REGISTER, STRUCT_REGISTER,
@ -39,7 +37,7 @@ enum TypeClass {
FLOAT, FLOAT,
VARARG_FLOAT; VARARG_FLOAT;
private static TypeClass classifyValueType(ValueLayout type) { private static TypeClass classifyValueType(ValueLayout type, boolean isVararg) {
// No 128 bit integers in the Windows C ABI. There are __m128(i|d) intrinsic types but they act just // No 128 bit integers in the Windows C ABI. There are __m128(i|d) intrinsic types but they act just
// like a struct when passing as an argument (passed by pointer). // like a struct when passing as an argument (passed by pointer).
// https://docs.microsoft.com/en-us/cpp/cpp/m128?view=vs-2019 // https://docs.microsoft.com/en-us/cpp/cpp/m128?view=vs-2019
@ -49,17 +47,21 @@ enum TypeClass {
// but must be considered volatile across function calls." // but must be considered volatile across function calls."
// https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=vs-2019 // https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=vs-2019
return switch (PlatformLayouts.getKind(type)) { Class<?> carrier = type.carrier();
case CHAR, SHORT, INT, LONG, LONG_LONG -> INTEGER; if (carrier == boolean.class || carrier == byte.class || carrier == char.class ||
case POINTER -> POINTER; carrier == short.class || carrier == int.class || carrier == long.class) {
case FLOAT, DOUBLE -> { return INTEGER;
if (type.attribute(VARARGS_ATTRIBUTE_NAME) } else if (carrier == float.class || carrier == double.class) {
.map(Boolean.class::cast).orElse(false)) { if (isVararg) {
yield VARARG_FLOAT; return VARARG_FLOAT;
} } else {
yield FLOAT; return FLOAT;
} }
}; } else if (carrier == MemoryAddress.class) {
return POINTER;
} else {
throw new IllegalStateException("Cannot get here: " + carrier.getName());
}
} }
static boolean isRegisterAggregate(MemoryLayout type) { static boolean isRegisterAggregate(MemoryLayout type) {
@ -77,9 +79,9 @@ enum TypeClass {
return STRUCT_REFERENCE; return STRUCT_REFERENCE;
} }
static TypeClass typeClassFor(MemoryLayout type) { static TypeClass typeClassFor(MemoryLayout type, boolean isVararg) {
if (type instanceof ValueLayout) { if (type instanceof ValueLayout) {
return classifyValueType((ValueLayout) type); return classifyValueType((ValueLayout) type, isVararg);
} else if (type instanceof GroupLayout) { } else if (type instanceof GroupLayout) {
return classifyStructType(type); return classifyStructType(type);
} else { } else {

View file

@ -26,7 +26,7 @@
package jdk.internal.foreign.abi.x64.windows; package jdk.internal.foreign.abi.x64.windows;
import jdk.incubator.foreign.*; import jdk.incubator.foreign.*;
import jdk.incubator.foreign.CLinker.VaList; import jdk.internal.foreign.Scoped;
import jdk.internal.foreign.ResourceScopeImpl; import jdk.internal.foreign.ResourceScopeImpl;
import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.foreign.abi.SharedUtils.SimpleVaArg; import jdk.internal.foreign.abi.SharedUtils.SimpleVaArg;
@ -55,10 +55,10 @@ import static jdk.internal.foreign.PlatformLayouts.Win64.C_POINTER;
// ? **(t**)((ap += sizeof(__int64)) - sizeof(__int64)) \ // ? **(t**)((ap += sizeof(__int64)) - sizeof(__int64)) \
// : *(t* )((ap += sizeof(__int64)) - sizeof(__int64))) // : *(t* )((ap += sizeof(__int64)) - sizeof(__int64)))
// //
public non-sealed class WinVaList implements VaList { public non-sealed class WinVaList implements VaList, Scoped {
public static final Class<?> CARRIER = MemoryAddress.class; public static final Class<?> CARRIER = MemoryAddress.class;
private static final long VA_SLOT_SIZE_BYTES = 8; private static final long VA_SLOT_SIZE_BYTES = 8;
private static final VarHandle VH_address = MemoryHandles.asAddressVarHandle(C_POINTER.varHandle(long.class)); private static final VarHandle VH_address = C_POINTER.varHandle();
private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemoryAddress.NULL); private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemoryAddress.NULL);
@ -75,63 +75,54 @@ public non-sealed class WinVaList implements VaList {
} }
@Override @Override
public int vargAsInt(MemoryLayout layout) { public int nextVarg(ValueLayout.OfInt layout) {
return (int) read(int.class, layout); return (int) read(int.class, layout);
} }
@Override @Override
public long vargAsLong(MemoryLayout layout) { public long nextVarg(ValueLayout.OfLong layout) {
return (long) read(long.class, layout); return (long) read(long.class, layout);
} }
@Override @Override
public double vargAsDouble(MemoryLayout layout) { public double nextVarg(ValueLayout.OfDouble layout) {
return (double) read(double.class, layout); return (double) read(double.class, layout);
} }
@Override @Override
public MemoryAddress vargAsAddress(MemoryLayout layout) { public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
return (MemoryAddress) read(MemoryAddress.class, layout); return (MemoryAddress) read(MemoryAddress.class, layout);
} }
@Override @Override
public MemorySegment vargAsSegment(MemoryLayout layout, SegmentAllocator allocator) { public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(allocator); Objects.requireNonNull(allocator);
return (MemorySegment) read(MemorySegment.class, layout, allocator); return (MemorySegment) read(MemorySegment.class, layout, allocator);
} }
@Override
public MemorySegment vargAsSegment(MemoryLayout layout, ResourceScope scope) {
return vargAsSegment(layout, SegmentAllocator.ofScope(scope));
}
private Object read(Class<?> carrier, MemoryLayout layout) { private Object read(Class<?> carrier, MemoryLayout layout) {
return read(carrier, layout, SharedUtils.THROWING_ALLOCATOR); return read(carrier, layout, SharedUtils.THROWING_ALLOCATOR);
} }
private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) { private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
SharedUtils.checkCompatibleType(carrier, layout, Windowsx64Linker.ADDRESS_SIZE);
Object res; Object res;
if (carrier == MemorySegment.class) { if (carrier == MemorySegment.class) {
TypeClass typeClass = TypeClass.typeClassFor(layout); TypeClass typeClass = TypeClass.typeClassFor(layout, false);
res = switch (typeClass) { res = switch (typeClass) {
case STRUCT_REFERENCE -> { case STRUCT_REFERENCE -> {
MemoryAddress structAddr = (MemoryAddress) VH_address.get(segment); MemoryAddress structAddr = (MemoryAddress) VH_address.get(segment);
MemorySegment struct = structAddr.asSegment(layout.byteSize(), scope()); MemorySegment struct = MemorySegment.ofAddress(structAddr, layout.byteSize(), scope());
MemorySegment seg = allocator.allocate(layout); MemorySegment seg = allocator.allocate(layout);
seg.copyFrom(struct); seg.copyFrom(struct);
yield seg; yield seg;
} }
case STRUCT_REGISTER -> { case STRUCT_REGISTER ->
MemorySegment struct = allocator.allocate(layout); allocator.allocate(layout).copyFrom(segment.asSlice(0, layout.byteSize()));
struct.copyFrom(segment.asSlice(0L, layout.byteSize()));
yield struct;
}
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass); default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
}; };
} else { } else {
VarHandle reader = SharedUtils.vhPrimitiveOrAddress(carrier, layout); VarHandle reader = layout.varHandle();
res = reader.get(segment); res = reader.get(segment);
} }
segment = segment.asSlice(VA_SLOT_SIZE_BYTES); segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
@ -141,12 +132,13 @@ public non-sealed class WinVaList implements VaList {
@Override @Override
public void skip(MemoryLayout... layouts) { public void skip(MemoryLayout... layouts) {
Objects.requireNonNull(layouts); Objects.requireNonNull(layouts);
((ResourceScopeImpl)scope).checkValidStateSlow();
Stream.of(layouts).forEach(Objects::requireNonNull); Stream.of(layouts).forEach(Objects::requireNonNull);
segment = segment.asSlice(layouts.length * VA_SLOT_SIZE_BYTES); segment = segment.asSlice(layouts.length * VA_SLOT_SIZE_BYTES);
} }
static WinVaList ofAddress(MemoryAddress addr, ResourceScope scope) { static WinVaList ofAddress(MemoryAddress addr, ResourceScope scope) {
MemorySegment segment = addr.asSegment(Long.MAX_VALUE, scope); MemorySegment segment = MemorySegment.ofAddress(addr, Long.MAX_VALUE, scope);
return new WinVaList(segment, scope); return new WinVaList(segment, scope);
} }
@ -183,33 +175,32 @@ public non-sealed class WinVaList implements VaList {
private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) { private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
Objects.requireNonNull(value); Objects.requireNonNull(value);
SharedUtils.checkCompatibleType(carrier, layout, Windowsx64Linker.ADDRESS_SIZE);
args.add(new SimpleVaArg(carrier, layout, value)); args.add(new SimpleVaArg(carrier, layout, value));
return this; return this;
} }
@Override @Override
public Builder vargFromInt(ValueLayout layout, int value) { public Builder addVarg(ValueLayout.OfInt layout, int value) {
return arg(int.class, layout, value); return arg(int.class, layout, value);
} }
@Override @Override
public Builder vargFromLong(ValueLayout layout, long value) { public Builder addVarg(ValueLayout.OfLong layout, long value) {
return arg(long.class, layout, value); return arg(long.class, layout, value);
} }
@Override @Override
public Builder vargFromDouble(ValueLayout layout, double value) { public Builder addVarg(ValueLayout.OfDouble layout, double value) {
return arg(double.class, layout, value); return arg(double.class, layout, value);
} }
@Override @Override
public Builder vargFromAddress(ValueLayout layout, Addressable value) { public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) {
return arg(MemoryAddress.class, layout, value.address()); return arg(MemoryAddress.class, layout, value.address());
} }
@Override @Override
public Builder vargFromSegment(GroupLayout layout, MemorySegment value) { public Builder addVarg(GroupLayout layout, MemorySegment value) {
return arg(MemorySegment.class, layout, value); return arg(MemorySegment.class, layout, value);
} }
@ -217,7 +208,7 @@ public non-sealed class WinVaList implements VaList {
if (args.isEmpty()) { if (args.isEmpty()) {
return EMPTY; return EMPTY;
} }
SegmentAllocator allocator = SegmentAllocator.arenaAllocator(scope); SegmentAllocator allocator = SegmentAllocator.newNativeArena(scope);
MemorySegment segment = allocator.allocate(VA_SLOT_SIZE_BYTES * args.size()); MemorySegment segment = allocator.allocate(VA_SLOT_SIZE_BYTES * args.size());
List<MemorySegment> attachedSegments = new ArrayList<>(); List<MemorySegment> attachedSegments = new ArrayList<>();
attachedSegments.add(segment); attachedSegments.add(segment);
@ -226,7 +217,7 @@ public non-sealed class WinVaList implements VaList {
for (SimpleVaArg arg : args) { for (SimpleVaArg arg : args) {
if (arg.carrier == MemorySegment.class) { if (arg.carrier == MemorySegment.class) {
MemorySegment msArg = ((MemorySegment) arg.value); MemorySegment msArg = ((MemorySegment) arg.value);
TypeClass typeClass = TypeClass.typeClassFor(arg.layout); TypeClass typeClass = TypeClass.typeClassFor(arg.layout, false);
switch (typeClass) { switch (typeClass) {
case STRUCT_REFERENCE -> { case STRUCT_REFERENCE -> {
MemorySegment copy = allocator.allocate(arg.layout); MemorySegment copy = allocator.allocate(arg.layout);
@ -234,10 +225,8 @@ public non-sealed class WinVaList implements VaList {
attachedSegments.add(copy); attachedSegments.add(copy);
VH_address.set(cursor, copy.address()); VH_address.set(cursor, copy.address());
} }
case STRUCT_REGISTER -> { case STRUCT_REGISTER ->
MemorySegment slice = cursor.asSlice(0, VA_SLOT_SIZE_BYTES); cursor.copyFrom(msArg.asSlice(0, VA_SLOT_SIZE_BYTES));
slice.copyFrom(msArg);
}
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass); default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
} }
} else { } else {

View file

@ -24,14 +24,14 @@
*/ */
package jdk.internal.foreign.abi.x64.windows; package jdk.internal.foreign.abi.x64.windows;
import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.FunctionDescriptor; import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.NativeSymbol;
import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.ResourceScope;
import jdk.internal.foreign.AbstractCLinker; import jdk.incubator.foreign.VaList;
import jdk.internal.foreign.ResourceScopeImpl;
import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.foreign.abi.UpcallStubs;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
@ -42,7 +42,7 @@ import java.util.function.Consumer;
/** /**
* ABI implementation based on Windows ABI AMD64 supplement v.0.99.6 * ABI implementation based on Windows ABI AMD64 supplement v.0.99.6
*/ */
public final class Windowsx64Linker extends AbstractCLinker { public final class Windowsx64Linker implements CLinker {
public static final int MAX_INTEGER_ARGUMENT_REGISTERS = 4; public static final int MAX_INTEGER_ARGUMENT_REGISTERS = 4;
public static final int MAX_INTEGER_RETURN_REGISTERS = 1; public static final int MAX_INTEGER_RETURN_REGISTERS = 1;
@ -55,21 +55,6 @@ public final class Windowsx64Linker extends AbstractCLinker {
static final long ADDRESS_SIZE = 64; // bits static final long ADDRESS_SIZE = 64; // bits
private static final MethodHandle MH_unboxVaList;
private static final MethodHandle MH_boxVaList;
static {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MH_unboxVaList = lookup.findVirtual(VaList.class, "address",
MethodType.methodType(MemoryAddress.class));
MH_boxVaList = MethodHandles.insertArguments(lookup.findStatic(Windowsx64Linker.class, "newVaListOfAddress",
MethodType.methodType(VaList.class, MemoryAddress.class, ResourceScope.class)), 1, ResourceScope.globalScope());
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}
public static Windowsx64Linker getInstance() { public static Windowsx64Linker getInstance() {
if (instance == null) { if (instance == null) {
instance = new Windowsx64Linker(); instance = new Windowsx64Linker();
@ -84,26 +69,28 @@ public final class Windowsx64Linker extends AbstractCLinker {
} }
@Override @Override
public final MethodHandle downcallHandle(MethodType type, FunctionDescriptor function) { public final MethodHandle downcallHandle(FunctionDescriptor function) {
Objects.requireNonNull(type);
Objects.requireNonNull(function); Objects.requireNonNull(function);
MethodType llMt = SharedUtils.convertVaListCarriers(type, WinVaList.CARRIER); MethodType type = SharedUtils.inferMethodType(function, false);
MethodHandle handle = CallArranger.arrangeDowncall(llMt, function); MethodHandle handle = CallArranger.arrangeDowncall(type, function);
if (!type.returnType().equals(MemorySegment.class)) { if (!type.returnType().equals(MemorySegment.class)) {
// not returning segment, just insert a throwing allocator // not returning segment, just insert a throwing allocator
handle = MethodHandles.insertArguments(handle, 1, SharedUtils.THROWING_ALLOCATOR); handle = MethodHandles.insertArguments(handle, 1, SharedUtils.THROWING_ALLOCATOR);
} }
handle = SharedUtils.unboxVaLists(type, handle, MH_unboxVaList); return SharedUtils.wrapDowncall(handle, function);
return handle;
} }
@Override @Override
public final MemoryAddress upcallStub(MethodHandle target, FunctionDescriptor function, ResourceScope scope) { public final NativeSymbol upcallStub(MethodHandle target, FunctionDescriptor function, ResourceScope scope) {
Objects.requireNonNull(scope); Objects.requireNonNull(scope);
Objects.requireNonNull(target); Objects.requireNonNull(target);
Objects.requireNonNull(function); Objects.requireNonNull(function);
target = SharedUtils.boxVaLists(target, MH_boxVaList); SharedUtils.checkExceptions(target);
return UpcallStubs.upcallAddress(CallArranger.arrangeUpcall(target, target.type(), function), (ResourceScopeImpl) scope); MethodType type = SharedUtils.inferMethodType(function, true);
if (!type.equals(target.type())) {
throw new IllegalArgumentException("Wrong method handle type: " + target.type());
}
return CallArranger.arrangeUpcall(target, target.type(), function, scope);
} }
public static VaList newVaListOfAddress(MemoryAddress ma, ResourceScope scope) { public static VaList newVaListOfAddress(MemoryAddress ma, ResourceScope scope) {

View file

@ -40,22 +40,19 @@
import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.FunctionDescriptor; import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.SymbolLookup; import jdk.incubator.foreign.SymbolLookup;
import jdk.incubator.foreign.ValueLayout;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import static jdk.incubator.foreign.CLinker.C_INT;
public class TestLinkToNativeRBP { public class TestLinkToNativeRBP {
static { static {
System.loadLibrary("LinkToNativeRBP"); System.loadLibrary("LinkToNativeRBP");
} }
final static CLinker abi = CLinker.getInstance(); final static CLinker abi = CLinker.systemCLinker();
static final SymbolLookup lookup = SymbolLookup.loaderLookup(); static final SymbolLookup lookup = SymbolLookup.loaderLookup();
final static MethodHandle foo = abi.downcallHandle(lookup.lookup("foo").get(), final static MethodHandle foo = abi.downcallHandle(lookup.lookup("foo").get(),
MethodType.methodType(int.class), FunctionDescriptor.of(ValueLayout.JAVA_INT));
FunctionDescriptor.of(C_INT));
static int foo() throws Throwable { static int foo() throws Throwable {
return (int)foo.invokeExact(); return (int)foo.invokeExact();

View file

@ -22,14 +22,19 @@
* *
*/ */
import jdk.incubator.foreign.Addressable;
import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.GroupLayout; import jdk.incubator.foreign.GroupLayout;
import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.NativeSymbol;
import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.ResourceScope;
import jdk.incubator.foreign.SegmentAllocator; import jdk.incubator.foreign.SegmentAllocator;
import jdk.incubator.foreign.ValueLayout; import jdk.incubator.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.VarHandle; import java.lang.invoke.VarHandle;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -40,12 +45,13 @@ import java.util.stream.IntStream;
import org.testng.annotations.*; import org.testng.annotations.*;
import static jdk.incubator.foreign.CLinker.*;
import static org.testng.Assert.*; import static org.testng.Assert.*;
public class CallGeneratorHelper extends NativeTestHelper { public class CallGeneratorHelper extends NativeTestHelper {
static SegmentAllocator IMPLICIT_ALLOCATOR = (size, align) -> MemorySegment.allocateNative(size, align, ResourceScope.newImplicitScope()); static SegmentAllocator THROWING_ALLOCATOR = (size, align) -> {
throw new UnsupportedOperationException();
};
static final int SAMPLE_FACTOR = Integer.parseInt((String)System.getProperties().getOrDefault("generator.sample.factor", "-1")); static final int SAMPLE_FACTOR = Integer.parseInt((String)System.getProperties().getOrDefault("generator.sample.factor", "-1"));
@ -58,7 +64,7 @@ public class CallGeneratorHelper extends NativeTestHelper {
GroupLayout g = (GroupLayout) layout; GroupLayout g = (GroupLayout) layout;
for (MemoryLayout field : g.memberLayouts()) { for (MemoryLayout field : g.memberLayouts()) {
if (field instanceof ValueLayout) { if (field instanceof ValueLayout) {
VarHandle vh = g.varHandle(vhCarrier(field), MemoryLayout.PathElement.groupElement(field.name().orElseThrow())); VarHandle vh = g.varHandle(MemoryLayout.PathElement.groupElement(field.name().orElseThrow()));
assertEquals(vh.get(actual), vh.get(expected)); assertEquals(vh.get(actual), vh.get(expected));
} }
} }
@ -410,12 +416,9 @@ public class CallGeneratorHelper extends NativeTestHelper {
static void initStruct(MemorySegment str, GroupLayout g, List<Consumer<Object>> checks, boolean check) throws ReflectiveOperationException { static void initStruct(MemorySegment str, GroupLayout g, List<Consumer<Object>> checks, boolean check) throws ReflectiveOperationException {
for (MemoryLayout l : g.memberLayouts()) { for (MemoryLayout l : g.memberLayouts()) {
if (l.isPadding()) continue; if (l.isPadding()) continue;
VarHandle accessor = g.varHandle(structFieldCarrier(l), MemoryLayout.PathElement.groupElement(l.name().get())); VarHandle accessor = g.varHandle(MemoryLayout.PathElement.groupElement(l.name().get()));
List<Consumer<Object>> fieldsCheck = new ArrayList<>(); List<Consumer<Object>> fieldsCheck = new ArrayList<>();
Object value = makeArg(l, fieldsCheck, check); Object value = makeArg(l, fieldsCheck, check);
if (isPointer(l)) {
value = ((MemoryAddress)value).toRawLongValue();
}
//set value //set value
accessor.set(str, value); accessor.set(str, value);
//add check //add check
@ -424,11 +427,7 @@ public class CallGeneratorHelper extends NativeTestHelper {
checks.add(o -> { checks.add(o -> {
MemorySegment actual = (MemorySegment)o; MemorySegment actual = (MemorySegment)o;
try { try {
if (isPointer(l)) { fieldsCheck.get(0).accept(accessor.get(actual));
fieldsCheck.get(0).accept(MemoryAddress.ofLong((long)accessor.get(actual)));
} else {
fieldsCheck.get(0).accept(accessor.get(actual));
}
} catch (Throwable ex) { } catch (Throwable ex) {
throw new IllegalStateException(ex); throw new IllegalStateException(ex);
} }
@ -437,37 +436,23 @@ public class CallGeneratorHelper extends NativeTestHelper {
} }
} }
static Class<?> structFieldCarrier(MemoryLayout layout) { static Class<?> carrier(MemoryLayout layout, boolean param) {
if (isPointer(layout)) { if (layout instanceof GroupLayout) {
return long.class; return MemorySegment.class;
} else if (layout instanceof ValueLayout) { } if (isPointer(layout)) {
if (isIntegral(layout)) { return param ? Addressable.class : MemoryAddress.class;
return int.class; } else if (layout instanceof ValueLayout valueLayout) {
} else if (layout.bitSize() == 32) { return valueLayout.carrier();
return float.class;
} else {
return double.class;
}
} else { } else {
throw new IllegalStateException("Unexpected layout: " + layout); throw new IllegalStateException("Unexpected layout: " + layout);
} }
} }
static Class<?> paramCarrier(MemoryLayout layout) { MethodHandle downcallHandle(CLinker abi, NativeSymbol symbol, SegmentAllocator allocator, FunctionDescriptor descriptor) {
if (layout instanceof GroupLayout) { MethodHandle mh = abi.downcallHandle(symbol, descriptor);
return MemorySegment.class; if (descriptor.returnLayout().isPresent() && descriptor.returnLayout().get() instanceof GroupLayout) {
} if (isPointer(layout)) { mh = mh.bindTo(allocator);
return MemoryAddress.class;
} else if (layout instanceof ValueLayout) {
if (isIntegral(layout)) {
return int.class;
} else if (layout.bitSize() == 32) {
return float.class;
} else {
return double.class;
}
} else {
throw new IllegalStateException("Unexpected layout: " + layout);
} }
return mh;
} }
} }

View file

@ -22,58 +22,90 @@
* *
*/ */
import jdk.incubator.foreign.Addressable;
import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.ResourceScope;
import jdk.incubator.foreign.SegmentAllocator; import jdk.incubator.foreign.SegmentAllocator;
import jdk.incubator.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;
public class NativeTestHelper { public class NativeTestHelper {
static CLinker.TypeKind kind(MemoryLayout layout) { public static boolean isIntegral(MemoryLayout layout) {
return (CLinker.TypeKind)layout.attribute(CLinker.TypeKind.ATTR_NAME).orElseThrow( return layout instanceof ValueLayout valueLayout && isIntegral(valueLayout.carrier());
() -> new IllegalStateException("Unexpected value layout: could not determine ABI class"));
} }
public static boolean isIntegral(MemoryLayout layout) { static boolean isIntegral(Class<?> clazz) {
return kind(layout).isIntegral(); return clazz == byte.class || clazz == char.class || clazz == short.class
|| clazz == int.class || clazz == long.class;
} }
public static boolean isPointer(MemoryLayout layout) { public static boolean isPointer(MemoryLayout layout) {
return kind(layout).isPointer(); return layout instanceof ValueLayout valueLayout && valueLayout.carrier() == MemoryAddress.class;
} }
public static class NativeScope implements SegmentAllocator, AutoCloseable { // the constants below are useful aliases for C types. The type/carrier association is only valid for 64-bit platforms.
final ResourceScope resourceScope;
final ResourceScope.Handle scopeHandle;
final SegmentAllocator allocator;
long allocatedBytes = 0; /**
* The layout for the {@code bool} C type
*/
public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN;
/**
* The layout for the {@code char} C type
*/
public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE;
/**
* The layout for the {@code short} C type
*/
public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT.withBitAlignment(16);
/**
* The layout for the {@code int} C type
*/
public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT.withBitAlignment(32);
public NativeScope() { /**
this.resourceScope = ResourceScope.newConfinedScope(); * The layout for the {@code long long} C type.
this.scopeHandle = resourceScope.acquire(); */
this.allocator = SegmentAllocator.arenaAllocator(resourceScope); public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG.withBitAlignment(64);
/**
* The layout for the {@code float} C type
*/
public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT.withBitAlignment(32);
/**
* The layout for the {@code double} C type
*/
public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE.withBitAlignment(64);
/**
* The {@code T*} native type.
*/
public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64);
private static CLinker LINKER = CLinker.systemCLinker();
private static final MethodHandle FREE = LINKER.downcallHandle(
LINKER.lookup("free").get(), FunctionDescriptor.ofVoid(ValueLayout.ADDRESS));
private static final MethodHandle MALLOC = LINKER.downcallHandle(
LINKER.lookup("malloc").get(), FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.JAVA_LONG));
public static void freeMemory(Addressable address) {
try {
FREE.invokeExact(address);
} catch (Throwable ex) {
throw new IllegalStateException(ex);
} }
}
@Override public static MemoryAddress allocateMemory(long size) {
public MemorySegment allocate(long bytesSize, long bytesAlignment) { try {
allocatedBytes += bytesSize; return (MemoryAddress)MALLOC.invokeExact(size);
return allocator.allocate(bytesSize, bytesAlignment); } catch (Throwable ex) {
} throw new IllegalStateException(ex);
public ResourceScope scope() {
return resourceScope;
}
public long allocatedBytes() {
return allocatedBytes;
}
@Override
public void close() {
resourceScope.release(scopeHandle);
resourceScope.close();
} }
} }
} }

View file

@ -27,27 +27,31 @@
* @run testng/othervm --enable-native-access=ALL-UNNAMED SafeFunctionAccessTest * @run testng/othervm --enable-native-access=ALL-UNNAMED SafeFunctionAccessTest
*/ */
import jdk.incubator.foreign.Addressable;
import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.FunctionDescriptor; import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.NativeSymbol;
import jdk.incubator.foreign.SymbolLookup; import jdk.incubator.foreign.SymbolLookup;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.ResourceScope;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import jdk.incubator.foreign.VaList;
import org.testng.annotations.*; import org.testng.annotations.*;
import static org.testng.Assert.*; import static org.testng.Assert.*;
public class SafeFunctionAccessTest { public class SafeFunctionAccessTest extends NativeTestHelper {
static { static {
System.loadLibrary("SafeAccess"); System.loadLibrary("SafeAccess");
} }
static MemoryLayout POINT = MemoryLayout.structLayout( static MemoryLayout POINT = MemoryLayout.structLayout(
CLinker.C_INT, CLinker.C_INT C_INT, C_INT
); );
static final SymbolLookup LOOKUP = SymbolLookup.loaderLookup(); static final SymbolLookup LOOKUP = SymbolLookup.loaderLookup();
@ -59,26 +63,135 @@ public class SafeFunctionAccessTest {
segment = MemorySegment.allocateNative(POINT, scope); segment = MemorySegment.allocateNative(POINT, scope);
} }
assertFalse(segment.scope().isAlive()); assertFalse(segment.scope().isAlive());
MethodHandle handle = CLinker.getInstance().downcallHandle( MethodHandle handle = CLinker.systemCLinker().downcallHandle(
LOOKUP.lookup("struct_func").get(), LOOKUP.lookup("struct_func").get(),
MethodType.methodType(void.class, MemorySegment.class),
FunctionDescriptor.ofVoid(POINT)); FunctionDescriptor.ofVoid(POINT));
handle.invokeExact(segment); handle.invokeExact(segment);
} }
@Test(expectedExceptions = IllegalStateException.class) @Test
public void testClosedPointer() throws Throwable { public void testClosedStructAddr_6() throws Throwable {
MemoryAddress address; MethodHandle handle = CLinker.systemCLinker().downcallHandle(
try (ResourceScope scope = ResourceScope.newConfinedScope()) { LOOKUP.lookup("addr_func_6").get(),
address = MemorySegment.allocateNative(POINT, scope).address(); FunctionDescriptor.ofVoid(C_POINTER, C_POINTER, C_POINTER, C_POINTER, C_POINTER, C_POINTER));
for (int i = 0 ; i < 6 ; i++) {
MemorySegment[] segments = new MemorySegment[]{
MemorySegment.allocateNative(POINT, ResourceScope.newImplicitScope()),
MemorySegment.allocateNative(POINT, ResourceScope.newImplicitScope()),
MemorySegment.allocateNative(POINT, ResourceScope.newImplicitScope()),
MemorySegment.allocateNative(POINT, ResourceScope.newImplicitScope()),
MemorySegment.allocateNative(POINT, ResourceScope.newImplicitScope()),
MemorySegment.allocateNative(POINT, ResourceScope.newImplicitScope())
};
// check liveness
segments[i].scope().close();
for (int j = 0 ; j < 6 ; j++) {
if (i == j) {
assertFalse(segments[j].scope().isAlive());
} else {
assertTrue(segments[j].scope().isAlive());
}
}
try {
handle.invokeWithArguments(segments);
fail();
} catch (IllegalStateException ex) {
assertTrue(ex.getMessage().contains("Already closed"));
}
for (int j = 0 ; j < 6 ; j++) {
if (i != j) {
segments[j].scope().close(); // should succeed!
}
}
} }
assertFalse(address.scope().isAlive()); }
MethodHandle handle = CLinker.getInstance().downcallHandle(
LOOKUP.lookup("addr_func").get(),
MethodType.methodType(void.class, MemoryAddress.class),
FunctionDescriptor.ofVoid(CLinker.C_POINTER));
handle.invokeExact(address); @Test(expectedExceptions = IllegalStateException.class)
public void testClosedVaList() throws Throwable {
VaList list;
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
list = VaList.make(b -> b.addVarg(C_INT, 42), scope);
}
assertFalse(list.scope().isAlive());
MethodHandle handle = CLinker.systemCLinker().downcallHandle(
LOOKUP.lookup("addr_func").get(),
FunctionDescriptor.ofVoid(C_POINTER));
handle.invokeExact((Addressable)list);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testClosedUpcall() throws Throwable {
NativeSymbol upcall;
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MethodHandle dummy = MethodHandles.lookup().findStatic(SafeFunctionAccessTest.class, "dummy", MethodType.methodType(void.class));
upcall = CLinker.systemCLinker().upcallStub(dummy, FunctionDescriptor.ofVoid(), scope);
}
assertFalse(upcall.scope().isAlive());
MethodHandle handle = CLinker.systemCLinker().downcallHandle(
LOOKUP.lookup("addr_func").get(),
FunctionDescriptor.ofVoid(C_POINTER));
handle.invokeExact((Addressable)upcall);
}
static void dummy() { }
@Test
public void testClosedVaListCallback() throws Throwable {
MethodHandle handle = CLinker.systemCLinker().downcallHandle(
LOOKUP.lookup("addr_func_cb").get(),
FunctionDescriptor.ofVoid(C_POINTER, C_POINTER));
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
VaList list = VaList.make(b -> b.addVarg(C_INT, 42), scope);
handle.invoke(list, scopeChecker(scope));
}
}
@Test
public void testClosedStructCallback() throws Throwable {
MethodHandle handle = CLinker.systemCLinker().downcallHandle(
LOOKUP.lookup("addr_func_cb").get(),
FunctionDescriptor.ofVoid(C_POINTER, C_POINTER));
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment segment = MemorySegment.allocateNative(POINT, scope);
handle.invoke(segment, scopeChecker(scope));
}
}
@Test
public void testClosedUpcallCallback() throws Throwable {
MethodHandle handle = CLinker.systemCLinker().downcallHandle(
LOOKUP.lookup("addr_func_cb").get(),
FunctionDescriptor.ofVoid(C_POINTER, C_POINTER));
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MethodHandle dummy = MethodHandles.lookup().findStatic(SafeFunctionAccessTest.class, "dummy", MethodType.methodType(void.class));
NativeSymbol upcall = CLinker.systemCLinker().upcallStub(dummy, FunctionDescriptor.ofVoid(), scope);
handle.invoke(upcall, scopeChecker(scope));
}
}
NativeSymbol scopeChecker(ResourceScope scope) {
try {
MethodHandle handle = MethodHandles.lookup().findStatic(SafeFunctionAccessTest.class, "checkScope",
MethodType.methodType(void.class, ResourceScope.class));
handle = handle.bindTo(scope);
return CLinker.systemCLinker().upcallStub(handle, FunctionDescriptor.ofVoid(), ResourceScope.newImplicitScope());
} catch (Throwable ex) {
throw new AssertionError(ex);
}
}
static void checkScope(ResourceScope scope) {
try {
scope.close();
fail("Scope closed unexpectedly!");
} catch (IllegalStateException ex) {
assertTrue(ex.getMessage().contains("kept alive")); //if acquired, fine
}
} }
} }

View file

@ -39,7 +39,6 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Function; import java.util.function.Function;
@ -48,17 +47,14 @@ import java.util.stream.Stream;
import jdk.incubator.foreign.*; import jdk.incubator.foreign.*;
import static jdk.incubator.foreign.MemoryAccess.*;
import org.testng.annotations.*; import org.testng.annotations.*;
import static jdk.incubator.foreign.CLinker.*;
import static org.testng.Assert.*; import static org.testng.Assert.*;
@Test @Test
public class StdLibTest { public class StdLibTest extends NativeTestHelper {
final static CLinker abi = CLinker.getInstance(); final static CLinker abi = CLinker.systemCLinker();
private StdLibHelper stdLibHelper = new StdLibHelper(); private StdLibHelper stdLibHelper = new StdLibHelper();
@ -155,45 +151,36 @@ public class StdLibTest {
static class StdLibHelper { static class StdLibHelper {
static final SymbolLookup LOOKUP = CLinker.systemLookup(); final static MethodHandle strcat = abi.downcallHandle(abi.lookup("strcat").get(),
FunctionDescriptor.of(C_POINTER, C_POINTER, C_POINTER))
.asType(MethodType.methodType(MemoryAddress.class, MemorySegment.class, MemorySegment.class)); // exact signature match
final static MethodHandle strcat = abi.downcallHandle(LOOKUP.lookup("strcat").get(), final static MethodHandle strcmp = abi.downcallHandle(abi.lookup("strcmp").get(),
MethodType.methodType(MemoryAddress.class, MemoryAddress.class, MemoryAddress.class),
FunctionDescriptor.of(C_POINTER, C_POINTER, C_POINTER));
final static MethodHandle strcmp = abi.downcallHandle(LOOKUP.lookup("strcmp").get(),
MethodType.methodType(int.class, MemoryAddress.class, MemoryAddress.class),
FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER)); FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER));
final static MethodHandle puts = abi.downcallHandle(LOOKUP.lookup("puts").get(), final static MethodHandle puts = abi.downcallHandle(abi.lookup("puts").get(),
MethodType.methodType(int.class, MemoryAddress.class),
FunctionDescriptor.of(C_INT, C_POINTER)); FunctionDescriptor.of(C_INT, C_POINTER));
final static MethodHandle strlen = abi.downcallHandle(LOOKUP.lookup("strlen").get(), final static MethodHandle strlen = abi.downcallHandle(abi.lookup("strlen").get(),
MethodType.methodType(int.class, MemoryAddress.class),
FunctionDescriptor.of(C_INT, C_POINTER)); FunctionDescriptor.of(C_INT, C_POINTER));
final static MethodHandle gmtime = abi.downcallHandle(LOOKUP.lookup("gmtime").get(), final static MethodHandle gmtime = abi.downcallHandle(abi.lookup("gmtime").get(),
MethodType.methodType(MemoryAddress.class, MemoryAddress.class),
FunctionDescriptor.of(C_POINTER, C_POINTER)); FunctionDescriptor.of(C_POINTER, C_POINTER));
final static MethodHandle qsort = abi.downcallHandle(LOOKUP.lookup("qsort").get(), final static MethodHandle qsort = abi.downcallHandle(abi.lookup("qsort").get(),
MethodType.methodType(void.class, MemoryAddress.class, long.class, long.class, MemoryAddress.class),
FunctionDescriptor.ofVoid(C_POINTER, C_LONG_LONG, C_LONG_LONG, C_POINTER)); FunctionDescriptor.ofVoid(C_POINTER, C_LONG_LONG, C_LONG_LONG, C_POINTER));
final static FunctionDescriptor qsortComparFunction = FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER); final static FunctionDescriptor qsortComparFunction = FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER);
final static MethodHandle qsortCompar; final static MethodHandle qsortCompar;
final static MethodHandle rand = abi.downcallHandle(LOOKUP.lookup("rand").get(), final static MethodHandle rand = abi.downcallHandle(abi.lookup("rand").get(),
MethodType.methodType(int.class),
FunctionDescriptor.of(C_INT)); FunctionDescriptor.of(C_INT));
final static MethodHandle vprintf = abi.downcallHandle(LOOKUP.lookup("vprintf").get(), final static MethodHandle vprintf = abi.downcallHandle(abi.lookup("vprintf").get(),
MethodType.methodType(int.class, MemoryAddress.class, VaList.class), FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER));
FunctionDescriptor.of(C_INT, C_POINTER, C_VA_LIST));
final static MemoryAddress printfAddr = LOOKUP.lookup("printf").get(); final static NativeSymbol printfAddr = abi.lookup("printf").get();
final static FunctionDescriptor printfBase = FunctionDescriptor.of(C_INT, C_POINTER); final static FunctionDescriptor printfBase = FunctionDescriptor.of(C_INT, C_POINTER);
@ -201,7 +188,7 @@ public class StdLibTest {
try { try {
//qsort upcall handle //qsort upcall handle
qsortCompar = MethodHandles.lookup().findStatic(StdLibTest.StdLibHelper.class, "qsortCompare", qsortCompar = MethodHandles.lookup().findStatic(StdLibTest.StdLibHelper.class, "qsortCompare",
MethodType.methodType(int.class, MemorySegment.class, MemoryAddress.class, MemoryAddress.class)); CLinker.upcallType(qsortComparFunction));
} catch (ReflectiveOperationException ex) { } catch (ReflectiveOperationException ex) {
throw new IllegalStateException(ex); throw new IllegalStateException(ex);
} }
@ -209,44 +196,44 @@ public class StdLibTest {
String strcat(String s1, String s2) throws Throwable { String strcat(String s1, String s2) throws Throwable {
try (ResourceScope scope = ResourceScope.newConfinedScope()) { try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment buf = MemorySegment.allocateNative(s1.length() + s2.length() + 1, scope); var malloc = SegmentAllocator.nativeAllocator(scope);
MemorySegment other = toCString(s2, scope); MemorySegment buf = malloc.allocate(s1.length() + s2.length() + 1);
char[] chars = s1.toCharArray(); buf.setUtf8String(0, s1);
for (long i = 0 ; i < chars.length ; i++) { MemorySegment other = malloc.allocateUtf8String(s2);
setByteAtOffset(buf, i, (byte)chars[(int)i]); return ((MemoryAddress)strcat.invokeExact(buf, other)).getUtf8String(0);
}
setByteAtOffset(buf, chars.length, (byte)'\0');
return toJavaString(((MemoryAddress)strcat.invokeExact(buf.address(), other.address())));
} }
} }
int strcmp(String s1, String s2) throws Throwable { int strcmp(String s1, String s2) throws Throwable {
try (ResourceScope scope = ResourceScope.newConfinedScope()) { try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment ns1 = toCString(s1, scope); var malloc = SegmentAllocator.nativeAllocator(scope);
MemorySegment ns2 = toCString(s2, scope); MemorySegment ns1 = malloc.allocateUtf8String(s1);
return (int)strcmp.invokeExact(ns1.address(), ns2.address()); MemorySegment ns2 = malloc.allocateUtf8String(s2);
return (int)strcmp.invoke(ns1, ns2);
} }
} }
int puts(String msg) throws Throwable { int puts(String msg) throws Throwable {
try (ResourceScope scope = ResourceScope.newConfinedScope()) { try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment s = toCString(msg, scope); var malloc = SegmentAllocator.nativeAllocator(scope);
return (int)puts.invokeExact(s.address()); MemorySegment s = malloc.allocateUtf8String(msg);
return (int)puts.invoke(s);
} }
} }
int strlen(String msg) throws Throwable { int strlen(String msg) throws Throwable {
try (ResourceScope scope = ResourceScope.newConfinedScope()) { try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment s = toCString(msg, scope); var malloc = SegmentAllocator.nativeAllocator(scope);
return (int)strlen.invokeExact(s.address()); MemorySegment s = malloc.allocateUtf8String(msg);
return (int)strlen.invoke(s);
} }
} }
Tm gmtime(long arg) throws Throwable { Tm gmtime(long arg) throws Throwable {
try (ResourceScope scope = ResourceScope.newConfinedScope()) { try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment time = MemorySegment.allocateNative(8, scope); MemorySegment time = MemorySegment.allocateNative(8, scope);
setLong(time, arg); time.set(C_LONG_LONG, 0, arg);
return new Tm((MemoryAddress)gmtime.invokeExact(time.address())); return new Tm((MemoryAddress)gmtime.invoke(time));
} }
} }
@ -258,58 +245,57 @@ public class StdLibTest {
static final long SIZE = 56; static final long SIZE = 56;
Tm(MemoryAddress addr) { Tm(MemoryAddress addr) {
this.base = addr.asSegment(SIZE, ResourceScope.globalScope()); this.base = MemorySegment.ofAddress(addr, SIZE, ResourceScope.globalScope());
} }
int sec() { int sec() {
return getIntAtOffset(base, 0); return base.get(C_INT, 0);
} }
int min() { int min() {
return getIntAtOffset(base, 4); return base.get(C_INT, 4);
} }
int hour() { int hour() {
return getIntAtOffset(base, 8); return base.get(C_INT, 8);
} }
int mday() { int mday() {
return getIntAtOffset(base, 12); return base.get(C_INT, 12);
} }
int mon() { int mon() {
return getIntAtOffset(base, 16); return base.get(C_INT, 16);
} }
int year() { int year() {
return getIntAtOffset(base, 20); return base.get(C_INT, 20);
} }
int wday() { int wday() {
return getIntAtOffset(base, 24); return base.get(C_INT, 24);
} }
int yday() { int yday() {
return getIntAtOffset(base, 28); return base.get(C_INT, 28);
} }
boolean isdst() { boolean isdst() {
byte b = getByteAtOffset(base, 32); return base.get(C_BOOL, 32);
return b != 0;
} }
} }
int[] qsort(int[] arr) throws Throwable { int[] qsort(int[] arr) throws Throwable {
//init native array //init native array
try (ResourceScope scope = ResourceScope.newConfinedScope()) { try (ResourceScope scope = ResourceScope.newConfinedScope()) {
SegmentAllocator allocator = SegmentAllocator.ofScope(scope); var malloc = SegmentAllocator.nativeAllocator(scope);
MemorySegment nativeArr = allocator.allocateArray(C_INT, arr); MemorySegment nativeArr = malloc.allocateArray(C_INT, arr);
//call qsort //call qsort
MemoryAddress qsortUpcallStub = abi.upcallStub(qsortCompar.bindTo(nativeArr), qsortComparFunction, scope); NativeSymbol qsortUpcallStub = abi.upcallStub(qsortCompar, qsortComparFunction, scope);
qsort.invokeExact(nativeArr.address(), (long)arr.length, C_INT.byteSize(), qsortUpcallStub); qsort.invoke(nativeArr, (long)arr.length, C_INT.byteSize(), qsortUpcallStub);
//convert back to Java array //convert back to Java array
return nativeArr.toIntArray(); return nativeArr.toArray(C_INT);
} }
} }
static int qsortCompare(MemorySegment base, MemoryAddress addr1, MemoryAddress addr2) { static int qsortCompare(MemoryAddress addr1, MemoryAddress addr2) {
return getIntAtOffset(base, addr1.segmentOffset(base)) - return addr1.get(C_INT, 0) -
getIntAtOffset(base, addr2.segmentOffset(base)); addr2.get(C_INT, 0);
} }
int rand() throws Throwable { int rand() throws Throwable {
@ -318,17 +304,19 @@ public class StdLibTest {
int printf(String format, List<PrintfArg> args) throws Throwable { int printf(String format, List<PrintfArg> args) throws Throwable {
try (ResourceScope scope = ResourceScope.newConfinedScope()) { try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment formatStr = toCString(format, scope); var malloc = SegmentAllocator.nativeAllocator(scope);
return (int)specializedPrintf(args).invokeExact(formatStr.address(), MemorySegment formatStr = malloc.allocateUtf8String(format);
return (int)specializedPrintf(args).invoke(formatStr,
args.stream().map(a -> a.nativeValue(scope)).toArray()); args.stream().map(a -> a.nativeValue(scope)).toArray());
} }
} }
int vprintf(String format, List<PrintfArg> args) throws Throwable { int vprintf(String format, List<PrintfArg> args) throws Throwable {
try (ResourceScope scope = ResourceScope.newConfinedScope()) { try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment formatStr = toCString(format, scope); var malloc = SegmentAllocator.nativeAllocator(scope);
MemorySegment formatStr = malloc.allocateUtf8String(format);
VaList vaList = VaList.make(b -> args.forEach(a -> a.accept(b, scope)), scope); VaList vaList = VaList.make(b -> args.forEach(a -> a.accept(b, scope)), scope);
return (int)vprintf.invokeExact(formatStr.address(), vaList); return (int)vprintf.invoke(formatStr, vaList);
} }
} }
@ -336,11 +324,13 @@ public class StdLibTest {
//method type //method type
MethodType mt = MethodType.methodType(int.class, MemoryAddress.class); MethodType mt = MethodType.methodType(int.class, MemoryAddress.class);
FunctionDescriptor fd = printfBase; FunctionDescriptor fd = printfBase;
List<MemoryLayout> variadicLayouts = new ArrayList<>(args.size());
for (PrintfArg arg : args) { for (PrintfArg arg : args) {
mt = mt.appendParameterTypes(arg.carrier); mt = mt.appendParameterTypes(arg.carrier);
fd = fd.withAppendedArgumentLayouts(arg.layout); variadicLayouts.add(arg.layout);
} }
MethodHandle mh = abi.downcallHandle(printfAddr, mt, fd); MethodHandle mh = abi.downcallHandle(printfAddr,
fd.asVariadic(variadicLayouts.toArray(new MemoryLayout[args.size()])));
return mh.asSpreader(1, Object[].class, args.size()); return mh.asSpreader(1, Object[].class, args.size());
} }
} }
@ -401,10 +391,14 @@ public class StdLibTest {
enum PrintfArg implements BiConsumer<VaList.Builder, ResourceScope> { enum PrintfArg implements BiConsumer<VaList.Builder, ResourceScope> {
INTEGRAL(int.class, asVarArg(C_INT), "%d", scope -> 42, 42, VaList.Builder::vargFromInt), INTEGRAL(int.class, C_INT, "%d", scope -> 42, 42, VaList.Builder::addVarg),
STRING(MemoryAddress.class, asVarArg(C_POINTER), "%s", scope -> toCString("str", scope).address(), "str", VaList.Builder::vargFromAddress), STRING(MemoryAddress.class, C_POINTER, "%s", scope -> {
CHAR(byte.class, asVarArg(C_CHAR), "%c", scope -> (byte) 'h', 'h', (builder, layout, value) -> builder.vargFromInt(C_INT, (int)value)), var segment = MemorySegment.allocateNative(4, scope);
DOUBLE(double.class, asVarArg(C_DOUBLE), "%.4f", scope ->1.2345d, 1.2345d, VaList.Builder::vargFromDouble); segment.setUtf8String(0, "str");
return segment.address();
}, "str", VaList.Builder::addVarg),
CHAR(byte.class, C_CHAR, "%c", scope -> (byte) 'h', 'h', (builder, layout, value) -> builder.addVarg(C_INT, (int)value)),
DOUBLE(double.class, C_DOUBLE, "%.4f", scope ->1.2345d, 1.2345d, VaList.Builder::addVarg);
final Class<?> carrier; final Class<?> carrier;
final ValueLayout layout; final ValueLayout layout;
@ -414,7 +408,7 @@ public class StdLibTest {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
final VaListBuilderCall builderCall; final VaListBuilderCall builderCall;
<Z> PrintfArg(Class<?> carrier, ValueLayout layout, String format, Function<ResourceScope, Z> nativeValueFactory, Object javaValue, VaListBuilderCall<Z> builderCall) { <Z, L extends ValueLayout> PrintfArg(Class<?> carrier, L layout, String format, Function<ResourceScope, Z> nativeValueFactory, Object javaValue, VaListBuilderCall<Z, L> builderCall) {
this.carrier = carrier; this.carrier = carrier;
this.layout = layout; this.layout = layout;
this.format = format; this.format = format;
@ -429,8 +423,8 @@ public class StdLibTest {
builderCall.build(builder, layout, nativeValueFactory.apply(scope)); builderCall.build(builder, layout, nativeValueFactory.apply(scope));
} }
interface VaListBuilderCall<V> { interface VaListBuilderCall<V, L> {
void build(VaList.Builder builder, ValueLayout layout, V value); void build(VaList.Builder builder, L layout, V value);
} }
public Object nativeValue(ResourceScope scope) { public Object nativeValue(ResourceScope scope) {

View file

@ -32,7 +32,6 @@
import jdk.incubator.foreign.MemoryHandles; import jdk.incubator.foreign.MemoryHandles;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.ResourceScope;
import jdk.incubator.foreign.ValueLayout; import jdk.incubator.foreign.ValueLayout;
@ -86,18 +85,18 @@ public class TestAdaptVarHandles {
} }
} }
static final VarHandle intHandleIndexed = MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT) static final VarHandle intHandleIndexed = MemoryLayout.sequenceLayout(ValueLayout.JAVA_INT)
.varHandle(int.class, MemoryLayout.PathElement.sequenceElement()); .varHandle(MemoryLayout.PathElement.sequenceElement());
static final VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); static final VarHandle intHandle = ValueLayout.JAVA_INT.varHandle();
static final VarHandle floatHandle = MemoryLayouts.JAVA_FLOAT.varHandle(float.class); static final VarHandle floatHandle = ValueLayout.JAVA_FLOAT.varHandle();
@Test @Test
public void testFilterValue() throws Throwable { public void testFilterValue() throws Throwable {
ValueLayout layout = MemoryLayouts.JAVA_INT; ValueLayout layout = ValueLayout.JAVA_INT;
MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope()); MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope());
VarHandle intHandle = layout.varHandle(int.class); VarHandle intHandle = layout.varHandle();
VarHandle i2SHandle = MemoryHandles.filterValue(intHandle, S2I, I2S); VarHandle i2SHandle = MemoryHandles.filterValue(intHandle, S2I, I2S);
i2SHandle.set(segment, "1"); i2SHandle.set(segment, "1");
String oldValue = (String)i2SHandle.getAndAdd(segment, "42"); String oldValue = (String)i2SHandle.getAndAdd(segment, "42");
@ -114,9 +113,9 @@ public class TestAdaptVarHandles {
@Test @Test
public void testFilterValueComposite() throws Throwable { public void testFilterValueComposite() throws Throwable {
ValueLayout layout = MemoryLayouts.JAVA_INT; ValueLayout layout = ValueLayout.JAVA_INT;
MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope()); MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope());
VarHandle intHandle = layout.varHandle(int.class); VarHandle intHandle = layout.varHandle();
MethodHandle CTX_S2I = MethodHandles.dropArguments(S2I, 0, String.class, String.class); MethodHandle CTX_S2I = MethodHandles.dropArguments(S2I, 0, String.class, String.class);
VarHandle i2SHandle = MemoryHandles.filterValue(intHandle, CTX_S2I, CTX_I2S); VarHandle i2SHandle = MemoryHandles.filterValue(intHandle, CTX_S2I, CTX_I2S);
i2SHandle = MemoryHandles.insertCoordinates(i2SHandle, 1, "a", "b"); i2SHandle = MemoryHandles.insertCoordinates(i2SHandle, 1, "a", "b");
@ -135,9 +134,9 @@ public class TestAdaptVarHandles {
@Test @Test
public void testFilterValueLoose() throws Throwable { public void testFilterValueLoose() throws Throwable {
ValueLayout layout = MemoryLayouts.JAVA_INT; ValueLayout layout = ValueLayout.JAVA_INT;
MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope()); MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope());
VarHandle intHandle = layout.varHandle(int.class); VarHandle intHandle = layout.varHandle();
VarHandle i2SHandle = MemoryHandles.filterValue(intHandle, O2I, I2O); VarHandle i2SHandle = MemoryHandles.filterValue(intHandle, O2I, I2O);
i2SHandle.set(segment, "1"); i2SHandle.set(segment, "1");
String oldValue = (String)i2SHandle.getAndAdd(segment, "42"); String oldValue = (String)i2SHandle.getAndAdd(segment, "42");
@ -159,19 +158,19 @@ public class TestAdaptVarHandles {
@Test(expectedExceptions = IllegalArgumentException.class) @Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterUnboxArity() { public void testBadFilterUnboxArity() {
VarHandle floatHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); VarHandle floatHandle = ValueLayout.JAVA_INT.varHandle();
MemoryHandles.filterValue(floatHandle, S2I.bindTo(""), I2S); MemoryHandles.filterValue(floatHandle, S2I.bindTo(""), I2S);
} }
@Test(expectedExceptions = IllegalArgumentException.class) @Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterBoxArity() { public void testBadFilterBoxArity() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); VarHandle intHandle = ValueLayout.JAVA_INT.varHandle();
MemoryHandles.filterValue(intHandle, S2I, I2S.bindTo(42)); MemoryHandles.filterValue(intHandle, S2I, I2S.bindTo(42));
} }
@Test(expectedExceptions = IllegalArgumentException.class) @Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterBoxPrefixCoordinates() { public void testBadFilterBoxPrefixCoordinates() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); VarHandle intHandle = ValueLayout.JAVA_INT.varHandle();
MemoryHandles.filterValue(intHandle, MemoryHandles.filterValue(intHandle,
MethodHandles.dropArguments(S2I, 1, int.class), MethodHandles.dropArguments(S2I, 1, int.class),
MethodHandles.dropArguments(I2S, 1, long.class)); MethodHandles.dropArguments(I2S, 1, long.class));
@ -179,31 +178,40 @@ public class TestAdaptVarHandles {
@Test(expectedExceptions = IllegalArgumentException.class) @Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterBoxException() { public void testBadFilterBoxException() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); VarHandle intHandle = ValueLayout.JAVA_INT.varHandle();
MemoryHandles.filterValue(intHandle, I2S, S2L_EX); MemoryHandles.filterValue(intHandle, I2S, S2L_EX);
} }
@Test(expectedExceptions = IllegalArgumentException.class) @Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterUnboxException() { public void testBadFilterUnboxException() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); VarHandle intHandle = ValueLayout.JAVA_INT.varHandle();
MemoryHandles.filterValue(intHandle, S2L_EX, I2S); MemoryHandles.filterValue(intHandle, S2L_EX, I2S);
} }
@Test(expectedExceptions = IllegalArgumentException.class) @Test(expectedExceptions = IllegalStateException.class)
public void testBadFilterBoxHandleException() { public void testBadFilterBoxHandleException() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); VarHandle intHandle = ValueLayout.JAVA_INT.varHandle();
MemoryHandles.filterValue(intHandle, S2I, I2S_EX); VarHandle vh = MemoryHandles.filterValue(intHandle, S2I, I2S_EX);
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment seg = MemorySegment.allocateNative(ValueLayout.JAVA_INT, scope);
vh.set(seg, "42");
String x = (String) vh.get(seg); // should throw
}
} }
@Test(expectedExceptions = IllegalArgumentException.class) @Test(expectedExceptions = IllegalStateException.class)
public void testBadFilterUnboxHandleException() { public void testBadFilterUnboxHandleException() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); VarHandle intHandle = ValueLayout.JAVA_INT.varHandle();
MemoryHandles.filterValue(intHandle, S2I_EX, I2S); VarHandle vh = MemoryHandles.filterValue(intHandle, S2I_EX, I2S);
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment seg = MemorySegment.allocateNative(ValueLayout.JAVA_INT, scope);
vh.set(seg, "42"); // should throw
}
} }
@Test @Test
public void testFilterCoordinates() throws Throwable { public void testFilterCoordinates() throws Throwable {
ValueLayout layout = MemoryLayouts.JAVA_INT; ValueLayout layout = ValueLayout.JAVA_INT;
MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope()); MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope());
VarHandle intHandle_longIndex = MemoryHandles.filterCoordinates(intHandleIndexed, 0, BASE_ADDR, S2L); VarHandle intHandle_longIndex = MemoryHandles.filterCoordinates(intHandleIndexed, 0, BASE_ADDR, S2L);
intHandle_longIndex.set(segment, "0", 1); intHandle_longIndex.set(segment, "0", 1);
@ -246,7 +254,7 @@ public class TestAdaptVarHandles {
@Test @Test
public void testInsertCoordinates() throws Throwable { public void testInsertCoordinates() throws Throwable {
ValueLayout layout = MemoryLayouts.JAVA_INT; ValueLayout layout = ValueLayout.JAVA_INT;
MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope()); MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope());
VarHandle intHandle_longIndex = MemoryHandles.insertCoordinates(intHandleIndexed, 0, segment, 0L); VarHandle intHandle_longIndex = MemoryHandles.insertCoordinates(intHandleIndexed, 0, segment, 0L);
intHandle_longIndex.set(1); intHandle_longIndex.set(1);
@ -284,7 +292,7 @@ public class TestAdaptVarHandles {
@Test @Test
public void testPermuteCoordinates() throws Throwable { public void testPermuteCoordinates() throws Throwable {
ValueLayout layout = MemoryLayouts.JAVA_INT; ValueLayout layout = ValueLayout.JAVA_INT;
MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope()); MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope());
VarHandle intHandle_swap = MemoryHandles.permuteCoordinates(intHandleIndexed, VarHandle intHandle_swap = MemoryHandles.permuteCoordinates(intHandleIndexed,
List.of(long.class, MemorySegment.class), 1, 0); List.of(long.class, MemorySegment.class), 1, 0);
@ -323,7 +331,7 @@ public class TestAdaptVarHandles {
@Test @Test
public void testCollectCoordinates() throws Throwable { public void testCollectCoordinates() throws Throwable {
ValueLayout layout = MemoryLayouts.JAVA_INT; ValueLayout layout = ValueLayout.JAVA_INT;
MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope()); MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope());
VarHandle intHandle_sum = MemoryHandles.collectCoordinates(intHandleIndexed, 1, SUM_OFFSETS); VarHandle intHandle_sum = MemoryHandles.collectCoordinates(intHandleIndexed, 1, SUM_OFFSETS);
intHandle_sum.set(segment, -2L, 2L, 1); intHandle_sum.set(segment, -2L, 2L, 1);
@ -366,7 +374,7 @@ public class TestAdaptVarHandles {
@Test @Test
public void testDropCoordinates() throws Throwable { public void testDropCoordinates() throws Throwable {
ValueLayout layout = MemoryLayouts.JAVA_INT; ValueLayout layout = ValueLayout.JAVA_INT;
MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope()); MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope());
VarHandle intHandle_dummy = MemoryHandles.dropCoordinates(intHandleIndexed, 1, float.class, String.class); VarHandle intHandle_dummy = MemoryHandles.dropCoordinates(intHandleIndexed, 1, float.class, String.class);
intHandle_dummy.set(segment, 1f, "hello", 0L, 1); intHandle_dummy.set(segment, 1f, "hello", 0L, 1);

View file

@ -1,160 +0,0 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
/*
* @test
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false -Xverify:all TestAddressHandle
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true -Xverify:all TestAddressHandle
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false -Xverify:all TestAddressHandle
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true -Xverify:all TestAddressHandle
*/
import java.lang.invoke.*;
import java.nio.ByteOrder;
import jdk.incubator.foreign.*;
import org.testng.annotations.*;
import static org.testng.Assert.*;
public class TestAddressHandle {
static final MethodHandle INT_TO_BOOL;
static final MethodHandle BOOL_TO_INT;
static final MethodHandle INT_TO_STRING;
static final MethodHandle STRING_TO_INT;
static {
try {
INT_TO_BOOL = MethodHandles.lookup().findStatic(TestAddressHandle.class, "intToBool",
MethodType.methodType(boolean.class, int.class));
BOOL_TO_INT = MethodHandles.lookup().findStatic(TestAddressHandle.class, "boolToInt",
MethodType.methodType(int.class, boolean.class));
INT_TO_STRING = MethodHandles.lookup().findStatic(TestAddressHandle.class, "intToString",
MethodType.methodType(String.class, int.class));
STRING_TO_INT = MethodHandles.lookup().findStatic(TestAddressHandle.class, "stringToInt",
MethodType.methodType(int.class, String.class));
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
@Test(dataProvider = "addressHandles")
public void testAddressHandle(VarHandle addrHandle, int byteSize) {
VarHandle longHandle = MemoryLayouts.JAVA_LONG.varHandle(long.class);
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment segment = MemorySegment.allocateNative(8, scope);
MemorySegment target = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN ?
segment.asSlice(8 - byteSize) :
segment;
longHandle.set(segment, 42L);
MemoryAddress address = (MemoryAddress)addrHandle.get(target);
assertEquals(address.toRawLongValue(), 42L);
addrHandle.set(target, address.addOffset(1));
long result = (long)longHandle.get(segment);
assertEquals(43L, result);
}
}
@Test(dataProvider = "addressHandles")
public void testNull(VarHandle addrHandle, int byteSize) {
VarHandle longHandle = MemoryLayouts.JAVA_LONG.varHandle(long.class);
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment segment = MemorySegment.allocateNative(8, scope);
longHandle.set(segment, 0L);
MemoryAddress address = (MemoryAddress)addrHandle.get(segment);
assertTrue(address == MemoryAddress.NULL);
}
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadAdaptFloat() {
VarHandle floatHandle = MemoryLayouts.JAVA_FLOAT.varHandle(float.class);
MemoryHandles.asAddressVarHandle(floatHandle);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadAdaptDouble() {
VarHandle doubleHandle = MemoryLayouts.JAVA_DOUBLE.varHandle(double.class);
MemoryHandles.asAddressVarHandle(doubleHandle);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadAdaptBoolean() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
VarHandle boolHandle = MemoryHandles.filterValue(intHandle, BOOL_TO_INT, INT_TO_BOOL);
MemoryHandles.asAddressVarHandle(boolHandle);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadAdaptString() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
VarHandle stringHandle = MemoryHandles.filterValue(intHandle, STRING_TO_INT, INT_TO_STRING);
MemoryHandles.asAddressVarHandle(stringHandle);
}
@DataProvider(name = "addressHandles")
static Object[][] addressHandles() {
return new Object[][] {
// long
{ MemoryHandles.asAddressVarHandle(at(MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder()), 0)), 8 },
{ MemoryHandles.asAddressVarHandle(MemoryLayouts.JAVA_LONG.varHandle(long.class)), 8 },
// int
{ MemoryHandles.asAddressVarHandle(at(MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder()), 0)), 4 },
{ MemoryHandles.asAddressVarHandle(MemoryLayouts.JAVA_INT.varHandle(int.class)), 4 },
// short
{ MemoryHandles.asAddressVarHandle(at(MemoryHandles.varHandle(short.class, ByteOrder.nativeOrder()), 0)), 2 },
{ MemoryHandles.asAddressVarHandle(MemoryLayouts.JAVA_SHORT.varHandle(short.class)), 2 },
// char
{ MemoryHandles.asAddressVarHandle(at(MemoryHandles.varHandle(char.class, ByteOrder.nativeOrder()), 0)), 2 },
{ MemoryHandles.asAddressVarHandle(MemoryLayouts.JAVA_CHAR.varHandle(char.class)), 2 },
// byte
{ MemoryHandles.asAddressVarHandle(at(MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder()), 0)), 1 },
{ MemoryHandles.asAddressVarHandle(MemoryLayouts.JAVA_BYTE.varHandle(byte.class)), 1 }
};
}
static VarHandle at(VarHandle handle, long offset) {
return MemoryHandles.insertCoordinates(handle, 1, offset);
}
static int boolToInt(boolean value) {
return value ? 1 : 0;
}
static boolean intToBool(int value) {
return value != 0;
}
static int stringToInt(String value) {
return value.length();
}
static String intToString(int value) {
return String.valueOf(value);
}
}

View file

@ -0,0 +1,542 @@
/*
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @run testng TestArrayCopy
*/
import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE;
import static jdk.incubator.foreign.ValueLayout.JAVA_INT;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.ValueLayout;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
/**
* These tests exercise the MemoryCopy copyFromArray(...) and copyToArray(...).
* To make these tests more challenging the segment is a view of the given array,
* which makes the copy operations overlapping self-copies. Thus, this checks the claim:
*
* <p>If the source (destination) segment is actually a view of the destination (source) array,
* and if the copy region of the source overlaps with the copy region of the destination,
* the copy of the overlapping region is performed as if the data in the overlapping region
* were first copied into a temporary segment before being copied to the destination.</p>
*/
public class TestArrayCopy {
private static final ByteOrder NATIVE_ORDER = ByteOrder.nativeOrder();
private static final ByteOrder NON_NATIVE_ORDER = NATIVE_ORDER == ByteOrder.LITTLE_ENDIAN
? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
private static final int SEG_LENGTH_BYTES = 32;
private static final int SEG_OFFSET_BYTES = 8;
@Test(dataProvider = "copyModesAndHelpers")
public void testSelfCopy(CopyMode mode, CopyHelper<Object, ValueLayout> helper, String helperDebugString) {
int bytesPerElement = (int)helper.elementLayout.byteSize();
int indexShifts = SEG_OFFSET_BYTES / bytesPerElement;
MemorySegment base = srcSegment(SEG_LENGTH_BYTES);
MemorySegment truth = truthSegment(base, helper, indexShifts, mode);
ByteOrder bo = mode.swap ? NON_NATIVE_ORDER : NATIVE_ORDER;
//CopyFrom
Object srcArr = helper.toArray(base);
int srcIndex = mode.direction ? 0 : indexShifts;
int srcCopyLen = helper.length(srcArr) - indexShifts;
MemorySegment dstSeg = helper.fromArray(srcArr);
long dstOffsetBytes = mode.direction ? SEG_OFFSET_BYTES : 0;
helper.copyFromArray(srcArr, srcIndex, srcCopyLen, dstSeg, dstOffsetBytes, bo);
assertEquals(truth.mismatch(dstSeg), -1);
//CopyTo
long srcOffsetBytes = mode.direction ? 0 : SEG_OFFSET_BYTES;
Object dstArr = helper.toArray(base);
MemorySegment srcSeg = helper.fromArray(dstArr).asReadOnly();
int dstIndex = mode.direction ? indexShifts : 0;
int dstCopyLen = helper.length(dstArr) - indexShifts;
helper.copyToArray(srcSeg, srcOffsetBytes, dstArr, dstIndex, dstCopyLen, bo);
MemorySegment result = helper.fromArray(dstArr);
assertEquals(truth.mismatch(result), -1);
}
@Test(dataProvider = "copyModesAndHelpers")
public void testUnalignedCopy(CopyMode mode, CopyHelper<Object, ValueLayout> helper, String helperDebugString) {
int bytesPerElement = (int)helper.elementLayout.byteSize();
int indexShifts = SEG_OFFSET_BYTES / bytesPerElement;
MemorySegment base = srcSegment(SEG_LENGTH_BYTES);
ByteOrder bo = mode.swap ? NON_NATIVE_ORDER : NATIVE_ORDER;
//CopyFrom
Object srcArr = helper.toArray(base);
int srcIndex = mode.direction ? 0 : indexShifts;
int srcCopyLen = helper.length(srcArr) - indexShifts;
MemorySegment dstSeg = helper.fromArray(srcArr);
long dstOffsetBytes = mode.direction ? (SEG_OFFSET_BYTES - 1) : 0;
helper.copyFromArray(srcArr, srcIndex, srcCopyLen, dstSeg, dstOffsetBytes, bo);
//CopyTo
long srcOffsetBytes = mode.direction ? 0 : (SEG_OFFSET_BYTES - 1);
Object dstArr = helper.toArray(base);
MemorySegment srcSeg = helper.fromArray(dstArr).asReadOnly();
int dstIndex = mode.direction ? indexShifts : 0;
int dstCopyLen = helper.length(dstArr) - indexShifts;
helper.copyToArray(srcSeg, srcOffsetBytes, dstArr, dstIndex, dstCopyLen, bo);
}
@Test(dataProvider = "copyModesAndHelpers")
public void testCopyOobLength(CopyMode mode, CopyHelper<Object, ValueLayout> helper, String helperDebugString) {
int bytesPerElement = (int)helper.elementLayout.byteSize();
MemorySegment base = srcSegment(SEG_LENGTH_BYTES);
//CopyFrom
Object srcArr = helper.toArray(base);
MemorySegment dstSeg = helper.fromArray(srcArr);
try {
helper.copyFromArray(srcArr, 0, (SEG_LENGTH_BYTES / bytesPerElement) * 2, dstSeg, 0, ByteOrder.nativeOrder());
fail();
} catch (IndexOutOfBoundsException ex) {
//ok
}
//CopyTo
Object dstArr = helper.toArray(base);
MemorySegment srcSeg = helper.fromArray(dstArr).asReadOnly();
try {
helper.copyToArray(srcSeg, 0, dstArr, 0, (SEG_LENGTH_BYTES / bytesPerElement) * 2, ByteOrder.nativeOrder());
fail();
} catch (IndexOutOfBoundsException ex) {
//ok
}
}
@Test(dataProvider = "copyModesAndHelpers")
public void testCopyNegativeIndices(CopyMode mode, CopyHelper<Object, ValueLayout> helper, String helperDebugString) {
int bytesPerElement = (int)helper.elementLayout.byteSize();
MemorySegment base = srcSegment(SEG_LENGTH_BYTES);
//CopyFrom
Object srcArr = helper.toArray(base);
MemorySegment dstSeg = helper.fromArray(srcArr);
try {
helper.copyFromArray(srcArr, -1, SEG_LENGTH_BYTES / bytesPerElement, dstSeg, 0, ByteOrder.nativeOrder());
fail();
} catch (IndexOutOfBoundsException ex) {
//ok
}
//CopyTo
Object dstArr = helper.toArray(base);
MemorySegment srcSeg = helper.fromArray(dstArr).asReadOnly();
try {
helper.copyToArray(srcSeg, 0, dstArr, -1, SEG_LENGTH_BYTES / bytesPerElement, ByteOrder.nativeOrder());
fail();
} catch (IndexOutOfBoundsException ex) {
//ok
}
}
@Test(dataProvider = "copyModesAndHelpers")
public void testCopyNegativeOffsets(CopyMode mode, CopyHelper<Object, ValueLayout> helper, String helperDebugString) {
int bytesPerElement = (int)helper.elementLayout.byteSize();
MemorySegment base = srcSegment(SEG_LENGTH_BYTES);
//CopyFrom
Object srcArr = helper.toArray(base);
MemorySegment dstSeg = helper.fromArray(srcArr);
try {
helper.copyFromArray(srcArr, 0, SEG_LENGTH_BYTES / bytesPerElement, dstSeg, -1, ByteOrder.nativeOrder());
fail();
} catch (IndexOutOfBoundsException ex) {
//ok
}
//CopyTo
Object dstArr = helper.toArray(base);
MemorySegment srcSeg = helper.fromArray(dstArr).asReadOnly();
try {
helper.copyToArray(srcSeg, -1, dstArr, 0, SEG_LENGTH_BYTES / bytesPerElement, ByteOrder.nativeOrder());
fail();
} catch (IndexOutOfBoundsException ex) {
//ok
}
}
@Test(dataProvider = "copyModesAndHelpers")
public void testCopyOobIndices(CopyMode mode, CopyHelper<Object, ValueLayout> helper, String helperDebugString) {
int bytesPerElement = (int)helper.elementLayout.byteSize();
MemorySegment base = srcSegment(SEG_LENGTH_BYTES);
//CopyFrom
Object srcArr = helper.toArray(base);
MemorySegment dstSeg = helper.fromArray(srcArr);
try {
helper.copyFromArray(srcArr, helper.length(srcArr) + 1, SEG_LENGTH_BYTES / bytesPerElement, dstSeg, 0, ByteOrder.nativeOrder());
fail();
} catch (IndexOutOfBoundsException ex) {
//ok
}
//CopyTo
Object dstArr = helper.toArray(base);
MemorySegment srcSeg = helper.fromArray(dstArr).asReadOnly();
try {
helper.copyToArray(srcSeg, 0, dstArr, helper.length(dstArr) + 1, SEG_LENGTH_BYTES / bytesPerElement, ByteOrder.nativeOrder());
fail();
} catch (IndexOutOfBoundsException ex) {
//ok
}
}
@Test(dataProvider = "copyModesAndHelpers")
public void testCopyOobOffsets(CopyMode mode, CopyHelper<Object, ValueLayout> helper, String helperDebugString) {
int bytesPerElement = (int)helper.elementLayout.byteSize();
MemorySegment base = srcSegment(SEG_LENGTH_BYTES);
//CopyFrom
Object srcArr = helper.toArray(base);
MemorySegment dstSeg = helper.fromArray(srcArr);
try {
helper.copyFromArray(srcArr, 0, SEG_LENGTH_BYTES / bytesPerElement, dstSeg, SEG_LENGTH_BYTES + 1, ByteOrder.nativeOrder());
fail();
} catch (IndexOutOfBoundsException ex) {
//ok
}
//CopyTo
Object dstArr = helper.toArray(base);
MemorySegment srcSeg = helper.fromArray(dstArr).asReadOnly();
try {
helper.copyToArray(srcSeg, SEG_OFFSET_BYTES + 1, dstArr, 0, SEG_LENGTH_BYTES / bytesPerElement, ByteOrder.nativeOrder());
fail();
} catch (IndexOutOfBoundsException ex) {
//ok
}
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testNotAnArraySrc() {
MemorySegment segment = MemorySegment.ofArray(new int[] {1, 2, 3, 4});
MemorySegment.copy(segment, JAVA_BYTE, 0, new String[] { "hello" }, 0, 4);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testNotAnArrayDst() {
MemorySegment segment = MemorySegment.ofArray(new int[] {1, 2, 3, 4});
MemorySegment.copy(new String[] { "hello" }, 0, segment, JAVA_BYTE, 0, 4);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testCarrierMismatchSrc() {
MemorySegment segment = MemorySegment.ofArray(new int[] {1, 2, 3, 4});
MemorySegment.copy(segment, JAVA_INT, 0, new byte[] { 1, 2, 3, 4 }, 0, 4);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testCarrierMismatchDst() {
MemorySegment segment = MemorySegment.ofArray(new int[] {1, 2, 3, 4});
MemorySegment.copy(new byte[] { 1, 2, 3, 4 }, 0, segment, JAVA_INT, 0, 4);
}
/***** Utilities *****/
public static MemorySegment srcSegment(int bytesLength) {
byte[] arr = new byte[bytesLength];
for (int i = 0; i < arr.length; i++) {
arr[i] = (byte)i;
}
return MemorySegment.ofArray(arr);
}
public static MemorySegment truthSegment(MemorySegment srcSeg, CopyHelper<?, ?> helper, int indexShifts, CopyMode mode) {
VarHandle indexedHandleNO = MemoryLayout.sequenceLayout(helper.elementLayout.withOrder(NATIVE_ORDER))
.varHandle(MemoryLayout.PathElement.sequenceElement());
VarHandle indexedHandleNNO = MemoryLayout.sequenceLayout(helper.elementLayout.withOrder(NON_NATIVE_ORDER))
.varHandle(MemoryLayout.PathElement.sequenceElement());
MemorySegment dstSeg = MemorySegment.ofArray(srcSeg.toArray(JAVA_BYTE));
int indexLength = (int) dstSeg.byteSize() / (int)helper.elementLayout.byteSize();
if (mode.direction) {
if (mode.swap) {
for (int i = indexLength - 1; i >= indexShifts; i--) {
Object v = indexedHandleNNO.get(dstSeg, i - indexShifts);
indexedHandleNO.set(dstSeg, i, v);
}
} else {
for (int i = indexLength - 1; i >= indexShifts; i--) {
Object v = indexedHandleNO.get(dstSeg, i - indexShifts);
indexedHandleNO.set(dstSeg, i, v);
}
}
} else { //down
if (mode.swap) {
for (int i = indexShifts; i < indexLength; i++) {
Object v = indexedHandleNNO.get(dstSeg, i);
indexedHandleNO.set(dstSeg, i - indexShifts, v);
}
} else {
for (int i = indexShifts; i < indexLength; i++) {
Object v = indexedHandleNO.get(dstSeg, i);
indexedHandleNO.set(dstSeg, i - indexShifts, v);
}
}
}
return dstSeg;
}
enum CopyMode {
UP_NO_SWAP(true, false),
UP_SWAP(true, true),
DOWN_NO_SWAP(false, false),
DOWN_SWAP(false, true);
final boolean direction;
final boolean swap;
CopyMode(boolean direction, boolean swap) {
this.direction = direction;
this.swap = swap;
}
}
abstract static class CopyHelper<X, L extends ValueLayout> {
final L elementLayout;
final Class<?> carrier;
public CopyHelper(L elementLayout, Class<X> carrier) {
this.elementLayout = elementLayout;
this.carrier = carrier;
}
abstract void copyFromArray(X srcArr, int srcIndex, int srcCopyLen, MemorySegment dstSeg, long dstOffsetBytes, ByteOrder bo);
abstract void copyToArray(MemorySegment srcSeg, long srcOffsetBytes, X dstArr, int dstIndex, int dstCopyLen, ByteOrder bo);
abstract X toArray(MemorySegment segment);
abstract MemorySegment fromArray(X array);
abstract int length(X arr);
@Override
public String toString() {
return "CopyHelper{" +
"elementLayout=" + elementLayout +
", carrier=" + carrier.getName() +
'}';
}
static final CopyHelper<byte[], ValueLayout.OfByte> BYTE = new CopyHelper<>(JAVA_BYTE, byte[].class) {
@Override
void copyFromArray(byte[] srcArr, int srcIndex, int srcCopyLen, MemorySegment dstSeg, long dstOffsetBytes, ByteOrder bo) {
MemorySegment.copy(srcArr, srcIndex, dstSeg, elementLayout.withOrder(bo), dstOffsetBytes, srcCopyLen);
}
@Override
void copyToArray(MemorySegment srcSeg, long srcOffsetBytes, byte[] dstArr, int dstIndex, int dstCopyLen, ByteOrder bo) {
MemorySegment.copy(srcSeg, elementLayout.withOrder(bo), srcOffsetBytes, dstArr, dstIndex, dstCopyLen);
}
@Override
byte[] toArray(MemorySegment segment) {
return segment.toArray(elementLayout);
}
@Override
MemorySegment fromArray(byte[] array) {
return MemorySegment.ofArray(array);
}
@Override
int length(byte[] arr) {
return arr.length;
}
};
static final CopyHelper<char[], ValueLayout.OfChar> CHAR = new CopyHelper<>(ValueLayout.JAVA_CHAR, char[].class) {
@Override
void copyFromArray(char[] srcArr, int srcIndex, int srcCopyLen, MemorySegment dstSeg, long dstOffsetBytes, ByteOrder bo) {
MemorySegment.copy(srcArr, srcIndex, dstSeg, elementLayout.withOrder(bo), dstOffsetBytes, srcCopyLen);
}
@Override
void copyToArray(MemorySegment srcSeg, long srcOffsetBytes, char[] dstArr, int dstIndex, int dstCopyLen, ByteOrder bo) {
MemorySegment.copy(srcSeg, elementLayout.withOrder(bo), srcOffsetBytes, dstArr, dstIndex, dstCopyLen);
}
@Override
char[] toArray(MemorySegment segment) {
return segment.toArray(elementLayout);
}
@Override
MemorySegment fromArray(char[] array) {
return MemorySegment.ofArray(array);
}
@Override
int length(char[] arr) {
return arr.length;
}
};
static final CopyHelper<short[], ValueLayout.OfShort> SHORT = new CopyHelper<>(ValueLayout.JAVA_SHORT, short[].class) {
@Override
void copyFromArray(short[] srcArr, int srcIndex, int srcCopyLen, MemorySegment dstSeg, long dstOffsetBytes, ByteOrder bo) {
MemorySegment.copy(srcArr, srcIndex, dstSeg, elementLayout.withOrder(bo), dstOffsetBytes, srcCopyLen);
}
@Override
void copyToArray(MemorySegment srcSeg, long srcOffsetBytes, short[] dstArr, int dstIndex, int dstCopyLen, ByteOrder bo) {
MemorySegment.copy(srcSeg, elementLayout.withOrder(bo), srcOffsetBytes, dstArr, dstIndex, dstCopyLen);
}
@Override
short[] toArray(MemorySegment segment) {
return segment.toArray(elementLayout);
}
@Override
MemorySegment fromArray(short[] array) {
return MemorySegment.ofArray(array);
}
@Override
int length(short[] arr) {
return arr.length;
}
};
static final CopyHelper<int[], ValueLayout.OfInt> INT = new CopyHelper<>(ValueLayout.JAVA_INT, int[].class) {
@Override
void copyFromArray(int[] srcArr, int srcIndex, int srcCopyLen, MemorySegment dstSeg, long dstOffsetBytes, ByteOrder bo) {
MemorySegment.copy(srcArr, srcIndex, dstSeg, elementLayout.withOrder(bo), dstOffsetBytes, srcCopyLen);
}
@Override
void copyToArray(MemorySegment srcSeg, long srcOffsetBytes, int[] dstArr, int dstIndex, int dstCopyLen, ByteOrder bo) {
MemorySegment.copy(srcSeg, elementLayout.withOrder(bo), srcOffsetBytes, dstArr, dstIndex, dstCopyLen);
}
@Override
int[] toArray(MemorySegment segment) {
return segment.toArray(elementLayout);
}
@Override
MemorySegment fromArray(int[] array) {
return MemorySegment.ofArray(array);
}
@Override
int length(int[] arr) {
return arr.length;
}
};
static final CopyHelper<float[], ValueLayout.OfFloat> FLOAT = new CopyHelper<>(ValueLayout.JAVA_FLOAT, float[].class) {
@Override
void copyFromArray(float[] srcArr, int srcIndex, int srcCopyLen, MemorySegment dstSeg, long dstOffsetBytes, ByteOrder bo) {
MemorySegment.copy(srcArr, srcIndex, dstSeg, elementLayout.withOrder(bo), dstOffsetBytes, srcCopyLen);
}
@Override
void copyToArray(MemorySegment srcSeg, long srcOffsetBytes, float[] dstArr, int dstIndex, int dstCopyLen, ByteOrder bo) {
MemorySegment.copy(srcSeg, elementLayout.withOrder(bo), srcOffsetBytes, dstArr, dstIndex, dstCopyLen);
}
@Override
float[] toArray(MemorySegment segment) {
return segment.toArray(elementLayout);
}
@Override
MemorySegment fromArray(float[] array) {
return MemorySegment.ofArray(array);
}
@Override
int length(float[] arr) {
return arr.length;
}
};
static final CopyHelper<long[], ValueLayout.OfLong> LONG = new CopyHelper<>(ValueLayout.JAVA_LONG, long[].class) {
@Override
void copyFromArray(long[] srcArr, int srcIndex, int srcCopyLen, MemorySegment dstSeg, long dstOffsetBytes, ByteOrder bo) {
MemorySegment.copy(srcArr, srcIndex, dstSeg, elementLayout.withOrder(bo), dstOffsetBytes, srcCopyLen);
}
@Override
void copyToArray(MemorySegment srcSeg, long srcOffsetBytes, long[] dstArr, int dstIndex, int dstCopyLen, ByteOrder bo) {
MemorySegment.copy(srcSeg, elementLayout.withOrder(bo), srcOffsetBytes, dstArr, dstIndex, dstCopyLen);
}
@Override
long[] toArray(MemorySegment segment) {
return segment.toArray(elementLayout);
}
@Override
MemorySegment fromArray(long[] array) {
return MemorySegment.ofArray(array);
}
@Override
int length(long[] arr) {
return arr.length;
}
};
static final CopyHelper<double[], ValueLayout.OfDouble> DOUBLE = new CopyHelper<>(ValueLayout.JAVA_DOUBLE, double[].class) {
@Override
void copyFromArray(double[] srcArr, int srcIndex, int srcCopyLen, MemorySegment dstSeg, long dstOffsetBytes, ByteOrder bo) {
MemorySegment.copy(srcArr, srcIndex, dstSeg, elementLayout.withOrder(bo), dstOffsetBytes, srcCopyLen);
}
@Override
void copyToArray(MemorySegment srcSeg, long srcOffsetBytes, double[] dstArr, int dstIndex, int dstCopyLen, ByteOrder bo) {
MemorySegment.copy(srcSeg, elementLayout.withOrder(bo), srcOffsetBytes, dstArr, dstIndex, dstCopyLen);
}
@Override
double[] toArray(MemorySegment segment) {
return segment.toArray(elementLayout);
}
@Override
MemorySegment fromArray(double[] array) {
return MemorySegment.ofArray(array);
}
@Override
int length(double[] arr) {
return arr.length;
}
};
}
@DataProvider
Object[][] copyModesAndHelpers() {
CopyHelper<?, ?>[] helpers = { CopyHelper.BYTE, CopyHelper.CHAR, CopyHelper.SHORT, CopyHelper.INT,
CopyHelper.FLOAT, CopyHelper.LONG, CopyHelper.DOUBLE };
List<Object[]> results = new ArrayList<>();
for (CopyHelper<?, ?> helper : helpers) {
for (CopyMode mode : CopyMode.values()) {
results.add(new Object[] { mode, helper, helper.toString() });
}
}
return results.stream().toArray(Object[][]::new);
}
}

View file

@ -30,7 +30,6 @@
import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayout.PathElement; import jdk.incubator.foreign.MemoryLayout.PathElement;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.ResourceScope;
import jdk.incubator.foreign.SequenceLayout; import jdk.incubator.foreign.SequenceLayout;
@ -43,45 +42,52 @@ import java.util.function.Function;
import org.testng.annotations.*; import org.testng.annotations.*;
import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE;
import static jdk.incubator.foreign.ValueLayout.JAVA_CHAR;
import static jdk.incubator.foreign.ValueLayout.JAVA_DOUBLE;
import static jdk.incubator.foreign.ValueLayout.JAVA_FLOAT;
import static jdk.incubator.foreign.ValueLayout.JAVA_INT;
import static jdk.incubator.foreign.ValueLayout.JAVA_LONG;
import static jdk.incubator.foreign.ValueLayout.JAVA_SHORT;
import static org.testng.Assert.*; import static org.testng.Assert.*;
public class TestArrays { public class TestArrays {
static SequenceLayout bytes = MemoryLayout.sequenceLayout(100, static SequenceLayout bytes = MemoryLayout.sequenceLayout(100,
MemoryLayouts.JAVA_BYTE JAVA_BYTE
); );
static SequenceLayout chars = MemoryLayout.sequenceLayout(100, static SequenceLayout chars = MemoryLayout.sequenceLayout(100,
MemoryLayouts.JAVA_CHAR JAVA_CHAR
); );
static SequenceLayout shorts = MemoryLayout.sequenceLayout(100, static SequenceLayout shorts = MemoryLayout.sequenceLayout(100,
MemoryLayouts.JAVA_SHORT JAVA_SHORT
); );
static SequenceLayout ints = MemoryLayout.sequenceLayout(100, static SequenceLayout ints = MemoryLayout.sequenceLayout(100,
MemoryLayouts.JAVA_INT JAVA_INT
); );
static SequenceLayout floats = MemoryLayout.sequenceLayout(100, static SequenceLayout floats = MemoryLayout.sequenceLayout(100,
MemoryLayouts.JAVA_FLOAT JAVA_FLOAT
); );
static SequenceLayout longs = MemoryLayout.sequenceLayout(100, static SequenceLayout longs = MemoryLayout.sequenceLayout(100,
MemoryLayouts.JAVA_LONG JAVA_LONG
); );
static SequenceLayout doubles = MemoryLayout.sequenceLayout(100, static SequenceLayout doubles = MemoryLayout.sequenceLayout(100,
MemoryLayouts.JAVA_DOUBLE JAVA_DOUBLE
); );
static VarHandle byteHandle = bytes.varHandle(byte.class, PathElement.sequenceElement()); static VarHandle byteHandle = bytes.varHandle(PathElement.sequenceElement());
static VarHandle charHandle = chars.varHandle(char.class, PathElement.sequenceElement()); static VarHandle charHandle = chars.varHandle(PathElement.sequenceElement());
static VarHandle shortHandle = shorts.varHandle(short.class, PathElement.sequenceElement()); static VarHandle shortHandle = shorts.varHandle(PathElement.sequenceElement());
static VarHandle intHandle = ints.varHandle(int.class, PathElement.sequenceElement()); static VarHandle intHandle = ints.varHandle(PathElement.sequenceElement());
static VarHandle floatHandle = floats.varHandle(float.class, PathElement.sequenceElement()); static VarHandle floatHandle = floats.varHandle(PathElement.sequenceElement());
static VarHandle longHandle = longs.varHandle(long.class, PathElement.sequenceElement()); static VarHandle longHandle = longs.varHandle(PathElement.sequenceElement());
static VarHandle doubleHandle = doubles.varHandle(double.class, PathElement.sequenceElement()); static VarHandle doubleHandle = doubles.varHandle(PathElement.sequenceElement());
static void initBytes(MemorySegment base, SequenceLayout seq, BiConsumer<MemorySegment, Long> handleSetter) { static void initBytes(MemorySegment base, SequenceLayout seq, BiConsumer<MemorySegment, Long> handleSetter) {
for (long i = 0; i < seq.elementCount().getAsLong() ; i++) { for (long i = 0; i < seq.elementCount().getAsLong() ; i++) {
@ -112,7 +118,7 @@ public class TestArrays {
public void testTooBigForArray(MemoryLayout layout, Function<MemorySegment, Object> arrayFactory) { public void testTooBigForArray(MemoryLayout layout, Function<MemorySegment, Object> arrayFactory) {
MemoryLayout seq = MemoryLayout.sequenceLayout((Integer.MAX_VALUE * layout.byteSize()) + 1, layout); MemoryLayout seq = MemoryLayout.sequenceLayout((Integer.MAX_VALUE * layout.byteSize()) + 1, layout);
//do not really allocate here, as it's way too much memory //do not really allocate here, as it's way too much memory
MemorySegment segment = MemoryAddress.NULL.asSegment(seq.byteSize(), ResourceScope.globalScope()); MemorySegment segment = MemorySegment.ofAddress(MemoryAddress.NULL, seq.byteSize(), ResourceScope.globalScope());
arrayFactory.apply(segment); arrayFactory.apply(segment);
} }
@ -152,19 +158,19 @@ public class TestArrays {
(base) -> initBytes(base, doubles, (addr, pos) -> doubleHandle.set(addr, pos, (double)(long)pos)); (base) -> initBytes(base, doubles, (addr, pos) -> doubleHandle.set(addr, pos, (double)(long)pos));
Consumer<MemorySegment> byteChecker = Consumer<MemorySegment> byteChecker =
(base) -> checkBytes(base, bytes, MemorySegment::toByteArray, (addr, pos) -> (byte)byteHandle.get(addr, pos)); (base) -> checkBytes(base, bytes, s -> s.toArray(JAVA_BYTE), (addr, pos) -> (byte)byteHandle.get(addr, pos));
Consumer<MemorySegment> shortChecker = Consumer<MemorySegment> shortChecker =
(base) -> checkBytes(base, shorts, MemorySegment::toShortArray, (addr, pos) -> (short)shortHandle.get(addr, pos)); (base) -> checkBytes(base, shorts, s -> s.toArray(JAVA_SHORT), (addr, pos) -> (short)shortHandle.get(addr, pos));
Consumer<MemorySegment> charChecker = Consumer<MemorySegment> charChecker =
(base) -> checkBytes(base, chars, MemorySegment::toCharArray, (addr, pos) -> (char)charHandle.get(addr, pos)); (base) -> checkBytes(base, chars, s -> s.toArray(JAVA_CHAR), (addr, pos) -> (char)charHandle.get(addr, pos));
Consumer<MemorySegment> intChecker = Consumer<MemorySegment> intChecker =
(base) -> checkBytes(base, ints, MemorySegment::toIntArray, (addr, pos) -> (int)intHandle.get(addr, pos)); (base) -> checkBytes(base, ints, s -> s.toArray(JAVA_INT), (addr, pos) -> (int)intHandle.get(addr, pos));
Consumer<MemorySegment> floatChecker = Consumer<MemorySegment> floatChecker =
(base) -> checkBytes(base, floats, MemorySegment::toFloatArray, (addr, pos) -> (float)floatHandle.get(addr, pos)); (base) -> checkBytes(base, floats, s -> s.toArray(JAVA_FLOAT), (addr, pos) -> (float)floatHandle.get(addr, pos));
Consumer<MemorySegment> longChecker = Consumer<MemorySegment> longChecker =
(base) -> checkBytes(base, longs, MemorySegment::toLongArray, (addr, pos) -> (long)longHandle.get(addr, pos)); (base) -> checkBytes(base, longs, s -> s.toArray(JAVA_LONG), (addr, pos) -> (long)longHandle.get(addr, pos));
Consumer<MemorySegment> doubleChecker = Consumer<MemorySegment> doubleChecker =
(base) -> checkBytes(base, doubles, MemorySegment::toDoubleArray, (addr, pos) -> (double)doubleHandle.get(addr, pos)); (base) -> checkBytes(base, doubles, s -> s.toArray(JAVA_DOUBLE), (addr, pos) -> (double)doubleHandle.get(addr, pos));
return new Object[][]{ return new Object[][]{
{byteInitializer, byteChecker, bytes}, {byteInitializer, byteChecker, bytes},
@ -180,13 +186,13 @@ public class TestArrays {
@DataProvider(name = "elemLayouts") @DataProvider(name = "elemLayouts")
public Object[][] elemLayouts() { public Object[][] elemLayouts() {
return new Object[][] { return new Object[][] {
{ MemoryLayouts.JAVA_BYTE, (Function<MemorySegment, Object>) MemorySegment::toByteArray }, { JAVA_BYTE, (Function<MemorySegment, Object>)s -> s.toArray(JAVA_BYTE)},
{ MemoryLayouts.JAVA_SHORT, (Function<MemorySegment, Object>) MemorySegment::toShortArray }, { JAVA_SHORT, (Function<MemorySegment, Object>) s -> s.toArray(JAVA_SHORT)},
{ MemoryLayouts.JAVA_CHAR, (Function<MemorySegment, Object>) MemorySegment::toCharArray }, { JAVA_CHAR, (Function<MemorySegment, Object>) s -> s.toArray(JAVA_CHAR)},
{ MemoryLayouts.JAVA_INT, (Function<MemorySegment, Object>) MemorySegment::toIntArray }, { JAVA_INT, (Function<MemorySegment, Object>)s -> s.toArray(JAVA_INT)},
{ MemoryLayouts.JAVA_FLOAT, (Function<MemorySegment, Object>) MemorySegment::toFloatArray }, { JAVA_FLOAT, (Function<MemorySegment, Object>)s -> s.toArray(JAVA_FLOAT)},
{ MemoryLayouts.JAVA_LONG, (Function<MemorySegment, Object>) MemorySegment::toLongArray }, { JAVA_LONG, (Function<MemorySegment, Object>)s -> s.toArray(JAVA_LONG)},
{ MemoryLayouts.JAVA_DOUBLE, (Function<MemorySegment, Object>) MemorySegment::toDoubleArray } { JAVA_DOUBLE, (Function<MemorySegment, Object>)s -> s.toArray(JAVA_DOUBLE)}
}; };
} }
} }

View file

@ -28,8 +28,6 @@
* @run testng/othervm --enable-native-access=ALL-UNNAMED TestByteBuffer * @run testng/othervm --enable-native-access=ALL-UNNAMED TestByteBuffer
*/ */
import jdk.incubator.foreign.MemoryAccess;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
@ -82,6 +80,13 @@ import org.testng.SkipException;
import org.testng.annotations.*; import org.testng.annotations.*;
import sun.nio.ch.DirectBuffer; import sun.nio.ch.DirectBuffer;
import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE;
import static jdk.incubator.foreign.ValueLayout.JAVA_CHAR;
import static jdk.incubator.foreign.ValueLayout.JAVA_DOUBLE;
import static jdk.incubator.foreign.ValueLayout.JAVA_FLOAT;
import static jdk.incubator.foreign.ValueLayout.JAVA_INT;
import static jdk.incubator.foreign.ValueLayout.JAVA_LONG;
import static jdk.incubator.foreign.ValueLayout.JAVA_SHORT;
import static org.testng.Assert.*; import static org.testng.Assert.*;
public class TestByteBuffer { public class TestByteBuffer {
@ -102,40 +107,40 @@ public class TestByteBuffer {
static SequenceLayout tuples = MemoryLayout.sequenceLayout(500, static SequenceLayout tuples = MemoryLayout.sequenceLayout(500,
MemoryLayout.structLayout( MemoryLayout.structLayout(
MemoryLayouts.BITS_32_BE.withName("index"), JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN).withName("index"),
MemoryLayouts.BITS_32_BE.withName("value") JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN).withName("value")
)); ));
static SequenceLayout bytes = MemoryLayout.sequenceLayout(100, static SequenceLayout bytes = MemoryLayout.sequenceLayout(100,
MemoryLayouts.BITS_8_BE JAVA_BYTE
); );
static SequenceLayout chars = MemoryLayout.sequenceLayout(100, static SequenceLayout chars = MemoryLayout.sequenceLayout(100,
MemoryLayouts.BITS_16_BE JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN)
); );
static SequenceLayout shorts = MemoryLayout.sequenceLayout(100, static SequenceLayout shorts = MemoryLayout.sequenceLayout(100,
MemoryLayouts.BITS_16_BE JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN)
); );
static SequenceLayout ints = MemoryLayout.sequenceLayout(100, static SequenceLayout ints = MemoryLayout.sequenceLayout(100,
MemoryLayouts.BITS_32_BE JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN)
); );
static SequenceLayout floats = MemoryLayout.sequenceLayout(100, static SequenceLayout floats = MemoryLayout.sequenceLayout(100,
MemoryLayouts.BITS_32_BE JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN)
); );
static SequenceLayout longs = MemoryLayout.sequenceLayout(100, static SequenceLayout longs = MemoryLayout.sequenceLayout(100,
MemoryLayouts.BITS_64_BE JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN)
); );
static SequenceLayout doubles = MemoryLayout.sequenceLayout(100, static SequenceLayout doubles = MemoryLayout.sequenceLayout(100,
MemoryLayouts.BITS_64_BE JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN)
); );
static VarHandle indexHandle = tuples.varHandle(int.class, PathElement.sequenceElement(), PathElement.groupElement("index")); static VarHandle indexHandle = tuples.varHandle(PathElement.sequenceElement(), PathElement.groupElement("index"));
static VarHandle valueHandle = tuples.varHandle(float.class, PathElement.sequenceElement(), PathElement.groupElement("value")); static VarHandle valueHandle = tuples.varHandle(PathElement.sequenceElement(), PathElement.groupElement("value"));
static void initTuples(MemorySegment base, long count) { static void initTuples(MemorySegment base, long count) {
for (long i = 0; i < count ; i++) { for (long i = 0; i < count ; i++) {
@ -263,7 +268,7 @@ public class TestByteBuffer {
} }
} }
@Test(dataProvider = "mappedOps", expectedExceptions = UnsupportedOperationException.class) @Test(dataProvider = "mappedOps", expectedExceptions = IllegalStateException.class)
public void testMappedSegmentOperations(MappedSegmentOp mappedBufferOp) throws Throwable { public void testMappedSegmentOperations(MappedSegmentOp mappedBufferOp) throws Throwable {
File f = new File("test3.out"); File f = new File("test3.out");
f.createNewFile(); f.createNewFile();
@ -324,6 +329,9 @@ public class TestByteBuffer {
segment.isLoaded(); segment.isLoaded();
segment.unload(); segment.unload();
segment.isLoaded(); segment.isLoaded();
} catch(IOException e) {
if (e.getMessage().equals("Function not implemented"))
throw new SkipException(e.getMessage(), e);
} }
} }
@ -480,7 +488,7 @@ public class TestByteBuffer {
@Test(expectedExceptions = IllegalStateException.class) @Test(expectedExceptions = IllegalStateException.class)
public void testTooBigForByteBuffer() { public void testTooBigForByteBuffer() {
MemorySegment segment = MemoryAddress.NULL.asSegment(Integer.MAX_VALUE + 10L, ResourceScope.globalScope()); MemorySegment segment = MemorySegment.ofAddress(MemoryAddress.NULL, Integer.MAX_VALUE + 10L, ResourceScope.newImplicitScope());
segment.asByteBuffer(); segment.asByteBuffer();
} }
@ -511,7 +519,7 @@ public class TestByteBuffer {
try (ResourceScope scope = ResourceScope.newConfinedScope()) { try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0, SIZE, FileChannel.MapMode.READ_WRITE, scope); MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0, SIZE, FileChannel.MapMode.READ_WRITE, scope);
for (byte offset = 0; offset < SIZE; offset++) { for (byte offset = 0; offset < SIZE; offset++) {
MemoryAccess.setByteAtOffset(segment, offset, offset); segment.set(JAVA_BYTE, offset, offset);
} }
segment.force(); segment.force();
} }
@ -519,7 +527,7 @@ public class TestByteBuffer {
for (int offset = 0 ; offset < SIZE ; offset++) { for (int offset = 0 ; offset < SIZE ; offset++) {
try (ResourceScope scope = ResourceScope.newConfinedScope()) { try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment segment = MemorySegment.mapFile(f.toPath(), offset, SIZE - offset, FileChannel.MapMode.READ_ONLY, scope); MemorySegment segment = MemorySegment.mapFile(f.toPath(), offset, SIZE - offset, FileChannel.MapMode.READ_ONLY, scope);
assertEquals(MemoryAccess.getByte(segment), offset); assertEquals(segment.get(JAVA_BYTE, 0), offset);
} }
} }
} }
@ -636,13 +644,13 @@ public class TestByteBuffer {
@Test(expectedExceptions = IllegalStateException.class) @Test(expectedExceptions = IllegalStateException.class)
public void testDeadAccessOnClosedBufferSegment() { public void testDeadAccessOnClosedBufferSegment() {
MemorySegment s1 = MemorySegment.allocateNative(MemoryLayouts.JAVA_INT, ResourceScope.newConfinedScope()); MemorySegment s1 = MemorySegment.allocateNative(JAVA_INT, ResourceScope.newConfinedScope());
MemorySegment s2 = MemorySegment.ofByteBuffer(s1.asByteBuffer()); MemorySegment s2 = MemorySegment.ofByteBuffer(s1.asByteBuffer());
// memory freed // memory freed
s1.scope().close(); s1.scope().close();
MemoryAccess.setInt(s2, 10); // Dead access! s2.set(JAVA_INT, 0, 10); // Dead access!
} }
@Test(dataProvider = "allScopes") @Test(dataProvider = "allScopes")
@ -654,7 +662,7 @@ public class TestByteBuffer {
ResourceScope scp = closeableScopeOrNull(scope = scopeSupplier.get())) { ResourceScope scp = closeableScopeOrNull(scope = scopeSupplier.get())) {
MemorySegment segment = MemorySegment.allocateNative(10, 1, scope); MemorySegment segment = MemorySegment.allocateNative(10, 1, scope);
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
MemoryAccess.setByteAtOffset(segment, i, (byte) i); segment.set(JAVA_BYTE, i, (byte) i);
} }
ByteBuffer bb = segment.asByteBuffer(); ByteBuffer bb = segment.asByteBuffer();
assertEquals(channel.write(bb), 10); assertEquals(channel.write(bb), 10);
@ -674,7 +682,7 @@ public class TestByteBuffer {
try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
MemorySegment segment = MemorySegment.allocateNative(10, scopeSupplier.get()); MemorySegment segment = MemorySegment.allocateNative(10, scopeSupplier.get());
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
MemoryAccess.setByteAtOffset(segment, i, (byte) i); segment.set(JAVA_BYTE, i, (byte) i);
} }
ByteBuffer bb = segment.asByteBuffer(); ByteBuffer bb = segment.asByteBuffer();
segment.scope().close(); segment.scope().close();
@ -694,7 +702,7 @@ public class TestByteBuffer {
int newSize = 8; int newSize = 8;
var slice = segment.asSlice(4, newSize); var slice = segment.asSlice(4, newSize);
var bytes = slice.toByteArray(); var bytes = slice.toArray(JAVA_BYTE);
assertEquals(newSize, bytes.length); assertEquals(newSize, bytes.length);
var buffer = slice.asByteBuffer(); var buffer = slice.asByteBuffer();
@ -719,6 +727,7 @@ public class TestByteBuffer {
public static Object[][] segments() throws Throwable { public static Object[][] segments() throws Throwable {
return new Object[][] { return new Object[][] {
{ (Supplier<MemorySegment>) () -> MemorySegment.allocateNative(16, ResourceScope.newImplicitScope()) }, { (Supplier<MemorySegment>) () -> MemorySegment.allocateNative(16, ResourceScope.newImplicitScope()) },
{ (Supplier<MemorySegment>) () -> MemorySegment.allocateNative(16, ResourceScope.newConfinedScope()) },
{ (Supplier<MemorySegment>) () -> MemorySegment.ofArray(new byte[16]) } { (Supplier<MemorySegment>) () -> MemorySegment.ofArray(new byte[16]) }
}; };
} }
@ -729,27 +738,20 @@ public class TestByteBuffer {
{ (Supplier<ResourceScope>) () -> ResourceScope.newSharedScope() }, { (Supplier<ResourceScope>) () -> ResourceScope.newSharedScope() },
{ (Supplier<ResourceScope>) () -> ResourceScope.newConfinedScope() }, { (Supplier<ResourceScope>) () -> ResourceScope.newConfinedScope() },
{ (Supplier<ResourceScope>) () -> ResourceScope.newSharedScope(Cleaner.create()) }, { (Supplier<ResourceScope>) () -> ResourceScope.newSharedScope(Cleaner.create()) },
{ (Supplier<ResourceScope>) () -> ResourceScope.newConfinedScope(Cleaner.create()) } { (Supplier<ResourceScope>) () -> ResourceScope.newConfinedScope(Cleaner.create()) },
}; { (Supplier<ResourceScope>) () -> ResourceScope.newImplicitScope() }
}
@DataProvider(name = "implicitScopes")
public static Object[][] implicitScopes() {
return new Object[][] {
{ (Supplier<ResourceScope>) ResourceScope::newImplicitScope },
{ (Supplier<ResourceScope>) ResourceScope::globalScope },
}; };
} }
@DataProvider(name = "allScopes") @DataProvider(name = "allScopes")
public static Object[][] allScopes() { public static Object[][] allScopes() {
return Stream.of(implicitScopes(), closeableScopes()) return Stream.of(new Object[][] { { (Supplier<ResourceScope>)ResourceScope::globalScope } }, closeableScopes())
.flatMap(Arrays::stream) .flatMap(Arrays::stream)
.toArray(Object[][]::new); .toArray(Object[][]::new);
} }
static ResourceScope closeableScopeOrNull(ResourceScope scope) { static ResourceScope closeableScopeOrNull(ResourceScope scope) {
if (scope.isImplicit()) if (scope == ResourceScope.globalScope())
return null; return null;
return scope; return scope;
} }
@ -812,34 +814,34 @@ public class TestByteBuffer {
@DataProvider(name = "resizeOps") @DataProvider(name = "resizeOps")
public Object[][] resizeOps() { public Object[][] resizeOps() {
Consumer<MemorySegment> byteInitializer = Consumer<MemorySegment> byteInitializer =
(base) -> initBytes(base, bytes, (addr, pos) -> MemoryAccess.setByteAtOffset(addr, pos, (byte)(long)pos)); (base) -> initBytes(base, bytes, (addr, pos) -> addr.set(JAVA_BYTE, pos, (byte)(long)pos));
Consumer<MemorySegment> charInitializer = Consumer<MemorySegment> charInitializer =
(base) -> initBytes(base, chars, (addr, pos) -> MemoryAccess.setCharAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (char)(long)pos)); (base) -> initBytes(base, chars, (addr, pos) -> addr.setAtIndex(JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), pos, (char)(long)pos));
Consumer<MemorySegment> shortInitializer = Consumer<MemorySegment> shortInitializer =
(base) -> initBytes(base, shorts, (addr, pos) -> MemoryAccess.setShortAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (short)(long)pos)); (base) -> initBytes(base, shorts, (addr, pos) -> addr.setAtIndex(JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), pos, (short)(long)pos));
Consumer<MemorySegment> intInitializer = Consumer<MemorySegment> intInitializer =
(base) -> initBytes(base, ints, (addr, pos) -> MemoryAccess.setIntAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (int)(long)pos)); (base) -> initBytes(base, ints, (addr, pos) -> addr.setAtIndex(JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), pos, (int)(long)pos));
Consumer<MemorySegment> floatInitializer = Consumer<MemorySegment> floatInitializer =
(base) -> initBytes(base, floats, (addr, pos) -> MemoryAccess.setFloatAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (float)(long)pos)); (base) -> initBytes(base, floats, (addr, pos) -> addr.setAtIndex(JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), pos, (float)(long)pos));
Consumer<MemorySegment> longInitializer = Consumer<MemorySegment> longInitializer =
(base) -> initBytes(base, longs, (addr, pos) -> MemoryAccess.setLongAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (long)pos)); (base) -> initBytes(base, longs, (addr, pos) -> addr.setAtIndex(JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), pos, (long)pos));
Consumer<MemorySegment> doubleInitializer = Consumer<MemorySegment> doubleInitializer =
(base) -> initBytes(base, doubles, (addr, pos) -> MemoryAccess.setDoubleAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (double)(long)pos)); (base) -> initBytes(base, doubles, (addr, pos) -> addr.setAtIndex(JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), pos, (double)(long)pos));
Consumer<MemorySegment> byteChecker = Consumer<MemorySegment> byteChecker =
(base) -> checkBytes(base, bytes, Function.identity(), (addr, pos) -> MemoryAccess.getByteAtOffset(addr, pos), ByteBuffer::get); (base) -> checkBytes(base, bytes, Function.identity(), (addr, pos) -> addr.get(JAVA_BYTE, pos), ByteBuffer::get);
Consumer<MemorySegment> charChecker = Consumer<MemorySegment> charChecker =
(base) -> checkBytes(base, chars, ByteBuffer::asCharBuffer, (addr, pos) -> MemoryAccess.getCharAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), CharBuffer::get); (base) -> checkBytes(base, chars, ByteBuffer::asCharBuffer, (addr, pos) -> addr.getAtIndex(JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), pos), CharBuffer::get);
Consumer<MemorySegment> shortChecker = Consumer<MemorySegment> shortChecker =
(base) -> checkBytes(base, shorts, ByteBuffer::asShortBuffer, (addr, pos) -> MemoryAccess.getShortAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), ShortBuffer::get); (base) -> checkBytes(base, shorts, ByteBuffer::asShortBuffer, (addr, pos) -> addr.getAtIndex(JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), pos), ShortBuffer::get);
Consumer<MemorySegment> intChecker = Consumer<MemorySegment> intChecker =
(base) -> checkBytes(base, ints, ByteBuffer::asIntBuffer, (addr, pos) -> MemoryAccess.getIntAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), IntBuffer::get); (base) -> checkBytes(base, ints, ByteBuffer::asIntBuffer, (addr, pos) -> addr.getAtIndex(JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), pos), IntBuffer::get);
Consumer<MemorySegment> floatChecker = Consumer<MemorySegment> floatChecker =
(base) -> checkBytes(base, floats, ByteBuffer::asFloatBuffer, (addr, pos) -> MemoryAccess.getFloatAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), FloatBuffer::get); (base) -> checkBytes(base, floats, ByteBuffer::asFloatBuffer, (addr, pos) -> addr.getAtIndex(JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), pos), FloatBuffer::get);
Consumer<MemorySegment> longChecker = Consumer<MemorySegment> longChecker =
(base) -> checkBytes(base, longs, ByteBuffer::asLongBuffer, (addr, pos) -> MemoryAccess.getLongAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), LongBuffer::get); (base) -> checkBytes(base, longs, ByteBuffer::asLongBuffer, (addr, pos) -> addr.getAtIndex(JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), pos), LongBuffer::get);
Consumer<MemorySegment> doubleChecker = Consumer<MemorySegment> doubleChecker =
(base) -> checkBytes(base, doubles, ByteBuffer::asDoubleBuffer, (addr, pos) -> MemoryAccess.getDoubleAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), DoubleBuffer::get); (base) -> checkBytes(base, doubles, ByteBuffer::asDoubleBuffer, (addr, pos) -> addr.getAtIndex(JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), pos), DoubleBuffer::get);
return new Object[][]{ return new Object[][]{
{byteChecker, byteInitializer, bytes}, {byteChecker, byteInitializer, bytes},

View file

@ -1,45 +0,0 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64"
* @modules jdk.incubator.foreign/jdk.internal.foreign
* @run testng/othervm TestCircularInit1
*/
import jdk.incubator.foreign.CLinker;
import jdk.internal.foreign.PlatformLayouts;
import org.testng.annotations.Test;
import static org.testng.Assert.assertNotNull;
public class TestCircularInit1 {
@Test
public void testCircularInit() {
System.out.println(PlatformLayouts.Win64.C_CHAR); // trigger clinit
assertNotNull(CLinker.C_CHAR); // should not be null
}
}

View file

@ -1,47 +0,0 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64"
* @modules jdk.incubator.foreign/jdk.internal.foreign
* @run testng/othervm TestCircularInit2
*/
import jdk.incubator.foreign.CLinker;
import jdk.internal.foreign.PlatformLayouts;
import org.testng.annotations.Test;
import static org.testng.Assert.assertNotNull;
public class TestCircularInit2 {
@Test
public void testCircularInit() {
System.out.println(CLinker.C_CHAR); // trigger clinit
assertNotNull(PlatformLayouts.Win64.C_CHAR);
assertNotNull(PlatformLayouts.SysV.C_CHAR);
assertNotNull(PlatformLayouts.AArch64.C_CHAR);
}
}

View file

@ -39,7 +39,15 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import static jdk.incubator.foreign.CLinker.*; import static jdk.incubator.foreign.ValueLayout.ADDRESS;
import static jdk.incubator.foreign.ValueLayout.JAVA_BOOLEAN;
import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE;
import static jdk.incubator.foreign.ValueLayout.JAVA_CHAR;
import static jdk.incubator.foreign.ValueLayout.JAVA_DOUBLE;
import static jdk.incubator.foreign.ValueLayout.JAVA_FLOAT;
import static jdk.incubator.foreign.ValueLayout.JAVA_INT;
import static jdk.incubator.foreign.ValueLayout.JAVA_LONG;
import static jdk.incubator.foreign.ValueLayout.JAVA_SHORT;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
public class TestCondy { public class TestCondy {
@ -53,14 +61,15 @@ public class TestCondy {
private static final MemoryLayout[] constants = { private static final MemoryLayout[] constants = {
C_CHAR, JAVA_BOOLEAN,
C_SHORT, JAVA_CHAR,
C_INT, JAVA_BYTE,
C_LONG, JAVA_SHORT,
C_LONG_LONG, JAVA_INT,
C_FLOAT, JAVA_FLOAT,
C_DOUBLE, JAVA_LONG,
C_POINTER JAVA_DOUBLE,
ADDRESS
}; };
@DataProvider @DataProvider
@ -78,7 +87,7 @@ public class TestCondy {
} }
testValues.add(FunctionDescriptor.ofVoid(constants)); testValues.add(FunctionDescriptor.ofVoid(constants));
testValues.add(FunctionDescriptor.of(C_CHAR, constants)); testValues.add(FunctionDescriptor.of(JAVA_BYTE, constants));
return testValues.stream().map(e -> new Object[] { e }).toArray(Object[][]::new); return testValues.stream().map(e -> new Object[] { e }).toArray(Object[][]::new);
} }

View file

@ -33,8 +33,11 @@
* TestDowncall * TestDowncall
*/ */
import jdk.incubator.foreign.Addressable;
import jdk.incubator.foreign.CLinker; import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.FunctionDescriptor; import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.NativeSymbol;
import jdk.incubator.foreign.ResourceScope;
import jdk.incubator.foreign.SymbolLookup; import jdk.incubator.foreign.SymbolLookup;
import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
@ -52,7 +55,7 @@ import static org.testng.Assert.*;
public class TestDowncall extends CallGeneratorHelper { public class TestDowncall extends CallGeneratorHelper {
static CLinker abi = CLinker.getInstance(); static CLinker abi = CLinker.systemCLinker();
static { static {
System.loadLibrary("TestDowncall"); System.loadLibrary("TestDowncall");
} }
@ -62,64 +65,37 @@ public class TestDowncall extends CallGeneratorHelper {
@Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class) @Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class)
public void testDowncall(int count, String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable { public void testDowncall(int count, String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {
List<Consumer<Object>> checks = new ArrayList<>(); List<Consumer<Object>> checks = new ArrayList<>();
MemoryAddress addr = LOOKUP.lookup(fName).get(); NativeSymbol addr = LOOKUP.lookup(fName).get();
MethodType mt = methodType(ret, paramTypes, fields); MethodType mt = methodType(ret, paramTypes, fields);
FunctionDescriptor descriptor = function(ret, paramTypes, fields); FunctionDescriptor descriptor = function(ret, paramTypes, fields);
Object[] args = makeArgs(paramTypes, fields, checks); Object[] args = makeArgs(paramTypes, fields, checks);
try (NativeScope scope = new NativeScope()) { try (ResourceScope scope = ResourceScope.newSharedScope()) {
boolean needsScope = mt.returnType().equals(MemorySegment.class); boolean needsScope = mt.returnType().equals(MemorySegment.class);
Object res = doCall(addr, scope, mt, descriptor, args); SegmentAllocator allocator = needsScope ?
SegmentAllocator.newNativeArena(scope) :
THROWING_ALLOCATOR;
Object res = doCall(addr, allocator, descriptor, args);
if (ret == Ret.NON_VOID) { if (ret == Ret.NON_VOID) {
checks.forEach(c -> c.accept(res)); checks.forEach(c -> c.accept(res));
if (needsScope) { if (needsScope) {
// check that return struct has indeed been allocated in the native scope // check that return struct has indeed been allocated in the native scope
assertEquals(((MemorySegment) res).scope(), scope.scope()); assertEquals(((MemorySegment) res).scope(), scope);
assertEquals(scope.allocatedBytes(), descriptor.returnLayout().get().byteSize());
} else {
// if here, there should be no allocation through the scope!
assertEquals(scope.allocatedBytes(), 0L);
}
} else {
// if here, there should be no allocation through the scope!
assertEquals(scope.allocatedBytes(), 0L);
}
}
}
@Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class)
public void testDowncallNoScope(int count, String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {
List<Consumer<Object>> checks = new ArrayList<>();
MemoryAddress addr = LOOKUP.lookup(fName).get();
MethodType mt = methodType(ret, paramTypes, fields);
FunctionDescriptor descriptor = function(ret, paramTypes, fields);
Object[] args = makeArgs(paramTypes, fields, checks);
boolean needsScope = mt.returnType().equals(MemorySegment.class);
Object res = doCall(addr, IMPLICIT_ALLOCATOR, mt, descriptor, args);
if (ret == Ret.NON_VOID) {
checks.forEach(c -> c.accept(res));
if (needsScope) {
// check that return struct has indeed been allocated in the default scope
try {
((MemorySegment)res).scope().close(); // should throw
fail("Expected exception!");
} catch (UnsupportedOperationException ex) {
// ok
} }
} }
} }
} }
Object doCall(MemoryAddress addr, SegmentAllocator allocator, MethodType type, FunctionDescriptor descriptor, Object[] args) throws Throwable { Object doCall(NativeSymbol symbol, SegmentAllocator allocator, FunctionDescriptor descriptor, Object[] args) throws Throwable {
MethodHandle mh = abi.downcallHandle(addr, allocator, type, descriptor); MethodHandle mh = downcallHandle(abi, symbol, allocator, descriptor);
Object res = mh.invokeWithArguments(args); Object res = mh.invokeWithArguments(args);
return res; return res;
} }
static MethodType methodType(Ret ret, List<ParamType> params, List<StructFieldType> fields) { static MethodType methodType(Ret ret, List<ParamType> params, List<StructFieldType> fields) {
MethodType mt = ret == Ret.VOID ? MethodType mt = ret == Ret.VOID ?
MethodType.methodType(void.class) : MethodType.methodType(paramCarrier(params.get(0).layout(fields))); MethodType.methodType(void.class) : MethodType.methodType(carrier(params.get(0).layout(fields), false));
for (ParamType p : params) { for (ParamType p : params) {
mt = mt.appendParameterTypes(paramCarrier(p.layout(fields))); mt = mt.appendParameterTypes(carrier(p.layout(fields), true));
} }
return mt; return mt;
} }

View file

@ -29,18 +29,16 @@
* @run testng/othervm --enable-native-access=ALL-UNNAMED TestFree * @run testng/othervm --enable-native-access=ALL-UNNAMED TestFree
*/ */
import jdk.incubator.foreign.MemoryAccess;
import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.ResourceScope;
import static jdk.incubator.foreign.CLinker.*;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
public class TestFree { public class TestFree extends NativeTestHelper {
private static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements) { private static MemorySegment asArray(MemoryAddress addr, MemoryLayout layout, int numElements) {
return addr.asSegment(numElements * layout.byteSize(), ResourceScope.globalScope()); return MemorySegment.ofAddress(addr, numElements * layout.byteSize(), ResourceScope.globalScope());
} }
public void test() throws Throwable { public void test() throws Throwable {
@ -48,8 +46,8 @@ public class TestFree {
MemoryAddress addr = allocateMemory(str.length() + 1); MemoryAddress addr = allocateMemory(str.length() + 1);
MemorySegment seg = asArray(addr, C_CHAR, str.length() + 1); MemorySegment seg = asArray(addr, C_CHAR, str.length() + 1);
seg.copyFrom(MemorySegment.ofArray(str.getBytes())); seg.copyFrom(MemorySegment.ofArray(str.getBytes()));
MemoryAccess.setByteAtOffset(seg, str.length(), (byte)0); seg.set(C_CHAR, str.length(), (byte)0);
assertEquals(str, toJavaString(seg)); assertEquals(str, seg.getUtf8String(0));
freeMemory(addr); freeMemory(addr);
} }
} }

View file

@ -25,7 +25,7 @@
/* /*
* @test * @test
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64"
* @run testng TestFunctionDescriptor * @run testng/othervm --enable-native-access=ALL-UNNAMED TestFunctionDescriptor
*/ */
import jdk.incubator.foreign.FunctionDescriptor; import jdk.incubator.foreign.FunctionDescriptor;
@ -37,15 +37,11 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static jdk.incubator.foreign.CLinker.C_DOUBLE;
import static jdk.incubator.foreign.CLinker.C_INT;
import static jdk.incubator.foreign.CLinker.C_LONG_LONG;
import static jdk.incubator.foreign.CLinker.C_POINTER;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue; import static org.testng.Assert.assertTrue;
public class TestFunctionDescriptor { public class TestFunctionDescriptor extends NativeTestHelper {
static final String DUMMY_ATTR = "dummy"; static final String DUMMY_ATTR = "dummy";
@ -68,56 +64,35 @@ public class TestFunctionDescriptor {
assertFalse(returnLayoutOp.isPresent()); assertFalse(returnLayoutOp.isPresent());
} }
@Test
public void testAttribute() {
FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG);
fd = fd.withAttribute(DUMMY_ATTR, true);
assertEquals(fd.argumentLayouts(), List.of(C_DOUBLE, C_LONG_LONG));
Optional<MemoryLayout> returnLayoutOp = fd.returnLayout();
assertTrue(returnLayoutOp.isPresent());
assertEquals(returnLayoutOp.get(), C_INT);
assertEquals(fd.attributes().collect(Collectors.toList()), List.of(DUMMY_ATTR));
Optional<Constable> attr = fd.attribute(DUMMY_ATTR);
assertTrue(attr.isPresent());
assertEquals(attr.get(), true);
}
@Test @Test
public void testAppendArgumentLayouts() { public void testAppendArgumentLayouts() {
FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG) FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG);
.withAttribute(DUMMY_ATTR, true);
fd = fd.withAppendedArgumentLayouts(C_POINTER); fd = fd.withAppendedArgumentLayouts(C_POINTER);
assertEquals(fd.argumentLayouts(), List.of(C_DOUBLE, C_LONG_LONG, C_POINTER)); assertEquals(fd.argumentLayouts(), List.of(C_DOUBLE, C_LONG_LONG, C_POINTER));
Optional<MemoryLayout> returnLayoutOp = fd.returnLayout(); Optional<MemoryLayout> returnLayoutOp = fd.returnLayout();
assertTrue(returnLayoutOp.isPresent()); assertTrue(returnLayoutOp.isPresent());
assertEquals(returnLayoutOp.get(), C_INT); assertEquals(returnLayoutOp.get(), C_INT);
assertEquals(fd.attributes().collect(Collectors.toList()), List.of(DUMMY_ATTR));
} }
@Test @Test
public void testChangeReturnLayout() { public void testChangeReturnLayout() {
FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG) FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG);
.withAttribute(DUMMY_ATTR, true);
fd = fd.withReturnLayout(C_INT); fd = fd.withReturnLayout(C_INT);
assertEquals(fd.argumentLayouts(), List.of(C_DOUBLE, C_LONG_LONG)); assertEquals(fd.argumentLayouts(), List.of(C_DOUBLE, C_LONG_LONG));
Optional<MemoryLayout> returnLayoutOp = fd.returnLayout(); Optional<MemoryLayout> returnLayoutOp = fd.returnLayout();
assertTrue(returnLayoutOp.isPresent()); assertTrue(returnLayoutOp.isPresent());
assertEquals(returnLayoutOp.get(), C_INT); assertEquals(returnLayoutOp.get(), C_INT);
assertEquals(fd.attributes().collect(Collectors.toList()), List.of(DUMMY_ATTR));
} }
@Test @Test
public void testDropReturnLayout() { public void testDropReturnLayout() {
FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG) FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG);
.withAttribute(DUMMY_ATTR, true);
fd = fd.withVoidReturnLayout(); fd = fd.withVoidReturnLayout();
assertEquals(fd.argumentLayouts(), List.of(C_DOUBLE, C_LONG_LONG)); assertEquals(fd.argumentLayouts(), List.of(C_DOUBLE, C_LONG_LONG));
Optional<MemoryLayout> returnLayoutOp = fd.returnLayout(); Optional<MemoryLayout> returnLayoutOp = fd.returnLayout();
assertFalse(returnLayoutOp.isPresent()); assertFalse(returnLayoutOp.isPresent());
assertEquals(fd.attributes().collect(Collectors.toList()), List.of(DUMMY_ATTR));
} }
} }

View file

@ -31,7 +31,6 @@
* @run testng/othervm -XX:-TieredCompilation TestHandshake * @run testng/othervm -XX:-TieredCompilation TestHandshake
*/ */
import jdk.incubator.foreign.MemoryAccess;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
@ -48,6 +47,8 @@ import java.util.concurrent.atomic.AtomicLong;
import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.ResourceScope;
import org.testng.annotations.DataProvider; import org.testng.annotations.DataProvider;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE;
import static org.testng.Assert.*; import static org.testng.Assert.*;
public class TestHandshake { public class TestHandshake {
@ -150,7 +151,7 @@ public class TestHandshake {
void doAccess() { void doAccess() {
int sum = 0; int sum = 0;
for (int i = 0; i < segment.byteSize(); i++) { for (int i = 0; i < segment.byteSize(); i++) {
sum += MemoryAccess.getByteAtOffset(segment, i); sum += segment.get(JAVA_BYTE, i);
} }
} }
} }
@ -193,7 +194,7 @@ public class TestHandshake {
super(id, segment); super(id, segment);
this.copy = MemorySegment.allocateNative(SEGMENT_SIZE, 1, segment.scope()); this.copy = MemorySegment.allocateNative(SEGMENT_SIZE, 1, segment.scope());
copy.copyFrom(segment); copy.copyFrom(segment);
MemoryAccess.setByteAtOffset(copy, ThreadLocalRandom.current().nextInt(SEGMENT_SIZE), (byte)42); copy.set(JAVA_BYTE, ThreadLocalRandom.current().nextInt(SEGMENT_SIZE), (byte)42);
} }
@Override @Override

View file

@ -32,26 +32,23 @@ import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.FunctionDescriptor; import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.NativeSymbol;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ResourceScope;
import org.testng.annotations.DataProvider; import org.testng.annotations.DataProvider;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import java.lang.invoke.MethodType;
import static jdk.incubator.foreign.CLinker.C_INT;
import static org.testng.Assert.assertTrue; import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail; import static org.testng.Assert.fail;
public class TestIllegalLink { public class TestIllegalLink extends NativeTestHelper {
private static final MemoryAddress DUMMY_TARGET = MemoryAddress.ofLong(1); private static final NativeSymbol DUMMY_TARGET = NativeSymbol.ofAddress("dummy", MemoryAddress.ofLong(1), ResourceScope.globalScope());
private static final CLinker ABI = CLinker.getInstance(); private static final CLinker ABI = CLinker.systemCLinker();
@Test(dataProvider = "types") @Test(dataProvider = "types")
public void testTypeMismatch(MethodType mt, FunctionDescriptor desc, String expectedExceptionMessage) { public void testTypeMismatch(FunctionDescriptor desc, String expectedExceptionMessage) {
try { try {
ABI.downcallHandle(DUMMY_TARGET, mt, desc); ABI.downcallHandle(DUMMY_TARGET, desc);
fail("Expected IllegalArgumentException was not thrown"); fail("Expected IllegalArgumentException was not thrown");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
assertTrue(e.getMessage().contains(expectedExceptionMessage)); assertTrue(e.getMessage().contains(expectedExceptionMessage));
@ -62,49 +59,20 @@ public class TestIllegalLink {
public static Object[][] types() { public static Object[][] types() {
return new Object[][]{ return new Object[][]{
{ {
MethodType.methodType(void.class), FunctionDescriptor.of(MemoryLayout.paddingLayout(64)),
FunctionDescriptor.of(C_INT), "Unsupported layout: x64"
"Return type mismatch"
}, },
{ {
MethodType.methodType(void.class),
FunctionDescriptor.ofVoid(C_INT),
"Arity mismatch"
},
{
MethodType.methodType(void.class, int.class),
FunctionDescriptor.ofVoid(MemoryLayout.paddingLayout(32)),
"Expected a ValueLayout"
},
{
MethodType.methodType(void.class, boolean.class),
FunctionDescriptor.ofVoid(MemoryLayouts.BITS_8_LE),
"Unsupported carrier"
},
{
MethodType.methodType(void.class, int.class),
FunctionDescriptor.ofVoid(MemoryLayouts.BITS_64_LE),
"Carrier size mismatch"
},
{
MethodType.methodType(void.class, MemoryAddress.class),
FunctionDescriptor.ofVoid(MemoryLayout.paddingLayout(64)), FunctionDescriptor.ofVoid(MemoryLayout.paddingLayout(64)),
"Expected a ValueLayout" "Unsupported layout: x64"
}, },
{ {
MethodType.methodType(void.class, MemoryAddress.class), FunctionDescriptor.of(MemoryLayout.sequenceLayout(C_INT)),
FunctionDescriptor.ofVoid(MemoryLayouts.BITS_16_LE), "Unsupported layout: [:b32]"
"Address size mismatch"
}, },
{ {
MethodType.methodType(void.class, MemorySegment.class), FunctionDescriptor.ofVoid(MemoryLayout.sequenceLayout(C_INT)),
FunctionDescriptor.ofVoid(MemoryLayouts.BITS_64_LE), "Unsupported layout: [:b32]"
"Expected a GroupLayout"
},
{
MethodType.methodType(void.class, String.class),
FunctionDescriptor.ofVoid(MemoryLayouts.BITS_64_LE),
"Unsupported carrier"
}, },
}; };
} }

View file

@ -42,17 +42,17 @@ import java.util.List;
import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.NativeSymbol;
import jdk.incubator.foreign.SymbolLookup; import jdk.incubator.foreign.SymbolLookup;
import org.testng.annotations.*; import org.testng.annotations.*;
import static java.lang.invoke.MethodType.methodType; import static java.lang.invoke.MethodType.methodType;
import static jdk.incubator.foreign.CLinker.*; import static jdk.incubator.foreign.ValueLayout.JAVA_CHAR;
import static jdk.incubator.foreign.FunctionDescriptor.TRIVIAL_ATTRIBUTE_NAME;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
public class TestIntrinsics { public class TestIntrinsics extends NativeTestHelper {
static final CLinker abi = CLinker.getInstance(); static final CLinker abi = CLinker.systemCLinker();
static { static {
System.loadLibrary("Intrinsics"); System.loadLibrary("Intrinsics");
} }
@ -88,52 +88,48 @@ public class TestIntrinsics {
} }
AddIdentity addIdentity = (name, carrier, layout, arg) -> { AddIdentity addIdentity = (name, carrier, layout, arg) -> {
MemoryAddress ma = LOOKUP.lookup(name).get(); NativeSymbol ma = LOOKUP.lookup(name).get();
MethodType mt = methodType(carrier, carrier); MethodType mt = methodType(carrier, carrier);
FunctionDescriptor fd = FunctionDescriptor.of(layout, layout); FunctionDescriptor fd = FunctionDescriptor.of(layout, layout);
tests.add(abi.downcallHandle(ma, mt, fd), arg, arg); tests.add(abi.downcallHandle(ma, fd), arg, arg);
tests.add(abi.downcallHandle(ma, mt, fd.withAttribute(TRIVIAL_ATTRIBUTE_NAME, true)), arg, arg); tests.add(abi.downcallHandle(fd), arg, ma, arg);
tests.add(abi.downcallHandle(mt, fd), arg, ma, arg);
}; };
{ // empty { // empty
MemoryAddress ma = LOOKUP.lookup("empty").get(); NativeSymbol ma = LOOKUP.lookup("empty").get();
MethodType mt = methodType(void.class); MethodType mt = methodType(void.class);
FunctionDescriptor fd = FunctionDescriptor.ofVoid(); FunctionDescriptor fd = FunctionDescriptor.ofVoid();
tests.add(abi.downcallHandle(ma, mt, fd), null); tests.add(abi.downcallHandle(ma, fd), null);
tests.add(abi.downcallHandle(ma, mt, fd.withAttribute(TRIVIAL_ATTRIBUTE_NAME, true)), null);
} }
addIdentity.add("identity_char", byte.class, C_CHAR, (byte) 10); addIdentity.add("identity_bool", boolean.class, C_BOOL, true);
addIdentity.add("identity_short", short.class, C_SHORT, (short) 10); addIdentity.add("identity_char", byte.class, C_CHAR, (byte) 10);
addIdentity.add("identity_int", int.class, C_INT, 10); addIdentity.add("identity_short", short.class, C_SHORT, (short) 10);
addIdentity.add("identity_long", long.class, C_LONG_LONG, 10L); addIdentity.add("identity_int", int.class, C_INT, 10);
addIdentity.add("identity_float", float.class, C_FLOAT, 10F); addIdentity.add("identity_long", long.class, C_LONG_LONG, 10L);
addIdentity.add("identity_double", double.class, C_DOUBLE, 10D); addIdentity.add("identity_float", float.class, C_FLOAT, 10F);
addIdentity.add("identity_double", double.class, C_DOUBLE, 10D);
{ // identity_va { // identity_va
MemoryAddress ma = LOOKUP.lookup("identity_va").get(); NativeSymbol ma = LOOKUP.lookup("identity_va").get();
MethodType mt = methodType(int.class, int.class, double.class, int.class, float.class, long.class); MethodType mt = methodType(int.class, int.class, double.class, int.class, float.class, long.class);
FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_INT, asVarArg(C_DOUBLE), FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_INT).asVariadic(C_DOUBLE, C_INT, C_FLOAT, C_LONG_LONG);
asVarArg(C_INT), asVarArg(C_FLOAT), asVarArg(C_LONG_LONG)); tests.add(abi.downcallHandle(ma, fd), 1, 1, 10D, 2, 3F, 4L);
tests.add(abi.downcallHandle(ma, mt, fd), 1, 1, 10D, 2, 3F, 4L);
tests.add(abi.downcallHandle(ma, mt, fd.withAttribute(TRIVIAL_ATTRIBUTE_NAME, true)), 1, 1, 10D, 2, 3F, 4L);
} }
{ // high_arity { // high_arity
MethodType baseMT = methodType(void.class, int.class, double.class, long.class, float.class, byte.class, MethodType baseMT = methodType(void.class, int.class, double.class, long.class, float.class, byte.class,
short.class, char.class); short.class, char.class);
FunctionDescriptor baseFD = FunctionDescriptor.ofVoid(C_INT, C_DOUBLE, C_LONG_LONG, C_FLOAT, C_CHAR, FunctionDescriptor baseFD = FunctionDescriptor.ofVoid(C_INT, C_DOUBLE, C_LONG_LONG, C_FLOAT, C_CHAR,
C_SHORT, C_SHORT); C_SHORT, JAVA_CHAR);
Object[] args = {1, 10D, 2L, 3F, (byte) 0, (short) 13, 'a'}; Object[] args = {1, 10D, 2L, 3F, (byte) 0, (short) 13, 'a'};
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
MemoryAddress ma = LOOKUP.lookup("invoke_high_arity" + i).get(); NativeSymbol ma = LOOKUP.lookup("invoke_high_arity" + i).get();
MethodType mt = baseMT.changeReturnType(baseMT.parameterType(i)); MethodType mt = baseMT.changeReturnType(baseMT.parameterType(i));
FunctionDescriptor fd = baseFD.withReturnLayout(baseFD.argumentLayouts().get(i)); FunctionDescriptor fd = baseFD.withReturnLayout(baseFD.argumentLayouts().get(i));
Object expected = args[i]; Object expected = args[i];
tests.add(abi.downcallHandle(ma, mt, fd), expected, args); tests.add(abi.downcallHandle(ma, fd), expected, args);
tests.add(abi.downcallHandle(ma, mt, fd.withAttribute(TRIVIAL_ATTRIBUTE_NAME, true)), expected, args);
} }
} }

View file

@ -1,82 +0,0 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @run testng TestLayoutAttributes
*/
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayouts;
import org.testng.annotations.Test;
import java.util.List;
import java.util.stream.Collectors;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
public class TestLayoutAttributes {
@Test
public void testAttribute() {
MemoryLayout ml = MemoryLayouts.BITS_32_LE
.withAttribute("MyAttribute", 10L);
assertEquals((long) ml.attribute("MyAttribute").orElseThrow(), 10L);
}
@Test
public void testAttributeOverwrite() {
MemoryLayout ml = MemoryLayouts.BITS_32_LE
.withAttribute("MyAttribute", 10L);
assertEquals((long) ml.attribute("MyAttribute").orElseThrow(), 10L);
ml = ml.withAttribute("MyAttribute", 11L);
assertEquals((long) ml.attribute("MyAttribute").orElseThrow(), 11L);
}
@Test
public void testAttributeNonExistent() {
MemoryLayout ml = MemoryLayouts.BITS_32_LE
.withAttribute("MyAttribute", 10L);
assertTrue(ml.attribute("Foo").isEmpty());
}
@Test
public void testNameAttribute() {
MemoryLayout ml = MemoryLayouts.BITS_32_LE
.withName("foo");
assertEquals(ml.name().orElseThrow(), "foo");
assertEquals(ml.attribute(MemoryLayout.LAYOUT_NAME).orElseThrow(), "foo");
}
@Test
public void testAttributesStream() {
MemoryLayout ml = MemoryLayouts.BITS_32_LE
.withName("foo")
.withAttribute("MyAttribute", 10L);
List<String> attribs = ml.attributes().collect(Collectors.toList());
assertEquals(attribs.size(), 2);
assertTrue(attribs.contains("MyAttribute"));
assertTrue(attribs.contains(MemoryLayout.LAYOUT_NAME));
}
}

View file

@ -27,11 +27,12 @@
*/ */
import jdk.incubator.foreign.FunctionDescriptor; import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.nio.ByteOrder;
import jdk.incubator.foreign.ValueLayout;
import org.testng.annotations.*; import org.testng.annotations.*;
import static org.testng.Assert.*; import static org.testng.Assert.*;
@ -66,49 +67,48 @@ public class TestLayoutConstants {
public Object[][] createLayouts() { public Object[][] createLayouts() {
return new Object[][] { return new Object[][] {
//padding //padding
{ MemoryLayouts.PAD_32 }, {MemoryLayout.paddingLayout(32)},
{ MemoryLayout.sequenceLayout(MemoryLayouts.PAD_32) }, { MemoryLayout.sequenceLayout(MemoryLayout.paddingLayout(32)) },
{ MemoryLayout.sequenceLayout(5, MemoryLayouts.PAD_32) }, { MemoryLayout.sequenceLayout(5, MemoryLayout.paddingLayout(32)) },
{ MemoryLayout.structLayout(MemoryLayouts.PAD_32, MemoryLayouts.PAD_32) }, { MemoryLayout.structLayout(MemoryLayout.paddingLayout(32), MemoryLayout.paddingLayout(32)) },
{ MemoryLayout.unionLayout(MemoryLayouts.PAD_32, MemoryLayouts.PAD_32) }, { MemoryLayout.unionLayout(MemoryLayout.paddingLayout(32), MemoryLayout.paddingLayout(32)) },
//values, big endian //values, big endian
{ MemoryLayouts.BITS_32_BE }, { ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN) },
{ MemoryLayout.structLayout( { MemoryLayout.structLayout(
MemoryLayouts.BITS_32_BE, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN),
MemoryLayouts.BITS_32_BE) }, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN)) },
{ MemoryLayout.unionLayout( { MemoryLayout.unionLayout(
MemoryLayouts.BITS_32_BE, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN),
MemoryLayouts.BITS_32_BE) }, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN)) },
//values, little endian //values, little endian
{ MemoryLayouts.BITS_32_LE }, { ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN) },
{ MemoryLayout.structLayout( { MemoryLayout.structLayout(
MemoryLayouts.BITS_32_LE, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN),
MemoryLayouts.BITS_32_LE) }, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN)) },
{ MemoryLayout.unionLayout( { MemoryLayout.unionLayout(
MemoryLayouts.BITS_32_LE, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN),
MemoryLayouts.BITS_32_LE) }, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN)) },
//deeply nested //deeply nested
{ MemoryLayout.structLayout( { MemoryLayout.structLayout(
MemoryLayouts.PAD_16, MemoryLayout.paddingLayout(16),
MemoryLayout.structLayout( MemoryLayout.structLayout(
MemoryLayouts.PAD_8, MemoryLayout.paddingLayout(8),
MemoryLayouts.BITS_32_BE)) }, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN))) },
{ MemoryLayout.unionLayout( { MemoryLayout.unionLayout(
MemoryLayouts.PAD_16, MemoryLayout.paddingLayout(16),
MemoryLayout.structLayout( MemoryLayout.structLayout(
MemoryLayouts.PAD_8, MemoryLayout.paddingLayout(8),
MemoryLayouts.BITS_32_BE)) }, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN))) },
{ MemoryLayout.sequenceLayout( { MemoryLayout.sequenceLayout(
MemoryLayout.structLayout( MemoryLayout.structLayout(
MemoryLayouts.PAD_8, MemoryLayout.paddingLayout(8),
MemoryLayouts.BITS_32_BE)) }, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN))) },
{ MemoryLayout.sequenceLayout(5, { MemoryLayout.sequenceLayout(5,
MemoryLayout.structLayout( MemoryLayout.structLayout(
MemoryLayouts.PAD_8, MemoryLayout.paddingLayout(8),
MemoryLayouts.BITS_32_BE)) }, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN))) },
{ MemoryLayouts.BITS_32_LE.withName("myInt") }, { ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN).withName("myInt") },
{ MemoryLayouts.BITS_32_LE.withBitAlignment(8) }, { ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN).withBitAlignment(8) },
{ MemoryLayouts.BITS_32_LE.withAttribute("xyz", "abc") },
}; };
} }

View file

@ -29,7 +29,7 @@
* @run testng TestLayoutEquality * @run testng TestLayoutEquality
*/ */
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.ValueLayout; import jdk.incubator.foreign.ValueLayout;
import jdk.internal.foreign.PlatformLayouts; import jdk.internal.foreign.PlatformLayouts;
import org.testng.annotations.DataProvider; import org.testng.annotations.DataProvider;
@ -39,23 +39,32 @@ import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static jdk.incubator.foreign.ValueLayout.ADDRESS;
import static jdk.incubator.foreign.ValueLayout.JAVA_BOOLEAN;
import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE;
import static jdk.incubator.foreign.ValueLayout.JAVA_CHAR;
import static jdk.incubator.foreign.ValueLayout.JAVA_DOUBLE;
import static jdk.incubator.foreign.ValueLayout.JAVA_FLOAT;
import static jdk.incubator.foreign.ValueLayout.JAVA_INT;
import static jdk.incubator.foreign.ValueLayout.JAVA_LONG;
import static jdk.incubator.foreign.ValueLayout.JAVA_SHORT;
import static org.testng.Assert.*; import static org.testng.Assert.*;
public class TestLayoutEquality { public class TestLayoutEquality {
@Test(dataProvider = "layoutConstants") @Test(dataProvider = "layoutConstants")
public void testReconstructedEquality(ValueLayout layout) { public void testReconstructedEquality(ValueLayout layout) {
ValueLayout newLayout = MemoryLayout.valueLayout(layout.bitSize(), layout.order()); ValueLayout newLayout = valueLayoutForCarrier(layout.carrier());
newLayout = newLayout.withBitAlignment(layout.bitAlignment());
newLayout = newLayout.withOrder(layout.order());
// properties should be equal // properties should be equal
assertEquals(newLayout.bitSize(), layout.bitSize()); assertEquals(newLayout.bitSize(), layout.bitSize());
assertEquals(newLayout.bitAlignment(), layout.bitAlignment()); assertEquals(newLayout.bitAlignment(), layout.bitAlignment());
assertEquals(newLayout.name(), layout.name()); assertEquals(newLayout.name(), layout.name());
assertEquals(newLayout.attributes().toArray().length, 0);
assertEquals(layout.attributes().toArray().length, 1);
// but equals should return false, because one is a ValueLayout with a CLinker kind // layouts should be equals
assertNotEquals(newLayout, layout); assertEquals(newLayout, layout);
} }
@DataProvider @DataProvider
@ -76,4 +85,27 @@ public class TestLayoutEquality {
} }
} }
static ValueLayout valueLayoutForCarrier(Class<?> carrier) {
if (carrier == boolean.class) {
return JAVA_BOOLEAN;
} else if (carrier == char.class) {
return JAVA_CHAR;
} else if (carrier == byte.class) {
return JAVA_BYTE;
} else if (carrier == short.class) {
return JAVA_SHORT;
} else if (carrier == int.class) {
return JAVA_INT;
} else if (carrier == long.class) {
return JAVA_LONG;
} else if (carrier == float.class) {
return JAVA_FLOAT;
} else if (carrier == double.class) {
return JAVA_DOUBLE;
} else if (carrier == MemoryAddress.class) {
return ADDRESS;
} else {
throw new UnsupportedOperationException();
}
}
} }

View file

@ -28,24 +28,23 @@
*/ */
import jdk.incubator.foreign.GroupLayout; import jdk.incubator.foreign.GroupLayout;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayout.PathElement; import jdk.incubator.foreign.MemoryLayout.PathElement;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.ResourceScope; import jdk.incubator.foreign.ResourceScope;
import jdk.incubator.foreign.SequenceLayout; import jdk.incubator.foreign.SequenceLayout;
import jdk.incubator.foreign.ValueLayout;
import org.testng.SkipException; import org.testng.SkipException;
import org.testng.annotations.*; import org.testng.annotations.*;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.nio.ByteOrder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static jdk.incubator.foreign.MemoryLayout.PathElement.groupElement; import static jdk.incubator.foreign.MemoryLayout.PathElement.groupElement;
import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement; import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement;
import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT; import static jdk.incubator.foreign.ValueLayout.JAVA_INT;
import static org.testng.Assert.*; import static org.testng.Assert.*;
public class TestLayoutPaths { public class TestLayoutPaths {
@ -153,7 +152,7 @@ public class TestLayoutPaths {
@Test(expectedExceptions = IllegalArgumentException.class) @Test(expectedExceptions = IllegalArgumentException.class)
public void testIncompleteAccess() { public void testIncompleteAccess() {
SequenceLayout seq = MemoryLayout.sequenceLayout(5, MemoryLayout.structLayout(JAVA_INT)); SequenceLayout seq = MemoryLayout.sequenceLayout(5, MemoryLayout.structLayout(JAVA_INT));
seq.varHandle(int.class, sequenceElement()); seq.varHandle(sequenceElement());
} }
@Test(expectedExceptions = IllegalArgumentException.class) @Test(expectedExceptions = IllegalArgumentException.class)
@ -221,7 +220,7 @@ public class TestLayoutPaths {
throw new AssertionError(ex); // should be ok! throw new AssertionError(ex); // should be ok!
} }
try { try {
g.varHandle(int.class, groupElement("foo")); //ok g.varHandle(groupElement("foo")); //ok
assertTrue(false); //should fail! assertTrue(false); //should fail!
} catch (UnsupportedOperationException ex) { } catch (UnsupportedOperationException ex) {
//ok //ok
@ -232,7 +231,7 @@ public class TestLayoutPaths {
@Test @Test
public void testBadAlignOffset() { public void testBadAlignOffset() {
GroupLayout g = MemoryLayout.structLayout(MemoryLayouts.PAD_8, JAVA_INT.withBitAlignment(16).withName("foo")); GroupLayout g = MemoryLayout.structLayout(MemoryLayout.paddingLayout(8), JAVA_INT.withBitAlignment(16).withName("foo"));
try { try {
g.bitOffset(groupElement("foo")); g.bitOffset(groupElement("foo"));
g.byteOffset(groupElement("foo")); g.byteOffset(groupElement("foo"));
@ -240,7 +239,7 @@ public class TestLayoutPaths {
throw new AssertionError(ex); // should be ok! throw new AssertionError(ex); // should be ok!
} }
try { try {
g.varHandle(int.class, groupElement("foo")); //ok g.varHandle(groupElement("foo")); //ok
assertTrue(false); //should fail! assertTrue(false); //should fail!
} catch (UnsupportedOperationException ex) { } catch (UnsupportedOperationException ex) {
//ok //ok
@ -299,10 +298,10 @@ public class TestLayoutPaths {
public void testStructPaths() { public void testStructPaths() {
long[] offsets = { 0, 8, 24, 56 }; long[] offsets = { 0, 8, 24, 56 };
GroupLayout g = MemoryLayout.structLayout( GroupLayout g = MemoryLayout.structLayout(
MemoryLayouts.JAVA_BYTE.withName("1"), ValueLayout.JAVA_BYTE.withName("1"),
MemoryLayouts.JAVA_CHAR.withName("2"), ValueLayout.JAVA_CHAR.withName("2"),
MemoryLayouts.JAVA_FLOAT.withName("3"), ValueLayout.JAVA_FLOAT.withName("3"),
MemoryLayouts.JAVA_LONG.withName("4") ValueLayout.JAVA_LONG.withName("4")
); );
// test select // test select
@ -324,11 +323,11 @@ public class TestLayoutPaths {
// test map // test map
for (int i = 1 ; i <= 4 ; i++) { for (int i = 1 ; i <= 4 ; i++) {
GroupLayout g2 = (GroupLayout)g.map(l -> MemoryLayouts.JAVA_DOUBLE, groupElement(String.valueOf(i))); GroupLayout g2 = (GroupLayout)g.map(l -> ValueLayout.JAVA_DOUBLE, groupElement(String.valueOf(i)));
assertTrue(g2.isStruct()); assertTrue(g2.isStruct());
for (int j = 0 ; j < 4 ; j++) { for (int j = 0 ; j < 4 ; j++) {
if (j == i - 1) { if (j == i - 1) {
assertEquals(g2.memberLayouts().get(j), MemoryLayouts.JAVA_DOUBLE); assertEquals(g2.memberLayouts().get(j), ValueLayout.JAVA_DOUBLE);
} else { } else {
assertEquals(g2.memberLayouts().get(j), g.memberLayouts().get(j)); assertEquals(g2.memberLayouts().get(j), g.memberLayouts().get(j));
} }
@ -340,10 +339,10 @@ public class TestLayoutPaths {
public void testUnionPaths() { public void testUnionPaths() {
long[] offsets = { 0, 0, 0, 0 }; long[] offsets = { 0, 0, 0, 0 };
GroupLayout g = MemoryLayout.unionLayout( GroupLayout g = MemoryLayout.unionLayout(
MemoryLayouts.JAVA_BYTE.withName("1"), ValueLayout.JAVA_BYTE.withName("1"),
MemoryLayouts.JAVA_CHAR.withName("2"), ValueLayout.JAVA_CHAR.withName("2"),
MemoryLayouts.JAVA_FLOAT.withName("3"), ValueLayout.JAVA_FLOAT.withName("3"),
MemoryLayouts.JAVA_LONG.withName("4") ValueLayout.JAVA_LONG.withName("4")
); );
// test select // test select
@ -365,11 +364,11 @@ public class TestLayoutPaths {
// test map // test map
for (int i = 1 ; i <= 4 ; i++) { for (int i = 1 ; i <= 4 ; i++) {
GroupLayout g2 = (GroupLayout)g.map(l -> MemoryLayouts.JAVA_DOUBLE, groupElement(String.valueOf(i))); GroupLayout g2 = (GroupLayout)g.map(l -> ValueLayout.JAVA_DOUBLE, groupElement(String.valueOf(i)));
assertTrue(g2.isUnion()); assertTrue(g2.isUnion());
for (int j = 0 ; j < 4 ; j++) { for (int j = 0 ; j < 4 ; j++) {
if (j == i - 1) { if (j == i - 1) {
assertEquals(g2.memberLayouts().get(j), MemoryLayouts.JAVA_DOUBLE); assertEquals(g2.memberLayouts().get(j), ValueLayout.JAVA_DOUBLE);
} else { } else {
assertEquals(g2.memberLayouts().get(j), g.memberLayouts().get(j)); assertEquals(g2.memberLayouts().get(j), g.memberLayouts().get(j));
} }
@ -380,12 +379,12 @@ public class TestLayoutPaths {
@Test @Test
public void testSequencePaths() { public void testSequencePaths() {
long[] offsets = { 0, 8, 16, 24 }; long[] offsets = { 0, 8, 16, 24 };
SequenceLayout g = MemoryLayout.sequenceLayout(4, MemoryLayouts.JAVA_BYTE); SequenceLayout g = MemoryLayout.sequenceLayout(4, ValueLayout.JAVA_BYTE);
// test select // test select
MemoryLayout selected = g.select(sequenceElement()); MemoryLayout selected = g.select(sequenceElement());
assertTrue(selected == MemoryLayouts.JAVA_BYTE); assertTrue(selected == ValueLayout.JAVA_BYTE);
// test offset // test offset
@ -398,8 +397,8 @@ public class TestLayoutPaths {
// test map // test map
SequenceLayout seq2 = (SequenceLayout)g.map(l -> MemoryLayouts.JAVA_DOUBLE, sequenceElement()); SequenceLayout seq2 = (SequenceLayout)g.map(l -> ValueLayout.JAVA_DOUBLE, sequenceElement());
assertTrue(seq2.elementLayout() == MemoryLayouts.JAVA_DOUBLE); assertTrue(seq2.elementLayout() == ValueLayout.JAVA_DOUBLE);
} }
@Test(dataProvider = "testLayouts") @Test(dataProvider = "testLayouts")
@ -506,26 +505,16 @@ public class TestLayoutPaths {
try (ResourceScope scope = ResourceScope.newConfinedScope()) { try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment segment = MemorySegment.allocateNative(layout, scope); MemorySegment segment = MemorySegment.allocateNative(layout, scope);
MemorySegment slice = (MemorySegment) sliceHandle.invokeExact(segment, indexes); MemorySegment slice = (MemorySegment) sliceHandle.invokeExact(segment, indexes);
assertEquals(slice.address().segmentOffset(segment), expectedBitOffset / 8); assertEquals(slice.address().toRawLongValue() - segment.address().toRawLongValue(), expectedBitOffset / 8);
assertEquals(slice.byteSize(), selected.byteSize()); assertEquals(slice.byteSize(), selected.byteSize());
} }
} }
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testSliceHandleUOEInvalidSize() {
MemoryLayout layout = MemoryLayout.structLayout(
MemoryLayout.valueLayout(32, ByteOrder.nativeOrder()).withName("x"),
MemoryLayout.valueLayout(31, ByteOrder.nativeOrder()).withName("y") // size not a multiple of 8
);
layout.sliceHandle(groupElement("y")); // should throw
}
@Test(expectedExceptions = UnsupportedOperationException.class) @Test(expectedExceptions = UnsupportedOperationException.class)
public void testSliceHandleUOEInvalidOffsetEager() throws Throwable { public void testSliceHandleUOEInvalidOffsetEager() throws Throwable {
MemoryLayout layout = MemoryLayout.structLayout( MemoryLayout layout = MemoryLayout.structLayout(
MemoryLayout.paddingLayout(5), MemoryLayout.paddingLayout(5),
MemoryLayout.valueLayout(32, ByteOrder.nativeOrder()).withName("y") // offset not a multiple of 8 JAVA_INT.withName("y") // offset not a multiple of 8
); );
layout.sliceHandle(groupElement("y")); // should throw layout.sliceHandle(groupElement("y")); // should throw
@ -536,7 +525,7 @@ public class TestLayoutPaths {
MemoryLayout layout = MemoryLayout.sequenceLayout(3, MemoryLayout layout = MemoryLayout.sequenceLayout(3,
MemoryLayout.structLayout( MemoryLayout.structLayout(
MemoryLayout.paddingLayout(4), MemoryLayout.paddingLayout(4),
MemoryLayout.valueLayout(32, ByteOrder.nativeOrder()).withName("y") // offset not a multiple of 8 JAVA_INT.withName("y") // offset not a multiple of 8
) )
); );

View file

@ -34,15 +34,15 @@ import java.util.function.LongFunction;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.testng.annotations.*; import org.testng.annotations.*;
import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE;
import static jdk.incubator.foreign.ValueLayout.JAVA_INT;
import static jdk.incubator.foreign.ValueLayout.JAVA_LONG;
import static jdk.incubator.foreign.ValueLayout.JAVA_SHORT;
import static org.testng.Assert.*; import static org.testng.Assert.*;
public class TestLayouts { public class TestLayouts {
@Test(dataProvider = "badLayoutSizes", expectedExceptions = IllegalArgumentException.class)
public void testBadLayoutSize(SizedLayoutFactory factory, long size) {
factory.make(size);
}
@Test(dataProvider = "badAlignments", expectedExceptions = IllegalArgumentException.class) @Test(dataProvider = "badAlignments", expectedExceptions = IllegalArgumentException.class)
public void testBadLayoutAlignment(MemoryLayout layout, long alignment) { public void testBadLayoutAlignment(MemoryLayout layout, long alignment) {
layout.withBitAlignment(alignment); layout.withBitAlignment(alignment);
@ -51,12 +51,12 @@ public class TestLayouts {
@Test @Test
public void testVLAInStruct() { public void testVLAInStruct() {
MemoryLayout layout = MemoryLayout.structLayout( MemoryLayout layout = MemoryLayout.structLayout(
MemoryLayouts.JAVA_INT.withName("size"), ValueLayout.JAVA_INT.withName("size"),
MemoryLayout.paddingLayout(32), MemoryLayout.paddingLayout(32),
MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_DOUBLE).withName("arr")); MemoryLayout.sequenceLayout(ValueLayout.JAVA_DOUBLE).withName("arr"));
assertFalse(layout.hasSize()); assertFalse(layout.hasSize());
VarHandle size_handle = layout.varHandle(int.class, MemoryLayout.PathElement.groupElement("size")); VarHandle size_handle = layout.varHandle(MemoryLayout.PathElement.groupElement("size"));
VarHandle array_elem_handle = layout.varHandle(double.class, VarHandle array_elem_handle = layout.varHandle(
MemoryLayout.PathElement.groupElement("arr"), MemoryLayout.PathElement.groupElement("arr"),
MemoryLayout.PathElement.sequenceElement()); MemoryLayout.PathElement.sequenceElement());
try (ResourceScope scope = ResourceScope.newConfinedScope()) { try (ResourceScope scope = ResourceScope.newConfinedScope()) {
@ -77,12 +77,12 @@ public class TestLayouts {
@Test @Test
public void testVLAInSequence() { public void testVLAInSequence() {
MemoryLayout layout = MemoryLayout.structLayout( MemoryLayout layout = MemoryLayout.structLayout(
MemoryLayouts.JAVA_INT.withName("size"), ValueLayout.JAVA_INT.withName("size"),
MemoryLayout.paddingLayout(32), MemoryLayout.paddingLayout(32),
MemoryLayout.sequenceLayout(1, MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_DOUBLE)).withName("arr")); MemoryLayout.sequenceLayout(1, MemoryLayout.sequenceLayout(ValueLayout.JAVA_DOUBLE)).withName("arr"));
assertFalse(layout.hasSize()); assertFalse(layout.hasSize());
VarHandle size_handle = layout.varHandle(int.class, MemoryLayout.PathElement.groupElement("size")); VarHandle size_handle = layout.varHandle(MemoryLayout.PathElement.groupElement("size"));
VarHandle array_elem_handle = layout.varHandle(double.class, VarHandle array_elem_handle = layout.varHandle(
MemoryLayout.PathElement.groupElement("arr"), MemoryLayout.PathElement.groupElement("arr"),
MemoryLayout.PathElement.sequenceElement(0), MemoryLayout.PathElement.sequenceElement(0),
MemoryLayout.PathElement.sequenceElement()); MemoryLayout.PathElement.sequenceElement());
@ -103,17 +103,17 @@ public class TestLayouts {
@Test @Test
public void testIndexedSequencePath() { public void testIndexedSequencePath() {
MemoryLayout seq = MemoryLayout.sequenceLayout(10, MemoryLayouts.JAVA_INT); MemoryLayout seq = MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_INT);
try (ResourceScope scope = ResourceScope.newConfinedScope()) { try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment segment = MemorySegment.allocateNative(seq, scope); MemorySegment segment = MemorySegment.allocateNative(seq, scope);
VarHandle indexHandle = seq.varHandle(int.class, MemoryLayout.PathElement.sequenceElement()); VarHandle indexHandle = seq.varHandle(MemoryLayout.PathElement.sequenceElement());
// init segment // init segment
for (int i = 0 ; i < 10 ; i++) { for (int i = 0 ; i < 10 ; i++) {
indexHandle.set(segment, (long)i, i); indexHandle.set(segment, (long)i, i);
} }
//check statically indexed handles //check statically indexed handles
for (int i = 0 ; i < 10 ; i++) { for (int i = 0 ; i < 10 ; i++) {
VarHandle preindexHandle = seq.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(i)); VarHandle preindexHandle = seq.varHandle(MemoryLayout.PathElement.sequenceElement(i));
int expected = (int)indexHandle.get(segment, (long)i); int expected = (int)indexHandle.get(segment, (long)i);
int found = (int)preindexHandle.get(segment); int found = (int)preindexHandle.get(segment);
assertEquals(expected, found); assertEquals(expected, found);
@ -143,13 +143,13 @@ public class TestLayouts {
@Test(expectedExceptions = IllegalArgumentException.class) @Test(expectedExceptions = IllegalArgumentException.class)
public void testBadUnboundSequenceLayoutResize() { public void testBadUnboundSequenceLayoutResize() {
SequenceLayout seq = MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT); SequenceLayout seq = MemoryLayout.sequenceLayout(ValueLayout.JAVA_INT);
seq.withElementCount(-1); seq.withElementCount(-1);
} }
@Test(expectedExceptions = IllegalArgumentException.class) @Test(expectedExceptions = IllegalArgumentException.class)
public void testBadBoundSequenceLayoutResize() { public void testBadBoundSequenceLayoutResize() {
SequenceLayout seq = MemoryLayout.sequenceLayout(10, MemoryLayouts.JAVA_INT); SequenceLayout seq = MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_INT);
seq.withElementCount(-1); seq.withElementCount(-1);
} }
@ -168,13 +168,13 @@ public class TestLayouts {
public void testStructSizeAndAlign() { public void testStructSizeAndAlign() {
MemoryLayout struct = MemoryLayout.structLayout( MemoryLayout struct = MemoryLayout.structLayout(
MemoryLayout.paddingLayout(8), MemoryLayout.paddingLayout(8),
MemoryLayouts.JAVA_BYTE, ValueLayout.JAVA_BYTE,
MemoryLayouts.JAVA_CHAR, ValueLayout.JAVA_CHAR,
MemoryLayouts.JAVA_INT, ValueLayout.JAVA_INT,
MemoryLayouts.JAVA_LONG ValueLayout.JAVA_LONG
); );
assertEquals(struct.byteSize(), 1 + 1 + 2 + 4 + 8); assertEquals(struct.byteSize(), 1 + 1 + 2 + 4 + 8);
assertEquals(struct.byteAlignment(), MemoryLayouts.ADDRESS.byteAlignment()); assertEquals(struct.byteAlignment(), ValueLayout.ADDRESS.byteAlignment());
} }
@Test(dataProvider="basicLayouts") @Test(dataProvider="basicLayouts")
@ -199,13 +199,13 @@ public class TestLayouts {
@Test @Test
public void testUnionSizeAndAlign() { public void testUnionSizeAndAlign() {
MemoryLayout struct = MemoryLayout.unionLayout( MemoryLayout struct = MemoryLayout.unionLayout(
MemoryLayouts.JAVA_BYTE, ValueLayout.JAVA_BYTE,
MemoryLayouts.JAVA_CHAR, ValueLayout.JAVA_CHAR,
MemoryLayouts.JAVA_INT, ValueLayout.JAVA_INT,
MemoryLayouts.JAVA_LONG ValueLayout.JAVA_LONG
); );
assertEquals(struct.byteSize(), 8); assertEquals(struct.byteSize(), 8);
assertEquals(struct.byteAlignment(), MemoryLayouts.ADDRESS.byteAlignment()); assertEquals(struct.byteAlignment(), ValueLayout.ADDRESS.byteAlignment());
} }
@Test(dataProvider = "layoutKinds") @Test(dataProvider = "layoutKinds")
@ -224,31 +224,19 @@ public class TestLayouts {
} }
} }
@DataProvider(name = "badLayoutSizes")
public Object[][] factoriesAndSizes() {
return new Object[][] {
{ SizedLayoutFactory.VALUE_BE, 0 },
{ SizedLayoutFactory.VALUE_BE, -1 },
{ SizedLayoutFactory.VALUE_LE, 0 },
{ SizedLayoutFactory.VALUE_LE, -1 },
{ SizedLayoutFactory.PADDING, 0 },
{ SizedLayoutFactory.PADDING, -1 },
{ SizedLayoutFactory.SEQUENCE, -1 }
};
}
@DataProvider(name = "unboundLayouts") @DataProvider(name = "unboundLayouts")
public Object[][] unboundLayouts() { public Object[][] unboundLayouts() {
ValueLayout alignedInt = JAVA_INT.withBitAlignment(32);
return new Object[][] { return new Object[][] {
{ MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT), 32 }, { MemoryLayout.sequenceLayout(alignedInt), 32 },
{ MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT)), 32 }, { MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(alignedInt)), 32 },
{ MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT)), 32 }, { MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(alignedInt)), 32 },
{ MemoryLayout.structLayout(MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT)), 32 }, { MemoryLayout.structLayout(MemoryLayout.sequenceLayout(alignedInt)), 32 },
{ MemoryLayout.structLayout(MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT))), 32 }, { MemoryLayout.structLayout(MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(alignedInt))), 32 },
{ MemoryLayout.structLayout(MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT))), 32 }, { MemoryLayout.structLayout(MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(alignedInt))), 32 },
{ MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT)), 32 }, { MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(alignedInt)), 32 },
{ MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT))), 32 }, { MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(alignedInt))), 32 },
{ MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT))), 32 }, { MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(alignedInt))), 32 },
}; };
} }
@ -271,10 +259,10 @@ public class TestLayouts {
} }
enum SizedLayoutFactory { enum SizedLayoutFactory {
VALUE_LE(size -> MemoryLayout.valueLayout(size, ByteOrder.LITTLE_ENDIAN)), VALUE_LE(size -> valueLayoutForSize((int)size).withOrder(ByteOrder.LITTLE_ENDIAN)),
VALUE_BE(size -> MemoryLayout.valueLayout(size, ByteOrder.BIG_ENDIAN)), VALUE_BE(size -> valueLayoutForSize((int)size).withOrder(ByteOrder.BIG_ENDIAN)),
PADDING(MemoryLayout::paddingLayout), PADDING(MemoryLayout::paddingLayout),
SEQUENCE(size -> MemoryLayout.sequenceLayout(size, MemoryLayouts.PAD_8)); SEQUENCE(size -> MemoryLayout.sequenceLayout(size, MemoryLayout.paddingLayout(8)));
private final LongFunction<MemoryLayout> factory; private final LongFunction<MemoryLayout> factory;
@ -287,13 +275,22 @@ public class TestLayouts {
} }
} }
static ValueLayout valueLayoutForSize(int size) {
return switch (size) {
case 1 -> JAVA_BYTE;
case 2 -> JAVA_SHORT;
case 4 -> JAVA_INT;
case 8 -> JAVA_LONG;
default -> throw new UnsupportedOperationException();
};
}
enum LayoutKind { enum LayoutKind {
VALUE_LE(MemoryLayouts.BITS_8_LE), VALUE(ValueLayout.JAVA_BYTE),
VALUE_BE(MemoryLayouts.BITS_8_BE), PADDING(MemoryLayout.paddingLayout(8)),
PADDING(MemoryLayouts.PAD_8), SEQUENCE(MemoryLayout.sequenceLayout(1, MemoryLayout.paddingLayout(8))),
SEQUENCE(MemoryLayout.sequenceLayout(1, MemoryLayouts.PAD_8)), STRUCT(MemoryLayout.structLayout(MemoryLayout.paddingLayout(8), MemoryLayout.paddingLayout(8))),
STRUCT(MemoryLayout.structLayout(MemoryLayouts.PAD_8, MemoryLayouts.PAD_8)), UNION(MemoryLayout.unionLayout(MemoryLayout.paddingLayout(8), MemoryLayout.paddingLayout(8)));
UNION(MemoryLayout.unionLayout(MemoryLayouts.PAD_8, MemoryLayouts.PAD_8));
final MemoryLayout layout; final MemoryLayout layout;
@ -333,12 +330,12 @@ public class TestLayouts {
} }
static MemoryLayout[] basicLayouts = { static MemoryLayout[] basicLayouts = {
MemoryLayouts.JAVA_BYTE, ValueLayout.JAVA_BYTE,
MemoryLayouts.JAVA_CHAR, ValueLayout.JAVA_CHAR,
MemoryLayouts.JAVA_SHORT, ValueLayout.JAVA_SHORT,
MemoryLayouts.JAVA_INT, ValueLayout.JAVA_INT,
MemoryLayouts.JAVA_FLOAT, ValueLayout.JAVA_FLOAT,
MemoryLayouts.JAVA_LONG, ValueLayout.JAVA_LONG,
MemoryLayouts.JAVA_DOUBLE, ValueLayout.JAVA_DOUBLE,
}; };
} }

View file

@ -30,7 +30,7 @@
*/ */
import jdk.incubator.foreign.GroupLayout; import jdk.incubator.foreign.GroupLayout;
import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayout.PathElement; import jdk.incubator.foreign.MemoryLayout.PathElement;
import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.MemorySegment;
@ -39,6 +39,7 @@ import jdk.incubator.foreign.SequenceLayout;
import jdk.incubator.foreign.ValueLayout; import jdk.incubator.foreign.ValueLayout;
import java.lang.invoke.VarHandle; import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
import java.util.function.Function; import java.util.function.Function;
import org.testng.annotations.*; import org.testng.annotations.*;
@ -47,39 +48,39 @@ import static org.testng.Assert.*;
public class TestMemoryAccess { public class TestMemoryAccess {
@Test(dataProvider = "elements") @Test(dataProvider = "elements")
public void testAccess(Function<MemorySegment, MemorySegment> viewFactory, ValueLayout elemLayout, Class<?> carrier, Checker checker) { public void testAccess(Function<MemorySegment, MemorySegment> viewFactory, ValueLayout elemLayout, Checker checker) {
ValueLayout layout = elemLayout.withName("elem"); ValueLayout layout = elemLayout.withName("elem");
testAccessInternal(viewFactory, layout, layout.varHandle(carrier), checker); testAccessInternal(viewFactory, layout, layout.varHandle(), checker);
} }
@Test(dataProvider = "elements") @Test(dataProvider = "elements")
public void testPaddedAccessByName(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, Checker checker) { public void testPaddedAccessByName(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Checker checker) {
GroupLayout layout = MemoryLayout.structLayout(MemoryLayout.paddingLayout(elemLayout.bitSize()), elemLayout.withName("elem")); GroupLayout layout = MemoryLayout.structLayout(MemoryLayout.paddingLayout(elemLayout.bitSize()), elemLayout.withName("elem"));
testAccessInternal(viewFactory, layout, layout.varHandle(carrier, PathElement.groupElement("elem")), checker); testAccessInternal(viewFactory, layout, layout.varHandle(PathElement.groupElement("elem")), checker);
} }
@Test(dataProvider = "elements") @Test(dataProvider = "elements")
public void testPaddedAccessByIndexSeq(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, Checker checker) { public void testPaddedAccessByIndexSeq(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Checker checker) {
SequenceLayout layout = MemoryLayout.sequenceLayout(2, elemLayout); SequenceLayout layout = MemoryLayout.sequenceLayout(2, elemLayout);
testAccessInternal(viewFactory, layout, layout.varHandle(carrier, PathElement.sequenceElement(1)), checker); testAccessInternal(viewFactory, layout, layout.varHandle(PathElement.sequenceElement(1)), checker);
} }
@Test(dataProvider = "arrayElements") @Test(dataProvider = "arrayElements")
public void testArrayAccess(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, ArrayChecker checker) { public void testArrayAccess(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, ArrayChecker checker) {
SequenceLayout seq = MemoryLayout.sequenceLayout(10, elemLayout.withName("elem")); SequenceLayout seq = MemoryLayout.sequenceLayout(10, elemLayout.withName("elem"));
testArrayAccessInternal(viewFactory, seq, seq.varHandle(carrier, PathElement.sequenceElement()), checker); testArrayAccessInternal(viewFactory, seq, seq.varHandle(PathElement.sequenceElement()), checker);
} }
@Test(dataProvider = "arrayElements") @Test(dataProvider = "arrayElements")
public void testPaddedArrayAccessByName(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, ArrayChecker checker) { public void testPaddedArrayAccessByName(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, ArrayChecker checker) {
SequenceLayout seq = MemoryLayout.sequenceLayout(10, MemoryLayout.structLayout(MemoryLayout.paddingLayout(elemLayout.bitSize()), elemLayout.withName("elem"))); SequenceLayout seq = MemoryLayout.sequenceLayout(10, MemoryLayout.structLayout(MemoryLayout.paddingLayout(elemLayout.bitSize()), elemLayout.withName("elem")));
testArrayAccessInternal(viewFactory, seq, seq.varHandle(carrier, MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("elem")), checker); testArrayAccessInternal(viewFactory, seq, seq.varHandle(MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("elem")), checker);
} }
@Test(dataProvider = "arrayElements") @Test(dataProvider = "arrayElements")
public void testPaddedArrayAccessByIndexSeq(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, ArrayChecker checker) { public void testPaddedArrayAccessByIndexSeq(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, ArrayChecker checker) {
SequenceLayout seq = MemoryLayout.sequenceLayout(10, MemoryLayout.sequenceLayout(2, elemLayout)); SequenceLayout seq = MemoryLayout.sequenceLayout(10, MemoryLayout.sequenceLayout(2, elemLayout));
testArrayAccessInternal(viewFactory, seq, seq.varHandle(carrier, PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement(1)), checker); testArrayAccessInternal(viewFactory, seq, seq.varHandle(PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement(1)), checker);
} }
private void testAccessInternal(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout layout, VarHandle handle, Checker checker) { private void testAccessInternal(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout layout, VarHandle handle, Checker checker) {
@ -149,40 +150,33 @@ public class TestMemoryAccess {
} }
@Test(dataProvider = "matrixElements") @Test(dataProvider = "matrixElements")
public void testMatrixAccess(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, MatrixChecker checker) { public void testMatrixAccess(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, MatrixChecker checker) {
SequenceLayout seq = MemoryLayout.sequenceLayout(20, SequenceLayout seq = MemoryLayout.sequenceLayout(20,
MemoryLayout.sequenceLayout(10, elemLayout.withName("elem"))); MemoryLayout.sequenceLayout(10, elemLayout.withName("elem")));
testMatrixAccessInternal(viewFactory, seq, seq.varHandle(carrier, testMatrixAccessInternal(viewFactory, seq, seq.varHandle(
PathElement.sequenceElement(), PathElement.sequenceElement()), checker); PathElement.sequenceElement(), PathElement.sequenceElement()), checker);
} }
@Test(dataProvider = "matrixElements") @Test(dataProvider = "matrixElements")
public void testPaddedMatrixAccessByName(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, MatrixChecker checker) { public void testPaddedMatrixAccessByName(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, MatrixChecker checker) {
SequenceLayout seq = MemoryLayout.sequenceLayout(20, SequenceLayout seq = MemoryLayout.sequenceLayout(20,
MemoryLayout.sequenceLayout(10, MemoryLayout.structLayout(MemoryLayout.paddingLayout(elemLayout.bitSize()), elemLayout.withName("elem")))); MemoryLayout.sequenceLayout(10, MemoryLayout.structLayout(MemoryLayout.paddingLayout(elemLayout.bitSize()), elemLayout.withName("elem"))));
testMatrixAccessInternal(viewFactory, seq, testMatrixAccessInternal(viewFactory, seq,
seq.varHandle(carrier, seq.varHandle(
PathElement.sequenceElement(), PathElement.sequenceElement(), PathElement.groupElement("elem")), PathElement.sequenceElement(), PathElement.sequenceElement(), PathElement.groupElement("elem")),
checker); checker);
} }
@Test(dataProvider = "matrixElements") @Test(dataProvider = "matrixElements")
public void testPaddedMatrixAccessByIndexSeq(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, MatrixChecker checker) { public void testPaddedMatrixAccessByIndexSeq(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, MatrixChecker checker) {
SequenceLayout seq = MemoryLayout.sequenceLayout(20, SequenceLayout seq = MemoryLayout.sequenceLayout(20,
MemoryLayout.sequenceLayout(10, MemoryLayout.sequenceLayout(2, elemLayout))); MemoryLayout.sequenceLayout(10, MemoryLayout.sequenceLayout(2, elemLayout)));
testMatrixAccessInternal(viewFactory, seq, testMatrixAccessInternal(viewFactory, seq,
seq.varHandle(carrier, seq.varHandle(
PathElement.sequenceElement(), PathElement.sequenceElement(), PathElement.sequenceElement(1)), PathElement.sequenceElement(), PathElement.sequenceElement(), PathElement.sequenceElement(1)),
checker); checker);
} }
@Test(dataProvider = "badCarriers",
expectedExceptions = IllegalArgumentException.class)
public void testBadCarriers(Class<?> carrier) {
ValueLayout l = MemoryLayouts.BITS_32_LE.withName("elem");
l.varHandle(carrier);
}
private void testMatrixAccessInternal(Function<MemorySegment, MemorySegment> viewFactory, SequenceLayout seq, VarHandle handle, MatrixChecker checker) { private void testMatrixAccessInternal(Function<MemorySegment, MemorySegment> viewFactory, SequenceLayout seq, VarHandle handle, MatrixChecker checker) {
MemorySegment outer_segment; MemorySegment outer_segment;
try (ResourceScope scope = ResourceScope.newConfinedScope()) { try (ResourceScope scope = ResourceScope.newConfinedScope()) {
@ -227,37 +221,37 @@ public class TestMemoryAccess {
public Object[][] createData() { public Object[][] createData() {
return new Object[][] { return new Object[][] {
//BE, RW //BE, RW
{ ID, MemoryLayouts.BITS_8_BE, byte.class, Checker.BYTE }, { ID, ValueLayout.JAVA_BYTE, Checker.BYTE },
{ ID, MemoryLayouts.BITS_16_BE, short.class, Checker.SHORT }, { ID, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), Checker.SHORT },
{ ID, MemoryLayouts.BITS_16_BE, char.class, Checker.CHAR }, { ID, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), Checker.CHAR },
{ ID, MemoryLayouts.BITS_32_BE, int.class, Checker.INT }, { ID, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), Checker.INT },
{ ID, MemoryLayouts.BITS_64_BE, long.class, Checker.LONG }, { ID, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), Checker.LONG },
{ ID, MemoryLayouts.BITS_32_BE, float.class, Checker.FLOAT }, { ID, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), Checker.FLOAT },
{ ID, MemoryLayouts.BITS_64_BE, double.class, Checker.DOUBLE }, { ID, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), Checker.DOUBLE },
//BE, RO //BE, RO
{ IMMUTABLE, MemoryLayouts.BITS_8_BE, byte.class, Checker.BYTE }, { IMMUTABLE, ValueLayout.JAVA_BYTE, Checker.BYTE },
{ IMMUTABLE, MemoryLayouts.BITS_16_BE, short.class, Checker.SHORT }, { IMMUTABLE, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), Checker.SHORT },
{ IMMUTABLE, MemoryLayouts.BITS_16_BE, char.class, Checker.CHAR }, { IMMUTABLE, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), Checker.CHAR },
{ IMMUTABLE, MemoryLayouts.BITS_32_BE, int.class, Checker.INT }, { IMMUTABLE, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), Checker.INT },
{ IMMUTABLE, MemoryLayouts.BITS_64_BE, long.class, Checker.LONG }, { IMMUTABLE, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), Checker.LONG },
{ IMMUTABLE, MemoryLayouts.BITS_32_BE, float.class, Checker.FLOAT }, { IMMUTABLE, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), Checker.FLOAT },
{ IMMUTABLE, MemoryLayouts.BITS_64_BE, double.class, Checker.DOUBLE }, { IMMUTABLE, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), Checker.DOUBLE },
//LE, RW //LE, RW
{ ID, MemoryLayouts.BITS_8_LE, byte.class, Checker.BYTE }, { ID, ValueLayout.JAVA_BYTE, Checker.BYTE },
{ ID, MemoryLayouts.BITS_16_LE, short.class, Checker.SHORT }, { ID, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.SHORT },
{ ID, MemoryLayouts.BITS_16_LE, char.class, Checker.CHAR }, { ID, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.CHAR },
{ ID, MemoryLayouts.BITS_32_LE, int.class, Checker.INT }, { ID, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.INT },
{ ID, MemoryLayouts.BITS_64_LE, long.class, Checker.LONG }, { ID, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.LONG },
{ ID, MemoryLayouts.BITS_32_LE, float.class, Checker.FLOAT }, { ID, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.FLOAT },
{ ID, MemoryLayouts.BITS_64_LE, double.class, Checker.DOUBLE }, { ID, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.DOUBLE },
//LE, RO //LE, RO
{ IMMUTABLE, MemoryLayouts.BITS_8_LE, byte.class, Checker.BYTE }, { IMMUTABLE, ValueLayout.JAVA_BYTE, Checker.BYTE },
{ IMMUTABLE, MemoryLayouts.BITS_16_LE, short.class, Checker.SHORT }, { IMMUTABLE, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.SHORT },
{ IMMUTABLE, MemoryLayouts.BITS_16_LE, char.class, Checker.CHAR }, { IMMUTABLE, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.CHAR },
{ IMMUTABLE, MemoryLayouts.BITS_32_LE, int.class, Checker.INT }, { IMMUTABLE, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.INT },
{ IMMUTABLE, MemoryLayouts.BITS_64_LE, long.class, Checker.LONG }, { IMMUTABLE, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.LONG },
{ IMMUTABLE, MemoryLayouts.BITS_32_LE, float.class, Checker.FLOAT }, { IMMUTABLE, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.FLOAT },
{ IMMUTABLE, MemoryLayouts.BITS_64_LE, double.class, Checker.DOUBLE }, { IMMUTABLE, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.DOUBLE },
}; };
} }
@ -304,37 +298,37 @@ public class TestMemoryAccess {
public Object[][] createArrayData() { public Object[][] createArrayData() {
return new Object[][] { return new Object[][] {
//BE, RW //BE, RW
{ ID, MemoryLayouts.BITS_8_BE, byte.class, ArrayChecker.BYTE }, { ID, ValueLayout.JAVA_BYTE, ArrayChecker.BYTE },
{ ID, MemoryLayouts.BITS_16_BE, short.class, ArrayChecker.SHORT }, { ID, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.SHORT },
{ ID, MemoryLayouts.BITS_16_BE, char.class, ArrayChecker.CHAR }, { ID, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.CHAR },
{ ID, MemoryLayouts.BITS_32_BE, int.class, ArrayChecker.INT }, { ID, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.INT },
{ ID, MemoryLayouts.BITS_64_BE, long.class, ArrayChecker.LONG }, { ID, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.LONG },
{ ID, MemoryLayouts.BITS_32_BE, float.class, ArrayChecker.FLOAT }, { ID, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.FLOAT },
{ ID, MemoryLayouts.BITS_64_BE, double.class, ArrayChecker.DOUBLE }, { ID, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.DOUBLE },
//BE, RO //BE, RO
{ IMMUTABLE, MemoryLayouts.BITS_8_BE, byte.class, ArrayChecker.BYTE }, { IMMUTABLE, ValueLayout.JAVA_BYTE, ArrayChecker.BYTE },
{ IMMUTABLE, MemoryLayouts.BITS_16_BE, short.class, ArrayChecker.SHORT }, { IMMUTABLE, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.SHORT },
{ IMMUTABLE, MemoryLayouts.BITS_16_BE, char.class, ArrayChecker.CHAR }, { IMMUTABLE, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.CHAR },
{ IMMUTABLE, MemoryLayouts.BITS_32_BE, int.class, ArrayChecker.INT }, { IMMUTABLE, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.INT },
{ IMMUTABLE, MemoryLayouts.BITS_64_BE, long.class, ArrayChecker.LONG }, { IMMUTABLE, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.LONG },
{ IMMUTABLE, MemoryLayouts.BITS_32_BE, float.class, ArrayChecker.FLOAT }, { IMMUTABLE, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.FLOAT },
{ IMMUTABLE, MemoryLayouts.BITS_64_BE, double.class, ArrayChecker.DOUBLE }, { IMMUTABLE, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.DOUBLE },
//LE, RW //LE, RW
{ ID, MemoryLayouts.BITS_8_LE, byte.class, ArrayChecker.BYTE }, { ID, ValueLayout.JAVA_BYTE, ArrayChecker.BYTE },
{ ID, MemoryLayouts.BITS_16_LE, short.class, ArrayChecker.SHORT }, { ID, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.SHORT },
{ ID, MemoryLayouts.BITS_16_LE, char.class, ArrayChecker.CHAR }, { ID, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.CHAR },
{ ID, MemoryLayouts.BITS_32_LE, int.class, ArrayChecker.INT }, { ID, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.INT },
{ ID, MemoryLayouts.BITS_64_LE, long.class, ArrayChecker.LONG }, { ID, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.LONG },
{ ID, MemoryLayouts.BITS_32_LE, float.class, ArrayChecker.FLOAT }, { ID, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.FLOAT },
{ ID, MemoryLayouts.BITS_64_LE, double.class, ArrayChecker.DOUBLE }, { ID, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.DOUBLE },
//LE, RO //LE, RO
{ IMMUTABLE, MemoryLayouts.BITS_8_LE, byte.class, ArrayChecker.BYTE }, { IMMUTABLE, ValueLayout.JAVA_BYTE, ArrayChecker.BYTE },
{ IMMUTABLE, MemoryLayouts.BITS_16_LE, short.class, ArrayChecker.SHORT }, { IMMUTABLE, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.SHORT },
{ IMMUTABLE, MemoryLayouts.BITS_16_LE, char.class, ArrayChecker.CHAR }, { IMMUTABLE, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.CHAR },
{ IMMUTABLE, MemoryLayouts.BITS_32_LE, int.class, ArrayChecker.INT }, { IMMUTABLE, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.INT },
{ IMMUTABLE, MemoryLayouts.BITS_64_LE, long.class, ArrayChecker.LONG }, { IMMUTABLE, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.LONG },
{ IMMUTABLE, MemoryLayouts.BITS_32_LE, float.class, ArrayChecker.FLOAT }, { IMMUTABLE, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.FLOAT },
{ IMMUTABLE, MemoryLayouts.BITS_64_LE, double.class, ArrayChecker.DOUBLE }, { IMMUTABLE, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.DOUBLE },
}; };
} }
@ -381,37 +375,45 @@ public class TestMemoryAccess {
public Object[][] createMatrixData() { public Object[][] createMatrixData() {
return new Object[][] { return new Object[][] {
//BE, RW //BE, RW
{ ID, MemoryLayouts.BITS_8_BE, byte.class, MatrixChecker.BYTE }, { ID, ValueLayout.JAVA_BYTE, MatrixChecker.BYTE },
{ ID, MemoryLayouts.BITS_16_BE, short.class, MatrixChecker.SHORT }, { ID, ValueLayout.JAVA_BOOLEAN, MatrixChecker.BOOLEAN },
{ ID, MemoryLayouts.BITS_16_BE, char.class, MatrixChecker.CHAR }, { ID, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.SHORT },
{ ID, MemoryLayouts.BITS_32_BE, int.class, MatrixChecker.INT }, { ID, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.CHAR },
{ ID, MemoryLayouts.BITS_64_BE, long.class, MatrixChecker.LONG }, { ID, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.INT },
{ ID, MemoryLayouts.BITS_32_BE, float.class, MatrixChecker.FLOAT }, { ID, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.LONG },
{ ID, MemoryLayouts.BITS_64_BE, double.class, MatrixChecker.DOUBLE }, { ID, ValueLayout.ADDRESS.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.ADDR },
{ ID, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.FLOAT },
{ ID, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.DOUBLE },
//BE, RO //BE, RO
{ IMMUTABLE, MemoryLayouts.BITS_8_BE, byte.class, MatrixChecker.BYTE }, { IMMUTABLE, ValueLayout.JAVA_BYTE, MatrixChecker.BYTE },
{ IMMUTABLE, MemoryLayouts.BITS_16_BE, short.class, MatrixChecker.SHORT }, { IMMUTABLE, ValueLayout.JAVA_BOOLEAN, MatrixChecker.BOOLEAN },
{ IMMUTABLE, MemoryLayouts.BITS_16_BE, char.class, MatrixChecker.CHAR }, { IMMUTABLE, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.SHORT },
{ IMMUTABLE, MemoryLayouts.BITS_32_BE, int.class, MatrixChecker.INT }, { IMMUTABLE, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.CHAR },
{ IMMUTABLE, MemoryLayouts.BITS_64_BE, long.class, MatrixChecker.LONG }, { IMMUTABLE, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.INT },
{ IMMUTABLE, MemoryLayouts.BITS_32_BE, float.class, MatrixChecker.FLOAT }, { IMMUTABLE, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.LONG },
{ IMMUTABLE, MemoryLayouts.BITS_64_BE, double.class, MatrixChecker.DOUBLE }, { IMMUTABLE, ValueLayout.ADDRESS.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.ADDR },
{ IMMUTABLE, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.FLOAT },
{ IMMUTABLE, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.DOUBLE },
//LE, RW //LE, RW
{ ID, MemoryLayouts.BITS_8_LE, byte.class, MatrixChecker.BYTE }, { ID, ValueLayout.JAVA_BYTE, MatrixChecker.BYTE },
{ ID, MemoryLayouts.BITS_16_LE, short.class, MatrixChecker.SHORT }, { ID, ValueLayout.JAVA_BOOLEAN, MatrixChecker.BOOLEAN },
{ ID, MemoryLayouts.BITS_16_LE, char.class, MatrixChecker.CHAR }, { ID, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.SHORT },
{ ID, MemoryLayouts.BITS_32_LE, int.class, MatrixChecker.INT }, { ID, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.CHAR },
{ ID, MemoryLayouts.BITS_64_LE, long.class, MatrixChecker.LONG }, { ID, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.INT },
{ ID, MemoryLayouts.BITS_32_LE, float.class, MatrixChecker.FLOAT }, { ID, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.LONG },
{ ID, MemoryLayouts.BITS_64_LE, double.class, MatrixChecker.DOUBLE }, { ID, ValueLayout.ADDRESS.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.ADDR },
{ ID, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.FLOAT },
{ ID, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.DOUBLE },
//LE, RO //LE, RO
{ IMMUTABLE, MemoryLayouts.BITS_8_LE, byte.class, MatrixChecker.BYTE }, { IMMUTABLE, ValueLayout.JAVA_BYTE, MatrixChecker.BYTE },
{ IMMUTABLE, MemoryLayouts.BITS_16_LE, short.class, MatrixChecker.SHORT }, { IMMUTABLE, ValueLayout.JAVA_BOOLEAN, MatrixChecker.BOOLEAN },
{ IMMUTABLE, MemoryLayouts.BITS_16_LE, char.class, MatrixChecker.CHAR }, { IMMUTABLE, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.SHORT },
{ IMMUTABLE, MemoryLayouts.BITS_32_LE, int.class, MatrixChecker.INT }, { IMMUTABLE, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.CHAR },
{ IMMUTABLE, MemoryLayouts.BITS_64_LE, long.class, MatrixChecker.LONG }, { IMMUTABLE, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.INT },
{ IMMUTABLE, MemoryLayouts.BITS_32_LE, float.class, MatrixChecker.FLOAT }, { IMMUTABLE, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.LONG },
{ IMMUTABLE, MemoryLayouts.BITS_64_LE, double.class, MatrixChecker.DOUBLE }, { IMMUTABLE, ValueLayout.ADDRESS.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.ADDR },
{ IMMUTABLE, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.FLOAT },
{ IMMUTABLE, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.DOUBLE },
}; };
} }
@ -423,6 +425,11 @@ public class TestMemoryAccess {
assertEquals(r + c, (byte)handle.get(segment, r, c)); assertEquals(r + c, (byte)handle.get(segment, r, c));
}; };
MatrixChecker BOOLEAN = (handle, segment, r, c) -> {
handle.set(segment, r, c, (r + c) != 0);
assertEquals((r + c) != 0, (boolean)handle.get(segment, r, c));
};
MatrixChecker SHORT = (handle, segment, r, c) -> { MatrixChecker SHORT = (handle, segment, r, c) -> {
handle.set(segment, r, c, (short)(r + c)); handle.set(segment, r, c, (short)(r + c));
assertEquals(r + c, (short)handle.get(segment, r, c)); assertEquals(r + c, (short)handle.get(segment, r, c));
@ -443,6 +450,11 @@ public class TestMemoryAccess {
assertEquals(r + c, (long)handle.get(segment, r, c)); assertEquals(r + c, (long)handle.get(segment, r, c));
}; };
MatrixChecker ADDR = (handle, segment, r, c) -> {
handle.set(segment, r, c, MemoryAddress.ofLong(r + c));
assertEquals(MemoryAddress.ofLong(r + c), (MemoryAddress)handle.get(segment, r, c));
};
MatrixChecker FLOAT = (handle, segment, r, c) -> { MatrixChecker FLOAT = (handle, segment, r, c) -> {
handle.set(segment, r, c, (float)(r + c)); handle.set(segment, r, c, (float)(r + c));
assertEquals((float)(r + c), (float)handle.get(segment, r, c)); assertEquals((float)(r + c), (float)handle.get(segment, r, c));
@ -453,14 +465,4 @@ public class TestMemoryAccess {
assertEquals((double)(r + c), (double)handle.get(segment, r, c)); assertEquals((double)(r + c), (double)handle.get(segment, r, c));
}; };
} }
@DataProvider(name = "badCarriers")
public Object[][] createBadCarriers() {
return new Object[][] {
{ void.class },
{ boolean.class },
{ Object.class },
{ int[].class }
};
}
} }

View file

@ -0,0 +1,296 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @run testng/othervm --enable-native-access=ALL-UNNAMED TestMemoryAccessInstance
*/
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.function.Function;
import jdk.incubator.foreign.ResourceScope;
import jdk.incubator.foreign.ValueLayout;
import org.testng.annotations.*;
import static org.testng.Assert.*;
public class TestMemoryAccessInstance {
static class Accessor<T, X, L> {
interface SegmentGetter<T, X, L> {
X get(T buffer, L layout, long offset);
}
interface SegmentSetter<T, X, L> {
void set(T buffer, L layout, long offset, X o);
}
interface BufferGetter<X> {
X get(ByteBuffer segment, int offset);
}
interface BufferSetter<X> {
void set(ByteBuffer buffer, int offset, X o);
}
final X value;
final L layout;
final Function<MemorySegment, T> transform;
final SegmentGetter<T, X, L> segmentGetter;
final SegmentSetter<T, X, L> segmentSetter;
final BufferGetter<X> bufferGetter;
final BufferSetter<X> bufferSetter;
Accessor(Function<MemorySegment, T> transform, L layout, X value,
SegmentGetter<T, X, L> segmentGetter, SegmentSetter<T, X, L> segmentSetter,
BufferGetter<X> bufferGetter, BufferSetter<X> bufferSetter) {
this.transform = transform;
this.layout = layout;
this.value = value;
this.segmentGetter = segmentGetter;
this.segmentSetter = segmentSetter;
this.bufferGetter = bufferGetter;
this.bufferSetter = bufferSetter;
}
void test() {
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment segment = MemorySegment.allocateNative(64, scope);
ByteBuffer buffer = segment.asByteBuffer();
T t = transform.apply(segment);
segmentSetter.set(t, layout, 4, value);
assertEquals(bufferGetter.get(buffer, 4), value);
bufferSetter.set(buffer, 4, value);
assertEquals(value, segmentGetter.get(t, layout, 4));
}
}
static <L, X> Accessor<MemorySegment, X, L> ofSegment(L layout, X value,
SegmentGetter<MemorySegment, X, L> segmentGetter, SegmentSetter<MemorySegment, X, L> segmentSetter,
BufferGetter<X> bufferGetter, BufferSetter<X> bufferSetter) {
return new Accessor<>(Function.identity(), layout, value, segmentGetter, segmentSetter, bufferGetter, bufferSetter);
}
static <L, X> Accessor<MemoryAddress, X, L> ofAddress(L layout, X value,
SegmentGetter<MemoryAddress, X, L> segmentGetter, SegmentSetter<MemoryAddress, X, L> segmentSetter,
BufferGetter<X> bufferGetter, BufferSetter<X> bufferSetter) {
return new Accessor<>(MemorySegment::address, layout, value, segmentGetter, segmentSetter, bufferGetter, bufferSetter);
}
}
@Test(dataProvider = "segmentAccessors")
public void testSegmentAccess(String testName, Accessor<?, ?, ?> accessor) {
accessor.test();
}
@Test(dataProvider = "addressAccessors")
public void testAddressAccess(String testName, Accessor<?, ?, ?> accessor) {
accessor.test();
}
static final ByteOrder NE = ByteOrder.nativeOrder();
@DataProvider(name = "segmentAccessors")
static Object[][] segmentAccessors() {
return new Object[][]{
{"byte", Accessor.ofSegment(ValueLayout.JAVA_BYTE, (byte) 42,
MemorySegment::get, MemorySegment::set,
ByteBuffer::get, ByteBuffer::put)
},
{"bool", Accessor.ofSegment(ValueLayout.JAVA_BOOLEAN, false,
MemorySegment::get, MemorySegment::set,
(bb, pos) -> bb.get(pos) != 0, (bb, pos, v) -> bb.put(pos, v ? (byte)1 : (byte)0))
},
{"char", Accessor.ofSegment(ValueLayout.JAVA_CHAR, (char) 42,
MemorySegment::get, MemorySegment::set,
(bb, pos) -> bb.order(NE).getChar(pos), (bb, pos, v) -> bb.order(NE).putChar(pos, v))
},
{"int", Accessor.ofSegment(ValueLayout.JAVA_INT, 42,
MemorySegment::get, MemorySegment::set,
(bb, pos) -> bb.order(NE).getInt(pos), (bb, pos, v) -> bb.order(NE).putInt(pos, v))
},
{"float", Accessor.ofSegment(ValueLayout.JAVA_FLOAT, 42f,
MemorySegment::get, MemorySegment::set,
(bb, pos) -> bb.order(NE).getFloat(pos), (bb, pos, v) -> bb.order(NE).putFloat(pos, v))
},
{"long", Accessor.ofSegment(ValueLayout.JAVA_LONG, 42L,
MemorySegment::get, MemorySegment::set,
(bb, pos) -> bb.order(NE).getLong(pos), (bb, pos, v) -> bb.order(NE).putLong(pos, v))
},
{"double", Accessor.ofSegment(ValueLayout.JAVA_DOUBLE, 42d,
MemorySegment::get, MemorySegment::set,
(bb, pos) -> bb.order(NE).getDouble(pos), (bb, pos, v) -> bb.order(NE).putDouble(pos, v))
},
{ "address", Accessor.ofSegment(ValueLayout.ADDRESS, MemoryAddress.ofLong(42),
MemorySegment::get, MemorySegment::set,
(bb, pos) -> {
ByteBuffer nb = bb.order(NE);
long addr = ValueLayout.ADDRESS.byteSize() == 8 ?
nb.getLong(pos) : nb.getInt(pos);
return MemoryAddress.ofLong(addr);
},
(bb, pos, v) -> {
ByteBuffer nb = bb.order(NE);
if (ValueLayout.ADDRESS.byteSize() == 8) {
nb.putLong(pos, v.toRawLongValue());
} else {
nb.putInt(pos, (int)v.toRawLongValue());
}
})
},
{"char/index", Accessor.ofSegment(ValueLayout.JAVA_CHAR, (char) 42,
MemorySegment::getAtIndex, MemorySegment::setAtIndex,
(bb, pos) -> bb.order(NE).getChar(pos * 2), (bb, pos, v) -> bb.order(NE).putChar(pos * 2, v))
},
{"int/index", Accessor.ofSegment(ValueLayout.JAVA_INT, 42,
MemorySegment::getAtIndex, MemorySegment::setAtIndex,
(bb, pos) -> bb.order(NE).getInt(pos * 4), (bb, pos, v) -> bb.order(NE).putInt(pos * 4, v))
},
{"float/index", Accessor.ofSegment(ValueLayout.JAVA_FLOAT, 42f,
MemorySegment::getAtIndex, MemorySegment::setAtIndex,
(bb, pos) -> bb.order(NE).getFloat(pos * 4), (bb, pos, v) -> bb.order(NE).putFloat(pos * 4, v))
},
{"long/index", Accessor.ofSegment(ValueLayout.JAVA_LONG, 42L,
MemorySegment::getAtIndex, MemorySegment::setAtIndex,
(bb, pos) -> bb.order(NE).getLong(pos * 8), (bb, pos, v) -> bb.order(NE).putLong(pos * 8, v))
},
{"double/index", Accessor.ofSegment(ValueLayout.JAVA_DOUBLE, 42d,
MemorySegment::getAtIndex, MemorySegment::setAtIndex,
(bb, pos) -> bb.order(NE).getDouble(pos * 8), (bb, pos, v) -> bb.order(NE).putDouble(pos * 8, v))
},
{ "address/index", Accessor.ofSegment(ValueLayout.ADDRESS, MemoryAddress.ofLong(42),
MemorySegment::getAtIndex, MemorySegment::setAtIndex,
(bb, pos) -> {
ByteBuffer nb = bb.order(NE);
long addr = ValueLayout.ADDRESS.byteSize() == 8 ?
nb.getLong(pos * 8) : nb.getInt(pos * 4);
return MemoryAddress.ofLong(addr);
},
(bb, pos, v) -> {
ByteBuffer nb = bb.order(NE);
if (ValueLayout.ADDRESS.byteSize() == 8) {
nb.putLong(pos * 8, v.toRawLongValue());
} else {
nb.putInt(pos * 4, (int)v.toRawLongValue());
}
})
},
};
}
@DataProvider(name = "addressAccessors")
static Object[][] addressAccessors() {
return new Object[][]{
{"byte", Accessor.ofAddress(ValueLayout.JAVA_BYTE, (byte) 42,
MemoryAddress::get, MemoryAddress::set,
ByteBuffer::get, ByteBuffer::put)
},
{"bool", Accessor.ofAddress(ValueLayout.JAVA_BOOLEAN, false,
MemoryAddress::get, MemoryAddress::set,
(bb, pos) -> bb.get(pos) != 0, (bb, pos, v) -> bb.put(pos, v ? (byte)1 : (byte)0))
},
{"char", Accessor.ofAddress(ValueLayout.JAVA_CHAR, (char) 42,
MemoryAddress::get, MemoryAddress::set,
(bb, pos) -> bb.order(NE).getChar(pos), (bb, pos, v) -> bb.order(NE).putChar(pos, v))
},
{"int", Accessor.ofAddress(ValueLayout.JAVA_INT, 42,
MemoryAddress::get, MemoryAddress::set,
(bb, pos) -> bb.order(NE).getInt(pos), (bb, pos, v) -> bb.order(NE).putInt(pos, v))
},
{"float", Accessor.ofAddress(ValueLayout.JAVA_FLOAT, 42f,
MemoryAddress::get, MemoryAddress::set,
(bb, pos) -> bb.order(NE).getFloat(pos), (bb, pos, v) -> bb.order(NE).putFloat(pos, v))
},
{"long", Accessor.ofAddress(ValueLayout.JAVA_LONG, 42L,
MemoryAddress::get, MemoryAddress::set,
(bb, pos) -> bb.order(NE).getLong(pos), (bb, pos, v) -> bb.order(NE).putLong(pos, v))
},
{"double", Accessor.ofAddress(ValueLayout.JAVA_DOUBLE, 42d,
MemoryAddress::get, MemoryAddress::set,
(bb, pos) -> bb.order(NE).getDouble(pos), (bb, pos, v) -> bb.order(NE).putDouble(pos, v))
},
{ "address", Accessor.ofAddress(ValueLayout.ADDRESS, MemoryAddress.ofLong(42),
MemoryAddress::get, MemoryAddress::set,
(bb, pos) -> {
ByteBuffer nb = bb.order(NE);
long addr = ValueLayout.ADDRESS.byteSize() == 8 ?
nb.getLong(pos) : nb.getInt(pos);
return MemoryAddress.ofLong(addr);
},
(bb, pos, v) -> {
ByteBuffer nb = bb.order(NE);
if (ValueLayout.ADDRESS.byteSize() == 8) {
nb.putLong(pos, v.toRawLongValue());
} else {
nb.putInt(pos, (int)v.toRawLongValue());
}
})
},
{"char/index", Accessor.ofAddress(ValueLayout.JAVA_CHAR, (char) 42,
MemoryAddress::getAtIndex, MemoryAddress::setAtIndex,
(bb, pos) -> bb.order(NE).getChar(pos * 2), (bb, pos, v) -> bb.order(NE).putChar(pos * 2, v))
},
{"int/index", Accessor.ofAddress(ValueLayout.JAVA_INT, 42,
MemoryAddress::getAtIndex, MemoryAddress::setAtIndex,
(bb, pos) -> bb.order(NE).getInt(pos * 4), (bb, pos, v) -> bb.order(NE).putInt(pos * 4, v))
},
{"float/index", Accessor.ofAddress(ValueLayout.JAVA_FLOAT, 42f,
MemoryAddress::getAtIndex, MemoryAddress::setAtIndex,
(bb, pos) -> bb.order(NE).getFloat(pos * 4), (bb, pos, v) -> bb.order(NE).putFloat(pos * 4, v))
},
{"long/index", Accessor.ofAddress(ValueLayout.JAVA_LONG, 42L,
MemoryAddress::getAtIndex, MemoryAddress::setAtIndex,
(bb, pos) -> bb.order(NE).getLong(pos * 8), (bb, pos, v) -> bb.order(NE).putLong(pos * 8, v))
},
{"double/index", Accessor.ofAddress(ValueLayout.JAVA_DOUBLE, 42d,
MemoryAddress::getAtIndex, MemoryAddress::setAtIndex,
(bb, pos) -> bb.order(NE).getDouble(pos * 8), (bb, pos, v) -> bb.order(NE).putDouble(pos * 8, v))
},
{ "address/index", Accessor.ofAddress(ValueLayout.ADDRESS, MemoryAddress.ofLong(42),
MemoryAddress::getAtIndex, MemoryAddress::setAtIndex,
(bb, pos) -> {
ByteBuffer nb = bb.order(NE);
long addr = ValueLayout.ADDRESS.byteSize() == 8 ?
nb.getLong(pos * 8) : nb.getInt(pos * 4);
return MemoryAddress.ofLong(addr);
},
(bb, pos, v) -> {
ByteBuffer nb = bb.order(NE);
if (ValueLayout.ADDRESS.byteSize() == 8) {
nb.putLong(pos * 8, v.toRawLongValue());
} else {
nb.putInt(pos * 4, (int)v.toRawLongValue());
}
})
}
};
}
}

View file

@ -1,364 +0,0 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @run testng TestMemoryAccessStatics
*/
import jdk.incubator.foreign.MemoryAccess;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import org.testng.annotations.*;
import static org.testng.Assert.*;
public class TestMemoryAccessStatics {
static class Accessor<X> {
interface SegmentGetter<X> {
X get(MemorySegment segment);
}
interface SegmentSetter<X> {
void set(MemorySegment segment, X o);
}
interface BufferGetter<X> {
X get(ByteBuffer segment);
}
interface BufferSetter<X> {
void set(ByteBuffer buffer, X o);
}
final X value;
final SegmentGetter<X> segmentGetter;
final SegmentSetter<X> segmentSetter;
final BufferGetter<X> bufferGetter;
final BufferSetter<X> bufferSetter;
Accessor(X value,
SegmentGetter<X> segmentGetter, SegmentSetter<X> segmentSetter,
BufferGetter<X> bufferGetter, BufferSetter<X> bufferSetter) {
this.value = value;
this.segmentGetter = segmentGetter;
this.segmentSetter = segmentSetter;
this.bufferGetter = bufferGetter;
this.bufferSetter = bufferSetter;
}
void test() {
MemorySegment segment = MemorySegment.ofArray(new byte[32]);
ByteBuffer buffer = segment.asByteBuffer();
segmentSetter.set(segment, value);
assertEquals(bufferGetter.get(buffer), value);
bufferSetter.set(buffer, value);
assertEquals(value, segmentGetter.get(segment));
}
<Z> Accessor<Z> of(Z value,
SegmentGetter<Z> segmentGetter, SegmentSetter<Z> segmentSetter,
BufferGetter<Z> bufferGetter, BufferSetter<Z> bufferSetter) {
return new Accessor<>(value, segmentGetter, segmentSetter, bufferGetter, bufferSetter);
}
}
@Test(dataProvider = "accessors")
public void testMemoryAccess(String testName, Accessor<?> accessor) {
accessor.test();
}
static final ByteOrder BE = ByteOrder.BIG_ENDIAN;
static final ByteOrder LE = ByteOrder.LITTLE_ENDIAN;
static final ByteOrder NE = ByteOrder.nativeOrder();
@DataProvider(name = "accessors")
static Object[][] accessors() {
return new Object[][]{
{"byte", new Accessor<>((byte) 42,
MemoryAccess::getByte, MemoryAccess::setByte,
(bb) -> bb.get(0), (bb, v) -> bb.put(0, v))
},
{"char", new Accessor<>((char) 42,
MemoryAccess::getChar, MemoryAccess::setChar,
(bb) -> bb.order(NE).getChar(0), (bb, v) -> bb.order(NE).putChar(0, v))
},
{"char/LE", new Accessor<>((char) 42,
s -> MemoryAccess.getChar(s, LE), (s, x) -> MemoryAccess.setChar(s, LE, x),
(bb) -> bb.order(LE).getChar(0), (bb, v) -> bb.order(LE).putChar(0, v))
},
{"char/BE", new Accessor<>((char) 42,
s -> MemoryAccess.getChar(s, BE), (s, x) -> MemoryAccess.setChar(s, BE, x),
(bb) -> bb.order(BE).getChar(0), (bb, v) -> bb.order(BE).putChar(0, v))
},
{"short", new Accessor<>((short) 42,
MemoryAccess::getShort, MemoryAccess::setShort,
(bb) -> bb.order(NE).getShort(0), (bb, v) -> bb.order(NE).putShort(0, v))
},
{"short/LE", new Accessor<>((short) 42,
s -> MemoryAccess.getShort(s, LE), (s, x) -> MemoryAccess.setShort(s, LE, x),
(bb) -> bb.order(LE).getShort(0), (bb, v) -> bb.order(LE).putShort(0, v))
},
{"short/BE", new Accessor<>((short) 42,
s -> MemoryAccess.getShort(s, BE), (s, x) -> MemoryAccess.setShort(s, BE, x),
(bb) -> bb.order(BE).getShort(0), (bb, v) -> bb.order(BE).putShort(0, v))
},
{"int", new Accessor<>(42,
MemoryAccess::getInt, MemoryAccess::setInt,
(bb) -> bb.order(NE).getInt(0), (bb, v) -> bb.order(NE).putInt(0, v))
},
{"int/LE", new Accessor<>(42,
s -> MemoryAccess.getInt(s, LE), (s, x) -> MemoryAccess.setInt(s, LE, x),
(bb) -> bb.order(LE).getInt(0), (bb, v) -> bb.order(LE).putInt(0, v))
},
{"int/BE", new Accessor<>(42,
s -> MemoryAccess.getInt(s, BE), (s, x) -> MemoryAccess.setInt(s, BE, x),
(bb) -> bb.order(BE).getInt(0), (bb, v) -> bb.order(BE).putInt(0, v))
},
// float, no offset
{"float", new Accessor<>(42f,
MemoryAccess::getFloat, MemoryAccess::setFloat,
(bb) -> bb.order(NE).getFloat(0), (bb, v) -> bb.order(NE).putFloat(0, v))
},
{"float/LE", new Accessor<>(42f,
s -> MemoryAccess.getFloat(s, LE), (s, x) -> MemoryAccess.setFloat(s, LE, x),
(bb) -> bb.order(LE).getFloat(0), (bb, v) -> bb.order(LE).putFloat(0, v))
},
{"float/BE", new Accessor<>(42f,
s -> MemoryAccess.getFloat(s, BE), (s, x) -> MemoryAccess.setFloat(s, BE, x),
(bb) -> bb.order(BE).getFloat(0), (bb, v) -> bb.order(BE).putFloat(0, v))
},
// double, no offset
{"double", new Accessor<>(42d,
MemoryAccess::getDouble, MemoryAccess::setDouble,
(bb) -> bb.order(NE).getDouble(0), (bb, v) -> bb.order(NE).putDouble(0, v))
},
{"double/LE", new Accessor<>(42d,
s -> MemoryAccess.getDouble(s, LE), (s, x) -> MemoryAccess.setDouble(s, LE, x),
(bb) -> bb.order(LE).getDouble(0), (bb, v) -> bb.order(LE).putDouble(0, v))
},
{"double/BE", new Accessor<>(42d,
s -> MemoryAccess.getDouble(s, BE), (s, x) -> MemoryAccess.setDouble(s, BE, x),
(bb) -> bb.order(BE).getDouble(0), (bb, v) -> bb.order(BE).putDouble(0, v))
},
// byte, offset
{"byte/offset", new Accessor<>((byte) 42,
s -> MemoryAccess.getByteAtOffset(s, 4), (s, x) -> MemoryAccess.setByteAtOffset(s, 4, x),
(bb) -> bb.get(4), (bb, v) -> bb.put(4, v))
},
// char, offset
{"char/offset", new Accessor<>((char) 42,
s -> MemoryAccess.getCharAtOffset(s, 4), (s, x) -> MemoryAccess.setCharAtOffset(s, 4, x),
(bb) -> bb.order(NE).getChar(4), (bb, v) -> bb.order(NE).putChar(4, v))
},
{"char/offset/LE", new Accessor<>((char) 42,
s -> MemoryAccess.getCharAtOffset(s, 4, LE), (s, x) -> MemoryAccess.setCharAtOffset(s, 4, LE, x),
(bb) -> bb.order(LE).getChar(4), (bb, v) -> bb.order(LE).putChar(4, v))
},
{"char/offset/BE", new Accessor<>((char) 42,
s -> MemoryAccess.getCharAtOffset(s, 4, BE), (s, x) -> MemoryAccess.setCharAtOffset(s, 4, BE, x),
(bb) -> bb.order(BE).getChar(4), (bb, v) -> bb.order(BE).putChar(4, v))
},
// short, offset
{"short/offset", new Accessor<>((short) 42,
s -> MemoryAccess.getShortAtOffset(s, 4), (s, x) -> MemoryAccess.setShortAtOffset(s, 4, x),
(bb) -> bb.order(NE).getShort(4), (bb, v) -> bb.order(NE).putShort(4, v))
},
{"short/offset/LE", new Accessor<>((short) 42,
s -> MemoryAccess.getShortAtOffset(s, 4, LE), (s, x) -> MemoryAccess.setShortAtOffset(s, 4, LE, x),
(bb) -> bb.order(LE).getShort(4), (bb, v) -> bb.order(LE).putShort(4, v))
},
{"short/offset/BE", new Accessor<>((short) 42,
s -> MemoryAccess.getShortAtOffset(s, 4, BE), (s, x) -> MemoryAccess.setShortAtOffset(s, 4, BE, x),
(bb) -> bb.order(BE).getShort(4), (bb, v) -> bb.order(BE).putShort(4, v))
},
// int, offset
{"int/offset", new Accessor<>(42,
s -> MemoryAccess.getIntAtOffset(s, 4), (s, x) -> MemoryAccess.setIntAtOffset(s, 4, x),
(bb) -> bb.order(NE).getInt(4), (bb, v) -> bb.order(NE).putInt(4, v))
},
{"int/offset/LE", new Accessor<>(42,
s -> MemoryAccess.getIntAtOffset(s, 4, LE), (s, x) -> MemoryAccess.setIntAtOffset(s, 4, LE, x),
(bb) -> bb.order(LE).getInt(4), (bb, v) -> bb.order(LE).putInt(4, v))
},
{"int/offset/BE", new Accessor<>(42,
s -> MemoryAccess.getIntAtOffset(s, 4, BE), (s, x) -> MemoryAccess.setIntAtOffset(s, 4, BE, x),
(bb) -> bb.order(BE).getInt(4), (bb, v) -> bb.order(BE).putInt(4, v))
},
// float, offset
{"float/offset", new Accessor<>(42f,
s -> MemoryAccess.getFloatAtOffset(s, 4), (s, x) -> MemoryAccess.setFloatAtOffset(s, 4, x),
(bb) -> bb.order(NE).getFloat(4), (bb, v) -> bb.order(NE).putFloat(4, v))
},
{"float/offset/LE", new Accessor<>(42f,
s -> MemoryAccess.getFloatAtOffset(s, 4, LE), (s, x) -> MemoryAccess.setFloatAtOffset(s, 4, LE, x),
(bb) -> bb.order(LE).getFloat(4), (bb, v) -> bb.order(LE).putFloat(4, v))
},
{"float/offset/BE", new Accessor<>(42f,
s -> MemoryAccess.getFloatAtOffset(s, 4, BE), (s, x) -> MemoryAccess.setFloatAtOffset(s, 4, BE, x),
(bb) -> bb.order(BE).getFloat(4), (bb, v) -> bb.order(BE).putFloat(4, v))
},
// double, offset
{"double/offset", new Accessor<>(42d,
s -> MemoryAccess.getDoubleAtOffset(s, 4), (s, x) -> MemoryAccess.setDoubleAtOffset(s, 4, x),
(bb) -> bb.order(NE).getDouble(4), (bb, v) -> bb.order(NE).putDouble(4, v))
},
{"double/offset/LE", new Accessor<>(42d,
s -> MemoryAccess.getDoubleAtOffset(s, 4, LE), (s, x) -> MemoryAccess.setDoubleAtOffset(s, 4, LE, x),
(bb) -> bb.order(LE).getDouble(4), (bb, v) -> bb.order(LE).putDouble(4, v))
},
{"double/offset/BE", new Accessor<>(42d,
s -> MemoryAccess.getDoubleAtOffset(s, 4, BE), (s, x) -> MemoryAccess.setDoubleAtOffset(s, 4, BE, x),
(bb) -> bb.order(BE).getDouble(4), (bb, v) -> bb.order(BE).putDouble(4, v))
},
// char, index
{"char/index", new Accessor<>((char) 42,
s -> MemoryAccess.getCharAtIndex(s, 2), (s, x) -> MemoryAccess.setCharAtIndex(s, 2, x),
(bb) -> bb.order(NE).asCharBuffer().get(2), (bb, v) -> bb.order(NE).asCharBuffer().put(2, v))
},
{"char/index/LE", new Accessor<>((char) 42,
s -> MemoryAccess.getCharAtIndex(s, 2, LE), (s, x) -> MemoryAccess.setCharAtIndex(s, 2, LE, x),
(bb) -> bb.order(LE).asCharBuffer().get(2), (bb, v) -> bb.order(LE).asCharBuffer().put(2, v))
},
{"char/index/BE", new Accessor<>((char) 42,
s -> MemoryAccess.getCharAtIndex(s, 2, BE), (s, x) -> MemoryAccess.setCharAtIndex(s, 2, BE, x),
(bb) -> bb.order(BE).asCharBuffer().get(2), (bb, v) -> bb.order(BE).asCharBuffer().put(2, v))
},
// short, index
{"short/index", new Accessor<>((short) 42,
s -> MemoryAccess.getShortAtIndex(s, 2), (s, x) -> MemoryAccess.setShortAtIndex(s, 2, x),
(bb) -> bb.order(NE).asShortBuffer().get(2), (bb, v) -> bb.order(NE).asShortBuffer().put(2, v))
},
{"short/index/LE", new Accessor<>((short) 42,
s -> MemoryAccess.getShortAtIndex(s, 2, LE), (s, x) -> MemoryAccess.setShortAtIndex(s, 2, LE, x),
(bb) -> bb.order(LE).asShortBuffer().get(2), (bb, v) -> bb.order(LE).asShortBuffer().put(2, v))
},
{"short/index/BE", new Accessor<>((short) 42,
s -> MemoryAccess.getShortAtIndex(s, 2, BE), (s, x) -> MemoryAccess.setShortAtIndex(s, 2, BE, x),
(bb) -> bb.order(BE).asShortBuffer().get(2), (bb, v) -> bb.order(BE).asShortBuffer().put(2, v))
},
{"int/index", new Accessor<>(42,
s -> MemoryAccess.getIntAtIndex(s, 2), (s, x) -> MemoryAccess.setIntAtIndex(s, 2, x),
(bb) -> bb.order(NE).asIntBuffer().get(2), (bb, v) -> bb.order(NE).asIntBuffer().put(2, v))
},
{"int/index/LE", new Accessor<>(42,
s -> MemoryAccess.getIntAtIndex(s, 2, LE), (s, x) -> MemoryAccess.setIntAtIndex(s, 2, LE, x),
(bb) -> bb.order(LE).asIntBuffer().get(2), (bb, v) -> bb.order(LE).asIntBuffer().put(2, v))
},
{"int/index/BE", new Accessor<>(42,
s -> MemoryAccess.getIntAtIndex(s, 2, BE), (s, x) -> MemoryAccess.setIntAtIndex(s, 2, BE, x),
(bb) -> bb.order(BE).asIntBuffer().get(2), (bb, v) -> bb.order(BE).asIntBuffer().put(2, v))
},
{"float/index", new Accessor<>(42f,
s -> MemoryAccess.getFloatAtIndex(s, 2), (s, x) -> MemoryAccess.setFloatAtIndex(s, 2, x),
(bb) -> bb.order(NE).asFloatBuffer().get(2), (bb, v) -> bb.order(NE).asFloatBuffer().put(2, v))
},
{"float/index/LE", new Accessor<>(42f,
s -> MemoryAccess.getFloatAtIndex(s, 2, LE), (s, x) -> MemoryAccess.setFloatAtIndex(s, 2, LE, x),
(bb) -> bb.order(LE).asFloatBuffer().get(2), (bb, v) -> bb.order(LE).asFloatBuffer().put(2, v))
},
{"float/index/BE", new Accessor<>(42f,
s -> MemoryAccess.getFloatAtIndex(s, 2, BE), (s, x) -> MemoryAccess.setFloatAtIndex(s, 2, BE, x),
(bb) -> bb.order(BE).asFloatBuffer().get(2), (bb, v) -> bb.order(BE).asFloatBuffer().put(2, v))
},
{"double/index", new Accessor<>(42d,
s -> MemoryAccess.getDoubleAtIndex(s, 2), (s, x) -> MemoryAccess.setDoubleAtIndex(s, 2, x),
(bb) -> bb.order(NE).asDoubleBuffer().get(2), (bb, v) -> bb.order(NE).asDoubleBuffer().put(2, v))
},
{"double/index/LE", new Accessor<>(42d,
s -> MemoryAccess.getDoubleAtIndex(s, 2, LE), (s, x) -> MemoryAccess.setDoubleAtIndex(s, 2, LE, x),
(bb) -> bb.order(LE).asDoubleBuffer().get(2), (bb, v) -> bb.order(LE).asDoubleBuffer().put(2, v))
},
{"double/index/BE", new Accessor<>(42d,
s -> MemoryAccess.getDoubleAtIndex(s, 2, BE), (s, x) -> MemoryAccess.setDoubleAtIndex(s, 2, BE, x),
(bb) -> bb.order(BE).asDoubleBuffer().get(2), (bb, v) -> bb.order(BE).asDoubleBuffer().put(2, v))
},
{ "address", new Accessor<>(MemoryAddress.ofLong(42),
MemoryAccess::getAddress, MemoryAccess::setAddress,
(bb) -> {
ByteBuffer nb = bb.order(NE);
long addr = MemoryLayouts.ADDRESS.byteSize() == 8 ?
nb.getLong(0) : nb.getInt(0);
return MemoryAddress.ofLong(addr);
},
(bb, v) -> {
ByteBuffer nb = bb.order(NE);
if (MemoryLayouts.ADDRESS.byteSize() == 8) {
nb.putLong(0, v.toRawLongValue());
} else {
nb.putInt(0, (int)v.toRawLongValue());
}
})
},
{ "address/offset", new Accessor<>(MemoryAddress.ofLong(42),
s -> MemoryAccess.getAddressAtOffset(s, 4), (s, x) -> MemoryAccess.setAddressAtOffset(s, 4, x),
(bb) -> {
ByteBuffer nb = bb.order(NE);
long addr = MemoryLayouts.ADDRESS.byteSize() == 8 ?
nb.getLong(4) : nb.getInt(4);
return MemoryAddress.ofLong(addr);
},
(bb, v) -> {
ByteBuffer nb = bb.order(NE);
if (MemoryLayouts.ADDRESS.byteSize() == 8) {
nb.putLong(4, v.toRawLongValue());
} else {
nb.putInt(4, (int)v.toRawLongValue());
}
})
},
{ "address/index", new Accessor<>(MemoryAddress.ofLong(42),
s -> MemoryAccess.getAddressAtIndex(s, 2), (s, x) -> MemoryAccess.setAddressAtIndex(s, 2, x),
(bb) -> {
ByteBuffer nb = bb.order(NE);
long addr = MemoryLayouts.ADDRESS.byteSize() == 8 ?
nb.asLongBuffer().get(2) : nb.asIntBuffer().get(2);
return MemoryAddress.ofLong(addr);
},
(bb, v) -> {
ByteBuffer nb = bb.order(NE);
if (MemoryLayouts.ADDRESS.byteSize() == 8) {
nb.asLongBuffer().put(2, v.toRawLongValue());
} else {
nb.asIntBuffer().put(2, (int)v.toRawLongValue());
}
})
},
};
}
}

View file

@ -26,7 +26,6 @@
* @run testng TestMemoryAlignment * @run testng TestMemoryAlignment
*/ */
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.GroupLayout; import jdk.incubator.foreign.GroupLayout;
@ -36,6 +35,7 @@ import jdk.incubator.foreign.ResourceScope;
import jdk.incubator.foreign.SequenceLayout; import jdk.incubator.foreign.SequenceLayout;
import jdk.incubator.foreign.ValueLayout; import jdk.incubator.foreign.ValueLayout;
import java.lang.invoke.VarHandle; import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
import java.util.stream.LongStream; import java.util.stream.LongStream;
import org.testng.annotations.*; import org.testng.annotations.*;
@ -45,11 +45,13 @@ public class TestMemoryAlignment {
@Test(dataProvider = "alignments") @Test(dataProvider = "alignments")
public void testAlignedAccess(long align) { public void testAlignedAccess(long align) {
ValueLayout layout = MemoryLayouts.BITS_32_BE; ValueLayout layout = ValueLayout.JAVA_INT
.withBitAlignment(32)
.withOrder(ByteOrder.BIG_ENDIAN);
assertEquals(layout.bitAlignment(), 32); assertEquals(layout.bitAlignment(), 32);
ValueLayout aligned = layout.withBitAlignment(align); ValueLayout aligned = layout.withBitAlignment(align);
assertEquals(aligned.bitAlignment(), align); //unreasonable alignment here, to make sure access throws assertEquals(aligned.bitAlignment(), align); //unreasonable alignment here, to make sure access throws
VarHandle vh = aligned.varHandle(int.class); VarHandle vh = aligned.varHandle();
try (ResourceScope scope = ResourceScope.newConfinedScope()) { try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment segment = MemorySegment.allocateNative(aligned, scope); MemorySegment segment = MemorySegment.allocateNative(aligned, scope);
vh.set(segment, -42); vh.set(segment, -42);
@ -60,12 +62,14 @@ public class TestMemoryAlignment {
@Test(dataProvider = "alignments") @Test(dataProvider = "alignments")
public void testUnalignedAccess(long align) { public void testUnalignedAccess(long align) {
ValueLayout layout = MemoryLayouts.BITS_32_BE; ValueLayout layout = ValueLayout.JAVA_INT
.withBitAlignment(32)
.withOrder(ByteOrder.BIG_ENDIAN);
assertEquals(layout.bitAlignment(), 32); assertEquals(layout.bitAlignment(), 32);
ValueLayout aligned = layout.withBitAlignment(align); ValueLayout aligned = layout.withBitAlignment(align);
MemoryLayout alignedGroup = MemoryLayout.structLayout(MemoryLayouts.PAD_8, aligned); MemoryLayout alignedGroup = MemoryLayout.structLayout(MemoryLayout.paddingLayout(8), aligned);
assertEquals(alignedGroup.bitAlignment(), align); assertEquals(alignedGroup.bitAlignment(), align);
VarHandle vh = aligned.varHandle(int.class); VarHandle vh = aligned.varHandle();
try (ResourceScope scope = ResourceScope.newConfinedScope()) { try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment segment = MemorySegment.allocateNative(alignedGroup, scope); MemorySegment segment = MemorySegment.allocateNative(alignedGroup, scope);
vh.set(segment.asSlice(1L), -42); vh.set(segment.asSlice(1L), -42);
@ -77,11 +81,11 @@ public class TestMemoryAlignment {
@Test(dataProvider = "alignments") @Test(dataProvider = "alignments")
public void testUnalignedPath(long align) { public void testUnalignedPath(long align) {
MemoryLayout layout = MemoryLayouts.BITS_32_BE; MemoryLayout layout = ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN);
MemoryLayout aligned = layout.withBitAlignment(align).withName("value"); MemoryLayout aligned = layout.withBitAlignment(align).withName("value");
GroupLayout alignedGroup = MemoryLayout.structLayout(MemoryLayouts.PAD_8, aligned); GroupLayout alignedGroup = MemoryLayout.structLayout(MemoryLayout.paddingLayout(8), aligned);
try { try {
alignedGroup.varHandle(int.class, PathElement.groupElement("value")); alignedGroup.varHandle(PathElement.groupElement("value"));
assertEquals(align, 8); //this is the only case where path is aligned assertEquals(align, 8); //this is the only case where path is aligned
} catch (UnsupportedOperationException ex) { } catch (UnsupportedOperationException ex) {
assertNotEquals(align, 8); //if align != 8, path is always unaligned assertNotEquals(align, 8); //if align != 8, path is always unaligned
@ -90,9 +94,9 @@ public class TestMemoryAlignment {
@Test(dataProvider = "alignments") @Test(dataProvider = "alignments")
public void testUnalignedSequence(long align) { public void testUnalignedSequence(long align) {
SequenceLayout layout = MemoryLayout.sequenceLayout(5, MemoryLayouts.BITS_32_BE.withBitAlignment(align)); SequenceLayout layout = MemoryLayout.sequenceLayout(5, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN).withBitAlignment(align));
try { try {
VarHandle vh = layout.varHandle(int.class, PathElement.sequenceElement()); VarHandle vh = layout.varHandle(PathElement.sequenceElement());
try (ResourceScope scope = ResourceScope.newConfinedScope()) { try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment segment = MemorySegment.allocateNative(layout, scope); MemorySegment segment = MemorySegment.allocateNative(layout, scope);
for (long i = 0 ; i < 5 ; i++) { for (long i = 0 ; i < 5 ; i++) {
@ -106,17 +110,17 @@ public class TestMemoryAlignment {
@Test @Test
public void testPackedAccess() { public void testPackedAccess() {
ValueLayout vChar = MemoryLayouts.BITS_8_BE; ValueLayout vChar = ValueLayout.JAVA_BYTE;
ValueLayout vShort = MemoryLayouts.BITS_16_BE; ValueLayout vShort = ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN);
ValueLayout vInt = MemoryLayouts.BITS_32_BE; ValueLayout vInt = ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN);
//mimic pragma pack(1) //mimic pragma pack(1)
GroupLayout g = MemoryLayout.structLayout(vChar.withBitAlignment(8).withName("a"), GroupLayout g = MemoryLayout.structLayout(vChar.withBitAlignment(8).withName("a"),
vShort.withBitAlignment(8).withName("b"), vShort.withBitAlignment(8).withName("b"),
vInt.withBitAlignment(8).withName("c")); vInt.withBitAlignment(8).withName("c"));
assertEquals(g.bitAlignment(), 8); assertEquals(g.bitAlignment(), 8);
VarHandle vh_c = g.varHandle(byte.class, PathElement.groupElement("a")); VarHandle vh_c = g.varHandle(PathElement.groupElement("a"));
VarHandle vh_s = g.varHandle(short.class, PathElement.groupElement("b")); VarHandle vh_s = g.varHandle(PathElement.groupElement("b"));
VarHandle vh_i = g.varHandle(int.class, PathElement.groupElement("c")); VarHandle vh_i = g.varHandle(PathElement.groupElement("c"));
try (ResourceScope scope = ResourceScope.newConfinedScope()) { try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment segment = MemorySegment.allocateNative(g, scope); MemorySegment segment = MemorySegment.allocateNative(g, scope);
vh_c.set(segment, Byte.MIN_VALUE); vh_c.set(segment, Byte.MIN_VALUE);

Some files were not shown because too many files have changed in this diff Show more