8234049: Implementation of Memory Access API (Incubator)

Co-authored-by: Vlaidmir Ivanov <vladimir.x.ivanov@oracle.com>
Reviewed-by: alanb, psandoz, chegar, rriggs, plevart, briangoetz, jrose, adinn, vlivanov
This commit is contained in:
Jorn Vernee 2019-12-12 22:59:57 +00:00 committed by Maurizio Cimadamore
parent 7cdecd8981
commit 8f4f088a12
59 changed files with 7849 additions and 147 deletions

View File

@ -58,6 +58,7 @@ BOOT_MODULES += \
java.rmi \
java.security.sasl \
java.xml \
jdk.incubator.foreign \
jdk.internal.vm.ci \
jdk.jfr \
jdk.management \
@ -162,6 +163,7 @@ DOCS_MODULES += \
jdk.jsobject \
jdk.jshell \
jdk.jstatd \
jdk.incubator.foreign \
jdk.localedata \
jdk.management \
jdk.management.agent \

View File

@ -159,6 +159,108 @@ endef
################################################################################
################################################################################
# Setup a rule for generating a VarHandleMemoryAddress java class
# Param 1 - Variable declaration prefix
# Param 2 - Type with first letter capitalized
define GenerateVarHandleMemoryAddress
$1_Type := $2
$1_FILENAME := $(VARHANDLES_GENSRC_DIR)/VarHandleMemoryAddressAs$$($1_Type)s.java
ifeq ($$($1_Type), Byte)
$1_type := byte
$1_BoxType := $$($1_Type)
$1_rawType := $$($1_type)
$1_RawType := $$($1_Type)
$1_RawBoxType := $$($1_BoxType)
$1_ARGS += -Kbyte
endif
ifeq ($$($1_Type), Short)
$1_type := short
$1_BoxType := $$($1_Type)
$1_rawType := $$($1_type)
$1_RawType := $$($1_Type)
$1_RawBoxType := $$($1_BoxType)
endif
ifeq ($$($1_Type), Char)
$1_type := char
$1_BoxType := Character
$1_rawType := $$($1_type)
$1_RawType := $$($1_Type)
$1_RawBoxType := $$($1_BoxType)
endif
ifeq ($$($1_Type), Int)
$1_type := int
$1_BoxType := Integer
$1_rawType := $$($1_type)
$1_RawType := $$($1_Type)
$1_RawBoxType := $$($1_BoxType)
$1_ARGS += -KCAS
$1_ARGS += -KAtomicAdd
$1_ARGS += -KBitwise
endif
ifeq ($$($1_Type), Long)
$1_type := long
$1_BoxType := $$($1_Type)
$1_rawType := $$($1_type)
$1_RawType := $$($1_Type)
$1_RawBoxType := $$($1_BoxType)
$1_ARGS += -KCAS
$1_ARGS += -KAtomicAdd
$1_ARGS += -KBitwise
endif
ifeq ($$($1_Type), Float)
$1_type := float
$1_BoxType := $$($1_Type)
$1_rawType := int
$1_RawType := Int
$1_RawBoxType := Integer
$1_ARGS += -KCAS
$1_ARGS += -KfloatingPoint
endif
ifeq ($$($1_Type), Double)
$1_type := double
$1_BoxType := $$($1_Type)
$1_rawType := long
$1_RawType := Long
$1_RawBoxType := Long
$1_ARGS += -KCAS
$1_ARGS += -KfloatingPoint
endif
$$($1_FILENAME): $(VARHANDLES_SRC_DIR)/X-VarHandleMemoryAddressView.java.template $(BUILD_TOOLS_JDK)
$$(call MakeDir, $$(@D))
$(RM) $$@
$(TOOL_SPP) -nel -K$$($1_type) \
-Dtype=$$($1_type) -DType=$$($1_Type) -DBoxType=$$($1_BoxType) \
-DrawType=$$($1_rawType) -DRawType=$$($1_RawType) -DRawBoxType=$$($1_RawBoxType) \
$$($1_ARGS) -i$$< -o$$@
GENSRC_VARHANDLES += $$($1_FILENAME)
endef
################################################################################
# List the types to generate source for, with capitalized first letter
VARHANDLES_TYPES := Boolean Byte Short Char Int Long Float Double Reference
$(foreach t, $(VARHANDLES_TYPES), \
@ -169,6 +271,11 @@ VARHANDLES_BYTE_ARRAY_TYPES := Short Char Int Long Float Double
$(foreach t, $(VARHANDLES_BYTE_ARRAY_TYPES), \
$(eval $(call GenerateVarHandleByteArray,VAR_HANDLE_BYTE_ARRAY_$t,$t)))
# List the types to generate source for, with capitalized first letter
VARHANDLES_MEMORY_ADDRESS_TYPES := Byte Short Char Int Long Float Double
$(foreach t, $(VARHANDLES_MEMORY_ADDRESS_TYPES), \
$(eval $(call GenerateVarHandleMemoryAddress,VAR_HANDLE_MEMORY_ADDRESS_$t,$t)))
GENSRC_JAVA_BASE += $(GENSRC_VARHANDLES)
# Include custom extension post hook

View File

@ -219,7 +219,9 @@ static bool trust_final_non_static_fields(ciInstanceKlass* holder) {
// Never trust strangely unstable finals: System.out, etc.
return false;
// Even if general trusting is disabled, trust system-built closures in these packages.
if (holder->is_in_package("java/lang/invoke") || holder->is_in_package("sun/invoke"))
if (holder->is_in_package("java/lang/invoke") || holder->is_in_package("sun/invoke") ||
holder->is_in_package("jdk/internal/foreign") || holder->is_in_package("jdk/incubator/foreign") ||
holder->is_in_package("java/lang"))
return true;
// Trust VM unsafe anonymous classes. They are private API (jdk.internal.misc.Unsafe)
// and can't be serialized, so there is no hacking of finals going on with them.

View File

@ -0,0 +1,494 @@
/*
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.invoke;
import jdk.internal.access.foreign.MemoryAddressProxy;
import jdk.internal.misc.Unsafe;
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.org.objectweb.asm.Type;
import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
import jdk.internal.vm.annotation.ForceInline;
import sun.security.action.GetBooleanAction;
import sun.security.action.GetPropertyAction;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
import static jdk.internal.org.objectweb.asm.Opcodes.BIPUSH;
import static jdk.internal.org.objectweb.asm.Opcodes.CHECKCAST;
import static jdk.internal.org.objectweb.asm.Opcodes.GETFIELD;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_0;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_1;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_2;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_3;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_4;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_5;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_M1;
import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static jdk.internal.org.objectweb.asm.Opcodes.LADD;
import static jdk.internal.org.objectweb.asm.Opcodes.LALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.LASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.LLOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.LMUL;
import static jdk.internal.org.objectweb.asm.Opcodes.NEWARRAY;
import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD;
import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
import static jdk.internal.org.objectweb.asm.Opcodes.DUP;
import static jdk.internal.org.objectweb.asm.Opcodes.SIPUSH;
import static jdk.internal.org.objectweb.asm.Opcodes.T_LONG;
class AddressVarHandleGenerator {
private static final String DEBUG_DUMP_CLASSES_DIR_PROPERTY = "jdk.internal.foreign.ClassGenerator.DEBUG_DUMP_CLASSES_DIR";
private static final boolean DEBUG =
GetBooleanAction.privilegedGetProperty("jdk.internal.foreign.ClassGenerator.DEBUG");
private static final Class<?> BASE_CLASS = VarHandleMemoryAddressBase.class;
private static final HashMap<Class<?>, Class<?>> helperClassCache;
static {
helperClassCache = new HashMap<>();
helperClassCache.put(byte.class, VarHandleMemoryAddressAsBytes.class);
helperClassCache.put(short.class, VarHandleMemoryAddressAsShorts.class);
helperClassCache.put(char.class, VarHandleMemoryAddressAsChars.class);
helperClassCache.put(int.class, VarHandleMemoryAddressAsInts.class);
helperClassCache.put(long.class, VarHandleMemoryAddressAsLongs.class);
helperClassCache.put(float.class, VarHandleMemoryAddressAsFloats.class);
helperClassCache.put(double.class, VarHandleMemoryAddressAsDoubles.class);
}
private static final File DEBUG_DUMP_CLASSES_DIR;
static {
String path = GetPropertyAction.privilegedGetProperty(DEBUG_DUMP_CLASSES_DIR_PROPERTY);
if (path == null) {
DEBUG_DUMP_CLASSES_DIR = null;
} else {
DEBUG_DUMP_CLASSES_DIR = new File(path);
}
}
private static final Unsafe U = Unsafe.getUnsafe();
private final String implClassName;
private final int dimensions;
private final Class<?> carrier;
private final Class<?> helperClass;
private final VarForm form;
AddressVarHandleGenerator(Class<?> carrier, int dims) {
this.dimensions = dims;
this.carrier = carrier;
Class<?>[] components = new Class<?>[dimensions];
Arrays.fill(components, long.class);
this.form = new VarForm(BASE_CLASS, MemoryAddressProxy.class, carrier, components);
this.helperClass = helperClassCache.get(carrier);
this.implClassName = helperClass.getName().replace('.', '/') + dimensions;
}
/*
* Generate a VarHandle memory access factory.
* The factory has type (ZJJ[J)VarHandle.
*/
MethodHandle generateHandleFactory() {
Class<?> implCls = generateClass();
try {
Class<?>[] components = new Class<?>[dimensions];
Arrays.fill(components, long.class);
VarForm form = new VarForm(implCls, MemoryAddressProxy.class, carrier, components);
MethodType constrType = MethodType.methodType(void.class, VarForm.class, boolean.class, long.class, long.class, long.class, long[].class);
MethodHandle constr = MethodHandles.Lookup.IMPL_LOOKUP.findConstructor(implCls, constrType);
constr = MethodHandles.insertArguments(constr, 0, form);
return constr;
} catch (Throwable ex) {
throw new AssertionError(ex);
}
}
/*
* Generate a specialized VarHandle class for given carrier
* and access coordinates.
*/
Class<?> generateClass() {
BinderClassWriter cw = new BinderClassWriter();
if (DEBUG) {
System.out.println("Generating header implementation class");
}
cw.visit(52, ACC_PUBLIC | ACC_SUPER, implClassName, null, Type.getInternalName(BASE_CLASS), null);
//add dimension fields
for (int i = 0; i < dimensions; i++) {
cw.visitField(ACC_PRIVATE | ACC_FINAL, "dim" + i, "J", null, null);
}
addConstructor(cw);
addAccessModeTypeMethod(cw);
addStridesAccessor(cw);
addCarrierAccessor(cw);
for (VarHandle.AccessMode mode : VarHandle.AccessMode.values()) {
addAccessModeMethodIfNeeded(mode, cw);
}
cw.visitEnd();
byte[] classBytes = cw.toByteArray();
return defineClass(cw, classBytes);
}
void addConstructor(BinderClassWriter cw) {
MethodType constrType = MethodType.methodType(void.class, VarForm.class, boolean.class, long.class, long.class, long.class, long[].class);
MethodVisitor mv = cw.visitMethod(0, "<init>", constrType.toMethodDescriptorString(), null, null);
mv.visitCode();
//super call
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(VarForm.class));
mv.visitVarInsn(ILOAD, 2);
mv.visitVarInsn(LLOAD, 3);
mv.visitVarInsn(LLOAD, 5);
mv.visitVarInsn(LLOAD, 7);
mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(BASE_CLASS), "<init>",
MethodType.methodType(void.class, VarForm.class, boolean.class, long.class, long.class, long.class).toMethodDescriptorString(), false);
//init dimensions
for (int i = 0 ; i < dimensions ; i++) {
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 9);
mv.visitLdcInsn(i);
mv.visitInsn(LALOAD);
mv.visitFieldInsn(PUTFIELD, implClassName, "dim" + i, "J");
}
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
void addAccessModeTypeMethod(BinderClassWriter cw) {
MethodType modeMethType = MethodType.methodType(MethodType.class, VarHandle.AccessMode.class);
MethodVisitor mv = cw.visitMethod(ACC_FINAL, "accessModeTypeUncached", modeMethType.toMethodDescriptorString(), null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 1);
mv.visitFieldInsn(GETFIELD, Type.getInternalName(VarHandle.AccessMode.class), "at", Type.getDescriptor(VarHandle.AccessType.class));
mv.visitLdcInsn(cw.makeConstantPoolPatch(MemoryAddressProxy.class));
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Class.class));
mv.visitLdcInsn(cw.makeConstantPoolPatch(carrier));
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Class.class));
Class<?>[] dims = new Class<?>[dimensions];
Arrays.fill(dims, long.class);
mv.visitLdcInsn(cw.makeConstantPoolPatch(dims));
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Class[].class));
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(VarHandle.AccessType.class),
"accessModeType", MethodType.methodType(MethodType.class, Class.class, Class.class, Class[].class).toMethodDescriptorString(), false);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
void addAccessModeMethodIfNeeded(VarHandle.AccessMode mode, BinderClassWriter cw) {
String methName = mode.methodName();
MethodType methType = form.getMethodType(mode.at.ordinal())
.insertParameterTypes(0, BASE_CLASS);
try {
MethodType helperType = methType.insertParameterTypes(2, long.class);
if (dimensions > 0) {
helperType = helperType.dropParameterTypes(3, 3 + dimensions);
}
//try to resolve...
String helperMethodName = methName + "0";
MethodHandles.Lookup.IMPL_LOOKUP
.findStatic(helperClass,
helperMethodName,
helperType);
MethodVisitor mv = cw.visitMethod(ACC_STATIC, methName, methType.toMethodDescriptorString(), null, null);
mv.visitAnnotation(Type.getDescriptor(ForceInline.class), true);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0); // handle impl
mv.visitVarInsn(ALOAD, 1); // receiver
// offset calculation
int slot = 2;
mv.visitVarInsn(ALOAD, 0); // load recv
mv.visitFieldInsn(GETFIELD, Type.getInternalName(BASE_CLASS), "offset", "J");
for (int i = 0 ; i < dimensions ; i++) {
mv.visitVarInsn(ALOAD, 0); // load recv
mv.visitTypeInsn(CHECKCAST, implClassName);
mv.visitFieldInsn(GETFIELD, implClassName, "dim" + i, "J");
mv.visitVarInsn(LLOAD, slot);
mv.visitInsn(LMUL);
mv.visitInsn(LADD);
slot += 2;
}
for (int i = 2 + dimensions; i < methType.parameterCount() ; i++) {
Class<?> param = methType.parameterType(i);
mv.visitVarInsn(loadInsn(param), slot); // load index
slot += getSlotsForType(param);
}
//call helper
mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(helperClass), helperMethodName,
helperType.toMethodDescriptorString(), false);
mv.visitInsn(returnInsn(helperType.returnType()));
mv.visitMaxs(0, 0);
mv.visitEnd();
} catch (ReflectiveOperationException ex) {
//not found, skip
}
}
void addStridesAccessor(BinderClassWriter cw) {
MethodVisitor mv = cw.visitMethod(ACC_FINAL, "strides", "()[J", null, null);
mv.visitCode();
iConstInsn(mv, dimensions);
mv.visitIntInsn(NEWARRAY, T_LONG);
for (int i = 0 ; i < dimensions ; i++) {
mv.visitInsn(DUP);
iConstInsn(mv, i);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, implClassName, "dim" + i, "J");
mv.visitInsn(LASTORE);
}
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
void addCarrierAccessor(BinderClassWriter cw) {
MethodVisitor mv = cw.visitMethod(ACC_FINAL, "carrier", "()Ljava/lang/Class;", null, null);
mv.visitCode();
mv.visitLdcInsn(cw.makeConstantPoolPatch(carrier));
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Class.class));
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
//where
private Class<?> defineClass(BinderClassWriter cw, byte[] classBytes) {
try {
if (DEBUG_DUMP_CLASSES_DIR != null) {
debugWriteClassToFile(classBytes);
}
Object[] patches = cw.resolvePatches(classBytes);
Class<?> c = U.defineAnonymousClass(BASE_CLASS, classBytes, patches);
return c;
} catch (Throwable e) {
debugPrintClass(classBytes);
throw e;
}
}
// shared code generation helpers
private static int getSlotsForType(Class<?> c) {
if (c == long.class || c == double.class) {
return 2;
}
return 1;
}
/**
* Emits an actual return instruction conforming to the given return type.
*/
private int returnInsn(Class<?> type) {
return switch (LambdaForm.BasicType.basicType(type)) {
case I_TYPE -> Opcodes.IRETURN;
case J_TYPE -> Opcodes.LRETURN;
case F_TYPE -> Opcodes.FRETURN;
case D_TYPE -> Opcodes.DRETURN;
case L_TYPE -> Opcodes.ARETURN;
case V_TYPE -> RETURN;
};
}
private int loadInsn(Class<?> type) {
return switch (LambdaForm.BasicType.basicType(type)) {
case I_TYPE -> Opcodes.ILOAD;
case J_TYPE -> LLOAD;
case F_TYPE -> Opcodes.FLOAD;
case D_TYPE -> Opcodes.DLOAD;
case L_TYPE -> Opcodes.ALOAD;
case V_TYPE -> throw new IllegalStateException("Cannot load void");
};
}
private static void iConstInsn(MethodVisitor mv, int i) {
switch (i) {
case -1, 0, 1, 2, 3, 4, 5:
mv.visitInsn(ICONST_0 + i);
break;
default:
if(i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE) {
mv.visitIntInsn(BIPUSH, i);
} else if (i >= Short.MIN_VALUE && i <= Short.MAX_VALUE) {
mv.visitIntInsn(SIPUSH, i);
} else {
mv.visitLdcInsn(i);
}
}
}
// debug helpers
private static String debugPrintClass(byte[] classFile) {
ClassReader cr = new ClassReader(classFile);
StringWriter sw = new StringWriter();
cr.accept(new TraceClassVisitor(new PrintWriter(sw)), 0);
return sw.toString();
}
private void debugWriteClassToFile(byte[] classFile) {
File file = new File(DEBUG_DUMP_CLASSES_DIR, implClassName + ".class");
if (DEBUG) {
System.err.println("Dumping class " + implClassName + " to " + file);
}
try {
debugWriteDataToFile(classFile, file);
} catch (Exception e) {
throw new RuntimeException("Failed to write class " + implClassName + " to file " + file);
}
}
private void debugWriteDataToFile(byte[] data, File file) {
if (file.exists()) {
file.delete();
}
if (file.exists()) {
throw new RuntimeException("Failed to remove pre-existing file " + file);
}
File parent = file.getParentFile();
if (!parent.exists()) {
parent.mkdirs();
}
if (!parent.exists()) {
throw new RuntimeException("Failed to create " + parent);
}
if (!parent.isDirectory()) {
throw new RuntimeException(parent + " is not a directory");
}
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(data);
} catch (IOException e) {
throw new RuntimeException("Failed to write class " + implClassName + " to file " + file);
}
}
static class BinderClassWriter extends ClassWriter {
private final ArrayList<ConstantPoolPatch> cpPatches = new ArrayList<>();
private int curUniquePatchIndex = 0;
BinderClassWriter() {
super(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
}
public String makeConstantPoolPatch(Object o) {
int myUniqueIndex = curUniquePatchIndex++;
String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + myUniqueIndex;
int index = newConst(cpPlaceholder);
cpPatches.add(new ConstantPoolPatch(index, cpPlaceholder, o));
return cpPlaceholder;
}
public Object[] resolvePatches(byte[] classFile) {
if (cpPatches.isEmpty()) {
return null;
}
int size = ((classFile[8] & 0xFF) << 8) | (classFile[9] & 0xFF);
Object[] patches = new Object[size];
for (ConstantPoolPatch p : cpPatches) {
if (p.index >= size) {
throw new InternalError("Failed to resolve constant pool patch entries");
}
patches[p.index] = p.value;
}
return patches;
}
static class ConstantPoolPatch {
final int index;
final String placeholder;
final Object value;
ConstantPoolPatch(int index, String placeholder, Object value) {
this.index = index;
this.placeholder = placeholder;
this.value = value;
}
@Override
public String toString() {
return "CpPatch/index="+index+",placeholder="+placeholder+",value="+value;
}
}
}
}

View File

@ -41,6 +41,7 @@ import sun.invoke.util.VerifyType;
import sun.invoke.util.Wrapper;
import java.lang.reflect.Array;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@ -1790,6 +1791,44 @@ abstract class MethodHandleImpl {
invokerMethodTypes, callSiteMethodTypes);
}
@Override
public VarHandle memoryAddressViewVarHandle(Class<?> carrier, long alignmentMask,
ByteOrder order, long offset, long[] strides) {
return VarHandles.makeMemoryAddressViewHandle(carrier, alignmentMask, order, offset, strides);
}
@Override
public Class<?> memoryAddressCarrier(VarHandle handle) {
return checkMemAccessHandle(handle).carrier();
}
@Override
public long memoryAddressAlignmentMask(VarHandle handle) {
return checkMemAccessHandle(handle).alignmentMask;
}
@Override
public ByteOrder memoryAddressByteOrder(VarHandle handle) {
return checkMemAccessHandle(handle).be ?
ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
}
@Override
public long memoryAddressOffset(VarHandle handle) {
return checkMemAccessHandle(handle).offset;
}
@Override
public long[] memoryAddressStrides(VarHandle handle) {
return checkMemAccessHandle(handle).strides();
}
private VarHandleMemoryAddressBase checkMemAccessHandle(VarHandle handle) {
if (!(handle instanceof VarHandleMemoryAddressBase)) {
throw new IllegalArgumentException("Not a memory access varhandle: " + handle);
}
return (VarHandleMemoryAddressBase) handle;
}
});
}

View File

@ -0,0 +1,63 @@
/*
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.invoke;
/**
* Base class for memory access var handle implementations.
*/
abstract class VarHandleMemoryAddressBase extends VarHandle {
/** endianness **/
final boolean be;
/** access size (in bytes, computed from var handle carrier type) **/
final long length;
/** access offset (in bytes); must be compatible with {@code alignmentMask} **/
final long offset;
/** alignment constraint (in bytes, expressed as a bit mask) **/
final long alignmentMask;
VarHandleMemoryAddressBase(VarForm form, boolean be, long length, long offset, long alignmentMask) {
super(form);
this.be = be;
this.length = length;
this.offset = offset;
this.alignmentMask = alignmentMask;
}
static IllegalStateException newIllegalStateExceptionForMisalignedAccess(long address) {
return new IllegalStateException("Misaligned access at address: " + address);
}
/**
* Strides used for multi-dimensional access; each stride must be compatible with {@code alignmentMask}.
*/
abstract long[] strides();
abstract Class<?> carrier();
}

View File

