mirror of
https://github.com/openjdk/jdk.git
synced 2026-07-30 20:55:34 +00:00
8275063: Implementation of Foreign Function & Memory API (Second incubator)
Reviewed-by: erikj, psandoz, jvernee, darcy
This commit is contained in:
parent
17e68caad7
commit
96e36071b6
@ -54,6 +54,7 @@ BUILD_JDK_JTREG_EXECUTABLES_CFLAGS_exeJliLaunchTest := \
|
||||
-I$(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS)/native/libjli
|
||||
|
||||
BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libAsyncStackWalk := $(LIBCXX)
|
||||
BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libAsyncInvokers := $(LIBCXX)
|
||||
|
||||
# Platform specific setup
|
||||
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_exerevokeall := advapi32.lib
|
||||
BUILD_JDK_JTREG_LIBRARIES_CFLAGS_libAsyncStackWalk := /EHsc
|
||||
BUILD_JDK_JTREG_LIBRARIES_CFLAGS_libAsyncInvokers := /EHsc
|
||||
else
|
||||
BUILD_JDK_JTREG_LIBRARIES_LIBS_libstringPlatformChars := -ljava
|
||||
BUILD_JDK_JTREG_LIBRARIES_LIBS_libDirectIO := -ljava
|
||||
|
||||
@ -351,7 +351,6 @@
|
||||
/* Foreign API Support */ \
|
||||
template(jdk_internal_invoke_NativeEntryPoint, "jdk/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 */ \
|
||||
JVMCI_VM_SYMBOLS_DO(template, do_alias) \
|
||||
|
||||
@ -1586,18 +1586,6 @@ bool MethodData::profile_unsafe(const methodHandle& m, int bci) {
|
||||
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() {
|
||||
return TypeProfileLevel % 10;
|
||||
}
|
||||
@ -1627,10 +1615,6 @@ bool MethodData::profile_arguments_for_invoke(const methodHandle& m, int bci) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (profile_memory_access(m, bci)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
assert(profile_arguments_jsr292_only(), "inconsistent");
|
||||
return profile_jsr292(m, bci);
|
||||
}
|
||||
|
||||
@ -174,6 +174,7 @@ ProgrammableUpcallHandler::ProgrammableUpcallHandler() {
|
||||
}
|
||||
|
||||
void ProgrammableUpcallHandler::handle_uncaught_exception(oop exception) {
|
||||
ResourceMark rm;
|
||||
// Based on CATCH macro
|
||||
tty->print_cr("Uncaught exception:");
|
||||
exception->print();
|
||||
|
||||
@ -68,6 +68,7 @@ import jdk.internal.org.objectweb.asm.ModuleVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
import sun.security.util.SecurityConstants;
|
||||
|
||||
/**
|
||||
@ -110,7 +111,8 @@ public final class Module implements AnnotatedElement {
|
||||
private final ModuleDescriptor descriptor;
|
||||
|
||||
// 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
|
||||
|
||||
@ -1667,6 +1667,10 @@ abstract class MethodHandleImpl {
|
||||
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:
|
||||
static final int
|
||||
MH_cast = 0,
|
||||
MH_selectAlternative = 1,
|
||||
MH_countedLoopPred = 2,
|
||||
MH_countedLoopStep = 3,
|
||||
MH_initIterator = 4,
|
||||
MH_iteratePred = 5,
|
||||
MH_iterateNext = 6,
|
||||
MH_Array_newInstance = 7,
|
||||
MH_LIMIT = 8;
|
||||
MH_cast = 0,
|
||||
MH_selectAlternative = 1,
|
||||
MH_countedLoopPred = 2,
|
||||
MH_countedLoopStep = 3,
|
||||
MH_initIterator = 4,
|
||||
MH_iteratePred = 5,
|
||||
MH_iterateNext = 6,
|
||||
MH_Array_newInstance = 7,
|
||||
MH_VarHandles_handleCheckedExceptions = 8,
|
||||
MH_LIMIT = 9;
|
||||
|
||||
static MethodHandle getConstantHandle(int idx) {
|
||||
MethodHandle handle = HANDLES[idx];
|
||||
@ -2331,6 +2336,9 @@ abstract class MethodHandleImpl {
|
||||
case MH_Array_newInstance:
|
||||
return IMPL_LOOKUP.findStatic(Array.class, "newInstance",
|
||||
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) {
|
||||
throw newInternalError(ex);
|
||||
|
||||
@ -31,12 +31,9 @@ import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
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.VAR_HANDLE_IDENTITY_ADAPT;
|
||||
import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
final class VarHandles {
|
||||
|
||||
@ -359,13 +354,13 @@ final class VarHandles {
|
||||
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(filterToTarget);
|
||||
Objects.requireNonNull(filterFromTarget);
|
||||
Objects.requireNonNull(pFilterToTarget);
|
||||
Objects.requireNonNull(pFilterFromTarget);
|
||||
//check that from/to filters do not throw checked exceptions
|
||||
noCheckedExceptions(filterToTarget);
|
||||
noCheckedExceptions(filterFromTarget);
|
||||
MethodHandle filterToTarget = adaptForCheckedExceptions(pFilterToTarget);
|
||||
MethodHandle filterFromTarget = adaptForCheckedExceptions(pFilterFromTarget);
|
||||
|
||||
List<Class<?>> newCoordinates = new ArrayList<>();
|
||||
List<Class<?>> additionalCoordinates = new ArrayList<>();
|
||||
@ -473,8 +468,9 @@ final class VarHandles {
|
||||
|
||||
List<Class<?>> newCoordinates = new ArrayList<>(targetCoordinates);
|
||||
for (int i = 0 ; i < filters.length ; i++) {
|
||||
noCheckedExceptions(filters[i]);
|
||||
MethodType filterType = filters[i].type();
|
||||
MethodHandle filter = Objects.requireNonNull(filters[i]);
|
||||
filter = adaptForCheckedExceptions(filter);
|
||||
MethodType filterType = filter.type();
|
||||
if (filterType.parameterCount() != 1) {
|
||||
throw newIllegalArgumentException("Invalid filter type " + filterType);
|
||||
} else if (newCoordinates.get(pos + i) != filterType.returnType()) {
|
||||
@ -564,10 +560,10 @@ final class VarHandles {
|
||||
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(filter);
|
||||
noCheckedExceptions(filter);
|
||||
Objects.requireNonNull(pFilter);
|
||||
MethodHandle filter = adaptForCheckedExceptions(pFilter);
|
||||
|
||||
List<Class<?>> targetCoordinates = target.coordinateTypes();
|
||||
if (pos < 0 || pos >= targetCoordinates.size()) {
|
||||
@ -604,42 +600,55 @@ final class VarHandles {
|
||||
(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) {
|
||||
byte refKind = directHandle.member.getReferenceKind();
|
||||
MethodHandleInfo info = new InfoFromMemberName(
|
||||
MethodHandles.Lookup.IMPL_LOOKUP,
|
||||
directHandle.member,
|
||||
refKind);
|
||||
final Class<?>[] exceptionTypes;
|
||||
if (MethodHandleNatives.refKindIsMethod(refKind)) {
|
||||
exceptionTypes = info.reflectAs(Method.class, MethodHandles.Lookup.IMPL_LOOKUP)
|
||||
return info.reflectAs(Method.class, MethodHandles.Lookup.IMPL_LOOKUP)
|
||||
.getExceptionTypes();
|
||||
} else if (MethodHandleNatives.refKindIsField(refKind)) {
|
||||
exceptionTypes = null;
|
||||
return new Class<?>[0];
|
||||
} else if (MethodHandleNatives.refKindIsConstructor(refKind)) {
|
||||
exceptionTypes = info.reflectAs(Constructor.class, MethodHandles.Lookup.IMPL_LOOKUP)
|
||||
return info.reflectAs(Constructor.class, MethodHandles.Lookup.IMPL_LOOKUP)
|
||||
.getExceptionTypes();
|
||||
} else {
|
||||
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) {
|
||||
noCheckedExceptions(((DelegatingMethodHandle)handle).getTarget());
|
||||
} else {
|
||||
//bound
|
||||
BoundMethodHandle boundHandle = (BoundMethodHandle)handle;
|
||||
for (int i = 0 ; i < boundHandle.fieldCount() ; i++) {
|
||||
Object arg = boundHandle.arg(i);
|
||||
if (arg instanceof MethodHandle){
|
||||
noCheckedExceptions((MethodHandle) arg);
|
||||
}
|
||||
}
|
||||
return exceptionTypes(((DelegatingMethodHandle)handle).getTarget());
|
||||
} else if (handle instanceof NativeMethodHandle) {
|
||||
return new Class<?>[0];
|
||||
}
|
||||
|
||||
assert handle instanceof BoundMethodHandle : "Unexpected handle type: " + handle;
|
||||
// unknown
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isCheckedException(Class<?> clazz) {
|
||||
|
||||
@ -820,7 +820,7 @@ public abstract class Buffer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scope.Handle acquireScope(Buffer buffer, boolean async) {
|
||||
public Runnable acquireScope(Buffer buffer, boolean async) {
|
||||
var scope = buffer.scope();
|
||||
if (scope == null) {
|
||||
return null;
|
||||
@ -828,7 +828,8 @@ public abstract class Buffer {
|
||||
if (async && scope.ownerThread() != null) {
|
||||
throw new IllegalStateException("Confined scope not supported");
|
||||
}
|
||||
return scope.acquire();
|
||||
scope.acquire0();
|
||||
return scope::release0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -181,4 +181,11 @@ public interface JavaLangInvokeAccess {
|
||||
* The given bytes is trusted.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
@ -92,7 +92,7 @@ public interface JavaNioAccess {
|
||||
* scope handle. Null is returned if the buffer has no scope, or
|
||||
* 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.
|
||||
|
||||
@ -103,19 +103,13 @@ public class ScopedMemoryAccess {
|
||||
*/
|
||||
public interface Scope {
|
||||
|
||||
interface Handle {
|
||||
Scope scope();
|
||||
}
|
||||
|
||||
void checkValidState();
|
||||
|
||||
Thread ownerThread();
|
||||
|
||||
boolean isImplicit();
|
||||
void acquire0();
|
||||
|
||||
Handle acquire();
|
||||
|
||||
void release(Handle handle);
|
||||
void release0();
|
||||
|
||||
/**
|
||||
* Error thrown when memory access fails because the memory has already been released.
|
||||
|
||||
@ -32,6 +32,7 @@ import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
import jdk.internal.vm.annotation.IntrinsicCandidate;
|
||||
|
||||
/** Common utility routines used by both java.lang and
|
||||
@ -106,6 +107,7 @@ public class Reflection {
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public static void ensureNativeAccess(Class<?> currentClass) {
|
||||
Module module = currentClass.getModule();
|
||||
if (!SharedSecrets.getJavaLangAccess().isEnableNativeAccess(module)) {
|
||||
|
||||
@ -475,15 +475,15 @@ public class IOUtil {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private static void releaseScope(Scope.Handle handle) {
|
||||
private static void releaseScope(Runnable handle) {
|
||||
if (handle == null)
|
||||
return;
|
||||
try {
|
||||
handle.scope().release(handle);
|
||||
handle.run();
|
||||
} catch (Exception 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) ; }
|
||||
@Override public void run() { releaseScope(handle); }
|
||||
static Runnable of(Scope.Handle handle) { return new Releaser(handle); }
|
||||
static Runnable ofNullable(Scope.Handle handle) {
|
||||
static Runnable of(Runnable handle) { return new Releaser(handle); }
|
||||
static Runnable ofNullable(Runnable handle) {
|
||||
if (handle == null)
|
||||
return () -> { };
|
||||
return new Releaser(handle);
|
||||
|
||||
@ -25,26 +25,23 @@
|
||||
*/
|
||||
package jdk.incubator.foreign;
|
||||
|
||||
import jdk.internal.foreign.Utils;
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.constant.Constable;
|
||||
import java.lang.constant.ConstantDesc;
|
||||
import java.lang.constant.ConstantDescs;
|
||||
import java.lang.constant.DirectMethodHandleDesc;
|
||||
import java.lang.constant.DynamicConstantDesc;
|
||||
import java.lang.constant.MethodHandleDesc;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
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_INVOKE;
|
||||
import static java.lang.constant.ConstantDescs.CD_Class;
|
||||
import static java.lang.constant.ConstantDescs.CD_String;
|
||||
import static java.lang.constant.ConstantDescs.CD_long;
|
||||
|
||||
@ -52,50 +49,33 @@ abstract non-sealed class AbstractLayout implements MemoryLayout {
|
||||
|
||||
private final OptionalLong size;
|
||||
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.alignment = alignment;
|
||||
this.attributes = Collections.unmodifiableMap(attributes);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractLayout withName(String name) {
|
||||
Objects.requireNonNull(name);
|
||||
return withAttribute(LAYOUT_NAME, name);
|
||||
return dup(alignment, Optional.of(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Optional<String> name() {
|
||||
return attribute(LAYOUT_NAME).map(String.class::cast);
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
abstract AbstractLayout dup(long alignment, Optional<String> name);
|
||||
|
||||
@Override
|
||||
public AbstractLayout withBitAlignment(long alignmentBits) {
|
||||
checkAlignment(alignmentBits);
|
||||
return dup(alignmentBits, attributes);
|
||||
return dup(alignmentBits, name);
|
||||
}
|
||||
|
||||
void checkAlignment(long alignmentBitCount) {
|
||||
@ -120,6 +100,15 @@ abstract non-sealed class AbstractLayout implements MemoryLayout {
|
||||
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
|
||||
public boolean hasSize() {
|
||||
return size.isPresent();
|
||||
@ -145,11 +134,6 @@ abstract non-sealed class AbstractLayout implements MemoryLayout {
|
||||
if (!hasNaturalAlignment()) {
|
||||
s = alignment + "%" + s;
|
||||
}
|
||||
if (!attributes.isEmpty()) {
|
||||
s += attributes.entrySet().stream()
|
||||
.map(e -> e.getKey() + "=" + e.getValue())
|
||||
.collect(Collectors.joining(",", "[", "]"));
|
||||
}
|
||||
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, bitAlignment());
|
||||
}
|
||||
for (var e : attributes.entrySet()) {
|
||||
desc = DynamicConstantDesc.ofNamed(BSM_INVOKE, "withAttribute", desc.constantType(), MH_WITH_ATTRIBUTE,
|
||||
desc, e.getKey(), e.getValue().describeConstable().orElseThrow());
|
||||
if (name().isPresent()) {
|
||||
desc = DynamicConstantDesc.ofNamed(BSM_INVOKE, "withName", desc.constantType(), MH_WITH_NAME,
|
||||
desc, name().get().describeConstable().orElseThrow());
|
||||
}
|
||||
|
||||
return desc;
|
||||
@ -177,7 +161,7 @@ abstract non-sealed class AbstractLayout implements MemoryLayout {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return attributes.hashCode() << Long.hashCode(alignment);
|
||||
return name.hashCode() << Long.hashCode(alignment);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -190,7 +174,7 @@ abstract non-sealed class AbstractLayout implements MemoryLayout {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Objects.equals(attributes, ((AbstractLayout) other).attributes) &&
|
||||
return Objects.equals(name, ((AbstractLayout) other).name) &&
|
||||
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_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 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",
|
||||
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",
|
||||
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",
|
||||
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",
|
||||
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",
|
||||
MethodTypeDesc.of(CD_MEMORY_LAYOUT, CD_long));
|
||||
|
||||
static final MethodHandleDesc MH_WITH_ATTRIBUTE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_VIRTUAL, CD_MEMORY_LAYOUT, "withAttribute",
|
||||
MethodTypeDesc.of(CD_MEMORY_LAYOUT, CD_String, CD_Constable));
|
||||
static final MethodHandleDesc MH_WITH_NAME = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_VIRTUAL, CD_MEMORY_LAYOUT, "withName",
|
||||
MethodTypeDesc.of(CD_MEMORY_LAYOUT, CD_String));
|
||||
}
|
||||
|
||||
@ -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
|
||||
* a memory address instance (see {@link #address()}). Examples of addressable types are {@link MemorySegment},
|
||||
* {@link MemoryAddress} and {@link CLinker.VaList}.
|
||||
* a {@linkplain #address() memory address}. Examples of addressable types are {@link MemorySegment},
|
||||
* {@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
|
||||
* 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.
|
||||
* @return the {@link MemoryAddress} instance associated with this object.
|
||||
* Returns the memory address associated with this addressable.
|
||||
* @return The memory address associated with this addressable.
|
||||
*/
|
||||
MemoryAddress address();
|
||||
}
|
||||
|
||||
@ -25,93 +25,123 @@
|
||||
*/
|
||||
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.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.foreign.abi.aarch64.linux.LinuxAArch64Linker;
|
||||
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker;
|
||||
import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
|
||||
import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
|
||||
import java.lang.constant.Constable;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static jdk.internal.foreign.PlatformLayouts.*;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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>
|
||||
* Linking a foreign function is a process which requires two components: a method type, and
|
||||
* 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.
|
||||
* On unsupported platforms this class will fail to initialize with an {@link ExceptionInInitializerError}.
|
||||
* <p>
|
||||
* Clients of this API can build function descriptors using the predefined memory layout constants
|
||||
* (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}
|
||||
* 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>
|
||||
*
|
||||
* <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
|
||||
* 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.
|
||||
@ -127,96 +157,75 @@ public sealed interface CLinker permits AbstractCLinker {
|
||||
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
|
||||
*/
|
||||
@CallerSensitive
|
||||
static CLinker getInstance() {
|
||||
static CLinker systemCLinker() {
|
||||
Reflection.ensureNativeAccess(Reflection.getCallerClass());
|
||||
return SharedUtils.getSystemLinker();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a system lookup which is suitable to find symbols in the standard C libraries. The set of symbols
|
||||
* available for lookup is unspecified, as it depends on the platform and on the operating system.
|
||||
* <p>
|
||||
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
|
||||
* Restricted methods are unsafe, and, if used incorrectly, their use might crash
|
||||
* the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
|
||||
* restricted methods, and use safe and supported functionalities, where possible.
|
||||
* @return 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.
|
||||
* Lookup a symbol 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.
|
||||
* @return a symbol in the standard libraries associated with this linker.
|
||||
*/
|
||||
@CallerSensitive
|
||||
static SymbolLookup systemLookup() {
|
||||
Reflection.ensureNativeAccess(Reflection.getCallerClass());
|
||||
return SystemLookup.getInstance();
|
||||
@Override
|
||||
default Optional<NativeSymbol> lookup(String name) {
|
||||
return SystemLookup.getInstance().lookup(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* If the provided method type's return type is {@code MemorySegment}, then the resulting method handle features
|
||||
* an additional prefix parameter, of type {@link SegmentAllocator}, which will be used by the linker runtime
|
||||
* to allocate structs returned by-value.
|
||||
* <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 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}
|
||||
* @return the downcall method handle. The method handle type is <a href="CLinker.html#downcall-method-handles"><em>inferred</em></a>
|
||||
* @throws IllegalArgumentException if the provided descriptor contains either a sequence or a padding layout,
|
||||
* or if the symbol is {@link MemoryAddress#NULL}
|
||||
*
|
||||
* @see SymbolLookup
|
||||
*/
|
||||
MethodHandle downcallHandle(Addressable symbol, MethodType type, FunctionDescriptor function);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
default MethodHandle downcallHandle(NativeSymbol symbol, FunctionDescriptor function) {
|
||||
SharedUtils.checkSymbol(symbol);
|
||||
return downcallHandle(function).bindTo(symbol);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* The resulting method handle features a prefix parameter (as the first parameter) corresponding to the address, of
|
||||
* type {@link Addressable}.
|
||||
* 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 foreign function
|
||||
* entry point, of type {@link NativeSymbol}.
|
||||
* <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}),
|
||||
* which will be used by the linker runtime to allocate structs returned by-value.
|
||||
* <p>
|
||||
* The returned method handle will throw an {@link IllegalArgumentException} if the target address passed to it is
|
||||
* {@link MemoryAddress#NULL}, or a {@link NullPointerException} if the target address is {@code null}.
|
||||
* The returned method handle will throw an {@link IllegalArgumentException} if the native symbol passed to it is
|
||||
* 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.
|
||||
* @return the downcall method handle.
|
||||
* @throws IllegalArgumentException in the case of a method type and function descriptor mismatch.
|
||||
* @return the downcall method handle. The method handle type is <a href="CLinker.html#downcall-method-handles"><em>inferred</em></a>
|
||||
* from the provided function descriptor.
|
||||
* @throws IllegalArgumentException if the provided descriptor contains either a sequence or a padding layout.
|
||||
*
|
||||
* @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);
|
||||
* calling such a function pointer from native code will result in the execution of the provided method handle.
|
||||
*
|
||||
* <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.
|
||||
* <p>
|
||||
* 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 function the function descriptor.
|
||||
* @param scope the upcall stub scope.
|
||||
* @return the native stub segment.
|
||||
* @throws IllegalArgumentException if the target's method type and the function descriptor mismatch.
|
||||
* @return the native stub symbol.
|
||||
* @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
|
||||
* 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);
|
||||
/**
|
||||
* 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);
|
||||
static MethodType downcallType(FunctionDescriptor functionDescriptor) {
|
||||
return SharedUtils.inferMethodType(functionDescriptor, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a Java string into a UTF-8 encoded, null-terminated C string,
|
||||
* storing the result into a native memory segment allocated using the provided allocator.
|
||||
* <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 allocator the allocator to be used for the native segment allocation.
|
||||
* @return a new native memory segment containing the converted C string.
|
||||
* Obtains the method handle {@linkplain MethodType type} associated with an upcall stub with given function descriptor.
|
||||
* @param functionDescriptor a function descriptor.
|
||||
* @return the method handle {@linkplain MethodType type} associated with an upcall stub with 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).
|
||||
*/
|
||||
static MemorySegment toCString(String str, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(str);
|
||||
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";
|
||||
static MethodType upcallType(FunctionDescriptor functionDescriptor) {
|
||||
return SharedUtils.inferMethodType(functionDescriptor, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.
|
||||
*
|
||||
* 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.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
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}
|
||||
* elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown. </p>
|
||||
*/
|
||||
public final class FunctionDescriptor implements Constable {
|
||||
|
||||
/**
|
||||
* 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";
|
||||
public sealed class FunctionDescriptor implements Constable permits FunctionDescriptor.VariadicFunction {
|
||||
|
||||
private final MemoryLayout resLayout;
|
||||
private final MemoryLayout[] argLayouts;
|
||||
private final Map<String, Constable> attributes;
|
||||
private final List<MemoryLayout> argLayouts;
|
||||
|
||||
private FunctionDescriptor(MemoryLayout resLayout, Map<String, Constable> attributes, MemoryLayout... argLayouts) {
|
||||
private FunctionDescriptor(MemoryLayout resLayout, List<MemoryLayout> argLayouts) {
|
||||
this.resLayout = resLayout;
|
||||
this.attributes = attributes;
|
||||
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.
|
||||
* @return the return layout.
|
||||
@ -113,7 +66,7 @@ public final class FunctionDescriptor implements Constable {
|
||||
* @return the argument layouts.
|
||||
*/
|
||||
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(argLayouts);
|
||||
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) {
|
||||
Objects.requireNonNull(argLayouts);
|
||||
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) {
|
||||
Objects.requireNonNull(addedLayouts);
|
||||
Arrays.stream(addedLayouts).forEach(Objects::requireNonNull);
|
||||
MemoryLayout[] newLayouts = Arrays.copyOf(argLayouts, argLayouts.length + addedLayouts.length);
|
||||
System.arraycopy(addedLayouts, 0, newLayouts, argLayouts.length, addedLayouts.length);
|
||||
return new FunctionDescriptor(resLayout, attributes, newLayouts);
|
||||
List<MemoryLayout> newLayouts = Stream.concat(argLayouts.stream(), Stream.of(addedLayouts)).toList();
|
||||
return new FunctionDescriptor(resLayout, newLayouts);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -161,7 +137,7 @@ public final class FunctionDescriptor implements Constable {
|
||||
*/
|
||||
public FunctionDescriptor withReturnLayout(MemoryLayout 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.
|
||||
*/
|
||||
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
|
||||
* 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>
|
||||
* <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)})
|
||||
@ -201,11 +177,10 @@ public final class FunctionDescriptor implements Constable {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof FunctionDescriptor)) {
|
||||
if (!(other instanceof FunctionDescriptor f)) {
|
||||
return false;
|
||||
}
|
||||
FunctionDescriptor f = (FunctionDescriptor) other;
|
||||
return Objects.equals(resLayout, f.resLayout) && Arrays.equals(argLayouts, f.argLayouts);
|
||||
return Objects.equals(resLayout, f.resLayout) && Objects.equals(argLayouts, f.argLayouts);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -214,10 +189,18 @@ public final class FunctionDescriptor implements Constable {
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = Arrays.hashCode(argLayouts);
|
||||
int hashCode = Objects.hashCode(argLayouts);
|
||||
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
|
||||
public Optional<DynamicConstantDesc<FunctionDescriptor>> describeConstable() {
|
||||
List<ConstantDesc> constants = new ArrayList<>();
|
||||
@ -231,4 +214,40 @@ public final class FunctionDescriptor implements Constable {
|
||||
return Optional.of(DynamicConstantDesc.ofNamed(
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,14 +25,12 @@
|
||||
*/
|
||||
package jdk.incubator.foreign;
|
||||
|
||||
import java.lang.constant.Constable;
|
||||
import java.lang.constant.ConstantDesc;
|
||||
import java.lang.constant.ConstantDescs;
|
||||
import java.lang.constant.DynamicConstantDesc;
|
||||
import java.lang.constant.MethodHandleDesc;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
@ -40,7 +38,7 @@ import java.util.function.LongBinaryOperator;
|
||||
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>
|
||||
* (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...)}).
|
||||
@ -105,11 +103,11 @@ public final class GroupLayout extends AbstractLayout implements MemoryLayout {
|
||||
private final 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) {
|
||||
super(kind.sizeof(elements), alignment, attributes);
|
||||
GroupLayout(Kind kind, List<MemoryLayout> elements, long alignment, Optional<String> name) {
|
||||
super(kind.sizeof(elements), alignment, name);
|
||||
this.kind = kind;
|
||||
this.elements = elements;
|
||||
}
|
||||
@ -160,10 +158,9 @@ public final class GroupLayout extends AbstractLayout implements MemoryLayout {
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
if (!(other instanceof GroupLayout)) {
|
||||
if (!(other instanceof GroupLayout g)) {
|
||||
return false;
|
||||
}
|
||||
GroupLayout g = (GroupLayout)other;
|
||||
return kind.equals(g.kind) && elements.equals(g.elements);
|
||||
}
|
||||
|
||||
@ -173,8 +170,8 @@ public final class GroupLayout extends AbstractLayout implements MemoryLayout {
|
||||
}
|
||||
|
||||
@Override
|
||||
GroupLayout dup(long alignment, Map<String, Constable> attributes) {
|
||||
return new GroupLayout(kind, elements, alignment, attributes);
|
||||
GroupLayout dup(long alignment, Optional<String> name) {
|
||||
return new GroupLayout(kind, elements, alignment, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -212,12 +209,4 @@ public final class GroupLayout extends AbstractLayout implements MemoryLayout {
|
||||
public GroupLayout withBitAlignment(long alignmentBits) {
|
||||
return (GroupLayout)super.withBitAlignment(alignmentBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GroupLayout withAttribute(String name, Constable value) {
|
||||
return (GroupLayout)super.withAttribute(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -27,33 +27,45 @@
|
||||
package jdk.incubator.foreign;
|
||||
|
||||
import jdk.internal.foreign.MemoryAddressImpl;
|
||||
import jdk.internal.ref.CleanerFactory;
|
||||
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
|
||||
* {@link MemorySegment#address()} method, and can refer to either off-heap or on-heap memory. Off-heap memory
|
||||
* addresses are referred to as <em>native</em> memory addresses (see {@link #isNative()}). Native memory addresses
|
||||
* allow clients to obtain a raw memory address (expressed as a long value) which can then be used e.g. when interacting
|
||||
* with native code.
|
||||
* <p>
|
||||
* Given an address, it is possible to compute its offset relative to a given segment, which can be useful
|
||||
* when performing memory dereference operations using a memory access var handle (see {@link MemoryHandles}).
|
||||
* <p>
|
||||
* 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
|
||||
* obtained from {@linkplain #ofLong(long) numeric values}, or from native code, are associated with the
|
||||
* {@linkplain ResourceScope#globalScope() global resource scope}. Memory addresses obtained from segments
|
||||
* are associated with the same scope as the segment from which they have been obtained.
|
||||
* A memory address models a reference into a memory location. Memory addresses are typically obtained in three ways:
|
||||
* <ul>
|
||||
* <li>By calling {@link Addressable#address()} on an instance of type {@link Addressable} (e.g. a memory segment);</li>
|
||||
* <li>By invoking a {@linkplain CLinker#downcallHandle(FunctionDescriptor) downcall method handle} which returns a pointer;</li>
|
||||
* <li>By reading an address from memory, e.g. via {@link MemorySegment#get(ValueLayout.OfAddress, long)}.</li>
|
||||
* </ul>
|
||||
* A memory address is backed by a raw machine pointer, expressed as a {@linkplain #toRawLongValue() long value}.
|
||||
*
|
||||
* <h2>Dereference</h2>
|
||||
*
|
||||
* A memory address can be read or written using various methods provided in this class (e.g. {@link #get(ValueLayout.OfInt, long)}).
|
||||
* Each dereference method takes a {@linkplain jdk.incubator.foreign.ValueLayout value layout}, which specifies the size,
|
||||
* alignment constraints, byte order as well as the Java type associated with the dereference operation, and an offset.
|
||||
* For instance, to read an int from a segment, using {@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>
|
||||
* 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
|
||||
* 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.
|
||||
* <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}
|
||||
* 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 {
|
||||
|
||||
@Override
|
||||
default MemoryAddress address() {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Returns the raw long value associated with this memory address.
|
||||
* @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.
|
||||
@ -76,127 +89,49 @@ public sealed interface MemoryAddress extends Addressable permits MemoryAddressI
|
||||
MemoryAddress addOffset(long offset);
|
||||
|
||||
/**
|
||||
* Returns the resource scope associated with this memory address.
|
||||
* @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}.
|
||||
* Reads a UTF-8 encoded, null-terminated string from this address and offset.
|
||||
* <p>
|
||||
* If the segment's base address and this address are both heap addresses, but with different base objects, the result is undefined
|
||||
* and an exception is thrown. Similarly, if the segment's base address is an heap address (resp. off-heap) and
|
||||
* this address is an off-heap (resp. heap) address, the result is undefined and an exception is thrown.
|
||||
* Otherwise, the result is a byte offset {@code SO}. If this address falls within the
|
||||
* 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>
|
||||
* 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 bytesSize the desired size.
|
||||
* @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.
|
||||
* @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 Java string constructed from the bytes read from the given starting address ({@code toRowLongValue() + offset})
|
||||
* up to (but not including) the first {@code '\0'} terminator character (assuming one is found).
|
||||
* @throws IllegalArgumentException if the size of the native string is greater than the largest string supported by the platform.
|
||||
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
|
||||
* {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or
|
||||
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
|
||||
*/
|
||||
@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
|
||||
* 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.
|
||||
* Writes the given string to this address at given offset, converting it to a null-terminated byte sequence using UTF-8 encoding.
|
||||
* <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>
|
||||
* Calling {@link ResourceScope#close()} on the scope associated with the returned segment will result in calling
|
||||
* the provided cleanup action (if any).
|
||||
* <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.
|
||||
* 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 offset offset in bytes (relative to this address). The final address of this read operation can be expressed as {@code toRowLongValue() + offset}.
|
||||
* @param str the Java string to be written at this address.
|
||||
* @throws IllegalCallerException if access to this method occurs from a module {@code M} and the command line option
|
||||
* {@code --enable-native-access} is either absent, or does not mention the module name {@code M}, or
|
||||
* {@code ALL-UNNAMED} in case {@code M} is an unnamed module.
|
||||
*/
|
||||
@CallerSensitive
|
||||
MemorySegment asSegment(long bytesSize, Runnable cleanupAction, ResourceScope scope);
|
||||
|
||||
/**
|
||||
* 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();
|
||||
void setUtf8String(long offset, String str);
|
||||
|
||||
/**
|
||||
* Compares the specified object with this address for equality. Returns {@code true} if and only if the specified
|
||||
* object is also an address, and it refers to the same memory location as this address.
|
||||
*
|
||||
* @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.
|
||||
* @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();
|
||||
|
||||
/**
|
||||
* The native memory address instance modelling the {@code NULL} address, associated
|
||||
* with the {@linkplain ResourceScope#globalScope() global} resource scope.
|
||||
* The native memory address instance modelling the {@code NULL} address.
|
||||
*/
|
||||
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
|
||||
* with the {@linkplain ResourceScope#globalScope() global} resource scope.
|
||||
* Obtain a native memory address instance from given long address.
|
||||
* @param value the long address.
|
||||
* @return the new memory address instance.
|
||||
*/
|
||||
static MemoryAddress ofLong(long value) {
|
||||
return value == 0 ?
|
||||
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);
|
||||
}
|
||||
|
||||
@ -27,24 +27,21 @@ package jdk.incubator.foreign;
|
||||
|
||||
import jdk.internal.access.JavaLangInvokeAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.foreign.Utils;
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* (see {@link MemoryHandles#varHandle(Class, ByteOrder)},
|
||||
* {@link MemoryHandles#varHandle(Class, long, ByteOrder)}). This determines the variable type
|
||||
* (all primitive types but {@code void} and {@code boolean} are supported), as well as the alignment constraint and the
|
||||
* byte order associated with a memory access var handle. The resulting memory access var handle can then be combined in various ways
|
||||
* Memory access var handles can be obtained using {@link MemoryHandles#varHandle(ValueLayout)}. The provided value layout
|
||||
* determines the type, as well as the alignment constraint and the byte order associated with the memory access var handle.
|
||||
* <p>
|
||||
* 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
|
||||
* (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.
|
||||
@ -53,12 +50,12 @@ import java.util.Objects;
|
||||
* <blockquote><pre>{@code
|
||||
GroupLayout seq = MemoryLayout.structLayout(
|
||||
MemoryLayout.paddingLayout(32),
|
||||
MemoryLayout.valueLayout(32, ByteOrder.BIG_ENDIAN).withName("value")
|
||||
ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN).withName("value")
|
||||
);
|
||||
* }</pre></blockquote>
|
||||
* To access the member layout named {@code value}, we can construct a memory access var handle as follows:
|
||||
* <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
|
||||
* }</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
|
||||
* {@code double} on 32-bit platforms.
|
||||
* <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
|
||||
* 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
|
||||
* 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
|
||||
* numeric types for certain currently unsupported access modes.)
|
||||
* </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
|
||||
* (see {@link Float#floatToRawIntBits} and
|
||||
* {@link Double#doubleToRawLongBits}, respectively).
|
||||
* (see {@link Float#floatToRawIntBits},
|
||||
* {@link Double#doubleToRawLongBits} and {@link MemoryAddress#toRawLongValue()}, respectively).
|
||||
* <p>
|
||||
* Alternatively, a memory access operation is <em>partially aligned</em> if it occurs at a memory address {@code A}
|
||||
* which is only compatible with the alignment constraint {@code B}; in such cases, access for anything other than the
|
||||
@ -109,8 +106,6 @@ public final class MemoryHandles {
|
||||
//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 BYTE_TO_UNSIGNED_INT;
|
||||
private static final MethodHandle INT_TO_SHORT;
|
||||
@ -124,10 +119,6 @@ public final class MemoryHandles {
|
||||
|
||||
static {
|
||||
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),
|
||||
MethodType.methodType(byte.class, int.class));
|
||||
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.
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
* 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 {@linkplain ValueLayout#byteAlignment() byte alignment} and the {@linkplain ValueLayout#order() byte order}
|
||||
* associated to the returned var handle.
|
||||
*
|
||||
* 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
|
||||
@ -192,56 +159,13 @@ public final class MemoryHandles {
|
||||
* @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 alignmentBytes the alignment constraint (in bytes). Must be a power of two.
|
||||
* @param byteOrder the required byte order.
|
||||
* @param layout the value layout for which a memory access handle is to be obtained.
|
||||
* @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.
|
||||
*/
|
||||
public static VarHandle varHandle(Class<?> carrier, long alignmentBytes, ByteOrder byteOrder) {
|
||||
Objects.requireNonNull(carrier);
|
||||
Objects.requireNonNull(byteOrder);
|
||||
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);
|
||||
}
|
||||
public static VarHandle varHandle(ValueLayout layout) {
|
||||
Objects.requireNonNull(layout);
|
||||
return layout.accessHandle();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -255,7 +179,7 @@ public final class MemoryHandles {
|
||||
* the case if modeled as a Java {@code short}. This is illustrated in the following example:
|
||||
* <blockquote><pre>{@code
|
||||
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);
|
||||
SHORT_VH.set(segment, (short)-1);
|
||||
INT_VH.get(segment); // returns 65535
|
||||
@ -276,7 +200,7 @@ public final class MemoryHandles {
|
||||
* <p>
|
||||
* The returned var handle will feature the variable type {@code adaptedType},
|
||||
* 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.
|
||||
*
|
||||
* @param target the memory access var handle to be adapted
|
||||
@ -284,7 +208,7 @@ public final class MemoryHandles {
|
||||
* @return the adapted var handle.
|
||||
* @throws IllegalArgumentException if the carrier type of {@code target}
|
||||
* 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}
|
||||
* 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
|
||||
* {@link java.lang.invoke.VarHandle.AccessMode#COMPARE_AND_EXCHANGE} might apply both filters at the same time.
|
||||
* <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,
|
||||
* 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).
|
||||
* <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.
|
||||
*
|
||||
* @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.
|
||||
* @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,
|
||||
* 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) {
|
||||
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
|
||||
* by the adaptation) to the target var handle.
|
||||
* <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.
|
||||
* <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
|
||||
* 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
|
||||
* 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 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) {
|
||||
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
|
||||
* are joined with bound coordinate values, and then passed to the target var handle.
|
||||
* <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.
|
||||
* <p>
|
||||
* 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>
|
||||
* The given array controls the reordering.
|
||||
* 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).
|
||||
* Then the length of the reordering array must be {@code #O},
|
||||
* 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).
|
||||
* 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)
|
||||
@ -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 is supplied by the return value of the filter.
|
||||
* <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
|
||||
* 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,
|
||||
* 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>,
|
||||
* 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) {
|
||||
return JLI.collectCoordinates(target, pos, filter);
|
||||
@ -505,24 +438,6 @@ public final class MemoryHandles {
|
||||
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) {
|
||||
if (!(carrier == byte.class || carrier == short.class || carrier == int.class)) {
|
||||
throw new IllegalArgumentException("illegal carrier:" + carrier.getSimpleName());
|
||||
@ -541,12 +456,4 @@ public final class MemoryHandles {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,11 +46,11 @@ import java.util.stream.Collectors;
|
||||
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
|
||||
* {@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)}).
|
||||
* 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>
|
||||
* 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
|
||||
@ -70,9 +70,9 @@ import java.util.stream.Stream;
|
||||
* <blockquote><pre>{@code
|
||||
SequenceLayout taggedValues = MemoryLayout.sequenceLayout(5,
|
||||
MemoryLayout.structLayout(
|
||||
MemoryLayout.valueLayout(8, ByteOrder.nativeOrder()).withName("kind"),
|
||||
ValueLayout.JAVA_BYTE.withName("kind"),
|
||||
MemoryLayout.paddingLayout(24),
|
||||
MemoryLayout.valueLayout(32, ByteOrder.nativeOrder()).withName("value")
|
||||
ValueLayout.JAVA_INT.withName("value")
|
||||
)
|
||||
).withName("TaggedValues");
|
||||
* }</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
|
||||
* 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.
|
||||
* <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}
|
||||
* 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.
|
||||
* Layout paths are typically expressed as a sequence of one or more {@link PathElement} instances.
|
||||
* <p>
|
||||
* Layout paths are for example useful in order to obtain offsets of arbitrarily nested layouts inside another layout
|
||||
* (see {@link MemoryLayout#bitOffset(PathElement...)}), to quickly obtain a memory access handle corresponding to the selected
|
||||
* layout (see {@link MemoryLayout#varHandle(Class, PathElement...)}), to select an arbitrarily nested layout inside
|
||||
* another layout (see {@link MemoryLayout#select(PathElement...)}, or to transform a nested layout element inside
|
||||
* another layout (see {@link MemoryLayout#map(UnaryOperator, PathElement...)}).
|
||||
* Layout paths are for example useful in order to obtain {@linkplain MemoryLayout#bitOffset(PathElement...) offsets} of
|
||||
* arbitrarily nested layouts inside another layout, to quickly obtain a {@linkplain #varHandle(PathElement...) memory access handle}
|
||||
* corresponding to the selected layout, to {@linkplain #select(PathElement...) select} an arbitrarily nested layout inside
|
||||
* another layout, or to {@link #map(UnaryOperator, PathElement...) transform} a nested layout element inside
|
||||
* another layout.
|
||||
* <p>
|
||||
* 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,
|
||||
@ -152,7 +150,7 @@ MemoryLayout taggedValuesWithHole = taggedValues.map(l -> MemoryLayout.paddingLa
|
||||
* <blockquote><pre>{@code
|
||||
MemoryLayout taggedValuesWithHole = MemoryLayout.sequenceLayout(5,
|
||||
MemoryLayout.structLayout(
|
||||
MemoryLayout.valueLayout(8, ByteOrder.nativeOrder()).withName("kind"),
|
||||
ValueLayout.JAVA_BYTE.withName("kind"),
|
||||
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:
|
||||
*
|
||||
* <blockquote><pre>{@code
|
||||
VarHandle valueHandle = taggedValues.varHandle(int.class,
|
||||
PathElement.sequenceElement(),
|
||||
VarHandle valueHandle = taggedValues.varHandle(PathElement.sequenceElement(),
|
||||
PathElement.groupElement("value"));
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
@ -189,9 +186,7 @@ long offset2 = (long) offsetHandle.invokeExact(2L); // 16
|
||||
*
|
||||
* <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>
|
||||
* 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
|
||||
* Layouts can be optionally associated with a <em>name</em>. A layout name can be referred to when
|
||||
* constructing <a href="MemoryLayout.html#layout-paths"><em>layout paths</em></a>.
|
||||
*
|
||||
* @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}),
|
||||
* or if {@code bitSize()} is not a multiple of 8.
|
||||
*/
|
||||
default long byteSize() {
|
||||
return Utils.bitsToBytesOrThrow(bitSize(),
|
||||
() -> new UnsupportedOperationException("Cannot compute byte size; bit size is not a multiple of 8"));
|
||||
}
|
||||
long byteSize();
|
||||
|
||||
/**
|
||||
* 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).
|
||||
* @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>.
|
||||
* <p>
|
||||
* This is equivalent to the following code:
|
||||
* <blockquote><pre>{@code
|
||||
withAttribute(LAYOUT_NAME, name);
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* @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.
|
||||
@ -313,36 +295,10 @@ public sealed interface MemoryLayout extends Constable permits AbstractLayout, S
|
||||
*
|
||||
* @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.
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* <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.
|
||||
* 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.
|
||||
@ -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.
|
||||
*
|
||||
* <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.
|
||||
* 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.
|
||||
@ -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
|
||||
* 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.
|
||||
* @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,
|
||||
* 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},
|
||||
* {@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.
|
||||
* @throws IllegalArgumentException if the layout path in {@code elements} does not select a value layout (see {@link ValueLayout}).
|
||||
*/
|
||||
default VarHandle varHandle(Class<?> carrier, PathElement... elements) {
|
||||
Objects.requireNonNull(carrier);
|
||||
return computePathOp(LayoutPath.rootPath(this, MemoryLayout::bitSize), path -> path.dereferenceHandle(carrier),
|
||||
default VarHandle varHandle(PathElement... elements) {
|
||||
return computePathOp(LayoutPath.rootPath(this, MemoryLayout::bitSize), LayoutPath::dereferenceHandle,
|
||||
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}
|
||||
* 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.
|
||||
* 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.
|
||||
@ -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
|
||||
* 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
|
||||
segment.asSlice(offset, layout.byteSize());
|
||||
* }</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.
|
||||
*/
|
||||
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
|
||||
* <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>.
|
||||
* <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}
|
||||
* 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.
|
||||
*
|
||||
* @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.
|
||||
* @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.
|
||||
*
|
||||
* @param size the value layout size.
|
||||
* Creates a value layout of given Java carrier and byte order. The type of resulting value layout is determined
|
||||
* by the carrier provided:
|
||||
* <ul>
|
||||
* <li>{@link ValueLayout.OfBoolean}, for {@code boolean.class}</li>
|
||||
* <li>{@link ValueLayout.OfByte}, for {@code byte.class}</li>
|
||||
* <li>{@link ValueLayout.OfShort}, for {@code short.class}</li>
|
||||
* <li>{@link ValueLayout.OfChar}, for {@code char.class}</li>
|
||||
* <li>{@link ValueLayout.OfInt}, for {@code int.class}</li>
|
||||
* <li>{@link ValueLayout.OfFloat}, for {@code float.class}</li>
|
||||
* <li>{@link ValueLayout.OfLong}, for {@code long.class}</li>
|
||||
* <li>{@link ValueLayout.OfDouble}, for {@code double.class}</li>
|
||||
* <li>{@link ValueLayout.OfAddress}, for {@code MemoryAddress.class}</li>
|
||||
* </ul>
|
||||
* @param carrier the value layout carrier.
|
||||
* @param order the value layout's byte order.
|
||||
* @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);
|
||||
AbstractLayout.checkSize(size);
|
||||
return new ValueLayout(order, size);
|
||||
if (carrier == boolean.class) {
|
||||
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)
|
||||
.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";
|
||||
}
|
||||
|
||||
@ -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());
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
||||
}
|
||||
@ -25,10 +25,8 @@
|
||||
*/
|
||||
package jdk.incubator.foreign;
|
||||
|
||||
import java.lang.constant.Constable;
|
||||
import java.lang.constant.ConstantDescs;
|
||||
import java.lang.constant.DynamicConstantDesc;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
@ -53,11 +51,11 @@ import java.util.OptionalLong;
|
||||
/* package-private */ final class PaddingLayout extends AbstractLayout implements MemoryLayout {
|
||||
|
||||
PaddingLayout(long size) {
|
||||
this(size, 1, Map.of());
|
||||
this(size, 1, Optional.empty());
|
||||
}
|
||||
|
||||
PaddingLayout(long size, long alignment, Map<String, Constable> attributes) {
|
||||
super(OptionalLong.of(size), alignment, attributes);
|
||||
PaddingLayout(long size, long alignment, Optional<String> name) {
|
||||
super(OptionalLong.of(size), alignment, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -73,10 +71,9 @@ import java.util.OptionalLong;
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
if (!(other instanceof PaddingLayout)) {
|
||||
if (!(other instanceof PaddingLayout p)) {
|
||||
return false;
|
||||
}
|
||||
PaddingLayout p = (PaddingLayout)other;
|
||||
return bitSize() == p.bitSize();
|
||||
}
|
||||
|
||||
@ -86,8 +83,8 @@ import java.util.OptionalLong;
|
||||
}
|
||||
|
||||
@Override
|
||||
PaddingLayout dup(long alignment, Map<String, Constable> attributes) {
|
||||
return new PaddingLayout(bitSize(), alignment, attributes);
|
||||
PaddingLayout dup(long alignment, Optional<String> name) {
|
||||
return new PaddingLayout(bitSize(), alignment, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -119,12 +116,4 @@ import java.util.OptionalLong;
|
||||
public PaddingLayout withBitAlignment(long alignmentBits) {
|
||||
return (PaddingLayout)super.withBitAlignment(alignmentBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public PaddingLayout withAttribute(String name, Constable value) {
|
||||
return (PaddingLayout)super.withAttribute(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,9 +26,11 @@
|
||||
package jdk.incubator.foreign;
|
||||
|
||||
import jdk.internal.foreign.ResourceScopeImpl;
|
||||
import jdk.internal.ref.CleanerFactory;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.ref.Cleaner;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.Path;
|
||||
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
|
||||
* with a resource scope can only be accessed while the resource scope is <em>alive</em> (see {@link #isAlive()}),
|
||||
* and by the thread associated with the resource scope (if any).
|
||||
* with a resource scope can only be accessed while the resource scope is {@linkplain #isAlive() alive},
|
||||
* 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>;
|
||||
* We call these resource scopes <em>explicit scopes</em>. Explicit resource scopes can be closed explicitly (see {@link ResourceScope#close()}).
|
||||
* When a resource scope is closed, it is no longer <em>alive</em> (see {@link #isAlive()}, and subsequent operations on
|
||||
* resources associated with that scope (e.g. attempting to access a {@link MemorySegment} instance) will fail with {@link IllegalStateException}.
|
||||
* Resource scopes support <em>deterministic deallocation</em>; that is, they can be {@linkplain ResourceScope#close() closed}
|
||||
* explicitly. When a resource scope is closed, it is no longer {@link #isAlive() alive}, and subsequent
|
||||
* operations on resources associated with that scope (e.g. attempting to access a {@link MemorySegment} instance)
|
||||
* will fail with {@link IllegalStateException}.
|
||||
* <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:
|
||||
* <ul>
|
||||
* <li>closing the scope associated with a native memory segment results in <em>freeing</em> the native memory associated with it
|
||||
* (see {@link MemorySegment#allocateNative(long, ResourceScope)}, or {@link SegmentAllocator#arenaAllocator(ResourceScope)})</li>
|
||||
* <li>closing the scope associated with a mapped memory segment results in the backing memory-mapped file to be unmapped
|
||||
* (see {@link MemorySegment#mapFile(Path, long, long, FileChannel.MapMode, ResourceScope)})</li>
|
||||
* <li>closing the scope associated with an upcall stub results in releasing the stub
|
||||
* (see {@link CLinker#upcallStub(MethodHandle, FunctionDescriptor, ResourceScope)}</li>
|
||||
* <li>closing the scope associated with a {@linkplain MemorySegment#allocateNative(long, long, ResourceScope) native memory segment}
|
||||
* results in <em>freeing</em> the native memory associated with it;</li>
|
||||
* <li>closing the scope associated with a {@linkplain MemorySegment#mapFile(Path, long, long, FileChannel.MapMode, ResourceScope) mapped memory segment}
|
||||
* results in the backing memory-mapped file to be unmapped;</li>
|
||||
* <li>closing the scope associated with an {@linkplain CLinker#upcallStub(MethodHandle, FunctionDescriptor, ResourceScope) upcall stub}
|
||||
* 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>
|
||||
* <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 implicit scope that is guaranteed to never become <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>.
|
||||
* As a results, the global scope will never attempt to release resources associated with it. Such resources must, where
|
||||
* a resource scope that cannot be closed, either explicitly or implicitly. As a result, the global scope will never
|
||||
* 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.
|
||||
*
|
||||
* <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.
|
||||
* <p>
|
||||
* Confined resource scopes (see {@link #newConfinedScope()}), 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()}).
|
||||
* {@linkplain #newConfinedScope() Confined resource scopes}, support strong thread-confinement guarantees. Upon creation,
|
||||
* they are assigned an {@linkplain #ownerThread() owner thread}, typically the thread which initiated the creation operation.
|
||||
* After creating a confined 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
|
||||
* owner thread will result in a runtime failure.
|
||||
* <p>
|
||||
* Shared resource scopes (see {@link #newSharedScope()} and {@link #newImplicitScope()}), on the other hand, have no owner thread;
|
||||
* as such resources associated with this shared resource scopes can be accessed by multiple threads.
|
||||
* {@linkplain #newSharedScope() Shared resource scopes}, on the other hand, have no owner thread;
|
||||
* 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).
|
||||
* 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
|
||||
* all int values in a memory segment in parallel:
|
||||
*
|
||||
* <blockquote><pre>{@code
|
||||
SequenceLayout SEQUENCE_LAYOUT = MemoryLayout.sequenceLayout(1024, MemoryLayouts.JAVA_INT);
|
||||
try (ResourceScope scope = ResourceScope.newSharedScope()) {
|
||||
SequenceLayout SEQUENCE_LAYOUT = MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_INT);
|
||||
MemorySegment segment = MemorySegment.allocateNative(SEQUENCE_LAYOUT, scope);
|
||||
VarHandle VH_int = SEQUENCE_LAYOUT.elementLayout().varHandle(int.class);
|
||||
int sum = StreamSupport.stream(segment.spliterator(SEQUENCE_LAYOUT), true)
|
||||
.mapToInt(s -> (int)VH_int.get(s.address()))
|
||||
.sum();
|
||||
int sum = segment.elements(ValueLayout.JAVA_INT).parallel()
|
||||
.mapToInt(s -> s.get(ValueLayout.JAVA_INT, 0))
|
||||
.sum();
|
||||
}
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* <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
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* <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
|
||||
* {@link #acquire()}. A resource scope handle can be used to make sure that resources associated with a given resource scope
|
||||
* (either explicit or implicit) cannot be released for a certain period of time - e.g. during a critical region of code
|
||||
* involving one or more resources associated with the scope. For instance, an explicit resource scope can only be closed
|
||||
* <em>after</em> all the handles acquired against that scope have been closed (see {@link Handle#close()}).
|
||||
* Resource scopes can depend on each other. More specifically, a scope can feature
|
||||
* {@linkplain #keepAlive(ResourceScope) temporal dependencies} on one or more other resource scopes.
|
||||
* Such a resource scope cannot be closed (either implicitly or explicitly) until <em>all</em> the scopes it depends on
|
||||
* have also been closed.
|
||||
* <p>
|
||||
* 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
|
||||
MemorySegment segment = ...
|
||||
ResourceScope.Handle segmentHandle = segment.scope().acquire()
|
||||
try {
|
||||
<critical operation on segment>
|
||||
} finally {
|
||||
segment.scope().release(segmentHandle);
|
||||
try (ResourceScope criticalScope = ResourceScope.newConfinedScope()) {
|
||||
criticalScope.keepAlive(segment.scope());
|
||||
<critical operation on segment>
|
||||
}
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* Acquiring implicit resource scopes is also possible, but it is often unnecessary: since resources associated with
|
||||
* an implicit scope will only be released when the scope becomes <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>,
|
||||
* 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.
|
||||
* Note that a resource scope does not become <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>
|
||||
* until all the scopes it depends on have been closed.
|
||||
*
|
||||
* @implSpec
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* Is this resource scope an <em>implicit scope</em>?
|
||||
* @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
|
||||
* 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}.
|
||||
* 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 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>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>
|
||||
* @throws UnsupportedOperationException if this resource scope is {@linkplain #isImplicit() implicit}.
|
||||
* @throws UnsupportedOperationException if this resource scope is the {@linkplain #globalScope() global scope}.
|
||||
*/
|
||||
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.
|
||||
* 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.
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Acquires a resource scope handle associated with this resource scope. An explicit resource scope cannot be
|
||||
* {@linkplain #close() closed} until all the resource scope handles acquired from it have been {@linkplain #release(Handle)} released}.
|
||||
* @return a resource scope handle.
|
||||
* Creates a temporal dependency between this scope and the target scope. As a result, the target scope cannot
|
||||
* be {@linkplain #close() closed} <em>before</em> this scope.
|
||||
* @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
|
||||
* 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}.
|
||||
* Creates a new confined scope.
|
||||
* @return a new confined scope.
|
||||
*/
|
||||
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.
|
||||
* @return a new confined scope, managed by {@code cleaner}.
|
||||
* @throws NullPointerException if {@code cleaner == null}.
|
||||
*/
|
||||
static ResourceScope newConfinedScope(Cleaner 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.
|
||||
*/
|
||||
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.
|
||||
* @return a new shared scope, managed by {@code cleaner}.
|
||||
* @throws NullPointerException if {@code cleaner == null}.
|
||||
*/
|
||||
static ResourceScope newSharedScope(Cleaner 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
|
||||
* <a href="ResourceScope.html#implicit-closure"><em>implicit closure</em></a>.
|
||||
* Since implicit scopes can only be closed implicitly by the garbage collector, it is recommended that implicit
|
||||
* scopes are only used in cases where deallocation performance is not a critical concern, to avoid unnecessary
|
||||
* memory pressure.
|
||||
*
|
||||
* @return a new implicit scope.
|
||||
* Creates a new shared scope, managed by a private {@link Cleaner} instance. Equivalent to (but likely more efficient than)
|
||||
* the following code:
|
||||
* <pre>{@code
|
||||
newSharedScope(Cleaner.create());
|
||||
* }</pre>
|
||||
* @return a shared scope, managed by a private {@link Cleaner} instance.
|
||||
*/
|
||||
static ResourceScope newImplicitScope() {
|
||||
return ResourceScopeImpl.createImplicitScope();
|
||||
return newSharedScope(CleanerFactory.cleaner());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an implicit scope which is assumed to be always alive.
|
||||
* @return the global scope.
|
||||
* Returns the <a href="ResourceScope.html#global-scope"><em>global scope</em></a>.
|
||||
* @return the <a href="ResourceScope.html#global-scope"><em>global scope</em></a>.
|
||||
*/
|
||||
static ResourceScope globalScope() {
|
||||
return ResourceScopeImpl.GLOBAL;
|
||||
|
||||
@ -25,306 +25,277 @@
|
||||
|
||||
package jdk.incubator.foreign;
|
||||
|
||||
import jdk.internal.foreign.ArenaAllocator;
|
||||
import jdk.internal.foreign.AbstractMemorySegmentImpl;
|
||||
import jdk.internal.foreign.ArenaAllocator;
|
||||
import jdk.internal.foreign.ResourceScopeImpl;
|
||||
import jdk.internal.foreign.Utils;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.lang.reflect.Array;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* This interface models a memory allocator. Clients implementing this interface
|
||||
* 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.
|
||||
* This interface can be seen as a thin wrapper around the basic capabilities for creating native segments
|
||||
* (e.g. {@link MemorySegment#allocateNative(long, long, ResourceScope)}); since {@link SegmentAllocator} is a <em>functional interface</em>,
|
||||
* This interface can be seen as a thin wrapper around the basic capabilities for
|
||||
* {@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.
|
||||
* <p>
|
||||
* This interface provides a factory, namely {@link SegmentAllocator#ofScope(ResourceScope)} which can be used to obtain
|
||||
* a <em>scoped</em> allocator, that is, an allocator which creates segment bound by a given scope. This can be useful
|
||||
* when working inside a <em>try-with-resources</em> construct:
|
||||
*
|
||||
* <blockquote><pre>{@code
|
||||
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
||||
SegmentAllocator allocator = SegmentAllocator.ofScope(scope);
|
||||
...
|
||||
}
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* In addition, this interface also defines factories for commonly used allocators; for instance {@link #arenaAllocator(ResourceScope)}
|
||||
* and {@link #arenaAllocator(long, ResourceScope)} are arena-style native allocators. Finally {@link #ofSegment(MemorySegment)}
|
||||
* returns an allocator which wraps a segment (either on-heap or off-heap) and recycles its content upon each new allocation request.
|
||||
* This interface also defines factories for commonly used allocators:
|
||||
* <ul>
|
||||
* <li>{@link #nativeAllocator(ResourceScope)} creates an allocator which
|
||||
* {@linkplain MemorySegment#allocateNative(long, long, ResourceScope) allocates} native segments, backed by a given scope;</li>
|
||||
* <li>{@link #newNativeArena(ResourceScope)} creates a more efficient arena-style native allocator, where memory
|
||||
* is allocated in bigger blocks, which are then sliced accordingly to fit allocation requests;</li>
|
||||
* <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>
|
||||
* <p>
|
||||
* Passing a segment allocator to an API can be especially useful in circumstances where a client wants to communicate <em>where</em>
|
||||
* the results of a certain operation (performed by the API) should be stored, as a memory segment. For instance,
|
||||
* {@linkplain CLinker#downcallHandle(FunctionDescriptor) downcall method handles} can accept an additional
|
||||
* {@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
|
||||
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)}.
|
||||
* @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.
|
||||
* @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);
|
||||
VarHandle handle = layout.varHandle(byte.class);
|
||||
VarHandle handle = layout.varHandle();
|
||||
MemorySegment addr = allocate(layout);
|
||||
handle.set(addr, value);
|
||||
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)}.
|
||||
* @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.
|
||||
* @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);
|
||||
VarHandle handle = layout.varHandle(char.class);
|
||||
VarHandle handle = layout.varHandle();
|
||||
MemorySegment addr = allocate(layout);
|
||||
handle.set(addr, value);
|
||||
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)}.
|
||||
* @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.
|
||||
* @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);
|
||||
VarHandle handle = layout.varHandle(short.class);
|
||||
VarHandle handle = layout.varHandle();
|
||||
MemorySegment addr = allocate(layout);
|
||||
handle.set(addr, value);
|
||||
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)}.
|
||||
* @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.
|
||||
* @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);
|
||||
VarHandle handle = layout.varHandle(int.class);
|
||||
VarHandle handle = layout.varHandle();
|
||||
MemorySegment addr = allocate(layout);
|
||||
handle.set(addr, value);
|
||||
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)}.
|
||||
* @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.
|
||||
* @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);
|
||||
VarHandle handle = layout.varHandle(float.class);
|
||||
VarHandle handle = layout.varHandle();
|
||||
MemorySegment addr = allocate(layout);
|
||||
handle.set(addr, value);
|
||||
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)}.
|
||||
* @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.
|
||||
* @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);
|
||||
VarHandle handle = layout.varHandle(long.class);
|
||||
VarHandle handle = layout.varHandle();
|
||||
MemorySegment addr = allocate(layout);
|
||||
handle.set(addr, value);
|
||||
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)}.
|
||||
* @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.
|
||||
* @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);
|
||||
VarHandle handle = layout.varHandle(double.class);
|
||||
VarHandle handle = layout.varHandle();
|
||||
MemorySegment addr = allocate(layout);
|
||||
handle.set(addr, value);
|
||||
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).
|
||||
* 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)}.
|
||||
* @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.
|
||||
* @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(layout);
|
||||
if (MemoryLayouts.ADDRESS.byteSize() != layout.byteSize()) {
|
||||
throw new IllegalArgumentException("Layout size mismatch - " + layout.byteSize() + " != " + MemoryLayouts.ADDRESS.byteSize());
|
||||
}
|
||||
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
|
||||
};
|
||||
MemorySegment segment = allocate(layout);
|
||||
layout.varHandle().set(segment, value.address());
|
||||
return segment;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)}.
|
||||
* @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 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)}.
|
||||
* @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 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)}.
|
||||
* @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 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)}.
|
||||
* @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 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)}.
|
||||
* @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 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)}.
|
||||
* @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 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)}.
|
||||
* @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 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
Function<Z, MemorySegment> heapSegmentFactory) {
|
||||
Objects.requireNonNull(array);
|
||||
Objects.requireNonNull(elementLayout);
|
||||
Utils.checkPrimitiveCarrierCompat(array.getClass().componentType(), elementLayout);
|
||||
MemorySegment addr = allocate(MemoryLayout.sequenceLayout(Array.getLength(array), elementLayout));
|
||||
if (elementLayout.byteSize() == 1 || (elementLayout.order() == ByteOrder.nativeOrder())) {
|
||||
addr.copyFrom(heapSegmentFactory.apply(array));
|
||||
} else {
|
||||
((AbstractMemorySegmentImpl)addr).copyFromSwap(heapSegmentFactory.apply(array), elementLayout.byteSize());
|
||||
}
|
||||
int size = Array.getLength(array);
|
||||
MemorySegment addr = allocate(MemoryLayout.sequenceLayout(size, elementLayout));
|
||||
MemorySegment.copy(heapSegmentFactory.apply(array), elementLayout, 0,
|
||||
addr, elementLayout.withOrder(ByteOrder.nativeOrder()), 0, size);
|
||||
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())}.
|
||||
* @param layout the layout of the block of memory to be allocated.
|
||||
* @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))}.
|
||||
* @param elementLayout the array element layout.
|
||||
* @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)}.
|
||||
* @param bytesSize the size (in bytes) of the block of memory to be allocated.
|
||||
* @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 bytesAlignment the alignment (in bytes) of the block of memory to be allocated.
|
||||
* @return a segment for the newly allocated memory block.
|
||||
@ -365,74 +337,91 @@ public interface SegmentAllocator {
|
||||
MemorySegment allocate(long bytesSize, long bytesAlignment);
|
||||
|
||||
/**
|
||||
* Returns a native arena-based allocator which allocates a single memory segment, of given size (using malloc),
|
||||
* and then responds to allocation request by returning different slices of that same segment
|
||||
* (until no further allocation is possible).
|
||||
* This can be useful when clients want to perform multiple allocation requests while avoiding the cost associated
|
||||
* with allocating a new off-heap memory region upon each allocation request.
|
||||
* <p>
|
||||
* 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.
|
||||
* Returns a native unbounded arena-based allocator, with predefined block size and maximum arena size,
|
||||
* associated with the provided scope. Equivalent to the following code:
|
||||
* <blockquote><pre>{@code
|
||||
SegmentAllocator.newNativeArena(Long.MAX_VALUE, predefinedBlockSize, scope);
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* @param size the size (in bytes) of the allocation arena.
|
||||
* @param scope the scope associated with the segments returned by this allocator.
|
||||
* @return a new bounded arena-based allocator
|
||||
* @throws IllegalArgumentException if {@code size <= 0}.
|
||||
* @param scope the scope associated with the segments returned by the arena-based allocator.
|
||||
* @return a new unbounded arena-based allocator
|
||||
* @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 arenaAllocator(long size, ResourceScope scope) {
|
||||
Objects.requireNonNull(scope);
|
||||
return scope.ownerThread() == null ?
|
||||
new ArenaAllocator.BoundedSharedArenaAllocator(scope, size) :
|
||||
new ArenaAllocator.BoundedArenaAllocator(scope, size);
|
||||
static SegmentAllocator newNativeArena(ResourceScope scope) {
|
||||
return newNativeArena(Long.MAX_VALUE, ArenaAllocator.DEFAULT_BLOCK_SIZE, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* The returned allocator allocates a memory segment {@code S} of a certain fixed size (using malloc) and then
|
||||
* responds to allocation requests in one of the following ways:
|
||||
* The returned allocator {@linkplain MemorySegment#allocateNative(long, ResourceScope) allocates} a memory segment
|
||||
* {@code S} of the specified block size and then responds to allocation requests in one of the following ways:
|
||||
* <ul>
|
||||
* <li>if the size of the allocation requests is smaller than the size of {@code S}, and {@code S} has a <em>free</em>
|
||||
* slice {@code S'} which fits that allocation request, return that {@code S'}.
|
||||
* <li>if the size of the allocation requests is smaller than the size of {@code S}, and {@code S} has no <em>free</em>
|
||||
* slices which fits that allocation request, allocate a new segment {@code S'} (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.
|
||||
* <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'}.
|
||||
* <li>if the size of the allocation requests is bigger than the size of {@code S}, allocate a new segment {@code S'},
|
||||
* which has a sufficient size to satisfy the allocation request, and return {@code S'}.
|
||||
* </ul>
|
||||
* <p>
|
||||
* This segment allocator can be useful when clients want to perform multiple allocation requests while avoiding the
|
||||
* cost associated with allocating a new off-heap memory region upon each allocation request.
|
||||
* <p>
|
||||
* 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 system capacity.
|
||||
* The returned allocator might throw an {@link OutOfMemoryError} if the total memory allocated with this allocator
|
||||
* exceeds the arena size, or the system capacity. Furthermore, the returned allocator is not thread safe.
|
||||
* Concurrent allocation needs to be guarded with synchronization primitives.
|
||||
*
|
||||
* @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
|
||||
* @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
|
||||
* than the thread owning {@code scope}.
|
||||
*/
|
||||
static SegmentAllocator arenaAllocator(ResourceScope scope) {
|
||||
static SegmentAllocator newNativeArena(long arenaSize, long blockSize, ResourceScope scope) {
|
||||
Objects.requireNonNull(scope);
|
||||
return scope.ownerThread() == null ?
|
||||
new ArenaAllocator.UnboundedSharedArenaAllocator(scope) :
|
||||
new ArenaAllocator.UnboundedArenaAllocator(scope);
|
||||
if (blockSize <= 0) {
|
||||
throw new IllegalArgumentException("Invalid block size: " + blockSize);
|
||||
}
|
||||
if (arenaSize <= 0 || arenaSize < blockSize) {
|
||||
throw new IllegalArgumentException("Invalid arena size: " + arenaSize);
|
||||
}
|
||||
return new ArenaAllocator(blockSize, arenaSize, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
* takes place.
|
||||
* <p>
|
||||
@ -442,25 +431,42 @@ public interface SegmentAllocator {
|
||||
* @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.
|
||||
*/
|
||||
static SegmentAllocator ofSegment(MemorySegment segment) {
|
||||
static SegmentAllocator prefixAllocator(MemorySegment 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
|
||||
* bound by the given resource scope, using the {@link MemorySegment#allocateNative(long, long, ResourceScope)}
|
||||
* factory. This code is equivalent (but likely more efficient) to the following:
|
||||
* Returns a native allocator, associated with the provided scope. Equivalent to (but likely more efficient than)
|
||||
* the following code:
|
||||
* <blockquote><pre>{@code
|
||||
Resource scope = ...
|
||||
SegmentAllocator scoped = (size, align) -> MemorySegment.allocateNative(size, align, scope);
|
||||
ResourceScope scope = ...
|
||||
SegmentAllocator nativeAllocator = (size, align) -> MemorySegment.allocateNative(size, align, scope);
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* @param scope the resource scope associated with the segments created by the returned allocator.
|
||||
* @return an allocator which allocates new memory segment bound by the provided resource scope.
|
||||
* @param scope the scope associated with the returned allocator.
|
||||
* @return a native allocator, associated with the provided scope.
|
||||
*/
|
||||
static SegmentAllocator ofScope(ResourceScope scope) {
|
||||
static SegmentAllocator nativeAllocator(ResourceScope scope) {
|
||||
Objects.requireNonNull(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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,31 +25,29 @@
|
||||
*/
|
||||
package jdk.incubator.foreign;
|
||||
|
||||
import java.lang.constant.Constable;
|
||||
import java.lang.constant.ConstantDescs;
|
||||
import java.lang.constant.DynamicConstantDesc;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
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>.
|
||||
* 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
|
||||
* that is equal to the sequence layout's element count. In other words this layout:
|
||||
*
|
||||
* <pre>{@code
|
||||
MemoryLayout.sequenceLayout(3, MemoryLayout.valueLayout(32, ByteOrder.BIG_ENDIAN));
|
||||
MemoryLayout.sequenceLayout(3, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN));
|
||||
* }</pre>
|
||||
*
|
||||
* is equivalent to the following layout:
|
||||
*
|
||||
* <pre>{@code
|
||||
MemoryLayout.structLayout(
|
||||
MemoryLayout.valueLayout(32, ByteOrder.BIG_ENDIAN),
|
||||
MemoryLayout.valueLayout(32, ByteOrder.BIG_ENDIAN),
|
||||
MemoryLayout.valueLayout(32, ByteOrder.BIG_ENDIAN));
|
||||
ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN),
|
||||
ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN),
|
||||
ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN));
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
@ -72,13 +70,13 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
|
||||
private final 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() ?
|
||||
OptionalLong.of(elemCount.getAsLong() * elementLayout.bitSize()) :
|
||||
OptionalLong.empty(), alignment, attributes);
|
||||
OptionalLong.empty(), alignment, name);
|
||||
this.elemCount = elemCount;
|
||||
this.elementLayout = elementLayout;
|
||||
}
|
||||
@ -110,7 +108,7 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
|
||||
*/
|
||||
public SequenceLayout withElementCount(long elementCount) {
|
||||
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>
|
||||
* For instance, given a sequence layout of the kind:
|
||||
* <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>
|
||||
* calling {@code seq.reshape(2, 6)} will yield the following sequence layout:
|
||||
* <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>
|
||||
* <p>
|
||||
* 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) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (!elementCount().isPresent()) {
|
||||
if (elementCount().isEmpty()) {
|
||||
throw new UnsupportedOperationException("Cannot reshape a sequence layout whose element count is unspecified");
|
||||
}
|
||||
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.
|
||||
* For instance, given a sequence layout of the kind:
|
||||
* <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>
|
||||
* calling {@code seq.flatten()} will yield the following sequence layout:
|
||||
* <pre>{@code
|
||||
var flattenedSeq = MemoryLayout.sequenceLayout(12, MemoryLayouts.JAVA_INT);
|
||||
var flattenedSeq = MemoryLayout.sequenceLayout(12, ValueLayout.JAVA_INT);
|
||||
* }</pre>
|
||||
* @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.
|
||||
@ -210,13 +208,12 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
|
||||
* flattened, does not have an element count.
|
||||
*/
|
||||
public SequenceLayout flatten() {
|
||||
if (!elementCount().isPresent()) {
|
||||
if (elementCount().isEmpty()) {
|
||||
throw badUnboundSequenceLayout();
|
||||
}
|
||||
long count = elementCount().getAsLong();
|
||||
MemoryLayout elemLayout = elementLayout();
|
||||
while (elemLayout instanceof SequenceLayout) {
|
||||
SequenceLayout elemSeq = (SequenceLayout)elemLayout;
|
||||
while (elemLayout instanceof SequenceLayout elemSeq) {
|
||||
count = count * elemSeq.elementCount().orElseThrow(this::badUnboundSequenceLayout);
|
||||
elemLayout = elemSeq.elementLayout();
|
||||
}
|
||||
@ -241,10 +238,9 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
if (!(other instanceof SequenceLayout)) {
|
||||
if (!(other instanceof SequenceLayout s)) {
|
||||
return false;
|
||||
}
|
||||
SequenceLayout s = (SequenceLayout)other;
|
||||
return elemCount.equals(s.elemCount) && elementLayout.equals(s.elementLayout);
|
||||
}
|
||||
|
||||
@ -254,8 +250,8 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
|
||||
}
|
||||
|
||||
@Override
|
||||
SequenceLayout dup(long alignment, Map<String, Constable> attributes) {
|
||||
return new SequenceLayout(elementCount(), elementLayout, alignment, attributes);
|
||||
SequenceLayout dup(long alignment, Optional<String> name) {
|
||||
return new SequenceLayout(elementCount(), elementLayout, alignment, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -290,12 +286,4 @@ public final class SequenceLayout extends AbstractLayout implements MemoryLayout
|
||||
public SequenceLayout withBitAlignment(long alignmentBits) {
|
||||
return (SequenceLayout)super.withBitAlignment(alignmentBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public SequenceLayout withAttribute(String name, Constable value) {
|
||||
return (SequenceLayout)super.withAttribute(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ package jdk.incubator.foreign;
|
||||
|
||||
import jdk.internal.access.JavaLangAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.foreign.ResourceScopeImpl;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
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 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)},
|
||||
* or {@link System#loadLibrary(String)}).
|
||||
* Alternatively, clients can obtain a {@linkplain CLinker#systemLookup() platform-dependent lookup}, to search symbols
|
||||
* in the standard C library.
|
||||
* Alternatively, clients can search symbols in the standard C library using a {@link CLinker}, which conveniently
|
||||
* implements this interface.
|
||||
* <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>
|
||||
*/
|
||||
@ -49,13 +50,15 @@ public interface SymbolLookup {
|
||||
* Looks up a symbol with given name in this lookup.
|
||||
*
|
||||
* @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
|
||||
* (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>
|
||||
* 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
|
||||
@ -72,11 +75,13 @@ public interface SymbolLookup {
|
||||
Class<?> caller = Reflection.getCallerClass();
|
||||
Reflection.ensureNativeAccess(caller);
|
||||
ClassLoader loader = Objects.requireNonNull(caller.getClassLoader());
|
||||
ResourceScope loaderScope = ResourceScopeImpl.heapScope(loader);
|
||||
return name -> {
|
||||
Objects.requireNonNull(name);
|
||||
JavaLangAccess javaLangAccess = SharedSecrets.getJavaLangAccess();
|
||||
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));
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -25,19 +25,30 @@
|
||||
*/
|
||||
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.DynamicConstantDesc;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
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
|
||||
* (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>
|
||||
* This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
|
||||
* class; programmers should treat instances that are
|
||||
@ -52,17 +63,22 @@ import java.util.OptionalLong;
|
||||
* @implSpec
|
||||
* 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;
|
||||
|
||||
ValueLayout(ByteOrder order, long size) {
|
||||
this(order, size, size, Map.of());
|
||||
private static final int ADDRESS_SIZE_BITS = Unsafe.ADDRESS_SIZE * 8;
|
||||
|
||||
ValueLayout(Class<?> carrier, ByteOrder order, long size) {
|
||||
this(carrier, order, size, size, Optional.empty());
|
||||
}
|
||||
|
||||
ValueLayout(ByteOrder order, long size, long alignment, Map<String, Constable> attributes) {
|
||||
super(OptionalLong.of(size), alignment, attributes);
|
||||
ValueLayout(Class<?> carrier, ByteOrder order, long size, long alignment, Optional<String> name) {
|
||||
super(OptionalLong.of(size), alignment, name);
|
||||
this.carrier = carrier;
|
||||
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.
|
||||
*/
|
||||
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
|
||||
@ -99,29 +115,37 @@ public final class ValueLayout extends AbstractLayout implements MemoryLayout {
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
if (!(other instanceof ValueLayout)) {
|
||||
if (!(other instanceof ValueLayout v)) {
|
||||
return false;
|
||||
}
|
||||
ValueLayout v = (ValueLayout)other;
|
||||
return order.equals(v.order) &&
|
||||
return carrier.equals(v.carrier) &&
|
||||
order.equals(v.order) &&
|
||||
bitSize() == v.bitSize() &&
|
||||
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
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), order, bitSize(), alignment);
|
||||
}
|
||||
|
||||
@Override
|
||||
ValueLayout dup(long alignment, Map<String, Constable> attributes) {
|
||||
return new ValueLayout(order, bitSize(), alignment, attributes);
|
||||
ValueLayout dup(long alignment, Optional<String> name) {
|
||||
return new ValueLayout(carrier, order, bitSize(), alignment, name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DynamicConstantDesc<ValueLayout>> describeConstable() {
|
||||
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
|
||||
@ -143,11 +167,439 @@ public final class ValueLayout extends AbstractLayout implements MemoryLayout {
|
||||
return (ValueLayout)super.withBitAlignment(alignmentBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public ValueLayout withAttribute(String name, Constable value) {
|
||||
return (ValueLayout)super.withAttribute(name, value);
|
||||
static void checkCarrierSize(Class<?> carrier, long size) {
|
||||
if (!isValidCarrier(carrier)) {
|
||||
throw new IllegalArgumentException("Invalid carrier: " + carrier.getName());
|
||||
}
|
||||
if (carrier == MemoryAddress.class && size != ADDRESS_SIZE_BITS) {
|
||||
throw new IllegalArgumentException("Address size mismatch: " + ADDRESS_SIZE_BITS + " != " + size);
|
||||
}
|
||||
if (carrier.isPrimitive()) {
|
||||
int expectedSize = carrier == boolean.class ? 8 : Wrapper.forPrimitiveType(carrier).bitWidth();
|
||||
if (size != expectedSize) {
|
||||
throw new IllegalArgumentException("Carrier size mismatch: " + carrier.getName() + " != " + size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isValidCarrier(Class<?> carrier) {
|
||||
return carrier == boolean.class
|
||||
|| carrier == byte.class
|
||||
|| carrier == short.class
|
||||
|| carrier == char.class
|
||||
|| carrier == int.class
|
||||
|| carrier == long.class
|
||||
|| carrier == float.class
|
||||
|| carrier == double.class
|
||||
|| carrier == MemoryAddress.class;
|
||||
}
|
||||
|
||||
@Stable
|
||||
private VarHandle handle;
|
||||
|
||||
@ForceInline
|
||||
VarHandle accessHandle() {
|
||||
if (handle == null) {
|
||||
// this store to stable field is safe, because return value of 'makeMemoryAccessVarHandle' has stable identity
|
||||
handle = Utils.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);
|
||||
}
|
||||
|
||||
@ -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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -30,12 +30,11 @@
|
||||
* <h2>Foreign memory access</h2>
|
||||
*
|
||||
* <p>
|
||||
* The key abstractions introduced to support foreign memory access are {@link jdk.incubator.foreign.MemorySegment} and {@link jdk.incubator.foreign.MemoryAddress}.
|
||||
* 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
|
||||
* reside either inside or outside the Java heap (and can sometimes be expressed as an offset into a given segment).
|
||||
* The main abstractions introduced to support foreign memory access is {@link jdk.incubator.foreign.MemorySegment}, which
|
||||
* models a contiguous memory region, which can reside either inside or outside the Java heap.
|
||||
* 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
|
||||
* 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
|
||||
* 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
|
||||
@ -47,16 +46,18 @@
|
||||
* <pre>{@code
|
||||
MemorySegment segment = MemorySegment.allocateNative(10 * 4, ResourceScope.newImplicitScope());
|
||||
for (int i = 0 ; i < 10 ; i++) {
|
||||
MemoryAccess.setIntAtIndex(segment, i, 42);
|
||||
segment.setAtIndex(ValueLayout.JAVA_INT, i, i);
|
||||
}
|
||||
* }</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}.
|
||||
* Inside a loop, we then initialize the contents of the memory segment using the
|
||||
* {@link jdk.incubator.foreign.MemoryAccess#setIntAtIndex(jdk.incubator.foreign.MemorySegment, long, int)} helper method;
|
||||
* 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
|
||||
* Inside a loop, we then initialize the contents of the memory segment; note how the
|
||||
* {@linkplain jdk.incubator.foreign.MemorySegment#setAtIndex(ValueLayout.OfInt, long, int) dereference method}
|
||||
* accepts a {@linkplain jdk.incubator.foreign.ValueLayout value layout}, which specifies the size, alignment constraints,
|
||||
* byte order as well as the Java type ({@code int}, in this case) associated with the dereference operation. More specifically,
|
||||
* if we view the memory segment as a set of 10 adjacent slots, {@code s[i]}, where {@code 0 <= i < 10},
|
||||
* where the size of each slot is exactly 4 bytes, the initialization logic above will set each slot
|
||||
* so that {@code s[i] = i}, again where {@code 0 <= i < 10}.
|
||||
*
|
||||
* <h3><a id="deallocation"></a>Deterministic deallocation</h3>
|
||||
@ -72,7 +73,7 @@ for (int i = 0 ; i < 10 ; i++) {
|
||||
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
||||
MemorySegment segment = MemorySegment.allocateNative(10 * 4, scope);
|
||||
for (int i = 0 ; i < 10 ; i++) {
|
||||
MemoryAccess.setIntAtIndex(segment, i, 42);
|
||||
segment.setAtIndex(ValueLayout.JAVA_INT, i, i);
|
||||
}
|
||||
}
|
||||
* }</pre>
|
||||
@ -96,9 +97,10 @@ try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
||||
* operation either succeeds - and accesses a valid memory location - or fails.
|
||||
*
|
||||
* <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 former is used to lookup symbols inside native libraries; the latter
|
||||
* provides linking capabilities which allow to model foreign functions as {@link java.lang.invoke.MethodHandle} instances,
|
||||
* The key abstractions introduced to support foreign function access are {@link jdk.incubator.foreign.SymbolLookup},
|
||||
* {@link jdk.incubator.foreign.MemoryAddress} and {@link jdk.incubator.foreign.CLinker}.
|
||||
* 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
|
||||
* code (as it's the case with the <a href="{@docRoot}/../specs/jni/index.html">Java Native Interface (JNI)</a>).
|
||||
* <p>
|
||||
@ -106,30 +108,32 @@ try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
||||
* we can use the following code:
|
||||
*
|
||||
* <pre>{@code
|
||||
MethodHandle strlen = CLinker.getInstance().downcallHandle(
|
||||
CLinker.systemLookup().lookup("strlen").get(),
|
||||
MethodType.methodType(long.class, MemoryAddress.class),
|
||||
FunctionDescriptor.of(CLinker.C_LONG, CLinker.C_POINTER)
|
||||
var linker = CLinker.systemCLinker();
|
||||
MethodHandle strlen = linker.downcallHandle(
|
||||
linker.lookup("strlen").get(),
|
||||
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS)
|
||||
);
|
||||
|
||||
try (var scope = ResourceScope.newConfinedScope()) {
|
||||
var cString = CLinker.toCString("Hello", scope);
|
||||
long len = (long)strlen.invokeExact(cString.address()); // 5
|
||||
var cString = MemorySegment.allocateNative(5 + 1, scope);
|
||||
cString.setUtf8String("Hello");
|
||||
long len = (long)strlen.invoke(cString); // 5
|
||||
}
|
||||
* }</pre>
|
||||
*
|
||||
* Here, we lookup the {@code strlen} symbol in the {@linkplain jdk.incubator.foreign.CLinker#systemLookup() system lookup}.
|
||||
* Then, we obtain a linker instance (see {@link jdk.incubator.foreign.CLinker#getInstance()}) and we use it to
|
||||
* obtain a method handle which targets the {@code strlen} library symbol. To complete the linking successfully,
|
||||
* we must provide (i) a {@link java.lang.invoke.MethodType} instance, describing the type of the resulting method handle
|
||||
* and (ii) a {@link jdk.incubator.foreign.FunctionDescriptor} instance, describing the signature of the {@code strlen}
|
||||
* function. From this information, the linker will uniquely determine the sequence of steps which will turn
|
||||
* the method handle invocation (here performed using {@link java.lang.invoke.MethodHandle#invokeExact(java.lang.Object...)})
|
||||
* into a foreign function call, according to the rules specified by the platform C ABI. The {@link jdk.incubator.foreign.CLinker}
|
||||
* class also provides many useful methods for interacting with native code, such as converting Java strings into
|
||||
* native strings and viceversa (see {@link jdk.incubator.foreign.CLinker#toCString(java.lang.String, ResourceScope)} and
|
||||
* {@link jdk.incubator.foreign.CLinker#toJavaString(jdk.incubator.foreign.MemorySegment)}, respectively), as
|
||||
* demonstrated in the above example.
|
||||
* Here, we obtain a {@linkplain jdk.incubator.foreign.CLinker#systemCLinker() linker instance} and we use it
|
||||
* to {@linkplain jdk.incubator.foreign.CLinker#lookup(java.lang.String) lookup} the {@code strlen} symbol in the
|
||||
* standard C library; a <em>downcall method handle</em> targeting said symbol is subsequently
|
||||
* {@linkplain jdk.incubator.foreign.CLinker#downcallHandle(jdk.incubator.foreign.FunctionDescriptor) obtained}.
|
||||
* To complete the linking successfully, we must provide a {@link jdk.incubator.foreign.FunctionDescriptor} instance,
|
||||
* describing the signature of the {@code strlen} function.
|
||||
* From this information, the linker will uniquely determine the sequence of steps which will turn
|
||||
* the method handle invocation (here performed using {@link java.lang.invoke.MethodHandle#invoke(java.lang.Object...)})
|
||||
* into a foreign function call, according to the rules specified by the platform C ABI.
|
||||
* The {@link jdk.incubator.foreign.MemorySegment} class also provides many useful methods for
|
||||
* interacting with native code, such as converting Java strings
|
||||
* {@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>
|
||||
*
|
||||
@ -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,
|
||||
* or an array of {@code char} values, of given size?), no notion of temporal bounds, nor thread-confinement.
|
||||
* <p>
|
||||
* When clients receive a {@link jdk.incubator.foreign.MemoryAddress} instance from a foreign function call, it might be
|
||||
* necessary to obtain a {@link jdk.incubator.foreign.MemorySegment} instance to dereference the memory pointed to by that address.
|
||||
* To do that, clients can proceed in three different ways, described below.
|
||||
* <p>
|
||||
* First, if the memory address is known to belong to a segment the client already owns, a <em>rebase</em> operation can be performed;
|
||||
* 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:
|
||||
* Raw pointers are modelled using the {@link jdk.incubator.foreign.MemoryAddress} class. When clients receive a
|
||||
* memory address instance from a foreign function call, they can perform memory dereference on it directly,
|
||||
* using one of the many <em>unsafe</em>
|
||||
* {@linkplain jdk.incubator.foreign.MemoryAddress#get(jdk.incubator.foreign.ValueLayout.OfInt, long) dereference methods}
|
||||
* provided:
|
||||
*
|
||||
* <pre>{@code
|
||||
MemorySegment segment = MemorySegment.allocateNative(100, scope);
|
||||
...
|
||||
MemoryAddress addr = ... //obtain address from native code
|
||||
int x = MemoryAccess.getIntAtOffset(segment, addr.segmentOffset(segment));
|
||||
int x = addr.get(ValueLayout.JAVA_INT, 0);
|
||||
* }</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>,
|
||||
* using the {@link jdk.incubator.foreign.MemoryAddress#asSegment(long, ResourceScope)} factory. This allows the client to
|
||||
* inject extra knowledge about spatial bounds which might, for instance, be available in the documentation of the foreign function
|
||||
* which produced the native address. Here is how an unsafe segment can be created from a native address:
|
||||
* Alternatively, the client can
|
||||
* {@linkplain jdk.incubator.foreign.MemorySegment#ofAddress(jdk.incubator.foreign.MemoryAddress, long, jdk.incubator.foreign.ResourceScope) create}
|
||||
* a memory segment <em>unsafely</em>. This allows the client to inject extra knowledge about spatial bounds which might,
|
||||
* for instance, be available in the documentation of the foreign function which produced the native address.
|
||||
* Here is how an unsafe segment can be created from a native address:
|
||||
*
|
||||
* <pre>{@code
|
||||
ResourceScope scope = ... // initialize a resource scope object
|
||||
MemoryAddress addr = ... //obtain address from native code
|
||||
MemorySegment segment = addr.asSegment(4, scope); // segment is 4 bytes long
|
||||
int x = MemoryAccess.getInt(segment);
|
||||
* }</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());
|
||||
MemorySegment segment = MemorySegment.ofAddress(addr, 4, scope); // segment is 4 bytes long
|
||||
int x = segment.get(ValueLayout.JAVA_INT, 0);
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Upcalls</h3>
|
||||
* 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
|
||||
* can effectively be passed to other foreign functions. For instance, we can write a method that compares two
|
||||
* integer values, as follows:
|
||||
* to a Java method) into a memory address, so that Java code can effectively be passed to other foreign functions.
|
||||
* For instance, we can write a method that compares two integer values, as follows:
|
||||
*
|
||||
* <pre>{@code
|
||||
class IntComparator {
|
||||
static int intCompare(MemoryAddress addr1, MemoryAddress addr2) {
|
||||
return MemoryAccess.getIntAtOffset(MemorySegment.globalNativeSegment(), addr1.toRawLongValue()) -
|
||||
MemoryAccess.getIntAtOffset(MemorySegment.globalNativeSegment(), addr2.toRawLongValue());
|
||||
return addr1.get(ValueLayout.JAVA_INT, 0) - addr2.get(ValueLayout.JAVA_INT, 0);
|
||||
}
|
||||
}
|
||||
* }</pre>
|
||||
@ -194,39 +185,45 @@ class IntComparator {
|
||||
* method, as follows:
|
||||
*
|
||||
* <pre>{@code
|
||||
FunctionDescriptor intCompareDescriptor = FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.ADDRESS);
|
||||
MethodHandle intCompareHandle = MethodHandles.lookup().findStatic(IntComparator.class,
|
||||
"intCompare",
|
||||
MethodType.methodType(int.class, MemoryAddress.class, MemoryAddress.class));
|
||||
CLinker.upcallType(comparFunction));
|
||||
* }</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
|
||||
ResourceScope scope = ...
|
||||
MemoryAddress comparFunc = CLinker.getInstance().upcallStub(
|
||||
intCompareHandle,
|
||||
FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER),
|
||||
scope
|
||||
Addressable comparFunc = CLinker.systemCLinker().upcallStub(
|
||||
intCompareHandle, intCompareDescriptor, scope);
|
||||
);
|
||||
* }</pre>
|
||||
*
|
||||
* As before, we need to provide a {@link jdk.incubator.foreign.FunctionDescriptor} instance describing the signature
|
||||
* of the function pointer we want to create; as before, this, coupled with the method handle type, uniquely determines the
|
||||
* sequence of steps which will allow foreign code to call {@code intCompareHandle} according to the rules specified
|
||||
* by the platform C ABI. The lifecycle of the memory address returned by
|
||||
* {@link jdk.incubator.foreign.CLinker#upcallStub(java.lang.invoke.MethodHandle, jdk.incubator.foreign.FunctionDescriptor, jdk.incubator.foreign.ResourceScope)}
|
||||
* is tied to the {@linkplain jdk.incubator.foreign.ResourceScope resource scope} parameter passed to that method.
|
||||
* The {@link jdk.incubator.foreign.FunctionDescriptor} instance created in the previous step is then used to
|
||||
* {@linkplain jdk.incubator.foreign.CLinker#upcallStub(java.lang.invoke.MethodHandle, jdk.incubator.foreign.FunctionDescriptor, jdk.incubator.foreign.ResourceScope) create}
|
||||
* a new upcall stub; the layouts in the function descriptors allow the linker to determine the sequence of steps which
|
||||
* allow foreign code to call the stub for {@code intCompareHandle} according to the rules specified by the platform C ABI.
|
||||
* The lifecycle of the upcall stub returned by is tied to the {@linkplain jdk.incubator.foreign.ResourceScope resource scope}
|
||||
* 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>
|
||||
* <h2>Restricted methods</h2>
|
||||
* Some methods in this package are considered <em>restricted</em>. Restricted methods are typically used to bind native
|
||||
* foreign data and/or functions to first-class Java API elements which can then be used directly by clients. For instance
|
||||
* the restricted method {@link jdk.incubator.foreign.MemoryAddress#asSegment(long, ResourceScope)} can be used to create
|
||||
* a fresh segment with given spatial bounds out of a native address.
|
||||
* the restricted method {@link MemorySegment#ofAddress(MemoryAddress, long, ResourceScope)}
|
||||
* can be used to create a fresh segment with given spatial bounds out of a native address.
|
||||
* <p>
|
||||
* Binding foreign data and/or functions is generally unsafe and, if done incorrectly, can result in VM crashes, or memory corruption when the bound Java API element is accessed.
|
||||
* For instance, in the case of {@link jdk.incubator.foreign.MemoryAddress#asSegment(long, ResourceScope)}, if the provided
|
||||
* spatial bounds are incorrect, a client of the segment returned by that method might crash the VM, or corrupt
|
||||
* For instance, in the case of {@link MemorySegment#ofAddress(MemoryAddress, long, ResourceScope)},
|
||||
* 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
|
||||
* to never pass arguments that might cause incorrect binding of foreign data and/or functions to a Java API.
|
||||
* <p>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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.
|
||||
*
|
||||
* 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 java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
@ -43,6 +44,8 @@ import java.util.function.IntFunction;
|
||||
import java.util.stream.Stream;
|
||||
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
|
||||
* 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
|
||||
* {@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();
|
||||
|
||||
@ -140,24 +143,9 @@ public abstract non-sealed class AbstractMemorySegmentImpl extends MemorySegment
|
||||
return this;
|
||||
}
|
||||
|
||||
public void copyFrom(MemorySegment src) {
|
||||
AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl)Objects.requireNonNull(src);
|
||||
long size = that.byteSize();
|
||||
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
|
||||
public MemorySegment allocate(long bytesSize, long bytesAlignment) {
|
||||
return asSlice(0, bytesSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -175,7 +163,7 @@ public abstract non-sealed class AbstractMemorySegmentImpl extends MemorySegment
|
||||
|
||||
long i = 0;
|
||||
if (length > 7) {
|
||||
if (MemoryAccess.getByte(this) != MemoryAccess.getByte(that)) {
|
||||
if (get(JAVA_BYTE, 0) != that.get(JAVA_BYTE, 0)) {
|
||||
return 0;
|
||||
}
|
||||
i = vectorizedMismatchLargeForBytes(scope, that.scope,
|
||||
@ -190,7 +178,7 @@ public abstract non-sealed class AbstractMemorySegmentImpl extends MemorySegment
|
||||
i = length - remaining;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -230,9 +218,8 @@ public abstract non-sealed class AbstractMemorySegmentImpl extends MemorySegment
|
||||
}
|
||||
|
||||
@Override
|
||||
@ForceInline
|
||||
public final MemoryAddress address() {
|
||||
return new MemoryAddressImpl(this, 0L);
|
||||
public MemoryAddress address() {
|
||||
throw new UnsupportedOperationException("Cannot obtain address of on-heap segment");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -269,6 +256,33 @@ public abstract non-sealed class AbstractMemorySegmentImpl extends MemorySegment
|
||||
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
|
||||
public void load() {
|
||||
throw new UnsupportedOperationException("Not a mapped segment");
|
||||
@ -290,45 +304,45 @@ public abstract non-sealed class AbstractMemorySegmentImpl extends MemorySegment
|
||||
}
|
||||
|
||||
@Override
|
||||
public final byte[] toByteArray() {
|
||||
return toArray(byte[].class, 1, byte[]::new, MemorySegment::ofArray);
|
||||
public final byte[] toArray(ValueLayout.OfByte elementLayout) {
|
||||
return toArray(byte[].class, elementLayout, byte[]::new, MemorySegment::ofArray);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final short[] toShortArray() {
|
||||
return toArray(short[].class, 2, short[]::new, MemorySegment::ofArray);
|
||||
public final short[] toArray(ValueLayout.OfShort elementLayout) {
|
||||
return toArray(short[].class, elementLayout, short[]::new, MemorySegment::ofArray);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final char[] toCharArray() {
|
||||
return toArray(char[].class, 2, char[]::new, MemorySegment::ofArray);
|
||||
public final char[] toArray(ValueLayout.OfChar elementLayout) {
|
||||
return toArray(char[].class, elementLayout, char[]::new, MemorySegment::ofArray);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int[] toIntArray() {
|
||||
return toArray(int[].class, 4, int[]::new, MemorySegment::ofArray);
|
||||
public final int[] toArray(ValueLayout.OfInt elementLayout) {
|
||||
return toArray(int[].class, elementLayout, int[]::new, MemorySegment::ofArray);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final float[] toFloatArray() {
|
||||
return toArray(float[].class, 4, float[]::new, MemorySegment::ofArray);
|
||||
public final float[] toArray(ValueLayout.OfFloat elementLayout) {
|
||||
return toArray(float[].class, elementLayout, float[]::new, MemorySegment::ofArray);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final long[] toLongArray() {
|
||||
return toArray(long[].class, 8, long[]::new, MemorySegment::ofArray);
|
||||
public final long[] toArray(ValueLayout.OfLong elementLayout) {
|
||||
return toArray(long[].class, elementLayout, long[]::new, MemorySegment::ofArray);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final double[] toDoubleArray() {
|
||||
return toArray(double[].class, 8, double[]::new, MemorySegment::ofArray);
|
||||
public final double[] toArray(ValueLayout.OfDouble elementLayout) {
|
||||
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) {
|
||||
int size = checkArraySize(arrayClass.getSimpleName(), elemSize);
|
||||
private <Z> Z toArray(Class<Z> arrayClass, ValueLayout elemLayout, IntFunction<Z> arrayFactory, Function<Z, MemorySegment> segmentFactory) {
|
||||
int size = checkArraySize(arrayClass.getSimpleName(), (int)elemLayout.byteSize());
|
||||
Z arr = arrayFactory.apply(size);
|
||||
MemorySegment arrSegment = segmentFactory.apply(arr);
|
||||
arrSegment.copyFrom(this);
|
||||
MemorySegment.copy(this, elemLayout, 0, arrSegment, elemLayout.withOrder(ByteOrder.nativeOrder()), 0, size);
|
||||
return arr;
|
||||
}
|
||||
|
||||
@ -382,11 +396,12 @@ public abstract non-sealed class AbstractMemorySegmentImpl extends MemorySegment
|
||||
|
||||
private void checkBounds(long offset, long length) {
|
||||
if (isSmall() &&
|
||||
offset < Integer.MAX_VALUE && length < Integer.MAX_VALUE &&
|
||||
offset > Integer.MIN_VALUE && length > Integer.MIN_VALUE) {
|
||||
offset <= Integer.MAX_VALUE && length <= Integer.MAX_VALUE &&
|
||||
offset >= Integer.MIN_VALUE && length >= Integer.MIN_VALUE) {
|
||||
checkBoundsSmall((int)offset, (int)length);
|
||||
} else {
|
||||
if (length < 0 ||
|
||||
} else if (this != NativeMemorySegmentImpl.EVERYTHING) { // oob not possible for everything segment
|
||||
if (
|
||||
length < 0 ||
|
||||
offset < 0 ||
|
||||
offset > this.length - length) { // careful of overflow
|
||||
throw outOfBoundException(offset, length);
|
||||
|
||||
@ -29,14 +29,23 @@ import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.SegmentAllocator;
|
||||
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) {
|
||||
this.segment = segment;
|
||||
private long sp = 0L;
|
||||
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) {
|
||||
@ -51,99 +60,37 @@ public abstract class ArenaAllocator implements SegmentAllocator {
|
||||
}
|
||||
}
|
||||
|
||||
void checkConfinementIfNeeded() {
|
||||
Thread ownerThread = scope().ownerThread();
|
||||
if (ownerThread != null && ownerThread != Thread.currentThread()) {
|
||||
throw new IllegalStateException("Attempt to allocate outside confinement thread");
|
||||
}
|
||||
public ResourceScope scope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
ResourceScope scope() {
|
||||
return segment.scope();
|
||||
private MemorySegment newSegment(long bytesSize, long bytesAlignment) {
|
||||
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 {
|
||||
|
||||
private static final long DEFAULT_BLOCK_SIZE = 4 * 1024;
|
||||
|
||||
public UnboundedArenaAllocator(ResourceScope scope) {
|
||||
super(MemorySegment.allocateNative(DEFAULT_BLOCK_SIZE, 1, scope));
|
||||
}
|
||||
|
||||
private MemorySegment newSegment(long size, long align) {
|
||||
return MemorySegment.allocateNative(size, align, segment.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;
|
||||
@Override
|
||||
public MemorySegment allocate(long bytesSize, long bytesAlignment) {
|
||||
// try to slice from current segment first...
|
||||
MemorySegment slice = trySlice(bytesSize, bytesAlignment);
|
||||
if (slice != null) {
|
||||
return slice;
|
||||
} else {
|
||||
long maxPossibleAllocationSize = bytesSize + bytesAlignment - 1;
|
||||
if (maxPossibleAllocationSize > blockSize) {
|
||||
// too big
|
||||
return newSegment(bytesSize, bytesAlignment);
|
||||
} else {
|
||||
long maxPossibleAllocationSize = bytesSize + bytesAlignment - 1;
|
||||
if (maxPossibleAllocationSize > DEFAULT_BLOCK_SIZE) {
|
||||
// too big
|
||||
return newSegment(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) {
|
||||
// allocate a new segment and slice from there
|
||||
sp = 0L;
|
||||
segment = newSegment(blockSize, 1L);
|
||||
slice = trySlice(bytesSize, bytesAlignment);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,9 +25,7 @@
|
||||
*/
|
||||
package jdk.internal.foreign;
|
||||
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
import static jdk.incubator.foreign.MemoryLayouts.ADDRESS;
|
||||
import static jdk.incubator.foreign.ValueLayout.ADDRESS;
|
||||
import static sun.security.action.GetPropertyAction.privilegedGetProperty;
|
||||
|
||||
public enum CABI {
|
||||
|
||||
@ -25,11 +25,11 @@
|
||||
|
||||
package jdk.internal.foreign;
|
||||
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
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.Reference;
|
||||
|
||||
/**
|
||||
* 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 int lockCount = 0;
|
||||
private int asyncReleaseCount = 0;
|
||||
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) {
|
||||
super(cleaner, new ConfinedResourceList());
|
||||
super(new ConfinedResourceList(), cleaner);
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
@ -64,18 +75,35 @@ final class ConfinedScope extends ResourceScopeImpl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandleImpl acquire() {
|
||||
@ForceInline
|
||||
public void acquire0() {
|
||||
checkValidState();
|
||||
if (lockCount == MAX_FORKS) {
|
||||
throw new IllegalStateException("Scope keep alive limit exceeded");
|
||||
}
|
||||
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() {
|
||||
this.checkValidState();
|
||||
if (lockCount == 0) {
|
||||
if (lockCount == 0 || lockCount - ((int)ASYNC_RELEASE_COUNT.getVolatile(this)) == 0) {
|
||||
closed = true;
|
||||
} 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--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,8 +28,6 @@ package jdk.internal.foreign;
|
||||
import jdk.incubator.foreign.MemoryHandles;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.internal.access.JavaLangInvokeAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.access.foreign.MemorySegmentProxy;
|
||||
|
||||
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)}).
|
||||
* 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
|
||||
* 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 {
|
||||
|
||||
private static final JavaLangInvokeAccess JLI = SharedSecrets.getJavaLangInvokeAccess();
|
||||
|
||||
private static final MethodHandle ADD_STRIDE;
|
||||
private static final MethodHandle MH_ADD_SCALED_OFFSET;
|
||||
private static final MethodHandle MH_SLICE;
|
||||
@ -156,8 +152,10 @@ public class LayoutPath {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public VarHandle dereferenceHandle(Class<?> carrier) {
|
||||
Utils.checkPrimitiveCarrierCompat(carrier, layout);
|
||||
public VarHandle dereferenceHandle() {
|
||||
if (!(layout instanceof ValueLayout valueLayout)) {
|
||||
throw new IllegalArgumentException("Path does not select a value layout");
|
||||
}
|
||||
checkAlignment(this);
|
||||
|
||||
List<Class<?>> expectedCoordinates = new ArrayList<>();
|
||||
@ -165,8 +163,7 @@ public class LayoutPath {
|
||||
perms.addFirst(0);
|
||||
expectedCoordinates.add(MemorySegment.class);
|
||||
|
||||
VarHandle handle = Utils.fixUpVarHandle(JLI.memoryAccessVarHandle(carrier, true, layout.byteAlignment() - 1,
|
||||
((ValueLayout)layout).order()));
|
||||
VarHandle handle = Utils.makeMemoryAccessVarHandle(valueLayout, true);
|
||||
|
||||
for (int i = 0 ; i < strides.length ; i++) {
|
||||
expectedCoordinates.add(long.class);
|
||||
@ -226,15 +223,13 @@ public class LayoutPath {
|
||||
MemoryLayout newLayout = op.apply(layout);
|
||||
if (enclosing == null) {
|
||||
return newLayout;
|
||||
} else if (enclosing.layout instanceof SequenceLayout) {
|
||||
SequenceLayout seq = (SequenceLayout)enclosing.layout;
|
||||
} else if (enclosing.layout instanceof SequenceLayout seq) {
|
||||
if (seq.elementCount().isPresent()) {
|
||||
return enclosing.map(l -> dup(l, MemoryLayout.sequenceLayout(seq.elementCount().getAsLong(), newLayout)));
|
||||
} else {
|
||||
return enclosing.map(l -> dup(l, MemoryLayout.sequenceLayout(newLayout)));
|
||||
}
|
||||
} else if (enclosing.layout instanceof GroupLayout) {
|
||||
GroupLayout g = (GroupLayout)enclosing.layout;
|
||||
} else if (enclosing.layout instanceof GroupLayout g) {
|
||||
List<MemoryLayout> newElements = new ArrayList<>(g.memberLayouts());
|
||||
//if we selected a layout in a group we must have a valid index
|
||||
newElements.set((int)elementIndex, newLayout);
|
||||
|
||||
@ -25,117 +25,62 @@
|
||||
*/
|
||||
package jdk.internal.foreign;
|
||||
|
||||
import jdk.incubator.foreign.Addressable;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
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.Reflection;
|
||||
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import java.util.Objects;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public final class MemoryAddressImpl implements MemoryAddress {
|
||||
public final class MemoryAddressImpl implements MemoryAddress, Scoped {
|
||||
|
||||
private final AbstractMemorySegmentImpl segment;
|
||||
private final long offset;
|
||||
|
||||
public MemoryAddressImpl(AbstractMemorySegmentImpl segment, long offset) {
|
||||
this.segment = segment;
|
||||
public MemoryAddressImpl(long offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
Object base() {
|
||||
return segment != null ? segment.base() : null;
|
||||
}
|
||||
|
||||
long offset() {
|
||||
return segment != null ?
|
||||
segment.min() + offset : offset;
|
||||
}
|
||||
|
||||
// MemoryAddress methods
|
||||
|
||||
@Override
|
||||
public ResourceScope scope() {
|
||||
return segment != null ?
|
||||
segment.scope() : ResourceScope.globalScope();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryAddress addOffset(long offset) {
|
||||
return new MemoryAddressImpl(segment, 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;
|
||||
return new MemoryAddressImpl(this.offset + offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long toRawLongValue() {
|
||||
if (segment != null) {
|
||||
if (segment.base() != null) {
|
||||
throw new UnsupportedOperationException("Not a native address");
|
||||
}
|
||||
segment.checkValidState();
|
||||
}
|
||||
return offset();
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final MemoryAddress address() {
|
||||
return this;
|
||||
}
|
||||
|
||||
// Object methods
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(base(), offset());
|
||||
return (int) toRawLongValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object that) {
|
||||
if (that instanceof MemoryAddressImpl) {
|
||||
MemoryAddressImpl addr = (MemoryAddressImpl)that;
|
||||
return Objects.equals(base(), addr.base()) &&
|
||||
offset() == addr.offset();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return (that instanceof MemoryAddressImpl addressImpl &&
|
||||
offset == addressImpl.offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MemoryAddress{ base: " + base() + " 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);
|
||||
return "MemoryAddress{ offset=0x" + Long.toHexString(offset) + " }";
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,8 +28,6 @@ package jdk.internal.foreign;
|
||||
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import jdk.incubator.foreign.SegmentAllocator;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
@ -43,12 +41,10 @@ import java.nio.ByteBuffer;
|
||||
*/
|
||||
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();
|
||||
|
||||
public static final SegmentAllocator IMPLICIT_ALLOCATOR = (size, align) -> MemorySegment.allocateNative(size, align, ResourceScope.newImplicitScope());
|
||||
|
||||
// The maximum alignment supported by malloc - typically 16 on
|
||||
// 64-bit platforms and 8 on 32-bit platforms.
|
||||
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;
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
@Override
|
||||
public MemoryAddress address() {
|
||||
checkValidState();
|
||||
return MemoryAddress.ofLong(unsafeGetOffset());
|
||||
}
|
||||
|
||||
@Override
|
||||
NativeMemorySegmentImpl dup(long offset, long size, int mask, ResourceScopeImpl scope) {
|
||||
return new NativeMemorySegmentImpl(min + offset, size, mask, scope);
|
||||
@ -123,12 +126,9 @@ public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl {
|
||||
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();
|
||||
AbstractMemorySegmentImpl segment = new NativeMemorySegmentImpl(min.toRawLongValue(), bytesSize, defaultAccessModes(bytesSize), scope);
|
||||
if (cleanupAction != null) {
|
||||
scope.addCloseAction(cleanupAction);
|
||||
}
|
||||
return segment;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.
|
||||
*
|
||||
* 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,
|
||||
* 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
|
||||
* 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 {
|
||||
long entryPoint();
|
||||
public record NativeSymbolImpl(String name, MemoryAddress address, ResourceScope scope) implements NativeSymbol, Scoped {
|
||||
@Override
|
||||
public MemoryAddress address() {
|
||||
((ResourceScopeImpl)scope).checkValidState();
|
||||
return address;
|
||||
}
|
||||
}
|
||||
@ -25,14 +25,9 @@
|
||||
*/
|
||||
package jdk.internal.foreign;
|
||||
|
||||
import jdk.incubator.foreign.CLinker;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.ValueLayout;
|
||||
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import static java.nio.ByteOrder.LITTLE_ENDIAN;
|
||||
|
||||
public class PlatformLayouts {
|
||||
public static <Z extends MemoryLayout> Z pick(Z sysv, Z win64, Z aarch64) {
|
||||
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.
|
||||
*/
|
||||
@ -103,50 +45,55 @@ public class PlatformLayouts {
|
||||
//just the one
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@code bool} native type.
|
||||
*/
|
||||
public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN;
|
||||
|
||||
/**
|
||||
* The {@code char} native type.
|
||||
*/
|
||||
public static final ValueLayout C_CHAR = ofChar(LITTLE_ENDIAN, 8);
|
||||
public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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
|
||||
* attribute value must be a boolean.
|
||||
* The {@code bool} native type.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
public static final MemoryLayout 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);
|
||||
}
|
||||
public static final ValueLayout.OfAddress C_VA_LIST = Win64.C_POINTER;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -228,69 +164,54 @@ public class PlatformLayouts {
|
||||
//just the one
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@code bool} native type.
|
||||
*/
|
||||
public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN;
|
||||
|
||||
/**
|
||||
* The {@code char} native type.
|
||||
*/
|
||||
public static final ValueLayout C_CHAR = ofChar(LITTLE_ENDIAN, 8);
|
||||
public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
public static final MemoryLayout 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);
|
||||
}
|
||||
public static final ValueLayout.OfAddress C_VA_LIST = AArch64.C_POINTER;
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import jdk.incubator.foreign.SegmentAllocator;
|
||||
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.Reference;
|
||||
@ -49,9 +49,12 @@ import java.util.Objects;
|
||||
* 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}).
|
||||
*/
|
||||
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 Cleaner.Cleanable cleanable;
|
||||
|
||||
static final int MAX_FORKS = Integer.MAX_VALUE;
|
||||
|
||||
@Override
|
||||
public void addCloseAction(Runnable runnable) {
|
||||
@ -59,11 +62,6 @@ public abstract non-sealed class ResourceScopeImpl implements ResourceScope, Sco
|
||||
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.
|
||||
* 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;
|
||||
if (cleaner != null) {
|
||||
cleaner.register(this, resourceList);
|
||||
}
|
||||
}
|
||||
|
||||
public static ResourceScopeImpl createImplicitScope() {
|
||||
return new ImplicitScopeImpl(CleanerFactory.cleaner());
|
||||
cleanable = (cleaner != null) ?
|
||||
cleaner.register(this, resourceList) : null;
|
||||
}
|
||||
|
||||
public static ResourceScopeImpl createConfined(Thread thread, Cleaner 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) {
|
||||
return new SharedScope(cleaner);
|
||||
}
|
||||
|
||||
private final void release0(HandleImpl handle) {
|
||||
try {
|
||||
Objects.requireNonNull(handle);
|
||||
if (handle.scope() != this) {
|
||||
throw new IllegalArgumentException("Cannot release an handle acquired from another scope");
|
||||
}
|
||||
handle.release();
|
||||
} finally {
|
||||
Reference.reachabilityFence(this);
|
||||
@Override
|
||||
public MemorySegment allocate(long bytesSize, long bytesAlignment) {
|
||||
return MemorySegment.allocateNative(bytesSize, bytesAlignment, this);
|
||||
}
|
||||
|
||||
public abstract void release0();
|
||||
|
||||
public abstract void acquire0();
|
||||
|
||||
@Override
|
||||
public void keepAlive(ResourceScope target) {
|
||||
Objects.requireNonNull(target);
|
||||
if (target == this) {
|
||||
throw new IllegalArgumentException("Invalid target scope.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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();
|
||||
ResourceScopeImpl targetImpl = (ResourceScopeImpl)target;
|
||||
targetImpl.acquire0();
|
||||
addCloseAction(targetImpl::release0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -167,7 +131,11 @@ public abstract non-sealed class ResourceScopeImpl implements ResourceScope, Sco
|
||||
public void close() {
|
||||
try {
|
||||
justClose();
|
||||
resourceList.cleanup();
|
||||
if (cleanable != null) {
|
||||
cleanable.clean();
|
||||
} else {
|
||||
resourceList.cleanup();
|
||||
}
|
||||
} finally {
|
||||
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
|
||||
public MemorySegment allocate(long bytesSize, long bytesAlignment) {
|
||||
return MemorySegment.allocateNative(bytesSize, bytesAlignment, this);
|
||||
}
|
||||
static class GlobalScopeImpl extends SharedScope {
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
final Object ref;
|
||||
|
||||
public ImplicitScopeImpl(Cleaner cleaner) {
|
||||
super(cleaner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandleImpl acquire() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isImplicit() {
|
||||
return true;
|
||||
public GlobalScopeImpl(Object ref) {
|
||||
super(null);
|
||||
this.ref = ref;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -249,27 +202,28 @@ public abstract non-sealed class ResourceScopeImpl implements ResourceScope, Sco
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
@ForceInline
|
||||
public void release0() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceScopeImpl scope() {
|
||||
return this;
|
||||
@ForceInline
|
||||
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
|
||||
void addInternal(ResourceList.ResourceCleanup resource) {
|
||||
// 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
|
||||
@ -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;
|
||||
|
||||
public abstract void cleanup();
|
||||
|
||||
@ -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();
|
||||
}
|
||||
@ -25,14 +25,12 @@
|
||||
|
||||
package jdk.internal.foreign;
|
||||
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import jdk.internal.misc.ScopedMemoryAccess;
|
||||
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.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
|
||||
@ -50,7 +48,6 @@ class SharedScope extends ResourceScopeImpl {
|
||||
private static final int ALIVE = 0;
|
||||
private static final int CLOSING = -1;
|
||||
private static final int CLOSED = -2;
|
||||
private static final int MAX_FORKS = Integer.MAX_VALUE;
|
||||
|
||||
private int state = ALIVE;
|
||||
|
||||
@ -65,7 +62,7 @@ class SharedScope extends ResourceScopeImpl {
|
||||
}
|
||||
|
||||
SharedScope(Cleaner cleaner) {
|
||||
super(cleaner, new SharedResourceList());
|
||||
super(new SharedResourceList(), cleaner);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -81,7 +78,8 @@ class SharedScope extends ResourceScopeImpl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandleImpl acquire() {
|
||||
@ForceInline
|
||||
public void acquire0() {
|
||||
int value;
|
||||
do {
|
||||
value = (int) STATE.getVolatile(this);
|
||||
@ -90,10 +88,22 @@ class SharedScope extends ResourceScopeImpl {
|
||||
throw new IllegalStateException("Already closed");
|
||||
} else if (value == MAX_FORKS) {
|
||||
//overflow
|
||||
throw new IllegalStateException("Segment acquire limit exceeded");
|
||||
throw new IllegalStateException("Scope keep alive limit exceeded");
|
||||
}
|
||||
} 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() {
|
||||
@ -101,7 +111,7 @@ class SharedScope extends ResourceScopeImpl {
|
||||
if (prevState < 0) {
|
||||
throw new IllegalStateException("Already closed");
|
||||
} 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);
|
||||
STATE.setVolatile(this, success ? CLOSED : ALIVE);
|
||||
@ -133,13 +143,13 @@ class SharedScope extends ResourceScopeImpl {
|
||||
@Override
|
||||
void add(ResourceCleanup cleanup) {
|
||||
while (true) {
|
||||
ResourceCleanup prev = (ResourceCleanup) FST.getAcquire(this);
|
||||
cleanup.next = prev;
|
||||
ResourceCleanup newSegment = (ResourceCleanup) FST.compareAndExchangeRelease(this, prev, cleanup);
|
||||
if (newSegment == ResourceCleanup.CLOSED_LIST) {
|
||||
ResourceCleanup prev = (ResourceCleanup) FST.getVolatile(this);
|
||||
if (prev == ResourceCleanup.CLOSED_LIST) {
|
||||
// too late
|
||||
throw new IllegalStateException("Already closed");
|
||||
} else if (newSegment == prev) {
|
||||
}
|
||||
cleanup.next = prev;
|
||||
if (FST.compareAndSet(this, prev, cleanup)) {
|
||||
return; //victory
|
||||
}
|
||||
// keep trying
|
||||
@ -155,9 +165,9 @@ class SharedScope extends ResourceScopeImpl {
|
||||
//ok now we're really closing down
|
||||
ResourceCleanup prev = null;
|
||||
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!
|
||||
if (FST.weakCompareAndSetRelease(this, prev, ResourceCleanup.CLOSED_LIST)) {
|
||||
if (FST.compareAndSet(this, prev, ResourceCleanup.CLOSED_LIST)) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,8 +25,8 @@
|
||||
|
||||
package jdk.internal.foreign;
|
||||
|
||||
import jdk.incubator.foreign.MemoryAccess;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.NativeSymbol;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import jdk.incubator.foreign.SymbolLookup;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
@ -39,13 +39,13 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
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 {
|
||||
|
||||
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
|
||||
@ -71,11 +71,11 @@ public class SystemLookup implements SymbolLookup {
|
||||
SymbolLookup fallbackLibLookup = libLookup(libs -> libs.loadLibrary("WinFallbackLookup"));
|
||||
|
||||
int numSymbols = WindowsFallbackSymbols.values().length;
|
||||
MemorySegment funcs = fallbackLibLookup.lookup("funcs").orElseThrow()
|
||||
.asSegment(C_POINTER.byteSize() * numSymbols, ResourceScope.newImplicitScope());
|
||||
MemorySegment funcs = MemorySegment.ofAddress(fallbackLibLookup.lookup("funcs").orElseThrow().address(),
|
||||
ADDRESS.byteSize() * numSymbols, ResourceScope.globalScope());
|
||||
|
||||
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;
|
||||
lookup = name -> finalLookup.lookup(name).or(() -> fallbackLookup.lookup(name));
|
||||
@ -91,7 +91,8 @@ public class SystemLookup implements SymbolLookup {
|
||||
try {
|
||||
long addr = lib.lookup(name);
|
||||
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) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@ -99,7 +100,7 @@ public class SystemLookup implements SymbolLookup {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<MemoryAddress> lookup(String name) {
|
||||
public Optional<NativeSymbol> lookup(String name) {
|
||||
return syslookup.lookup(name);
|
||||
}
|
||||
|
||||
|
||||
@ -27,17 +27,19 @@
|
||||
package jdk.internal.foreign;
|
||||
|
||||
import jdk.incubator.foreign.*;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.access.foreign.MemorySegmentProxy;
|
||||
import jdk.internal.misc.VM;
|
||||
import sun.invoke.util.Wrapper;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.Optional;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE;
|
||||
import static sun.security.action.GetPropertyAction.*;
|
||||
|
||||
/**
|
||||
@ -49,6 +51,10 @@ public final class Utils {
|
||||
= Boolean.parseBoolean(privilegedGetProperty("jdk.internal.foreign.SHOULD_ADAPT_HANDLES", "true"));
|
||||
|
||||
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 Supplier<RuntimeException> bitsToBytesThrowOffset
|
||||
@ -59,6 +65,14 @@ public final class Utils {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
SEGMENT_FILTER = lookup.findStatic(Utils.class, "filterSegment",
|
||||
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(
|
||||
lookup.findStatic(Utils.class, "bitsToBytesOrThrow",
|
||||
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,
|
||||
// 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)
|
||||
: 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) {
|
||||
return (AbstractMemorySegmentImpl)segment;
|
||||
}
|
||||
|
||||
public static void checkPrimitiveCarrierCompat(Class<?> carrier, MemoryLayout layout) {
|
||||
checkLayoutType(layout, ValueLayout.class);
|
||||
if (!isValidPrimitiveCarrier(carrier))
|
||||
throw new IllegalArgumentException("Unsupported carrier: " + carrier);
|
||||
if (Wrapper.forPrimitiveType(carrier).bitWidth() != layout.bitSize())
|
||||
throw new IllegalArgumentException("Carrier size mismatch: " + carrier + " != " + layout);
|
||||
private static boolean byteToBoolean(byte b) {
|
||||
return b != 0;
|
||||
}
|
||||
|
||||
public static boolean isValidPrimitiveCarrier(Class<?> carrier) {
|
||||
return carrier == byte.class
|
||||
|| carrier == short.class
|
||||
|| carrier == char.class
|
||||
|| carrier == int.class
|
||||
|| carrier == long.class
|
||||
|| carrier == float.class
|
||||
|| carrier == double.class;
|
||||
private static byte booleanToByte(boolean b) {
|
||||
return b ? (byte)1 : (byte)0;
|
||||
}
|
||||
|
||||
public static void checkLayoutType(MemoryLayout layout, Class<? extends MemoryLayout> layoutType) {
|
||||
if (!layoutType.isInstance(layout))
|
||||
throw new IllegalArgumentException("Expected a " + layoutType.getSimpleName() + ": " + layout);
|
||||
public static void copy(MemorySegment addr, byte[] bytes) {
|
||||
var heapSegment = MemorySegment.ofArray(bytes);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,17 +24,20 @@
|
||||
*/
|
||||
package jdk.internal.foreign.abi;
|
||||
|
||||
import jdk.incubator.foreign.Addressable;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryHandles;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import jdk.incubator.foreign.SegmentAllocator;
|
||||
import jdk.incubator.foreign.ValueLayout;
|
||||
import jdk.internal.foreign.MemoryAddressImpl;
|
||||
import jdk.internal.foreign.ResourceScopeImpl;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
@ -204,7 +207,6 @@ import static java.lang.invoke.MethodType.methodType;
|
||||
public abstract class Binding {
|
||||
private static final MethodHandle MH_UNBOX_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_ALLOCATE_BUFFER;
|
||||
private static final MethodHandle MH_TO_SEGMENT;
|
||||
@ -216,8 +218,6 @@ public abstract class Binding {
|
||||
methodType(long.class));
|
||||
MH_BOX_ADDRESS = lookup.findStatic(MemoryAddress.class, "ofLong",
|
||||
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",
|
||||
methodType(MemorySegment.class, MemorySegment.class, long.class, long.class, Context.class));
|
||||
MH_ALLOCATE_BUFFER = lookup.findStatic(Binding.Allocate.class, "allocateBuffer",
|
||||
@ -262,7 +262,7 @@ public abstract class Binding {
|
||||
*/
|
||||
public static Context ofBoundedAllocator(long size) {
|
||||
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,
|
||||
BOX_ADDRESS,
|
||||
UNBOX_ADDRESS,
|
||||
BASE_ADDRESS,
|
||||
TO_SEGMENT,
|
||||
DUP
|
||||
}
|
||||
@ -344,7 +343,7 @@ public abstract class Binding {
|
||||
public abstract MethodHandle specialize(MethodHandle specializedHandle, int insertPos, int allocatorPos);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -388,11 +387,11 @@ public abstract class Binding {
|
||||
}
|
||||
|
||||
public static UnboxAddress unboxAddress() {
|
||||
return UnboxAddress.INSTANCE;
|
||||
return UnboxAddress.INSTANCE.get(MemoryAddress.class);
|
||||
}
|
||||
|
||||
public static BaseAddress baseAddress() {
|
||||
return BaseAddress.INSTANCE;
|
||||
public static UnboxAddress unboxAddress(Class<?> carrier) {
|
||||
return UnboxAddress.INSTANCE.get(carrier);
|
||||
}
|
||||
|
||||
public static ToSegment toSegment(MemoryLayout layout) {
|
||||
@ -467,8 +466,8 @@ public abstract class Binding {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Binding.Builder baseAddress() {
|
||||
bindings.add(Binding.baseAddress());
|
||||
public Binding.Builder unboxAddress(Class<?> carrier) {
|
||||
bindings.add(Binding.unboxAddress(carrier));
|
||||
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 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 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
|
||||
// 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)
|
||||
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,
|
||||
Context context) {
|
||||
MemorySegment copy = context.allocator().allocate(size, alignment);
|
||||
copy.copyFrom(operand.asSlice(0, size));
|
||||
return copy;
|
||||
return context.allocator().allocate(size, alignment)
|
||||
.copyFrom(operand.asSlice(0, size));
|
||||
}
|
||||
|
||||
public long size() {
|
||||
@ -875,27 +874,44 @@ public abstract class Binding {
|
||||
* and pushes that onto the operand stack.
|
||||
*/
|
||||
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);
|
||||
this.carrier = carrier;
|
||||
try {
|
||||
this.toAddress = MethodHandles.lookup().findVirtual(carrier, "address", MethodType.methodType(MemoryAddress.class));
|
||||
} catch (Throwable ex) {
|
||||
throw new IllegalArgumentException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verify(Deque<Class<?>> stack) {
|
||||
Class<?> actualType = stack.pop();
|
||||
SharedUtils.checkType(actualType, MemoryAddress.class);
|
||||
SharedUtils.checkType(actualType, carrier);
|
||||
stack.push(long.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
|
||||
BindingInterpreter.LoadFunc loadFunc, Context context) {
|
||||
stack.push(((MemoryAddress)stack.pop()).toRawLongValue());
|
||||
stack.push(((Addressable)stack.pop()).address().toRawLongValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
@ -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])
|
||||
* Pops a MemoryAddress from the operand stack, and converts it to a MemorySegment
|
||||
|
||||
@ -24,8 +24,8 @@
|
||||
*/
|
||||
package jdk.internal.foreign.abi;
|
||||
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.ValueLayout;
|
||||
import jdk.internal.foreign.MemoryAddressImpl;
|
||||
|
||||
import java.io.PrintStream;
|
||||
@ -34,7 +34,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
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 arguments_next_pc;
|
||||
|
||||
@ -28,7 +28,6 @@ import jdk.incubator.foreign.FunctionDescriptor;
|
||||
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class CallingSequence {
|
||||
|
||||
@ -100,7 +100,6 @@ public class CallingSequenceBuilder {
|
||||
//ALLOC_BUFFER,
|
||||
//BOX_ADDRESS,
|
||||
UNBOX_ADDRESS,
|
||||
BASE_ADDRESS,
|
||||
//TO_SEGMENT,
|
||||
DUP
|
||||
);
|
||||
@ -129,7 +128,6 @@ public class CallingSequenceBuilder {
|
||||
ALLOC_BUFFER,
|
||||
BOX_ADDRESS,
|
||||
//UNBOX_ADDRESS,
|
||||
//BASE_ADDRESS,
|
||||
TO_SEGMENT,
|
||||
DUP
|
||||
);
|
||||
|
||||
@ -25,11 +25,11 @@
|
||||
package jdk.internal.foreign.abi;
|
||||
|
||||
import jdk.incubator.foreign.Addressable;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.NativeSymbol;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import jdk.incubator.foreign.SegmentAllocator;
|
||||
import jdk.incubator.foreign.ValueLayout;
|
||||
import jdk.internal.access.JavaLangInvokeAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.invoke.NativeEntryPoint;
|
||||
@ -40,7 +40,6 @@ import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.lang.ref.Reference;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -70,7 +69,7 @@ public class ProgrammableInvoker {
|
||||
|
||||
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_INTERP_BINDINGS;
|
||||
@ -87,10 +86,10 @@ public class ProgrammableInvoker {
|
||||
MH_INVOKE_MOVES = lookup.findVirtual(ProgrammableInvoker.class, "invokeMoves",
|
||||
methodType(Object.class, long.class, Object[].class, Binding.VMStore[].class, Binding.VMLoad[].class));
|
||||
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",
|
||||
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) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@ -172,9 +171,9 @@ public class ProgrammableInvoker {
|
||||
return handle;
|
||||
}
|
||||
|
||||
private static long unboxTargetAddress(Addressable addr) {
|
||||
MemoryAddress ma = SharedUtils.checkSymbol(addr);
|
||||
return ma.toRawLongValue();
|
||||
private static long unboxTargetAddress(NativeSymbol addr) {
|
||||
SharedUtils.checkSymbol(addr);
|
||||
return addr.address().toRawLongValue();
|
||||
}
|
||||
|
||||
// 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> retIndexMap) throws Throwable {
|
||||
Binding.Context unboxContext = bufferCopySize != 0
|
||||
@ -321,21 +320,17 @@ public class ProgrammableInvoker {
|
||||
try (unboxContext) {
|
||||
// do argument processing, get Object[] as result
|
||||
Object[] leafArgs = new Object[leaf.type().parameterCount()];
|
||||
leafArgs[0] = address; // addr
|
||||
leafArgs[0] = symbol; // symbol
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
Object arg = args[i];
|
||||
BindingInterpreter.unbox(arg, callingSequence.argumentBindings(i),
|
||||
(storage, type, value) -> {
|
||||
leafArgs[argIndexMap.get(storage) + 1] = value; // +1 to skip addr
|
||||
leafArgs[argIndexMap.get(storage) + 1] = value; // +1 to skip symbol
|
||||
}, unboxContext);
|
||||
}
|
||||
|
||||
// call leaf
|
||||
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
|
||||
if (o == null) {
|
||||
|
||||
@ -26,10 +26,10 @@
|
||||
package jdk.internal.foreign.abi;
|
||||
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.NativeSymbol;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import jdk.incubator.foreign.SegmentAllocator;
|
||||
import jdk.incubator.foreign.ValueLayout;
|
||||
import jdk.internal.access.JavaLangInvokeAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.foreign.MemoryAddressImpl;
|
||||
@ -46,6 +46,7 @@ import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
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.identity;
|
||||
import static java.lang.invoke.MethodHandles.insertArguments;
|
||||
@ -69,7 +70,7 @@ public class ProgrammableUpcallHandler {
|
||||
|
||||
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_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.VMStore[] retMoves = retMoveBindings(callingSequence);
|
||||
|
||||
@ -134,7 +135,7 @@ public class ProgrammableUpcallHandler {
|
||||
MethodHandle invokeMoves = insertArguments(MH_invokeMoves, 1, doBindingsErased, argMoves, retMoves, abi, layout);
|
||||
entryPoint = allocateUpcallStub(invokeMoves, abi, layout);
|
||||
}
|
||||
return () -> entryPoint;
|
||||
return UpcallStubs.makeUpcall(entryPoint, scope);
|
||||
}
|
||||
|
||||
private static void checkPrimitive(MethodType type) {
|
||||
|
||||
@ -25,40 +25,45 @@
|
||||
package jdk.internal.foreign.abi;
|
||||
|
||||
import jdk.incubator.foreign.Addressable;
|
||||
import jdk.incubator.foreign.CLinker;
|
||||
import jdk.incubator.foreign.FunctionDescriptor;
|
||||
import jdk.incubator.foreign.GroupLayout;
|
||||
import jdk.incubator.foreign.MemoryAccess;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryHandles;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.NativeSymbol;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import jdk.incubator.foreign.SegmentAllocator;
|
||||
import jdk.incubator.foreign.SequenceLayout;
|
||||
import jdk.incubator.foreign.CLinker;
|
||||
import jdk.incubator.foreign.VaList;
|
||||
import jdk.incubator.foreign.ValueLayout;
|
||||
import jdk.internal.access.JavaLangAccess;
|
||||
import jdk.internal.access.JavaLangInvokeAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.foreign.Scoped;
|
||||
import jdk.internal.foreign.CABI;
|
||||
import jdk.internal.foreign.MemoryAddressImpl;
|
||||
import jdk.internal.foreign.ResourceScopeImpl;
|
||||
import jdk.internal.foreign.Utils;
|
||||
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker;
|
||||
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker;
|
||||
import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
|
||||
import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.lang.ref.Reference;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.stream.Collectors;
|
||||
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.dropReturn;
|
||||
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.insertArguments;
|
||||
import static java.lang.invoke.MethodHandles.permuteArguments;
|
||||
import static java.lang.invoke.MethodHandles.tryFinally;
|
||||
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 {
|
||||
|
||||
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_BASEADDRESS;
|
||||
@ -87,6 +101,8 @@ public class SharedUtils {
|
||||
private static final MethodHandle MH_CLOSE_CONTEXT;
|
||||
private static final MethodHandle MH_REACHBILITY_FENCE;
|
||||
private static final MethodHandle MH_HANDLE_UNCAUGHT_EXCEPTION;
|
||||
private static final MethodHandle ACQUIRE_MH;
|
||||
private static final MethodHandle RELEASE_MH;
|
||||
|
||||
static {
|
||||
try {
|
||||
@ -107,13 +123,19 @@ public class SharedUtils {
|
||||
methodType(void.class, Object.class));
|
||||
MH_HANDLE_UNCAUGHT_EXCEPTION = lookup.findStatic(SharedUtils.class, "handleUncaughtException",
|
||||
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) {
|
||||
throw new BootstrapMethodError(e);
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -225,34 +247,6 @@ public class SharedUtils {
|
||||
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) {
|
||||
if (useFloat) {
|
||||
if (size == 4) {
|
||||
@ -287,15 +281,14 @@ public class SharedUtils {
|
||||
public static String toJavaStringInternal(MemorySegment segment, long start) {
|
||||
int len = strlen(segment, start);
|
||||
byte[] bytes = new byte[len];
|
||||
MemorySegment.ofArray(bytes)
|
||||
.copyFrom(segment.asSlice(start, len));
|
||||
MemorySegment.copy(segment, JAVA_BYTE, start, bytes, 0, len);
|
||||
return new String(bytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
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)
|
||||
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) {
|
||||
return offset;
|
||||
}
|
||||
@ -392,31 +385,21 @@ public class SharedUtils {
|
||||
insertPos = 1;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
// downcalls get the leading Addressable/SegmentAllocator param as well
|
||||
// downcalls get the leading NativeSymbol/SegmentAllocator param as well
|
||||
if (!upcall) {
|
||||
closer = collectArguments(closer, insertPos++, reachabilityFenceHandle(Addressable.class));
|
||||
closer = dropArguments(closer, insertPos++, SegmentAllocator.class); // (Throwable, V?, Addressable, SegmentAllocator) -> V/void
|
||||
closer = collectArguments(closer, insertPos++, reachabilityFenceHandle(NativeSymbol.class));
|
||||
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
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
closer = collectArguments(closer, insertPos++, MH_CLOSE_CONTEXT); // (Throwable, V?, NativeSymbol?, BindingContext) -> V/void
|
||||
|
||||
MethodHandle contextFactory;
|
||||
|
||||
@ -434,34 +417,171 @@ public class SharedUtils {
|
||||
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
|
||||
private static class AllocHolder {
|
||||
|
||||
private static final CLinker SYS_LINKER = getSystemLinker();
|
||||
|
||||
static final MethodHandle MH_MALLOC = SYS_LINKER.downcallHandle(CLinker.systemLookup().lookup("malloc").get(),
|
||||
MethodType.methodType(MemoryAddress.class, long.class),
|
||||
FunctionDescriptor.of(C_POINTER, C_LONG_LONG));
|
||||
static final MethodHandle MH_MALLOC = SYS_LINKER.downcallHandle(CLinker.systemCLinker().lookup("malloc").get(),
|
||||
FunctionDescriptor.of(ADDRESS, JAVA_LONG));
|
||||
|
||||
static final MethodHandle MH_FREE = SYS_LINKER.downcallHandle(CLinker.systemLookup().lookup("free").get(),
|
||||
MethodType.methodType(void.class, MemoryAddress.class),
|
||||
FunctionDescriptor.ofVoid(C_POINTER));
|
||||
static final MethodHandle MH_FREE = SYS_LINKER.downcallHandle(CLinker.systemCLinker().lookup("free").get(),
|
||||
FunctionDescriptor.ofVoid(ADDRESS));
|
||||
}
|
||||
|
||||
public static MemoryAddress checkSymbol(Addressable symbol) {
|
||||
return checkAddressable(symbol, "Symbol is NULL");
|
||||
public static void checkSymbol(NativeSymbol symbol) {
|
||||
checkAddressable(symbol, "Symbol is NULL");
|
||||
}
|
||||
|
||||
public static MemoryAddress checkAddress(MemoryAddress address) {
|
||||
return checkAddressable(address, "Address is NULL");
|
||||
public static void checkAddress(MemoryAddress address) {
|
||||
checkAddressable(address, "Address is NULL");
|
||||
}
|
||||
|
||||
private static MemoryAddress checkAddressable(Addressable symbol, String msg) {
|
||||
private static void checkAddressable(Addressable symbol, String msg) {
|
||||
Objects.requireNonNull(symbol);
|
||||
MemoryAddress symbolAddr = symbol.address();
|
||||
if (symbolAddr.equals(MemoryAddress.NULL))
|
||||
throw new IllegalArgumentException("Symbol is NULL: " + symbolAddr);
|
||||
return symbolAddr;
|
||||
if (symbol.address().toRawLongValue() == 0)
|
||||
throw new IllegalArgumentException("Symbol is NULL: " + symbol);
|
||||
}
|
||||
|
||||
public static MemoryAddress allocateMemoryInternal(long size) {
|
||||
@ -474,7 +594,7 @@ public class SharedUtils {
|
||||
|
||||
public static void freeMemoryInternal(MemoryAddress addr) {
|
||||
try {
|
||||
AllocHolder.MH_FREE.invokeExact(addr);
|
||||
AllocHolder.MH_FREE.invokeExact((Addressable)addr);
|
||||
} catch (Throwable 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) {
|
||||
return switch (CABI.current()) {
|
||||
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) {
|
||||
if (expectedType != actualType) {
|
||||
throw new IllegalArgumentException(
|
||||
@ -549,9 +635,12 @@ public class SharedUtils {
|
||||
}
|
||||
|
||||
public static boolean isTrivial(FunctionDescriptor cDesc) {
|
||||
return cDesc.attribute(FunctionDescriptor.TRIVIAL_ATTRIBUTE_NAME)
|
||||
.map(Boolean.class::cast)
|
||||
.orElse(false);
|
||||
return false; // FIXME: use system property?
|
||||
}
|
||||
|
||||
public static boolean isVarargsIndex(FunctionDescriptor descriptor, int argIndex) {
|
||||
int firstPos = descriptor.firstVariadicArgumentIndex();
|
||||
return firstPos != -1 && argIndex >= firstPos;
|
||||
}
|
||||
|
||||
public static class SimpleVaArg {
|
||||
@ -566,13 +655,11 @@ public class SharedUtils {
|
||||
}
|
||||
|
||||
public VarHandle varHandle() {
|
||||
return carrier == MemoryAddress.class
|
||||
? MemoryHandles.asAddressVarHandle(layout.varHandle(primitiveCarrierForSize(layout.byteSize(), false)))
|
||||
: layout.varHandle(carrier);
|
||||
return layout.varHandle();
|
||||
}
|
||||
}
|
||||
|
||||
public static non-sealed class EmptyVaList implements VaList {
|
||||
public static non-sealed class EmptyVaList implements VaList, Scoped {
|
||||
|
||||
private final MemoryAddress address;
|
||||
|
||||
@ -585,32 +672,27 @@ public class SharedUtils {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int vargAsInt(MemoryLayout layout) {
|
||||
public int nextVarg(ValueLayout.OfInt layout) {
|
||||
throw uoe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long vargAsLong(MemoryLayout layout) {
|
||||
public long nextVarg(ValueLayout.OfLong layout) {
|
||||
throw uoe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double vargAsDouble(MemoryLayout layout) {
|
||||
public double nextVarg(ValueLayout.OfDouble layout) {
|
||||
throw uoe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryAddress vargAsAddress(MemoryLayout layout) {
|
||||
public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
|
||||
throw uoe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment vargAsSegment(MemoryLayout layout, SegmentAllocator allocator) {
|
||||
throw uoe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment vargAsSegment(MemoryLayout layout, ResourceScope scope) {
|
||||
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
|
||||
throw uoe();
|
||||
}
|
||||
|
||||
@ -638,19 +720,21 @@ public class SharedUtils {
|
||||
static void writeOverSized(MemorySegment ptr, Class<?> type, Object o) {
|
||||
// use VH_LONG for integers to zero out the whole register in the process
|
||||
if (type == long.class) {
|
||||
MemoryAccess.setLong(ptr, (long) o);
|
||||
ptr.set(JAVA_LONG, 0, (long) o);
|
||||
} else if (type == int.class) {
|
||||
MemoryAccess.setLong(ptr, (int) o);
|
||||
ptr.set(JAVA_LONG, 0, (int) o);
|
||||
} else if (type == short.class) {
|
||||
MemoryAccess.setLong(ptr, (short) o);
|
||||
ptr.set(JAVA_LONG, 0, (short) o);
|
||||
} else if (type == char.class) {
|
||||
MemoryAccess.setLong(ptr, (char) o);
|
||||
ptr.set(JAVA_LONG, 0, (char) o);
|
||||
} else if (type == byte.class) {
|
||||
MemoryAccess.setLong(ptr, (byte) o);
|
||||
ptr.set(JAVA_LONG, 0, (byte) o);
|
||||
} else if (type == float.class) {
|
||||
MemoryAccess.setFloat(ptr, (float) o);
|
||||
ptr.set(JAVA_FLOAT, 0, (float) o);
|
||||
} 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 {
|
||||
throw new IllegalArgumentException("Unsupported carrier: " + type);
|
||||
}
|
||||
@ -658,19 +742,21 @@ public class SharedUtils {
|
||||
|
||||
static void write(MemorySegment ptr, Class<?> type, Object o) {
|
||||
if (type == long.class) {
|
||||
MemoryAccess.setLong(ptr, (long) o);
|
||||
ptr.set(JAVA_LONG, 0, (long) o);
|
||||
} else if (type == int.class) {
|
||||
MemoryAccess.setInt(ptr, (int) o);
|
||||
ptr.set(JAVA_INT, 0, (int) o);
|
||||
} else if (type == short.class) {
|
||||
MemoryAccess.setShort(ptr, (short) o);
|
||||
ptr.set(JAVA_SHORT, 0, (short) o);
|
||||
} else if (type == char.class) {
|
||||
MemoryAccess.setChar(ptr, (char) o);
|
||||
ptr.set(JAVA_CHAR, 0, (char) o);
|
||||
} else if (type == byte.class) {
|
||||
MemoryAccess.setByte(ptr, (byte) o);
|
||||
ptr.set(JAVA_BYTE, 0, (byte) o);
|
||||
} else if (type == float.class) {
|
||||
MemoryAccess.setFloat(ptr, (float) o);
|
||||
ptr.set(JAVA_FLOAT, 0, (float) o);
|
||||
} 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 {
|
||||
throw new IllegalArgumentException("Unsupported carrier: " + type);
|
||||
}
|
||||
@ -678,21 +764,43 @@ public class SharedUtils {
|
||||
|
||||
static Object read(MemorySegment ptr, Class<?> type) {
|
||||
if (type == long.class) {
|
||||
return MemoryAccess.getLong(ptr);
|
||||
return ptr.get(JAVA_LONG, 0);
|
||||
} else if (type == int.class) {
|
||||
return MemoryAccess.getInt(ptr);
|
||||
return ptr.get(JAVA_INT, 0);
|
||||
} else if (type == short.class) {
|
||||
return MemoryAccess.getShort(ptr);
|
||||
return ptr.get(JAVA_SHORT, 0);
|
||||
} else if (type == char.class) {
|
||||
return MemoryAccess.getChar(ptr);
|
||||
return ptr.get(JAVA_CHAR, 0);
|
||||
} else if (type == byte.class) {
|
||||
return MemoryAccess.getByte(ptr);
|
||||
return ptr.get(JAVA_BYTE, 0);
|
||||
} else if (type == float.class) {
|
||||
return MemoryAccess.getFloat(ptr);
|
||||
return ptr.get(JAVA_FLOAT, 0);
|
||||
} 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 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,18 +25,13 @@
|
||||
package jdk.internal.foreign.abi;
|
||||
|
||||
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.NativeMemorySegmentImpl;
|
||||
|
||||
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) {
|
||||
if (!freeUpcallStub0(stubAddress)) {
|
||||
throw new IllegalStateException("Not a stub address: " + stubAddress);
|
||||
@ -52,4 +47,14 @@ public class UpcallStubs {
|
||||
static {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,9 +30,10 @@ import jdk.incubator.foreign.GroupLayout;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.NativeSymbol;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import jdk.internal.foreign.Utils;
|
||||
import jdk.internal.foreign.abi.CallingSequenceBuilder;
|
||||
import jdk.internal.foreign.abi.UpcallHandler;
|
||||
import jdk.internal.foreign.abi.ABIDescriptor;
|
||||
import jdk.internal.foreign.abi.Binding;
|
||||
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.VMStorage;
|
||||
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.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.
|
||||
*
|
||||
* 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;
|
||||
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) {
|
||||
SharedUtils.checkFunctionTypes(mt, cDesc, AArch64.C_POINTER.bitSize());
|
||||
public static final CallArranger LINUX = new LinuxAArch64CallArranger();
|
||||
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);
|
||||
|
||||
BindingCalculator argCalc = forUpcall ? new BoxBindingCalculator(true) : new UnboxBindingCalculator(true);
|
||||
@ -116,6 +134,9 @@ public class CallArranger {
|
||||
for (int i = 0; i < mt.parameterCount(); i++) {
|
||||
Class<?> carrier = mt.parameterType(i);
|
||||
MemoryLayout layout = cDesc.argumentLayouts().get(i);
|
||||
if (varArgsOnStack() && SharedUtils.isVarargsIndex(cDesc, i)) {
|
||||
argCalc.storageCalculator.adjustForVarArgs();
|
||||
}
|
||||
csb.addArgumentBindings(carrier, layout, argCalc.getBindings(carrier, layout));
|
||||
}
|
||||
|
||||
@ -124,7 +145,7 @@ public class CallArranger {
|
||||
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);
|
||||
|
||||
MethodHandle handle = new ProgrammableInvoker(C, bindings.callingSequence).getBoundMethodHandle();
|
||||
@ -136,14 +157,14 @@ public class CallArranger {
|
||||
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);
|
||||
|
||||
if (bindings.isInMemoryReturn) {
|
||||
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) {
|
||||
@ -208,18 +229,15 @@ public class CallArranger {
|
||||
return storage[0];
|
||||
}
|
||||
|
||||
void adjustForVarArgs(MemoryLayout layout) {
|
||||
if (layout.attribute(AArch64.STACK_VARARGS_ATTRIBUTE_NAME)
|
||||
.map(Boolean.class::cast).orElse(false)) {
|
||||
// This system passes all variadic parameters on the stack. Ensure
|
||||
// no further arguments are allocated to registers.
|
||||
nRegs[StorageClasses.INTEGER] = MAX_REGISTER_ARGUMENTS;
|
||||
nRegs[StorageClasses.VECTOR] = MAX_REGISTER_ARGUMENTS;
|
||||
}
|
||||
void adjustForVarArgs() {
|
||||
// This system passes all variadic parameters on the stack. Ensure
|
||||
// no further arguments are allocated to registers.
|
||||
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 BindingCalculator(boolean forArguments) {
|
||||
@ -288,7 +306,6 @@ public class CallArranger {
|
||||
List<Binding> getBindings(Class<?> carrier, MemoryLayout layout) {
|
||||
TypeClass argumentClass = TypeClass.classifyLayout(layout);
|
||||
Binding.Builder bindings = Binding.builder();
|
||||
storageCalculator.adjustForVarArgs(layout);
|
||||
switch (argumentClass) {
|
||||
case STRUCT_REGISTER: {
|
||||
assert carrier == MemorySegment.class;
|
||||
@ -317,8 +334,7 @@ public class CallArranger {
|
||||
case STRUCT_REFERENCE: {
|
||||
assert carrier == MemorySegment.class;
|
||||
bindings.copy(layout)
|
||||
.baseAddress()
|
||||
.unboxAddress();
|
||||
.unboxAddress(MemorySegment.class);
|
||||
VMStorage storage = storageCalculator.nextStorage(
|
||||
StorageClasses.INTEGER, AArch64.C_POINTER);
|
||||
bindings.vmStore(storage, long.class);
|
||||
@ -349,7 +365,7 @@ public class CallArranger {
|
||||
break;
|
||||
}
|
||||
case POINTER: {
|
||||
bindings.unboxAddress();
|
||||
bindings.unboxAddress(carrier);
|
||||
VMStorage storage =
|
||||
storageCalculator.nextStorage(StorageClasses.INTEGER, layout);
|
||||
bindings.vmStore(storage, long.class);
|
||||
@ -391,7 +407,6 @@ public class CallArranger {
|
||||
List<Binding> getBindings(Class<?> carrier, MemoryLayout layout) {
|
||||
TypeClass argumentClass = TypeClass.classifyLayout(layout);
|
||||
Binding.Builder bindings = Binding.builder();
|
||||
assert !layout.attribute(AArch64.STACK_VARARGS_ATTRIBUTE_NAME).isPresent() : "no variadic upcalls";
|
||||
switch (argumentClass) {
|
||||
case STRUCT_REGISTER: {
|
||||
assert carrier == MemorySegment.class;
|
||||
|
||||
@ -26,10 +26,10 @@
|
||||
package jdk.internal.foreign.abi.aarch64;
|
||||
|
||||
import jdk.incubator.foreign.GroupLayout;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.SequenceLayout;
|
||||
import jdk.incubator.foreign.ValueLayout;
|
||||
import jdk.internal.foreign.PlatformLayouts;
|
||||
|
||||
public enum TypeClass {
|
||||
STRUCT_REGISTER,
|
||||
@ -42,11 +42,17 @@ public enum TypeClass {
|
||||
private static final int MAX_AGGREGATE_REGS_SIZE = 2;
|
||||
|
||||
private static TypeClass classifyValueType(ValueLayout type) {
|
||||
return switch (PlatformLayouts.getKind(type)) {
|
||||
case CHAR, SHORT, INT, LONG, LONG_LONG -> INTEGER;
|
||||
case POINTER -> POINTER;
|
||||
case FLOAT, DOUBLE -> FLOAT;
|
||||
};
|
||||
Class<?> carrier = type.carrier();
|
||||
if (carrier == boolean.class || carrier == byte.class || carrier == char.class ||
|
||||
carrier == short.class || carrier == int.class || carrier == long.class) {
|
||||
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) {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -25,14 +25,14 @@
|
||||
*/
|
||||
package jdk.internal.foreign.abi.aarch64.linux;
|
||||
|
||||
import jdk.incubator.foreign.CLinker;
|
||||
import jdk.incubator.foreign.FunctionDescriptor;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.NativeSymbol;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import jdk.internal.foreign.AbstractCLinker;
|
||||
import jdk.internal.foreign.ResourceScopeImpl;
|
||||
import jdk.incubator.foreign.VaList;
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
import jdk.internal.foreign.abi.UpcallStubs;
|
||||
import jdk.internal.foreign.abi.aarch64.CallArranger;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
@ -45,26 +45,11 @@ import java.util.function.Consumer;
|
||||
* ABI implementation based on ARM document "Procedure Call Standard for
|
||||
* the ARM 64-bit Architecture".
|
||||
*/
|
||||
public final class LinuxAArch64Linker extends AbstractCLinker {
|
||||
public final class LinuxAArch64Linker implements CLinker {
|
||||
private static LinuxAArch64Linker instance;
|
||||
|
||||
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() {
|
||||
if (instance == null) {
|
||||
instance = new LinuxAArch64Linker();
|
||||
@ -73,26 +58,28 @@ public final class LinuxAArch64Linker extends AbstractCLinker {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final MethodHandle downcallHandle(MethodType type, FunctionDescriptor function) {
|
||||
Objects.requireNonNull(type);
|
||||
public final MethodHandle downcallHandle(FunctionDescriptor function) {
|
||||
Objects.requireNonNull(function);
|
||||
MethodType llMt = SharedUtils.convertVaListCarriers(type, LinuxAArch64VaList.CARRIER);
|
||||
MethodHandle handle = CallArranger.arrangeDowncall(llMt, function);
|
||||
MethodType type = SharedUtils.inferMethodType(function, false);
|
||||
MethodHandle handle = CallArranger.LINUX.arrangeDowncall(type, function);
|
||||
if (!type.returnType().equals(MemorySegment.class)) {
|
||||
// not returning segment, just insert a throwing allocator
|
||||
handle = MethodHandles.insertArguments(handle, 1, SharedUtils.THROWING_ALLOCATOR);
|
||||
}
|
||||
handle = SharedUtils.unboxVaLists(type, handle, MH_unboxVaList);
|
||||
return handle;
|
||||
return SharedUtils.wrapDowncall(handle, function);
|
||||
}
|
||||
|
||||
@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(target);
|
||||
Objects.requireNonNull(function);
|
||||
target = SharedUtils.boxVaLists(target, MH_boxVaList);
|
||||
return UpcallStubs.upcallAddress(CallArranger.arrangeUpcall(target, target.type(), function), (ResourceScopeImpl) scope);
|
||||
SharedUtils.checkExceptions(target);
|
||||
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) {
|
||||
|
||||
@ -26,32 +26,30 @@
|
||||
package jdk.internal.foreign.abi.aarch64.linux;
|
||||
|
||||
import jdk.incubator.foreign.*;
|
||||
import jdk.internal.foreign.ResourceScopeImpl;
|
||||
import jdk.internal.foreign.Scoped;
|
||||
import jdk.internal.foreign.Utils;
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
import jdk.internal.foreign.abi.aarch64.*;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.lang.ref.Cleaner;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
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.internal.foreign.abi.SharedUtils.SimpleVaArg;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Standard va_list implementation as defined by AAPCS document and used on
|
||||
* Linux. Variadic parameters may be passed in registers or on the stack.
|
||||
*/
|
||||
public non-sealed class LinuxAArch64VaList implements VaList {
|
||||
public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
|
||||
static final Class<?> CARRIER = MemoryAddress.class;
|
||||
@ -76,9 +74,9 @@ public non-sealed class LinuxAArch64VaList implements VaList {
|
||||
).withName("__va_list");
|
||||
|
||||
private static final MemoryLayout GP_REG
|
||||
= MemoryLayout.valueLayout(64, ByteOrder.nativeOrder());
|
||||
= MemoryLayout.paddingLayout(64).withBitAlignment(64);
|
||||
private static final MemoryLayout FP_REG
|
||||
= MemoryLayout.valueLayout(128, ByteOrder.nativeOrder());
|
||||
= MemoryLayout.paddingLayout(128).withBitAlignment(128);
|
||||
|
||||
private static final MemoryLayout LAYOUT_GP_REGS
|
||||
= 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_FP_OFFSET = (int) LAYOUT_FP_REGS.byteSize();
|
||||
|
||||
private static final VarHandle VH_stack
|
||||
= MemoryHandles.asAddressVarHandle(LAYOUT.varHandle(long.class, groupElement("__stack")));
|
||||
private static final VarHandle VH_gr_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_stack = LAYOUT.varHandle(groupElement("__stack"));
|
||||
private static final VarHandle VH_gr_top = LAYOUT.varHandle(groupElement("__gr_top"));
|
||||
private static final VarHandle VH_vr_top = LAYOUT.varHandle(groupElement("__vr_top"));
|
||||
private static final VarHandle VH_gr_offs
|
||||
= LAYOUT.varHandle(int.class, groupElement("__gr_offs"));
|
||||
= LAYOUT.varHandle(groupElement("__gr_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
|
||||
= new SharedUtils.EmptyVaList(emptyListAddress());
|
||||
|
||||
@ -117,19 +111,20 @@ public non-sealed class LinuxAArch64VaList implements VaList {
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
MemorySegment fpRegsArea = vrTop(segment).addOffset(-MAX_FP_OFFSET).asSegment(
|
||||
MemorySegment fpRegsArea = MemorySegment.ofAddress(vrTop(segment).addOffset(-MAX_FP_OFFSET),
|
||||
MAX_FP_OFFSET, segment.scope());
|
||||
return new LinuxAArch64VaList(segment, gpRegsArea, fpRegsArea);
|
||||
}
|
||||
|
||||
private static MemoryAddress emptyListAddress() {
|
||||
long ptr = U.allocateMemory(LAYOUT.byteSize());
|
||||
MemorySegment ms = MemoryAddress.ofLong(ptr).asSegment(
|
||||
LAYOUT.byteSize(), () -> U.freeMemory(ptr), ResourceScope.newSharedScope());
|
||||
cleaner.register(LinuxAArch64VaList.class, () -> ms.scope().close());
|
||||
ResourceScope scope = ResourceScope.newImplicitScope();
|
||||
scope.addCloseAction(() -> U.freeMemory(ptr));
|
||||
MemorySegment ms = MemorySegment.ofAddress(MemoryAddress.ofLong(ptr),
|
||||
LAYOUT.byteSize(), scope);
|
||||
VH_stack.set(ms, MemoryAddress.NULL);
|
||||
VH_gr_top.set(ms, MemoryAddress.NULL);
|
||||
VH_vr_top.set(ms, MemoryAddress.NULL);
|
||||
@ -215,58 +210,51 @@ public non-sealed class LinuxAArch64VaList implements VaList {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int vargAsInt(MemoryLayout layout) {
|
||||
public int nextVarg(ValueLayout.OfInt layout) {
|
||||
return (int) read(int.class, layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long vargAsLong(MemoryLayout layout) {
|
||||
public long nextVarg(ValueLayout.OfLong layout) {
|
||||
return (long) read(long.class, layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double vargAsDouble(MemoryLayout layout) {
|
||||
public double nextVarg(ValueLayout.OfDouble layout) {
|
||||
return (double) read(double.class, layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryAddress vargAsAddress(MemoryLayout layout) {
|
||||
public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
|
||||
return (MemoryAddress) read(MemoryAddress.class, layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment vargAsSegment(MemoryLayout layout, SegmentAllocator allocator) {
|
||||
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(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) {
|
||||
return read(carrier, layout, THROWING_ALLOCATOR);
|
||||
}
|
||||
|
||||
private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(layout);
|
||||
checkCompatibleType(carrier, layout, LinuxAArch64Linker.ADDRESS_SIZE);
|
||||
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass, layout)) {
|
||||
preAlignStack(layout);
|
||||
return switch (typeClass) {
|
||||
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);
|
||||
seg.copyFrom(slice);
|
||||
postAlignStack(layout);
|
||||
yield seg;
|
||||
}
|
||||
case POINTER, INTEGER, FLOAT -> {
|
||||
VarHandle reader = vhPrimitiveOrAddress(carrier, layout);
|
||||
MemorySegment slice = stackPtr().asSegment(layout.byteSize(), scope());
|
||||
VarHandle reader = layout.varHandle();
|
||||
MemorySegment slice = MemorySegment.ofAddress(stackPtr(), layout.byteSize(), scope());
|
||||
Object res = reader.get(slice);
|
||||
postAlignStack(layout);
|
||||
yield res;
|
||||
@ -280,8 +268,7 @@ public non-sealed class LinuxAArch64VaList implements VaList {
|
||||
long offset = 0;
|
||||
while (offset < layout.byteSize()) {
|
||||
final long copy = Math.min(layout.byteSize() - offset, 8);
|
||||
MemorySegment slice = value.asSlice(offset, copy);
|
||||
slice.copyFrom(gpRegsArea.asSlice(currentGPOffset(), copy));
|
||||
MemorySegment.copy(gpRegsArea, currentGPOffset(), value, offset, copy);
|
||||
consumeGPSlots(1);
|
||||
offset += copy;
|
||||
}
|
||||
@ -296,8 +283,7 @@ public non-sealed class LinuxAArch64VaList implements VaList {
|
||||
for (MemoryLayout elem : group.memberLayouts()) {
|
||||
assert elem.byteSize() <= 8;
|
||||
final long copy = elem.byteSize();
|
||||
MemorySegment slice = value.asSlice(offset, copy);
|
||||
slice.copyFrom(fpRegsArea.asSlice(currentFPOffset(), copy));
|
||||
MemorySegment.copy(fpRegsArea, currentFPOffset(), value, offset, copy);
|
||||
consumeFPSlots(1);
|
||||
offset += copy;
|
||||
}
|
||||
@ -305,25 +291,24 @@ public non-sealed class LinuxAArch64VaList implements VaList {
|
||||
}
|
||||
case STRUCT_REFERENCE -> {
|
||||
// Struct is passed indirectly via a pointer in an integer register.
|
||||
VarHandle ptrReader
|
||||
= SharedUtils.vhPrimitiveOrAddress(MemoryAddress.class, AArch64.C_POINTER);
|
||||
VarHandle ptrReader = AArch64.C_POINTER.varHandle();
|
||||
MemoryAddress ptr = (MemoryAddress) ptrReader.get(
|
||||
gpRegsArea.asSlice(currentGPOffset()));
|
||||
consumeGPSlots(1);
|
||||
|
||||
MemorySegment slice = ptr.asSegment(layout.byteSize(), scope());
|
||||
MemorySegment slice = MemorySegment.ofAddress(ptr, layout.byteSize(), scope());
|
||||
MemorySegment seg = allocator.allocate(layout);
|
||||
seg.copyFrom(slice);
|
||||
yield seg;
|
||||
}
|
||||
case POINTER, INTEGER -> {
|
||||
VarHandle reader = SharedUtils.vhPrimitiveOrAddress(carrier, layout);
|
||||
VarHandle reader = layout.varHandle();
|
||||
Object res = reader.get(gpRegsArea.asSlice(currentGPOffset()));
|
||||
consumeGPSlots(1);
|
||||
yield res;
|
||||
}
|
||||
case FLOAT -> {
|
||||
VarHandle reader = layout.varHandle(carrier);
|
||||
VarHandle reader = layout.varHandle();
|
||||
Object res = reader.get(fpRegsArea.asSlice(currentFPOffset()));
|
||||
consumeFPSlots(1);
|
||||
yield res;
|
||||
@ -335,6 +320,7 @@ public non-sealed class LinuxAArch64VaList implements VaList {
|
||||
@Override
|
||||
public void skip(MemoryLayout... layouts) {
|
||||
Objects.requireNonNull(layouts);
|
||||
((ResourceScopeImpl)segment.scope()).checkValidStateSlow();
|
||||
for (MemoryLayout layout : layouts) {
|
||||
Objects.requireNonNull(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) {
|
||||
return readFromSegment(ma.asSegment(LAYOUT.byteSize(), scope));
|
||||
return readFromSegment(MemorySegment.ofAddress(ma, LAYOUT.byteSize(), scope));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -418,35 +404,33 @@ public non-sealed class LinuxAArch64VaList implements VaList {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder vargFromInt(ValueLayout layout, int value) {
|
||||
public Builder addVarg(ValueLayout.OfInt layout, int value) {
|
||||
return arg(int.class, layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder vargFromLong(ValueLayout layout, long value) {
|
||||
public Builder addVarg(ValueLayout.OfLong layout, long value) {
|
||||
return arg(long.class, layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder vargFromDouble(ValueLayout layout, double value) {
|
||||
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
|
||||
return arg(double.class, layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder vargFromAddress(ValueLayout layout, Addressable value) {
|
||||
public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) {
|
||||
return arg(MemoryAddress.class, layout, value.address());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder vargFromSegment(GroupLayout layout, MemorySegment value) {
|
||||
public Builder addVarg(GroupLayout layout, MemorySegment value) {
|
||||
return arg(MemorySegment.class, layout, value);
|
||||
}
|
||||
|
||||
private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) {
|
||||
Objects.requireNonNull(layout);
|
||||
Objects.requireNonNull(value);
|
||||
checkCompatibleType(carrier, layout, LinuxAArch64Linker.ADDRESS_SIZE);
|
||||
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
if (isRegOverflow(currentGPOffset, currentFPOffset, typeClass, layout)) {
|
||||
stackArgs.add(new SimpleVaArg(carrier, layout, value));
|
||||
@ -458,8 +442,7 @@ public non-sealed class LinuxAArch64VaList implements VaList {
|
||||
long offset = 0;
|
||||
while (offset < layout.byteSize()) {
|
||||
final long copy = Math.min(layout.byteSize() - offset, 8);
|
||||
MemorySegment slice = valueSegment.asSlice(offset, copy);
|
||||
gpRegs.asSlice(currentGPOffset, copy).copyFrom(slice);
|
||||
MemorySegment.copy(valueSegment, offset, gpRegs, currentGPOffset, copy);
|
||||
currentGPOffset += GP_SLOT_SIZE;
|
||||
offset += copy;
|
||||
}
|
||||
@ -473,8 +456,7 @@ public non-sealed class LinuxAArch64VaList implements VaList {
|
||||
for (MemoryLayout elem : group.memberLayouts()) {
|
||||
assert elem.byteSize() <= 8;
|
||||
final long copy = elem.byteSize();
|
||||
MemorySegment slice = valueSegment.asSlice(offset, copy);
|
||||
fpRegs.asSlice(currentFPOffset, copy).copyFrom(slice);
|
||||
MemorySegment.copy(valueSegment, offset, fpRegs, currentFPOffset, copy);
|
||||
currentFPOffset += FP_SLOT_SIZE;
|
||||
offset += copy;
|
||||
}
|
||||
@ -482,20 +464,18 @@ public non-sealed class LinuxAArch64VaList implements VaList {
|
||||
case STRUCT_REFERENCE -> {
|
||||
// Struct is passed indirectly via a pointer in an integer register.
|
||||
MemorySegment valueSegment = (MemorySegment) value;
|
||||
VarHandle writer
|
||||
= SharedUtils.vhPrimitiveOrAddress(MemoryAddress.class,
|
||||
AArch64.C_POINTER);
|
||||
VarHandle writer = AArch64.C_POINTER.varHandle();
|
||||
writer.set(gpRegs.asSlice(currentGPOffset),
|
||||
valueSegment.address());
|
||||
currentGPOffset += GP_SLOT_SIZE;
|
||||
}
|
||||
case POINTER, INTEGER -> {
|
||||
VarHandle writer = SharedUtils.vhPrimitiveOrAddress(carrier, layout);
|
||||
VarHandle writer = layout.varHandle();
|
||||
writer.set(gpRegs.asSlice(currentGPOffset), value);
|
||||
currentGPOffset += GP_SLOT_SIZE;
|
||||
}
|
||||
case FLOAT -> {
|
||||
VarHandle writer = layout.varHandle(carrier);
|
||||
VarHandle writer = layout.varHandle();
|
||||
writer.set(fpRegs.asSlice(currentFPOffset), value);
|
||||
currentFPOffset += FP_SLOT_SIZE;
|
||||
}
|
||||
@ -513,7 +493,7 @@ public non-sealed class LinuxAArch64VaList implements VaList {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
SegmentAllocator allocator = SegmentAllocator.arenaAllocator(scope);
|
||||
SegmentAllocator allocator = SegmentAllocator.newNativeArena(scope);
|
||||
MemorySegment vaListSegment = allocator.allocate(LAYOUT);
|
||||
MemoryAddress stackArgsPtr = MemoryAddress.NULL;
|
||||
if (!stackArgs.isEmpty()) {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -25,16 +25,14 @@
|
||||
*/
|
||||
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.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.NativeSymbol;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import jdk.internal.foreign.AbstractCLinker;
|
||||
import jdk.internal.foreign.ResourceScopeImpl;
|
||||
import jdk.incubator.foreign.VaList;
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
import jdk.internal.foreign.abi.UpcallStubs;
|
||||
import jdk.internal.foreign.abi.aarch64.CallArranger;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
@ -43,32 +41,15 @@ import java.lang.invoke.MethodType;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static jdk.internal.foreign.PlatformLayouts.*;
|
||||
|
||||
/**
|
||||
* ABI implementation for macOS on Apple silicon. Based on AAPCS with
|
||||
* 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;
|
||||
|
||||
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() {
|
||||
if (instance == null) {
|
||||
instance = new MacOsAArch64Linker();
|
||||
@ -77,26 +58,27 @@ public final class MacOsAArch64Linker extends AbstractCLinker {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final MethodHandle downcallHandle(MethodType type, FunctionDescriptor function) {
|
||||
Objects.requireNonNull(type);
|
||||
public final MethodHandle downcallHandle(FunctionDescriptor function) {
|
||||
Objects.requireNonNull(function);
|
||||
MethodType llMt = SharedUtils.convertVaListCarriers(type, MacOsAArch64VaList.CARRIER);
|
||||
MethodHandle handle = CallArranger.arrangeDowncall(llMt, function);
|
||||
MethodType type = SharedUtils.inferMethodType(function, false);
|
||||
MethodHandle handle = CallArranger.MACOS.arrangeDowncall(type, function);
|
||||
if (!type.returnType().equals(MemorySegment.class)) {
|
||||
// not returning segment, just insert a throwing allocator
|
||||
handle = MethodHandles.insertArguments(handle, 1, SharedUtils.THROWING_ALLOCATOR);
|
||||
}
|
||||
handle = SharedUtils.unboxVaLists(type, handle, MH_unboxVaList);
|
||||
return handle;
|
||||
return SharedUtils.wrapDowncall(handle, function);
|
||||
}
|
||||
|
||||
@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(target);
|
||||
Objects.requireNonNull(function);
|
||||
target = SharedUtils.boxVaLists(target, MH_boxVaList);
|
||||
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.MACOS.arrangeUpcall(target, target.type(), function, scope);
|
||||
}
|
||||
|
||||
public static VaList newVaList(Consumer<VaList.Builder> actions, ResourceScope scope) {
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
package jdk.internal.foreign.abi.aarch64.macos;
|
||||
|
||||
import jdk.incubator.foreign.*;
|
||||
import jdk.incubator.foreign.CLinker.VaList;
|
||||
import jdk.internal.foreign.Scoped;
|
||||
import jdk.internal.foreign.ResourceScopeImpl;
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
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
|
||||
* 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;
|
||||
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);
|
||||
|
||||
@ -65,65 +65,59 @@ public non-sealed class MacOsAArch64VaList implements VaList {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int vargAsInt(MemoryLayout layout) {
|
||||
public int nextVarg(ValueLayout.OfInt layout) {
|
||||
return (int) read(int.class, layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long vargAsLong(MemoryLayout layout) {
|
||||
public long nextVarg(ValueLayout.OfLong layout) {
|
||||
return (long) read(long.class, layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double vargAsDouble(MemoryLayout layout) {
|
||||
public double nextVarg(ValueLayout.OfDouble layout) {
|
||||
return (double) read(double.class, layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryAddress vargAsAddress(MemoryLayout layout) {
|
||||
public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
|
||||
return (MemoryAddress) read(MemoryAddress.class, layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment vargAsSegment(MemoryLayout layout, SegmentAllocator allocator) {
|
||||
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(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) {
|
||||
return read(carrier, layout, SharedUtils.THROWING_ALLOCATOR);
|
||||
}
|
||||
|
||||
private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(layout);
|
||||
SharedUtils.checkCompatibleType(carrier, layout, MacOsAArch64Linker.ADDRESS_SIZE);
|
||||
Object res;
|
||||
if (carrier == MemorySegment.class) {
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
res = switch (typeClass) {
|
||||
case STRUCT_REFERENCE -> {
|
||||
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);
|
||||
seg.copyFrom(struct);
|
||||
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
yield seg;
|
||||
}
|
||||
case STRUCT_REGISTER, STRUCT_HFA -> {
|
||||
MemorySegment struct = allocator.allocate(layout);
|
||||
struct.copyFrom(segment.asSlice(0L, layout.byteSize()));
|
||||
MemorySegment struct = allocator.allocate(layout)
|
||||
.copyFrom(segment.asSlice(0, layout.byteSize()));
|
||||
segment = segment.asSlice(alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES));
|
||||
yield struct;
|
||||
}
|
||||
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
|
||||
};
|
||||
} else {
|
||||
VarHandle reader = SharedUtils.vhPrimitiveOrAddress(carrier, layout);
|
||||
VarHandle reader = layout.varHandle();
|
||||
res = reader.get(segment);
|
||||
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
}
|
||||
@ -133,6 +127,7 @@ public non-sealed class MacOsAArch64VaList implements VaList {
|
||||
@Override
|
||||
public void skip(MemoryLayout... layouts) {
|
||||
Objects.requireNonNull(layouts);
|
||||
((ResourceScopeImpl)scope).checkValidStateSlow();
|
||||
|
||||
for (MemoryLayout layout : layouts) {
|
||||
Objects.requireNonNull(layout);
|
||||
@ -144,7 +139,7 @@ public non-sealed class MacOsAArch64VaList implements VaList {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -181,33 +176,32 @@ public non-sealed class MacOsAArch64VaList implements VaList {
|
||||
private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) {
|
||||
Objects.requireNonNull(layout);
|
||||
Objects.requireNonNull(value);
|
||||
SharedUtils.checkCompatibleType(carrier, layout, MacOsAArch64Linker.ADDRESS_SIZE);
|
||||
args.add(new SimpleVaArg(carrier, layout, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder vargFromInt(ValueLayout layout, int value) {
|
||||
public Builder addVarg(ValueLayout.OfInt layout, int value) {
|
||||
return arg(int.class, layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder vargFromLong(ValueLayout layout, long value) {
|
||||
public Builder addVarg(ValueLayout.OfLong layout, long value) {
|
||||
return arg(long.class, layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder vargFromDouble(ValueLayout layout, double value) {
|
||||
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
|
||||
return arg(double.class, layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder vargFromAddress(ValueLayout layout, Addressable value) {
|
||||
public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) {
|
||||
return arg(MemoryAddress.class, layout, value.address());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder vargFromSegment(GroupLayout layout, MemorySegment value) {
|
||||
public Builder addVarg(GroupLayout layout, MemorySegment value) {
|
||||
return arg(MemorySegment.class, layout, value);
|
||||
}
|
||||
|
||||
@ -216,7 +210,7 @@ public non-sealed class MacOsAArch64VaList implements VaList {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
SegmentAllocator allocator = SegmentAllocator.arenaAllocator(scope);
|
||||
SegmentAllocator allocator = SegmentAllocator.newNativeArena(scope);
|
||||
|
||||
// Each argument may occupy up to four slots
|
||||
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());
|
||||
cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
}
|
||||
case STRUCT_REGISTER, STRUCT_HFA -> {
|
||||
cursor.copyFrom(msArg.asSlice(0, arg.layout.byteSize()));
|
||||
cursor = cursor.asSlice(alignUp(arg.layout.byteSize(), VA_SLOT_SIZE_BYTES));
|
||||
}
|
||||
case STRUCT_REGISTER, STRUCT_HFA ->
|
||||
cursor.copyFrom(msArg.asSlice(0, arg.layout.byteSize()))
|
||||
.asSlice(alignUp(arg.layout.byteSize(), VA_SLOT_SIZE_BYTES));
|
||||
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -30,8 +30,9 @@ import jdk.incubator.foreign.GroupLayout;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
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.UpcallHandler;
|
||||
import jdk.internal.foreign.abi.ABIDescriptor;
|
||||
import jdk.internal.foreign.abi.Binding;
|
||||
import jdk.internal.foreign.abi.CallingSequence;
|
||||
@ -86,8 +87,6 @@ public class CallArranger {
|
||||
}
|
||||
|
||||
public static Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall) {
|
||||
SharedUtils.checkFunctionTypes(mt, cDesc, SysVx64Linker.ADDRESS_SIZE);
|
||||
|
||||
CallingSequenceBuilder csb = new CallingSequenceBuilder(forUpcall);
|
||||
|
||||
BindingCalculator argCalc = forUpcall ? new BoxBindingCalculator(true) : new UnboxBindingCalculator(true);
|
||||
@ -134,14 +133,14 @@ public class CallArranger {
|
||||
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);
|
||||
|
||||
if (bindings.isInMemoryReturn) {
|
||||
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) {
|
||||
@ -239,7 +238,7 @@ public class CallArranger {
|
||||
}
|
||||
}
|
||||
|
||||
static abstract class BindingCalculator {
|
||||
abstract static class BindingCalculator {
|
||||
protected final StorageCalculator storageCalculator;
|
||||
|
||||
protected BindingCalculator(boolean forArguments) {
|
||||
@ -280,7 +279,7 @@ public class CallArranger {
|
||||
break;
|
||||
}
|
||||
case POINTER: {
|
||||
bindings.unboxAddress();
|
||||
bindings.unboxAddress(carrier);
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER);
|
||||
bindings.vmStore(storage, long.class);
|
||||
break;
|
||||
|
||||
@ -26,27 +26,25 @@
|
||||
package jdk.internal.foreign.abi.x64.sysv;
|
||||
|
||||
import jdk.incubator.foreign.*;
|
||||
import jdk.internal.foreign.ResourceScopeImpl;
|
||||
import jdk.internal.foreign.Scoped;
|
||||
import jdk.internal.foreign.Utils;
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.lang.ref.Cleaner;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
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.internal.foreign.abi.SharedUtils.SimpleVaArg;
|
||||
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"
|
||||
public non-sealed class SysVVaList implements VaList {
|
||||
public non-sealed class SysVVaList implements VaList, Scoped {
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
|
||||
static final Class<?> CARRIER = MemoryAddress.class;
|
||||
@ -67,8 +65,8 @@ public non-sealed class SysVVaList implements VaList {
|
||||
SysV.C_POINTER.withName("reg_save_area")
|
||||
).withName("__va_list_tag");
|
||||
|
||||
private static final MemoryLayout GP_REG = MemoryLayout.valueLayout(64, ByteOrder.nativeOrder());
|
||||
private static final MemoryLayout FP_REG = MemoryLayout.valueLayout(128, ByteOrder.nativeOrder());
|
||||
private static final MemoryLayout GP_REG = MemoryLayout.paddingLayout(64).withBitAlignment(64);
|
||||
private static final MemoryLayout FP_REG = MemoryLayout.paddingLayout(128).withBitAlignment(128);
|
||||
|
||||
private static final GroupLayout LAYOUT_REG_SAVE_AREA = MemoryLayout.structLayout(
|
||||
GP_REG.withName("%rdi"),
|
||||
@ -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_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_gp_offset = LAYOUT.varHandle(int.class, groupElement("gp_offset"));
|
||||
private static final VarHandle VH_overflow_arg_area
|
||||
= MemoryHandles.asAddressVarHandle(LAYOUT.varHandle(long.class, groupElement("overflow_arg_area")));
|
||||
private static final VarHandle VH_reg_save_area
|
||||
= MemoryHandles.asAddressVarHandle(LAYOUT.varHandle(long.class, groupElement("reg_save_area")));
|
||||
private static final VarHandle VH_fp_offset = LAYOUT.varHandle(groupElement("fp_offset"));
|
||||
private static final VarHandle VH_gp_offset = LAYOUT.varHandle(groupElement("gp_offset"));
|
||||
private static final VarHandle VH_overflow_arg_area = LAYOUT.varHandle(groupElement("overflow_arg_area"));
|
||||
private static final VarHandle VH_reg_save_area = LAYOUT.varHandle(groupElement("reg_save_area"));
|
||||
|
||||
private static final Cleaner cleaner = Cleaner.create();
|
||||
private static final VaList EMPTY = new SharedUtils.EmptyVaList(emptyListAddress());
|
||||
|
||||
private final MemorySegment segment;
|
||||
@ -130,9 +125,10 @@ public non-sealed class SysVVaList implements VaList {
|
||||
|
||||
private static MemoryAddress emptyListAddress() {
|
||||
long ptr = U.allocateMemory(LAYOUT.byteSize());
|
||||
MemorySegment base = MemoryAddress.ofLong(ptr).asSegment(
|
||||
LAYOUT.byteSize(), () -> U.freeMemory(ptr), ResourceScope.newSharedScope());
|
||||
cleaner.register(SysVVaList.class, () -> base.scope().close());
|
||||
ResourceScope scope = ResourceScope.newImplicitScope();
|
||||
scope.addCloseAction(() -> U.freeMemory(ptr));
|
||||
MemorySegment base = MemorySegment.ofAddress(MemoryAddress.ofLong(ptr),
|
||||
LAYOUT.byteSize(), scope);
|
||||
VH_gp_offset.set(base, MAX_GP_OFFSET);
|
||||
VH_fp_offset.set(base, MAX_FP_OFFSET);
|
||||
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) {
|
||||
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());
|
||||
}
|
||||
|
||||
@ -188,59 +184,53 @@ public non-sealed class SysVVaList implements VaList {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int vargAsInt(MemoryLayout layout) {
|
||||
public int nextVarg(ValueLayout.OfInt layout) {
|
||||
return (int) read(int.class, layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long vargAsLong(MemoryLayout layout) {
|
||||
public long nextVarg(ValueLayout.OfLong layout) {
|
||||
return (long) read(long.class, layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double vargAsDouble(MemoryLayout layout) {
|
||||
public double nextVarg(ValueLayout.OfDouble layout) {
|
||||
return (double) read(double.class, layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryAddress vargAsAddress(MemoryLayout layout) {
|
||||
public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
|
||||
return (MemoryAddress) read(MemoryAddress.class, layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment vargAsSegment(MemoryLayout layout, SegmentAllocator allocator) {
|
||||
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(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) {
|
||||
return read(carrier, layout, THROWING_ALLOCATOR);
|
||||
}
|
||||
|
||||
private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(layout);
|
||||
checkCompatibleType(carrier, layout, SysVx64Linker.ADDRESS_SIZE);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass)
|
||||
|| typeClass.inMemory()) {
|
||||
preAlignStack(layout);
|
||||
return switch (typeClass.kind()) {
|
||||
case STRUCT -> {
|
||||
MemorySegment slice = stackPtr().asSegment(layout.byteSize(), scope());
|
||||
MemorySegment slice = MemorySegment.ofAddress(stackPtr(), layout.byteSize(), scope());
|
||||
MemorySegment seg = allocator.allocate(layout);
|
||||
seg.copyFrom(slice);
|
||||
postAlignStack(layout);
|
||||
yield seg;
|
||||
}
|
||||
case POINTER, INTEGER, FLOAT -> {
|
||||
VarHandle reader = vhPrimitiveOrAddress(carrier, layout);
|
||||
VarHandle reader = layout.varHandle();
|
||||
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);
|
||||
postAlignStack(layout);
|
||||
yield res;
|
||||
@ -256,12 +246,11 @@ public non-sealed class SysVVaList implements VaList {
|
||||
while (offset < layout.byteSize()) {
|
||||
final long copy = Math.min(layout.byteSize() - offset, 8);
|
||||
boolean isSSE = typeClass.classes.get(classIdx++) == ArgumentClassImpl.SSE;
|
||||
MemorySegment slice = value.asSlice(offset, copy);
|
||||
if (isSSE) {
|
||||
slice.copyFrom(regSaveArea.asSlice(currentFPOffset(), copy));
|
||||
MemorySegment.copy(regSaveArea, currentFPOffset(), value, offset, copy);
|
||||
currentFPOffset(currentFPOffset() + FP_SLOT_SIZE);
|
||||
} else {
|
||||
slice.copyFrom(regSaveArea.asSlice(currentGPOffset(), copy));
|
||||
MemorySegment.copy(regSaveArea, currentGPOffset(), value, offset, copy);
|
||||
currentGPOffset(currentGPOffset() + GP_SLOT_SIZE);
|
||||
}
|
||||
offset += copy;
|
||||
@ -269,13 +258,13 @@ public non-sealed class SysVVaList implements VaList {
|
||||
yield value;
|
||||
}
|
||||
case POINTER, INTEGER -> {
|
||||
VarHandle reader = SharedUtils.vhPrimitiveOrAddress(carrier, layout);
|
||||
VarHandle reader = layout.varHandle();
|
||||
Object res = reader.get(regSaveArea.asSlice(currentGPOffset()));
|
||||
currentGPOffset(currentGPOffset() + GP_SLOT_SIZE);
|
||||
yield res;
|
||||
}
|
||||
case FLOAT -> {
|
||||
VarHandle reader = layout.varHandle(carrier);
|
||||
VarHandle reader = layout.varHandle();
|
||||
Object res = reader.get(regSaveArea.asSlice(currentFPOffset()));
|
||||
currentFPOffset(currentFPOffset() + FP_SLOT_SIZE);
|
||||
yield res;
|
||||
@ -287,6 +276,7 @@ public non-sealed class SysVVaList implements VaList {
|
||||
@Override
|
||||
public void skip(MemoryLayout... layouts) {
|
||||
Objects.requireNonNull(layouts);
|
||||
((ResourceScopeImpl)segment.scope()).checkValidStateSlow();
|
||||
for (MemoryLayout layout : layouts) {
|
||||
Objects.requireNonNull(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) {
|
||||
return readFromSegment(ma.asSegment(LAYOUT.byteSize(), scope));
|
||||
return readFromSegment(MemorySegment.ofAddress(ma, LAYOUT.byteSize(), scope));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -353,34 +343,33 @@ public non-sealed class SysVVaList implements VaList {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder vargFromInt(ValueLayout layout, int value) {
|
||||
public Builder addVarg(ValueLayout.OfInt layout, int value) {
|
||||
return arg(int.class, layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder vargFromLong(ValueLayout layout, long value) {
|
||||
public Builder addVarg(ValueLayout.OfLong layout, long value) {
|
||||
return arg(long.class, layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder vargFromDouble(ValueLayout layout, double value) {
|
||||
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
|
||||
return arg(double.class, layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder vargFromAddress(ValueLayout layout, Addressable value) {
|
||||
public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) {
|
||||
return arg(MemoryAddress.class, layout, value.address());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder vargFromSegment(GroupLayout layout, MemorySegment value) {
|
||||
public Builder addVarg(GroupLayout layout, MemorySegment value) {
|
||||
return arg(MemorySegment.class, layout, value);
|
||||
}
|
||||
|
||||
private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) {
|
||||
Objects.requireNonNull(layout);
|
||||
Objects.requireNonNull(value);
|
||||
checkCompatibleType(carrier, layout, SysVx64Linker.ADDRESS_SIZE);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
if (isRegOverflow(currentGPOffset, currentFPOffset, typeClass)
|
||||
|| typeClass.inMemory()) {
|
||||
@ -395,24 +384,23 @@ public non-sealed class SysVVaList implements VaList {
|
||||
while (offset < layout.byteSize()) {
|
||||
final long copy = Math.min(layout.byteSize() - offset, 8);
|
||||
boolean isSSE = typeClass.classes.get(classIdx++) == ArgumentClassImpl.SSE;
|
||||
MemorySegment slice = valueSegment.asSlice(offset, copy);
|
||||
if (isSSE) {
|
||||
reg_save_area.asSlice(currentFPOffset, copy).copyFrom(slice);
|
||||
MemorySegment.copy(valueSegment, offset, reg_save_area, currentFPOffset, copy);
|
||||
currentFPOffset += FP_SLOT_SIZE;
|
||||
} else {
|
||||
reg_save_area.asSlice(currentGPOffset, copy).copyFrom(slice);
|
||||
MemorySegment.copy(valueSegment, offset, reg_save_area, currentGPOffset, copy);
|
||||
currentGPOffset += GP_SLOT_SIZE;
|
||||
}
|
||||
offset += copy;
|
||||
}
|
||||
}
|
||||
case POINTER, INTEGER -> {
|
||||
VarHandle writer = SharedUtils.vhPrimitiveOrAddress(carrier, layout);
|
||||
VarHandle writer = layout.varHandle();
|
||||
writer.set(reg_save_area.asSlice(currentGPOffset), value);
|
||||
currentGPOffset += GP_SLOT_SIZE;
|
||||
}
|
||||
case FLOAT -> {
|
||||
VarHandle writer = layout.varHandle(carrier);
|
||||
VarHandle writer = layout.varHandle();
|
||||
writer.set(reg_save_area.asSlice(currentFPOffset), value);
|
||||
currentFPOffset += FP_SLOT_SIZE;
|
||||
}
|
||||
@ -430,7 +418,7 @@ public non-sealed class SysVVaList implements VaList {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
SegmentAllocator allocator = SegmentAllocator.arenaAllocator(scope);
|
||||
SegmentAllocator allocator = SegmentAllocator.newNativeArena(scope);
|
||||
MemorySegment vaListSegment = allocator.allocate(LAYOUT);
|
||||
MemoryAddress stackArgsPtr = MemoryAddress.NULL;
|
||||
if (!stackArgs.isEmpty()) {
|
||||
|
||||
@ -25,14 +25,14 @@
|
||||
package jdk.internal.foreign.abi.x64.sysv;
|
||||
|
||||
|
||||
import jdk.incubator.foreign.CLinker;
|
||||
import jdk.incubator.foreign.FunctionDescriptor;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.NativeSymbol;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import jdk.internal.foreign.AbstractCLinker;
|
||||
import jdk.internal.foreign.ResourceScopeImpl;
|
||||
import jdk.incubator.foreign.VaList;
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
import jdk.internal.foreign.abi.UpcallStubs;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
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
|
||||
*/
|
||||
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_RETURN_REGISTERS = 2;
|
||||
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
|
||||
|
||||
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() {
|
||||
if (instance == null) {
|
||||
instance = new SysVx64Linker();
|
||||
@ -83,26 +68,28 @@ public final class SysVx64Linker extends AbstractCLinker {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final MethodHandle downcallHandle(MethodType type, FunctionDescriptor function) {
|
||||
Objects.requireNonNull(type);
|
||||
public final MethodHandle downcallHandle(FunctionDescriptor function) {
|
||||
Objects.requireNonNull(function);
|
||||
MethodType llMt = SharedUtils.convertVaListCarriers(type, SysVVaList.CARRIER);
|
||||
MethodHandle handle = CallArranger.arrangeDowncall(llMt, function);
|
||||
MethodType type = SharedUtils.inferMethodType(function, false);
|
||||
MethodHandle handle = CallArranger.arrangeDowncall(type, function);
|
||||
if (!type.returnType().equals(MemorySegment.class)) {
|
||||
// not returning segment, just insert a throwing allocator
|
||||
handle = MethodHandles.insertArguments(handle, 1, SharedUtils.THROWING_ALLOCATOR);
|
||||
}
|
||||
handle = SharedUtils.unboxVaLists(type, handle, MH_unboxVaList);
|
||||
return handle;
|
||||
return SharedUtils.wrapDowncall(handle, function);
|
||||
}
|
||||
|
||||
@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(target);
|
||||
Objects.requireNonNull(function);
|
||||
target = SharedUtils.boxVaLists(target, MH_boxVaList);
|
||||
return UpcallStubs.upcallAddress(CallArranger.arrangeUpcall(target, target.type(), function), (ResourceScopeImpl) scope);
|
||||
SharedUtils.checkExceptions(target);
|
||||
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) {
|
||||
|
||||
@ -25,10 +25,10 @@
|
||||
package jdk.internal.foreign.abi.x64.sysv;
|
||||
|
||||
import jdk.incubator.foreign.GroupLayout;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.SequenceLayout;
|
||||
import jdk.incubator.foreign.ValueLayout;
|
||||
import jdk.internal.foreign.PlatformLayouts;
|
||||
import jdk.internal.foreign.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -107,11 +107,17 @@ class TypeClass {
|
||||
}
|
||||
|
||||
private static ArgumentClassImpl argumentClassFor(MemoryLayout layout) {
|
||||
return switch (PlatformLayouts.getKind(layout)) {
|
||||
case CHAR, SHORT, INT, LONG, LONG_LONG -> ArgumentClassImpl.INTEGER;
|
||||
case FLOAT, DOUBLE -> ArgumentClassImpl.SSE;
|
||||
case POINTER -> ArgumentClassImpl.POINTER;
|
||||
};
|
||||
Class<?> carrier = ((ValueLayout)layout).carrier();
|
||||
if (carrier == boolean.class || carrier == byte.class || carrier == char.class ||
|
||||
carrier == short.class || carrier == int.class || carrier == long.class) {
|
||||
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
|
||||
|
||||
@ -29,9 +29,10 @@ import jdk.incubator.foreign.GroupLayout;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.NativeSymbol;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import jdk.internal.foreign.Utils;
|
||||
import jdk.internal.foreign.abi.CallingSequenceBuilder;
|
||||
import jdk.internal.foreign.abi.UpcallHandler;
|
||||
import jdk.internal.foreign.abi.ABIDescriptor;
|
||||
import jdk.internal.foreign.abi.Binding;
|
||||
import jdk.internal.foreign.abi.CallingSequence;
|
||||
@ -82,8 +83,6 @@ public class CallArranger {
|
||||
}
|
||||
|
||||
public static Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall) {
|
||||
SharedUtils.checkFunctionTypes(mt, cDesc, Windowsx64Linker.ADDRESS_SIZE);
|
||||
|
||||
class CallingSequenceBuilderHelper {
|
||||
final CallingSequenceBuilder csb = new CallingSequenceBuilder(forUpcall);
|
||||
final BindingCalculator argCalc =
|
||||
@ -91,12 +90,12 @@ public class CallArranger {
|
||||
final BindingCalculator retCalc =
|
||||
forUpcall ? new UnboxBindingCalculator(false) : new BoxBindingCalculator(false);
|
||||
|
||||
void addArgumentBindings(Class<?> carrier, MemoryLayout layout) {
|
||||
csb.addArgumentBindings(carrier, layout, argCalc.getBindings(carrier, layout));
|
||||
void addArgumentBindings(Class<?> carrier, MemoryLayout layout, boolean isVararg) {
|
||||
csb.addArgumentBindings(carrier, layout, argCalc.getBindings(carrier, layout, isVararg));
|
||||
}
|
||||
|
||||
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();
|
||||
@ -105,7 +104,7 @@ public class CallArranger {
|
||||
if (returnInMemory) {
|
||||
Class<?> carrier = MemoryAddress.class;
|
||||
MemoryLayout layout = Win64.C_POINTER;
|
||||
csb.addArgumentBindings(carrier, layout);
|
||||
csb.addArgumentBindings(carrier, layout, false);
|
||||
if (forUpcall) {
|
||||
csb.setReturnBindings(carrier, layout);
|
||||
}
|
||||
@ -114,7 +113,7 @@ public class CallArranger {
|
||||
}
|
||||
|
||||
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));
|
||||
@ -134,14 +133,14 @@ public class CallArranger {
|
||||
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);
|
||||
|
||||
if (bindings.isInMemoryReturn) {
|
||||
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) {
|
||||
@ -185,7 +184,7 @@ public class CallArranger {
|
||||
}
|
||||
|
||||
private interface BindingCalculator {
|
||||
List<Binding> getBindings(Class<?> carrier, MemoryLayout layout);
|
||||
List<Binding> getBindings(Class<?> carrier, MemoryLayout layout, boolean isVararg);
|
||||
}
|
||||
|
||||
static class UnboxBindingCalculator implements BindingCalculator {
|
||||
@ -196,8 +195,8 @@ public class CallArranger {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Binding> getBindings(Class<?> carrier, MemoryLayout layout) {
|
||||
TypeClass argumentClass = TypeClass.typeClassFor(layout);
|
||||
public List<Binding> getBindings(Class<?> carrier, MemoryLayout layout, boolean isVararg) {
|
||||
TypeClass argumentClass = TypeClass.typeClassFor(layout, isVararg);
|
||||
Binding.Builder bindings = Binding.builder();
|
||||
switch (argumentClass) {
|
||||
case STRUCT_REGISTER: {
|
||||
@ -211,14 +210,13 @@ public class CallArranger {
|
||||
case STRUCT_REFERENCE: {
|
||||
assert carrier == MemorySegment.class;
|
||||
bindings.copy(layout)
|
||||
.baseAddress()
|
||||
.unboxAddress();
|
||||
.unboxAddress(MemorySegment.class);
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER, layout);
|
||||
bindings.vmStore(storage, long.class);
|
||||
break;
|
||||
}
|
||||
case POINTER: {
|
||||
bindings.unboxAddress();
|
||||
bindings.unboxAddress(carrier);
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER, layout);
|
||||
bindings.vmStore(storage, long.class);
|
||||
break;
|
||||
@ -259,8 +257,8 @@ public class CallArranger {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Binding> getBindings(Class<?> carrier, MemoryLayout layout) {
|
||||
TypeClass argumentClass = TypeClass.typeClassFor(layout);
|
||||
public List<Binding> getBindings(Class<?> carrier, MemoryLayout layout, boolean isVararg) {
|
||||
TypeClass argumentClass = TypeClass.typeClassFor(layout, isVararg);
|
||||
Binding.Builder bindings = Binding.builder();
|
||||
switch (argumentClass) {
|
||||
case STRUCT_REGISTER: {
|
||||
|
||||
@ -25,11 +25,9 @@
|
||||
package jdk.internal.foreign.abi.x64.windows;
|
||||
|
||||
import jdk.incubator.foreign.GroupLayout;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.ValueLayout;
|
||||
import jdk.internal.foreign.PlatformLayouts;
|
||||
|
||||
import static jdk.internal.foreign.PlatformLayouts.Win64.VARARGS_ATTRIBUTE_NAME;
|
||||
|
||||
enum TypeClass {
|
||||
STRUCT_REGISTER,
|
||||
@ -39,7 +37,7 @@ enum TypeClass {
|
||||
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
|
||||
// like a struct when passing as an argument (passed by pointer).
|
||||
// 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."
|
||||
// https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=vs-2019
|
||||
|
||||
return switch (PlatformLayouts.getKind(type)) {
|
||||
case CHAR, SHORT, INT, LONG, LONG_LONG -> INTEGER;
|
||||
case POINTER -> POINTER;
|
||||
case FLOAT, DOUBLE -> {
|
||||
if (type.attribute(VARARGS_ATTRIBUTE_NAME)
|
||||
.map(Boolean.class::cast).orElse(false)) {
|
||||
yield VARARG_FLOAT;
|
||||
}
|
||||
yield FLOAT;
|
||||
Class<?> carrier = type.carrier();
|
||||
if (carrier == boolean.class || carrier == byte.class || carrier == char.class ||
|
||||
carrier == short.class || carrier == int.class || carrier == long.class) {
|
||||
return INTEGER;
|
||||
} else if (carrier == float.class || carrier == double.class) {
|
||||
if (isVararg) {
|
||||
return VARARG_FLOAT;
|
||||
} else {
|
||||
return FLOAT;
|
||||
}
|
||||
};
|
||||
} else if (carrier == MemoryAddress.class) {
|
||||
return POINTER;
|
||||
} else {
|
||||
throw new IllegalStateException("Cannot get here: " + carrier.getName());
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isRegisterAggregate(MemoryLayout type) {
|
||||
@ -77,9 +79,9 @@ enum TypeClass {
|
||||
return STRUCT_REFERENCE;
|
||||
}
|
||||
|
||||
static TypeClass typeClassFor(MemoryLayout type) {
|
||||
static TypeClass typeClassFor(MemoryLayout type, boolean isVararg) {
|
||||
if (type instanceof ValueLayout) {
|
||||
return classifyValueType((ValueLayout) type);
|
||||
return classifyValueType((ValueLayout) type, isVararg);
|
||||
} else if (type instanceof GroupLayout) {
|
||||
return classifyStructType(type);
|
||||
} else {
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
package jdk.internal.foreign.abi.x64.windows;
|
||||
|
||||
import jdk.incubator.foreign.*;
|
||||
import jdk.incubator.foreign.CLinker.VaList;
|
||||
import jdk.internal.foreign.Scoped;
|
||||
import jdk.internal.foreign.ResourceScopeImpl;
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
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)))
|
||||
//
|
||||
public non-sealed class WinVaList implements VaList {
|
||||
public non-sealed class WinVaList implements VaList, Scoped {
|
||||
public static final Class<?> CARRIER = MemoryAddress.class;
|
||||
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);
|
||||
|
||||
@ -75,63 +75,54 @@ public non-sealed class WinVaList implements VaList {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int vargAsInt(MemoryLayout layout) {
|
||||
public int nextVarg(ValueLayout.OfInt layout) {
|
||||
return (int) read(int.class, layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long vargAsLong(MemoryLayout layout) {
|
||||
public long nextVarg(ValueLayout.OfLong layout) {
|
||||
return (long) read(long.class, layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double vargAsDouble(MemoryLayout layout) {
|
||||
public double nextVarg(ValueLayout.OfDouble layout) {
|
||||
return (double) read(double.class, layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryAddress vargAsAddress(MemoryLayout layout) {
|
||||
public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
|
||||
return (MemoryAddress) read(MemoryAddress.class, layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment vargAsSegment(MemoryLayout layout, SegmentAllocator allocator) {
|
||||
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(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) {
|
||||
return read(carrier, layout, SharedUtils.THROWING_ALLOCATOR);
|
||||
}
|
||||
|
||||
private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(layout);
|
||||
SharedUtils.checkCompatibleType(carrier, layout, Windowsx64Linker.ADDRESS_SIZE);
|
||||
Object res;
|
||||
if (carrier == MemorySegment.class) {
|
||||
TypeClass typeClass = TypeClass.typeClassFor(layout);
|
||||
TypeClass typeClass = TypeClass.typeClassFor(layout, false);
|
||||
res = switch (typeClass) {
|
||||
case STRUCT_REFERENCE -> {
|
||||
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);
|
||||
seg.copyFrom(struct);
|
||||
yield seg;
|
||||
}
|
||||
case STRUCT_REGISTER -> {
|
||||
MemorySegment struct = allocator.allocate(layout);
|
||||
struct.copyFrom(segment.asSlice(0L, layout.byteSize()));
|
||||
yield struct;
|
||||
}
|
||||
case STRUCT_REGISTER ->
|
||||
allocator.allocate(layout).copyFrom(segment.asSlice(0, layout.byteSize()));
|
||||
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
|
||||
};
|
||||
} else {
|
||||
VarHandle reader = SharedUtils.vhPrimitiveOrAddress(carrier, layout);
|
||||
VarHandle reader = layout.varHandle();
|
||||
res = reader.get(segment);
|
||||
}
|
||||
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
@ -141,12 +132,13 @@ public non-sealed class WinVaList implements VaList {
|
||||
@Override
|
||||
public void skip(MemoryLayout... layouts) {
|
||||
Objects.requireNonNull(layouts);
|
||||
((ResourceScopeImpl)scope).checkValidStateSlow();
|
||||
Stream.of(layouts).forEach(Objects::requireNonNull);
|
||||
segment = segment.asSlice(layouts.length * VA_SLOT_SIZE_BYTES);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -183,33 +175,32 @@ public non-sealed class WinVaList implements VaList {
|
||||
private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) {
|
||||
Objects.requireNonNull(layout);
|
||||
Objects.requireNonNull(value);
|
||||
SharedUtils.checkCompatibleType(carrier, layout, Windowsx64Linker.ADDRESS_SIZE);
|
||||
args.add(new SimpleVaArg(carrier, layout, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder vargFromInt(ValueLayout layout, int value) {
|
||||
public Builder addVarg(ValueLayout.OfInt layout, int value) {
|
||||
return arg(int.class, layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder vargFromLong(ValueLayout layout, long value) {
|
||||
public Builder addVarg(ValueLayout.OfLong layout, long value) {
|
||||
return arg(long.class, layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder vargFromDouble(ValueLayout layout, double value) {
|
||||
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
|
||||
return arg(double.class, layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder vargFromAddress(ValueLayout layout, Addressable value) {
|
||||
public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) {
|
||||
return arg(MemoryAddress.class, layout, value.address());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder vargFromSegment(GroupLayout layout, MemorySegment value) {
|
||||
public Builder addVarg(GroupLayout layout, MemorySegment value) {
|
||||
return arg(MemorySegment.class, layout, value);
|
||||
}
|
||||
|
||||
@ -217,7 +208,7 @@ public non-sealed class WinVaList implements VaList {
|
||||
if (args.isEmpty()) {
|
||||
return EMPTY;
|
||||
}
|
||||
SegmentAllocator allocator = SegmentAllocator.arenaAllocator(scope);
|
||||
SegmentAllocator allocator = SegmentAllocator.newNativeArena(scope);
|
||||
MemorySegment segment = allocator.allocate(VA_SLOT_SIZE_BYTES * args.size());
|
||||
List<MemorySegment> attachedSegments = new ArrayList<>();
|
||||
attachedSegments.add(segment);
|
||||
@ -226,7 +217,7 @@ public non-sealed class WinVaList implements VaList {
|
||||
for (SimpleVaArg arg : args) {
|
||||
if (arg.carrier == MemorySegment.class) {
|
||||
MemorySegment msArg = ((MemorySegment) arg.value);
|
||||
TypeClass typeClass = TypeClass.typeClassFor(arg.layout);
|
||||
TypeClass typeClass = TypeClass.typeClassFor(arg.layout, false);
|
||||
switch (typeClass) {
|
||||
case STRUCT_REFERENCE -> {
|
||||
MemorySegment copy = allocator.allocate(arg.layout);
|
||||
@ -234,10 +225,8 @@ public non-sealed class WinVaList implements VaList {
|
||||
attachedSegments.add(copy);
|
||||
VH_address.set(cursor, copy.address());
|
||||
}
|
||||
case STRUCT_REGISTER -> {
|
||||
MemorySegment slice = cursor.asSlice(0, VA_SLOT_SIZE_BYTES);
|
||||
slice.copyFrom(msArg);
|
||||
}
|
||||
case STRUCT_REGISTER ->
|
||||
cursor.copyFrom(msArg.asSlice(0, VA_SLOT_SIZE_BYTES));
|
||||
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -24,14 +24,14 @@
|
||||
*/
|
||||
package jdk.internal.foreign.abi.x64.windows;
|
||||
|
||||
import jdk.incubator.foreign.CLinker;
|
||||
import jdk.incubator.foreign.FunctionDescriptor;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.NativeSymbol;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import jdk.internal.foreign.AbstractCLinker;
|
||||
import jdk.internal.foreign.ResourceScopeImpl;
|
||||
import jdk.incubator.foreign.VaList;
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
import jdk.internal.foreign.abi.UpcallStubs;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
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
|
||||
*/
|
||||
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_RETURN_REGISTERS = 1;
|
||||
@ -55,21 +55,6 @@ public final class Windowsx64Linker extends AbstractCLinker {
|
||||
|
||||
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() {
|
||||
if (instance == null) {
|
||||
instance = new Windowsx64Linker();
|
||||
@ -84,26 +69,28 @@ public final class Windowsx64Linker extends AbstractCLinker {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final MethodHandle downcallHandle(MethodType type, FunctionDescriptor function) {
|
||||
Objects.requireNonNull(type);
|
||||
public final MethodHandle downcallHandle(FunctionDescriptor function) {
|
||||
Objects.requireNonNull(function);
|
||||
MethodType llMt = SharedUtils.convertVaListCarriers(type, WinVaList.CARRIER);
|
||||
MethodHandle handle = CallArranger.arrangeDowncall(llMt, function);
|
||||
MethodType type = SharedUtils.inferMethodType(function, false);
|
||||
MethodHandle handle = CallArranger.arrangeDowncall(type, function);
|
||||
if (!type.returnType().equals(MemorySegment.class)) {
|
||||
// not returning segment, just insert a throwing allocator
|
||||
handle = MethodHandles.insertArguments(handle, 1, SharedUtils.THROWING_ALLOCATOR);
|
||||
}
|
||||
handle = SharedUtils.unboxVaLists(type, handle, MH_unboxVaList);
|
||||
return handle;
|
||||
return SharedUtils.wrapDowncall(handle, function);
|
||||
}
|
||||
|
||||
@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(target);
|
||||
Objects.requireNonNull(function);
|
||||
target = SharedUtils.boxVaLists(target, MH_boxVaList);
|
||||
return UpcallStubs.upcallAddress(CallArranger.arrangeUpcall(target, target.type(), function), (ResourceScopeImpl) scope);
|
||||
SharedUtils.checkExceptions(target);
|
||||
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) {
|
||||
|
||||
@ -40,22 +40,19 @@
|
||||
import jdk.incubator.foreign.CLinker;
|
||||
import jdk.incubator.foreign.FunctionDescriptor;
|
||||
import jdk.incubator.foreign.SymbolLookup;
|
||||
import jdk.incubator.foreign.ValueLayout;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
|
||||
import static jdk.incubator.foreign.CLinker.C_INT;
|
||||
|
||||
public class TestLinkToNativeRBP {
|
||||
static {
|
||||
System.loadLibrary("LinkToNativeRBP");
|
||||
}
|
||||
|
||||
final static CLinker abi = CLinker.getInstance();
|
||||
final static CLinker abi = CLinker.systemCLinker();
|
||||
static final SymbolLookup lookup = SymbolLookup.loaderLookup();
|
||||
final static MethodHandle foo = abi.downcallHandle(lookup.lookup("foo").get(),
|
||||
MethodType.methodType(int.class),
|
||||
FunctionDescriptor.of(C_INT));
|
||||
FunctionDescriptor.of(ValueLayout.JAVA_INT));
|
||||
|
||||
static int foo() throws Throwable {
|
||||
return (int)foo.invokeExact();
|
||||
|
||||
@ -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.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.NativeSymbol;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import jdk.incubator.foreign.SegmentAllocator;
|
||||
import jdk.incubator.foreign.ValueLayout;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -40,12 +45,13 @@ import java.util.stream.IntStream;
|
||||
|
||||
import org.testng.annotations.*;
|
||||
|
||||
import static jdk.incubator.foreign.CLinker.*;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
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"));
|
||||
|
||||
@ -58,7 +64,7 @@ public class CallGeneratorHelper extends NativeTestHelper {
|
||||
GroupLayout g = (GroupLayout) layout;
|
||||
for (MemoryLayout field : g.memberLayouts()) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
@ -410,12 +416,9 @@ public class CallGeneratorHelper extends NativeTestHelper {
|
||||
static void initStruct(MemorySegment str, GroupLayout g, List<Consumer<Object>> checks, boolean check) throws ReflectiveOperationException {
|
||||
for (MemoryLayout l : g.memberLayouts()) {
|
||||
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<>();
|
||||
Object value = makeArg(l, fieldsCheck, check);
|
||||
if (isPointer(l)) {
|
||||
value = ((MemoryAddress)value).toRawLongValue();
|
||||
}
|
||||
//set value
|
||||
accessor.set(str, value);
|
||||
//add check
|
||||
@ -424,11 +427,7 @@ public class CallGeneratorHelper extends NativeTestHelper {
|
||||
checks.add(o -> {
|
||||
MemorySegment actual = (MemorySegment)o;
|
||||
try {
|
||||
if (isPointer(l)) {
|
||||
fieldsCheck.get(0).accept(MemoryAddress.ofLong((long)accessor.get(actual)));
|
||||
} else {
|
||||
fieldsCheck.get(0).accept(accessor.get(actual));
|
||||
}
|
||||
fieldsCheck.get(0).accept(accessor.get(actual));
|
||||
} catch (Throwable ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
@ -437,37 +436,23 @@ public class CallGeneratorHelper extends NativeTestHelper {
|
||||
}
|
||||
}
|
||||
|
||||
static Class<?> structFieldCarrier(MemoryLayout layout) {
|
||||
if (isPointer(layout)) {
|
||||
return long.class;
|
||||
} else if (layout instanceof ValueLayout) {
|
||||
if (isIntegral(layout)) {
|
||||
return int.class;
|
||||
} else if (layout.bitSize() == 32) {
|
||||
return float.class;
|
||||
} else {
|
||||
return double.class;
|
||||
}
|
||||
static Class<?> carrier(MemoryLayout layout, boolean param) {
|
||||
if (layout instanceof GroupLayout) {
|
||||
return MemorySegment.class;
|
||||
} if (isPointer(layout)) {
|
||||
return param ? Addressable.class : MemoryAddress.class;
|
||||
} else if (layout instanceof ValueLayout valueLayout) {
|
||||
return valueLayout.carrier();
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected layout: " + layout);
|
||||
}
|
||||
}
|
||||
|
||||
static Class<?> paramCarrier(MemoryLayout layout) {
|
||||
if (layout instanceof GroupLayout) {
|
||||
return MemorySegment.class;
|
||||
} if (isPointer(layout)) {
|
||||
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);
|
||||
MethodHandle downcallHandle(CLinker abi, NativeSymbol symbol, SegmentAllocator allocator, FunctionDescriptor descriptor) {
|
||||
MethodHandle mh = abi.downcallHandle(symbol, descriptor);
|
||||
if (descriptor.returnLayout().isPresent() && descriptor.returnLayout().get() instanceof GroupLayout) {
|
||||
mh = mh.bindTo(allocator);
|
||||
}
|
||||
return mh;
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,58 +22,90 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import jdk.incubator.foreign.Addressable;
|
||||
import jdk.incubator.foreign.CLinker;
|
||||
import jdk.incubator.foreign.FunctionDescriptor;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import jdk.incubator.foreign.SegmentAllocator;
|
||||
import jdk.incubator.foreign.ValueLayout;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
|
||||
public class NativeTestHelper {
|
||||
|
||||
static CLinker.TypeKind kind(MemoryLayout layout) {
|
||||
return (CLinker.TypeKind)layout.attribute(CLinker.TypeKind.ATTR_NAME).orElseThrow(
|
||||
() -> new IllegalStateException("Unexpected value layout: could not determine ABI class"));
|
||||
public static boolean isIntegral(MemoryLayout layout) {
|
||||
return layout instanceof ValueLayout valueLayout && isIntegral(valueLayout.carrier());
|
||||
}
|
||||
|
||||
public static boolean isIntegral(MemoryLayout layout) {
|
||||
return kind(layout).isIntegral();
|
||||
static boolean isIntegral(Class<?> clazz) {
|
||||
return clazz == byte.class || clazz == char.class || clazz == short.class
|
||||
|| clazz == int.class || clazz == long.class;
|
||||
}
|
||||
|
||||
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 {
|
||||
final ResourceScope resourceScope;
|
||||
final ResourceScope.Handle scopeHandle;
|
||||
final SegmentAllocator allocator;
|
||||
// the constants below are useful aliases for C types. The type/carrier association is only valid for 64-bit platforms.
|
||||
|
||||
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();
|
||||
this.scopeHandle = resourceScope.acquire();
|
||||
this.allocator = SegmentAllocator.arenaAllocator(resourceScope);
|
||||
/**
|
||||
* The layout for the {@code long long} C type.
|
||||
*/
|
||||
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 MemorySegment allocate(long bytesSize, long bytesAlignment) {
|
||||
allocatedBytes += bytesSize;
|
||||
return allocator.allocate(bytesSize, bytesAlignment);
|
||||
}
|
||||
|
||||
public ResourceScope scope() {
|
||||
return resourceScope;
|
||||
}
|
||||
|
||||
public long allocatedBytes() {
|
||||
return allocatedBytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
resourceScope.release(scopeHandle);
|
||||
resourceScope.close();
|
||||
public static MemoryAddress allocateMemory(long size) {
|
||||
try {
|
||||
return (MemoryAddress)MALLOC.invokeExact(size);
|
||||
} catch (Throwable ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,27 +27,31 @@
|
||||
* @run testng/othervm --enable-native-access=ALL-UNNAMED SafeFunctionAccessTest
|
||||
*/
|
||||
|
||||
import jdk.incubator.foreign.Addressable;
|
||||
import jdk.incubator.foreign.CLinker;
|
||||
import jdk.incubator.foreign.FunctionDescriptor;
|
||||
import jdk.incubator.foreign.NativeSymbol;
|
||||
import jdk.incubator.foreign.SymbolLookup;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
|
||||
import jdk.incubator.foreign.VaList;
|
||||
import org.testng.annotations.*;
|
||||
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class SafeFunctionAccessTest {
|
||||
public class SafeFunctionAccessTest extends NativeTestHelper {
|
||||
static {
|
||||
System.loadLibrary("SafeAccess");
|
||||
}
|
||||
|
||||
static MemoryLayout POINT = MemoryLayout.structLayout(
|
||||
CLinker.C_INT, CLinker.C_INT
|
||||
C_INT, C_INT
|
||||
);
|
||||
|
||||
static final SymbolLookup LOOKUP = SymbolLookup.loaderLookup();
|
||||
@ -59,26 +63,135 @@ public class SafeFunctionAccessTest {
|
||||
segment = MemorySegment.allocateNative(POINT, scope);
|
||||
}
|
||||
assertFalse(segment.scope().isAlive());
|
||||
MethodHandle handle = CLinker.getInstance().downcallHandle(
|
||||
MethodHandle handle = CLinker.systemCLinker().downcallHandle(
|
||||
LOOKUP.lookup("struct_func").get(),
|
||||
MethodType.methodType(void.class, MemorySegment.class),
|
||||
FunctionDescriptor.ofVoid(POINT));
|
||||
|
||||
handle.invokeExact(segment);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalStateException.class)
|
||||
public void testClosedPointer() throws Throwable {
|
||||
MemoryAddress address;
|
||||
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
||||
address = MemorySegment.allocateNative(POINT, scope).address();
|
||||
@Test
|
||||
public void testClosedStructAddr_6() throws Throwable {
|
||||
MethodHandle handle = CLinker.systemCLinker().downcallHandle(
|
||||
LOOKUP.lookup("addr_func_6").get(),
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,7 +39,6 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
@ -48,17 +47,14 @@ import java.util.stream.Stream;
|
||||
|
||||
import jdk.incubator.foreign.*;
|
||||
|
||||
import static jdk.incubator.foreign.MemoryAccess.*;
|
||||
|
||||
import org.testng.annotations.*;
|
||||
|
||||
import static jdk.incubator.foreign.CLinker.*;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
@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();
|
||||
|
||||
@ -155,45 +151,36 @@ public class StdLibTest {
|
||||
|
||||
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(),
|
||||
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),
|
||||
final static MethodHandle strcmp = abi.downcallHandle(abi.lookup("strcmp").get(),
|
||||
FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER));
|
||||
|
||||
final static MethodHandle puts = abi.downcallHandle(LOOKUP.lookup("puts").get(),
|
||||
MethodType.methodType(int.class, MemoryAddress.class),
|
||||
final static MethodHandle puts = abi.downcallHandle(abi.lookup("puts").get(),
|
||||
FunctionDescriptor.of(C_INT, C_POINTER));
|
||||
|
||||
final static MethodHandle strlen = abi.downcallHandle(LOOKUP.lookup("strlen").get(),
|
||||
MethodType.methodType(int.class, MemoryAddress.class),
|
||||
final static MethodHandle strlen = abi.downcallHandle(abi.lookup("strlen").get(),
|
||||
FunctionDescriptor.of(C_INT, C_POINTER));
|
||||
|
||||
final static MethodHandle gmtime = abi.downcallHandle(LOOKUP.lookup("gmtime").get(),
|
||||
MethodType.methodType(MemoryAddress.class, MemoryAddress.class),
|
||||
final static MethodHandle gmtime = abi.downcallHandle(abi.lookup("gmtime").get(),
|
||||
FunctionDescriptor.of(C_POINTER, C_POINTER));
|
||||
|
||||
final static MethodHandle qsort = abi.downcallHandle(LOOKUP.lookup("qsort").get(),
|
||||
MethodType.methodType(void.class, MemoryAddress.class, long.class, long.class, MemoryAddress.class),
|
||||
final static MethodHandle qsort = abi.downcallHandle(abi.lookup("qsort").get(),
|
||||
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 MethodHandle qsortCompar;
|
||||
|
||||
final static MethodHandle rand = abi.downcallHandle(LOOKUP.lookup("rand").get(),
|
||||
MethodType.methodType(int.class),
|
||||
final static MethodHandle rand = abi.downcallHandle(abi.lookup("rand").get(),
|
||||
FunctionDescriptor.of(C_INT));
|
||||
|
||||
final static MethodHandle vprintf = abi.downcallHandle(LOOKUP.lookup("vprintf").get(),
|
||||
MethodType.methodType(int.class, MemoryAddress.class, VaList.class),
|
||||
FunctionDescriptor.of(C_INT, C_POINTER, C_VA_LIST));
|
||||
final static MethodHandle vprintf = abi.downcallHandle(abi.lookup("vprintf").get(),
|
||||
FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER));
|
||||
|
||||
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);
|
||||
|
||||
@ -201,7 +188,7 @@ public class StdLibTest {
|
||||
try {
|
||||
//qsort upcall handle
|
||||
qsortCompar = MethodHandles.lookup().findStatic(StdLibTest.StdLibHelper.class, "qsortCompare",
|
||||
MethodType.methodType(int.class, MemorySegment.class, MemoryAddress.class, MemoryAddress.class));
|
||||
CLinker.upcallType(qsortComparFunction));
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
@ -209,44 +196,44 @@ public class StdLibTest {
|
||||
|
||||
String strcat(String s1, String s2) throws Throwable {
|
||||
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
||||
MemorySegment buf = MemorySegment.allocateNative(s1.length() + s2.length() + 1, scope);
|
||||
MemorySegment other = toCString(s2, scope);
|
||||
char[] chars = s1.toCharArray();
|
||||
for (long i = 0 ; i < chars.length ; i++) {
|
||||
setByteAtOffset(buf, i, (byte)chars[(int)i]);
|
||||
}
|
||||
setByteAtOffset(buf, chars.length, (byte)'\0');
|
||||
return toJavaString(((MemoryAddress)strcat.invokeExact(buf.address(), other.address())));
|
||||
var malloc = SegmentAllocator.nativeAllocator(scope);
|
||||
MemorySegment buf = malloc.allocate(s1.length() + s2.length() + 1);
|
||||
buf.setUtf8String(0, s1);
|
||||
MemorySegment other = malloc.allocateUtf8String(s2);
|
||||
return ((MemoryAddress)strcat.invokeExact(buf, other)).getUtf8String(0);
|
||||
}
|
||||
}
|
||||
|
||||
int strcmp(String s1, String s2) throws Throwable {
|
||||
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
||||
MemorySegment ns1 = toCString(s1, scope);
|
||||
MemorySegment ns2 = toCString(s2, scope);
|
||||
return (int)strcmp.invokeExact(ns1.address(), ns2.address());
|
||||
var malloc = SegmentAllocator.nativeAllocator(scope);
|
||||
MemorySegment ns1 = malloc.allocateUtf8String(s1);
|
||||
MemorySegment ns2 = malloc.allocateUtf8String(s2);
|
||||
return (int)strcmp.invoke(ns1, ns2);
|
||||
}
|
||||
}
|
||||
|
||||
int puts(String msg) throws Throwable {
|
||||
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
||||
MemorySegment s = toCString(msg, scope);
|
||||
return (int)puts.invokeExact(s.address());
|
||||
var malloc = SegmentAllocator.nativeAllocator(scope);
|
||||
MemorySegment s = malloc.allocateUtf8String(msg);
|
||||
return (int)puts.invoke(s);
|
||||
}
|
||||
}
|
||||
|
||||
int strlen(String msg) throws Throwable {
|
||||
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
||||
MemorySegment s = toCString(msg, scope);
|
||||
return (int)strlen.invokeExact(s.address());
|
||||
var malloc = SegmentAllocator.nativeAllocator(scope);
|
||||
MemorySegment s = malloc.allocateUtf8String(msg);
|
||||
return (int)strlen.invoke(s);
|
||||
}
|
||||
}
|
||||
|
||||
Tm gmtime(long arg) throws Throwable {
|
||||
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
||||
MemorySegment time = MemorySegment.allocateNative(8, scope);
|
||||
setLong(time, arg);
|
||||
return new Tm((MemoryAddress)gmtime.invokeExact(time.address()));
|
||||
time.set(C_LONG_LONG, 0, arg);
|
||||
return new Tm((MemoryAddress)gmtime.invoke(time));
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,58 +245,57 @@ public class StdLibTest {
|
||||
static final long SIZE = 56;
|
||||
|
||||
Tm(MemoryAddress addr) {
|
||||
this.base = addr.asSegment(SIZE, ResourceScope.globalScope());
|
||||
this.base = MemorySegment.ofAddress(addr, SIZE, ResourceScope.globalScope());
|
||||
}
|
||||
|
||||
int sec() {
|
||||
return getIntAtOffset(base, 0);
|
||||
return base.get(C_INT, 0);
|
||||
}
|
||||
int min() {
|
||||
return getIntAtOffset(base, 4);
|
||||
return base.get(C_INT, 4);
|
||||
}
|
||||
int hour() {
|
||||
return getIntAtOffset(base, 8);
|
||||
return base.get(C_INT, 8);
|
||||
}
|
||||
int mday() {
|
||||
return getIntAtOffset(base, 12);
|
||||
return base.get(C_INT, 12);
|
||||
}
|
||||
int mon() {
|
||||
return getIntAtOffset(base, 16);
|
||||
return base.get(C_INT, 16);
|
||||
}
|
||||
int year() {
|
||||
return getIntAtOffset(base, 20);
|
||||
return base.get(C_INT, 20);
|
||||
}
|
||||
int wday() {
|
||||
return getIntAtOffset(base, 24);
|
||||
return base.get(C_INT, 24);
|
||||
}
|
||||
int yday() {
|
||||
return getIntAtOffset(base, 28);
|
||||
return base.get(C_INT, 28);
|
||||
}
|
||||
boolean isdst() {
|
||||
byte b = getByteAtOffset(base, 32);
|
||||
return b != 0;
|
||||
return base.get(C_BOOL, 32);
|
||||
}
|
||||
}
|
||||
|
||||
int[] qsort(int[] arr) throws Throwable {
|
||||
//init native array
|
||||
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
||||
SegmentAllocator allocator = SegmentAllocator.ofScope(scope);
|
||||
MemorySegment nativeArr = allocator.allocateArray(C_INT, arr);
|
||||
var malloc = SegmentAllocator.nativeAllocator(scope);
|
||||
MemorySegment nativeArr = malloc.allocateArray(C_INT, arr);
|
||||
|
||||
//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
|
||||
return nativeArr.toIntArray();
|
||||
return nativeArr.toArray(C_INT);
|
||||
}
|
||||
}
|
||||
|
||||
static int qsortCompare(MemorySegment base, MemoryAddress addr1, MemoryAddress addr2) {
|
||||
return getIntAtOffset(base, addr1.segmentOffset(base)) -
|
||||
getIntAtOffset(base, addr2.segmentOffset(base));
|
||||
static int qsortCompare(MemoryAddress addr1, MemoryAddress addr2) {
|
||||
return addr1.get(C_INT, 0) -
|
||||
addr2.get(C_INT, 0);
|
||||
}
|
||||
|
||||
int rand() throws Throwable {
|
||||
@ -318,17 +304,19 @@ public class StdLibTest {
|
||||
|
||||
int printf(String format, List<PrintfArg> args) throws Throwable {
|
||||
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
||||
MemorySegment formatStr = toCString(format, scope);
|
||||
return (int)specializedPrintf(args).invokeExact(formatStr.address(),
|
||||
var malloc = SegmentAllocator.nativeAllocator(scope);
|
||||
MemorySegment formatStr = malloc.allocateUtf8String(format);
|
||||
return (int)specializedPrintf(args).invoke(formatStr,
|
||||
args.stream().map(a -> a.nativeValue(scope)).toArray());
|
||||
}
|
||||
}
|
||||
|
||||
int vprintf(String format, List<PrintfArg> args) throws Throwable {
|
||||
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);
|
||||
return (int)vprintf.invokeExact(formatStr.address(), vaList);
|
||||
return (int)vprintf.invoke(formatStr, vaList);
|
||||
}
|
||||
}
|
||||
|
||||
@ -336,11 +324,13 @@ public class StdLibTest {
|
||||
//method type
|
||||
MethodType mt = MethodType.methodType(int.class, MemoryAddress.class);
|
||||
FunctionDescriptor fd = printfBase;
|
||||
List<MemoryLayout> variadicLayouts = new ArrayList<>(args.size());
|
||||
for (PrintfArg arg : args) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
@ -401,10 +391,14 @@ public class StdLibTest {
|
||||
|
||||
enum PrintfArg implements BiConsumer<VaList.Builder, ResourceScope> {
|
||||
|
||||
INTEGRAL(int.class, asVarArg(C_INT), "%d", scope -> 42, 42, VaList.Builder::vargFromInt),
|
||||
STRING(MemoryAddress.class, asVarArg(C_POINTER), "%s", scope -> toCString("str", scope).address(), "str", VaList.Builder::vargFromAddress),
|
||||
CHAR(byte.class, asVarArg(C_CHAR), "%c", scope -> (byte) 'h', 'h', (builder, layout, value) -> builder.vargFromInt(C_INT, (int)value)),
|
||||
DOUBLE(double.class, asVarArg(C_DOUBLE), "%.4f", scope ->1.2345d, 1.2345d, VaList.Builder::vargFromDouble);
|
||||
INTEGRAL(int.class, C_INT, "%d", scope -> 42, 42, VaList.Builder::addVarg),
|
||||
STRING(MemoryAddress.class, C_POINTER, "%s", scope -> {
|
||||
var segment = MemorySegment.allocateNative(4, scope);
|
||||
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 ValueLayout layout;
|
||||
@ -414,7 +408,7 @@ public class StdLibTest {
|
||||
@SuppressWarnings("rawtypes")
|
||||
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.layout = layout;
|
||||
this.format = format;
|
||||
@ -429,8 +423,8 @@ public class StdLibTest {
|
||||
builderCall.build(builder, layout, nativeValueFactory.apply(scope));
|
||||
}
|
||||
|
||||
interface VaListBuilderCall<V> {
|
||||
void build(VaList.Builder builder, ValueLayout layout, V value);
|
||||
interface VaListBuilderCall<V, L> {
|
||||
void build(VaList.Builder builder, L layout, V value);
|
||||
}
|
||||
|
||||
public Object nativeValue(ResourceScope scope) {
|
||||
|
||||
@ -32,7 +32,6 @@
|
||||
|
||||
import jdk.incubator.foreign.MemoryHandles;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import jdk.incubator.foreign.ValueLayout;
|
||||
@ -86,18 +85,18 @@ public class TestAdaptVarHandles {
|
||||
}
|
||||
}
|
||||
|
||||
static final VarHandle intHandleIndexed = MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT)
|
||||
.varHandle(int.class, MemoryLayout.PathElement.sequenceElement());
|
||||
static final VarHandle intHandleIndexed = MemoryLayout.sequenceLayout(ValueLayout.JAVA_INT)
|
||||
.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
|
||||
public void testFilterValue() throws Throwable {
|
||||
ValueLayout layout = MemoryLayouts.JAVA_INT;
|
||||
ValueLayout layout = ValueLayout.JAVA_INT;
|
||||
MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope());
|
||||
VarHandle intHandle = layout.varHandle(int.class);
|
||||
VarHandle intHandle = layout.varHandle();
|
||||
VarHandle i2SHandle = MemoryHandles.filterValue(intHandle, S2I, I2S);
|
||||
i2SHandle.set(segment, "1");
|
||||
String oldValue = (String)i2SHandle.getAndAdd(segment, "42");
|
||||
@ -114,9 +113,9 @@ public class TestAdaptVarHandles {
|
||||
|
||||
@Test
|
||||
public void testFilterValueComposite() throws Throwable {
|
||||
ValueLayout layout = MemoryLayouts.JAVA_INT;
|
||||
ValueLayout layout = ValueLayout.JAVA_INT;
|
||||
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);
|
||||
VarHandle i2SHandle = MemoryHandles.filterValue(intHandle, CTX_S2I, CTX_I2S);
|
||||
i2SHandle = MemoryHandles.insertCoordinates(i2SHandle, 1, "a", "b");
|
||||
@ -135,9 +134,9 @@ public class TestAdaptVarHandles {
|
||||
|
||||
@Test
|
||||
public void testFilterValueLoose() throws Throwable {
|
||||
ValueLayout layout = MemoryLayouts.JAVA_INT;
|
||||
ValueLayout layout = ValueLayout.JAVA_INT;
|
||||
MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope());
|
||||
VarHandle intHandle = layout.varHandle(int.class);
|
||||
VarHandle intHandle = layout.varHandle();
|
||||
VarHandle i2SHandle = MemoryHandles.filterValue(intHandle, O2I, I2O);
|
||||
i2SHandle.set(segment, "1");
|
||||
String oldValue = (String)i2SHandle.getAndAdd(segment, "42");
|
||||
@ -159,19 +158,19 @@ public class TestAdaptVarHandles {
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadFilterUnboxArity() {
|
||||
VarHandle floatHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
|
||||
VarHandle floatHandle = ValueLayout.JAVA_INT.varHandle();
|
||||
MemoryHandles.filterValue(floatHandle, S2I.bindTo(""), I2S);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadFilterBoxArity() {
|
||||
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
|
||||
VarHandle intHandle = ValueLayout.JAVA_INT.varHandle();
|
||||
MemoryHandles.filterValue(intHandle, S2I, I2S.bindTo(42));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadFilterBoxPrefixCoordinates() {
|
||||
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
|
||||
VarHandle intHandle = ValueLayout.JAVA_INT.varHandle();
|
||||
MemoryHandles.filterValue(intHandle,
|
||||
MethodHandles.dropArguments(S2I, 1, int.class),
|
||||
MethodHandles.dropArguments(I2S, 1, long.class));
|
||||
@ -179,31 +178,40 @@ public class TestAdaptVarHandles {
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadFilterBoxException() {
|
||||
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
|
||||
VarHandle intHandle = ValueLayout.JAVA_INT.varHandle();
|
||||
MemoryHandles.filterValue(intHandle, I2S, S2L_EX);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadFilterUnboxException() {
|
||||
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
|
||||
VarHandle intHandle = ValueLayout.JAVA_INT.varHandle();
|
||||
MemoryHandles.filterValue(intHandle, S2L_EX, I2S);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
@Test(expectedExceptions = IllegalStateException.class)
|
||||
public void testBadFilterBoxHandleException() {
|
||||
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
|
||||
MemoryHandles.filterValue(intHandle, S2I, I2S_EX);
|
||||
VarHandle intHandle = ValueLayout.JAVA_INT.varHandle();
|
||||
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() {
|
||||
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
|
||||
MemoryHandles.filterValue(intHandle, S2I_EX, I2S);
|
||||
VarHandle intHandle = ValueLayout.JAVA_INT.varHandle();
|
||||
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
|
||||
public void testFilterCoordinates() throws Throwable {
|
||||
ValueLayout layout = MemoryLayouts.JAVA_INT;
|
||||
ValueLayout layout = ValueLayout.JAVA_INT;
|
||||
MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope());
|
||||
VarHandle intHandle_longIndex = MemoryHandles.filterCoordinates(intHandleIndexed, 0, BASE_ADDR, S2L);
|
||||
intHandle_longIndex.set(segment, "0", 1);
|
||||
@ -246,7 +254,7 @@ public class TestAdaptVarHandles {
|
||||
|
||||
@Test
|
||||
public void testInsertCoordinates() throws Throwable {
|
||||
ValueLayout layout = MemoryLayouts.JAVA_INT;
|
||||
ValueLayout layout = ValueLayout.JAVA_INT;
|
||||
MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope());
|
||||
VarHandle intHandle_longIndex = MemoryHandles.insertCoordinates(intHandleIndexed, 0, segment, 0L);
|
||||
intHandle_longIndex.set(1);
|
||||
@ -284,7 +292,7 @@ public class TestAdaptVarHandles {
|
||||
|
||||
@Test
|
||||
public void testPermuteCoordinates() throws Throwable {
|
||||
ValueLayout layout = MemoryLayouts.JAVA_INT;
|
||||
ValueLayout layout = ValueLayout.JAVA_INT;
|
||||
MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope());
|
||||
VarHandle intHandle_swap = MemoryHandles.permuteCoordinates(intHandleIndexed,
|
||||
List.of(long.class, MemorySegment.class), 1, 0);
|
||||
@ -323,7 +331,7 @@ public class TestAdaptVarHandles {
|
||||
|
||||
@Test
|
||||
public void testCollectCoordinates() throws Throwable {
|
||||
ValueLayout layout = MemoryLayouts.JAVA_INT;
|
||||
ValueLayout layout = ValueLayout.JAVA_INT;
|
||||
MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope());
|
||||
VarHandle intHandle_sum = MemoryHandles.collectCoordinates(intHandleIndexed, 1, SUM_OFFSETS);
|
||||
intHandle_sum.set(segment, -2L, 2L, 1);
|
||||
@ -366,7 +374,7 @@ public class TestAdaptVarHandles {
|
||||
|
||||
@Test
|
||||
public void testDropCoordinates() throws Throwable {
|
||||
ValueLayout layout = MemoryLayouts.JAVA_INT;
|
||||
ValueLayout layout = ValueLayout.JAVA_INT;
|
||||
MemorySegment segment = MemorySegment.allocateNative(layout, ResourceScope.newImplicitScope());
|
||||
VarHandle intHandle_dummy = MemoryHandles.dropCoordinates(intHandleIndexed, 1, float.class, String.class);
|
||||
intHandle_dummy.set(segment, 1f, "hello", 0L, 1);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
542
test/jdk/java/foreign/TestArrayCopy.java
Normal file
542
test/jdk/java/foreign/TestArrayCopy.java
Normal 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);
|
||||
}
|
||||
}
|
||||
@ -30,7 +30,6 @@
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemoryLayout.PathElement;
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import jdk.incubator.foreign.SequenceLayout;
|
||||
@ -43,45 +42,52 @@ import java.util.function.Function;
|
||||
|
||||
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.*;
|
||||
|
||||
public class TestArrays {
|
||||
|
||||
static SequenceLayout bytes = MemoryLayout.sequenceLayout(100,
|
||||
MemoryLayouts.JAVA_BYTE
|
||||
JAVA_BYTE
|
||||
);
|
||||
|
||||
static SequenceLayout chars = MemoryLayout.sequenceLayout(100,
|
||||
MemoryLayouts.JAVA_CHAR
|
||||
JAVA_CHAR
|
||||
);
|
||||
|
||||
static SequenceLayout shorts = MemoryLayout.sequenceLayout(100,
|
||||
MemoryLayouts.JAVA_SHORT
|
||||
JAVA_SHORT
|
||||
);
|
||||
|
||||
static SequenceLayout ints = MemoryLayout.sequenceLayout(100,
|
||||
MemoryLayouts.JAVA_INT
|
||||
JAVA_INT
|
||||
);
|
||||
|
||||
static SequenceLayout floats = MemoryLayout.sequenceLayout(100,
|
||||
MemoryLayouts.JAVA_FLOAT
|
||||
JAVA_FLOAT
|
||||
);
|
||||
|
||||
static SequenceLayout longs = MemoryLayout.sequenceLayout(100,
|
||||
MemoryLayouts.JAVA_LONG
|
||||
JAVA_LONG
|
||||
);
|
||||
|
||||
static SequenceLayout doubles = MemoryLayout.sequenceLayout(100,
|
||||
MemoryLayouts.JAVA_DOUBLE
|
||||
JAVA_DOUBLE
|
||||
);
|
||||
|
||||
static VarHandle byteHandle = bytes.varHandle(byte.class, PathElement.sequenceElement());
|
||||
static VarHandle charHandle = chars.varHandle(char.class, PathElement.sequenceElement());
|
||||
static VarHandle shortHandle = shorts.varHandle(short.class, PathElement.sequenceElement());
|
||||
static VarHandle intHandle = ints.varHandle(int.class, PathElement.sequenceElement());
|
||||
static VarHandle floatHandle = floats.varHandle(float.class, PathElement.sequenceElement());
|
||||
static VarHandle longHandle = longs.varHandle(long.class, PathElement.sequenceElement());
|
||||
static VarHandle doubleHandle = doubles.varHandle(double.class, PathElement.sequenceElement());
|
||||
static VarHandle byteHandle = bytes.varHandle(PathElement.sequenceElement());
|
||||
static VarHandle charHandle = chars.varHandle(PathElement.sequenceElement());
|
||||
static VarHandle shortHandle = shorts.varHandle(PathElement.sequenceElement());
|
||||
static VarHandle intHandle = ints.varHandle(PathElement.sequenceElement());
|
||||
static VarHandle floatHandle = floats.varHandle(PathElement.sequenceElement());
|
||||
static VarHandle longHandle = longs.varHandle(PathElement.sequenceElement());
|
||||
static VarHandle doubleHandle = doubles.varHandle(PathElement.sequenceElement());
|
||||
|
||||
static void initBytes(MemorySegment base, SequenceLayout seq, BiConsumer<MemorySegment, Long> handleSetter) {
|
||||
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) {
|
||||
MemoryLayout seq = MemoryLayout.sequenceLayout((Integer.MAX_VALUE * layout.byteSize()) + 1, layout);
|
||||
//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);
|
||||
}
|
||||
|
||||
@ -152,19 +158,19 @@ public class TestArrays {
|
||||
(base) -> initBytes(base, doubles, (addr, pos) -> doubleHandle.set(addr, pos, (double)(long)pos));
|
||||
|
||||
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 =
|
||||
(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 =
|
||||
(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 =
|
||||
(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 =
|
||||
(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 =
|
||||
(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 =
|
||||
(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[][]{
|
||||
{byteInitializer, byteChecker, bytes},
|
||||
@ -180,13 +186,13 @@ public class TestArrays {
|
||||
@DataProvider(name = "elemLayouts")
|
||||
public Object[][] elemLayouts() {
|
||||
return new Object[][] {
|
||||
{ MemoryLayouts.JAVA_BYTE, (Function<MemorySegment, Object>) MemorySegment::toByteArray },
|
||||
{ MemoryLayouts.JAVA_SHORT, (Function<MemorySegment, Object>) MemorySegment::toShortArray },
|
||||
{ MemoryLayouts.JAVA_CHAR, (Function<MemorySegment, Object>) MemorySegment::toCharArray },
|
||||
{ MemoryLayouts.JAVA_INT, (Function<MemorySegment, Object>) MemorySegment::toIntArray },
|
||||
{ MemoryLayouts.JAVA_FLOAT, (Function<MemorySegment, Object>) MemorySegment::toFloatArray },
|
||||
{ MemoryLayouts.JAVA_LONG, (Function<MemorySegment, Object>) MemorySegment::toLongArray },
|
||||
{ MemoryLayouts.JAVA_DOUBLE, (Function<MemorySegment, Object>) MemorySegment::toDoubleArray }
|
||||
{ JAVA_BYTE, (Function<MemorySegment, Object>)s -> s.toArray(JAVA_BYTE)},
|
||||
{ JAVA_SHORT, (Function<MemorySegment, Object>) s -> s.toArray(JAVA_SHORT)},
|
||||
{ JAVA_CHAR, (Function<MemorySegment, Object>) s -> s.toArray(JAVA_CHAR)},
|
||||
{ JAVA_INT, (Function<MemorySegment, Object>)s -> s.toArray(JAVA_INT)},
|
||||
{ JAVA_FLOAT, (Function<MemorySegment, Object>)s -> s.toArray(JAVA_FLOAT)},
|
||||
{ JAVA_LONG, (Function<MemorySegment, Object>)s -> s.toArray(JAVA_LONG)},
|
||||
{ JAVA_DOUBLE, (Function<MemorySegment, Object>)s -> s.toArray(JAVA_DOUBLE)}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,8 +28,6 @@
|
||||
* @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.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
@ -82,6 +80,13 @@ import org.testng.SkipException;
|
||||
import org.testng.annotations.*;
|
||||
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.*;
|
||||
|
||||
public class TestByteBuffer {
|
||||
@ -102,40 +107,40 @@ public class TestByteBuffer {
|
||||
|
||||
static SequenceLayout tuples = MemoryLayout.sequenceLayout(500,
|
||||
MemoryLayout.structLayout(
|
||||
MemoryLayouts.BITS_32_BE.withName("index"),
|
||||
MemoryLayouts.BITS_32_BE.withName("value")
|
||||
JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN).withName("index"),
|
||||
JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN).withName("value")
|
||||
));
|
||||
|
||||
static SequenceLayout bytes = MemoryLayout.sequenceLayout(100,
|
||||
MemoryLayouts.BITS_8_BE
|
||||
JAVA_BYTE
|
||||
);
|
||||
|
||||
static SequenceLayout chars = MemoryLayout.sequenceLayout(100,
|
||||
MemoryLayouts.BITS_16_BE
|
||||
JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN)
|
||||
);
|
||||
|
||||
static SequenceLayout shorts = MemoryLayout.sequenceLayout(100,
|
||||
MemoryLayouts.BITS_16_BE
|
||||
JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN)
|
||||
);
|
||||
|
||||
static SequenceLayout ints = MemoryLayout.sequenceLayout(100,
|
||||
MemoryLayouts.BITS_32_BE
|
||||
JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN)
|
||||
);
|
||||
|
||||
static SequenceLayout floats = MemoryLayout.sequenceLayout(100,
|
||||
MemoryLayouts.BITS_32_BE
|
||||
JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN)
|
||||
);
|
||||
|
||||
static SequenceLayout longs = MemoryLayout.sequenceLayout(100,
|
||||
MemoryLayouts.BITS_64_BE
|
||||
JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN)
|
||||
);
|
||||
|
||||
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 valueHandle = tuples.varHandle(float.class, PathElement.sequenceElement(), PathElement.groupElement("value"));
|
||||
static VarHandle indexHandle = tuples.varHandle(PathElement.sequenceElement(), PathElement.groupElement("index"));
|
||||
static VarHandle valueHandle = tuples.varHandle(PathElement.sequenceElement(), PathElement.groupElement("value"));
|
||||
|
||||
static void initTuples(MemorySegment base, long count) {
|
||||
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 {
|
||||
File f = new File("test3.out");
|
||||
f.createNewFile();
|
||||
@ -324,6 +329,9 @@ public class TestByteBuffer {
|
||||
segment.isLoaded();
|
||||
segment.unload();
|
||||
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)
|
||||
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();
|
||||
}
|
||||
|
||||
@ -511,7 +519,7 @@ public class TestByteBuffer {
|
||||
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
||||
MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0, SIZE, FileChannel.MapMode.READ_WRITE, scope);
|
||||
for (byte offset = 0; offset < SIZE; offset++) {
|
||||
MemoryAccess.setByteAtOffset(segment, offset, offset);
|
||||
segment.set(JAVA_BYTE, offset, offset);
|
||||
}
|
||||
segment.force();
|
||||
}
|
||||
@ -519,7 +527,7 @@ public class TestByteBuffer {
|
||||
for (int offset = 0 ; offset < SIZE ; offset++) {
|
||||
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
||||
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)
|
||||
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());
|
||||
|
||||
// memory freed
|
||||
s1.scope().close();
|
||||
|
||||
MemoryAccess.setInt(s2, 10); // Dead access!
|
||||
s2.set(JAVA_INT, 0, 10); // Dead access!
|
||||
}
|
||||
|
||||
@Test(dataProvider = "allScopes")
|
||||
@ -654,7 +662,7 @@ public class TestByteBuffer {
|
||||
ResourceScope scp = closeableScopeOrNull(scope = scopeSupplier.get())) {
|
||||
MemorySegment segment = MemorySegment.allocateNative(10, 1, scope);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
MemoryAccess.setByteAtOffset(segment, i, (byte) i);
|
||||
segment.set(JAVA_BYTE, i, (byte) i);
|
||||
}
|
||||
ByteBuffer bb = segment.asByteBuffer();
|
||||
assertEquals(channel.write(bb), 10);
|
||||
@ -674,7 +682,7 @@ public class TestByteBuffer {
|
||||
try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
|
||||
MemorySegment segment = MemorySegment.allocateNative(10, scopeSupplier.get());
|
||||
for (int i = 0; i < 10; i++) {
|
||||
MemoryAccess.setByteAtOffset(segment, i, (byte) i);
|
||||
segment.set(JAVA_BYTE, i, (byte) i);
|
||||
}
|
||||
ByteBuffer bb = segment.asByteBuffer();
|
||||
segment.scope().close();
|
||||
@ -694,7 +702,7 @@ public class TestByteBuffer {
|
||||
int newSize = 8;
|
||||
var slice = segment.asSlice(4, newSize);
|
||||
|
||||
var bytes = slice.toByteArray();
|
||||
var bytes = slice.toArray(JAVA_BYTE);
|
||||
assertEquals(newSize, bytes.length);
|
||||
|
||||
var buffer = slice.asByteBuffer();
|
||||
@ -719,6 +727,7 @@ public class TestByteBuffer {
|
||||
public static Object[][] segments() throws Throwable {
|
||||
return new Object[][] {
|
||||
{ (Supplier<MemorySegment>) () -> MemorySegment.allocateNative(16, ResourceScope.newImplicitScope()) },
|
||||
{ (Supplier<MemorySegment>) () -> MemorySegment.allocateNative(16, ResourceScope.newConfinedScope()) },
|
||||
{ (Supplier<MemorySegment>) () -> MemorySegment.ofArray(new byte[16]) }
|
||||
};
|
||||
}
|
||||
@ -729,27 +738,20 @@ public class TestByteBuffer {
|
||||
{ (Supplier<ResourceScope>) () -> ResourceScope.newSharedScope() },
|
||||
{ (Supplier<ResourceScope>) () -> ResourceScope.newConfinedScope() },
|
||||
{ (Supplier<ResourceScope>) () -> ResourceScope.newSharedScope(Cleaner.create()) },
|
||||
{ (Supplier<ResourceScope>) () -> ResourceScope.newConfinedScope(Cleaner.create()) }
|
||||
};
|
||||
}
|
||||
|
||||
@DataProvider(name = "implicitScopes")
|
||||
public static Object[][] implicitScopes() {
|
||||
return new Object[][] {
|
||||
{ (Supplier<ResourceScope>) ResourceScope::newImplicitScope },
|
||||
{ (Supplier<ResourceScope>) ResourceScope::globalScope },
|
||||
{ (Supplier<ResourceScope>) () -> ResourceScope.newConfinedScope(Cleaner.create()) },
|
||||
{ (Supplier<ResourceScope>) () -> ResourceScope.newImplicitScope() }
|
||||
};
|
||||
}
|
||||
|
||||
@DataProvider(name = "allScopes")
|
||||
public static Object[][] allScopes() {
|
||||
return Stream.of(implicitScopes(), closeableScopes())
|
||||
return Stream.of(new Object[][] { { (Supplier<ResourceScope>)ResourceScope::globalScope } }, closeableScopes())
|
||||
.flatMap(Arrays::stream)
|
||||
.toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
static ResourceScope closeableScopeOrNull(ResourceScope scope) {
|
||||
if (scope.isImplicit())
|
||||
if (scope == ResourceScope.globalScope())
|
||||
return null;
|
||||
return scope;
|
||||
}
|
||||
@ -812,34 +814,34 @@ public class TestByteBuffer {
|
||||
@DataProvider(name = "resizeOps")
|
||||
public Object[][] resizeOps() {
|
||||
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 =
|
||||
(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 =
|
||||
(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 =
|
||||
(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 =
|
||||
(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 =
|
||||
(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 =
|
||||
(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 =
|
||||
(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 =
|
||||
(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 =
|
||||
(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 =
|
||||
(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 =
|
||||
(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 =
|
||||
(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 =
|
||||
(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[][]{
|
||||
{byteChecker, byteInitializer, bytes},
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -39,7 +39,15 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
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;
|
||||
|
||||
public class TestCondy {
|
||||
@ -53,14 +61,15 @@ public class TestCondy {
|
||||
|
||||
|
||||
private static final MemoryLayout[] constants = {
|
||||
C_CHAR,
|
||||
C_SHORT,
|
||||
C_INT,
|
||||
C_LONG,
|
||||
C_LONG_LONG,
|
||||
C_FLOAT,
|
||||
C_DOUBLE,
|
||||
C_POINTER
|
||||
JAVA_BOOLEAN,
|
||||
JAVA_CHAR,
|
||||
JAVA_BYTE,
|
||||
JAVA_SHORT,
|
||||
JAVA_INT,
|
||||
JAVA_FLOAT,
|
||||
JAVA_LONG,
|
||||
JAVA_DOUBLE,
|
||||
ADDRESS
|
||||
};
|
||||
|
||||
@DataProvider
|
||||
@ -78,7 +87,7 @@ public class TestCondy {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -33,8 +33,11 @@
|
||||
* TestDowncall
|
||||
*/
|
||||
|
||||
import jdk.incubator.foreign.Addressable;
|
||||
import jdk.incubator.foreign.CLinker;
|
||||
import jdk.incubator.foreign.FunctionDescriptor;
|
||||
import jdk.incubator.foreign.NativeSymbol;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import jdk.incubator.foreign.SymbolLookup;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
@ -52,7 +55,7 @@ import static org.testng.Assert.*;
|
||||
|
||||
public class TestDowncall extends CallGeneratorHelper {
|
||||
|
||||
static CLinker abi = CLinker.getInstance();
|
||||
static CLinker abi = CLinker.systemCLinker();
|
||||
static {
|
||||
System.loadLibrary("TestDowncall");
|
||||
}
|
||||
@ -62,64 +65,37 @@ public class TestDowncall extends CallGeneratorHelper {
|
||||
@Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class)
|
||||
public void testDowncall(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();
|
||||
NativeSymbol addr = LOOKUP.lookup(fName).get();
|
||||
MethodType mt = methodType(ret, paramTypes, fields);
|
||||
FunctionDescriptor descriptor = function(ret, paramTypes, fields);
|
||||
Object[] args = makeArgs(paramTypes, fields, checks);
|
||||
try (NativeScope scope = new NativeScope()) {
|
||||
try (ResourceScope scope = ResourceScope.newSharedScope()) {
|
||||
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) {
|
||||
checks.forEach(c -> c.accept(res));
|
||||
if (needsScope) {
|
||||
// check that return struct has indeed been allocated in the native scope
|
||||
assertEquals(((MemorySegment) res).scope(), 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
|
||||
assertEquals(((MemorySegment) res).scope(), scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object doCall(MemoryAddress addr, SegmentAllocator allocator, MethodType type, FunctionDescriptor descriptor, Object[] args) throws Throwable {
|
||||
MethodHandle mh = abi.downcallHandle(addr, allocator, type, descriptor);
|
||||
Object doCall(NativeSymbol symbol, SegmentAllocator allocator, FunctionDescriptor descriptor, Object[] args) throws Throwable {
|
||||
MethodHandle mh = downcallHandle(abi, symbol, allocator, descriptor);
|
||||
Object res = mh.invokeWithArguments(args);
|
||||
return res;
|
||||
}
|
||||
|
||||
static MethodType methodType(Ret ret, List<ParamType> params, List<StructFieldType> fields) {
|
||||
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) {
|
||||
mt = mt.appendParameterTypes(paramCarrier(p.layout(fields)));
|
||||
mt = mt.appendParameterTypes(carrier(p.layout(fields), true));
|
||||
}
|
||||
return mt;
|
||||
}
|
||||
|
||||
@ -29,18 +29,16 @@
|
||||
* @run testng/othervm --enable-native-access=ALL-UNNAMED TestFree
|
||||
*/
|
||||
|
||||
import jdk.incubator.foreign.MemoryAccess;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
|
||||
import static jdk.incubator.foreign.CLinker.*;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
public class TestFree {
|
||||
public class TestFree extends NativeTestHelper {
|
||||
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 {
|
||||
@ -48,8 +46,8 @@ public class TestFree {
|
||||
MemoryAddress addr = allocateMemory(str.length() + 1);
|
||||
MemorySegment seg = asArray(addr, C_CHAR, str.length() + 1);
|
||||
seg.copyFrom(MemorySegment.ofArray(str.getBytes()));
|
||||
MemoryAccess.setByteAtOffset(seg, str.length(), (byte)0);
|
||||
assertEquals(str, toJavaString(seg));
|
||||
seg.set(C_CHAR, str.length(), (byte)0);
|
||||
assertEquals(str, seg.getUtf8String(0));
|
||||
freeMemory(addr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
/*
|
||||
* @test
|
||||
* @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;
|
||||
@ -37,15 +37,11 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
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.assertFalse;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
public class TestFunctionDescriptor {
|
||||
public class TestFunctionDescriptor extends NativeTestHelper {
|
||||
|
||||
static final String DUMMY_ATTR = "dummy";
|
||||
|
||||
@ -68,56 +64,35 @@ public class TestFunctionDescriptor {
|
||||
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
|
||||
public void testAppendArgumentLayouts() {
|
||||
FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG)
|
||||
.withAttribute(DUMMY_ATTR, true);
|
||||
FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG);
|
||||
fd = fd.withAppendedArgumentLayouts(C_POINTER);
|
||||
|
||||
assertEquals(fd.argumentLayouts(), List.of(C_DOUBLE, C_LONG_LONG, C_POINTER));
|
||||
Optional<MemoryLayout> returnLayoutOp = fd.returnLayout();
|
||||
assertTrue(returnLayoutOp.isPresent());
|
||||
assertEquals(returnLayoutOp.get(), C_INT);
|
||||
assertEquals(fd.attributes().collect(Collectors.toList()), List.of(DUMMY_ATTR));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangeReturnLayout() {
|
||||
FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG)
|
||||
.withAttribute(DUMMY_ATTR, true);
|
||||
FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG);
|
||||
fd = fd.withReturnLayout(C_INT);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDropReturnLayout() {
|
||||
FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG)
|
||||
.withAttribute(DUMMY_ATTR, true);
|
||||
FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_DOUBLE, C_LONG_LONG);
|
||||
fd = fd.withVoidReturnLayout();
|
||||
|
||||
assertEquals(fd.argumentLayouts(), List.of(C_DOUBLE, C_LONG_LONG));
|
||||
Optional<MemoryLayout> returnLayoutOp = fd.returnLayout();
|
||||
assertFalse(returnLayoutOp.isPresent());
|
||||
assertEquals(fd.attributes().collect(Collectors.toList()), List.of(DUMMY_ATTR));
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,7 +31,6 @@
|
||||
* @run testng/othervm -XX:-TieredCompilation TestHandshake
|
||||
*/
|
||||
|
||||
import jdk.incubator.foreign.MemoryAccess;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
@ -48,6 +47,8 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class TestHandshake {
|
||||
@ -150,7 +151,7 @@ public class TestHandshake {
|
||||
void doAccess() {
|
||||
int sum = 0;
|
||||
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);
|
||||
this.copy = MemorySegment.allocateNative(SEGMENT_SIZE, 1, segment.scope());
|
||||
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
|
||||
|
||||
@ -32,26 +32,23 @@ import jdk.incubator.foreign.CLinker;
|
||||
import jdk.incubator.foreign.FunctionDescriptor;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.NativeSymbol;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import org.testng.annotations.DataProvider;
|
||||
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.fail;
|
||||
|
||||
public class TestIllegalLink {
|
||||
public class TestIllegalLink extends NativeTestHelper {
|
||||
|
||||
private static final MemoryAddress DUMMY_TARGET = MemoryAddress.ofLong(1);
|
||||
private static final CLinker ABI = CLinker.getInstance();
|
||||
private static final NativeSymbol DUMMY_TARGET = NativeSymbol.ofAddress("dummy", MemoryAddress.ofLong(1), ResourceScope.globalScope());
|
||||
private static final CLinker ABI = CLinker.systemCLinker();
|
||||
|
||||
@Test(dataProvider = "types")
|
||||
public void testTypeMismatch(MethodType mt, FunctionDescriptor desc, String expectedExceptionMessage) {
|
||||
public void testTypeMismatch(FunctionDescriptor desc, String expectedExceptionMessage) {
|
||||
try {
|
||||
ABI.downcallHandle(DUMMY_TARGET, mt, desc);
|
||||
ABI.downcallHandle(DUMMY_TARGET, desc);
|
||||
fail("Expected IllegalArgumentException was not thrown");
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertTrue(e.getMessage().contains(expectedExceptionMessage));
|
||||
@ -62,49 +59,20 @@ public class TestIllegalLink {
|
||||
public static Object[][] types() {
|
||||
return new Object[][]{
|
||||
{
|
||||
MethodType.methodType(void.class),
|
||||
FunctionDescriptor.of(C_INT),
|
||||
"Return type mismatch"
|
||||
FunctionDescriptor.of(MemoryLayout.paddingLayout(64)),
|
||||
"Unsupported layout: x64"
|
||||
},
|
||||
{
|
||||
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)),
|
||||
"Expected a ValueLayout"
|
||||
"Unsupported layout: x64"
|
||||
},
|
||||
{
|
||||
MethodType.methodType(void.class, MemoryAddress.class),
|
||||
FunctionDescriptor.ofVoid(MemoryLayouts.BITS_16_LE),
|
||||
"Address size mismatch"
|
||||
FunctionDescriptor.of(MemoryLayout.sequenceLayout(C_INT)),
|
||||
"Unsupported layout: [:b32]"
|
||||
},
|
||||
{
|
||||
MethodType.methodType(void.class, MemorySegment.class),
|
||||
FunctionDescriptor.ofVoid(MemoryLayouts.BITS_64_LE),
|
||||
"Expected a GroupLayout"
|
||||
},
|
||||
{
|
||||
MethodType.methodType(void.class, String.class),
|
||||
FunctionDescriptor.ofVoid(MemoryLayouts.BITS_64_LE),
|
||||
"Unsupported carrier"
|
||||
FunctionDescriptor.ofVoid(MemoryLayout.sequenceLayout(C_INT)),
|
||||
"Unsupported layout: [:b32]"
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -42,17 +42,17 @@ import java.util.List;
|
||||
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.NativeSymbol;
|
||||
import jdk.incubator.foreign.SymbolLookup;
|
||||
import org.testng.annotations.*;
|
||||
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
import static jdk.incubator.foreign.CLinker.*;
|
||||
import static jdk.incubator.foreign.FunctionDescriptor.TRIVIAL_ATTRIBUTE_NAME;
|
||||
import static jdk.incubator.foreign.ValueLayout.JAVA_CHAR;
|
||||
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 {
|
||||
System.loadLibrary("Intrinsics");
|
||||
}
|
||||
@ -88,52 +88,48 @@ public class TestIntrinsics {
|
||||
}
|
||||
|
||||
AddIdentity addIdentity = (name, carrier, layout, arg) -> {
|
||||
MemoryAddress ma = LOOKUP.lookup(name).get();
|
||||
NativeSymbol ma = LOOKUP.lookup(name).get();
|
||||
MethodType mt = methodType(carrier, carrier);
|
||||
FunctionDescriptor fd = FunctionDescriptor.of(layout, layout);
|
||||
|
||||
tests.add(abi.downcallHandle(ma, mt, fd), arg, arg);
|
||||
tests.add(abi.downcallHandle(ma, mt, fd.withAttribute(TRIVIAL_ATTRIBUTE_NAME, true)), arg, arg);
|
||||
tests.add(abi.downcallHandle(mt, fd), arg, ma, arg);
|
||||
tests.add(abi.downcallHandle(ma, fd), arg, arg);
|
||||
tests.add(abi.downcallHandle(fd), arg, ma, arg);
|
||||
};
|
||||
|
||||
{ // empty
|
||||
MemoryAddress ma = LOOKUP.lookup("empty").get();
|
||||
NativeSymbol ma = LOOKUP.lookup("empty").get();
|
||||
MethodType mt = methodType(void.class);
|
||||
FunctionDescriptor fd = FunctionDescriptor.ofVoid();
|
||||
tests.add(abi.downcallHandle(ma, mt, fd), null);
|
||||
tests.add(abi.downcallHandle(ma, mt, fd.withAttribute(TRIVIAL_ATTRIBUTE_NAME, true)), null);
|
||||
tests.add(abi.downcallHandle(ma, fd), null);
|
||||
}
|
||||
|
||||
addIdentity.add("identity_char", byte.class, C_CHAR, (byte) 10);
|
||||
addIdentity.add("identity_short", short.class, C_SHORT, (short) 10);
|
||||
addIdentity.add("identity_int", int.class, C_INT, 10);
|
||||
addIdentity.add("identity_long", long.class, C_LONG_LONG, 10L);
|
||||
addIdentity.add("identity_float", float.class, C_FLOAT, 10F);
|
||||
addIdentity.add("identity_double", double.class, C_DOUBLE, 10D);
|
||||
addIdentity.add("identity_bool", boolean.class, C_BOOL, true);
|
||||
addIdentity.add("identity_char", byte.class, C_CHAR, (byte) 10);
|
||||
addIdentity.add("identity_short", short.class, C_SHORT, (short) 10);
|
||||
addIdentity.add("identity_int", int.class, C_INT, 10);
|
||||
addIdentity.add("identity_long", long.class, C_LONG_LONG, 10L);
|
||||
addIdentity.add("identity_float", float.class, C_FLOAT, 10F);
|
||||
addIdentity.add("identity_double", double.class, C_DOUBLE, 10D);
|
||||
|
||||
{ // 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);
|
||||
FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_INT, asVarArg(C_DOUBLE),
|
||||
asVarArg(C_INT), asVarArg(C_FLOAT), asVarArg(C_LONG_LONG));
|
||||
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);
|
||||
FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_INT).asVariadic(C_DOUBLE, C_INT, C_FLOAT, C_LONG_LONG);
|
||||
tests.add(abi.downcallHandle(ma, fd), 1, 1, 10D, 2, 3F, 4L);
|
||||
}
|
||||
|
||||
{ // high_arity
|
||||
MethodType baseMT = methodType(void.class, int.class, double.class, long.class, float.class, byte.class,
|
||||
short.class, char.class);
|
||||
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'};
|
||||
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));
|
||||
FunctionDescriptor fd = baseFD.withReturnLayout(baseFD.argumentLayouts().get(i));
|
||||
Object expected = args[i];
|
||||
tests.add(abi.downcallHandle(ma, mt, fd), expected, args);
|
||||
tests.add(abi.downcallHandle(ma, mt, fd.withAttribute(TRIVIAL_ATTRIBUTE_NAME, true)), expected, args);
|
||||
tests.add(abi.downcallHandle(ma, fd), expected, args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
@ -27,11 +27,12 @@
|
||||
*/
|
||||
|
||||
import jdk.incubator.foreign.FunctionDescriptor;
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import jdk.incubator.foreign.ValueLayout;
|
||||
import org.testng.annotations.*;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
@ -66,49 +67,48 @@ public class TestLayoutConstants {
|
||||
public Object[][] createLayouts() {
|
||||
return new Object[][] {
|
||||
//padding
|
||||
{ MemoryLayouts.PAD_32 },
|
||||
{ MemoryLayout.sequenceLayout(MemoryLayouts.PAD_32) },
|
||||
{ MemoryLayout.sequenceLayout(5, MemoryLayouts.PAD_32) },
|
||||
{ MemoryLayout.structLayout(MemoryLayouts.PAD_32, MemoryLayouts.PAD_32) },
|
||||
{ MemoryLayout.unionLayout(MemoryLayouts.PAD_32, MemoryLayouts.PAD_32) },
|
||||
{MemoryLayout.paddingLayout(32)},
|
||||
{ MemoryLayout.sequenceLayout(MemoryLayout.paddingLayout(32)) },
|
||||
{ MemoryLayout.sequenceLayout(5, MemoryLayout.paddingLayout(32)) },
|
||||
{ MemoryLayout.structLayout(MemoryLayout.paddingLayout(32), MemoryLayout.paddingLayout(32)) },
|
||||
{ MemoryLayout.unionLayout(MemoryLayout.paddingLayout(32), MemoryLayout.paddingLayout(32)) },
|
||||
//values, big endian
|
||||
{ MemoryLayouts.BITS_32_BE },
|
||||
{ ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN) },
|
||||
{ MemoryLayout.structLayout(
|
||||
MemoryLayouts.BITS_32_BE,
|
||||
MemoryLayouts.BITS_32_BE) },
|
||||
ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN),
|
||||
ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN)) },
|
||||
{ MemoryLayout.unionLayout(
|
||||
MemoryLayouts.BITS_32_BE,
|
||||
MemoryLayouts.BITS_32_BE) },
|
||||
ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN),
|
||||
ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN)) },
|
||||
//values, little endian
|
||||
{ MemoryLayouts.BITS_32_LE },
|
||||
{ ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN) },
|
||||
{ MemoryLayout.structLayout(
|
||||
MemoryLayouts.BITS_32_LE,
|
||||
MemoryLayouts.BITS_32_LE) },
|
||||
ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN),
|
||||
ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN)) },
|
||||
{ MemoryLayout.unionLayout(
|
||||
MemoryLayouts.BITS_32_LE,
|
||||
MemoryLayouts.BITS_32_LE) },
|
||||
ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN),
|
||||
ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN)) },
|
||||
//deeply nested
|
||||
{ MemoryLayout.structLayout(
|
||||
MemoryLayouts.PAD_16,
|
||||
MemoryLayout.paddingLayout(16),
|
||||
MemoryLayout.structLayout(
|
||||
MemoryLayouts.PAD_8,
|
||||
MemoryLayouts.BITS_32_BE)) },
|
||||
MemoryLayout.paddingLayout(8),
|
||||
ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN))) },
|
||||
{ MemoryLayout.unionLayout(
|
||||
MemoryLayouts.PAD_16,
|
||||
MemoryLayout.paddingLayout(16),
|
||||
MemoryLayout.structLayout(
|
||||
MemoryLayouts.PAD_8,
|
||||
MemoryLayouts.BITS_32_BE)) },
|
||||
MemoryLayout.paddingLayout(8),
|
||||
ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN))) },
|
||||
{ MemoryLayout.sequenceLayout(
|
||||
MemoryLayout.structLayout(
|
||||
MemoryLayouts.PAD_8,
|
||||
MemoryLayouts.BITS_32_BE)) },
|
||||
MemoryLayout.paddingLayout(8),
|
||||
ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN))) },
|
||||
{ MemoryLayout.sequenceLayout(5,
|
||||
MemoryLayout.structLayout(
|
||||
MemoryLayouts.PAD_8,
|
||||
MemoryLayouts.BITS_32_BE)) },
|
||||
{ MemoryLayouts.BITS_32_LE.withName("myInt") },
|
||||
{ MemoryLayouts.BITS_32_LE.withBitAlignment(8) },
|
||||
{ MemoryLayouts.BITS_32_LE.withAttribute("xyz", "abc") },
|
||||
MemoryLayout.paddingLayout(8),
|
||||
ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN))) },
|
||||
{ ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN).withName("myInt") },
|
||||
{ ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN).withBitAlignment(8) },
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
* @run testng TestLayoutEquality
|
||||
*/
|
||||
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.ValueLayout;
|
||||
import jdk.internal.foreign.PlatformLayouts;
|
||||
import org.testng.annotations.DataProvider;
|
||||
@ -39,23 +39,32 @@ import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
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.*;
|
||||
|
||||
public class TestLayoutEquality {
|
||||
|
||||
@Test(dataProvider = "layoutConstants")
|
||||
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
|
||||
assertEquals(newLayout.bitSize(), layout.bitSize());
|
||||
assertEquals(newLayout.bitAlignment(), layout.bitAlignment());
|
||||
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
|
||||
assertNotEquals(newLayout, layout);
|
||||
// layouts should be equals
|
||||
assertEquals(newLayout, layout);
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,24 +28,23 @@
|
||||
*/
|
||||
|
||||
import jdk.incubator.foreign.GroupLayout;
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemoryLayout.PathElement;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import jdk.incubator.foreign.SequenceLayout;
|
||||
|
||||
import jdk.incubator.foreign.ValueLayout;
|
||||
import org.testng.SkipException;
|
||||
import org.testng.annotations.*;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static jdk.incubator.foreign.MemoryLayout.PathElement.groupElement;
|
||||
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.*;
|
||||
|
||||
public class TestLayoutPaths {
|
||||
@ -153,7 +152,7 @@ public class TestLayoutPaths {
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testIncompleteAccess() {
|
||||
SequenceLayout seq = MemoryLayout.sequenceLayout(5, MemoryLayout.structLayout(JAVA_INT));
|
||||
seq.varHandle(int.class, sequenceElement());
|
||||
seq.varHandle(sequenceElement());
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
@ -221,7 +220,7 @@ public class TestLayoutPaths {
|
||||
throw new AssertionError(ex); // should be ok!
|
||||
}
|
||||
try {
|
||||
g.varHandle(int.class, groupElement("foo")); //ok
|
||||
g.varHandle(groupElement("foo")); //ok
|
||||
assertTrue(false); //should fail!
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
//ok
|
||||
@ -232,7 +231,7 @@ public class TestLayoutPaths {
|
||||
|
||||
@Test
|
||||
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 {
|
||||
g.bitOffset(groupElement("foo"));
|
||||
g.byteOffset(groupElement("foo"));
|
||||
@ -240,7 +239,7 @@ public class TestLayoutPaths {
|
||||
throw new AssertionError(ex); // should be ok!
|
||||
}
|
||||
try {
|
||||
g.varHandle(int.class, groupElement("foo")); //ok
|
||||
g.varHandle(groupElement("foo")); //ok
|
||||
assertTrue(false); //should fail!
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
//ok
|
||||
@ -299,10 +298,10 @@ public class TestLayoutPaths {
|
||||
public void testStructPaths() {
|
||||
long[] offsets = { 0, 8, 24, 56 };
|
||||
GroupLayout g = MemoryLayout.structLayout(
|
||||
MemoryLayouts.JAVA_BYTE.withName("1"),
|
||||
MemoryLayouts.JAVA_CHAR.withName("2"),
|
||||
MemoryLayouts.JAVA_FLOAT.withName("3"),
|
||||
MemoryLayouts.JAVA_LONG.withName("4")
|
||||
ValueLayout.JAVA_BYTE.withName("1"),
|
||||
ValueLayout.JAVA_CHAR.withName("2"),
|
||||
ValueLayout.JAVA_FLOAT.withName("3"),
|
||||
ValueLayout.JAVA_LONG.withName("4")
|
||||
);
|
||||
|
||||
// test select
|
||||
@ -324,11 +323,11 @@ public class TestLayoutPaths {
|
||||
// test map
|
||||
|
||||
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());
|
||||
for (int j = 0 ; j < 4 ; j++) {
|
||||
if (j == i - 1) {
|
||||
assertEquals(g2.memberLayouts().get(j), MemoryLayouts.JAVA_DOUBLE);
|
||||
assertEquals(g2.memberLayouts().get(j), ValueLayout.JAVA_DOUBLE);
|
||||
} else {
|
||||
assertEquals(g2.memberLayouts().get(j), g.memberLayouts().get(j));
|
||||
}
|
||||
@ -340,10 +339,10 @@ public class TestLayoutPaths {
|
||||
public void testUnionPaths() {
|
||||
long[] offsets = { 0, 0, 0, 0 };
|
||||
GroupLayout g = MemoryLayout.unionLayout(
|
||||
MemoryLayouts.JAVA_BYTE.withName("1"),
|
||||
MemoryLayouts.JAVA_CHAR.withName("2"),
|
||||
MemoryLayouts.JAVA_FLOAT.withName("3"),
|
||||
MemoryLayouts.JAVA_LONG.withName("4")
|
||||
ValueLayout.JAVA_BYTE.withName("1"),
|
||||
ValueLayout.JAVA_CHAR.withName("2"),
|
||||
ValueLayout.JAVA_FLOAT.withName("3"),
|
||||
ValueLayout.JAVA_LONG.withName("4")
|
||||
);
|
||||
|
||||
// test select
|
||||
@ -365,11 +364,11 @@ public class TestLayoutPaths {
|
||||
// test map
|
||||
|
||||
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());
|
||||
for (int j = 0 ; j < 4 ; j++) {
|
||||
if (j == i - 1) {
|
||||
assertEquals(g2.memberLayouts().get(j), MemoryLayouts.JAVA_DOUBLE);
|
||||
assertEquals(g2.memberLayouts().get(j), ValueLayout.JAVA_DOUBLE);
|
||||
} else {
|
||||
assertEquals(g2.memberLayouts().get(j), g.memberLayouts().get(j));
|
||||
}
|
||||
@ -380,12 +379,12 @@ public class TestLayoutPaths {
|
||||
@Test
|
||||
public void testSequencePaths() {
|
||||
long[] offsets = { 0, 8, 16, 24 };
|
||||
SequenceLayout g = MemoryLayout.sequenceLayout(4, MemoryLayouts.JAVA_BYTE);
|
||||
SequenceLayout g = MemoryLayout.sequenceLayout(4, ValueLayout.JAVA_BYTE);
|
||||
|
||||
// test select
|
||||
|
||||
MemoryLayout selected = g.select(sequenceElement());
|
||||
assertTrue(selected == MemoryLayouts.JAVA_BYTE);
|
||||
assertTrue(selected == ValueLayout.JAVA_BYTE);
|
||||
|
||||
// test offset
|
||||
|
||||
@ -398,8 +397,8 @@ public class TestLayoutPaths {
|
||||
|
||||
// test map
|
||||
|
||||
SequenceLayout seq2 = (SequenceLayout)g.map(l -> MemoryLayouts.JAVA_DOUBLE, sequenceElement());
|
||||
assertTrue(seq2.elementLayout() == MemoryLayouts.JAVA_DOUBLE);
|
||||
SequenceLayout seq2 = (SequenceLayout)g.map(l -> ValueLayout.JAVA_DOUBLE, sequenceElement());
|
||||
assertTrue(seq2.elementLayout() == ValueLayout.JAVA_DOUBLE);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "testLayouts")
|
||||
@ -506,26 +505,16 @@ public class TestLayoutPaths {
|
||||
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
||||
MemorySegment segment = MemorySegment.allocateNative(layout, scope);
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@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)
|
||||
public void testSliceHandleUOEInvalidOffsetEager() throws Throwable {
|
||||
MemoryLayout layout = MemoryLayout.structLayout(
|
||||
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
|
||||
@ -536,7 +525,7 @@ public class TestLayoutPaths {
|
||||
MemoryLayout layout = MemoryLayout.sequenceLayout(3,
|
||||
MemoryLayout.structLayout(
|
||||
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
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@ -34,15 +34,15 @@ import java.util.function.LongFunction;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
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.*;
|
||||
|
||||
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)
|
||||
public void testBadLayoutAlignment(MemoryLayout layout, long alignment) {
|
||||
layout.withBitAlignment(alignment);
|
||||
@ -51,12 +51,12 @@ public class TestLayouts {
|
||||
@Test
|
||||
public void testVLAInStruct() {
|
||||
MemoryLayout layout = MemoryLayout.structLayout(
|
||||
MemoryLayouts.JAVA_INT.withName("size"),
|
||||
ValueLayout.JAVA_INT.withName("size"),
|
||||
MemoryLayout.paddingLayout(32),
|
||||
MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_DOUBLE).withName("arr"));
|
||||
MemoryLayout.sequenceLayout(ValueLayout.JAVA_DOUBLE).withName("arr"));
|
||||
assertFalse(layout.hasSize());
|
||||
VarHandle size_handle = layout.varHandle(int.class, MemoryLayout.PathElement.groupElement("size"));
|
||||
VarHandle array_elem_handle = layout.varHandle(double.class,
|
||||
VarHandle size_handle = layout.varHandle(MemoryLayout.PathElement.groupElement("size"));
|
||||
VarHandle array_elem_handle = layout.varHandle(
|
||||
MemoryLayout.PathElement.groupElement("arr"),
|
||||
MemoryLayout.PathElement.sequenceElement());
|
||||
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
||||
@ -77,12 +77,12 @@ public class TestLayouts {
|
||||
@Test
|
||||
public void testVLAInSequence() {
|
||||
MemoryLayout layout = MemoryLayout.structLayout(
|
||||
MemoryLayouts.JAVA_INT.withName("size"),
|
||||
ValueLayout.JAVA_INT.withName("size"),
|
||||
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());
|
||||
VarHandle size_handle = layout.varHandle(int.class, MemoryLayout.PathElement.groupElement("size"));
|
||||
VarHandle array_elem_handle = layout.varHandle(double.class,
|
||||
VarHandle size_handle = layout.varHandle(MemoryLayout.PathElement.groupElement("size"));
|
||||
VarHandle array_elem_handle = layout.varHandle(
|
||||
MemoryLayout.PathElement.groupElement("arr"),
|
||||
MemoryLayout.PathElement.sequenceElement(0),
|
||||
MemoryLayout.PathElement.sequenceElement());
|
||||
@ -103,17 +103,17 @@ public class TestLayouts {
|
||||
|
||||
@Test
|
||||
public void testIndexedSequencePath() {
|
||||
MemoryLayout seq = MemoryLayout.sequenceLayout(10, MemoryLayouts.JAVA_INT);
|
||||
MemoryLayout seq = MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_INT);
|
||||
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
||||
MemorySegment segment = MemorySegment.allocateNative(seq, scope);
|
||||
VarHandle indexHandle = seq.varHandle(int.class, MemoryLayout.PathElement.sequenceElement());
|
||||
VarHandle indexHandle = seq.varHandle(MemoryLayout.PathElement.sequenceElement());
|
||||
// init segment
|
||||
for (int i = 0 ; i < 10 ; i++) {
|
||||
indexHandle.set(segment, (long)i, i);
|
||||
}
|
||||
//check statically indexed handles
|
||||
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 found = (int)preindexHandle.get(segment);
|
||||
assertEquals(expected, found);
|
||||
@ -143,13 +143,13 @@ public class TestLayouts {
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadUnboundSequenceLayoutResize() {
|
||||
SequenceLayout seq = MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT);
|
||||
SequenceLayout seq = MemoryLayout.sequenceLayout(ValueLayout.JAVA_INT);
|
||||
seq.withElementCount(-1);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadBoundSequenceLayoutResize() {
|
||||
SequenceLayout seq = MemoryLayout.sequenceLayout(10, MemoryLayouts.JAVA_INT);
|
||||
SequenceLayout seq = MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_INT);
|
||||
seq.withElementCount(-1);
|
||||
}
|
||||
|
||||
@ -168,13 +168,13 @@ public class TestLayouts {
|
||||
public void testStructSizeAndAlign() {
|
||||
MemoryLayout struct = MemoryLayout.structLayout(
|
||||
MemoryLayout.paddingLayout(8),
|
||||
MemoryLayouts.JAVA_BYTE,
|
||||
MemoryLayouts.JAVA_CHAR,
|
||||
MemoryLayouts.JAVA_INT,
|
||||
MemoryLayouts.JAVA_LONG
|
||||
ValueLayout.JAVA_BYTE,
|
||||
ValueLayout.JAVA_CHAR,
|
||||
ValueLayout.JAVA_INT,
|
||||
ValueLayout.JAVA_LONG
|
||||
);
|
||||
assertEquals(struct.byteSize(), 1 + 1 + 2 + 4 + 8);
|
||||
assertEquals(struct.byteAlignment(), MemoryLayouts.ADDRESS.byteAlignment());
|
||||
assertEquals(struct.byteAlignment(), ValueLayout.ADDRESS.byteAlignment());
|
||||
}
|
||||
|
||||
@Test(dataProvider="basicLayouts")
|
||||
@ -199,13 +199,13 @@ public class TestLayouts {
|
||||
@Test
|
||||
public void testUnionSizeAndAlign() {
|
||||
MemoryLayout struct = MemoryLayout.unionLayout(
|
||||
MemoryLayouts.JAVA_BYTE,
|
||||
MemoryLayouts.JAVA_CHAR,
|
||||
MemoryLayouts.JAVA_INT,
|
||||
MemoryLayouts.JAVA_LONG
|
||||
ValueLayout.JAVA_BYTE,
|
||||
ValueLayout.JAVA_CHAR,
|
||||
ValueLayout.JAVA_INT,
|
||||
ValueLayout.JAVA_LONG
|
||||
);
|
||||
assertEquals(struct.byteSize(), 8);
|
||||
assertEquals(struct.byteAlignment(), MemoryLayouts.ADDRESS.byteAlignment());
|
||||
assertEquals(struct.byteAlignment(), ValueLayout.ADDRESS.byteAlignment());
|
||||
}
|
||||
|
||||
@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")
|
||||
public Object[][] unboundLayouts() {
|
||||
ValueLayout alignedInt = JAVA_INT.withBitAlignment(32);
|
||||
return new Object[][] {
|
||||
{ MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT), 32 },
|
||||
{ MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT)), 32 },
|
||||
{ MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT)), 32 },
|
||||
{ MemoryLayout.structLayout(MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT)), 32 },
|
||||
{ MemoryLayout.structLayout(MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT))), 32 },
|
||||
{ MemoryLayout.structLayout(MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT))), 32 },
|
||||
{ MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT)), 32 },
|
||||
{ MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT))), 32 },
|
||||
{ MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT))), 32 },
|
||||
{ MemoryLayout.sequenceLayout(alignedInt), 32 },
|
||||
{ MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(alignedInt)), 32 },
|
||||
{ MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(alignedInt)), 32 },
|
||||
{ MemoryLayout.structLayout(MemoryLayout.sequenceLayout(alignedInt)), 32 },
|
||||
{ MemoryLayout.structLayout(MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(alignedInt))), 32 },
|
||||
{ MemoryLayout.structLayout(MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(alignedInt))), 32 },
|
||||
{ MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(alignedInt)), 32 },
|
||||
{ MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(alignedInt))), 32 },
|
||||
{ MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(alignedInt))), 32 },
|
||||
};
|
||||
}
|
||||
|
||||
@ -271,10 +259,10 @@ public class TestLayouts {
|
||||
}
|
||||
|
||||
enum SizedLayoutFactory {
|
||||
VALUE_LE(size -> MemoryLayout.valueLayout(size, ByteOrder.LITTLE_ENDIAN)),
|
||||
VALUE_BE(size -> MemoryLayout.valueLayout(size, ByteOrder.BIG_ENDIAN)),
|
||||
VALUE_LE(size -> valueLayoutForSize((int)size).withOrder(ByteOrder.LITTLE_ENDIAN)),
|
||||
VALUE_BE(size -> valueLayoutForSize((int)size).withOrder(ByteOrder.BIG_ENDIAN)),
|
||||
PADDING(MemoryLayout::paddingLayout),
|
||||
SEQUENCE(size -> MemoryLayout.sequenceLayout(size, MemoryLayouts.PAD_8));
|
||||
SEQUENCE(size -> MemoryLayout.sequenceLayout(size, MemoryLayout.paddingLayout(8)));
|
||||
|
||||
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 {
|
||||
VALUE_LE(MemoryLayouts.BITS_8_LE),
|
||||
VALUE_BE(MemoryLayouts.BITS_8_BE),
|
||||
PADDING(MemoryLayouts.PAD_8),
|
||||
SEQUENCE(MemoryLayout.sequenceLayout(1, MemoryLayouts.PAD_8)),
|
||||
STRUCT(MemoryLayout.structLayout(MemoryLayouts.PAD_8, MemoryLayouts.PAD_8)),
|
||||
UNION(MemoryLayout.unionLayout(MemoryLayouts.PAD_8, MemoryLayouts.PAD_8));
|
||||
VALUE(ValueLayout.JAVA_BYTE),
|
||||
PADDING(MemoryLayout.paddingLayout(8)),
|
||||
SEQUENCE(MemoryLayout.sequenceLayout(1, MemoryLayout.paddingLayout(8))),
|
||||
STRUCT(MemoryLayout.structLayout(MemoryLayout.paddingLayout(8), MemoryLayout.paddingLayout(8))),
|
||||
UNION(MemoryLayout.unionLayout(MemoryLayout.paddingLayout(8), MemoryLayout.paddingLayout(8)));
|
||||
|
||||
final MemoryLayout layout;
|
||||
|
||||
@ -333,12 +330,12 @@ public class TestLayouts {
|
||||
}
|
||||
|
||||
static MemoryLayout[] basicLayouts = {
|
||||
MemoryLayouts.JAVA_BYTE,
|
||||
MemoryLayouts.JAVA_CHAR,
|
||||
MemoryLayouts.JAVA_SHORT,
|
||||
MemoryLayouts.JAVA_INT,
|
||||
MemoryLayouts.JAVA_FLOAT,
|
||||
MemoryLayouts.JAVA_LONG,
|
||||
MemoryLayouts.JAVA_DOUBLE,
|
||||
ValueLayout.JAVA_BYTE,
|
||||
ValueLayout.JAVA_CHAR,
|
||||
ValueLayout.JAVA_SHORT,
|
||||
ValueLayout.JAVA_INT,
|
||||
ValueLayout.JAVA_FLOAT,
|
||||
ValueLayout.JAVA_LONG,
|
||||
ValueLayout.JAVA_DOUBLE,
|
||||
};
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
*/
|
||||
|
||||
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.PathElement;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
@ -39,6 +39,7 @@ import jdk.incubator.foreign.SequenceLayout;
|
||||
import jdk.incubator.foreign.ValueLayout;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.testng.annotations.*;
|
||||
@ -47,39 +48,39 @@ import static org.testng.Assert.*;
|
||||
public class TestMemoryAccess {
|
||||
|
||||
@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");
|
||||
testAccessInternal(viewFactory, layout, layout.varHandle(carrier), checker);
|
||||
testAccessInternal(viewFactory, layout, layout.varHandle(), checker);
|
||||
}
|
||||
|
||||
@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"));
|
||||
testAccessInternal(viewFactory, layout, layout.varHandle(carrier, PathElement.groupElement("elem")), checker);
|
||||
testAccessInternal(viewFactory, layout, layout.varHandle(PathElement.groupElement("elem")), checker);
|
||||
}
|
||||
|
||||
@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);
|
||||
testAccessInternal(viewFactory, layout, layout.varHandle(carrier, PathElement.sequenceElement(1)), checker);
|
||||
testAccessInternal(viewFactory, layout, layout.varHandle(PathElement.sequenceElement(1)), checker);
|
||||
}
|
||||
|
||||
@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"));
|
||||
testArrayAccessInternal(viewFactory, seq, seq.varHandle(carrier, PathElement.sequenceElement()), checker);
|
||||
testArrayAccessInternal(viewFactory, seq, seq.varHandle(PathElement.sequenceElement()), checker);
|
||||
}
|
||||
|
||||
@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")));
|
||||
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")
|
||||
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));
|
||||
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) {
|
||||
@ -149,40 +150,33 @@ public class TestMemoryAccess {
|
||||
}
|
||||
|
||||
@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,
|
||||
MemoryLayout.sequenceLayout(10, elemLayout.withName("elem")));
|
||||
testMatrixAccessInternal(viewFactory, seq, seq.varHandle(carrier,
|
||||
testMatrixAccessInternal(viewFactory, seq, seq.varHandle(
|
||||
PathElement.sequenceElement(), PathElement.sequenceElement()), checker);
|
||||
}
|
||||
|
||||
@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,
|
||||
MemoryLayout.sequenceLayout(10, MemoryLayout.structLayout(MemoryLayout.paddingLayout(elemLayout.bitSize()), elemLayout.withName("elem"))));
|
||||
testMatrixAccessInternal(viewFactory, seq,
|
||||
seq.varHandle(carrier,
|
||||
seq.varHandle(
|
||||
PathElement.sequenceElement(), PathElement.sequenceElement(), PathElement.groupElement("elem")),
|
||||
checker);
|
||||
}
|
||||
|
||||
@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,
|
||||
MemoryLayout.sequenceLayout(10, MemoryLayout.sequenceLayout(2, elemLayout)));
|
||||
testMatrixAccessInternal(viewFactory, seq,
|
||||
seq.varHandle(carrier,
|
||||
seq.varHandle(
|
||||
PathElement.sequenceElement(), PathElement.sequenceElement(), PathElement.sequenceElement(1)),
|
||||
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) {
|
||||
MemorySegment outer_segment;
|
||||
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
||||
@ -227,37 +221,37 @@ public class TestMemoryAccess {
|
||||
public Object[][] createData() {
|
||||
return new Object[][] {
|
||||
//BE, RW
|
||||
{ ID, MemoryLayouts.BITS_8_BE, byte.class, Checker.BYTE },
|
||||
{ ID, MemoryLayouts.BITS_16_BE, short.class, Checker.SHORT },
|
||||
{ ID, MemoryLayouts.BITS_16_BE, char.class, Checker.CHAR },
|
||||
{ ID, MemoryLayouts.BITS_32_BE, int.class, Checker.INT },
|
||||
{ ID, MemoryLayouts.BITS_64_BE, long.class, Checker.LONG },
|
||||
{ ID, MemoryLayouts.BITS_32_BE, float.class, Checker.FLOAT },
|
||||
{ ID, MemoryLayouts.BITS_64_BE, double.class, Checker.DOUBLE },
|
||||
{ ID, ValueLayout.JAVA_BYTE, Checker.BYTE },
|
||||
{ ID, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), Checker.SHORT },
|
||||
{ ID, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), Checker.CHAR },
|
||||
{ ID, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), Checker.INT },
|
||||
{ ID, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), Checker.LONG },
|
||||
{ ID, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), Checker.FLOAT },
|
||||
{ ID, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), Checker.DOUBLE },
|
||||
//BE, RO
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_8_BE, byte.class, Checker.BYTE },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_BE, short.class, Checker.SHORT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_BE, char.class, Checker.CHAR },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_BE, int.class, Checker.INT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_BE, long.class, Checker.LONG },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_BE, float.class, Checker.FLOAT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_BE, double.class, Checker.DOUBLE },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_BYTE, Checker.BYTE },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), Checker.SHORT },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), Checker.CHAR },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), Checker.INT },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), Checker.LONG },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), Checker.FLOAT },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), Checker.DOUBLE },
|
||||
//LE, RW
|
||||
{ ID, MemoryLayouts.BITS_8_LE, byte.class, Checker.BYTE },
|
||||
{ ID, MemoryLayouts.BITS_16_LE, short.class, Checker.SHORT },
|
||||
{ ID, MemoryLayouts.BITS_16_LE, char.class, Checker.CHAR },
|
||||
{ ID, MemoryLayouts.BITS_32_LE, int.class, Checker.INT },
|
||||
{ ID, MemoryLayouts.BITS_64_LE, long.class, Checker.LONG },
|
||||
{ ID, MemoryLayouts.BITS_32_LE, float.class, Checker.FLOAT },
|
||||
{ ID, MemoryLayouts.BITS_64_LE, double.class, Checker.DOUBLE },
|
||||
{ ID, ValueLayout.JAVA_BYTE, Checker.BYTE },
|
||||
{ ID, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.SHORT },
|
||||
{ ID, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.CHAR },
|
||||
{ ID, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.INT },
|
||||
{ ID, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.LONG },
|
||||
{ ID, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.FLOAT },
|
||||
{ ID, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.DOUBLE },
|
||||
//LE, RO
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_8_LE, byte.class, Checker.BYTE },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_LE, short.class, Checker.SHORT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_LE, char.class, Checker.CHAR },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_LE, int.class, Checker.INT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_LE, long.class, Checker.LONG },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_LE, float.class, Checker.FLOAT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_LE, double.class, Checker.DOUBLE },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_BYTE, Checker.BYTE },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.SHORT },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.CHAR },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.INT },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.LONG },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.FLOAT },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN), Checker.DOUBLE },
|
||||
};
|
||||
}
|
||||
|
||||
@ -304,37 +298,37 @@ public class TestMemoryAccess {
|
||||
public Object[][] createArrayData() {
|
||||
return new Object[][] {
|
||||
//BE, RW
|
||||
{ ID, MemoryLayouts.BITS_8_BE, byte.class, ArrayChecker.BYTE },
|
||||
{ ID, MemoryLayouts.BITS_16_BE, short.class, ArrayChecker.SHORT },
|
||||
{ ID, MemoryLayouts.BITS_16_BE, char.class, ArrayChecker.CHAR },
|
||||
{ ID, MemoryLayouts.BITS_32_BE, int.class, ArrayChecker.INT },
|
||||
{ ID, MemoryLayouts.BITS_64_BE, long.class, ArrayChecker.LONG },
|
||||
{ ID, MemoryLayouts.BITS_32_BE, float.class, ArrayChecker.FLOAT },
|
||||
{ ID, MemoryLayouts.BITS_64_BE, double.class, ArrayChecker.DOUBLE },
|
||||
{ ID, ValueLayout.JAVA_BYTE, ArrayChecker.BYTE },
|
||||
{ ID, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.SHORT },
|
||||
{ ID, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.CHAR },
|
||||
{ ID, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.INT },
|
||||
{ ID, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.LONG },
|
||||
{ ID, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.FLOAT },
|
||||
{ ID, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.DOUBLE },
|
||||
//BE, RO
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_8_BE, byte.class, ArrayChecker.BYTE },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_BE, short.class, ArrayChecker.SHORT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_BE, char.class, ArrayChecker.CHAR },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_BE, int.class, ArrayChecker.INT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_BE, long.class, ArrayChecker.LONG },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_BE, float.class, ArrayChecker.FLOAT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_BE, double.class, ArrayChecker.DOUBLE },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_BYTE, ArrayChecker.BYTE },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.SHORT },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.CHAR },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.INT },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.LONG },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.FLOAT },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), ArrayChecker.DOUBLE },
|
||||
//LE, RW
|
||||
{ ID, MemoryLayouts.BITS_8_LE, byte.class, ArrayChecker.BYTE },
|
||||
{ ID, MemoryLayouts.BITS_16_LE, short.class, ArrayChecker.SHORT },
|
||||
{ ID, MemoryLayouts.BITS_16_LE, char.class, ArrayChecker.CHAR },
|
||||
{ ID, MemoryLayouts.BITS_32_LE, int.class, ArrayChecker.INT },
|
||||
{ ID, MemoryLayouts.BITS_64_LE, long.class, ArrayChecker.LONG },
|
||||
{ ID, MemoryLayouts.BITS_32_LE, float.class, ArrayChecker.FLOAT },
|
||||
{ ID, MemoryLayouts.BITS_64_LE, double.class, ArrayChecker.DOUBLE },
|
||||
{ ID, ValueLayout.JAVA_BYTE, ArrayChecker.BYTE },
|
||||
{ ID, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.SHORT },
|
||||
{ ID, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.CHAR },
|
||||
{ ID, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.INT },
|
||||
{ ID, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.LONG },
|
||||
{ ID, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.FLOAT },
|
||||
{ ID, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.DOUBLE },
|
||||
//LE, RO
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_8_LE, byte.class, ArrayChecker.BYTE },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_LE, short.class, ArrayChecker.SHORT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_LE, char.class, ArrayChecker.CHAR },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_LE, int.class, ArrayChecker.INT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_LE, long.class, ArrayChecker.LONG },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_LE, float.class, ArrayChecker.FLOAT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_LE, double.class, ArrayChecker.DOUBLE },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_BYTE, ArrayChecker.BYTE },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.SHORT },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.CHAR },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.INT },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.LONG },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.FLOAT },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN), ArrayChecker.DOUBLE },
|
||||
};
|
||||
}
|
||||
|
||||
@ -381,37 +375,45 @@ public class TestMemoryAccess {
|
||||
public Object[][] createMatrixData() {
|
||||
return new Object[][] {
|
||||
//BE, RW
|
||||
{ ID, MemoryLayouts.BITS_8_BE, byte.class, MatrixChecker.BYTE },
|
||||
{ ID, MemoryLayouts.BITS_16_BE, short.class, MatrixChecker.SHORT },
|
||||
{ ID, MemoryLayouts.BITS_16_BE, char.class, MatrixChecker.CHAR },
|
||||
{ ID, MemoryLayouts.BITS_32_BE, int.class, MatrixChecker.INT },
|
||||
{ ID, MemoryLayouts.BITS_64_BE, long.class, MatrixChecker.LONG },
|
||||
{ ID, MemoryLayouts.BITS_32_BE, float.class, MatrixChecker.FLOAT },
|
||||
{ ID, MemoryLayouts.BITS_64_BE, double.class, MatrixChecker.DOUBLE },
|
||||
{ ID, ValueLayout.JAVA_BYTE, MatrixChecker.BYTE },
|
||||
{ ID, ValueLayout.JAVA_BOOLEAN, MatrixChecker.BOOLEAN },
|
||||
{ ID, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.SHORT },
|
||||
{ ID, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.CHAR },
|
||||
{ ID, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.INT },
|
||||
{ ID, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.LONG },
|
||||
{ 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
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_8_BE, byte.class, MatrixChecker.BYTE },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_BE, short.class, MatrixChecker.SHORT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_BE, char.class, MatrixChecker.CHAR },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_BE, int.class, MatrixChecker.INT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_BE, long.class, MatrixChecker.LONG },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_BE, float.class, MatrixChecker.FLOAT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_BE, double.class, MatrixChecker.DOUBLE },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_BYTE, MatrixChecker.BYTE },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_BOOLEAN, MatrixChecker.BOOLEAN },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.SHORT },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.CHAR },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.INT },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), MatrixChecker.LONG },
|
||||
{ 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
|
||||
{ ID, MemoryLayouts.BITS_8_LE, byte.class, MatrixChecker.BYTE },
|
||||
{ ID, MemoryLayouts.BITS_16_LE, short.class, MatrixChecker.SHORT },
|
||||
{ ID, MemoryLayouts.BITS_16_LE, char.class, MatrixChecker.CHAR },
|
||||
{ ID, MemoryLayouts.BITS_32_LE, int.class, MatrixChecker.INT },
|
||||
{ ID, MemoryLayouts.BITS_64_LE, long.class, MatrixChecker.LONG },
|
||||
{ ID, MemoryLayouts.BITS_32_LE, float.class, MatrixChecker.FLOAT },
|
||||
{ ID, MemoryLayouts.BITS_64_LE, double.class, MatrixChecker.DOUBLE },
|
||||
{ ID, ValueLayout.JAVA_BYTE, MatrixChecker.BYTE },
|
||||
{ ID, ValueLayout.JAVA_BOOLEAN, MatrixChecker.BOOLEAN },
|
||||
{ ID, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.SHORT },
|
||||
{ ID, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.CHAR },
|
||||
{ ID, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.INT },
|
||||
{ ID, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.LONG },
|
||||
{ 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
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_8_LE, byte.class, MatrixChecker.BYTE },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_LE, short.class, MatrixChecker.SHORT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_LE, char.class, MatrixChecker.CHAR },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_LE, int.class, MatrixChecker.INT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_LE, long.class, MatrixChecker.LONG },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_LE, float.class, MatrixChecker.FLOAT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_LE, double.class, MatrixChecker.DOUBLE },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_BYTE, MatrixChecker.BYTE },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_BOOLEAN, MatrixChecker.BOOLEAN },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.SHORT },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.CHAR },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.INT },
|
||||
{ IMMUTABLE, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN), MatrixChecker.LONG },
|
||||
{ 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));
|
||||
};
|
||||
|
||||
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) -> {
|
||||
handle.set(segment, r, c, (short)(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));
|
||||
};
|
||||
|
||||
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) -> {
|
||||
handle.set(segment, r, c, (float)(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));
|
||||
};
|
||||
}
|
||||
|
||||
@DataProvider(name = "badCarriers")
|
||||
public Object[][] createBadCarriers() {
|
||||
return new Object[][] {
|
||||
{ void.class },
|
||||
{ boolean.class },
|
||||
{ Object.class },
|
||||
{ int[].class }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
296
test/jdk/java/foreign/TestMemoryAccessInstance.java
Normal file
296
test/jdk/java/foreign/TestMemoryAccessInstance.java
Normal 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());
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
})
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -26,7 +26,6 @@
|
||||
* @run testng TestMemoryAlignment
|
||||
*/
|
||||
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
|
||||
import jdk.incubator.foreign.GroupLayout;
|
||||
@ -36,6 +35,7 @@ import jdk.incubator.foreign.ResourceScope;
|
||||
import jdk.incubator.foreign.SequenceLayout;
|
||||
import jdk.incubator.foreign.ValueLayout;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.stream.LongStream;
|
||||
|
||||
import org.testng.annotations.*;
|
||||
@ -45,11 +45,13 @@ public class TestMemoryAlignment {
|
||||
|
||||
@Test(dataProvider = "alignments")
|
||||
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);
|
||||
ValueLayout aligned = layout.withBitAlignment(align);
|
||||
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()) {
|
||||
MemorySegment segment = MemorySegment.allocateNative(aligned, scope);
|
||||
vh.set(segment, -42);
|
||||
@ -60,12 +62,14 @@ public class TestMemoryAlignment {
|
||||
|
||||
@Test(dataProvider = "alignments")
|
||||
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);
|
||||
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);
|
||||
VarHandle vh = aligned.varHandle(int.class);
|
||||
VarHandle vh = aligned.varHandle();
|
||||
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
||||
MemorySegment segment = MemorySegment.allocateNative(alignedGroup, scope);
|
||||
vh.set(segment.asSlice(1L), -42);
|
||||
@ -77,11 +81,11 @@ public class TestMemoryAlignment {
|
||||
|
||||
@Test(dataProvider = "alignments")
|
||||
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");
|
||||
GroupLayout alignedGroup = MemoryLayout.structLayout(MemoryLayouts.PAD_8, aligned);
|
||||
GroupLayout alignedGroup = MemoryLayout.structLayout(MemoryLayout.paddingLayout(8), aligned);
|
||||
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
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
assertNotEquals(align, 8); //if align != 8, path is always unaligned
|
||||
@ -90,9 +94,9 @@ public class TestMemoryAlignment {
|
||||
|
||||
@Test(dataProvider = "alignments")
|
||||
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 {
|
||||
VarHandle vh = layout.varHandle(int.class, PathElement.sequenceElement());
|
||||
VarHandle vh = layout.varHandle(PathElement.sequenceElement());
|
||||
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
||||
MemorySegment segment = MemorySegment.allocateNative(layout, scope);
|
||||
for (long i = 0 ; i < 5 ; i++) {
|
||||
@ -106,17 +110,17 @@ public class TestMemoryAlignment {
|
||||
|
||||
@Test
|
||||
public void testPackedAccess() {
|
||||
ValueLayout vChar = MemoryLayouts.BITS_8_BE;
|
||||
ValueLayout vShort = MemoryLayouts.BITS_16_BE;
|
||||
ValueLayout vInt = MemoryLayouts.BITS_32_BE;
|
||||
ValueLayout vChar = ValueLayout.JAVA_BYTE;
|
||||
ValueLayout vShort = ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN);
|
||||
ValueLayout vInt = ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN);
|
||||
//mimic pragma pack(1)
|
||||
GroupLayout g = MemoryLayout.structLayout(vChar.withBitAlignment(8).withName("a"),
|
||||
vShort.withBitAlignment(8).withName("b"),
|
||||
vInt.withBitAlignment(8).withName("c"));
|
||||
assertEquals(g.bitAlignment(), 8);
|
||||
VarHandle vh_c = g.varHandle(byte.class, PathElement.groupElement("a"));
|
||||
VarHandle vh_s = g.varHandle(short.class, PathElement.groupElement("b"));
|
||||
VarHandle vh_i = g.varHandle(int.class, PathElement.groupElement("c"));
|
||||
VarHandle vh_c = g.varHandle(PathElement.groupElement("a"));
|
||||
VarHandle vh_s = g.varHandle(PathElement.groupElement("b"));
|
||||
VarHandle vh_i = g.varHandle(PathElement.groupElement("c"));
|
||||
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
||||
MemorySegment segment = MemorySegment.allocateNative(g, scope);
|
||||
vh_c.set(segment, Byte.MIN_VALUE);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user