@ -25,13 +25,26 @@
package java.lang.invoke;
import sun.invoke.util.Wrapper;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.ByteOrder;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
final class VarHandles {
static ClassValue<ConcurrentMap<Integer, MethodHandle>> ADDRESS_FACTORIES = new ClassValue<>() {
@Override
protected ConcurrentMap<Integer, MethodHandle> computeValue(Class<?> type) {
return new ConcurrentHashMap<>();
}
};
static VarHandle makeFieldHandle(MemberName f, Class<?> refc, Class<?> type, boolean isWriteAllowedOnFinalFields) {
if (!f.isStatic()) {
long foffset = MethodHandleNatives.objectFieldOffset(f);
@ -279,6 +292,43 @@ final class VarHandles {
throw new UnsupportedOperationException();
}
/**
* Creates a memory access VarHandle.
*
* Resulting VarHandle will take a memory address as first argument,
* and a certain number of coordinate {@code long} parameters, depending on the length
* of the {@code strides} argument array.
*
* Coordinates are multiplied with corresponding scale factors ({@code strides}) and added
* to a single fixed offset to compute an effective offset from the given MemoryAddress for the access.
*
* @param carrier the Java carrier type.
* @param alignmentMask alignment requirement to be checked upon access. In bytes. Expressed as a mask.
* @param byteOrder the byte order.
* @param offset a constant offset for the access.
* @param strides the scale factors with which to multiply given access coordinates.
* @return the created VarHandle.
*/
static VarHandle makeMemoryAddressViewHandle(Class<?> carrier, long alignmentMask,
ByteOrder byteOrder, long offset, long[] strides) {
if (!carrier.isPrimitive() || carrier == void.class || carrier == boolean.class) {
throw new IllegalArgumentException("Invalid carrier: " + carrier.getName());
}
long size = Wrapper.forPrimitiveType(carrier).bitWidth() / 8;
boolean be = byteOrder == ByteOrder.BIG_ENDIAN;
Map<Integer, MethodHandle> carrierFactory = ADDRESS_FACTORIES.get(carrier);
MethodHandle fac = carrierFactory.computeIfAbsent(strides.length,
dims -> new AddressVarHandleGenerator(carrier, dims)
.generateHandleFactory());
try {
return (VarHandle)fac.invoke(be, size, offset, alignmentMask, strides);
} catch (Throwable ex) {
throw new IllegalStateException(ex);
}
}
// /**
// * A helper program to generate the VarHandleGuards class with a set of
// * static guard methods each of which corresponds to a particular shape and

View File

@ -24,6 +24,8 @@
*/
package java.lang.invoke;
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Unsafe;
import jdk.internal.util.Preconditions;
import jdk.internal.vm.annotation.ForceInline;
@ -38,6 +40,8 @@ import static java.lang.invoke.MethodHandleStatics.UNSAFE;
final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
static JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
static final int ALIGN = $BoxType$.BYTES - 1;
#if[floatingPoint]
@ -529,6 +533,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
@ForceInline
static int index(ByteBuffer bb, int index) {
nioAccess.checkSegment(bb);
return Preconditions.checkIndex(index, UNSAFE.getInt(bb, BUFFER_LIMIT) - ALIGN, null);
}
@ -536,7 +541,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
static int indexRO(ByteBuffer bb, int index) {
if (UNSAFE.getBoolean(bb, BYTE_BUFFER_IS_READ_ONLY))
throw new ReadOnlyBufferException();
return Preconditions.checkIndex(index, UNSAFE.getInt(bb, BUFFER_LIMIT) - ALIGN, null);
return index(bb, index);
}
@ForceInline

View File

@ -0,0 +1,512 @@
/*
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.invoke;
import jdk.internal.access.foreign.MemoryAddressProxy;
import jdk.internal.vm.annotation.ForceInline;
import java.util.Objects;
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
#warn
final class VarHandleMemoryAddressAs$Type$s {
static final boolean BE = UNSAFE.isBigEndian();
static final int VM_ALIGN = $BoxType$.BYTES - 1;
#if[floatingPoint]
@ForceInline
static $rawType$ convEndian(boolean big, $type$ v) {
$rawType$ rv = $Type$.$type$ToRaw$RawType$Bits(v);
return big == BE ? rv : $RawBoxType$.reverseBytes(rv);
}
@ForceInline
static $type$ convEndian(boolean big, $rawType$ rv) {
rv = big == BE ? rv : $RawBoxType$.reverseBytes(rv);
return $Type$.$rawType$BitsTo$Type$(rv);
}
#else[floatingPoint]
#if[byte]
@ForceInline
static $type$ convEndian(boolean big, $type$ n) {
return n;
}
#else[byte]
@ForceInline
static $type$ convEndian(boolean big, $type$ n) {
return big == BE ? n : $BoxType$.reverseBytes(n);
}
#end[byte]
#end[floatingPoint]
@ForceInline
static MemoryAddressProxy checkAddress(Object obb, long offset, long length, boolean ro) {
MemoryAddressProxy oo = (MemoryAddressProxy)Objects.requireNonNull(obb);
oo.checkAccess(offset, length, ro);
return oo;
}
@ForceInline
static long offset(MemoryAddressProxy bb, long offset, long alignmentMask) {
long address = offsetNoVMAlignCheck(bb, offset, alignmentMask);
if ((address & VM_ALIGN) != 0) {
throw VarHandleMemoryAddressBase.newIllegalStateExceptionForMisalignedAccess(address);
}
return address;
}
@ForceInline
static long offsetNoVMAlignCheck(MemoryAddressProxy bb, long offset, long alignmentMask) {
long base = bb.unsafeGetOffset();
long address = base + offset;
//note: the offset portion has already been aligned-checked, by construction
if ((base & alignmentMask) != 0) {
throw VarHandleMemoryAddressBase.newIllegalStateExceptionForMisalignedAccess(address);
}
return address;
}
@ForceInline
static $type$ get0(VarHandleMemoryAddressBase handle, Object obb, long base) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true);
#if[floatingPoint]
$rawType$ rawValue = UNSAFE.get$RawType$Unaligned(
bb.unsafeGetBase(),
offsetNoVMAlignCheck(bb, base, handle.alignmentMask),
handle.be);
return $Type$.$rawType$BitsTo$Type$(rawValue);
#else[floatingPoint]
#if[byte]
return UNSAFE.get$Type$(
bb.unsafeGetBase(),
offsetNoVMAlignCheck(bb, base, handle.alignmentMask));
#else[byte]
return UNSAFE.get$Type$Unaligned(
bb.unsafeGetBase(),
offsetNoVMAlignCheck(bb, base, handle.alignmentMask),
handle.be);
#end[byte]
#end[floatingPoint]
}
@ForceInline
static void set0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
#if[floatingPoint]
UNSAFE.put$RawType$Unaligned(
bb.unsafeGetBase(),
offsetNoVMAlignCheck(bb, base, handle.alignmentMask),
$Type$.$type$ToRaw$RawType$Bits(value),
handle.be);
#else[floatingPoint]
#if[byte]
UNSAFE.put$Type$(
bb.unsafeGetBase(),
offsetNoVMAlignCheck(bb, base, handle.alignmentMask),
value);
#else[byte]
UNSAFE.put$Type$Unaligned(
bb.unsafeGetBase(),
offsetNoVMAlignCheck(bb, base, handle.alignmentMask),
value,
handle.be);
#end[byte]
#end[floatingPoint]
}
@ForceInline
static $type$ getVolatile0(VarHandleMemoryAddressBase handle, Object obb, long base) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true);
return convEndian(handle.be,
UNSAFE.get$RawType$Volatile(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask)));
}
@ForceInline
static void setVolatile0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
UNSAFE.put$RawType$Volatile(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, value));
}
@ForceInline
static $type$ getAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true);
return convEndian(handle.be,
UNSAFE.get$RawType$Acquire(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask)));
}
@ForceInline
static void setRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
UNSAFE.put$RawType$Release(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, value));
}
@ForceInline
static $type$ getOpaque0(VarHandleMemoryAddressBase handle, Object obb, long base) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true);
return convEndian(handle.be,
UNSAFE.get$RawType$Opaque(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask)));
}
@ForceInline
static void setOpaque0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
UNSAFE.put$RawType$Opaque(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, value));
}
#if[CAS]
@ForceInline
static boolean compareAndSet0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return UNSAFE.compareAndSet$RawType$(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value));
}
@ForceInline
static $type$ compareAndExchange0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.compareAndExchange$RawType$(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value)));
}
@ForceInline
static $type$ compareAndExchangeAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.compareAndExchange$RawType$Acquire(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value)));
}
@ForceInline
static $type$ compareAndExchangeRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.compareAndExchange$RawType$Release(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value)));
}
@ForceInline
static boolean weakCompareAndSetPlain0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return UNSAFE.weakCompareAndSet$RawType$Plain(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value));
}
@ForceInline
static boolean weakCompareAndSet0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return UNSAFE.weakCompareAndSet$RawType$(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value));
}
@ForceInline
static boolean weakCompareAndSetAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return UNSAFE.weakCompareAndSet$RawType$Acquire(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value));
}
@ForceInline
static boolean weakCompareAndSetRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return UNSAFE.weakCompareAndSet$RawType$Release(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value));
}
@ForceInline
static $type$ getAndSet0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.getAndSet$RawType$(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, value)));
}
@ForceInline
static $type$ getAndSetAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.getAndSet$RawType$Acquire(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, value)));
}
@ForceInline
static $type$ getAndSetRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.getAndSet$RawType$Release(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, value)));
}
#end[CAS]
#if[AtomicAdd]
@ForceInline
static $type$ getAndAdd0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ delta) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndAdd$RawType$(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
delta);
} else {
return getAndAddConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), delta);
}
}
@ForceInline
static $type$ getAndAddAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ delta) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndAdd$RawType$Acquire(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
delta);
} else {
return getAndAddConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), delta);
}
}
@ForceInline
static $type$ getAndAddRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ delta) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndAdd$RawType$Release(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
delta);
} else {
return getAndAddConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), delta);
}
}
@ForceInline
static $type$ getAndAddConvEndianWithCAS(MemoryAddressProxy bb, long offset, $type$ delta) {
$type$ nativeExpectedValue, expectedValue;
Object base = bb.unsafeGetBase();
do {
nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset);
expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue);
} while (!UNSAFE.weakCompareAndSet$RawType$(base, offset,
nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue + delta)));
return expectedValue;
}
#end[AtomicAdd]
#if[Bitwise]
@ForceInline
static $type$ getAndBitwiseOr0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseOr$RawType$(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseOrRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseOr$RawType$Release(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseOrAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseOr$RawType$Acquire(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseOrConvEndianWithCAS(MemoryAddressProxy bb, long offset, $type$ value) {
$type$ nativeExpectedValue, expectedValue;
Object base = bb.unsafeGetBase();
do {
nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset);
expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue);
} while (!UNSAFE.weakCompareAndSet$RawType$(base, offset,
nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue | value)));
return expectedValue;
}
@ForceInline
static $type$ getAndBitwiseAnd0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseAnd$RawType$(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseAndRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseAnd$RawType$Release(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseAndAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseAnd$RawType$Acquire(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseAndConvEndianWithCAS(MemoryAddressProxy bb, long offset, $type$ value) {
$type$ nativeExpectedValue, expectedValue;
Object base = bb.unsafeGetBase();
do {
nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset);
expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue);
} while (!UNSAFE.weakCompareAndSet$RawType$(base, offset,
nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue & value)));
return expectedValue;
}
@ForceInline
static $type$ getAndBitwiseXor0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseXor$RawType$(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseXorRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseXor$RawType$Release(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseXorAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseXor$RawType$Acquire(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseXorConvEndianWithCAS(MemoryAddressProxy bb, long offset, $type$ value) {
$type$ nativeExpectedValue, expectedValue;
Object base = bb.unsafeGetBase();
do {
nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset);
expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue);
} while (!UNSAFE.weakCompareAndSet$RawType$(base, offset,
nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue ^ value)));
return expectedValue;
}
#end[Bitwise]
}

View File

@ -28,7 +28,9 @@ package java.nio;
import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.Unsafe;
import jdk.internal.vm.annotation.ForceInline;
import java.util.Spliterator;
@ -213,13 +215,26 @@ public abstract class Buffer {
// NOTE: hoisted here for speed in JNI GetDirectBufferAddress
long address;
// Used by buffers generated by the memory access API (JEP-370)
final MemorySegmentProxy segment;
// Creates a new buffer with given address and capacity.
//
Buffer(long addr, int cap, MemorySegmentProxy segment) {
this.address = addr;
this.capacity = cap;
this.segment = segment;
}
// Creates a new buffer with the given mark, position, limit, and capacity,
// after checking invariants.
//
Buffer(int mark, int pos, int lim, int cap) { // package-private
Buffer(int mark, int pos, int lim, int cap, MemorySegmentProxy segment) { // package-private
if (cap < 0)
throw createCapacityException(cap);
this.capacity = cap;
this.segment = segment;
limit(lim);
position(pos);
if (mark >= 0) {
@ -731,6 +746,13 @@ public abstract class Buffer {
mark = -1;
}
@ForceInline
final void checkSegment() {
if (segment != null) {
segment.checkValidState();
}
}
static {
// setup access to this package in SharedSecrets
SharedSecrets.setJavaNioAccess(
@ -739,6 +761,31 @@ public abstract class Buffer {
public JavaNioAccess.BufferPool getDirectBufferPool() {
return Bits.BUFFER_POOL;
}
@Override
public ByteBuffer newDirectByteBuffer(long addr, int cap, Object obj, MemorySegmentProxy segment) {
return new DirectByteBuffer(addr, cap, obj, segment);
}
@Override
public ByteBuffer newHeapByteBuffer(byte[] hb, int offset, int capacity, MemorySegmentProxy segment) {
return new HeapByteBuffer(hb, offset, capacity, segment);
}
@Override
public Object getBufferBase(ByteBuffer bb) {
return bb.base();
}
@Override
public long getBufferAddress(ByteBuffer bb) {
return bb.address;
}
@Override
public void checkSegment(Buffer buffer) {
buffer.checkSegment();
}
});
}

View File

@ -28,6 +28,7 @@
package java.nio;
import java.util.Objects;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.Unsafe;
class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
@ -40,11 +41,11 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
#end[rw]
ByteBufferAs$Type$Buffer$RW$$BO$(ByteBuffer bb) { // package-private
ByteBufferAs$Type$Buffer$RW$$BO$(ByteBuffer bb, MemorySegmentProxy segment) { // package-private
#if[rw]
super(-1, 0,
bb.remaining() >> $LG_BYTES_PER_VALUE$,
bb.remaining() >> $LG_BYTES_PER_VALUE$);
bb.remaining() >> $LG_BYTES_PER_VALUE$, segment);
this.bb = bb;
// enforce limit == capacity
int cap = this.capacity();
@ -53,21 +54,21 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
assert (pos <= cap);
address = bb.address;
#else[rw]
super(bb);
super(bb, segment);
#end[rw]
}
ByteBufferAs$Type$Buffer$RW$$BO$(ByteBuffer bb,
int mark, int pos, int lim, int cap,
long addr)
long addr, MemorySegmentProxy segment)
{
#if[rw]
super(mark, pos, lim, cap);
super(mark, pos, lim, cap, segment);
this.bb = bb;
address = addr;
assert address >= bb.address;
#else[rw]
super(bb, mark, pos, lim, cap, addr);
super(bb, mark, pos, lim, cap, addr, segment);
#end[rw]
}
@ -82,7 +83,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
assert (pos <= lim);
int rem = (pos <= lim ? lim - pos : 0);
long addr = byteOffset(pos);
return new ByteBufferAs$Type$Buffer$RW$$BO$(bb, -1, 0, rem, rem, addr);
return new ByteBufferAs$Type$Buffer$RW$$BO$(bb, -1, 0, rem, rem, addr, segment);
}
@Override
@ -93,7 +94,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
0,
length,
length,
byteOffset(index));
byteOffset(index), segment);
}
public $Type$Buffer duplicate() {
@ -102,7 +103,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
this.position(),
this.limit(),
this.capacity(),
address);
address, segment);
}
public $Type$Buffer asReadOnlyBuffer() {
@ -112,7 +113,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
this.position(),
this.limit(),
this.capacity(),
address);
address, segment);
#else[rw]
return duplicate();
#end[rw]
@ -130,12 +131,14 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
}
public $type$ get() {
checkSegment();
$memtype$ x = UNSAFE.get$Memtype$Unaligned(bb.hb, byteOffset(nextGetIndex()),
{#if[boB]?true:false});
return $fromBits$(x);
}
public $type$ get(int i) {
checkSegment();
$memtype$ x = UNSAFE.get$Memtype$Unaligned(bb.hb, byteOffset(checkIndex(i)),
{#if[boB]?true:false});
return $fromBits$(x);
@ -153,6 +156,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
public $Type$Buffer put($type$ x) {
#if[rw]
checkSegment();
$memtype$ y = $toBits$(x);
UNSAFE.put$Memtype$Unaligned(bb.hb, byteOffset(nextPutIndex()), y,
{#if[boB]?true:false});
@ -164,6 +168,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
public $Type$Buffer put(int i, $type$ x) {
#if[rw]
checkSegment();
$memtype$ y = $toBits$(x);
UNSAFE.put$Memtype$Unaligned(bb.hb, byteOffset(checkIndex(i)), y,
{#if[boB]?true:false});
@ -237,7 +242,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
pos + start,
pos + end,
capacity(),
address);
address, segment);
}
#end[char]

View File

@ -33,6 +33,7 @@ class XXX {
private $type$ get$Type$(long a) {
try {
checkSegment();
$memtype$ x = UNSAFE.get$Memtype$Unaligned(null, a, bigEndian);
return $fromBits$(x);
} finally {
@ -61,6 +62,7 @@ class XXX {
private ByteBuffer put$Type$(long a, $type$ x) {
#if[rw]
try {
checkSegment();
$memtype$ y = $toBits$(x);
UNSAFE.put$Memtype$Unaligned(null, a, y, bigEndian);
} finally {
@ -104,13 +106,13 @@ class XXX {
0,
size,
size,
address + off))
address + off, segment))
: ($Type$Buffer)(new ByteBufferAs$Type$Buffer$RW$L(this,
-1,
0,
size,
size,
address + off)));
address + off, segment)));
} else {
return (nativeByteOrder
? ($Type$Buffer)(new Direct$Type$Buffer$RW$U(this,
@ -118,13 +120,13 @@ class XXX {
0,
size,
size,
off))
off, segment))
: ($Type$Buffer)(new Direct$Type$Buffer$RW$S(this,
-1,
0,
size,
size,
off)));
off, segment)));
}
}

View File

@ -30,6 +30,7 @@ package java.nio;
import java.io.FileDescriptor;
import java.lang.ref.Reference;
import java.util.Objects;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.VM;
import jdk.internal.ref.Cleaner;
import sun.nio.ch.DirectBuffer;
@ -112,7 +113,7 @@ class Direct$Type$Buffer$RW$$BO$
//
Direct$Type$Buffer$RW$(int cap) { // package-private
#if[rw]
super(-1, 0, cap, cap);
super(-1, 0, cap, cap, null);
boolean pa = VM.isDirectMemoryPageAligned();
int ps = Bits.pageSize();
long size = Math.max(1L, (long)cap + (pa ? ps : 0));
@ -145,8 +146,8 @@ class Direct$Type$Buffer$RW$$BO$
// Invoked to construct a direct ByteBuffer referring to the block of
// memory. A given arbitrary object may also be attached to the buffer.
//
Direct$Type$Buffer(long addr, int cap, Object ob) {
super(-1, 0, cap, cap);
Direct$Type$Buffer(long addr, int cap, Object ob, MemorySegmentProxy segment) {
super(-1, 0, cap, cap, segment);
address = addr;
cleaner = null;
att = ob;
@ -156,7 +157,7 @@ class Direct$Type$Buffer$RW$$BO$
// Invoked only by JNI: NewDirectByteBuffer(void*, long)
//
private Direct$Type$Buffer(long addr, int cap) {
super(-1, 0, cap, cap);
super(-1, 0, cap, cap, null);
address = addr;
cleaner = null;
att = null;
@ -169,15 +170,15 @@ class Direct$Type$Buffer$RW$$BO$
protected Direct$Type$Buffer$RW$(int cap, long addr,
FileDescriptor fd,
Runnable unmapper,
boolean isSync)
boolean isSync, MemorySegmentProxy segment)
{
#if[rw]
super(-1, 0, cap, cap, fd, isSync);
super(-1, 0, cap, cap, fd, isSync, segment);
address = addr;
cleaner = Cleaner.create(this, unmapper);
att = null;
#else[rw]
super(cap, addr, fd, unmapper, isSync);
super(cap, addr, fd, unmapper, isSync, segment);
this.isReadOnly = true;
#end[rw]
}
@ -188,10 +189,10 @@ class Direct$Type$Buffer$RW$$BO$
//
Direct$Type$Buffer$RW$$BO$(DirectBuffer db, // package-private
int mark, int pos, int lim, int cap,
int off)
int off, MemorySegmentProxy segment)
{
#if[rw]
super(mark, pos, lim, cap);
super(mark, pos, lim, cap, segment);
address = db.address() + off;
#if[byte]
cleaner = null;
@ -199,7 +200,7 @@ class Direct$Type$Buffer$RW$$BO$
Object attachment = db.attachment();
att = (attachment == null ? db : attachment);
#else[rw]
super(db, mark, pos, lim, cap, off);
super(db, mark, pos, lim, cap, off, segment);
this.isReadOnly = true;
#end[rw]
}
@ -216,7 +217,7 @@ class Direct$Type$Buffer$RW$$BO$
int rem = (pos <= lim ? lim - pos : 0);
int off = (pos << $LG_BYTES_PER_VALUE$);
assert (off >= 0);
return new Direct$Type$Buffer$RW$$BO$(this, -1, 0, rem, rem, off);
return new Direct$Type$Buffer$RW$$BO$(this, -1, 0, rem, rem, off, segment);
}
@Override
@ -227,7 +228,7 @@ class Direct$Type$Buffer$RW$$BO$
0,
length,
length,
index);
index, segment);
}
public $Type$Buffer duplicate() {
@ -236,7 +237,7 @@ class Direct$Type$Buffer$RW$$BO$
this.position(),
this.limit(),
this.capacity(),
0);
0, segment);
}
public $Type$Buffer asReadOnlyBuffer() {
@ -246,7 +247,7 @@ class Direct$Type$Buffer$RW$$BO$
this.position(),
this.limit(),
this.capacity(),
0);
0, segment);
#else[rw]
return duplicate();
#end[rw]
@ -264,6 +265,7 @@ class Direct$Type$Buffer$RW$$BO$
public $type$ get() {
try {
checkSegment();
return $fromBits$($swap$(UNSAFE.get$Swaptype$(ix(nextGetIndex()))));
} finally {
Reference.reachabilityFence(this);
@ -272,6 +274,7 @@ class Direct$Type$Buffer$RW$$BO$
public $type$ get(int i) {
try {
checkSegment();
return $fromBits$($swap$(UNSAFE.get$Swaptype$(ix(checkIndex(i)))));
} finally {
Reference.reachabilityFence(this);
@ -290,6 +293,7 @@ class Direct$Type$Buffer$RW$$BO$
public $Type$Buffer get($type$[] dst, int offset, int length) {
#if[rw]
checkSegment();
if (((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_TO_ARRAY_THRESHOLD) {
Objects.checkFromIndexSize(offset, length, dst.length);
int pos = position();
@ -331,6 +335,7 @@ class Direct$Type$Buffer$RW$$BO$
public $Type$Buffer get(int index, $type$[] dst, int offset, int length) {
#if[rw]
checkSegment();
if (((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_TO_ARRAY_THRESHOLD) {
Objects.checkFromIndexSize(index, length, limit());
Objects.checkFromIndexSize(offset, length, dst.length);
@ -368,6 +373,7 @@ class Direct$Type$Buffer$RW$$BO$
public $Type$Buffer put($type$ x) {
#if[rw]
try {
checkSegment();
UNSAFE.put$Swaptype$(ix(nextPutIndex()), $swap$($toBits$(x)));
} finally {
Reference.reachabilityFence(this);
@ -381,6 +387,7 @@ class Direct$Type$Buffer$RW$$BO$
public $Type$Buffer put(int i, $type$ x) {
#if[rw]
try {
checkSegment();
UNSAFE.put$Swaptype$(ix(checkIndex(i)), $swap$($toBits$(x)));
} finally {
Reference.reachabilityFence(this);
@ -393,6 +400,7 @@ class Direct$Type$Buffer$RW$$BO$
public $Type$Buffer put($Type$Buffer src) {
#if[rw]
checkSegment();
if (src instanceof Direct$Type$Buffer$BO$) {
if (src == this)
throw createSameBufferException();
@ -439,6 +447,7 @@ class Direct$Type$Buffer$RW$$BO$
public $Type$Buffer put($type$[] src, int offset, int length) {
#if[rw]
checkSegment();
if (((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_FROM_ARRAY_THRESHOLD) {
Objects.checkFromIndexSize(offset, length, src.length);
int pos = position();
@ -480,6 +489,7 @@ class Direct$Type$Buffer$RW$$BO$
public $Type$Buffer put(int index, $type$[] src, int offset, int length) {
#if[rw]
checkSegment();
if (((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_FROM_ARRAY_THRESHOLD) {
Objects.checkFromIndexSize(index, length, limit());
Objects.checkFromIndexSize(offset, length, src.length);
@ -577,7 +587,7 @@ class Direct$Type$Buffer$RW$$BO$
pos + start,
pos + end,
capacity(),
offset);
offset, segment);
}
#end[char]

View File

@ -28,6 +28,7 @@
package java.nio;
import java.util.Objects;
import jdk.internal.access.foreign.MemorySegmentProxy;
/**
#if[rw]
@ -58,47 +59,47 @@ class Heap$Type$Buffer$RW$
#end[rw]
*/
Heap$Type$Buffer$RW$(int cap, int lim) { // package-private
Heap$Type$Buffer$RW$(int cap, int lim, MemorySegmentProxy segment) { // package-private
#if[rw]
super(-1, 0, lim, cap, new $type$[cap], 0);
super(-1, 0, lim, cap, new $type$[cap], 0, segment);
/*
hb = new $type$[cap];
offset = 0;
*/
this.address = ARRAY_BASE_OFFSET;
#else[rw]
super(cap, lim);
super(cap, lim, segment);
this.isReadOnly = true;
#end[rw]
}
Heap$Type$Buffer$RW$($type$[] buf, int off, int len) { // package-private
Heap$Type$Buffer$RW$($type$[] buf, int off, int len, MemorySegmentProxy segment) { // package-private
#if[rw]
super(-1, off, off + len, buf.length, buf, 0);
super(-1, off, off + len, buf.length, buf, 0, segment);
/*
hb = buf;
offset = 0;
*/
this.address = ARRAY_BASE_OFFSET;
#else[rw]
super(buf, off, len);
super(buf, off, len, segment);
this.isReadOnly = true;
#end[rw]
}
protected Heap$Type$Buffer$RW$($type$[] buf,
int mark, int pos, int lim, int cap,
int off)
int off, MemorySegmentProxy segment)
{
#if[rw]
super(mark, pos, lim, cap, buf, off);
super(mark, pos, lim, cap, buf, off, segment);
/*
hb = buf;
offset = off;
*/
this.address = ARRAY_BASE_OFFSET + off * ARRAY_INDEX_SCALE;
#else[rw]
super(buf, mark, pos, lim, cap, off);
super(buf, mark, pos, lim, cap, off, segment);
this.isReadOnly = true;
#end[rw]
}
@ -110,7 +111,7 @@ class Heap$Type$Buffer$RW$
0,
rem,
rem,
this.position() + offset);
this.position() + offset, segment);
}
@Override
@ -121,7 +122,7 @@ class Heap$Type$Buffer$RW$
0,
length,
length,
index + offset);
index + offset, segment);
}
public $Type$Buffer duplicate() {
@ -130,7 +131,7 @@ class Heap$Type$Buffer$RW$
this.position(),
this.limit(),
this.capacity(),
offset);
offset, segment);
}
public $Type$Buffer asReadOnlyBuffer() {
@ -140,7 +141,7 @@ class Heap$Type$Buffer$RW$
this.position(),
this.limit(),
this.capacity(),
offset);
offset, segment);
#else[rw]
return duplicate();
#end[rw]
@ -159,20 +160,23 @@ class Heap$Type$Buffer$RW$
#end[byte]
public $type$ get() {
checkSegment();
return hb[ix(nextGetIndex())];
}
public $type$ get(int i) {
checkSegment();
return hb[ix(checkIndex(i))];
}
#if[streamableType]
$type$ getUnchecked(int i) {
return hb[ix(i)];
return hb[ix(i)];
}
#end[streamableType]
public $Type$Buffer get($type$[] dst, int offset, int length) {
checkSegment();
Objects.checkFromIndexSize(offset, length, dst.length);
int pos = position();
if (length > limit() - pos)
@ -183,6 +187,7 @@ class Heap$Type$Buffer$RW$
}
public $Type$Buffer get(int index, $type$[] dst, int offset, int length) {
checkSegment();
Objects.checkFromIndexSize(index, length, limit());
Objects.checkFromIndexSize(offset, length, dst.length);
System.arraycopy(hb, ix(index), dst, offset, length);
@ -201,6 +206,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer put($type$ x) {
#if[rw]
checkSegment();
hb[ix(nextPutIndex())] = x;
return this;
#else[rw]
@ -210,6 +216,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer put(int i, $type$ x) {
#if[rw]
checkSegment();
hb[ix(checkIndex(i))] = x;
return this;
#else[rw]
@ -219,6 +226,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer put($type$[] src, int offset, int length) {
#if[rw]
checkSegment();
Objects.checkFromIndexSize(offset, length, src.length);
int pos = position();
if (length > limit() - pos)
@ -233,6 +241,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer put($Type$Buffer src) {
#if[rw]
checkSegment();
if (src instanceof Heap$Type$Buffer) {
if (src == this)
throw createSameBufferException();
@ -264,6 +273,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer put(int index, $type$[] src, int offset, int length) {
#if[rw]
checkSegment();
Objects.checkFromIndexSize(index, length, limit());
Objects.checkFromIndexSize(offset, length, src.length);
System.arraycopy(src, offset, hb, ix(index), length);
@ -276,6 +286,7 @@ class Heap$Type$Buffer$RW$
#if[char]
public $Type$Buffer put(String src, int start, int end) {
checkSegment();
int length = end - start;
Objects.checkFromIndexSize(start, length, src.length());
if (isReadOnly())
@ -327,6 +338,7 @@ class Heap$Type$Buffer$RW$
#if[rw]
public char getChar() {
checkSegment();
return UNSAFE.getCharUnaligned(hb, byteOffset(nextGetIndex(2)), bigEndian);
}
@ -338,6 +350,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putChar(char x) {
#if[rw]
checkSegment();
UNSAFE.putCharUnaligned(hb, byteOffset(nextPutIndex(2)), x, bigEndian);
return this;
#else[rw]
@ -347,6 +360,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putChar(int i, char x) {
#if[rw]
checkSegment();
UNSAFE.putCharUnaligned(hb, byteOffset(checkIndex(i, 2)), x, bigEndian);
return this;
#else[rw]
@ -364,13 +378,13 @@ class Heap$Type$Buffer$RW$
0,
size,
size,
addr))
addr, segment))
: (CharBuffer)(new ByteBufferAsCharBuffer$RW$L(this,
-1,
0,
size,
size,
addr)));
addr, segment)));
}
@ -379,10 +393,12 @@ class Heap$Type$Buffer$RW$
#if[rw]
public short getShort() {
checkSegment();
return UNSAFE.getShortUnaligned(hb, byteOffset(nextGetIndex(2)), bigEndian);
}
public short getShort(int i) {
checkSegment();
return UNSAFE.getShortUnaligned(hb, byteOffset(checkIndex(i, 2)), bigEndian);
}
@ -390,6 +406,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putShort(short x) {
#if[rw]
checkSegment();
UNSAFE.putShortUnaligned(hb, byteOffset(nextPutIndex(2)), x, bigEndian);
return this;
#else[rw]
@ -399,6 +416,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putShort(int i, short x) {
#if[rw]
checkSegment();
UNSAFE.putShortUnaligned(hb, byteOffset(checkIndex(i, 2)), x, bigEndian);
return this;
#else[rw]
@ -416,13 +434,13 @@ class Heap$Type$Buffer$RW$
0,
size,
size,
addr))
addr, segment))
: (ShortBuffer)(new ByteBufferAsShortBuffer$RW$L(this,
-1,
0,
size,
size,
addr)));
addr, segment)));
}
@ -431,10 +449,12 @@ class Heap$Type$Buffer$RW$
#if[rw]
public int getInt() {
checkSegment();
return UNSAFE.getIntUnaligned(hb, byteOffset(nextGetIndex(4)), bigEndian);
}
public int getInt(int i) {
checkSegment();
return UNSAFE.getIntUnaligned(hb, byteOffset(checkIndex(i, 4)), bigEndian);
}
@ -442,6 +462,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putInt(int x) {
#if[rw]
checkSegment();
UNSAFE.putIntUnaligned(hb, byteOffset(nextPutIndex(4)), x, bigEndian);
return this;
#else[rw]
@ -451,6 +472,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putInt(int i, int x) {
#if[rw]
checkSegment();
UNSAFE.putIntUnaligned(hb, byteOffset(checkIndex(i, 4)), x, bigEndian);
return this;
#else[rw]
@ -468,13 +490,13 @@ class Heap$Type$Buffer$RW$
0,
size,
size,
addr))
addr, segment))
: (IntBuffer)(new ByteBufferAsIntBuffer$RW$L(this,
-1,
0,
size,
size,
addr)));
addr, segment)));
}
@ -483,10 +505,12 @@ class Heap$Type$Buffer$RW$
#if[rw]
public long getLong() {
checkSegment();
return UNSAFE.getLongUnaligned(hb, byteOffset(nextGetIndex(8)), bigEndian);
}
public long getLong(int i) {
checkSegment();
return UNSAFE.getLongUnaligned(hb, byteOffset(checkIndex(i, 8)), bigEndian);
}
@ -494,6 +518,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putLong(long x) {
#if[rw]
checkSegment();
UNSAFE.putLongUnaligned(hb, byteOffset(nextPutIndex(8)), x, bigEndian);
return this;
#else[rw]
@ -503,6 +528,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putLong(int i, long x) {
#if[rw]
checkSegment();
UNSAFE.putLongUnaligned(hb, byteOffset(checkIndex(i, 8)), x, bigEndian);
return this;
#else[rw]
@ -520,13 +546,13 @@ class Heap$Type$Buffer$RW$
0,
size,
size,
addr))
addr, segment))
: (LongBuffer)(new ByteBufferAsLongBuffer$RW$L(this,
-1,
0,
size,
size,
addr)));
addr, segment)));
}
@ -535,11 +561,13 @@ class Heap$Type$Buffer$RW$
#if[rw]
public float getFloat() {
checkSegment();
int x = UNSAFE.getIntUnaligned(hb, byteOffset(nextGetIndex(4)), bigEndian);
return Float.intBitsToFloat(x);
}
public float getFloat(int i) {
checkSegment();
int x = UNSAFE.getIntUnaligned(hb, byteOffset(checkIndex(i, 4)), bigEndian);
return Float.intBitsToFloat(x);
}
@ -548,6 +576,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putFloat(float x) {
#if[rw]
checkSegment();
int y = Float.floatToRawIntBits(x);
UNSAFE.putIntUnaligned(hb, byteOffset(nextPutIndex(4)), y, bigEndian);
return this;
@ -558,6 +587,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putFloat(int i, float x) {
#if[rw]
checkSegment();
int y = Float.floatToRawIntBits(x);
UNSAFE.putIntUnaligned(hb, byteOffset(checkIndex(i, 4)), y, bigEndian);
return this;
@ -576,13 +606,13 @@ class Heap$Type$Buffer$RW$
0,
size,
size,
addr))
addr, segment))
: (FloatBuffer)(new ByteBufferAsFloatBuffer$RW$L(this,
-1,
0,
size,
size,
addr)));
addr, segment)));
}
@ -591,11 +621,13 @@ class Heap$Type$Buffer$RW$
#if[rw]
public double getDouble() {
checkSegment();
long x = UNSAFE.getLongUnaligned(hb, byteOffset(nextGetIndex(8)), bigEndian);
return Double.longBitsToDouble(x);
}
public double getDouble(int i) {
checkSegment();
long x = UNSAFE.getLongUnaligned(hb, byteOffset(checkIndex(i, 8)), bigEndian);
return Double.longBitsToDouble(x);
}
@ -604,6 +636,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putDouble(double x) {
#if[rw]
checkSegment();
long y = Double.doubleToRawLongBits(x);
UNSAFE.putLongUnaligned(hb, byteOffset(nextPutIndex(8)), y, bigEndian);
return this;
@ -614,6 +647,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putDouble(int i, double x) {
#if[rw]
checkSegment();
long y = Double.doubleToRawLongBits(x);
UNSAFE.putLongUnaligned(hb, byteOffset(checkIndex(i, 8)), y, bigEndian);
return this;
@ -632,13 +666,13 @@ class Heap$Type$Buffer$RW$
0,
size,
size,
addr))
addr, segment))
: (DoubleBuffer)(new ByteBufferAsDoubleBuffer$RW$L(this,
-1,
0,
size,
size,
addr)));
addr, segment)));
}
@ -666,7 +700,7 @@ class Heap$Type$Buffer$RW$
pos + start,
pos + end,
capacity(),
offset);
offset, segment);
}
#end[char]

View File

@ -28,6 +28,8 @@ package java.nio;
import java.io.FileDescriptor;
import java.lang.ref.Reference;
import java.util.Objects;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.Unsafe;
@ -88,21 +90,21 @@ public abstract class MappedByteBuffer
// This should only be invoked by the DirectByteBuffer constructors
//
MappedByteBuffer(int mark, int pos, int lim, int cap, // package-private
FileDescriptor fd, boolean isSync) {
super(mark, pos, lim, cap);
FileDescriptor fd, boolean isSync, MemorySegmentProxy segment) {
super(mark, pos, lim, cap, segment);
this.fd = fd;
this.isSync = isSync;
}
MappedByteBuffer(int mark, int pos, int lim, int cap, // package-private
boolean isSync) {
super(mark, pos, lim, cap);
boolean isSync, MemorySegmentProxy segment) {
super(mark, pos, lim, cap, segment);
this.fd = null;
this.isSync = isSync;
}
MappedByteBuffer(int mark, int pos, int lim, int cap) { // package-private
super(mark, pos, lim, cap);
MappedByteBuffer(int mark, int pos, int lim, int cap, MemorySegmentProxy segment) { // package-private
super(mark, pos, lim, cap, segment);
this.fd = null;
this.isSync = false;
}

View File

@ -35,7 +35,7 @@ class StringCharBuffer // package-private
CharSequence str;
StringCharBuffer(CharSequence s, int start, int end) { // package-private
super(-1, start, end, s.length());
super(-1, start, end, s.length(), null);
int n = s.length();
Objects.checkFromToIndex(start, end, n);
str = s;
@ -68,7 +68,7 @@ class StringCharBuffer // package-private
int limit,
int cap,
int offset) {
super(mark, pos, limit, cap, null, offset);
super(mark, pos, limit, cap, null, offset, null);
str = s;
this.isReadOnly = true;
}

View File

@ -37,6 +37,7 @@ import java.util.stream.$Streamtype$Stream;
#end[streamableType]
import java.util.Objects;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.util.ArraysSupport;
/**
@ -279,17 +280,25 @@ public abstract class $Type$Buffer
// backing array, and array offset
//
$Type$Buffer(int mark, int pos, int lim, int cap, // package-private
$type$[] hb, int offset)
$type$[] hb, int offset, MemorySegmentProxy segment)
{
super(mark, pos, lim, cap);
super(mark, pos, lim, cap, segment);
this.hb = hb;
this.offset = offset;
}
// Creates a new buffer with the given mark, position, limit, and capacity
//
$Type$Buffer(int mark, int pos, int lim, int cap) { // package-private
this(mark, pos, lim, cap, null, 0);
$Type$Buffer(int mark, int pos, int lim, int cap, MemorySegmentProxy segment) { // package-private
this(mark, pos, lim, cap, null, 0, segment);
}
// Creates a new buffer with given base, address and capacity
//
$Type$Buffer($type$[] hb, long addr, int cap, MemorySegmentProxy segment) { // package-private
super(addr, cap, segment);
this.hb = hb;
this.offset = 0;
}
@Override
@ -348,7 +357,7 @@ public abstract class $Type$Buffer
public static $Type$Buffer allocate(int capacity) {
if (capacity < 0)
throw createCapacityException(capacity);
return new Heap$Type$Buffer(capacity, capacity);
return new Heap$Type$Buffer(capacity, capacity, null);
}
/**
@ -393,7 +402,7 @@ public abstract class $Type$Buffer
int offset, int length)
{
try {
return new Heap$Type$Buffer(array, offset, length);
return new Heap$Type$Buffer(array, offset, length, null);
} catch (IllegalArgumentException x) {
throw new IndexOutOfBoundsException();
}

View File

@ -26,6 +26,8 @@
package jdk.internal.access;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
import java.util.Map;
public interface JavaLangInvokeAccess {
@ -106,4 +108,41 @@ public interface JavaLangInvokeAccess {
MethodType[] invokerMethodTypes,
MethodType[] callSiteMethodTypes);
/**
* Returns a var handle view of a given memory address.
* Used by {@code jdk.internal.foreign.LayoutPath} and
* {@code jdk.incubator.foreign.MemoryHandles}.
*/
VarHandle memoryAddressViewVarHandle(Class<?> carrier, long alignmentMask,
ByteOrder order, long offset, long[] strides);
/**
* Returns the carrier associated with a memory access var handle.
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
Class<?> memoryAddressCarrier(VarHandle handle);
/**
* Returns the alignment mask associated with a memory access var handle.
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
long memoryAddressAlignmentMask(VarHandle handle);
/**
* Returns the byte order associated with a memory access var handle.
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
ByteOrder memoryAddressByteOrder(VarHandle handle);
/**
* Returns the offset associated with a memory access var handle.
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
long memoryAddressOffset(VarHandle handle);
/**
* Returns the strides associated with a memory access var handle.
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
long[] memoryAddressStrides(VarHandle handle);
}

View File

@ -25,6 +25,8 @@
package jdk.internal.access;
import jdk.internal.access.foreign.MemorySegmentProxy;
import java.nio.Buffer;
import java.nio.ByteBuffer;
@ -39,4 +41,34 @@ public interface JavaNioAccess {
long getMemoryUsed();
}
BufferPool getDirectBufferPool();
/**
* Constructs a direct ByteBuffer referring to the block of memory starting
* at the given memory address and extending {@code cap} bytes.
* The {@code ob} parameter is an arbitrary object that is attached
* to the resulting buffer.
* Used by {@code jdk.internal.foreignMemorySegmentImpl}.
*/
ByteBuffer newDirectByteBuffer(long addr, int cap, Object obj, MemorySegmentProxy segment);
/**
* Constructs an heap ByteBuffer with given backing array, offset, capacity and segment.
* Used by {@code jdk.internal.foreignMemorySegmentImpl}.
*/
ByteBuffer newHeapByteBuffer(byte[] hb, int offset, int capacity, MemorySegmentProxy segment);
/**
* Used by {@code jdk.internal.foreign.Utils}.
*/
Object getBufferBase(ByteBuffer bb);
/**
* Used by {@code jdk.internal.foreign.Utils}.
*/
long getBufferAddress(ByteBuffer bb);
/**
* Used by byte buffer var handle views.
*/
void checkSegment(Buffer buffer);
}

View File

@ -0,0 +1,42 @@
/*
* 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. 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.access.foreign;
/**
* This proxy interface is required to allow instances of the {@code MemoryAddress} interface (which is defined inside
* an incubating module) to be accessed from the memory access var handles.
*/
public interface MemoryAddressProxy {
/**
* Check that memory access is within spatial and temporal bounds.
* @throws IllegalStateException if underlying segment has been closed already.
* @throws IndexOutOfBoundsException if access is out-of-bounds.
*/
void checkAccess(long offset, long length, boolean readOnly);
long unsafeGetOffset();
Object unsafeGetBase();
}

View File

@ -0,0 +1,35 @@
/*
* 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. 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.access.foreign;
/**
* This proxy interface is required to allow instances of the {@code MemorySegment} interface (which is defined inside
* an incubating module) to be accessed from the memory access var handles.
*/
public interface MemorySegmentProxy {
void checkValidState();
}

View File

@ -0,0 +1,36 @@
/*
* 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. 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.access.foreign;
/**
* This proxy interface is required to allow instances of the {@code FileChannelImpl.Unmapper} interface (which is a non-public class
* inside the {@code sun.nio.ch} package) to be accessed from the mapped memory segment factory.
*/
public interface UnmapperProxy {
long address();
void unmap();
}

View File

@ -134,7 +134,8 @@ module java.base {
// see make/gensrc/GenModuleInfo.gmk
exports sun.invoke.util to
jdk.compiler;
jdk.compiler,
jdk.incubator.foreign;
exports com.sun.security.ntlm to
java.security.sasl;
exports jdk.internal to
@ -149,7 +150,10 @@ module java.base {
java.naming,
java.rmi,
jdk.jlink,
jdk.net;
jdk.net,
jdk.incubator.foreign;
exports jdk.internal.access.foreign to
jdk.incubator.foreign;
exports jdk.internal.event to
jdk.jfr;
exports jdk.internal.jimage to
@ -202,7 +206,8 @@ module java.base {
jdk.scripting.nashorn,
jdk.scripting.nashorn.shell,
jdk.unsupported,
jdk.internal.vm.ci;
jdk.internal.vm.ci,
jdk.incubator.foreign;
exports jdk.internal.module to
java.instrument,
java.management.rmi,
@ -260,7 +265,8 @@ module java.base {
java.management,
jdk.crypto.cryptoki,
jdk.net,
jdk.sctp;
jdk.sctp,
jdk.incubator.foreign;
exports sun.nio.cs to
jdk.charsets;
exports sun.reflect.annotation to
@ -276,7 +282,8 @@ module java.base {
java.sql.rowset;
exports sun.security.action to
java.desktop,
java.security.jgss;
java.security.jgss,
jdk.incubator.foreign;
exports sun.security.internal.interfaces to
jdk.crypto.cryptoki;
exports sun.security.internal.spec to

View File

@ -42,6 +42,7 @@ import java.nio.channels.NonWritableChannelException;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.WritableByteChannel;
import java.util.Objects;
import jdk.internal.access.JavaIOFileDescriptorAccess;
import jdk.internal.access.JavaNioAccess;
@ -51,6 +52,8 @@ import jdk.internal.misc.Unsafe;
import jdk.internal.ref.Cleaner;
import jdk.internal.ref.CleanerFactory;
import jdk.internal.access.foreign.UnmapperProxy;
public class FileChannelImpl
extends FileChannel
{
@ -863,27 +866,39 @@ public class FileChannelImpl
// -- Memory-mapped buffers --
private static abstract class Unmapper
implements Runnable
implements Runnable, UnmapperProxy
{
// may be required to close file
private static final NativeDispatcher nd = new FileDispatcherImpl();
private volatile long address;
protected final long size;
protected final int cap;
protected final long cap;
private final FileDescriptor fd;
private final int pagePosition;
private Unmapper(long address, long size, int cap,
FileDescriptor fd)
private Unmapper(long address, long size, long cap,
FileDescriptor fd, int pagePosition)
{
assert (address != 0);
this.address = address;
this.size = size;
this.cap = cap;
this.fd = fd;
this.pagePosition = pagePosition;
}
@Override
public long address() {
return address;
}
@Override
public void run() {
unmap();
}
public void unmap() {
if (address == 0)
return;
unmap0(address, size);
@ -911,9 +926,9 @@ public class FileChannelImpl
static volatile long totalSize;
static volatile long totalCapacity;
public DefaultUnmapper(long address, long size, int cap,
FileDescriptor fd) {
super(address, size, cap, fd);
public DefaultUnmapper(long address, long size, long cap,
FileDescriptor fd, int pagePosition) {
super(address, size, cap, fd, pagePosition);
incrementStats();
}
@ -940,9 +955,9 @@ public class FileChannelImpl
static volatile long totalSize;
static volatile long totalCapacity;
public SyncUnmapper(long address, long size, int cap,
FileDescriptor fd) {
super(address, size, cap, fd);
public SyncUnmapper(long address, long size, long cap,
FileDescriptor fd, int pagePosition) {
super(address, size, cap, fd, pagePosition);
incrementStats();
}
@ -968,11 +983,44 @@ public class FileChannelImpl
cl.clean();
}
private static final int MAP_INVALID = -1;
private static final int MAP_RO = 0;
private static final int MAP_RW = 1;
private static final int MAP_PV = 2;
public MappedByteBuffer map(MapMode mode, long position, long size)
public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException {
if (size > Integer.MAX_VALUE)
throw new IllegalArgumentException("Size exceeds Integer.MAX_VALUE");
boolean isSync = isSync(Objects.requireNonNull(mode, "Mode is null"));
int prot = toProt(mode);
Unmapper unmapper = mapInternal(mode, position, size, prot, isSync);
if (unmapper == null) {
// a valid file descriptor is not required
FileDescriptor dummy = new FileDescriptor();
if ((!writable) || (prot == MAP_RO))
return Util.newMappedByteBufferR(0, 0, dummy, null, isSync);
else
return Util.newMappedByteBuffer(0, 0, dummy, null, isSync);
} else if ((!writable) || (prot == MAP_RO)) {
return Util.newMappedByteBufferR((int)unmapper.cap,
unmapper.address + unmapper.pagePosition,
unmapper.fd,
unmapper, isSync);
} else {
return Util.newMappedByteBuffer((int)unmapper.cap,
unmapper.address + unmapper.pagePosition,
unmapper.fd,
unmapper, isSync);
}
}
public Unmapper mapInternal(MapMode mode, long position, long size) throws IOException {
boolean isSync = isSync(Objects.requireNonNull(mode, "Mode is null"));
int prot = toProt(mode);
return mapInternal(mode, position, size, prot, isSync);
}
private Unmapper mapInternal(MapMode mode, long position, long size, int prot, boolean isSync)
throws IOException
{
ensureOpen();
@ -984,35 +1032,8 @@ public class FileChannelImpl
throw new IllegalArgumentException("Negative size");
if (position + size < 0)
throw new IllegalArgumentException("Position + size overflow");
if (size > Integer.MAX_VALUE)
throw new IllegalArgumentException("Size exceeds Integer.MAX_VALUE");
int imode;
boolean isSync = false;
if (mode == MapMode.READ_ONLY)
imode = MAP_RO;
else if (mode == MapMode.READ_WRITE)
imode = MAP_RW;
else if (mode == MapMode.PRIVATE)
imode = MAP_PV;
else if (mode == ExtendedMapMode.READ_ONLY_SYNC) {
imode = MAP_RO;
isSync = true;
} else if (mode == ExtendedMapMode.READ_WRITE_SYNC) {
imode = MAP_RW;
isSync = true;
} else {
throw new UnsupportedOperationException();
}
if ((mode != MapMode.READ_ONLY) && mode != ExtendedMapMode.READ_ONLY_SYNC && !writable)
throw new NonWritableChannelException();
if (!readable)
throw new NonReadableChannelException();
// reject SYNC request if writeback is not enabled for this platform
if (isSync && !Unsafe.isWritebackEnabled()) {
throw new UnsupportedOperationException();
}
checkMode(mode, prot, isSync);
long addr = -1;
int ti = -1;
try {
@ -1045,13 +1066,7 @@ public class FileChannelImpl
}
if (size == 0) {
addr = 0;
// a valid file descriptor is not required
FileDescriptor dummy = new FileDescriptor();
if ((!writable) || (imode == MAP_RO))
return Util.newMappedByteBufferR(0, 0, dummy, null, isSync);
else
return Util.newMappedByteBuffer(0, 0, dummy, null, isSync);
return null;
}
pagePosition = (int)(position % allocationGranularity);
@ -1059,7 +1074,7 @@ public class FileChannelImpl
mapSize = size + pagePosition;
try {
// If map0 did not throw an exception, the address is valid
addr = map0(imode, mapPosition, mapSize, isSync);
addr = map0(prot, mapPosition, mapSize, isSync);
} catch (OutOfMemoryError x) {
// An OutOfMemoryError may indicate that we've exhausted
// memory so force gc and re-attempt map
@ -1070,7 +1085,7 @@ public class FileChannelImpl
Thread.currentThread().interrupt();
}
try {
addr = map0(imode, mapPosition, mapSize, isSync);
addr = map0(prot, mapPosition, mapSize, isSync);
} catch (OutOfMemoryError y) {
// After a second OOME, fail
throw new IOException("Map failed", y);
@ -1090,29 +1105,53 @@ public class FileChannelImpl
assert (IOStatus.checkAll(addr));
assert (addr % allocationGranularity == 0);
int isize = (int)size;
Unmapper um = (isSync
? new SyncUnmapper(addr, mapSize, isize, mfd)
: new DefaultUnmapper(addr, mapSize, isize, mfd));
if ((!writable) || (imode == MAP_RO)) {
return Util.newMappedByteBufferR(isize,
addr + pagePosition,
mfd,
um,
isSync);
} else {
return Util.newMappedByteBuffer(isize,
addr + pagePosition,
mfd,
um,
isSync);
}
? new SyncUnmapper(addr, mapSize, size, mfd, pagePosition)
: new DefaultUnmapper(addr, mapSize, size, mfd, pagePosition));
return um;
} finally {
threads.remove(ti);
endBlocking(IOStatus.checkAll(addr));
}
}
private boolean isSync(MapMode mode) {
return mode == ExtendedMapMode.READ_ONLY_SYNC ||
mode == ExtendedMapMode.READ_WRITE_SYNC;
}
private int toProt(MapMode mode) {
int prot;
if (mode == MapMode.READ_ONLY) {
prot = MAP_RO;
} else if (mode == MapMode.READ_WRITE) {
prot = MAP_RW;
} else if (mode == MapMode.PRIVATE) {
prot = MAP_PV;
} else if (mode == ExtendedMapMode.READ_ONLY_SYNC) {
prot = MAP_RO;
} else if (mode == ExtendedMapMode.READ_WRITE_SYNC) {
prot = MAP_RW;
} else {
prot = MAP_INVALID;
}
return prot;
}
private void checkMode(MapMode mode, int prot, boolean isSync) {
if (prot == MAP_INVALID) {
throw new UnsupportedOperationException();
}
if ((mode != MapMode.READ_ONLY) && mode != ExtendedMapMode.READ_ONLY_SYNC && !writable)
throw new NonWritableChannelException();
if (!readable)
throw new NonReadableChannelException();
// reject SYNC request if writeback is not enabled for this platform
if (isSync && !Unsafe.isWritebackEnabled()) {
throw new UnsupportedOperationException();
}
}
/**
* Invoked by sun.management.ManagementFactoryHelper to create the management
* interface for mapped buffers.

View File

@ -37,6 +37,7 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.TerminatingThreadLocal;
import jdk.internal.misc.Unsafe;
import sun.security.action.GetPropertyAction;
@ -416,7 +417,7 @@ public class Util {
long.class,
FileDescriptor.class,
Runnable.class,
boolean.class });
boolean.class, MemorySegmentProxy.class});
ctor.setAccessible(true);
directByteBufferConstructor = ctor;
} catch (ClassNotFoundException |
@ -443,7 +444,7 @@ public class Util {
addr,
fd,
unmapper,
isSync});
isSync, null});
} catch (InstantiationException |
IllegalAccessException |
InvocationTargetException e) {
@ -464,7 +465,7 @@ public class Util {
long.class,
FileDescriptor.class,
Runnable.class,
boolean.class });
boolean.class, MemorySegmentProxy.class });
ctor.setAccessible(true);
directByteBufferRConstructor = ctor;
} catch (ClassNotFoundException |
@ -491,7 +492,7 @@ public class Util {
addr,
fd,
unmapper,
isSync});
isSync, null});
} catch (InstantiationException |
IllegalAccessException |
InvocationTargetException e) {

View File

@ -0,0 +1,178 @@
/*
* 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. 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 java.lang.constant.ClassDesc;
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.Objects;
import java.util.Optional;
import java.util.OptionalLong;
abstract class AbstractLayout implements MemoryLayout {
private final OptionalLong size;
final long alignment;
private final Optional<String> name;
public AbstractLayout(OptionalLong size, long alignment, Optional<String> name) {
this.size = size;
this.alignment = alignment;
this.name = name;
}
Optional<String> optName() {
return name;
}
@Override
public AbstractLayout withName(String name) {
return dup(alignment, Optional.of(name));
}
@Override
public final Optional<String> name() {
return name;
}
abstract AbstractLayout dup(long alignment, Optional<String> name);
@Override
public AbstractLayout withBitAlignment(long alignmentBits) {
checkAlignment(alignmentBits);
return dup(alignmentBits, name);
}
void checkAlignment(long alignmentBitCount) {
if (((alignmentBitCount & (alignmentBitCount - 1)) != 0L) || //alignment must be a power of two
(alignmentBitCount < 8)) { //alignment must be greater than 8
throw new IllegalArgumentException("Invalid alignment: " + alignmentBitCount);
}
}
static void checkSize(long size) {
checkSize(size, false);
}
static void checkSize(long size, boolean includeZero) {
if (size < 0 || (!includeZero && size == 0)) {
throw new IllegalArgumentException("Invalid size for layout: " + size);
}
}
@Override
public final long bitAlignment() {
return alignment;
}
@Override
public long bitSize() {
return size.orElseThrow(this::badSizeException);
}
static OptionalLong optSize(MemoryLayout layout) {
return ((AbstractLayout)layout).size;
}
private UnsupportedOperationException badSizeException() {
return new UnsupportedOperationException("Cannot compute size of a layout which is, or depends on a sequence layout with unspecified size");
}
String decorateLayoutString(String s) {
if (name.isPresent()) {
s = String.format("%s(%s)", s, name.get());
}
if (!hasNaturalAlignment()) {
s = alignment + "%" + s;
}
return s;
}
boolean hasNaturalAlignment() {
return size.isPresent() && size.getAsLong() == alignment;
}
@Override
public int hashCode() {
return name.hashCode() << Long.hashCode(alignment);
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof AbstractLayout)) {
return false;
}
return Objects.equals(name, ((AbstractLayout)other).name) &&
Objects.equals(alignment, ((AbstractLayout)other).alignment);
}
/*** Helper constants for implementing Layout::describeConstable ***/
public static final DirectMethodHandleDesc BSM_GET_STATIC_FINAL
= ConstantDescs.ofConstantBootstrap(ConstantDescs.CD_ConstantBootstraps, "getStaticFinal",
ConstantDescs.CD_Object, ConstantDescs.CD_Class);
static final ClassDesc CD_LAYOUT = MemoryLayout.class.describeConstable().get();
static final ClassDesc CD_VALUE_LAYOUT = ValueLayout.class.describeConstable().get();
static final ClassDesc CD_SEQUENCE_LAYOUT = SequenceLayout.class.describeConstable().get();
static final ClassDesc CD_GROUP_LAYOUT = GroupLayout.class.describeConstable().get();
static final ClassDesc CD_BYTEORDER = ByteOrder.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);
static final MethodHandleDesc MH_PADDING = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_LAYOUT, "ofPaddingBits",
MethodTypeDesc.of(CD_LAYOUT, ConstantDescs.CD_long));
static final MethodHandleDesc MH_VALUE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_LAYOUT, "ofValueBits",
MethodTypeDesc.of(CD_VALUE_LAYOUT, ConstantDescs.CD_long, CD_BYTEORDER));
static final MethodHandleDesc MH_SIZED_SEQUENCE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_LAYOUT, "ofSequence",
MethodTypeDesc.of(CD_SEQUENCE_LAYOUT, ConstantDescs.CD_long, CD_LAYOUT));
static final MethodHandleDesc MH_UNSIZED_SEQUENCE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_LAYOUT, "ofSequence",
MethodTypeDesc.of(CD_SEQUENCE_LAYOUT, CD_LAYOUT));
static final MethodHandleDesc MH_STRUCT = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_LAYOUT, "ofStruct",
MethodTypeDesc.of(CD_GROUP_LAYOUT, CD_LAYOUT.arrayType()));
static final MethodHandleDesc MH_UNION = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_LAYOUT, "ofUnion",
MethodTypeDesc.of(CD_GROUP_LAYOUT, CD_LAYOUT.arrayType()));
}

View File

@ -0,0 +1,210 @@
/*
* 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. 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 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.Objects;
import java.util.Optional;
import java.util.OptionalLong;
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
* 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#ofStruct(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#ofUnion(MemoryLayout...)}).
*
* <p>
* This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
* class; use of identity-sensitive operations (including reference equality
* ({@code ==}), identity hash code, or synchronization) on instances of
* {@code GroupLayout} may have unpredictable results and should be avoided.
* The {@code equals} method should be used for comparisons.
*
* @implSpec
* This class is immutable and thread-safe.
*/
public final class GroupLayout extends AbstractLayout {
/**
* The group kind.
*/
enum Kind {
/**
* A 'struct' kind.
*/
STRUCT("", MH_STRUCT, Long::sum),
/**
* A 'union' kind.
*/
UNION("|", MH_UNION, Math::max);
final String delimTag;
final MethodHandleDesc mhDesc;
final LongBinaryOperator sizeOp;
Kind(String delimTag, MethodHandleDesc mhDesc, LongBinaryOperator sizeOp) {
this.delimTag = delimTag;
this.mhDesc = mhDesc;
this.sizeOp = sizeOp;
}
OptionalLong sizeof(List<MemoryLayout> elems) {
long size = 0;
for (MemoryLayout elem : elems) {
if (AbstractLayout.optSize(elem).isPresent()) {
size = sizeOp.applyAsLong(size, elem.bitSize());
} else {
return OptionalLong.empty();
}
}
return OptionalLong.of(size);
}
long alignof(List<MemoryLayout> elems) {
return elems.stream().mapToLong(MemoryLayout::bitAlignment).max() // max alignment in case we have member layouts
.orElse(1); // or minimal alignment if no member layout is given
}
}
private final Kind kind;
private final List<MemoryLayout> elements;
GroupLayout(Kind kind, List<MemoryLayout> elements) {
this(kind, elements, kind.alignof(elements), Optional.empty());
}
GroupLayout(Kind kind, List<MemoryLayout> elements, long alignment, Optional<String> name) {
super(kind.sizeof(elements), alignment, name);
this.kind = kind;
this.elements = elements;
}
/**
* Returns the member layouts associated with this group.
*
* @apiNote the order in which member layouts are returned is the same order in which member layouts have
* been passed to one of the group layout factory methods (see {@link MemoryLayout#ofStruct(MemoryLayout...)},
* {@link MemoryLayout#ofUnion(MemoryLayout...)}).
*
* @return the member layouts associated with this group.
*/
public List<MemoryLayout> memberLayouts() {
return Collections.unmodifiableList(elements);
}
@Override
public String toString() {
return decorateLayoutString(elements.stream()
.map(Object::toString)
.collect(Collectors.joining(kind.delimTag, "[", "]")));
}
/**
* Is this group layout a <em>struct</em>?
*
* @return true, if this group layout is a <em>struct</em>.
*/
public boolean isStruct() {
return kind == Kind.STRUCT;
}
/**
* Is this group layout a <em>union</em>?
*
* @return true, if this group layout is a <em>union</em>.
*/
public boolean isUnion() {
return kind == Kind.UNION;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!super.equals(other)) {
return false;
}
if (!(other instanceof GroupLayout)) {
return false;
}
GroupLayout g = (GroupLayout)other;
return kind.equals(g.kind) && elements.equals(g.elements);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), kind, elements);
}
@Override
GroupLayout dup(long alignment, Optional<String> name) {
return new GroupLayout(kind, elements, alignment, name);
}
@Override
boolean hasNaturalAlignment() {
return alignment == kind.alignof(elements);
}
@Override
public Optional<DynamicConstantDesc<GroupLayout>> describeConstable() {
ConstantDesc[] constants = new ConstantDesc[1 + elements.size()];
constants[0] = kind.mhDesc;
for (int i = 0 ; i < elements.size() ; i++) {
constants[i + 1] = elements.get(i).describeConstable().get();
}
return Optional.of(DynamicConstantDesc.ofNamed(
ConstantDescs.BSM_INVOKE, kind.name().toLowerCase(),
CD_GROUP_LAYOUT, constants));
}
//hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
//but that causes issues with javadoc, see JDK-8224052
/**
* {@inheritDoc}
*/
@Override
public GroupLayout withName(String name) {
return (GroupLayout)super.withName(name);
}
/**
* {@inheritDoc}
*/
@Override
public GroupLayout withBitAlignment(long alignmentBits) {
return (GroupLayout)super.withBitAlignment(alignmentBits);
}
}

View File

@ -0,0 +1,111 @@
/*
* 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. 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.MemoryAddressImpl;
/**
* A memory address encodes an offset within a given {@link MemorySegment}. Memory addresses are typically obtained
* using the {@link MemorySegment#baseAddress()} method; such addresses can then be adjusted as required,
* using {@link MemoryAddress#offset(long)}.
* <p>
* A memory address is typically used as the first argument in a memory access var handle call, to perform some operation
* on the underlying memory backing a given memory segment. Since a memory address is always associated with a memory segment,
* such access operations are always subject to spatial and temporal checks as enforced by the address' owning memory region.
* <p>
* All implementations of this interface must be <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>;
* use of identity-sensitive operations (including reference equality ({@code ==}), identity hash code, or synchronization) on
* instances of {@code MemoryAddress} may have unpredictable results and should be avoided. The {@code equals} method should
* be used for comparisons.
* <p>
* Non-platform classes should not implement {@linkplain MemoryAddress} directly.
*
* @apiNote In the future, if the Java language permits, {@link MemoryAddress}
* may become a {@code sealed} interface, which would prohibit subclassing except by
* explicitly permitted types.
*
* @implSpec
* Implementations of this interface are immutable and thread-safe.
*/
public interface MemoryAddress {
/**
* Creates a new memory address with given offset (in bytes) from current one.
* @param l specified offset (in bytes), relative to this address, which should be used to create the new address.
* @return a new memory address with given offset from current one.
*/
MemoryAddress offset(long l);
/**
* The offset of this memory address into the underlying segment.
*
* @return the offset
*/
long offset();
/**
* The memory segment this address belongs to.
* @return The memory segment this address belongs to.
*/
MemorySegment segment();
/**
* Compares the specified object with this address for equality. Returns {@code true} if and only if the specified
* object is also a address, and it is equal to this address.
*
* @param that the object to be compared for equality with this address.
* @return {@code true} if the specified object is equal to this address.
*/
@Override
boolean equals(Object that);
/**
* Returns the hash code value for this address.
* @return the hash code value for this address.
*/
@Override
int hashCode();
/**
* Perform bulk copy from source address to target address. More specifically, the bytes at addresses {@code src}
* through {@code src.offset(bytes - 1)} are copied into addresses {@code dst} through {@code dst.offset(bytes - 1)}.
* If the source and address ranges overlap, then the copying is performed as if the bytes at addresses {@code src}
* through {@code src.offset(bytes - 1)} were first copied into a temporary segment with size {@code bytes},
* and then the contents of the temporary segment were copied into the bytes at addresses {@code dst} through {@code dst.offset(bytes - 1)}.
* @param src the source address.
* @param dst the target address.
* @param bytes the number of bytes to be copied.
* @throws IndexOutOfBoundsException if {@code bytes < 0}, or if it is greater than the size of the segments
* associated with either {@code src} or {@code dst}.
* @throws IllegalStateException if either the source address or the target address belong to memory segments
* which have been already closed, or if access occurs from a thread other than the thread owning either segment.
* @throws UnsupportedOperationException if {@code dst} is associated with a read-only segment (see {@link MemorySegment#isReadOnly()}).
*/
static void copy(MemoryAddress src, MemoryAddress dst, long bytes) {
MemoryAddressImpl.copy((MemoryAddressImpl)src, (MemoryAddressImpl)dst, bytes);
}
}

View File

@ -0,0 +1,268 @@
/*
* 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. 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.access.JavaLangInvokeAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.foreign.Utils;
import sun.invoke.util.Wrapper;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
/**
* 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 to a memory access var handle. The resulting memory access var handle can then be combined in various ways
* to emulate different addressing modes. The var handles created by this class feature a <em>mandatory</em> coordinate type
* (of type {@link MemoryAddress}), and zero or more {@code long} coordinate types, which can be used to emulate
* multi-dimensional array indexing.
* <p>
* As an example, consider the memory layout expressed by a {@link SequenceLayout} instance constructed as follows:
* <blockquote><pre>{@code
SequenceLayout seq = MemoryLayout.ofSequence(5,
MemoryLayout.ofStruct(
MemoryLayout.ofPaddingBits(32),
MemoryLayout.ofValueBits(32, 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); //(MemoryAddress) -> int
handle = MemoryHandles.withOffset(handle, 4); //(MemoryAddress) -> int
handle = MemoryHandles.withStride(handle, 8); //(MemoryAddress, long) -> int
* }</pre></blockquote>
*
* <h2>Addressing mode</h2>
*
* The final memory location accessed by a memory access var handle can be computed as follows:
*
* <blockquote><pre>{@code
address = base + offset
* }</pre></blockquote>
*
* where {@code base} denotes the address expressed by the {@link MemoryAddress} access coordinate, and {@code offset}
* can be expressed in the following form:
*
* <blockquote><pre>{@code
offset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
* }</pre></blockquote>
*
* where {@code x_1}, {@code x_2}, ... {@code x_n} are <em>dynamic</em> values provided as optional {@code long}
* access coordinates, whereas {@code c_1}, {@code c_2}, ... {@code c_m} and {@code s_0}, {@code s_1}, ... {@code s_n} are
* <em>static</em> constants which are can be acquired through the {@link MemoryHandles#withOffset(VarHandle, long)}
* and the {@link MemoryHandles#withStride(VarHandle, long)} combinators, respectively.
*
* <h2><a id="memaccess-mode"></a>Alignment and access modes</h2>
*
* A memory access var handle is associated with an access size {@code S} and an alignment constraint {@code B}
* (both expressed in bytes). We say that a memory access operation is <em>fully aligned</em> if it occurs
* at a memory address {@code A} which is compatible with both alignment constraints {@code S} and {@code B}.
* If access is fully aligned then following access modes are supported and are
* guaranteed to support atomic access:
* <ul>
* <li>read write access modes for all {@code T}, with the exception of
* 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}.
* (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}.
* (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}.
* (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
* update access modes compare values using their bitwise representation
* (see {@link Float#floatToRawIntBits} and
* {@link Double#doubleToRawLongBits}, 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
* {@code get} and {@code set} access modes will result in an {@code IllegalStateException}. If access is partially aligned,
* atomic access is only guaranteed with respect to the largest power of two that divides the GCD of {@code A} and {@code S}.
* <p>
* Finally, in all other cases, we say that a memory access operation is <em>misaligned</em>; in such cases an
* {@code IllegalStateException} is thrown, irrespective of the access mode being used.
*/
public final class MemoryHandles {
private final static JavaLangInvokeAccess JLI = SharedSecrets.getJavaLangInvokeAccess();
private MemoryHandles() {
//sorry, just the one!
}
/**
* Creates a memory access var handle with the given carrier type and byte order.
*
* The resulting memory access var handle features a single {@link MemoryAddress} access coordinate,
* and its variable type is set by the given carrier type.
*
* The alignment constraint for the resulting memory access var handle is the same as the in memory size of the
* carrier type, and the accessed offset is set at zero.
*
* @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) {
checkCarrier(carrier);
return varHandle(carrier,
carrierSize(carrier),
byteOrder);
}
/**
* Creates a memory access var handle with the given carrier type, alignment constraint, and byte order.
*
* The resulting memory access var handle features a single {@link MemoryAddress} access coordinate,
* and its variable type is set by the given carrier type.
*
* The accessed offset is zero.
*
* @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.
* @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) {
checkCarrier(carrier);
if (alignmentBytes <= 0
|| (alignmentBytes & (alignmentBytes - 1)) != 0) { // is power of 2?
throw new IllegalArgumentException("Bad alignment: " + alignmentBytes);
}
return JLI.memoryAddressViewVarHandle(carrier, alignmentBytes - 1, byteOrder, 0, new long[]{});
}
/**
* Creates a memory access var handle with a fixed offset added to the accessed offset. That is,
* if the target memory access var handle accesses a memory location at offset <em>O</em>, the new memory access var
* handle will access a memory location at offset <em>O' + O</em>.
*
* The resulting memory access var handle will feature the same access coordinates as the ones in the target memory access var handle.
*
* @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 target the target memory access handle to access after the offset adjustment.
* @param bytesOffset the offset, in bytes. Must be positive or zero.
* @return the new memory access var handle.
* @throws IllegalArgumentException when the target var handle is not a memory access var handle,
* or when {@code bytesOffset < 0}, or otherwise incompatible with the alignment constraint.
*/
public static VarHandle withOffset(VarHandle target, long bytesOffset) {
if (bytesOffset < 0) {
throw new IllegalArgumentException("Illegal offset: " + bytesOffset);
}
long alignMask = JLI.memoryAddressAlignmentMask(target);
if ((bytesOffset & alignMask) != 0) {
throw new IllegalArgumentException("Offset " + bytesOffset + " does not conform to alignment " + (alignMask + 1));
}
return JLI.memoryAddressViewVarHandle(
JLI.memoryAddressCarrier(target),
alignMask,
JLI.memoryAddressByteOrder(target),
JLI.memoryAddressOffset(target) + bytesOffset,
JLI.memoryAddressStrides(target));
}
/**
* Creates a memory access var handle with a <em>variable</em> offset added to the accessed offset.
* That is, if the target memory access var handle accesses a memory location at offset <em>O</em>,
* the new memory access var handle will access a memory location at offset <em>(S * X) + O</em>, where <em>S</em>
* is a constant <em>stride</em>, whereas <em>X</em> is a dynamic value that will be provided as an additional access
* coordinate (of type {@code long}). The new access coordinate will be <em>prepended</em> to the ones available
* in the target memory access var handles (if any).
*
* @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 target the target memory access handle to access after the scale adjustment.
* @param bytesStride the stride, in bytes, by which to multiply the coordinate value. Must be greater than zero.
* @return the new memory access var handle.
* @throws IllegalArgumentException when the target var handle is not a memory access var handle,
* or if {@code bytesStride <= 0}, or otherwise incompatible with the alignment constraint.
*/
public static VarHandle withStride(VarHandle target, long bytesStride) {
if (bytesStride == 0) {
throw new IllegalArgumentException("Stride must be positive: " + bytesStride);
}
long alignMask = JLI.memoryAddressAlignmentMask(target);
if ((bytesStride & alignMask) != 0) {
throw new IllegalArgumentException("Stride " + bytesStride + " does not conform to alignment " + (alignMask + 1));
}
long offset = JLI.memoryAddressOffset(target);
long[] strides = JLI.memoryAddressStrides(target);
long[] newStrides = new long[strides.length + 1];
System.arraycopy(strides, 0, newStrides, 1, strides.length);
newStrides[0] = bytesStride;
return JLI.memoryAddressViewVarHandle(
JLI.memoryAddressCarrier(target),
alignMask,
JLI.memoryAddressByteOrder(target),
offset,
newStrides);
}
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);
}
}

View File

@ -0,0 +1,445 @@
/*
* 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. 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.LayoutPath;
import jdk.internal.foreign.Utils;
import java.lang.constant.Constable;
import java.lang.constant.DynamicConstantDesc;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
/**
* A memory layout can be used to describe the contents of a memory segment in a <em>language neutral</em> fashion.
* 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#ofPaddingBits(long)}).
* Some common value layout constants are defined in the {@link MemoryLayouts} 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
* member layouts (see {@link GroupLayout}).
* <p>
* All implementations of this interface must be <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>;
* use of identity-sensitive operations (including reference equality ({@code ==}), identity hash code, or synchronization) on
* instances of {@code MemoryLayout} may have unpredictable results and should be avoided. The {@code equals} method should
* be used for comparisons.
* <p>
* Non-platform classes should not implement {@linkplain MemoryLayout} directly.
*
* <h2>Size, alignment and byte order</h2>
*
* All layouts have a size; layout size for value and padding layouts is always explicitly denoted; this means that a layout description
* always has the same size in bits, regardless of the platform in which it is used. For derived layouts, the size is computed
* as follows:
* <ul>
* <li>for a <em>finite</em> sequence layout <em>S</em> whose element layout is <em>E</em> and size is L,
* the size of <em>S</em> is that of <em>E, multiplied by L</em></li>
* <li>the size of an <em>unbounded</em> sequence layout is <em>unknown</em></li>
* <li>for a group layout <em>G</em> with member layouts <em>M1</em>, <em>M2</em>, ... <em>Mn</em> whose sizes are
* <em>S1</em>, <em>S2</em>, ... <em>Sn</em>, respectively, the size of <em>G</em> is either <em>S1 + S2 + ... + Sn</em> or
* <em>max(S1, S2, ... Sn)</em> depending on whether the group is a <em>struct</em> or an <em>union</em>, respectively</li>
* </ul>
* <p>
* Furthermore, all layouts feature a <em>natural alignment</em> which can be inferred as follows:
* <ul>
* <li>for value and padding layout <em>L</em> whose size is <em>N</em>, the natural alignment of <em>L</em> is <em>N</em></li>
* <li>for a sequence layout <em>S</em> whose element layout is <em>E</em>, the natural alignment of <em>S</em> is that of <em>E</em></li>
* <li>for a group layout <em>G</em> with member layouts <em>M1</em>, <em>M2</em>, ... <em>Mn</em> whose alignments are
* <em>A1</em>, <em>A2</em>, ... <em>An</em>, respectively, the natural alignment of <em>G</em> is <em>max(A1, A2 ... An)</em></li>
* </ul>
* A layout's natural alignment can be overridden if needed (see {@link MemoryLayout#withBitAlignment(long)}), which can be useful to describe
* hyper-aligned layouts.
* <p>
* All value layouts have an <em>explicit</em> byte order (see {@link java.nio.ByteOrder}) which is set when the layout is created.
*
* <h2><a id = "layout-paths">Layout paths</a></h2>
*
* A <em>layout path</em> originates from a <em>root</em> layout (typically a group or a sequence layout) and terminates
* 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 useful in order to e.g. to obtain offset of leaf elements inside arbitrarily nested layouts
* (see {@link MemoryLayout#offset(PathElement...)}), or to quickly obtain a memory access handle corresponding to the selected
* layout (see {@link MemoryLayout#varHandle(Class, PathElement...)}).
* <p>
* Such <em>layout paths</em> can be constructed programmatically using the methods in this class.
* For instance, given a layout constructed as follows:
* <blockquote><pre>{@code
SequenceLayout seq = MemoryLayout.ofSequence(5,
MemoryLayout.ofStruct(
MemoryLayout.ofPaddingBits(32),
MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN).withName("value")
));
* }</pre></blockquote>
*
* We can obtain the offset of the member layout named <code>value</code> from <code>seq</code>, as follows:
* <blockquote><pre>{@code
long valueOffset = seq.offset(PathElement.sequenceElement(), PathElement.groupElement("value"));
* }</pre></blockquote>
*
* Layout paths can feature one or more <em>free dimensions</em>. For instance, a layout path traversing
* an unspecified sequence element (that is, where one of the path component was obtained with the
* {@link PathElement#sequenceElement()} method) features an additional free dimension, which will have to be bound at runtime;
* that is, the memory access var handle associated with such a layout path expression will feature an extra {@code long}
* access coordinate. The layout path constructed in the above example features exactly one free dimension.
*
* @apiNote In the future, if the Java language permits, {@link MemoryLayout}
* may become a {@code sealed} interface, which would prohibit subclassing except by
* explicitly permitted types.
*
* @implSpec
* Implementations of this class are immutable and thread-safe.
*/
public interface MemoryLayout extends Constable {
/**
* Returns an {@link Optional} containing the nominal descriptor for this
* layout, 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
Optional<? extends DynamicConstantDesc<? extends MemoryLayout>> describeConstable();
/**
* Computes the layout size, in bits.
*
* @return the layout size, in bits.
* @throws UnsupportedOperationException if the layout is, or contains, a sequence layout with unspecified size (see {@link SequenceLayout}).
*/
long bitSize();
/**
* Computes the layout size, in bytes.
*
* @return the layout size, in bytes.
* @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"));
}
/**
* Return the <em>name</em> (if any) associated with this layout.
*
* @return the layout <em>name</em> (if any).
* @see MemoryLayout#withName(String)
*/
Optional<String> name();
/**
* Creates a new layout which features the desired layout <em>name</em>.
*
* @param name the layout name.
* @return a new layout which is the same as this layout, except for the <em>name</em> associated to it.
* @see MemoryLayout#name()
*/
MemoryLayout withName(String name);
/**
* Returns the alignment constraint associated with this layout, expressed in bits. Layout alignment defines a power
* of two {@code A} which is the bit-wise alignment of the layout. If {@code A <= 8} then {@code A/8} is the number of
* bytes that must be aligned for any pointer that correctly points to this layout. Thus:
*
* <ul>
* <li>{@code A=8} means unaligned (in the usual sense), which is common in packets.</li>
* <li>{@code A=64} means word aligned (on LP64), {@code A=32} int aligned, {@code A=16} short aligned, etc.</li>
* <li>{@code A=512} is the most strict alignment required by the x86/SV ABI (for AVX-512 data).</li>
* </ul>
*
* @return the layout alignment constraint, in bits.
*/
long bitAlignment();
/**
* Returns the alignment constraint associated with this layout, expressed in bytes. Layout alignment defines a power
* of two {@code A} which is the byte-wise alignment of the layout, where {@code A} is the number of bytes that must be aligned
* for any pointer that correctly points to this layout. Thus:
*
* <ul>
* <li>{@code A=1} means unaligned (in the usual sense), which is common in packets.</li>
* <li>{@code A=8} means word aligned (on LP64), {@code A=4} int aligned, {@code A=2} short aligned, etc.</li>
* <li>{@code A=64} is the most strict alignment required by the x86/SV ABI (for AVX-512 data).</li>
* </ul>
*
* @return the layout alignment constraint, in bytes.
* @throws UnsupportedOperationException if {@code bitAlignment()} is not a multiple of 8.
*/
default long byteAlignment() {
return Utils.bitsToBytesOrThrow(bitAlignment(),
() -> new UnsupportedOperationException("Cannot compute byte alignment; bit alignment is not a multiple of 8"));
}
/**
* Creates a new layout which features the desired alignment constraint.
*
* @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 to it.
* @throws IllegalArgumentException if {@code bitAlignment} is not a power of two, or if it's less than than 8.
*/
MemoryLayout withBitAlignment(long bitAlignment);
/**
* Computes the offset of the layout selected by a given layout path, where the path is considered rooted in this
* layout.
*
* @apiNote if the layout path has one (or more) free dimensions,
* the offset is computed as if all the indices corresponding to such dimensions were set to {@code 0}.
*
* @param elements the layout path elements.
* @return The offset of layout selected by a the layout path obtained by concatenating the path elements in {@code elements}.
* @throws IllegalArgumentException if the layout path obtained by concatenating the path elements in {@code elements}
* does not select a valid layout element.
*/
default long offset(PathElement... elements) {
LayoutPath path = LayoutPath.rootPath(this);
for (PathElement e : elements) {
path = ((LayoutPath.PathElementImpl)e).apply(path);
}
return path.offset();
}
/**
* Creates a memory access var handle that can be used to dereference memory at the layout selected by a given layout path,
* where the path is considered rooted in this layout.
*
* @apiNote the resulting var handle will feature an additional {@code long} access coordinate for every
* unspecified sequence access component contained in this layout path. Moreover, the resulting var handle
* 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 layout denoted by given layout path.
* @throws UnsupportedOperationException if the layout path has one or more elements with incompatible alignment constraints.
* @throws IllegalArgumentException if the carrier does not represent a primitive type, if the carrier is {@code void},
* {@code boolean}, or if the layout path obtained by concatenating the path elements in {@code elements}
* does not select a value layout (see {@link ValueLayout}), or if the selected value layout has a size that
* that does not match that of the specified carrier type.
*/
default VarHandle varHandle(Class<?> carrier, PathElement... elements) {
LayoutPath path = LayoutPath.rootPath(this);
for (PathElement e : elements) {
path = ((LayoutPath.PathElementImpl)e).apply(path);
}
return path.dereferenceHandle(carrier);
}
/**
* Instances of this class are used to form <a href="MemoryLayout.html#layout-paths"><em>layout paths</em></a>. There
* are two kinds of path elements: <em>group path elements</em> and <em>sequence path elements</em>. Group
* path elements are used to select a given named member layout within a {@link GroupLayout}. Sequence
* path elements are used to select a sequence element layout within a {@link SequenceLayout}; selection
* 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.
*
* @apiNote In the future, if the Java language permits, {@link PathElement}
* may become a {@code sealed} interface, which would prohibit subclassing except by
* explicitly permitted types.
*
* @implSpec
* Implementations of this interface are immutable and thread-safe.
*/
interface PathElement {
/**
* Returns a path element which selects a member layout with given name from a given group layout.
* The path element returned by this method does not alter the number of free dimensions of any path
* that is combined with such element.
*
* @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.
*
* @param name the name of the group element to be selected.
* @return a path element which selects the group element with given name.
* @throws NullPointerException if the specified group element name is {@code null}.
*/
static PathElement groupElement(String name) {
Objects.requireNonNull(name);
return new LayoutPath.PathElementImpl(path -> path.groupElement(name));
}
/**
* Returns a path element which selects the element layout at the specified position in a given the sequence layout.
* The path element returned by this method does not alter the number of free dimensions of any path
* that is combined with such element.
*
* @param index the index of the sequence element to be selected.
* @return a path element which selects the sequence element layout with given index.
* @throws IllegalArgumentException if {@code index < 0}.
*/
static PathElement sequenceElement(long index) {
if (index < 0) {
throw new IllegalArgumentException("Index must be positive: " + index);
}
return new LayoutPath.PathElementImpl(path -> path.sequenceElement(index));
}
/**
* Returns a path element which selects the element layout in a <em>range</em> of positions in a given the sequence layout,
* where the range is expressed as a pair of starting index (inclusive) {@code S} and step factor (which can also be negative)
* {@code F}.
* If a path with free dimensions {@code n} is combined with the path element returned by this method,
* the number of free dimensions of the resulting path will be {@code 1 + n}. If the free dimension associated
* with this path is bound by an index {@code I}, the resulting accessed offset can be obtained with the following
* formula:
* <blockquote><pre>{@code
E * (S + I * F)
* }</pre></blockquote>
* where {@code E} is the size (in bytes) of the sequence element layout.
*
* @param start the index of the first sequence element to be selected.
* @param step the step factor at which subsequence sequence elements are to be selected.
* @return a path element which selects the sequence element layout with given index.
* @throws IllegalArgumentException if {@code start < 0}, or {@code step == 0}.
*/
static PathElement sequenceElement(long start, long step) {
if (start < 0) {
throw new IllegalArgumentException("Start index must be positive: " + start);
}
if (step == 0) {
throw new IllegalArgumentException("Step must be != 0: " + step);
}
return new LayoutPath.PathElementImpl(path -> path.sequenceElement(start, step));
}
/**
* Returns a path element which selects an unspecified element layout from a given sequence layout.
* If a path with free dimensions {@code n} is combined with the path element returned by this method,
* the number of free dimensions of the resulting path will be {@code 1 + n}.
*
* @return a path element which selects an unspecified sequence element layout.
*/
static PathElement sequenceElement() {
return new LayoutPath.PathElementImpl(LayoutPath::sequenceElement);
}
}
/**
* Compares the specified object with this layout for equality. Returns {@code true} if and only if the specified
* object is also a layout, and it is equal to this layout.
*
* @param that the object to be compared for equality with this layout.
* @return {@code true} if the specified object is equal to this layout.
*/
boolean equals(Object that);
/**
* Returns the hash code value for this layout.
*
* @return the hash code value for this layout.
*/
int hashCode();
/**
* Returns a string representation of this layout.
*
* @return a string representation of this layout.
*/
@Override
String toString();
/**
* Create a new padding layout with given size.
*
* @param size the padding size in bits.
* @return the new selector layout.
* @throws IllegalArgumentException if {@code size <= 0}.
*/
static MemoryLayout ofPaddingBits(long size) {
AbstractLayout.checkSize(size);
return new PaddingLayout(size);
}
/**
* Create a value layout of given byte order and size.
*
* @param size the value layout size.
* @param order the value layout's byte order.
* @return a new value layout.
* @throws IllegalArgumentException if {@code size <= 0}.
*/
static ValueLayout ofValueBits(long size, ByteOrder order) {
AbstractLayout.checkSize(size);
return new ValueLayout(order, size);
}
/**
* Create a new sequence layout with given element layout and element count.
*
* @param elementCount the sequence element count.
* @param elementLayout the sequence element layout.
* @return the new sequence layout with given element layout and size.
* @throws IllegalArgumentException if {@code elementCount < 0}.
*/
static SequenceLayout ofSequence(long elementCount, MemoryLayout elementLayout) {
AbstractLayout.checkSize(elementCount, true);
OptionalLong size = OptionalLong.of(elementCount);
return new SequenceLayout(size, elementLayout);
}
/**
* Create a new sequence layout, with unbounded element count and given element layout.
*
* @param elementLayout the element layout of the sequence layout.
* @return the new sequence layout with given element layout.
*/
static SequenceLayout ofSequence(MemoryLayout elementLayout) {
return new SequenceLayout(OptionalLong.empty(), elementLayout);
}
/**
* Create a new <em>struct</em> group layout with given member layouts.
*
* @param elements The member layouts of the <em>struct</em> group layout.
* @return a new <em>struct</em> group layout with given member layouts.
*/
static GroupLayout ofStruct(MemoryLayout... elements) {
return new GroupLayout(GroupLayout.Kind.STRUCT, List.of(elements));
}
/**
* Create a new <em>union</em> group layout with given member layouts.
*
* @param elements The member layouts of the <em>union</em> layout.
* @return a new <em>union</em> group layout with given member layouts.
*/
static GroupLayout ofUnion(MemoryLayout... elements) {
return new GroupLayout(GroupLayout.Kind.UNION, List.of(elements));
}
}

View File

@ -0,0 +1,138 @@
/*
* 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. 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 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 interoperate 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.ofValueBits(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.ofValueBits(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.ofValueBits(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.ofValueBits(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.ofValueBits(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.ofValueBits(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.ofValueBits(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.ofValueBits(64, ByteOrder.BIG_ENDIAN);
/**
* A padding layout constant with size of one byte.
*/
public static final MemoryLayout PAD_8 = MemoryLayout.ofPaddingBits(8);
/**
* A padding layout constant with size of two bytes.
*/
public static final MemoryLayout PAD_16 = MemoryLayout.ofPaddingBits(16);
/**
* A padding layout constant with size of four bytes.
*/
public static final MemoryLayout PAD_32 = MemoryLayout.ofPaddingBits(32);
/**
* A padding layout constant with size of eight bytes.
*/
public static final MemoryLayout PAD_64 = MemoryLayout.ofPaddingBits(64);
/**
* 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.ofValueBits(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.ofValueBits(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.ofValueBits(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.ofValueBits(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()}.
*/
public static final ValueLayout JAVA_LONG = MemoryLayout.ofValueBits(64, ByteOrder.nativeOrder());
/**
* 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.ofValueBits(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()}.
*/
public static final ValueLayout JAVA_DOUBLE = MemoryLayout.ofValueBits(64, ByteOrder.nativeOrder());
}

View File

@ -0,0 +1,430 @@
/*
* 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. 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 java.nio.ByteBuffer;
import jdk.internal.foreign.Utils;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
/**
* A memory segment models a contiguous region of memory. A memory segment is associated with both spatial
* and temporal bounds. Spatial bounds ensure that memory access operations on a memory segment cannot affect a memory location
* which falls <em>outside</em> the boundaries of the memory segment being accessed. Temporal checks ensure that memory access
* operations on a segment cannot occur after a memory segment has been closed (see {@link MemorySegment#close()}).
* <p>
* All implementations of this interface must be <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>;
* use of identity-sensitive operations (including reference equality ({@code ==}), identity hash code, or synchronization) on
* instances of {@code MemorySegment} may have unpredictable results and should be avoided. The {@code equals} method should
* be used for comparisons.
* <p>
* Non-platform classes should not implement {@linkplain MemorySegment} directly.
*
* <h2>Constructing memory segments from different sources</h2>
*
* There are multiple ways to obtain a memory segment. First, memory segments backed by off-heap memory can
* be allocated using one of the many factory methods provided (see {@link MemorySegment#allocateNative(MemoryLayout)},
* {@link MemorySegment#allocateNative(long)} and {@link MemorySegment#allocateNative(long, long)}). Memory segments obtained
* in this way are called <em>native memory segments</em>.
* <p>
* It is also possible to obtain a memory segment backed by an existing heap-allocated Java array,
* using one of the provided factory methods (e.g. {@link MemorySegment#ofArray(int[])}). Memory segments obtained
* in this way are called <em>array memory segments</em>.
* <p>
* It is possible to obtain a memory segment backed by an existing Java byte buffer (see {@link ByteBuffer}),
* using the factory method {@link MemorySegment#ofByteBuffer(ByteBuffer)}.
* Memory segments obtained in this way are called <em>buffer memory segments</em>. Note that buffer memory segments might
* be backed by native memory (as in the case of native memory segments) or heap memory (as in the case of array memory segments),
* depending on the characteristics of the byte buffer instance the segment is associated with. For instance, a buffer memory
* segment obtained from a byte buffer created with the {@link ByteBuffer#allocateDirect(int)} method will be backed
* by native memory.
* <p>
* Finally, it is also possible to obtain a memory segment backed by a memory-mapped file using the factory method
* {@link MemorySegment#mapFromPath(Path, long, FileChannel.MapMode)}. Such memory segments are called <em>mapped memory segments</em>.
*
* <h2>Closing a memory segment</h2>
*
* Memory segments are closed explicitly (see {@link MemorySegment#close()}). In general when a segment is closed, all off-heap
* resources associated with it are released; this has different meanings depending on the kind of memory segment being
* considered:
* <ul>
* <li>closing a native memory segment results in <em>freeing</em> the native memory associated with it</li>
* <li>closing a mapped memory segment results in the backing memory-mapped file to be unmapped</li>
* <li>closing an acquired memory segment <b>does not</b> result in the release of resources
* (see the section on <a href="#thread-confinement">thread confinement</a> for more details)</li>
* <li>closing a buffer, or a heap segment does not have any side-effect, other than marking the segment
* as <em>not alive</em> (see {@link MemorySegment#isAlive()}). Also, since the buffer and heap segments might keep
* strong references to the original buffer or array instance, it is the responsibility of clients to ensure that
* these segments are discarded in a timely manner, so as not to prevent garbage collection to reclaim the underlying
* objects.</li>
* </ul>
*
* <h2><a id = "thread-confinement">Thread confinement</a></h2>
*
* Memory segments support strong thread-confinement guarantees. Upon creation, they are assigned an <em>owner thread</em>,
* typically the thread which initiated the creation operation. After creation, only the owner thread will be allowed
* to directly manipulate the memory segment (e.g. close the memory segment) or access the underlying memory associated with
* the segment using a memory access var handle. Any attempt to perform such operations from a thread other than the
* owner thread will result in a runtime failure.
* <p>
* If a memory segment S owned by thread A needs to be used by thread B, B needs to explicitly <em>acquire</em> S,
* which will create a so called <em>acquired</em> memory segment owned by B (see {@link #acquire()}) backed by the same resources
* as S. A memory segment can be acquired multiple times by one or more threads; in that case, a memory segment S cannot
* be closed until all the acquired memory segments derived from S have been closed. Furthermore, closing an acquired
* memory segment does <em>not</em> trigger any deallocation action. It is therefore important that clients remember to
* explicitly close the original segment from which the acquired memory segments have been obtained in order to truly
* ensure that off-heap resources associated with the memory segment are released.
*
* <h2>Memory segment views</h2>
*
* Memory segments support <em>views</em>. It is possible to create an <em>immutable</em> view of a memory segment
* (see {@link MemorySegment#asReadOnly()}) which does not support write operations. It is also possible to create views
* whose spatial bounds are stricter than the ones of the original segment (see {@link MemorySegment#asSlice(long, long)}).
* <p>
* Temporal bounds of the original segment are inherited by the view; that is, closing a segment view, such as a sliced
* view, will cause the original segment to be closed; as such special care must be taken when sharing views
* between multiple clients. If a client want to protect itself against early closure of a segment by
* another actor, it is the responsibility of that client to take protective measures, such as calling
* {@link MemorySegment#acquire()} before sharing the view with another client.
* <p>
* To allow for interoperability with existing code, a byte buffer view can be obtained from a memory segment
* (see {@link #asByteBuffer()}). This can be useful, for instance, for those clients that want to keep using the
* {@link ByteBuffer} API, but need to operate on large memory segments. Byte buffers obtained in such a way support
* the same spatial and temporal access restrictions associated to the memory address from which they originated.
*
* @apiNote In the future, if the Java language permits, {@link MemorySegment}
* may become a {@code sealed} interface, which would prohibit subclassing except by
* explicitly permitted types.
*
* @implSpec
* Implementations of this interface are immutable and thread-safe.
*/
public interface MemorySegment extends AutoCloseable {
/**
* The base memory address associated with this memory segment.
* @return The base memory address.
*/
MemoryAddress baseAddress();
/**
* Obtains an <a href="#thread-confinement">acquired</a> memory segment which can be used to access memory associated
* with this segment from the current thread. As a side-effect, this segment cannot be closed until the acquired
* view has been closed too (see {@link #close()}).
* @return an <a href="#thread-confinement">acquired</a> memory segment which can be used to access memory associated
* with this segment from the current thread.
* @throws IllegalStateException if this segment has been closed.
*/
MemorySegment acquire();
/**
* Is this segment accessible from the current thread?
* @return true, if this segment is accessible from the current thread.
*/
boolean isAccessible();
/**
* The size (in bytes) of this memory segment.
* @return The size (in bytes) of this memory segment.
*/
long byteSize();
/**
* Obtains a read-only view of this segment. An attempt to write memory associated with a read-only memory segment
* will fail with {@link UnsupportedOperationException}.
* @return a read-only view of this segment.
* @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the
* thread owning this segment.
*/
MemorySegment asReadOnly();
/**
* Obtains a new memory segment view whose base address is the same as the base address of this segment plus a given offset,
* and whose new size is specified by the given argument.
* @param offset The new segment base offset (relative to the current segment base address), specified in bytes.
* @param newSize The new segment size, specified in bytes.
* @return a new memory segment view with updated base/limit addresses.
* @throws IndexOutOfBoundsException if {@code offset < 0}, {@code offset > byteSize()}, {@code newSize < 0}, or {@code newSize > byteSize() - offset}
* @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the
* thread owning this segment.
*/
MemorySegment asSlice(long offset, long newSize);
/**
* Is this segment alive?
* @return true, if the segment is alive.
* @see MemorySegment#close()
*/
boolean isAlive();
/**
* Is this segment read-only?
* @return true, if the segment is read-only.
* @see MemorySegment#asReadOnly()
*/
boolean isReadOnly();
/**
* Closes this memory segment. Once a memory segment has been closed, any attempt to use the memory segment,
* or to access the memory associated with the segment will fail with {@link IllegalStateException}. Depending on
* the kind of memory segment being closed, calling this method further trigger deallocation of all the resources
* associated with the memory segment.
* @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the
* thread owning this segment, or if existing acquired views of this segment are still in use (see {@link MemorySegment#acquire()}).
*/
void close();
/**
* Wraps this segment in a {@link ByteBuffer}. Some of the properties of the returned buffer are linked to
* the properties of this segment. For instance, if this segment is <em>immutable</em>
* (see {@link MemorySegment#asReadOnly()}, then the resulting buffer is <em>read-only</em>
* (see {@link ByteBuffer#isReadOnly()}. Additionally, if this is a native memory segment, the resulting buffer is
* <em>direct</em> (see {@link ByteBuffer#isDirect()}).
* <p>
* The life-cycle of the returned buffer will be tied to that of this segment. That means that if the this segment
* is closed (see {@link MemorySegment#close()}, accessing the returned
* buffer will throw an {@link IllegalStateException}.
* <p>
* The resulting buffer's byte order is {@link java.nio.ByteOrder#BIG_ENDIAN}; this can be changed using
* {@link ByteBuffer#order(java.nio.ByteOrder)}.
*
* @return a {@link ByteBuffer} view of this memory segment.
* @throws UnsupportedOperationException if this segment cannot be mapped onto a {@link ByteBuffer} instance,
* e.g. because it models an heap-based segment that is not based on a {@code byte[]}), or if its size is greater
* than {@link Integer#MAX_VALUE}.
* @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the
* thread owning this segment.
*/
ByteBuffer asByteBuffer();
/**
* Copy the contents of this memory segment into a fresh byte array.
* @return a fresh byte array copy of this memory segment.
* @throws UnsupportedOperationException if this segment's contents cannot be copied into a {@link byte[]} instance,
* e.g. its size is greater than {@link Integer#MAX_VALUE}.
* @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the
* thread owning this segment.
*/
byte[] toByteArray();
/**
* Creates a new buffer memory segment that models the memory associated with the given byte
* buffer. The segment starts relative to the buffer's position (inclusive)
* and ends relative to the buffer's limit (exclusive).
* <p>
* The resulting memory segment keeps a reference to the backing buffer, to ensure it remains <em>reachable</em>
* for the life-time of the segment.
*
* @param bb the byte buffer backing the buffer memory segment.
* @return a new buffer memory segment.
*/
static MemorySegment ofByteBuffer(ByteBuffer bb) {
return Utils.makeBufferSegment(bb);
}
/**
* Creates a new array memory segment that models the memory associated with a given heap-allocated byte array.
* <p>
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
* for the life-time of the segment.
*
* @param arr the primitive array backing the array memory segment.
* @return a new array memory segment.
*/
static MemorySegment ofArray(byte[] arr) {
return Utils.makeArraySegment(arr);
}
/**
* Creates a new array memory segment that models the memory associated with a given heap-allocated char array.
* <p>
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
* for the life-time of the segment.
*
* @param arr the primitive array backing the array memory segment.
* @return a new array memory segment.
*/
static MemorySegment ofArray(char[] arr) {
return Utils.makeArraySegment(arr);
}
/**
* Creates a new array memory segment that models the memory associated with a given heap-allocated short array.
* <p>
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
* for the life-time of the segment.
*
* @param arr the primitive array backing the array memory segment.
* @return a new array memory segment.
*/
static MemorySegment ofArray(short[] arr) {
return Utils.makeArraySegment(arr);
}
/**
* Creates a new array memory segment that models the memory associated with a given heap-allocated int array.
* <p>
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
* for the life-time of the segment.
*
* @param arr the primitive array backing the array memory segment.
* @return a new array memory segment.
*/
static MemorySegment ofArray(int[] arr) {
return Utils.makeArraySegment(arr);
}
/**
* Creates a new array memory segment that models the memory associated with a given heap-allocated float array.
* <p>
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
* for the life-time of the segment.
*
* @param arr the primitive array backing the array memory segment.
* @return a new array memory segment.
*/
static MemorySegment ofArray(float[] arr) {
return Utils.makeArraySegment(arr);
}
/**
* Creates a new array memory segment that models the memory associated with a given heap-allocated long array.
* <p>
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
* for the life-time of the segment.
*
* @param arr the primitive array backing the array memory segment.
* @return a new array memory segment.
*/
static MemorySegment ofArray(long[] arr) {
return Utils.makeArraySegment(arr);
}
/**
* Creates a new array memory segment that models the memory associated with a given heap-allocated double array.
* <p>
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
* for the life-time of the segment.
*
* @param arr the primitive array backing the array memory segment.
* @return a new array memory segment.
*/
static MemorySegment ofArray(double[] arr) {
return Utils.makeArraySegment(arr);
}
/**
* Creates a new native memory segment that models a newly allocated block of off-heap memory with given layout.
* <p>
* This is equivalent to the following code:
* <blockquote><pre>{@code
allocateNative(layout.bytesSize(), layout.bytesAlignment());
* }</pre></blockquote>
*
* @implNote The initialization state of the contents of the block of off-heap memory associated with the returned native memory
* segment is unspecified and should not be relied upon. Moreover, a client is responsible to call the {@link MemorySegment#close()}
* on a native memory segment, to make sure the backing off-heap memory block is deallocated accordingly. Failure to do so
* will result in off-heap memory leaks.
*
* @param layout the layout of the off-heap memory block backing the native memory segment.
* @return a new native memory segment.
* @throws IllegalArgumentException if the specified layout has illegal size or alignment constraint.
*/
static MemorySegment allocateNative(MemoryLayout layout) {
return allocateNative(layout.byteSize(), layout.byteAlignment());
}
/**
* Creates a new native memory segment that models a newly allocated block of off-heap memory with given size (in bytes).
* <p>
* This is equivalent to the following code:
* <blockquote><pre>{@code
allocateNative(bytesSize, 1);
* }</pre></blockquote>
*
* @implNote The initialization state of the contents of the block of off-heap memory associated with the returned native memory
* segment is unspecified and should not be relied upon. Moreover, a client is responsible to call the {@link MemorySegment#close()}
* on a native memory segment, to make sure the backing off-heap memory block is deallocated accordingly. Failure to do so
* will result in off-heap memory leaks.
*
* @param bytesSize the size (in bytes) of the off-heap memory block backing the native memory segment.
* @return a new native memory segment.
* @throws IllegalArgumentException if {@code bytesSize < 0}.
*/
static MemorySegment allocateNative(long bytesSize) {
return allocateNative(bytesSize, 1);
}
/**
* Creates a new mapped memory segment that models a memory-mapped region of a file from a given path.
*
* @implNote When obtaining a mapped segment from a newly created file, the initialization state of the contents of the block
* of mapped memory associated with the returned mapped memory segment is unspecified and should not be relied upon.
*
* @param path the path to the file to memory map.
* @param bytesSize the size (in bytes) of the mapped memory backing the memory segment.
* @param mapMode a file mapping mode, see {@link FileChannel#map(FileChannel.MapMode, long, long)}.
* @return a new mapped memory segment.
* @throws IllegalArgumentException if {@code bytesSize < 0}.
* @throws UnsupportedOperationException if an unsupported map mode is specified.
* @throws IOException if the specified path does not point to an existing file, or if some other I/O error occurs.
*/
static MemorySegment mapFromPath(Path path, long bytesSize, FileChannel.MapMode mapMode) throws IOException {
return Utils.makeMappedSegment(path, bytesSize, mapMode);
}
/**
* Creates a new native memory segment that models a newly allocated block of off-heap memory with given size and
* alignment constraint (in bytes).
*
* @implNote The initialization state of the contents of the block of off-heap memory associated with the returned native memory
* segment is unspecified and should not be relied upon. Moreover, a client is responsible to call the {@link MemorySegment#close()}
* on a native memory segment, to make sure the backing off-heap memory block is deallocated accordingly. Failure to do so
* will result in off-heap memory leaks.
*
* @param bytesSize the size (in bytes) of the off-heap memory block backing the native memory segment.
* @param alignmentBytes the alignment constraint (in bytes) of the off-heap memory block backing the native memory segment.
* @return a new native memory segment.
* @throws IllegalArgumentException if {@code bytesSize < 0}, {@code alignmentBytes < 0}, or if {@code alignmentBytes}
* is not a power of 2.
*/
static MemorySegment allocateNative(long bytesSize, long alignmentBytes) {
if (bytesSize <= 0) {
throw new IllegalArgumentException("Invalid allocation size : " + bytesSize);
}
if (alignmentBytes < 0 ||
((alignmentBytes & (alignmentBytes - 1)) != 0L)) {
throw new IllegalArgumentException("Invalid alignment constraint : " + alignmentBytes);
}
return Utils.makeNativeSegment(bytesSize, alignmentBytes);
}
}

View File

@ -0,0 +1,111 @@
/*
* 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. 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 java.lang.constant.ConstantDescs;
import java.lang.constant.DynamicConstantDesc;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
/**
* A padding layout. A padding layout specifies the size of extra space which is typically not accessed by applications,
* and is typically used for aligning member layouts around word boundaries.
* <p>
* This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
* class; use of identity-sensitive operations (including reference equality
* ({@code ==}), identity hash code, or synchronization) on instances of
* {@code PaddingLayout} may have unpredictable results and should be avoided.
* The {@code equals} method should be used for comparisons.
*
* @implSpec
* This class is immutable and thread-safe.
*/
/* package-private */ final class PaddingLayout extends AbstractLayout implements MemoryLayout {
PaddingLayout(long size) {
this(size, size, Optional.empty());
}
PaddingLayout(long size, long alignment, Optional<String> name) {
super(OptionalLong.of(size), alignment, name);
}
@Override
public String toString() {
return decorateLayoutString("x" + bitSize());
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!super.equals(other)) {
return false;
}
if (!(other instanceof PaddingLayout)) {
return false;
}
PaddingLayout p = (PaddingLayout)other;
return bitSize() == p.bitSize();
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), bitSize());
}
@Override
PaddingLayout dup(long alignment, Optional<String> name) {
return new PaddingLayout(bitSize(), alignment, name);
}
@Override
public Optional<DynamicConstantDesc<MemoryLayout>> describeConstable() {
return Optional.of(DynamicConstantDesc.ofNamed(ConstantDescs.BSM_INVOKE, "padding",
CD_LAYOUT, MH_PADDING, bitSize()));
}
//hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
//but that causes issues with javadoc, see JDK-8224052
/**
* {@inheritDoc}
*/
@Override
public PaddingLayout withName(String name) {
return (PaddingLayout)super.withName(name);
}
/**
* {@inheritDoc}
*/
@Override
public PaddingLayout withBitAlignment(long alignmentBits) {
return (PaddingLayout)super.withBitAlignment(alignmentBits);
}
}

View File

@ -0,0 +1,161 @@
/*
* 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. 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 java.lang.constant.ConstantDescs;
import java.lang.constant.DynamicConstantDesc;
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>.
* 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.ofSequence(3, MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN));
* }</pre>
*
* is equivalent to the following layout:
*
* <pre>{@code
MemoryLayout.ofStruct(
MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN),
MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN),
MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN));
* }</pre>
*
* <p>
* This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
* class; use of identity-sensitive operations (including reference equality
* ({@code ==}), identity hash code, or synchronization) on instances of
* {@code SequenceLayout} may have unpredictable results and should be avoided.
* The {@code equals} method should be used for comparisons.
*
* @implSpec
* This class is immutable and thread-safe.
*/
public final class SequenceLayout extends AbstractLayout {
private final OptionalLong elemCount;
private final MemoryLayout elementLayout;
SequenceLayout(OptionalLong elemCount, MemoryLayout elementLayout) {
this(elemCount, elementLayout, elementLayout.bitAlignment(), Optional.empty());
}
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, name);
this.elemCount = elemCount;
this.elementLayout = elementLayout;
}
/**
* Returns the element layout associated with this sequence layout.
*
* @return The element layout associated with this sequence layout.
*/
public MemoryLayout elementLayout() {
return elementLayout;
}
/**
* Returns the element count of this sequence layout (if any).
*
* @return the element count of this sequence layout (if any).
*/
public OptionalLong elementCount() {
return elemCount;
}
@Override
public String toString() {
return decorateLayoutString(String.format("[%s:%s]",
elemCount.isPresent() ? elemCount.getAsLong() : "", elementLayout));
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!super.equals(other)) {
return false;
}
if (!(other instanceof SequenceLayout)) {
return false;
}
SequenceLayout s = (SequenceLayout)other;
return elemCount.equals(s.elemCount) && elementLayout.equals(s.elementLayout);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), elemCount, elementLayout);
}
@Override
SequenceLayout dup(long alignment, Optional<String> name) {
return new SequenceLayout(elementCount(), elementLayout, alignment, name);
}
@Override
boolean hasNaturalAlignment() {
return alignment == elementLayout.bitAlignment();
}
@Override
public Optional<DynamicConstantDesc<SequenceLayout>> describeConstable() {
return elemCount.isPresent() ?
Optional.of(DynamicConstantDesc.ofNamed(ConstantDescs.BSM_INVOKE, "value",
CD_SEQUENCE_LAYOUT, MH_SIZED_SEQUENCE, elemCount.getAsLong(), elementLayout.describeConstable().get())) :
Optional.of(DynamicConstantDesc.ofNamed(ConstantDescs.BSM_INVOKE, "value",
CD_SEQUENCE_LAYOUT, MH_UNSIZED_SEQUENCE, elementLayout.describeConstable().get()));
}
//hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
//but that causes issues with javadoc, see JDK-8224052
/**
* {@inheritDoc}
*/
@Override
public SequenceLayout withName(String name) {
return (SequenceLayout)super.withName(name);
}
/**
* {@inheritDoc}
*/
@Override
public SequenceLayout withBitAlignment(long alignmentBits) {
return (SequenceLayout)super.withBitAlignment(alignmentBits);
}
}

View File

@ -0,0 +1,140 @@
/*
* 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. 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 java.lang.constant.ConstantDescs;
import java.lang.constant.DynamicConstantDesc;
import java.lang.constant.MethodHandleDesc;
import java.nio.ByteOrder;
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}).
*
* <p>
* This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
* class; use of identity-sensitive operations (including reference equality
* ({@code ==}), identity hash code, or synchronization) on instances of
* {@code ValueLayout} may have unpredictable results and should be avoided.
* The {@code equals} method should be used for comparisons.
*
* @implSpec
* This class is immutable and thread-safe.
*/
public final class ValueLayout extends AbstractLayout implements MemoryLayout {
private final ByteOrder order;
ValueLayout(ByteOrder order, long size) {
this(order, size, size, Optional.empty());
}
ValueLayout(ByteOrder order, long size, long alignment, Optional<String> name) {
super(OptionalLong.of(size), alignment, name);
this.order = order;
}
/**
* Returns the value's byte order.
*
* @return the value's byte order.
*/
public ByteOrder order() {
return order;
}
/**
* Returns a new value layout with given byte order.
*
* @param order the desired byte order.
* @return a new value layout with given byte order.
*/
public ValueLayout withOrder(ByteOrder order) {
return new ValueLayout(order, bitSize(), alignment, optName());
}
@Override
public String toString() {
return decorateLayoutString(String.format("%s%d",
order == ByteOrder.BIG_ENDIAN ? "B" : "b",
bitSize()));
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!super.equals(other)) {
return false;
}
if (!(other instanceof ValueLayout)) {
return false;
}
ValueLayout v = (ValueLayout)other;
return order.equals(v.order) &&
bitSize() == v.bitSize() &&
alignment == v.alignment;
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), order, bitSize(), alignment);
}
@Override
ValueLayout dup(long alignment, Optional<String> name) {
return new ValueLayout(order, bitSize(), alignment, name);
}
@Override
public Optional<DynamicConstantDesc<ValueLayout>> describeConstable() {
return Optional.of(DynamicConstantDesc.ofNamed(ConstantDescs.BSM_INVOKE, "value",
CD_VALUE_LAYOUT, MH_VALUE, bitSize(), order == ByteOrder.BIG_ENDIAN ? BIG_ENDIAN : LITTLE_ENDIAN));
}
//hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
//but that causes issues with javadoc, see JDK-8224052
/**
* {@inheritDoc}
*/
@Override
public ValueLayout withName(String name) {
return (ValueLayout)super.withName(name);
}
/**
* {@inheritDoc}
*/
@Override
public ValueLayout withBitAlignment(long alignmentBits) {
return (ValueLayout)super.withBitAlignment(alignmentBits);
}
}

View File

@ -0,0 +1,90 @@
/*
* 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. 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.
*
*/
/**
* <p> Classes to support low-level, safe and efficient memory access. For example:
*
* <pre>{@code
static final VarHandle intHandle = MemoryHandles.varHandle(int.class, ByteOrder.BIG_ENDIAN);
try (MemorySegment segment = MemorySegment.allocateNative(10 * 4)) {
MemoryAddress base = segment.baseAddress();
for (long i = 0 ; i < 10 ; i++) {
intHandle.set(base.offset(i * 4), (int)i);
}
}
* }</pre>
*
* Here we create a var handle, namely {@code intHandle}, to manipulate values of the primitive type {@code int}, at
* a given memory location. We then create a <em>native</em> memory segment, that is, a memory segment backed by
* off-heap memory; the size of the segment is 40 bytes, enough to store 10 values of the primitive type {@code int}.
* The segment is created inside a <em>try-with-resources</em> construct: this idiom ensures that all the memory resources
* associated with the segment will be released at the end of the block. Inside the try-with-resources block, we initialize
* the contents of the memory segment; 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}.
*
* <p>
* The key abstractions introduced by this package 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 - that is,
* an offset inside a given segment. A memory address 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. 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
* memory access var handles, e.g. using <a href="MemoryLayout.html#layout-paths"><em>layout paths</em></a>.
*
* <h2><a id="deallocation"></a>Deterministic deallocation</h2>
*
* When writing code that manipulates memory segments, especially if backed by memory which resides outside the Java heap, it is
* crucial that the resources associated with a memory segment are released when the segment is no longer in use, by calling the {@link jdk.incubator.foreign.MemorySegment#close()}
* method either explicitly, or implicitly, by relying on try-with-resources construct (as demonstrated in the example above).
* Closing a given memory segment is an <em>atomic</em> operation which can either succeed - and result in the underlying
* memory associated with the segment to be released, or <em>fail</em> with an exception.
* <p>
* The deterministic deallocation model differs significantly from the implicit strategies adopted within other APIs, most
* notably the {@link java.nio.ByteBuffer} API: in that case, when a native byte buffer is created (see {@link java.nio.ByteBuffer#allocateDirect(int)}),
* the underlying memory is not released until the byte buffer reference becomes <em>unreachable</em>. While implicit deallocation
* models such as this can be very convenient - clients do not have to remember to <em>close</em> a direct buffer - such models can also make it
* hard for clients to ensure that the memory associated with a direct buffer has indeed been released.
*
* <h2><a id="safety"></a>Safety</h2>
*
* This API provides strong safety guarantees when it comes to memory access. First, when dereferencing a memory segment using
* a memory address, such an address is validated (upon access), to make sure that it does not point to a memory location
* which resides <em>outside</em> the boundaries of the memory segment it refers to. We call this guarantee <em>spatial safety</em>.
* <p>
* Since memory segments can be closed (see above), a memory address is also validated (upon access) to make sure that
* the segment it belongs to has not been closed prematurely. We call this guarantee <em>temporal safety</em>. Note that,
* in the general case, guaranteeing temporal safety can be hard, as multiple threads could attempt to access and/or close
* the same memory segment concurrently. The memory access API addresses this problem by imposing strong
* <a href="MemorySegment.html#thread-confinement"><em>thread-confinement</em></a> guarantees on memory segments: each
* memory segment is associated with an owner thread, which is the only thread that can either access or close the segment.
* A thread other than the owner thread will have to explicitly <em>acquire</em> a segment in order to be able to use it.
* <p>
* Together, spatial and temporal safety ensure that each memory access operation either succeeds - and accesses a valid
* memory location - or fails.
*/
package jdk.incubator.foreign;

View File

@ -0,0 +1,68 @@
/*
* 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. 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.unsafe;
import jdk.incubator.foreign.MemoryAddress;
import jdk.internal.foreign.MemoryAddressImpl;
/**
* Unsafe methods to allow interop between sun.misc.unsafe and memory access API.
*/
public final class ForeignUnsafe {
private ForeignUnsafe() {
//just the one, please
}
// The following methods can be used in conjunction with the java.foreign API.
/**
* Obtain the base object (if any) associated with this address. This can be used in conjunction with
* {@link #getUnsafeOffset(MemoryAddress)} in order to obtain a base/offset addressing coordinate pair
* to be used with methods like {@link sun.misc.Unsafe#getInt(Object, long)} and the likes.
*
* @param address the address whose base object is to be obtained.
* @return the base object associated with the address, or {@code null}.
*/
public static Object getUnsafeBase(MemoryAddress address) {
return ((MemoryAddressImpl)address).unsafeGetBase();
}
/**
* Obtain the offset associated with this address. If {@link #getUnsafeBase(MemoryAddress)} returns {@code null} on the passed
* address, then the offset is to be interpreted as the (absolute) numerical value associated said address.
* Alternatively, the offset represents the displacement of a field or an array element within the containing
* base object. This can be used in conjunction with {@link #getUnsafeBase(MemoryAddress)} in order to obtain a base/offset
* addressing coordinate pair to be used with methods like {@link sun.misc.Unsafe#getInt(Object, long)} and the likes.
*
* @param address the address whose offset is to be obtained.
* @return the offset associated with the address.
*/
public static long getUnsafeOffset(MemoryAddress address) {
return ((MemoryAddressImpl)address).unsafeGetOffset();
}
}

View File

@ -0,0 +1,216 @@
/*
* 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. 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.MemoryLayout;
import jdk.internal.access.JavaLangInvokeAccess;
import jdk.internal.access.SharedSecrets;
import sun.invoke.util.Wrapper;
import jdk.incubator.foreign.GroupLayout;
import jdk.incubator.foreign.SequenceLayout;
import jdk.incubator.foreign.ValueLayout;
import java.lang.invoke.VarHandle;
import java.util.function.UnaryOperator;
import java.util.stream.LongStream;
/**
* This class provide support for constructing layout paths; that is, starting from a root path (see {@link #rootPath(MemoryLayout)},
* a path can be constructed by selecting layout elements using the selector methods provided by this class
* (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)}).
*/
public class LayoutPath {
private static JavaLangInvokeAccess JLI = SharedSecrets.getJavaLangInvokeAccess();
private final MemoryLayout layout;
private final long offset;
private final LayoutPath enclosing;
private final long[] strides;
private LayoutPath(MemoryLayout layout, long offset, long[] strides, LayoutPath enclosing) {
this.layout = layout;
this.offset = offset;
this.strides = strides;
this.enclosing = enclosing;
}
// Layout path selector methods
public LayoutPath sequenceElement() {
check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
SequenceLayout seq = (SequenceLayout)layout;
MemoryLayout elem = seq.elementLayout();
return LayoutPath.nestedPath(elem, offset, addStride(elem.bitSize()), this);
}
public LayoutPath sequenceElement(long start, long step) {
check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
SequenceLayout seq = (SequenceLayout)layout;
checkSequenceBounds(seq, start);
MemoryLayout elem = seq.elementLayout();
long elemSize = elem.bitSize();
return LayoutPath.nestedPath(elem, offset + (start * elemSize), addStride(elemSize * step), this);
}
public LayoutPath sequenceElement(long index) {
check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
SequenceLayout seq = (SequenceLayout)layout;
checkSequenceBounds(seq, index);
long elemOffset = 0;
if (index > 0) {
//if index == 0, we do not depend on sequence element size, so skip
long elemSize = seq.elementLayout().bitSize();
elemOffset = elemSize * index;
}
return LayoutPath.nestedPath(seq.elementLayout(), offset + elemOffset, strides, this);
}
public LayoutPath groupElement(String name) {
check(GroupLayout.class, "attempting to select a group element from a non-group layout");
GroupLayout g = (GroupLayout)layout;
long offset = 0;
MemoryLayout elem = null;
for (int i = 0; i < g.memberLayouts().size(); i++) {
MemoryLayout l = g.memberLayouts().get(i);
if (l.name().isPresent() &&
l.name().get().equals(name)) {
elem = l;
break;
} else {
offset += l.bitSize();
}
}
if (elem == null) {
throw badLayoutPath("cannot resolve '" + name + "' in layout " + layout);
}
return LayoutPath.nestedPath(elem, this.offset + offset, strides, this);
}
// Layout path projections
public long offset() {
return offset;
}
public VarHandle dereferenceHandle(Class<?> carrier) {
if (!(layout instanceof ValueLayout)) {
throw badLayoutPath("layout path does not select a value layout");
}
if (!carrier.isPrimitive() || carrier == void.class || carrier == boolean.class // illegal carrier?
|| Wrapper.forPrimitiveType(carrier).bitWidth() != layout.bitSize()) { // carrier has the right size?
throw new IllegalArgumentException("Invalid carrier: " + carrier + ", for layout " + layout);
}
checkAlignment(this);
return JLI.memoryAddressViewVarHandle(
carrier,
layout.byteAlignment() - 1, //mask
((ValueLayout) layout).order(),
Utils.bitsToBytesOrThrow(offset, IllegalStateException::new),
LongStream.of(strides).map(s -> Utils.bitsToBytesOrThrow(s, IllegalStateException::new)).toArray());
}
// Layout path construction
public static LayoutPath rootPath(MemoryLayout layout) {
return new LayoutPath(layout, 0L, EMPTY_STRIDES, null);
}
private static LayoutPath nestedPath(MemoryLayout layout, long offset, long[] strides, LayoutPath encl) {
return new LayoutPath(layout, offset, strides, encl);
}
// Helper methods
private void check(Class<?> layoutClass, String msg) {
if (!layoutClass.isAssignableFrom(layout.getClass())) {
throw badLayoutPath(msg);
}
}
private void checkSequenceBounds(SequenceLayout seq, long index) {
if (seq.elementCount().isPresent() && index >= seq.elementCount().getAsLong()) {
throw badLayoutPath(String.format("Sequence index out of bound; found: %d, size: %d", index, seq.elementCount().getAsLong()));
}
}
private static IllegalArgumentException badLayoutPath(String cause) {
return new IllegalArgumentException("Bad layout path: " + cause);
}
private static void checkAlignment(LayoutPath path) {
MemoryLayout layout = path.layout;
long alignment = layout.bitAlignment();
if (path.offset % alignment != 0) {
throw new UnsupportedOperationException("Invalid alignment requirements for layout " + layout);
}
for (long stride : path.strides) {
if (stride % alignment != 0) {
throw new UnsupportedOperationException("Alignment requirements for layout " + layout + " do not match stride " + stride);
}
}
LayoutPath encl = path.enclosing;
if (encl != null) {
if (encl.layout.bitAlignment() < alignment) {
throw new UnsupportedOperationException("Alignment requirements for layout " + layout + " do not match those for enclosing layout " + encl.layout);
}
checkAlignment(encl);
}
}
private long[] addStride(long stride) {
long[] newStrides = new long[strides.length + 1];
System.arraycopy(strides, 0, newStrides, 0, strides.length);
newStrides[strides.length] = stride;
return newStrides;
}
private static long[] EMPTY_STRIDES = new long[0];
/**
* This class provides an immutable implementation for the {@code PathElement} interface. A path element implementation
* is simply a pointer to one of the selector methods provided by the {@code LayoutPath} class.
*/
public static class PathElementImpl implements MemoryLayout.PathElement, UnaryOperator<LayoutPath> {
final UnaryOperator<LayoutPath> pathOp;
public PathElementImpl(UnaryOperator<LayoutPath> pathOp) {
this.pathOp = pathOp;
}
@Override
public LayoutPath apply(LayoutPath layoutPath) {
return pathOp.apply(layoutPath);
}
}
}

View File

@ -0,0 +1,117 @@
/*
* 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. 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.internal.access.foreign.MemoryAddressProxy;
import jdk.internal.misc.Unsafe;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import java.lang.ref.Reference;
import java.util.Objects;
/**
* 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, MemoryAddressProxy {
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
private final MemorySegmentImpl segment;
private final long offset;
public MemoryAddressImpl(MemorySegmentImpl segment, long offset) {
this.segment = Objects.requireNonNull(segment);
this.offset = offset;
}
public static void copy(MemoryAddressImpl src, MemoryAddressImpl dst, long size) {
src.checkAccess(0, size, true);
dst.checkAccess(0, size, false);
//check disjoint
long offsetSrc = src.unsafeGetOffset();
long offsetDst = dst.unsafeGetOffset();
Object baseSrc = src.unsafeGetBase();
Object baseDst = dst.unsafeGetBase();
UNSAFE.copyMemory(baseSrc, offsetSrc, baseDst, offsetDst, size);
}
// MemoryAddress methods
@Override
public long offset() {
return offset;
}
@Override
public MemorySegment segment() {
return segment;
}
@Override
public MemoryAddress offset(long bytes) {
return new MemoryAddressImpl(segment, offset + bytes);
}
// MemoryAddressProxy methods
public void checkAccess(long offset, long length, boolean readOnly) {
segment.checkRange(this.offset + offset, length, !readOnly);
}
public long unsafeGetOffset() {
return segment.min + offset;
}
public Object unsafeGetBase() {
return segment.base();
}
// Object methods
@Override
public int hashCode() {
return Objects.hash(unsafeGetBase(), unsafeGetOffset());
}
@Override
public boolean equals(Object that) {
if (that instanceof MemoryAddressImpl) {
MemoryAddressImpl addr = (MemoryAddressImpl)that;
return Objects.equals(unsafeGetBase(), ((MemoryAddressImpl) that).unsafeGetBase()) &&
unsafeGetOffset() == addr.unsafeGetOffset();
} else {
return false;
}
}
@Override
public String toString() {
return "MemoryAddress{ region: " + segment + " offset=0x" + Long.toHexString(offset) + " }";
}
}

View File

@ -0,0 +1,119 @@
/*
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package jdk.internal.foreign;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
/**
* This class manages the temporal bounds associated with a memory segment. A scope has a liveness bit, which is updated
* when the scope is closed (this operation is triggered by {@link MemorySegmentImpl#close()}). Furthermore, a scope is
* associated with an <em>atomic</em> counter which can be incremented (upon calling the {@link #acquire()} method),
* and is decremented (when a previously acquired segment is later closed).
*/
public final class MemoryScope {
//reference to keep hold onto
final Object ref;
int activeCount = UNACQUIRED;
final static VarHandle COUNT_HANDLE;
static {
try {
COUNT_HANDLE = MethodHandles.lookup().findVarHandle(MemoryScope.class, "activeCount", int.class);
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
final static int UNACQUIRED = 0;
final static int CLOSED = -1;
final static int MAX_ACQUIRE = Integer.MAX_VALUE;
final Runnable cleanupAction;
public MemoryScope(Object ref, Runnable cleanupAction) {
this.ref = ref;
this.cleanupAction = cleanupAction;
}
/**
* This method performs a full, thread-safe liveness check; can be used outside confinement thread.
*/
final boolean isAliveThreadSafe() {
return ((int)COUNT_HANDLE.getVolatile(this)) != CLOSED;
}
/**
* This method performs a quick liveness check; must be called from the confinement thread.
*/
final void checkAliveConfined() {
if (activeCount == CLOSED) {
throw new IllegalStateException("Segment is not alive");
}
}
MemoryScope acquire() {
int value;
do {
value = (int)COUNT_HANDLE.getVolatile(this);
if (value == CLOSED) {
//segment is not alive!
throw new IllegalStateException("Segment is not alive");
} else if (value == MAX_ACQUIRE) {
//overflow
throw new IllegalStateException("Segment acquire limit exceeded");
}
} while (!COUNT_HANDLE.compareAndSet(this, value, value + 1));
return new MemoryScope(ref, this::release);
}
private void release() {
int value;
do {
value = (int)COUNT_HANDLE.getVolatile(this);
if (value <= UNACQUIRED) {
//cannot get here - we can't close segment twice
throw new IllegalStateException();
}
} while (!COUNT_HANDLE.compareAndSet(this, value, value - 1));
}
void close() {
if (!COUNT_HANDLE.compareAndSet(this, UNACQUIRED, CLOSED)) {
//first check if already closed...
checkAliveConfined();
//...if not, then we have acquired views that are still active
throw new IllegalStateException("Cannot close a segment that has active acquired views");
}
if (cleanupAction != null) {
cleanupAction.run();
}
}
}

View File

@ -0,0 +1,209 @@
/*
* 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. 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.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.Unsafe;
import java.nio.ByteBuffer;
import java.util.Objects;
import java.util.Random;
/**
* This class provides an immutable implementation for the {@code MemorySegment} interface. This class contains information
* about the segment's spatial and temporal bounds, as well as the addressing coordinates (base + offset) which allows
* unsafe access; each memory segment implementation is associated with an owner thread which is set at creation time.
* Access to certain sensitive operations on the memory segment will fail with {@code IllegalStateException} if the
* segment is either in an invalid state (e.g. it has already been closed) or if access occurs from a thread other
* than the owner thread. See {@link MemoryScope} for more details on management of temporal bounds.
*/
public final class MemorySegmentImpl implements MemorySegment, MemorySegmentProxy {
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
private static final int BYTE_ARR_BASE = UNSAFE.arrayBaseOffset(byte[].class);
final long length;
final int mask;
final long min;
final Object base;
final Thread owner;
final MemoryScope scope;
final static int READ_ONLY = 1;
final static long NONCE = new Random().nextLong();
public MemorySegmentImpl(long min, Object base, long length, int mask, Thread owner, MemoryScope scope) {
this.length = length;
this.mask = mask;
this.min = min;
this.base = base;
this.owner = owner;
this.scope = scope;
}
// MemorySegment methods
@Override
public final MemorySegmentImpl asSlice(long offset, long newSize) {
checkValidState();
checkBounds(offset, newSize);
return new MemorySegmentImpl(min + offset, base, newSize, mask, owner, scope);
}
@Override
public MemorySegment acquire() {
return new MemorySegmentImpl(min, base, length, mask, Thread.currentThread(), scope.acquire());
}
@Override
public final MemoryAddress baseAddress() {
return new MemoryAddressImpl(this, 0);
}
@Override
public final long byteSize() {
return length;
}
@Override
public final MemorySegmentImpl asReadOnly() {
checkValidState();
return new MemorySegmentImpl(min, base, length, mask | READ_ONLY, owner, scope);
}
@Override
public final boolean isAlive() {
return scope.isAliveThreadSafe();
}
@Override
public final boolean isReadOnly() {
return isSet(READ_ONLY);
}
@Override
public boolean isAccessible() {
return owner == Thread.currentThread();
}
@Override
public final void close() {
checkValidState();
scope.close();
}
@Override
public ByteBuffer asByteBuffer() {
checkValidState();
checkIntSize("ByteBuffer");
JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
ByteBuffer _bb;
if (base() != null) {
if (!(base() instanceof byte[])) {
throw new UnsupportedOperationException("Not an address to an heap-allocated byte array");
}
_bb = nioAccess.newHeapByteBuffer((byte[]) base(), (int)min - BYTE_ARR_BASE, (int) length, this);
} else {
_bb = nioAccess.newDirectByteBuffer(min, (int) length, null, this);
}
if (isReadOnly()) {
//scope is IMMUTABLE - obtain a RO byte buffer
_bb = _bb.asReadOnlyBuffer();
}
return _bb;
}
@Override
public byte[] toByteArray() {
checkValidState();
checkIntSize("byte[]");
byte[] arr = new byte[(int)length];
MemorySegment arrSegment = MemorySegment.ofArray(arr);
MemoryAddress.copy(this.baseAddress(), arrSegment.baseAddress(), length);
return arr;
}
// MemorySegmentProxy methods
@Override
public final void checkValidState() {
if (owner != Thread.currentThread()) {
throw new IllegalStateException("Attempt to access segment outside owning thread");
}
scope.checkAliveConfined();
}
// Object methods
@Override
public String toString() {
return "MemorySegment{ id=0x" + Long.toHexString(id()) + " limit: " + byteSize() + " }";
}
// Helper methods
void checkRange(long offset, long length, boolean writeAccess) {
checkValidState();
if (isReadOnly() && writeAccess) {
throw new UnsupportedOperationException("Cannot write to read-only memory segment");
}
checkBounds(offset, length);
}
Object base() {
return base;
}
private boolean isSet(int mask) {
return (this.mask & mask) != 0;
}
private void checkIntSize(String typeName) {
if (length > (Integer.MAX_VALUE - 8)) { //conservative check
throw new UnsupportedOperationException(String.format("Segment is too large to wrap as %s. Size: %d", typeName, length));
}
}
private void checkBounds(long offset, long length) {
if (length < 0 ||
offset < 0 ||
offset > this.length - length) { // careful of overflow
throw new IndexOutOfBoundsException(String.format("Out of bound access on segment %s; new offset = %d; new length = %d",
this, offset, length));
}
}
private int id() {
//compute a stable and random id for this memory segment
return Math.abs(Objects.hash(base, min, NONCE));
}
}

View File

@ -0,0 +1,157 @@
/*
* 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. 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.MemorySegment;
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.foreign.UnmapperProxy;
import jdk.internal.misc.Unsafe;
import sun.nio.ch.FileChannelImpl;
import sun.security.action.GetBooleanAction;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.function.Supplier;
/**
* This class contains misc helper functions to support creation of memory segments.
*/
public final class Utils {
private static Unsafe unsafe = Unsafe.getUnsafe();
// The maximum alignment supported by malloc - typically 16 on 64-bit platforms.
private final static long MAX_ALIGN = 16;
private static final JavaNioAccess javaNioAccess = SharedSecrets.getJavaNioAccess();
private static final boolean skipZeroMemory = GetBooleanAction.privilegedGetProperty("jdk.internal.foreign.skipZeroMemory");
public static long alignUp(long n, long alignment) {
return (n + alignment - 1) & -alignment;
}
public static long bitsToBytesOrThrow(long bits, Supplier<RuntimeException> exFactory) {
if (bits % 8 == 0) {
return bits / 8;
} else {
throw exFactory.get();
}
}
// segment factories
public static MemorySegment makeNativeSegment(long bytesSize, long alignmentBytes) {
long alignedSize = bytesSize;
if (alignmentBytes > MAX_ALIGN) {
alignedSize = bytesSize + (alignmentBytes - 1);
}
long buf = unsafe.allocateMemory(alignedSize);
if (!skipZeroMemory) {
unsafe.setMemory(buf, alignedSize, (byte)0);
}
long alignedBuf = Utils.alignUp(buf, alignmentBytes);
MemoryScope scope = new MemoryScope(null, () -> unsafe.freeMemory(buf));
MemorySegment segment = new MemorySegmentImpl(buf, null, alignedSize, 0, Thread.currentThread(), scope);
if (alignedBuf != buf) {
long delta = alignedBuf - buf;
segment = segment.asSlice(delta, bytesSize);
}
return segment;
}
public static MemorySegment makeArraySegment(byte[] arr) {
return makeArraySegment(arr, arr.length, Unsafe.ARRAY_BYTE_BASE_OFFSET, Unsafe.ARRAY_BYTE_INDEX_SCALE);
}
public static MemorySegment makeArraySegment(char[] arr) {
return makeArraySegment(arr, arr.length, Unsafe.ARRAY_CHAR_BASE_OFFSET, Unsafe.ARRAY_CHAR_INDEX_SCALE);
}
public static MemorySegment makeArraySegment(short[] arr) {
return makeArraySegment(arr, arr.length, Unsafe.ARRAY_SHORT_BASE_OFFSET, Unsafe.ARRAY_SHORT_INDEX_SCALE);
}
public static MemorySegment makeArraySegment(int[] arr) {
return makeArraySegment(arr, arr.length, Unsafe.ARRAY_INT_BASE_OFFSET, Unsafe.ARRAY_INT_INDEX_SCALE);
}
public static MemorySegment makeArraySegment(float[] arr) {
return makeArraySegment(arr, arr.length, Unsafe.ARRAY_FLOAT_BASE_OFFSET, Unsafe.ARRAY_FLOAT_INDEX_SCALE);
}
public static MemorySegment makeArraySegment(long[] arr) {
return makeArraySegment(arr, arr.length, Unsafe.ARRAY_LONG_BASE_OFFSET, Unsafe.ARRAY_LONG_INDEX_SCALE);
}
public static MemorySegment makeArraySegment(double[] arr) {
return makeArraySegment(arr, arr.length, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, Unsafe.ARRAY_DOUBLE_INDEX_SCALE);
}
private static MemorySegment makeArraySegment(Object arr, int size, int base, int scale) {
MemoryScope scope = new MemoryScope(null, null);
return new MemorySegmentImpl(base, arr, size * scale, 0, Thread.currentThread(), scope);
}
public static MemorySegment makeBufferSegment(ByteBuffer bb) {
long bbAddress = javaNioAccess.getBufferAddress(bb);
Object base = javaNioAccess.getBufferBase(bb);
int pos = bb.position();
int limit = bb.limit();
MemoryScope bufferScope = new MemoryScope(bb, null);
return new MemorySegmentImpl(bbAddress + pos, base, limit - pos, 0, Thread.currentThread(), bufferScope);
}
// create and map a file into a fresh segment
public static MemorySegment makeMappedSegment(Path path, long bytesSize, FileChannel.MapMode mapMode) throws IOException {
if (bytesSize <= 0) throw new IllegalArgumentException("Requested bytes size must be > 0.");
try (FileChannelImpl channelImpl = (FileChannelImpl)FileChannel.open(path, openOptions(mapMode))) {
UnmapperProxy unmapperProxy = channelImpl.mapInternal(mapMode, 0L, bytesSize);
MemoryScope scope = new MemoryScope(null, unmapperProxy::unmap);
return new MemorySegmentImpl(unmapperProxy.address(), null, bytesSize, 0, Thread.currentThread(), scope);
}
}
private static OpenOption[] openOptions(FileChannel.MapMode mapMode) {
if (mapMode == FileChannel.MapMode.READ_ONLY) {
return new OpenOption[] { StandardOpenOption.READ };
} else if (mapMode == FileChannel.MapMode.READ_WRITE || mapMode == FileChannel.MapMode.PRIVATE) {
return new OpenOption[] { StandardOpenOption.READ, StandardOpenOption.WRITE };
} else {
throw new UnsupportedOperationException("Unsupported map mode: " + mapMode);
}
}
}

View File

@ -0,0 +1,35 @@
/*
* 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. 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.
*/
/**
* Defines the experimental foreign memory access API.
*
* {@Incubating}
*
* @moduleGraph
*/
module jdk.incubator.foreign {
exports jdk.incubator.foreign;
}

View File

@ -40,6 +40,7 @@ tier1_part2 = \
tier1_part3 = \
:jdk_math \
:jdk_svc_sanity \
:jdk_foreign \
java/nio/Buffer \
com/sun/crypto/provider/Cipher \
sun/nio/cs/ISO8859x.java
@ -332,6 +333,9 @@ jdk_svc = \
:jdk_jfr \
:svc_tools
jdk_foreign = \
java/foreign
#############################
#

View File

@ -0,0 +1,25 @@
#
# 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.
#
#
modules = jdk.incubator.foreign

View File

@ -0,0 +1,145 @@
/*
* 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 TestArrays
*/
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.SequenceLayout;
import java.lang.invoke.VarHandle;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.testng.annotations.*;
import static org.testng.Assert.*;
public class TestArrays {
static SequenceLayout bytes = MemoryLayout.ofSequence(100,
MemoryLayouts.JAVA_BYTE
);
static SequenceLayout chars = MemoryLayout.ofSequence(100,
MemoryLayouts.JAVA_CHAR
);
static SequenceLayout shorts = MemoryLayout.ofSequence(100,
MemoryLayouts.JAVA_SHORT
);
static SequenceLayout ints = MemoryLayout.ofSequence(100,
MemoryLayouts.JAVA_INT
);
static SequenceLayout floats = MemoryLayout.ofSequence(100,
MemoryLayouts.JAVA_FLOAT
);
static SequenceLayout longs = MemoryLayout.ofSequence(100,
MemoryLayouts.JAVA_LONG
);
static SequenceLayout doubles = MemoryLayout.ofSequence(100,
MemoryLayouts.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 = doubles.varHandle(long.class, PathElement.sequenceElement());
static VarHandle doubleHandle = longs.varHandle(double.class, PathElement.sequenceElement());
static void initBytes(MemoryAddress base, SequenceLayout seq, BiConsumer<MemoryAddress, Long> handleSetter) {
for (long i = 0; i < seq.elementCount().getAsLong() ; i++) {
handleSetter.accept(base, i);
}
}
static void checkBytes(MemoryAddress base, SequenceLayout layout) {
long nBytes = layout.elementCount().getAsLong() * layout.elementLayout().byteSize();
byte[] arr = base.segment().toByteArray();
for (long i = 0 ; i < nBytes ; i++) {
byte expected = (byte)byteHandle.get(base, i);
byte found = arr[(int)i];
assertEquals(expected, found);
}
}
@Test(dataProvider = "arrays")
public void testArrays(Consumer<MemoryAddress> init, SequenceLayout layout) {
try (MemorySegment segment = MemorySegment.allocateNative(layout)) {
init.accept(segment.baseAddress());
checkBytes(segment.baseAddress(), layout);
}
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testTooBigForArray() {
MemorySegment.allocateNative((long) Integer.MAX_VALUE * 2).toByteArray();
}
@Test(expectedExceptions = IllegalStateException.class)
public void testArrayFromClosedSegment() {
MemorySegment segment = MemorySegment.allocateNative(8);
segment.close();
segment.toByteArray();
}
@DataProvider(name = "arrays")
public Object[][] nativeAccessOps() {
Consumer<MemoryAddress> byteInitializer =
(base) -> initBytes(base, bytes, (addr, pos) -> byteHandle.set(addr, pos, (byte)(long)pos));
Consumer<MemoryAddress> charInitializer =
(base) -> initBytes(base, chars, (addr, pos) -> charHandle.set(addr, pos, (char)(long)pos));
Consumer<MemoryAddress> shortInitializer =
(base) -> initBytes(base, shorts, (addr, pos) -> shortHandle.set(addr, pos, (short)(long)pos));
Consumer<MemoryAddress> intInitializer =
(base) -> initBytes(base, ints, (addr, pos) -> intHandle.set(addr, pos, (int)(long)pos));
Consumer<MemoryAddress> floatInitializer =
(base) -> initBytes(base, floats, (addr, pos) -> floatHandle.set(addr, pos, (float)(long)pos));
Consumer<MemoryAddress> longInitializer =
(base) -> initBytes(base, longs, (addr, pos) -> longHandle.set(addr, pos, (long)pos));
Consumer<MemoryAddress> doubleInitializer =
(base) -> initBytes(base, doubles, (addr, pos) -> doubleHandle.set(addr, pos, (double)(long)pos));
return new Object[][]{
{byteInitializer, bytes},
{charInitializer, chars},
{shortInitializer, shorts},
{intInitializer, ints},
{floatInitializer, floats},
{longInitializer, longs},
{doubleInitializer, doubles}
};
}
}

View File

@ -0,0 +1,555 @@
/*
* 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. 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.
*/
/*
* @test
* @modules java.base/sun.nio.ch
* jdk.incubator.foreign/jdk.internal.foreign
* @run testng TestByteBuffer
*/
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.MemoryLayout.PathElement;
import jdk.incubator.foreign.SequenceLayout;
import java.io.File;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.CharBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.InvalidMarkException;
import java.nio.LongBuffer;
import java.nio.MappedByteBuffer;
import java.nio.ShortBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.StandardOpenOption;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import jdk.internal.foreign.MemoryAddressImpl;
import org.testng.annotations.*;
import sun.nio.ch.DirectBuffer;
import static org.testng.Assert.*;
public class TestByteBuffer {
static SequenceLayout tuples = MemoryLayout.ofSequence(500,
MemoryLayout.ofStruct(
MemoryLayouts.BITS_32_BE.withName("index"),
MemoryLayouts.BITS_32_BE.withName("value")
));
static SequenceLayout bytes = MemoryLayout.ofSequence(100,
MemoryLayouts.BITS_8_BE
);
static SequenceLayout chars = MemoryLayout.ofSequence(100,
MemoryLayouts.BITS_16_BE
);
static SequenceLayout shorts = MemoryLayout.ofSequence(100,
MemoryLayouts.BITS_16_BE
);
static SequenceLayout ints = MemoryLayout.ofSequence(100,
MemoryLayouts.BITS_32_BE
);
static SequenceLayout floats = MemoryLayout.ofSequence(100,
MemoryLayouts.BITS_32_BE
);
static SequenceLayout longs = MemoryLayout.ofSequence(100,
MemoryLayouts.BITS_64_BE
);
static SequenceLayout doubles = MemoryLayout.ofSequence(100,
MemoryLayouts.BITS_64_BE
);
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 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 = doubles.varHandle(long.class, PathElement.sequenceElement());
static VarHandle doubleHandle = longs.varHandle(double.class, PathElement.sequenceElement());
static void initTuples(MemoryAddress base) {
for (long i = 0; i < tuples.elementCount().getAsLong() ; i++) {
indexHandle.set(base, i, (int)i);
valueHandle.set(base, i, (float)(i / 500f));
}
}
static void checkTuples(MemoryAddress base, ByteBuffer bb) {
for (long i = 0; i < tuples.elementCount().getAsLong() ; i++) {
assertEquals(bb.getInt(), (int)indexHandle.get(base, i));
assertEquals(bb.getFloat(), (float)valueHandle.get(base, i));
}
}
static void initBytes(MemoryAddress base, SequenceLayout seq, BiConsumer<MemoryAddress, Long> handleSetter) {
for (long i = 0; i < seq.elementCount().getAsLong() ; i++) {
handleSetter.accept(base, i);
}
}
static <Z extends Buffer> void checkBytes(MemoryAddress base, SequenceLayout layout,
Function<ByteBuffer, Z> bufFactory,
BiFunction<MemoryAddress, Long, Object> handleExtractor,
Function<Z, Object> bufferExtractor) {
long nelems = layout.elementCount().getAsLong();
long elemSize = layout.elementLayout().byteSize();
for (long i = 0 ; i < nelems ; i++) {
long limit = nelems - i;
MemorySegment resizedSegment = base.segment().asSlice(i * elemSize, limit * elemSize);
ByteBuffer bb = resizedSegment.asByteBuffer();
Z z = bufFactory.apply(bb);
for (long j = i ; j < limit ; j++) {
Object handleValue = handleExtractor.apply(resizedSegment.baseAddress(), j - i);
Object bufferValue = bufferExtractor.apply(z);
if (handleValue instanceof Number) {
assertEquals(((Number)handleValue).longValue(), j);
assertEquals(((Number)bufferValue).longValue(), j);
} else {
assertEquals((long)(char)handleValue, j);
assertEquals((long)(char)bufferValue, j);
}
}
}
}
@Test
public void testOffheap() {
try (MemorySegment segment = MemorySegment.allocateNative(tuples)) {
MemoryAddress base = segment.baseAddress();
initTuples(base);
ByteBuffer bb = segment.asByteBuffer();
checkTuples(base, bb);
}
}
@Test
public void testHeap() {
byte[] arr = new byte[(int) tuples.byteSize()];
MemorySegment region = MemorySegment.ofArray(arr);
MemoryAddress base = region.baseAddress();
initTuples(base);
ByteBuffer bb = region.asByteBuffer();
checkTuples(base, bb);
}
@Test
public void testChannel() throws Throwable {
File f = new File("test.out");
assertTrue(f.createNewFile());
f.deleteOnExit();
//write to channel
try (FileChannel channel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
withMappedBuffer(channel, FileChannel.MapMode.READ_WRITE, 0, tuples.byteSize(), mbb -> {
MemorySegment segment = MemorySegment.ofByteBuffer(mbb);
MemoryAddress base = segment.baseAddress();
initTuples(base);
mbb.force();
});
}
//read from channel
try (FileChannel channel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) {
withMappedBuffer(channel, FileChannel.MapMode.READ_ONLY, 0, tuples.byteSize(), mbb -> {
MemorySegment segment = MemorySegment.ofByteBuffer(mbb);
MemoryAddress base = segment.baseAddress();
checkTuples(base, mbb);
});
}
}
@Test
public void testMappedSegment() throws Throwable {
File f = new File("test2.out");
f.createNewFile();
f.deleteOnExit();
//write to channel
try (MemorySegment segment = MemorySegment.mapFromPath(f.toPath(), tuples.byteSize(), FileChannel.MapMode.READ_WRITE)) {
MemoryAddress base = segment.baseAddress();
initTuples(base);
}
//read from channel
try (MemorySegment segment = MemorySegment.mapFromPath(f.toPath(), tuples.byteSize(), FileChannel.MapMode.READ_ONLY)) {
MemoryAddress base = segment.baseAddress();
checkTuples(base, segment.asByteBuffer());
}
}
static void withMappedBuffer(FileChannel channel, FileChannel.MapMode mode, long pos, long size, Consumer<MappedByteBuffer> action) throws Throwable {
MappedByteBuffer mbb = channel.map(mode, pos, size);
var ref = new WeakReference<>(mbb);
action.accept(mbb);
mbb = null;
//wait for it to be GCed
System.gc();
while (ref.get() != null) {
Thread.sleep(20);
}
}
@Test(dataProvider = "bufferOps")
public void testScopedBuffer(Function<ByteBuffer, Buffer> bufferFactory, Map<Method, Object[]> members) {
Buffer bb;
try (MemorySegment segment = MemorySegment.allocateNative(bytes)) {
MemoryAddress base = segment.baseAddress();
bb = bufferFactory.apply(segment.asByteBuffer());
}
//outside of scope!!
for (Map.Entry<Method, Object[]> e : members.entrySet()) {
if (!e.getKey().getName().contains("get") &&
!e.getKey().getName().contains("put")) {
//skip
return;
}
try {
e.getKey().invoke(bb, e.getValue());
assertTrue(false);
} catch (InvocationTargetException ex) {
Throwable cause = ex.getCause();
if (cause instanceof IllegalStateException) {
//all get/set buffer operation should fail because of the scope check
assertTrue(ex.getCause().getMessage().contains("not alive"));
} else {
//all other exceptions were unexpected - fail
assertTrue(false);
}
} catch (Throwable ex) {
//unexpected exception - fail
assertTrue(false);
}
}
}
@Test(dataProvider = "bufferHandleOps")
public void testScopedBufferAndVarHandle(VarHandle bufferHandle) {
ByteBuffer bb;
try (MemorySegment segment = MemorySegment.allocateNative(bytes)) {
bb = segment.asByteBuffer();
for (Map.Entry<MethodHandle, Object[]> e : varHandleMembers(bb, bufferHandle).entrySet()) {
MethodHandle handle = e.getKey().bindTo(bufferHandle)
.asSpreader(Object[].class, e.getValue().length);
try {
handle.invoke(e.getValue());
} catch (UnsupportedOperationException ex) {
//skip
} catch (Throwable ex) {
//should not fail - segment is alive!
fail();
}
}
}
for (Map.Entry<MethodHandle, Object[]> e : varHandleMembers(bb, bufferHandle).entrySet()) {
try {
MethodHandle handle = e.getKey().bindTo(bufferHandle)
.asSpreader(Object[].class, e.getValue().length);
handle.invoke(e.getValue());
fail();
} catch (IllegalStateException ex) {
assertTrue(ex.getMessage().contains("not alive"));
} catch (UnsupportedOperationException ex) {
//skip
} catch (Throwable ex) {
fail();
}
}
}
@Test(dataProvider = "bufferOps")
public void testDirectBuffer(Function<ByteBuffer, Buffer> bufferFactory, Map<Method, Object[]> members) {
try (MemorySegment segment = MemorySegment.allocateNative(bytes)) {
MemoryAddress base = segment.baseAddress();
Buffer bb = bufferFactory.apply(segment.asByteBuffer());
assertTrue(bb.isDirect());
DirectBuffer directBuffer = ((DirectBuffer)bb);
assertEquals(directBuffer.address(), ((MemoryAddressImpl)base).unsafeGetOffset());
assertTrue((directBuffer.attachment() == null) == (bb instanceof ByteBuffer));
assertTrue(directBuffer.cleaner() == null);
}
}
@Test(dataProvider="resizeOps")
public void testResizeOffheap(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
try (MemorySegment segment = MemorySegment.allocateNative(seq)) {
MemoryAddress base = segment.baseAddress();
initializer.accept(base);
checker.accept(base);
}
}
@Test(dataProvider="resizeOps")
public void testResizeHeap(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
int capacity = (int)seq.byteSize();
MemoryAddress base = MemorySegment.ofArray(new byte[capacity]).baseAddress();
initializer.accept(base);
checker.accept(base);
}
@Test(dataProvider="resizeOps")
public void testResizeBuffer(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
int capacity = (int)seq.byteSize();
MemoryAddress base = MemorySegment.ofByteBuffer(ByteBuffer.wrap(new byte[capacity])).baseAddress();
initializer.accept(base);
checker.accept(base);
}
@Test(dataProvider="resizeOps")
public void testResizeRoundtripHeap(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
int capacity = (int)seq.byteSize();
byte[] arr = new byte[capacity];
MemorySegment segment = MemorySegment.ofArray(arr);
MemoryAddress first = segment.baseAddress();
initializer.accept(first);
MemoryAddress second = MemorySegment.ofByteBuffer(segment.asByteBuffer()).baseAddress();
checker.accept(second);
}
@Test(dataProvider="resizeOps")
public void testResizeRoundtripNative(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
try (MemorySegment segment = MemorySegment.allocateNative(seq)) {
MemoryAddress first = segment.baseAddress();
initializer.accept(first);
MemoryAddress second = MemorySegment.ofByteBuffer(segment.asByteBuffer()).baseAddress();
checker.accept(second);
}
}
@Test(expectedExceptions = IllegalStateException.class)
public void testBufferOnClosedScope() {
MemorySegment leaked;
try (MemorySegment segment = MemorySegment.allocateNative(bytes)) {
leaked = segment;
}
leaked.asByteBuffer();
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testTooBigForByteBuffer() {
MemorySegment.allocateNative((long) Integer.MAX_VALUE * 2).asByteBuffer();
}
@Test(dataProvider="resizeOps")
public void testCopyHeapToNative(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
int bytes = (int)seq.byteSize();
try (MemorySegment nativeArray = MemorySegment.allocateNative(bytes);
MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes])) {
initializer.accept(heapArray.baseAddress());
MemoryAddress.copy(heapArray.baseAddress(), nativeArray.baseAddress(), bytes);
checker.accept(nativeArray.baseAddress());
}
}
@Test(dataProvider="resizeOps")
public void testCopyNativeToHeap(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
int bytes = (int)seq.byteSize();
try (MemorySegment nativeArray = MemorySegment.allocateNative(seq);
MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes])) {
initializer.accept(nativeArray.baseAddress());
MemoryAddress.copy(nativeArray.baseAddress(), heapArray.baseAddress(), bytes);
checker.accept(heapArray.baseAddress());
}
}
@DataProvider(name = "bufferOps")
public static Object[][] bufferOps() throws Throwable {
return new Object[][]{
{ (Function<ByteBuffer, Buffer>) bb -> bb, bufferMembers(ByteBuffer.class)},
{ (Function<ByteBuffer, Buffer>) ByteBuffer::asCharBuffer, bufferMembers(CharBuffer.class)},
{ (Function<ByteBuffer, Buffer>) ByteBuffer::asShortBuffer, bufferMembers(ShortBuffer.class)},
{ (Function<ByteBuffer, Buffer>) ByteBuffer::asIntBuffer, bufferMembers(IntBuffer.class)},
{ (Function<ByteBuffer, Buffer>) ByteBuffer::asFloatBuffer, bufferMembers(FloatBuffer.class)},
{ (Function<ByteBuffer, Buffer>) ByteBuffer::asLongBuffer, bufferMembers(LongBuffer.class)},
{ (Function<ByteBuffer, Buffer>) ByteBuffer::asDoubleBuffer, bufferMembers(DoubleBuffer.class)},
};
}
static Map<Method, Object[]> bufferMembers(Class<?> bufferClass) {
Map<Method, Object[]> members = new HashMap<>();
for (Method m : bufferClass.getMethods()) {
//skip statics and method declared in j.l.Object
if (m.getDeclaringClass().equals(Object.class) ||
(m.getModifiers() & Modifier.STATIC) != 0) continue;
Object[] args = Stream.of(m.getParameterTypes())
.map(TestByteBuffer::defaultValue)
.toArray();
members.put(m, args);
}
return members;
}
@DataProvider(name = "bufferHandleOps")
public static Object[][] bufferHandleOps() throws Throwable {
return new Object[][]{
{ MethodHandles.byteBufferViewVarHandle(char[].class, ByteOrder.nativeOrder()) },
{ MethodHandles.byteBufferViewVarHandle(short[].class, ByteOrder.nativeOrder()) },
{ MethodHandles.byteBufferViewVarHandle(int[].class, ByteOrder.nativeOrder()) },
{ MethodHandles.byteBufferViewVarHandle(long[].class, ByteOrder.nativeOrder()) },
{ MethodHandles.byteBufferViewVarHandle(float[].class, ByteOrder.nativeOrder()) },
{ MethodHandles.byteBufferViewVarHandle(double[].class, ByteOrder.nativeOrder()) }
};
}
static Map<MethodHandle, Object[]> varHandleMembers(ByteBuffer bb, VarHandle handle) {
Map<MethodHandle, Object[]> members = new HashMap<>();
for (VarHandle.AccessMode mode : VarHandle.AccessMode.values()) {
Class<?>[] params = handle.accessModeType(mode).parameterArray();
Object[] args = Stream.concat(Stream.of(bb), Stream.of(params).skip(1)
.map(TestByteBuffer::defaultValue))
.toArray();
try {
members.put(MethodHandles.varHandleInvoker(mode, handle.accessModeType(mode)), args);
} catch (Throwable ex) {
throw new AssertionError(ex);
}
}
return members;
}
@DataProvider(name = "resizeOps")
public Object[][] resizeOps() {
Consumer<MemoryAddress> byteInitializer =
(base) -> initBytes(base, bytes, (addr, pos) -> byteHandle.set(addr, pos, (byte)(long)pos));
Consumer<MemoryAddress> charInitializer =
(base) -> initBytes(base, chars, (addr, pos) -> charHandle.set(addr, pos, (char)(long)pos));
Consumer<MemoryAddress> shortInitializer =
(base) -> initBytes(base, shorts, (addr, pos) -> shortHandle.set(addr, pos, (short)(long)pos));
Consumer<MemoryAddress> intInitializer =
(base) -> initBytes(base, ints, (addr, pos) -> intHandle.set(addr, pos, (int)(long)pos));
Consumer<MemoryAddress> floatInitializer =
(base) -> initBytes(base, floats, (addr, pos) -> floatHandle.set(addr, pos, (float)(long)pos));
Consumer<MemoryAddress> longInitializer =
(base) -> initBytes(base, longs, (addr, pos) -> longHandle.set(addr, pos, (long)pos));
Consumer<MemoryAddress> doubleInitializer =
(base) -> initBytes(base, doubles, (addr, pos) -> doubleHandle.set(addr, pos, (double)(long)pos));
Consumer<MemoryAddress> byteChecker =
(base) -> checkBytes(base, bytes, Function.identity(), byteHandle::get, ByteBuffer::get);
Consumer<MemoryAddress> charChecker =
(base) -> checkBytes(base, chars, ByteBuffer::asCharBuffer, charHandle::get, CharBuffer::get);
Consumer<MemoryAddress> shortChecker =
(base) -> checkBytes(base, shorts, ByteBuffer::asShortBuffer, shortHandle::get, ShortBuffer::get);
Consumer<MemoryAddress> intChecker =
(base) -> checkBytes(base, ints, ByteBuffer::asIntBuffer, intHandle::get, IntBuffer::get);
Consumer<MemoryAddress> floatChecker =
(base) -> checkBytes(base, floats, ByteBuffer::asFloatBuffer, floatHandle::get, FloatBuffer::get);
Consumer<MemoryAddress> longChecker =
(base) -> checkBytes(base, longs, ByteBuffer::asLongBuffer, longHandle::get, LongBuffer::get);
Consumer<MemoryAddress> doubleChecker =
(base) -> checkBytes(base, doubles, ByteBuffer::asDoubleBuffer, doubleHandle::get, DoubleBuffer::get);
return new Object[][]{
{byteChecker, byteInitializer, bytes},
{charChecker, charInitializer, chars},
{shortChecker, shortInitializer, shorts},
{intChecker, intInitializer, ints},
{floatChecker, floatInitializer, floats},
{longChecker, longInitializer, longs},
{doubleChecker, doubleInitializer, doubles}
};
}
static Object defaultValue(Class<?> c) {
if (c.isPrimitive()) {
if (c == char.class) {
return (char)0;
} else if (c == boolean.class) {
return false;
} else if (c == byte.class) {
return (byte)0;
} else if (c == short.class) {
return (short)0;
} else if (c == int.class) {
return 0;
} else if (c == long.class) {
return 0L;
} else if (c == float.class) {
return 0f;
} else if (c == double.class) {
return 0d;
} else {
throw new IllegalStateException();
}
} else if (c.isArray()) {
if (c == char[].class) {
return new char[1];
} else if (c == boolean[].class) {
return new boolean[1];
} else if (c == byte[].class) {
return new byte[1];
} else if (c == short[].class) {
return new short[1];
} else if (c == int[].class) {
return new int[1];
} else if (c == long[].class) {
return new long[1];
} else if (c == float[].class) {
return new float[1];
} else if (c == double[].class) {
return new double[1];
} else {
throw new IllegalStateException();
}
} else {
return null;
}
}
}

View File

@ -0,0 +1,96 @@
/*
* 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 TestLayoutConstants
*/
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemoryLayout;
import java.lang.invoke.MethodHandles;
import org.testng.annotations.*;
import static org.testng.Assert.*;
public class TestLayoutConstants {
@Test(dataProvider = "layouts")
public void testDescribeResolve(MemoryLayout expected) {
try {
MemoryLayout actual = expected.describeConstable().get()
.resolveConstantDesc(MethodHandles.lookup());
assertEquals(actual, expected);
} catch (ReflectiveOperationException ex) {
throw new AssertionError(ex);
}
}
@DataProvider(name = "layouts")
public Object[][] createLayouts() {
return new Object[][] {
//padding
{ MemoryLayouts.PAD_32 },
{ MemoryLayout.ofSequence(MemoryLayouts.PAD_32) },
{ MemoryLayout.ofSequence(5, MemoryLayouts.PAD_32) },
{ MemoryLayout.ofStruct(MemoryLayouts.PAD_32, MemoryLayouts.PAD_32) },
{ MemoryLayout.ofUnion(MemoryLayouts.PAD_32, MemoryLayouts.PAD_32) },
//values, big endian
{ MemoryLayouts.BITS_32_BE },
{ MemoryLayout.ofStruct(
MemoryLayouts.BITS_32_BE,
MemoryLayouts.BITS_32_BE) },
{ MemoryLayout.ofUnion(
MemoryLayouts.BITS_32_BE,
MemoryLayouts.BITS_32_BE) },
//values, little endian
{ MemoryLayouts.BITS_32_LE },
{ MemoryLayout.ofStruct(
MemoryLayouts.BITS_32_LE,
MemoryLayouts.BITS_32_LE) },
{ MemoryLayout.ofUnion(
MemoryLayouts.BITS_32_LE,
MemoryLayouts.BITS_32_LE) },
//deeply nested
{ MemoryLayout.ofStruct(
MemoryLayouts.PAD_16,
MemoryLayout.ofStruct(
MemoryLayouts.PAD_8,
MemoryLayouts.BITS_32_BE)) },
{ MemoryLayout.ofUnion(
MemoryLayouts.PAD_16,
MemoryLayout.ofStruct(
MemoryLayouts.PAD_8,
MemoryLayouts.BITS_32_BE)) },
{ MemoryLayout.ofSequence(
MemoryLayout.ofStruct(
MemoryLayouts.PAD_8,
MemoryLayouts.BITS_32_BE)) },
{ MemoryLayout.ofSequence(5,
MemoryLayout.ofStruct(
MemoryLayouts.PAD_8,
MemoryLayouts.BITS_32_BE)) },
};
}
}

View File

@ -0,0 +1,137 @@
/*
* 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 TestLayoutPaths
*/
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.SequenceLayout;
import org.testng.annotations.*;
import static org.testng.Assert.*;
public class TestLayoutPaths {
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadSelectFromSeq() {
SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT);
seq.offset(PathElement.groupElement("foo"));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadSelectFromStruct() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
g.offset(PathElement.sequenceElement());
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadSelectFromValue() {
SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT);
seq.offset(PathElement.sequenceElement(), PathElement.sequenceElement());
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testUnknownStructField() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
g.offset(PathElement.groupElement("foo"));
}
@Test(expectedExceptions = NullPointerException.class)
public void testNullGroupElementName() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
g.offset(PathElement.groupElement(null));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testOutOfBoundsSeqIndex() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.offset(PathElement.sequenceElement(6));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testNegativeSeqIndex() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.offset(PathElement.sequenceElement(-2));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testOutOfBoundsSeqRange() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.offset(PathElement.sequenceElement(6, 2));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testNegativeSeqRange() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.offset(PathElement.sequenceElement(-2, 2));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testIncompleteAccess() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT));
seq.varHandle(int.class, PathElement.sequenceElement());
}
@Test
public void testBadContainerAlign() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT.withBitAlignment(16).withName("foo")).withBitAlignment(8);
try {
g.offset(PathElement.groupElement("foo"));
} catch (Throwable ex) {
throw new AssertionError(ex); // should be ok!
}
try {
g.varHandle(int.class, PathElement.groupElement("foo")); //ok
assertTrue(false); //should fail!
} catch (UnsupportedOperationException ex) {
//ok
} catch (Throwable ex) {
throw new AssertionError(ex); //should fail!
}
}
@Test
public void testBadAlignOffset() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.PAD_8, MemoryLayouts.JAVA_INT.withBitAlignment(16).withName("foo"));
try {
g.offset(PathElement.groupElement("foo"));
} catch (Throwable ex) {
throw new AssertionError(ex); // should be ok!
}
try {
g.varHandle(int.class, PathElement.groupElement("foo")); //ok
assertTrue(false); //should fail!
} catch (UnsupportedOperationException ex) {
//ok
} catch (Throwable ex) {
throw new AssertionError(ex); //should fail!
}
}
}

View File

@ -0,0 +1,289 @@
/*
* 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 TestLayouts
*/
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemoryLayout;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
import java.util.function.LongFunction;
import jdk.incubator.foreign.MemorySegment;
import org.testng.annotations.*;
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);
}
@Test
public void testVLAInStruct() {
MemoryLayout layout = MemoryLayout.ofStruct(
MemoryLayouts.JAVA_INT.withName("size"),
MemoryLayout.ofPaddingBits(32),
MemoryLayout.ofSequence(MemoryLayouts.JAVA_DOUBLE).withName("arr"));
VarHandle size_handle = layout.varHandle(int.class, MemoryLayout.PathElement.groupElement("size"));
VarHandle array_elem_handle = layout.varHandle(double.class,
MemoryLayout.PathElement.groupElement("arr"),
MemoryLayout.PathElement.sequenceElement());
try (MemorySegment segment = MemorySegment.allocateNative(8 + 8 * 4)) {
size_handle.set(segment.baseAddress(), 4);
for (int i = 0 ; i < 4 ; i++) {
array_elem_handle.set(segment.baseAddress(), i, (double)i);
}
//check
assertEquals(4, (int)size_handle.get(segment.baseAddress()));
for (int i = 0 ; i < 4 ; i++) {
assertEquals((double)i, (double)array_elem_handle.get(segment.baseAddress(), i));
}
}
}
@Test
public void testVLAInSequence() {
MemoryLayout layout = MemoryLayout.ofStruct(
MemoryLayouts.JAVA_INT.withName("size"),
MemoryLayout.ofPaddingBits(32),
MemoryLayout.ofSequence(1, MemoryLayout.ofSequence(MemoryLayouts.JAVA_DOUBLE)).withName("arr"));
VarHandle size_handle = layout.varHandle(int.class, MemoryLayout.PathElement.groupElement("size"));
VarHandle array_elem_handle = layout.varHandle(double.class,
MemoryLayout.PathElement.groupElement("arr"),
MemoryLayout.PathElement.sequenceElement(0),
MemoryLayout.PathElement.sequenceElement());
try (MemorySegment segment = MemorySegment.allocateNative(8 + 8 * 4)) {
size_handle.set(segment.baseAddress(), 4);
for (int i = 0 ; i < 4 ; i++) {
array_elem_handle.set(segment.baseAddress(), i, (double)i);
}
//check
assertEquals(4, (int)size_handle.get(segment.baseAddress()));
for (int i = 0 ; i < 4 ; i++) {
assertEquals((double)i, (double)array_elem_handle.get(segment.baseAddress(), i));
}
}
}
@Test
public void testIndexedSequencePath() {
MemoryLayout seq = MemoryLayout.ofSequence(10, MemoryLayouts.JAVA_INT);
try (MemorySegment segment = MemorySegment.allocateNative(seq)) {
VarHandle indexHandle = seq.varHandle(int.class, MemoryLayout.PathElement.sequenceElement());
// init segment
for (int i = 0 ; i < 10 ; i++) {
indexHandle.set(segment.baseAddress(), (long)i, i);
}
//check statically indexed handles
for (int i = 0 ; i < 10 ; i++) {
VarHandle preindexHandle = seq.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(i));
int expected = (int)indexHandle.get(segment.baseAddress(), (long)i);
int found = (int)preindexHandle.get(segment.baseAddress());
assertEquals(expected, found);
}
}
}
@Test(dataProvider = "unboundLayouts", expectedExceptions = UnsupportedOperationException.class)
public void testUnboundSize(MemoryLayout layout, long align) {
layout.bitSize();
}
@Test(dataProvider = "unboundLayouts")
public void testUnboundAlignment(MemoryLayout layout, long align) {
assertEquals(align, layout.bitAlignment());
}
@Test(dataProvider = "unboundLayouts")
public void testUnboundEquals(MemoryLayout layout, long align) {
assertTrue(layout.equals(layout));
}
@Test(dataProvider = "unboundLayouts")
public void testUnboundHash(MemoryLayout layout, long align) {
layout.hashCode();
}
@Test
public void testEmptyGroup() {
MemoryLayout struct = MemoryLayout.ofStruct();
assertEquals(struct.bitSize(), 0);
assertEquals(struct.bitAlignment(), 1);
MemoryLayout union = MemoryLayout.ofUnion();
assertEquals(union.bitSize(), 0);
assertEquals(union.bitAlignment(), 1);
}
@Test
public void testStructSizeAndAlign() {
MemoryLayout struct = MemoryLayout.ofStruct(
MemoryLayout.ofPaddingBits(8),
MemoryLayouts.JAVA_BYTE,
MemoryLayouts.JAVA_CHAR,
MemoryLayouts.JAVA_INT,
MemoryLayouts.JAVA_LONG
);
assertEquals(struct.byteSize(), 1 + 1 + 2 + 4 + 8);
assertEquals(struct.byteAlignment(), 8);
}
@Test
public void testUnionSizeAndAlign() {
MemoryLayout struct = MemoryLayout.ofUnion(
MemoryLayouts.JAVA_BYTE,
MemoryLayouts.JAVA_CHAR,
MemoryLayouts.JAVA_INT,
MemoryLayouts.JAVA_LONG
);
assertEquals(struct.byteSize(), 8);
assertEquals(struct.byteAlignment(), 8);
}
@Test(dataProvider="layoutsAndAlignments")
public void testAlignmentString(MemoryLayout layout, long bitAlign) {
long[] alignments = { 8, 16, 32, 64, 128 };
for (long a : alignments) {
assertFalse(layout.toString().contains("%"));
assertEquals(layout.withBitAlignment(a).toString().contains("%"), a != bitAlign);
}
}
@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() {
return new Object[][] {
{ MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT), 32 },
{ MemoryLayout.ofSequence(MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT)), 32 },
{ MemoryLayout.ofSequence(4, MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT)), 32 },
{ MemoryLayout.ofStruct(MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT)), 32 },
{ MemoryLayout.ofStruct(MemoryLayout.ofSequence(MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT))), 32 },
{ MemoryLayout.ofStruct(MemoryLayout.ofSequence(4, MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT))), 32 },
{ MemoryLayout.ofUnion(MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT)), 32 },
{ MemoryLayout.ofUnion(MemoryLayout.ofSequence(MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT))), 32 },
{ MemoryLayout.ofUnion(MemoryLayout.ofSequence(4, MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT))), 32 },
};
}
@DataProvider(name = "badAlignments")
public Object[][] layoutsAndBadAlignments() {
LayoutKind[] layoutKinds = LayoutKind.values();
Object[][] values = new Object[layoutKinds.length * 2][2];
for (int i = 0; i < layoutKinds.length ; i++) {
values[i * 2] = new Object[] { layoutKinds[i].layout, 3 }; // smaller than 8
values[(i * 2) + 1] = new Object[] { layoutKinds[i].layout, 18 }; // not a power of 2
}
return values;
}
enum SizedLayoutFactory {
VALUE_LE(size -> MemoryLayout.ofValueBits(size, ByteOrder.LITTLE_ENDIAN)),
VALUE_BE(size -> MemoryLayout.ofValueBits(size, ByteOrder.BIG_ENDIAN)),
PADDING(MemoryLayout::ofPaddingBits),
SEQUENCE(size -> MemoryLayout.ofSequence(size, MemoryLayouts.PAD_8));
private final LongFunction<MemoryLayout> factory;
SizedLayoutFactory(LongFunction<MemoryLayout> factory) {
this.factory = factory;
}
MemoryLayout make(long size) {
return factory.apply(size);
}
}
enum LayoutKind {
VALUE_LE(MemoryLayouts.BITS_8_LE),
VALUE_BE(MemoryLayouts.BITS_8_BE),
PADDING(MemoryLayouts.PAD_8),
SEQUENCE(MemoryLayout.ofSequence(1, MemoryLayouts.PAD_8)),
STRUCT(MemoryLayout.ofStruct(MemoryLayouts.PAD_8, MemoryLayouts.PAD_8)),
UNION(MemoryLayout.ofUnion(MemoryLayouts.PAD_8, MemoryLayouts.PAD_8));
final MemoryLayout layout;
LayoutKind(MemoryLayout layout) {
this.layout = layout;
}
}
@DataProvider(name = "layoutsAndAlignments")
public Object[][] layoutsAndAlignments() {
MemoryLayout[] basicLayouts = {
MemoryLayouts.JAVA_BYTE,
MemoryLayouts.JAVA_CHAR,
MemoryLayouts.JAVA_SHORT,
MemoryLayouts.JAVA_INT,
MemoryLayouts.JAVA_FLOAT,
MemoryLayouts.JAVA_LONG,
MemoryLayouts.JAVA_DOUBLE,
};
Object[][] layoutsAndAlignments = new Object[basicLayouts.length * 5][];
int i = 0;
//add basic layouts
for (MemoryLayout l : basicLayouts) {
layoutsAndAlignments[i++] = new Object[] { l, l.bitAlignment() };
}
//add basic layouts wrapped in a sequence with unspecified size
for (MemoryLayout l : basicLayouts) {
layoutsAndAlignments[i++] = new Object[] { MemoryLayout.ofSequence(l), l.bitAlignment() };
}
//add basic layouts wrapped in a sequence with given size
for (MemoryLayout l : basicLayouts) {
layoutsAndAlignments[i++] = new Object[] { MemoryLayout.ofSequence(4, l), l.bitAlignment() };
}
//add basic layouts wrapped in a struct
for (MemoryLayout l : basicLayouts) {
layoutsAndAlignments[i++] = new Object[] { MemoryLayout.ofStruct(l), l.bitAlignment() };
}
//add basic layouts wrapped in a union
for (MemoryLayout l : basicLayouts) {
layoutsAndAlignments[i++] = new Object[] { MemoryLayout.ofUnion(l), l.bitAlignment() };
}
return layoutsAndAlignments;
}
}

View File

@ -0,0 +1,459 @@
/*
* 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 -Xverify:all TestMemoryAccess
*/
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.SequenceLayout;
import jdk.incubator.foreign.ValueLayout;
import jdk.incubator.foreign.MemoryAddress;
import java.lang.invoke.VarHandle;
import java.util.function.Function;
import org.testng.annotations.*;
import static org.testng.Assert.*;
public class TestMemoryAccess {
@Test(dataProvider = "elements")
public void testAccess(Function<MemorySegment, MemorySegment> viewFactory, ValueLayout elemLayout, Class<?> carrier, Checker checker) {
ValueLayout layout = elemLayout.withName("elem");
testAccessInternal(viewFactory, layout, layout.varHandle(carrier), checker);
}
@Test(dataProvider = "elements")
public void testPaddedAccessByName(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, Checker checker) {
GroupLayout layout = MemoryLayout.ofStruct(MemoryLayout.ofPaddingBits(elemLayout.bitSize()), elemLayout.withName("elem"));
testAccessInternal(viewFactory, layout, layout.varHandle(carrier, PathElement.groupElement("elem")), checker);
}
@Test(dataProvider = "elements")
public void testPaddedAccessByIndexSeq(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, Checker checker) {
SequenceLayout layout = MemoryLayout.ofSequence(2, elemLayout);
testAccessInternal(viewFactory, layout, layout.varHandle(carrier, PathElement.sequenceElement(1)), checker);
}
@Test(dataProvider = "arrayElements")
public void testArrayAccess(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, ArrayChecker checker) {
SequenceLayout seq = MemoryLayout.ofSequence(10, elemLayout.withName("elem"));
testArrayAccessInternal(viewFactory, seq, seq.varHandle(carrier, PathElement.sequenceElement()), checker);
}
@Test(dataProvider = "arrayElements")
public void testPaddedArrayAccessByName(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, ArrayChecker checker) {
SequenceLayout seq = MemoryLayout.ofSequence(10, MemoryLayout.ofStruct(MemoryLayout.ofPaddingBits(elemLayout.bitSize()), elemLayout.withName("elem")));
testArrayAccessInternal(viewFactory, seq, seq.varHandle(carrier, MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("elem")), checker);
}
@Test(dataProvider = "arrayElements")
public void testPaddedArrayAccessByIndexSeq(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, ArrayChecker checker) {
SequenceLayout seq = MemoryLayout.ofSequence(10, MemoryLayout.ofSequence(2, elemLayout));
testArrayAccessInternal(viewFactory, seq, seq.varHandle(carrier, PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement(1)), checker);
}
private void testAccessInternal(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout layout, VarHandle handle, Checker checker) {
MemoryAddress outer_address;
try (MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(layout))) {
MemoryAddress addr = segment.baseAddress();
try {
checker.check(handle, addr);
if (segment.isReadOnly()) {
throw new AssertionError(); //not ok, memory should be immutable
}
} catch (UnsupportedOperationException ex) {
if (!segment.isReadOnly()) {
throw new AssertionError(); //we should not have failed!
}
return;
}
try {
checker.check(handle, addr.offset(layout.byteSize()));
throw new AssertionError(); //not ok, out of bounds
} catch (IndexOutOfBoundsException ex) {
//ok, should fail (out of bounds)
}
outer_address = addr; //leak!
}
try {
checker.check(handle, outer_address);
throw new AssertionError(); //not ok, scope is closed
} catch (IllegalStateException ex) {
//ok, should fail (scope is closed)
}
}
private void testArrayAccessInternal(Function<MemorySegment, MemorySegment> viewFactory, SequenceLayout seq, VarHandle handle, ArrayChecker checker) {
MemoryAddress outer_address;
try (MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(seq))) {
MemoryAddress addr = segment.baseAddress();
try {
for (int i = 0; i < seq.elementCount().getAsLong(); i++) {
checker.check(handle, addr, i);
}
if (segment.isReadOnly()) {
throw new AssertionError(); //not ok, memory should be immutable
}
} catch (UnsupportedOperationException ex) {
if (!segment.isReadOnly()) {
throw new AssertionError(); //we should not have failed!
}
return;
}
try {
checker.check(handle, addr, seq.elementCount().getAsLong());
throw new AssertionError(); //not ok, out of bounds
} catch (IndexOutOfBoundsException ex) {
//ok, should fail (out of bounds)
}
outer_address = addr; //leak!
}
try {
checker.check(handle, outer_address, 0);
throw new AssertionError(); //not ok, scope is closed
} catch (IllegalStateException ex) {
//ok, should fail (scope is closed)
}
}
@Test(dataProvider = "matrixElements")
public void testMatrixAccess(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, MatrixChecker checker) {
SequenceLayout seq = MemoryLayout.ofSequence(20,
MemoryLayout.ofSequence(10, elemLayout.withName("elem")));
testMatrixAccessInternal(viewFactory, seq, seq.varHandle(carrier,
PathElement.sequenceElement(), PathElement.sequenceElement()), checker);
}
@Test(dataProvider = "matrixElements")
public void testPaddedMatrixAccessByName(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, MatrixChecker checker) {
SequenceLayout seq = MemoryLayout.ofSequence(20,
MemoryLayout.ofSequence(10, MemoryLayout.ofStruct(MemoryLayout.ofPaddingBits(elemLayout.bitSize()), elemLayout.withName("elem"))));
testMatrixAccessInternal(viewFactory, seq,
seq.varHandle(carrier,
PathElement.sequenceElement(), PathElement.sequenceElement(), PathElement.groupElement("elem")),
checker);
}
@Test(dataProvider = "matrixElements")
public void testPaddedMatrixAccessByIndexSeq(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, MatrixChecker checker) {
SequenceLayout seq = MemoryLayout.ofSequence(20,
MemoryLayout.ofSequence(10, MemoryLayout.ofSequence(2, elemLayout)));
testMatrixAccessInternal(viewFactory, seq,
seq.varHandle(carrier,
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) {
MemoryAddress outer_address;
try (MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(seq))) {
MemoryAddress addr = segment.baseAddress();
try {
for (int i = 0; i < seq.elementCount().getAsLong(); i++) {
for (int j = 0; j < ((SequenceLayout) seq.elementLayout()).elementCount().getAsLong(); j++) {
checker.check(handle, addr, i, j);
}
}
if (segment.isReadOnly()) {
throw new AssertionError(); //not ok, memory should be immutable
}
} catch (UnsupportedOperationException ex) {
if (!segment.isReadOnly()) {
throw new AssertionError(); //we should not have failed!
}
return;
}
try {
checker.check(handle, addr, seq.elementCount().getAsLong(),
((SequenceLayout)seq.elementLayout()).elementCount().getAsLong());
throw new AssertionError(); //not ok, out of bounds
} catch (IndexOutOfBoundsException ex) {
//ok, should fail (out of bounds)
}
outer_address = addr; //leak!
}
try {
checker.check(handle, outer_address, 0, 0);
throw new AssertionError(); //not ok, scope is closed
} catch (IllegalStateException ex) {
//ok, should fail (scope is closed)
}
}
static Function<MemorySegment, MemorySegment> ID = Function.identity();
static Function<MemorySegment, MemorySegment> IMMUTABLE = MemorySegment::asReadOnly;
@DataProvider(name = "elements")
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 },
//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 },
//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 },
//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 },
};
}
interface Checker {
void check(VarHandle handle, MemoryAddress addr);
Checker BYTE = (handle, addr) -> {
handle.set(addr, (byte)42);
assertEquals(42, (byte)handle.get(addr));
};
Checker SHORT = (handle, addr) -> {
handle.set(addr, (short)42);
assertEquals(42, (short)handle.get(addr));
};
Checker CHAR = (handle, addr) -> {
handle.set(addr, (char)42);
assertEquals(42, (char)handle.get(addr));
};
Checker INT = (handle, addr) -> {
handle.set(addr, 42);
assertEquals(42, (int)handle.get(addr));
};
Checker LONG = (handle, addr) -> {
handle.set(addr, (long)42);
assertEquals(42, (long)handle.get(addr));
};
Checker FLOAT = (handle, addr) -> {
handle.set(addr, (float)42);
assertEquals((float)42, (float)handle.get(addr));
};
Checker DOUBLE = (handle, addr) -> {
handle.set(addr, (double)42);
assertEquals((double)42, (double)handle.get(addr));
};
}
@DataProvider(name = "arrayElements")
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 },
//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 },
//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 },
//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 },
};
}
interface ArrayChecker {
void check(VarHandle handle, MemoryAddress addr, long index);
ArrayChecker BYTE = (handle, addr, i) -> {
handle.set(addr, i, (byte)i);
assertEquals(i, (byte)handle.get(addr, i));
};
ArrayChecker SHORT = (handle, addr, i) -> {
handle.set(addr, i, (short)i);
assertEquals(i, (short)handle.get(addr, i));
};
ArrayChecker CHAR = (handle, addr, i) -> {
handle.set(addr, i, (char)i);
assertEquals(i, (char)handle.get(addr, i));
};
ArrayChecker INT = (handle, addr, i) -> {
handle.set(addr, i, (int)i);
assertEquals(i, (int)handle.get(addr, i));
};
ArrayChecker LONG = (handle, addr, i) -> {
handle.set(addr, i, (long)i);
assertEquals(i, (long)handle.get(addr, i));
};
ArrayChecker FLOAT = (handle, addr, i) -> {
handle.set(addr, i, (float)i);
assertEquals((float)i, (float)handle.get(addr, i));
};
ArrayChecker DOUBLE = (handle, addr, i) -> {
handle.set(addr, i, (double)i);
assertEquals((double)i, (double)handle.get(addr, i));
};
}
@DataProvider(name = "matrixElements")
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 },
//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 },
//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 },
//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 },
};
}
interface MatrixChecker {
void check(VarHandle handle, MemoryAddress addr, long row, long col);
MatrixChecker BYTE = (handle, addr, r, c) -> {
handle.set(addr, r, c, (byte)(r + c));
assertEquals(r + c, (byte)handle.get(addr, r, c));
};
MatrixChecker SHORT = (handle, addr, r, c) -> {
handle.set(addr, r, c, (short)(r + c));
assertEquals(r + c, (short)handle.get(addr, r, c));
};
MatrixChecker CHAR = (handle, addr, r, c) -> {
handle.set(addr, r, c, (char)(r + c));
assertEquals(r + c, (char)handle.get(addr, r, c));
};
MatrixChecker INT = (handle, addr, r, c) -> {
handle.set(addr, r, c, (int)(r + c));
assertEquals(r + c, (int)handle.get(addr, r, c));
};
MatrixChecker LONG = (handle, addr, r, c) -> {
handle.set(addr, r, c, r + c);
assertEquals(r + c, (long)handle.get(addr, r, c));
};
MatrixChecker FLOAT = (handle, addr, r, c) -> {
handle.set(addr, r, c, (float)(r + c));
assertEquals((float)(r + c), (float)handle.get(addr, r, c));
};
MatrixChecker DOUBLE = (handle, addr, r, c) -> {
handle.set(addr, r, c, (double)(r + c));
assertEquals((double)(r + c), (double)handle.get(addr, r, c));
};
}
@DataProvider(name = "badCarriers")
public Object[][] createBadCarriers() {
return new Object[][] {
{ void.class },
{ boolean.class },
{ Object.class },
{ int[].class }
};
}
}

View File

@ -0,0 +1,137 @@
/*
* 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 TestMemoryAlignment
*/
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.GroupLayout;
import jdk.incubator.foreign.MemoryLayout.PathElement;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.SequenceLayout;
import jdk.incubator.foreign.ValueLayout;
import java.lang.invoke.VarHandle;
import java.util.stream.LongStream;
import org.testng.annotations.*;
import static org.testng.Assert.*;
public class TestMemoryAlignment {
@Test(dataProvider = "alignments")
public void testAlignedAccess(long align) {
ValueLayout layout = MemoryLayouts.BITS_32_BE;
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);
try (MemorySegment segment = MemorySegment.allocateNative(aligned)) {
MemoryAddress addr = segment.baseAddress();
vh.set(addr, -42);
int val = (int)vh.get(addr);
assertEquals(val, -42);
}
}
@Test(dataProvider = "alignments")
public void testUnalignedAccess(long align) {
ValueLayout layout = MemoryLayouts.BITS_32_BE;
assertEquals(layout.bitAlignment(), 32);
ValueLayout aligned = layout.withBitAlignment(align);
MemoryLayout alignedGroup = MemoryLayout.ofStruct(MemoryLayouts.PAD_8, aligned);
assertEquals(alignedGroup.bitAlignment(), align);
VarHandle vh = aligned.varHandle(int.class);
try (MemorySegment segment = MemorySegment.allocateNative(alignedGroup)) {
MemoryAddress addr = segment.baseAddress();
vh.set(addr.offset(1L), -42);
assertEquals(align, 8); //this is the only case where access is aligned
} catch (IllegalStateException ex) {
assertNotEquals(align, 8); //if align != 8, access is always unaligned
}
}
@Test(dataProvider = "alignments")
public void testUnalignedPath(long align) {
MemoryLayout layout = MemoryLayouts.BITS_32_BE;
MemoryLayout aligned = layout.withBitAlignment(align).withName("value");
GroupLayout alignedGroup = MemoryLayout.ofStruct(MemoryLayouts.PAD_8, aligned);
try {
alignedGroup.varHandle(int.class, 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
}
}
@Test(dataProvider = "alignments")
public void testUnalignedSequence(long align) {
SequenceLayout layout = MemoryLayout.ofSequence(5, MemoryLayouts.BITS_32_BE.withBitAlignment(align));
try {
VarHandle vh = layout.varHandle(int.class, PathElement.sequenceElement());
try (MemorySegment segment = MemorySegment.allocateNative(layout)) {
MemoryAddress addr = segment.baseAddress();
for (long i = 0 ; i < 5 ; i++) {
vh.set(addr, i, -42);
}
}
} catch (UnsupportedOperationException ex) {
assertTrue(align > 32); //if align > 32, access is always unaligned (for some elements)
}
}
@Test
public void testPackedAccess() {
ValueLayout vChar = MemoryLayouts.BITS_8_BE;
ValueLayout vShort = MemoryLayouts.BITS_16_BE;
ValueLayout vInt = MemoryLayouts.BITS_32_BE;
//mimic pragma pack(1)
GroupLayout g = MemoryLayout.ofStruct(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"));
try (MemorySegment segment = MemorySegment.allocateNative(g)) {
MemoryAddress addr = segment.baseAddress();
vh_c.set(addr, Byte.MIN_VALUE);
assertEquals(vh_c.get(addr), Byte.MIN_VALUE);
vh_s.set(addr, Short.MIN_VALUE);
assertEquals(vh_s.get(addr), Short.MIN_VALUE);
vh_i.set(addr, Integer.MIN_VALUE);
assertEquals(vh_i.get(addr), Integer.MIN_VALUE);
}
}
@DataProvider(name = "alignments")
public Object[][] createAlignments() {
return LongStream.range(3, 32)
.mapToObj(v -> new Object[] { 1L << v })
.toArray(Object[][]::new);
}
}

View File

@ -0,0 +1,127 @@
/*
* 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 TestMemoryCopy
*/
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryHandles;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.lang.invoke.VarHandle;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.IntFunction;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import static org.testng.Assert.*;
public class TestMemoryCopy {
final static VarHandle BYTE_HANDLE = MemoryLayouts.JAVA_BYTE.varHandle(byte.class);
@Test(dataProvider = "slices")
public void testCopy(SegmentSlice s1, SegmentSlice s2) {
MemoryAddress addr1 = s1.segment.baseAddress();
MemoryAddress addr2 = s2.segment.baseAddress();
int size = Math.min(s1.size(), s2.size());
//prepare source and target segments
for (int i = 0 ; i < size ; i++) {
BYTE_HANDLE.set(addr2.offset(i), (byte)0);
}
for (int i = 0 ; i < size ; i++) {
BYTE_HANDLE.set(addr1.offset(i), (byte) i);
}
//perform copy
MemoryAddress.copy(addr1, addr2, size);
//check that copy actually worked
for (int i = 0 ; i < size ; i++) {
assertEquals((byte)i, BYTE_HANDLE.get(addr2.offset(i)));
}
}
static class SegmentSlice {
enum Kind {
NATIVE(MemorySegment::allocateNative),
ARRAY(i -> MemorySegment.ofArray(new byte[i]));
final IntFunction<MemorySegment> segmentFactory;
Kind(IntFunction<MemorySegment> segmentFactory) {
this.segmentFactory = segmentFactory;
}
MemorySegment makeSegment(int elems) {
return segmentFactory.apply(elems);
}
}
final Kind kind;
final int first;
final int last;
final MemorySegment segment;
public SegmentSlice(Kind kind, int first, int last, MemorySegment segment) {
this.kind = kind;
this.first = first;
this.last = last;
this.segment = segment;
}
int size() {
return last - first + 1;
}
}
@DataProvider(name = "slices")
static Object[][] slices() {
int[] sizes = { 16, 8, 4, 2, 1 };
List<SegmentSlice> slices = new ArrayList<>();
for (SegmentSlice.Kind kind : SegmentSlice.Kind.values()) {
MemorySegment segment = kind.makeSegment(16);
//compute all slices
for (int size : sizes) {
for (int index = 0 ; index < 16 ; index += size) {
MemorySegment slice = segment.asSlice(index, size);
slices.add(new SegmentSlice(kind, index, index + size - 1, slice));
}
}
}
Object[][] sliceArray = new Object[slices.size() * slices.size()][];
for (int i = 0 ; i < slices.size() ; i++) {
for (int j = 0 ; j < slices.size() ; j++) {
sliceArray[i * slices.size() + j] = new Object[] { slices.get(i), slices.get(j) };
}
}
return sliceArray;
}
}

View File

@ -0,0 +1,232 @@
/*
* 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
* @modules java.base/jdk.internal.misc
* jdk.incubator.foreign/jdk.incubator.foreign.unsafe
* @run testng TestNative
*/
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayout.PathElement;
import jdk.incubator.foreign.unsafe.ForeignUnsafe;
import jdk.internal.misc.Unsafe;
import org.testng.annotations.*;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.SequenceLayout;
import java.lang.invoke.VarHandle;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.CharBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.ShortBuffer;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import static org.testng.Assert.*;
public class TestNative {
static Unsafe UNSAFE;
static {
UNSAFE = Unsafe.getUnsafe();
}
static SequenceLayout bytes = MemoryLayout.ofSequence(100,
MemoryLayouts.JAVA_BYTE.withOrder(ByteOrder.nativeOrder())
);
static SequenceLayout chars = MemoryLayout.ofSequence(100,
MemoryLayouts.JAVA_CHAR.withOrder(ByteOrder.nativeOrder())
);
static SequenceLayout shorts = MemoryLayout.ofSequence(100,
MemoryLayouts.JAVA_SHORT.withOrder(ByteOrder.nativeOrder())
);
static SequenceLayout ints = MemoryLayout.ofSequence(100,
MemoryLayouts.JAVA_INT.withOrder(ByteOrder.nativeOrder())
);
static SequenceLayout floats = MemoryLayout.ofSequence(100,
MemoryLayouts.JAVA_FLOAT.withOrder(ByteOrder.nativeOrder())
);
static SequenceLayout longs = MemoryLayout.ofSequence(100,
MemoryLayouts.JAVA_LONG.withOrder(ByteOrder.nativeOrder())
);
static SequenceLayout doubles = MemoryLayout.ofSequence(100,
MemoryLayouts.JAVA_DOUBLE.withOrder(ByteOrder.nativeOrder())
);
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 = doubles.varHandle(long.class, PathElement.sequenceElement());
static VarHandle doubleHandle = longs.varHandle(double.class, PathElement.sequenceElement());
static void initBytes(MemoryAddress base, SequenceLayout seq, BiConsumer<MemoryAddress, Long> handleSetter) {
for (long i = 0; i < seq.elementCount().getAsLong() ; i++) {
handleSetter.accept(base, i);
}
}
static <Z extends Buffer> void checkBytes(MemoryAddress base, SequenceLayout layout,
BiFunction<MemoryAddress, Long, Object> handleExtractor,
Function<ByteBuffer, Z> bufferFactory,
BiFunction<Z, Integer, Object> nativeBufferExtractor,
BiFunction<Long, Integer, Object> nativeRawExtractor) {
long nelems = layout.elementCount().getAsLong();
ByteBuffer bb = base.segment().asSlice(base.offset(), (int)layout.byteSize()).asByteBuffer();
Z z = bufferFactory.apply(bb);
for (long i = 0 ; i < nelems ; i++) {
Object handleValue = handleExtractor.apply(base, i);
Object bufferValue = nativeBufferExtractor.apply(z, (int)i);
Object rawValue = nativeRawExtractor.apply(ForeignUnsafe.getUnsafeOffset(base), (int)i);
if (handleValue instanceof Number) {
assertEquals(((Number)handleValue).longValue(), i);
assertEquals(((Number)bufferValue).longValue(), i);
assertEquals(((Number)rawValue).longValue(), i);
} else {
assertEquals((long)(char)handleValue, i);
assertEquals((long)(char)bufferValue, i);
assertEquals((long)(char)rawValue, i);
}
}
}
public static native byte getByteBuffer(ByteBuffer buf, int index);
public static native char getCharBuffer(CharBuffer buf, int index);
public static native short getShortBuffer(ShortBuffer buf, int index);
public static native int getIntBuffer(IntBuffer buf, int index);
public static native float getFloatBuffer(FloatBuffer buf, int index);
public static native long getLongBuffer(LongBuffer buf, int index);
public static native double getDoubleBuffer(DoubleBuffer buf, int index);
public static native byte getByteRaw(long addr, int index);
public static native char getCharRaw(long addr, int index);
public static native short getShortRaw(long addr, int index);
public static native int getIntRaw(long addr, int index);
public static native float getFloatRaw(long addr, int index);
public static native long getLongRaw(long addr, int index);
public static native double getDoubleRaw(long addr, int index);
public static native long getCapacity(Buffer buffer);
@Test(dataProvider="nativeAccessOps")
public void testNativeAccess(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
try (MemorySegment segment = MemorySegment.allocateNative(seq)) {
MemoryAddress address = segment.baseAddress();
initializer.accept(address);
checker.accept(address);
}
}
@Test(dataProvider="buffers")
public void testNativeCapacity(Function<ByteBuffer, Buffer> bufferFunction, int elemSize) {
int capacity = (int)doubles.byteSize();
try (MemorySegment segment = MemorySegment.allocateNative(doubles)) {
ByteBuffer bb = segment.asByteBuffer();
Buffer buf = bufferFunction.apply(bb);
int expected = capacity / elemSize;
assertEquals(buf.capacity(), expected);
assertEquals(getCapacity(buf), expected);
}
}
static {
System.loadLibrary("NativeAccess");
}
@DataProvider(name = "nativeAccessOps")
public Object[][] nativeAccessOps() {
Consumer<MemoryAddress> byteInitializer =
(base) -> initBytes(base, bytes, (addr, pos) -> byteHandle.set(addr, pos, (byte)(long)pos));
Consumer<MemoryAddress> charInitializer =
(base) -> initBytes(base, chars, (addr, pos) -> charHandle.set(addr, pos, (char)(long)pos));
Consumer<MemoryAddress> shortInitializer =
(base) -> initBytes(base, shorts, (addr, pos) -> shortHandle.set(addr, pos, (short)(long)pos));
Consumer<MemoryAddress> intInitializer =
(base) -> initBytes(base, ints, (addr, pos) -> intHandle.set(addr, pos, (int)(long)pos));
Consumer<MemoryAddress> floatInitializer =
(base) -> initBytes(base, floats, (addr, pos) -> floatHandle.set(addr, pos, (float)(long)pos));
Consumer<MemoryAddress> longInitializer =
(base) -> initBytes(base, longs, (addr, pos) -> longHandle.set(addr, pos, (long)pos));
Consumer<MemoryAddress> doubleInitializer =
(base) -> initBytes(base, doubles, (addr, pos) -> doubleHandle.set(addr, pos, (double)(long)pos));
Consumer<MemoryAddress> byteChecker =
(base) -> checkBytes(base, bytes, byteHandle::get, bb -> bb, TestNative::getByteBuffer, TestNative::getByteRaw);
Consumer<MemoryAddress> charChecker =
(base) -> checkBytes(base, chars, charHandle::get, ByteBuffer::asCharBuffer, TestNative::getCharBuffer, TestNative::getCharRaw);
Consumer<MemoryAddress> shortChecker =
(base) -> checkBytes(base, shorts, shortHandle::get, ByteBuffer::asShortBuffer, TestNative::getShortBuffer, TestNative::getShortRaw);
Consumer<MemoryAddress> intChecker =
(base) -> checkBytes(base, ints, intHandle::get, ByteBuffer::asIntBuffer, TestNative::getIntBuffer, TestNative::getIntRaw);
Consumer<MemoryAddress> floatChecker =
(base) -> checkBytes(base, floats, floatHandle::get, ByteBuffer::asFloatBuffer, TestNative::getFloatBuffer, TestNative::getFloatRaw);
Consumer<MemoryAddress> longChecker =
(base) -> checkBytes(base, longs, longHandle::get, ByteBuffer::asLongBuffer, TestNative::getLongBuffer, TestNative::getLongRaw);
Consumer<MemoryAddress> doubleChecker =
(base) -> checkBytes(base, doubles, doubleHandle::get, ByteBuffer::asDoubleBuffer, TestNative::getDoubleBuffer, TestNative::getDoubleRaw);
return new Object[][]{
{byteChecker, byteInitializer, bytes},
{charChecker, charInitializer, chars},
{shortChecker, shortInitializer, shorts},
{intChecker, intInitializer, ints},
{floatChecker, floatInitializer, floats},
{longChecker, longInitializer, longs},
{doubleChecker, doubleInitializer, doubles}
};
}
@DataProvider(name = "buffers")
public Object[][] buffers() {
return new Object[][] {
{ (Function<ByteBuffer, Buffer>)bb -> bb, 1 },
{ (Function<ByteBuffer, Buffer>)ByteBuffer::asCharBuffer, 2 },
{ (Function<ByteBuffer, Buffer>)ByteBuffer::asShortBuffer, 2 },
{ (Function<ByteBuffer, Buffer>)ByteBuffer::asIntBuffer, 4 },
{ (Function<ByteBuffer, Buffer>)ByteBuffer::asFloatBuffer, 4 },
{ (Function<ByteBuffer, Buffer>)ByteBuffer::asLongBuffer, 8 },
{ (Function<ByteBuffer, Buffer>)ByteBuffer::asDoubleBuffer, 8 },
};
}
}

View File

@ -0,0 +1,196 @@
/*
* 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 TestSegments
*/
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment;
import java.awt.font.LayoutPath;
import java.lang.invoke.VarHandle;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.LongFunction;
import java.util.stream.Stream;
import jdk.incubator.foreign.SequenceLayout;
import org.testng.annotations.*;
import static org.testng.Assert.*;
public class TestSegments {
@Test(dataProvider = "badSizeAndAlignments", expectedExceptions = IllegalArgumentException.class)
public void testBadAllocateAlign(long size, long align) {
MemorySegment.allocateNative(size, align);
}
@Test(dataProvider = "badLayouts", expectedExceptions = UnsupportedOperationException.class)
public void testBadAllocateLayout(MemoryLayout layout) {
MemorySegment.allocateNative(layout);
}
@Test(expectedExceptions = OutOfMemoryError.class)
public void testAllocateTooBig() {
MemorySegment.allocateNative(Long.MAX_VALUE);
}
@Test(dataProvider = "segmentOperations")
public void testOpOutsideConfinement(SegmentMember member) throws Throwable {
try (MemorySegment segment = MemorySegment.allocateNative(4)) {
AtomicBoolean failed = new AtomicBoolean(false);
Thread t = new Thread(() -> {
try {
Object o = member.method.invoke(segment, member.params);
if (member.method.getName().equals("acquire")) {
((MemorySegment)o).close();
}
} catch (ReflectiveOperationException ex) {
throw new IllegalStateException(ex);
}
});
t.setUncaughtExceptionHandler((thread, ex) -> failed.set(true));
t.start();
t.join();
assertEquals(failed.get(), member.isConfined());
}
}
@Test
public void testNativeSegmentIsZeroed() {
VarHandle byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE)
.varHandle(byte.class, MemoryLayout.PathElement.sequenceElement());
try (MemorySegment segment = MemorySegment.allocateNative(1000)) {
for (long i = 0 ; i < segment.byteSize() ; i++) {
assertEquals(0, (byte)byteHandle.get(segment.baseAddress(), i));
}
}
}
@DataProvider(name = "badSizeAndAlignments")
public Object[][] sizesAndAlignments() {
return new Object[][] {
{ -1, 8 },
{ 1, 15 },
{ 1, -15 }
};
}
@DataProvider(name = "badLayouts")
public Object[][] layouts() {
SizedLayoutFactory[] layoutFactories = SizedLayoutFactory.values();
Object[][] values = new Object[layoutFactories.length * 2][2];
for (int i = 0; i < layoutFactories.length ; i++) {
values[i * 2] = new Object[] { MemoryLayout.ofStruct(layoutFactories[i].make(7), MemoryLayout.ofPaddingBits(9)) }; // good size, bad align
values[(i * 2) + 1] = new Object[] { layoutFactories[i].make(15).withBitAlignment(16) }; // bad size, good align
}
return values;
}
enum SizedLayoutFactory {
VALUE_BE(size -> MemoryLayout.ofValueBits(size, ByteOrder.BIG_ENDIAN)),
VALUE_LE(size -> MemoryLayout.ofValueBits(size, ByteOrder.LITTLE_ENDIAN)),
PADDING(MemoryLayout::ofPaddingBits);
private final LongFunction<MemoryLayout> factory;
SizedLayoutFactory(LongFunction<MemoryLayout> factory) {
this.factory = factory;
}
MemoryLayout make(long size) {
return factory.apply(size);
}
}
@DataProvider(name = "segmentOperations")
static Object[][] segmentMembers() {
List<SegmentMember> members = new ArrayList<>();
for (Method m : MemorySegment.class.getDeclaredMethods()) {
//skip statics and method declared in j.l.Object
if (m.getDeclaringClass().equals(Object.class) ||
(m.getModifiers() & Modifier.STATIC) != 0) continue;
Object[] args = Stream.of(m.getParameterTypes())
.map(TestSegments::defaultValue)
.toArray();
members.add(new SegmentMember(m, args));
}
return members.stream().map(ms -> new Object[] { ms }).toArray(Object[][]::new);
}
static class SegmentMember {
final Method method;
final Object[] params;
public SegmentMember(Method method, Object[] params) {
this.method = method;
this.params = params;
}
boolean isConfined() {
return method.getName().startsWith("as") ||
method.getName().startsWith("to") ||
method.getName().equals("close") ||
method.getName().equals("slice");
}
@Override
public String toString() {
return method.getName();
}
}
static Object defaultValue(Class<?> c) {
if (c.isPrimitive()) {
if (c == char.class) {
return (char)0;
} else if (c == boolean.class) {
return false;
} else if (c == byte.class) {
return (byte)0;
} else if (c == short.class) {
return (short)0;
} else if (c == int.class) {
return 0;
} else if (c == long.class) {
return 0L;
} else if (c == float.class) {
return 0f;
} else if (c == double.class) {
return 0d;
} else {
throw new IllegalStateException();
}
} else {
return null;
}
}
}

View File

@ -0,0 +1,82 @@
/*
* 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 TestSharedAccess
*/
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.MemoryLayouts;
import org.testng.annotations.*;
import java.lang.invoke.VarHandle;
import java.util.ArrayList;
import java.util.List;
import static org.testng.Assert.*;
public class TestSharedAccess {
static final VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
@Test
public void testShared() throws Throwable {
try (MemorySegment s = MemorySegment.allocateNative(4)) {
setInt(s, 42);
assertEquals(getInt(s), 42);
List<Thread> threads = new ArrayList<>();
for (int i = 0 ; i < 1000 ; i++) {
threads.add(new Thread(() -> {
try (MemorySegment local = s.acquire()) {
assertEquals(getInt(local), 42);
}
}));
}
threads.forEach(Thread::start);
threads.forEach(t -> {
try {
t.join();
} catch (Throwable e) {
throw new IllegalStateException(e);
}
});
}
}
@Test(expectedExceptions=IllegalStateException.class)
public void testBadCloseWithPendingAcquire() {
try (MemorySegment segment = MemorySegment.allocateNative(8)) {
segment.acquire();
} //should fail here!
}
static int getInt(MemorySegment handle) {
return (int)intHandle.getVolatile(handle.baseAddress());
}
static void setInt(MemorySegment handle, int value) {
intHandle.setVolatile(handle.baseAddress(), value);
}
}

View File

@ -0,0 +1,97 @@
/*
* 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.
*
*/
import jdk.incubator.foreign.MemoryHandles;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment;
import java.lang.invoke.VarHandle;
import org.testng.annotations.*;
import static org.testng.Assert.*;
/*
* @test
* @run testng/othervm -Xverify:all TestSlices
*/
public class TestSlices {
static MemoryLayout LAYOUT = MemoryLayout.ofSequence(2,
MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT));
static VarHandle VH_ALL = LAYOUT.varHandle(int.class,
MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement());
static VarHandle VH_INT = MemoryLayouts.JAVA_INT.varHandle(int.class);
@Test(dataProvider = "slices")
public void testSlices(VarHandle handle, int lo, int hi, int[] values) {
try (MemorySegment segment = MemorySegment.allocateNative(LAYOUT)) {
//init
for (long i = 0 ; i < 2 ; i++) {
for (long j = 0 ; j < 5 ; j++) {
VH_ALL.set(segment.baseAddress(), i, j, (int)j + 1 + ((int)i * 5));
}
}
checkSlice(segment, handle, lo, hi, values);
}
}
static void checkSlice(MemorySegment segment, VarHandle handle, long i_max, long j_max, int... values) {
int index = 0;
for (long i = 0 ; i < i_max ; i++) {
for (long j = 0 ; j < j_max ; j++) {
int x = (int) handle.get(segment.baseAddress(), i, j);
assertEquals(x, values[index++]);
}
}
assertEquals(index, values.length);
}
@DataProvider(name = "slices")
static Object[][] slices() {
return new Object[][] {
// x
{ VH_ALL, 2, 5, new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } },
// x[0::2]
{ LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(),
MemoryLayout.PathElement.sequenceElement(0, 2)), 2, 3, new int[] { 1, 3, 5, 6, 8, 10 } },
{ MemoryHandles.withStride(MemoryHandles.withStride(VH_INT, 8), 20), 2, 3, new int[] { 1, 3, 5, 6, 8, 10 } },
// x[1::2]
{ LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(),
MemoryLayout.PathElement.sequenceElement(1, 2)), 2, 2, new int[] { 2, 4, 7, 9 } },
{ MemoryHandles.withOffset(MemoryHandles.withStride(MemoryHandles.withStride(VH_INT, 8), 20), 4), 2, 2, new int[] { 2, 4, 7, 9 } },
// x[4::-2]
{ LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(),
MemoryLayout.PathElement.sequenceElement(4, -2)), 2, 3, new int[] { 5, 3, 1, 10, 8, 6 } },
{ MemoryHandles.withOffset(MemoryHandles.withStride(MemoryHandles.withStride(VH_INT, -8), 20), 16), 2, 3, new int[] { 5, 3, 1, 10, 8, 6 } },
// x[3::-2]
{ LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(),
MemoryLayout.PathElement.sequenceElement(3, -2)), 2, 2, new int[] { 4, 2, 9, 7 } },
{ MemoryHandles.withOffset(MemoryHandles.withStride(MemoryHandles.withStride(VH_INT, -8), 20), 12), 2, 2, new int[] { 4, 2, 9, 7 } },
};
}
}

View File

@ -0,0 +1,189 @@
/*
* 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 TestVarHandleCombinators
*/
import jdk.incubator.foreign.MemoryHandles;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
import static org.testng.Assert.assertEquals;
public class TestVarHandleCombinators {
@Test
public void testElementAccess() {
VarHandle vh = MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder());
vh = MemoryHandles.withStride(vh, 1);
byte[] arr = { 0, 0, -1, 0 };
MemorySegment segment = MemorySegment.ofArray(arr);
MemoryAddress addr = segment.baseAddress();
assertEquals((byte) vh.get(addr, 2), (byte) -1);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testUnalignedElement() {
VarHandle vh = MemoryHandles.varHandle(byte.class, 4, ByteOrder.nativeOrder());
MemoryHandles.withStride(vh, 2);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadStrideElement() {
VarHandle vh = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
MemoryHandles.withStride(vh, 0); //scale factor cant be zero
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testAlignNotPowerOf2() {
VarHandle vh = MemoryHandles.varHandle(byte.class, 3, ByteOrder.nativeOrder());
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testAlignNegative() {
VarHandle vh = MemoryHandles.varHandle(byte.class, -1, ByteOrder.nativeOrder());
}
@Test
public void testAlign() {
VarHandle vh = MemoryHandles.varHandle(byte.class, 2, ByteOrder.nativeOrder());
MemorySegment segment = MemorySegment.allocateNative(1, 2);
MemoryAddress address = segment.baseAddress();
vh.set(address, (byte) 10); // fine, memory region is aligned
assertEquals((byte) vh.get(address), (byte) 10);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testAlignBadAccess() {
VarHandle vh = MemoryHandles.varHandle(byte.class, 2, ByteOrder.nativeOrder());
vh = MemoryHandles.withOffset(vh, 1); // offset by 1 byte
MemorySegment segment = MemorySegment.allocateNative(2, 2);
MemoryAddress address = segment.baseAddress();
vh.set(address, (byte) 10); // should be bad align
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testOffsetNegative() {
VarHandle vh = MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder());
MemoryHandles.withOffset(vh, -1);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testUnalignedOffset() {
VarHandle vh = MemoryHandles.varHandle(byte.class, 4, ByteOrder.nativeOrder());
MemoryHandles.withOffset(vh, 2);
}
@Test
public void testOffset() {
VarHandle vh = MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder());
vh = MemoryHandles.withOffset(vh, 1);
MemorySegment segment = MemorySegment.ofArray(new byte[2]);
MemoryAddress address = segment.baseAddress();
vh.set(address, (byte) 10);
assertEquals((byte) vh.get(address), (byte) 10);
}
@Test
public void testByteOrderLE() {
VarHandle vh = MemoryHandles.varHandle(short.class, 2, ByteOrder.LITTLE_ENDIAN);
byte[] arr = new byte[2];
MemorySegment segment = MemorySegment.ofArray(arr);
MemoryAddress address = segment.baseAddress();
vh.set(address, (short) 0xFF);
assertEquals(arr[0], (byte) 0xFF);
assertEquals(arr[1], (byte) 0);
}
@Test
public void testByteOrderBE() {
VarHandle vh = MemoryHandles.varHandle(short.class, 2, ByteOrder.BIG_ENDIAN);
byte[] arr = new byte[2];
MemorySegment segment = MemorySegment.ofArray(arr);
MemoryAddress address = segment.baseAddress();
vh.set(address, (short) 0xFF);
assertEquals(arr[0], (byte) 0);
assertEquals(arr[1], (byte) 0xFF);
}
@Test
public void testNestedSequenceAccess() {
int outer_size = 10;
int inner_size = 5;
//[10 : [5 : [x32 i32]]]
VarHandle vh = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
vh = MemoryHandles.withOffset(vh, 4);
VarHandle inner_vh = MemoryHandles.withStride(vh, 8);
VarHandle outer_vh = MemoryHandles.withStride(inner_vh, 5 * 8);
int count = 0;
try (MemorySegment segment = MemorySegment.allocateNative(inner_size * outer_size * 8)) {
for (long i = 0; i < outer_size; i++) {
for (long j = 0; j < inner_size; j++) {
outer_vh.set(segment.baseAddress(), i, j, count);
assertEquals(
(int)inner_vh.get(segment.baseAddress().offset(i * inner_size * 8), j),
count);
count++;
}
}
}
}
@Test(dataProvider = "badCarriers", expectedExceptions = IllegalArgumentException.class)
public void testBadCarrier(Class<?> carrier) {
MemoryHandles.varHandle(carrier, ByteOrder.nativeOrder());
}
@DataProvider(name = "badCarriers")
public Object[][] createBadCarriers() {
return new Object[][] {
{ void.class },
{ boolean.class },
{ Object.class },
{ int[].class },
{ MemoryAddress.class }
};
}
}

View File

@ -0,0 +1,115 @@
/*
* 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.
*
*/
#include "jni.h"
#include <stdio.h>
JNIEXPORT jbyte JNICALL
Java_TestNative_getByteRaw(JNIEnv *env, jclass cls, jlong addr, jint index) {
jbyte *arr = (jbyte*)addr;
return arr[index];
}
JNIEXPORT jbyte JNICALL
Java_TestNative_getByteBuffer(JNIEnv *env, jclass cls, jobject buf, jint index) {
jlong addr = (jlong)(*env)->GetDirectBufferAddress(env, buf);
return Java_TestNative_getByteRaw(env, cls, addr, index);
}
JNIEXPORT jchar JNICALL
Java_TestNative_getCharRaw(JNIEnv *env, jclass cls, jlong addr, jint index) {
jchar *arr = (jchar*)addr;
return arr[index];
}
JNIEXPORT jchar JNICALL
Java_TestNative_getCharBuffer(JNIEnv *env, jclass cls, jobject buf, jint index) {
jlong addr = (jlong)(*env)->GetDirectBufferAddress(env, buf);
return Java_TestNative_getCharRaw(env, cls, addr, index);
}
JNIEXPORT jshort JNICALL
Java_TestNative_getShortRaw(JNIEnv *env, jclass cls, jlong addr, jint index) {
jshort *arr = (jshort*)addr;
return arr[index];
}
JNIEXPORT jshort JNICALL
Java_TestNative_getShortBuffer(JNIEnv *env, jclass cls, jobject buf, jint index) {
jlong addr = (jlong)(*env)->GetDirectBufferAddress(env, buf);
return Java_TestNative_getShortRaw(env, cls, addr, index);
}
JNIEXPORT jint JNICALL
Java_TestNative_getIntRaw(JNIEnv *env, jclass cls, jlong addr, jint index) {
jint *arr = (jint*)addr;
return arr[index];
}
JNIEXPORT jint JNICALL
Java_TestNative_getIntBuffer(JNIEnv *env, jclass cls, jobject buf, jint index) {
jlong addr = (jlong)(*env)->GetDirectBufferAddress(env, buf);
return Java_TestNative_getIntRaw(env, cls, addr, index);
}
JNIEXPORT jfloat JNICALL
Java_TestNative_getFloatRaw(JNIEnv *env, jclass cls, jlong addr, jint index) {
jfloat *arr = (jfloat*)addr;
return arr[index];
}
JNIEXPORT jfloat JNICALL
Java_TestNative_getFloatBuffer(JNIEnv *env, jclass cls, jobject buf, jint index) {
jlong addr = (jlong)(*env)->GetDirectBufferAddress(env, buf);
return Java_TestNative_getFloatRaw(env, cls, addr, index);
}
JNIEXPORT jlong JNICALL
Java_TestNative_getLongRaw(JNIEnv *env, jclass cls, jlong addr, jint index) {
jlong *arr = (jlong*)addr;
return arr[index];
}
JNIEXPORT jlong JNICALL
Java_TestNative_getLongBuffer(JNIEnv *env, jclass cls, jobject buf, jint index) {
jlong addr = (jlong)(*env)->GetDirectBufferAddress(env, buf);
return Java_TestNative_getLongRaw(env, cls, addr, index);
}
JNIEXPORT jdouble JNICALL
Java_TestNative_getDoubleRaw(JNIEnv *env, jclass cls, jlong addr, jint index) {
jdouble *arr = (jdouble*)addr;
return arr[index];
}
JNIEXPORT jdouble JNICALL
Java_TestNative_getDoubleBuffer(JNIEnv *env, jclass cls, jobject buf, jint index) {
jlong addr = (jlong)(*env)->GetDirectBufferAddress(env, buf);
return Java_TestNative_getDoubleRaw(env, cls, addr, index);
}
JNIEXPORT jlong JNICALL
Java_TestNative_getCapacity(JNIEnv *env, jclass cls, jobject buf) {
return (*env)->GetDirectBufferCapacity(env, buf);
}