8243491: Implementation of Foreign-Memory Access API (Second Incubator)

Upstream latest changes of the Foreign-Memory Access API

Co-authored-by: Jorn Vernee <jorn.vernee@oracle.com>
Co-authored-by: Mandy Chung <mandy.chung@oracle.com>
Co-authored-by: Paul Sandoz <paul.sandoz@oracle.com>
Co-authored-by: Peter Levart <peter.levart@gmail.com>
Reviewed-by: chegar, psandoz
This commit is contained in:
Chris Hegarty 2020-05-25 10:54:39 +01:00 committed by Maurizio Cimadamore
parent 9b94b9d1a1
commit f3eb44a94d
94 changed files with 7496 additions and 1388 deletions

View File

@ -680,8 +680,6 @@ define SetupRunMicroTestBody
# Current tests needs to open java.io
$1_MICRO_JAVA_OPTIONS += --add-opens=java.base/java.io=ALL-UNNAMED
# Set library path for native dependencies
$1_MICRO_JAVA_OPTIONS += -Djava.library.path=$$(TEST_IMAGE_DIR)/micro/native
# Save output as JSON or CSV file
ifneq ($$(MICRO_RESULTS_FORMAT), )
@ -691,6 +689,8 @@ define SetupRunMicroTestBody
ifneq ($$(MICRO_VM_OPTIONS)$$(MICRO_JAVA_OPTIONS), )
JMH_JVM_ARGS := $$(MICRO_VM_OPTIONS) $$(MICRO_JAVA_OPTIONS)
# Set library path for native dependencies
JMH_JVM_ARGS += -Djava.library.path=$$(TEST_IMAGE_DIR)/micro/native
$1_MICRO_VM_OPTIONS := -jvmArgs $(call ShellQuote,$$(JMH_JVM_ARGS))
endif

View File

@ -160,14 +160,14 @@ endef
################################################################################
################################################################################
# Setup a rule for generating a VarHandleMemoryAddress java class
# Setup a rule for generating a memory access var handle helper classes
# Param 1 - Variable declaration prefix
# Param 2 - Type with first letter capitalized
define GenerateVarHandleMemoryAddress
define GenerateVarHandleMemoryAccess
$1_Type := $2
$1_FILENAME := $(VARHANDLES_GENSRC_DIR)/VarHandleMemoryAddressAs$$($1_Type)s.java
$1_FILENAME := $(VARHANDLES_GENSRC_DIR)/MemoryAccessVarHandle$$($1_Type)Helper.java
ifeq ($$($1_Type), Byte)
$1_type := byte
@ -248,7 +248,7 @@ define GenerateVarHandleMemoryAddress
$1_ARGS += -KfloatingPoint
endif
$$($1_FILENAME): $(VARHANDLES_SRC_DIR)/X-VarHandleMemoryAddressView.java.template $(BUILD_TOOLS_JDK)
$$($1_FILENAME): $(VARHANDLES_SRC_DIR)/X-VarHandleMemoryAccess.java.template $(BUILD_TOOLS_JDK)
$$(call MakeDir, $$(@D))
$(RM) $$@
$(TOOL_SPP) -nel -K$$($1_type) \
@ -274,6 +274,6 @@ $(foreach t, $(VARHANDLES_BYTE_ARRAY_TYPES), \
# 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)))
$(eval $(call GenerateVarHandleMemoryAccess,VAR_HANDLE_MEMORY_ADDRESS_$t,$t)))
TARGETS += $(GENSRC_VARHANDLES)

View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package java.lang.invoke;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Stable;
import java.util.List;
import java.util.function.BiFunction;
/**
* An indirect var handle can be thought of as an aggregate of the method handles implementing its supported access modes.
* Its varform contains no method name table (given that some of the method handles composing a bound var handle might
* not be direct). The set of method handles constituting an inditrect var handle are retrieved lazily, to minimize
* code spinning (since not all the access modes will be used anyway).
* Indirect var handles are useful when constructing var handle adapters - that is, an adapter var handle
* can be constructed by extracting the method handles constituting the target var handle, adapting them
* (using the method handle combinator API) and then repackaging the adapted method handles into a new, indirect
* var handle.
*/
/* package */ class IndirectVarHandle extends VarHandle {
@Stable
private final MethodHandle[] handleMap = new MethodHandle[AccessMode.values().length];
private final VarHandle directTarget; // cache, for performance reasons
private final VarHandle target;
private final BiFunction<AccessMode, MethodHandle, MethodHandle> handleFactory;
private final Class<?> value;
private final Class<?>[] coordinates;
IndirectVarHandle(VarHandle target, Class<?> value, Class<?>[] coordinates, BiFunction<AccessMode, MethodHandle, MethodHandle> handleFactory) {
super(new VarForm(value, coordinates));
this.handleFactory = handleFactory;
this.target = target;
this.directTarget = target.asDirect();
this.value = value;
this.coordinates = coordinates;
}
@Override
public Class<?> varType() {
return value;
}
@Override
public List<Class<?>> coordinateTypes() {
return List.of(coordinates);
}
@Override
MethodType accessModeTypeUncached(AccessMode accessMode) {
return accessMode.at.accessModeType(directTarget.getClass(), value, coordinates);
}
@Override
boolean isDirect() {
return false;
}
@Override
VarHandle asDirect() {
return directTarget;
}
VarHandle target() {
return target;
}
@Override
@ForceInline
MethodHandle getMethodHandle(int mode) {
MethodHandle handle = handleMap[mode];
if (handle == null) {
MethodHandle targetHandle = target.getMethodHandle(mode); // might throw UOE of access mode is not supported, which is ok
handle = handleMap[mode] = handleFactory.apply(AccessMode.values()[mode], targetHandle);
}
return handle;
}
@Override
public MethodHandle toMethodHandle(AccessMode accessMode) {
return getMethodHandle(accessMode.ordinal()).bindTo(directTarget);
}
}

View File

@ -365,6 +365,7 @@ class Invokers {
final int ARG_LIMIT = ARG_BASE + mtype.parameterCount();
int nameCursor = ARG_LIMIT;
final int VAD_ARG = nameCursor++;
final int UNBOUND_VH = nameCursor++;
final int CHECK_TYPE = nameCursor++;
final int CHECK_CUSTOM = (CUSTOMIZE_THRESHOLD >= 0) ? nameCursor++ : -1;
final int LINKER_CALL = nameCursor++;
@ -376,11 +377,14 @@ class Invokers {
}
names[VAD_ARG] = new Name(ARG_LIMIT, BasicType.basicType(Object.class));
names[UNBOUND_VH] = new Name(getFunction(NF_directVarHandleTarget), names[THIS_VH]);
names[CHECK_TYPE] = new Name(getFunction(NF_checkVarHandleGenericType), names[THIS_VH], names[VAD_ARG]);
Object[] outArgs = new Object[ARG_LIMIT + 1];
outArgs[0] = names[CHECK_TYPE];
for (int i = 0; i < ARG_LIMIT; i++) {
outArgs[1] = names[UNBOUND_VH];
for (int i = 1; i < ARG_LIMIT; i++) {
outArgs[i + 1] = names[i];
}
@ -411,6 +415,7 @@ class Invokers {
final int ARG_LIMIT = ARG_BASE + mtype.parameterCount();
int nameCursor = ARG_LIMIT;
final int VAD_ARG = nameCursor++;
final int UNBOUND_VH = nameCursor++;
final int CHECK_TYPE = nameCursor++;
final int LINKER_CALL = nameCursor++;
@ -427,6 +432,8 @@ class Invokers {
NamedFunction getter = speciesData.getterFunction(0);
names[VAD_ARG] = new Name(getter, names[THIS_MH]);
names[UNBOUND_VH] = new Name(getFunction(NF_directVarHandleTarget), names[CALL_VH]);
if (isExact) {
names[CHECK_TYPE] = new Name(getFunction(NF_checkVarHandleExactType), names[CALL_VH], names[VAD_ARG]);
} else {
@ -434,7 +441,8 @@ class Invokers {
}
Object[] outArgs = new Object[ARG_LIMIT];
outArgs[0] = names[CHECK_TYPE];
for (int i = 1; i < ARG_LIMIT; i++) {
outArgs[1] = names[UNBOUND_VH];
for (int i = 2; i < ARG_LIMIT; i++) {
outArgs[i] = names[i];
}
@ -520,6 +528,12 @@ class Invokers {
*/
}
@ForceInline
/*non-public*/
static VarHandle directVarHandleTarget(VarHandle handle) {
return handle.asDirect();
}
static MemberName linkToCallSiteMethod(MethodType mtype) {
LambdaForm lform = callSiteForm(mtype, false);
return lform.vmentry;
@ -600,7 +614,8 @@ class Invokers {
NF_checkCustomized = 3,
NF_checkVarHandleGenericType = 4,
NF_checkVarHandleExactType = 5,
NF_LIMIT = 6;
NF_directVarHandleTarget = 6,
NF_LIMIT = 7;
private static final @Stable NamedFunction[] NFS = new NamedFunction[NF_LIMIT];
@ -630,6 +645,8 @@ class Invokers {
return getNamedFunction("checkVarHandleGenericType", MethodType.methodType(MethodHandle.class, VarHandle.class, VarHandle.AccessDescriptor.class));
case NF_checkVarHandleExactType:
return getNamedFunction("checkVarHandleExactType", MethodType.methodType(MethodHandle.class, VarHandle.class, VarHandle.AccessDescriptor.class));
case NF_directVarHandleTarget:
return getNamedFunction("directVarHandleTarget", MethodType.methodType(VarHandle.class, VarHandle.class));
default:
throw newInternalError("Unknown function: " + func);
}

View File

@ -28,7 +28,7 @@ package java.lang.invoke;
/**
* Base class for memory access var handle implementations.
*/
abstract class VarHandleMemoryAddressBase extends VarHandle {
abstract class MemoryAccessVarHandleBase extends VarHandle {
/** endianness **/
final boolean be;
@ -42,7 +42,7 @@ abstract class VarHandleMemoryAddressBase extends VarHandle {
/** alignment constraint (in bytes, expressed as a bit mask) **/
final long alignmentMask;
VarHandleMemoryAddressBase(VarForm form, boolean be, long length, long offset, long alignmentMask) {
MemoryAccessVarHandleBase(VarForm form, boolean be, long length, long offset, long alignmentMask) {
super(form);
this.be = be;
this.length = length;

View File

@ -26,9 +26,10 @@
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.ConstantDynamic;
import jdk.internal.org.objectweb.asm.Handle;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.org.objectweb.asm.Type;
@ -42,10 +43,10 @@ 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.AALOAD;
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;
@ -53,51 +54,65 @@ 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.ASTORE;
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.GETSTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
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.PUTSTATIC;
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;
import static jdk.internal.org.objectweb.asm.Opcodes.V14;
class AddressVarHandleGenerator {
class MemoryAccessVarHandleGenerator {
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 Class<?> BASE_CLASS = MemoryAccessVarHandleBase.class;
private static final HashMap<Class<?>, Class<?>> helperClassCache;
private final static MethodType OFFSET_OP_TYPE;
private final static MethodHandle ADD_OFFSETS_HANDLE;
private final static MethodHandle MUL_OFFSETS_HANDLE;
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);
helperClassCache.put(byte.class, MemoryAccessVarHandleByteHelper.class);
helperClassCache.put(short.class, MemoryAccessVarHandleShortHelper.class);
helperClassCache.put(char.class, MemoryAccessVarHandleCharHelper.class);
helperClassCache.put(int.class, MemoryAccessVarHandleIntHelper.class);
helperClassCache.put(long.class, MemoryAccessVarHandleLongHelper.class);
helperClassCache.put(float.class, MemoryAccessVarHandleFloatHelper.class);
helperClassCache.put(double.class, MemoryAccessVarHandleDoubleHelper.class);
OFFSET_OP_TYPE = MethodType.methodType(long.class, long.class, long.class, MemoryAddressProxy.class);
try {
ADD_OFFSETS_HANDLE = MethodHandles.Lookup.IMPL_LOOKUP.findStatic(MemoryAddressProxy.class, "addOffsets", OFFSET_OP_TYPE);
MUL_OFFSETS_HANDLE = MethodHandles.Lookup.IMPL_LOOKUP.findStatic(MemoryAddressProxy.class, "multiplyOffsets", OFFSET_OP_TYPE);
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
private static final File DEBUG_DUMP_CLASSES_DIR;
@ -111,15 +126,14 @@ class AddressVarHandleGenerator {
}
}
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;
private final Object[] classData;
AddressVarHandleGenerator(Class<?> carrier, int dims) {
MemoryAccessVarHandleGenerator(Class<?> carrier, int dims) {
this.dimensions = dims;
this.carrier = carrier;
Class<?>[] components = new Class<?>[dimensions];
@ -127,6 +141,10 @@ class AddressVarHandleGenerator {
this.form = new VarForm(BASE_CLASS, MemoryAddressProxy.class, carrier, components);
this.helperClass = helperClassCache.get(carrier);
this.implClassName = helperClass.getName().replace('.', '/') + dimensions;
// live constants
Class<?>[] intermediate = new Class<?>[dimensions];
Arrays.fill(intermediate, long.class);
this.classData = new Object[] { carrier, intermediate, ADD_OFFSETS_HANDLE, MUL_OFFSETS_HANDLE };
}
/*
@ -134,18 +152,24 @@ class AddressVarHandleGenerator {
* The factory has type (ZJJ[J)VarHandle.
*/
MethodHandle generateHandleFactory() {
Class<?> implCls = generateClass();
byte[] classBytes = generateClassBytes();
if (DEBUG_DUMP_CLASSES_DIR != null) {
debugWriteClassToFile(classBytes);
}
try {
MethodHandles.Lookup lookup = MethodHandles.lookup().defineHiddenClassWithClassData(classBytes, classData);
Class<?> implCls = lookup.lookupClass();
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);
MethodHandle constr = lookup.findConstructor(implCls, constrType);
constr = MethodHandles.insertArguments(constr, 0, form);
return constr;
} catch (Throwable ex) {
debugPrintClass(classBytes);
throw new AssertionError(ex);
}
}
@ -154,20 +178,22 @@ class AddressVarHandleGenerator {
* Generate a specialized VarHandle class for given carrier
* and access coordinates.
*/
Class<?> generateClass() {
BinderClassWriter cw = new BinderClassWriter();
byte[] generateClassBytes() {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
if (DEBUG) {
System.out.println("Generating header implementation class");
}
cw.visit(52, ACC_PUBLIC | ACC_SUPER, implClassName, null, Type.getInternalName(BASE_CLASS), null);
cw.visit(V14, 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);
}
addStaticInitializer(cw);
addConstructor(cw);
addAccessModeTypeMethod(cw);
@ -180,13 +206,53 @@ class AddressVarHandleGenerator {
addAccessModeMethodIfNeeded(mode, cw);
}
cw.visitEnd();
byte[] classBytes = cw.toByteArray();
return defineClass(cw, classBytes);
return cw.toByteArray();
}
void addConstructor(BinderClassWriter cw) {
void addStaticInitializer(ClassWriter cw) {
// carrier and intermediate
cw.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, "carrier", Class.class.descriptorString(), null, null);
cw.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, "intermediate", Class[].class.descriptorString(), null, null);
cw.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, "addHandle", MethodHandle.class.descriptorString(), null, null);
cw.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, "mulHandle", MethodHandle.class.descriptorString(), null, null);
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_STATIC, "<clinit>", "()V", null, null);
mv.visitCode();
// extract class data in static final fields
MethodType mtype = MethodType.methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class);
Handle bsm = new Handle(H_INVOKESTATIC, Type.getInternalName(MethodHandles.class), "classData",
mtype.descriptorString(), false);
ConstantDynamic dynamic = new ConstantDynamic("classData", Object[].class.descriptorString(), bsm);
mv.visitLdcInsn(dynamic);
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Object[].class));
mv.visitVarInsn(ASTORE, 0);
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(ICONST_0);
mv.visitInsn(AALOAD);
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Class.class));
mv.visitFieldInsn(PUTSTATIC, implClassName, "carrier", Class.class.descriptorString());
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(ICONST_1);
mv.visitInsn(AALOAD);
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Class[].class));
mv.visitFieldInsn(PUTSTATIC, implClassName, "intermediate", Class[].class.descriptorString());
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(ICONST_2);
mv.visitInsn(AALOAD);
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(MethodHandle.class));
mv.visitFieldInsn(PUTSTATIC, implClassName, "addHandle", MethodHandle.class.descriptorString());
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(ICONST_3);
mv.visitInsn(AALOAD);
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(MethodHandle.class));
mv.visitFieldInsn(PUTSTATIC, implClassName, "mulHandle", MethodHandle.class.descriptorString());
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
void addConstructor(ClassWriter 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();
@ -213,21 +279,16 @@ class AddressVarHandleGenerator {
mv.visitEnd();
}
void addAccessModeTypeMethod(BinderClassWriter cw) {
void addAccessModeTypeMethod(ClassWriter 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.visitFieldInsn(GETFIELD, Type.getInternalName(VarHandle.AccessMode.class), "at", VarHandle.AccessType.class.descriptorString());
mv.visitLdcInsn(Type.getType(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.visitFieldInsn(GETSTATIC, implClassName, "carrier", Class.class.descriptorString());
mv.visitFieldInsn(GETSTATIC, implClassName, "intermediate", Class[].class.descriptorString());
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(VarHandle.AccessType.class),
"accessModeType", MethodType.methodType(MethodType.class, Class.class, Class.class, Class[].class).toMethodDescriptorString(), false);
@ -238,10 +299,10 @@ class AddressVarHandleGenerator {
mv.visitEnd();
}
void addAccessModeMethodIfNeeded(VarHandle.AccessMode mode, BinderClassWriter cw) {
void addAccessModeMethodIfNeeded(VarHandle.AccessMode mode, ClassWriter cw) {
String methName = mode.methodName();
MethodType methType = form.getMethodType(mode.at.ordinal())
.insertParameterTypes(0, BASE_CLASS);
.insertParameterTypes(0, VarHandle.class);
try {
MethodType helperType = methType.insertParameterTypes(2, long.class);
@ -266,14 +327,38 @@ class AddressVarHandleGenerator {
// offset calculation
int slot = 2;
mv.visitVarInsn(ALOAD, 0); // load recv
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(BASE_CLASS));
mv.visitFieldInsn(GETFIELD, Type.getInternalName(BASE_CLASS), "offset", "J");
for (int i = 0 ; i < dimensions ; i++) {
// load ADD MH
mv.visitFieldInsn(GETSTATIC, implClassName, "addHandle", MethodHandle.class.descriptorString());
//fixup stack so that ADD MH ends up bottom
mv.visitInsn(Opcodes.DUP_X2);
mv.visitInsn(Opcodes.POP);
// load MUL MH
mv.visitFieldInsn(GETSTATIC, implClassName, "mulHandle", MethodHandle.class.descriptorString());
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(MethodHandle.class));
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);
mv.visitVarInsn(ALOAD, 1); // receiver
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(MemoryAddressProxy.class));
//MUL
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(MethodHandle.class), "invokeExact",
OFFSET_OP_TYPE.toMethodDescriptorString(), false);
mv.visitVarInsn(ALOAD, 1); // receiver
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(MemoryAddressProxy.class));
//ADD
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(MethodHandle.class), "invokeExact",
OFFSET_OP_TYPE.toMethodDescriptorString(), false);
slot += 2;
}
@ -296,7 +381,7 @@ class AddressVarHandleGenerator {
}
}
void addStridesAccessor(BinderClassWriter cw) {
void addStridesAccessor(ClassWriter cw) {
MethodVisitor mv = cw.visitMethod(ACC_FINAL, "strides", "()[J", null, null);
mv.visitCode();
iConstInsn(mv, dimensions);
@ -315,31 +400,15 @@ class AddressVarHandleGenerator {
mv.visitEnd();
}
void addCarrierAccessor(BinderClassWriter cw) {
void addCarrierAccessor(ClassWriter 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.visitFieldInsn(GETSTATIC, implClassName, "carrier", Class.class.descriptorString());
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) {
@ -438,57 +507,4 @@ class AddressVarHandleGenerator {
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

@ -1802,42 +1802,90 @@ abstract class MethodHandleImpl {
}
@Override
public VarHandle memoryAddressViewVarHandle(Class<?> carrier, long alignmentMask,
ByteOrder order, long offset, long[] strides) {
public VarHandle memoryAccessVarHandle(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();
return checkMemoryAccessHandle(handle).carrier();
}
@Override
public long memoryAddressAlignmentMask(VarHandle handle) {
return checkMemAccessHandle(handle).alignmentMask;
return checkMemoryAccessHandle(handle).alignmentMask;
}
@Override
public ByteOrder memoryAddressByteOrder(VarHandle handle) {
return checkMemAccessHandle(handle).be ?
return checkMemoryAccessHandle(handle).be ?
ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
}
@Override
public long memoryAddressOffset(VarHandle handle) {
return checkMemAccessHandle(handle).offset;
return checkMemoryAccessHandle(handle).offset;
}
@Override
public long[] memoryAddressStrides(VarHandle handle) {
return checkMemAccessHandle(handle).strides();
return checkMemoryAccessHandle(handle).strides();
}
private VarHandleMemoryAddressBase checkMemAccessHandle(VarHandle handle) {
if (!(handle instanceof VarHandleMemoryAddressBase)) {
@Override
public boolean isMemoryAccessVarHandle(VarHandle handle) {
return asMemoryAccessVarHandle(handle) != null;
}
@Override
public VarHandle filterValue(VarHandle target, MethodHandle filterToTarget, MethodHandle filterFromTarget) {
return VarHandles.filterValue(target, filterToTarget, filterFromTarget);
}
@Override
public VarHandle filterCoordinates(VarHandle target, int pos, MethodHandle... filters) {
return VarHandles.filterCoordinates(target, pos, filters);
}
@Override
public VarHandle dropCoordinates(VarHandle target, int pos, Class<?>... valueTypes) {
return VarHandles.dropCoordinates(target, pos, valueTypes);
}
@Override
public VarHandle permuteCoordinates(VarHandle target, List<Class<?>> newCoordinates, int... reorder) {
return VarHandles.permuteCoordinates(target, newCoordinates, reorder);
}
@Override
public VarHandle collectCoordinates(VarHandle target, int pos, MethodHandle filter) {
return VarHandles.collectCoordinates(target, pos, filter);
}
@Override
public VarHandle insertCoordinates(VarHandle target, int pos, Object... values) {
return VarHandles.insertCoordinates(target, pos, values);
}
private MemoryAccessVarHandleBase asMemoryAccessVarHandle(VarHandle handle) {
if (handle instanceof MemoryAccessVarHandleBase) {
return (MemoryAccessVarHandleBase)handle;
} else if (handle.target() instanceof MemoryAccessVarHandleBase) {
// skip first adaptation, since we have to step over MemoryAddressProxy
// see JDK-8237349
return (MemoryAccessVarHandleBase)handle.target();
} else {
return null;
}
}
private MemoryAccessVarHandleBase checkMemoryAccessHandle(VarHandle handle) {
MemoryAccessVarHandleBase base = asMemoryAccessVarHandle(handle);
if (base == null) {
throw new IllegalArgumentException("Not a memory access varhandle: " + handle);
}
return (VarHandleMemoryAddressBase) handle;
return base;
}
});
}

View File

@ -56,6 +56,7 @@ class MethodHandleStatics {
static final int CUSTOMIZE_THRESHOLD;
static final boolean VAR_HANDLE_GUARDS;
static final int MAX_ARITY;
static final boolean VAR_HANDLE_IDENTITY_ADAPT;
static {
Properties props = GetPropertyAction.privilegedGetProperties();
@ -83,6 +84,8 @@ class MethodHandleStatics {
props.getProperty("java.lang.invoke.MethodHandle.CUSTOMIZE_THRESHOLD", "127"));
VAR_HANDLE_GUARDS = Boolean.parseBoolean(
props.getProperty("java.lang.invoke.VarHandle.VAR_HANDLE_GUARDS", "true"));
VAR_HANDLE_IDENTITY_ADAPT = Boolean.parseBoolean(
props.getProperty("java.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT", "false"));
// Do not adjust this except for special platforms:
MAX_ARITY = Integer.parseInt(

View File

@ -4609,7 +4609,7 @@ assert((int)twice.invokeExact(21) == 42);
}
}
private static boolean permuteArgumentChecks(int[] reorder, MethodType newType, MethodType oldType) {
static boolean permuteArgumentChecks(int[] reorder, MethodType newType, MethodType oldType) {
if (newType.returnType() != oldType.returnType())
throw newIllegalArgumentException("return types do not match",
oldType, newType);

View File

@ -45,38 +45,45 @@ final class VarForm {
VarForm(Class<?> implClass, Class<?> receiver, Class<?> value, Class<?>... intermediate) {
this.methodType_table = new MethodType[VarHandle.AccessType.values().length];
if (receiver == null) {
initMethodTypes(value, intermediate);
} else {
Class<?>[] coordinates = new Class<?>[intermediate.length + 1];
coordinates[0] = receiver;
System.arraycopy(intermediate, 0, coordinates, 1, intermediate.length);
initMethodTypes(value, coordinates);
}
// TODO lazily calculate
this.memberName_table = linkFromStatic(implClass);
}
// (Receiver, <Intermediates>)
List<Class<?>> l = new ArrayList<>();
if (receiver != null)
l.add(receiver);
for (Class<?> c : intermediate)
l.add(c);
VarForm(Class<?> value, Class<?>[] coordinates) {
this.methodType_table = new MethodType[VarHandle.AccessType.values().length];
this.memberName_table = null;
initMethodTypes(value, coordinates);
}
void initMethodTypes(Class<?> value, Class<?>... coordinates) {
// (Receiver, <Intermediates>)Value
methodType_table[VarHandle.AccessType.GET.ordinal()] =
MethodType.methodType(value, l).erase();
MethodType.methodType(value, coordinates).erase();
// (Receiver, <Intermediates>, Value)void
l.add(value);
methodType_table[VarHandle.AccessType.SET.ordinal()] =
MethodType.methodType(void.class, l).erase();
MethodType.methodType(void.class, coordinates).appendParameterTypes(value).erase();
// (Receiver, <Intermediates>, Value)Value
methodType_table[VarHandle.AccessType.GET_AND_UPDATE.ordinal()] =
MethodType.methodType(value, l).erase();
MethodType.methodType(value, coordinates).appendParameterTypes(value).erase();
// (Receiver, <Intermediates>, Value, Value)boolean
l.add(value);
methodType_table[VarHandle.AccessType.COMPARE_AND_SET.ordinal()] =
MethodType.methodType(boolean.class, l).erase();
MethodType.methodType(boolean.class, coordinates).appendParameterTypes(value, value).erase();
// (Receiver, <Intermediates>, Value, Value)Value
methodType_table[VarHandle.AccessType.COMPARE_AND_EXCHANGE.ordinal()] =
MethodType.methodType(value, l).erase();
MethodType.methodType(value, coordinates).appendParameterTypes(value, value).erase();
}
@ForceInline

View File

@ -455,6 +455,16 @@ public abstract class VarHandle implements Constable {
return new UnsupportedOperationException();
}
boolean isDirect() {
return true;
}
VarHandle asDirect() {
return this;
}
VarHandle target() { return null; }
// Plain accessors
/**
@ -1882,7 +1892,7 @@ public abstract class VarHandle implements Constable {
*
* @return the variable type of variables referenced by this VarHandle
*/
public final Class<?> varType() {
public Class<?> varType() {
MethodType typeSet = accessModeType(AccessMode.SET);
return typeSet.parameterType(typeSet.parameterCount() - 1);
}
@ -1893,7 +1903,7 @@ public abstract class VarHandle implements Constable {
* @return the coordinate types for this VarHandle. The returned
* list is unmodifiable
*/
public final List<Class<?>> coordinateTypes() {
public List<Class<?>> coordinateTypes() {
MethodType typeGet = accessModeType(AccessMode.GET);
return typeGet.parameterList();
}
@ -1958,7 +1968,7 @@ public abstract class VarHandle implements Constable {
* signature-polymorphic method of the same name
* @return a method handle bound to this VarHandle and the given access mode
*/
public final MethodHandle toMethodHandle(AccessMode accessMode) {
public MethodHandle toMethodHandle(AccessMode accessMode) {
MemberName mn = AccessMode.getMemberName(accessMode.ordinal(), vform);
if (mn != null) {
MethodHandle mh = getMethodHandle(accessMode.ordinal());
@ -2008,7 +2018,7 @@ public abstract class VarHandle implements Constable {
}
@ForceInline
final MethodHandle getMethodHandle(int mode) {
MethodHandle getMethodHandle(int mode) {
TypesAndInvokers tis = getTypesAndInvokers();
MethodHandle mh = tis.methodHandle_table[mode];
if (mh == null) {

View File

@ -27,14 +27,22 @@ package java.lang.invoke;
import sun.invoke.util.Wrapper;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
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.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Stream;
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
import static java.lang.invoke.MethodHandleStatics.VAR_HANDLE_IDENTITY_ADAPT;
import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException;
final class VarHandles {
@ -49,49 +57,49 @@ final class VarHandles {
if (!f.isStatic()) {
long foffset = MethodHandleNatives.objectFieldOffset(f);
if (!type.isPrimitive()) {
return f.isFinal() && !isWriteAllowedOnFinalFields
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
? new VarHandleReferences.FieldInstanceReadOnly(refc, foffset, type)
: new VarHandleReferences.FieldInstanceReadWrite(refc, foffset, type);
: new VarHandleReferences.FieldInstanceReadWrite(refc, foffset, type));
}
else if (type == boolean.class) {
return f.isFinal() && !isWriteAllowedOnFinalFields
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
? new VarHandleBooleans.FieldInstanceReadOnly(refc, foffset)
: new VarHandleBooleans.FieldInstanceReadWrite(refc, foffset);
: new VarHandleBooleans.FieldInstanceReadWrite(refc, foffset));
}
else if (type == byte.class) {
return f.isFinal() && !isWriteAllowedOnFinalFields
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
? new VarHandleBytes.FieldInstanceReadOnly(refc, foffset)
: new VarHandleBytes.FieldInstanceReadWrite(refc, foffset);
: new VarHandleBytes.FieldInstanceReadWrite(refc, foffset));
}
else if (type == short.class) {
return f.isFinal() && !isWriteAllowedOnFinalFields
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
? new VarHandleShorts.FieldInstanceReadOnly(refc, foffset)
: new VarHandleShorts.FieldInstanceReadWrite(refc, foffset);
: new VarHandleShorts.FieldInstanceReadWrite(refc, foffset));
}
else if (type == char.class) {
return f.isFinal() && !isWriteAllowedOnFinalFields
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
? new VarHandleChars.FieldInstanceReadOnly(refc, foffset)
: new VarHandleChars.FieldInstanceReadWrite(refc, foffset);
: new VarHandleChars.FieldInstanceReadWrite(refc, foffset));
}
else if (type == int.class) {
return f.isFinal() && !isWriteAllowedOnFinalFields
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
? new VarHandleInts.FieldInstanceReadOnly(refc, foffset)
: new VarHandleInts.FieldInstanceReadWrite(refc, foffset);
: new VarHandleInts.FieldInstanceReadWrite(refc, foffset));
}
else if (type == long.class) {
return f.isFinal() && !isWriteAllowedOnFinalFields
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
? new VarHandleLongs.FieldInstanceReadOnly(refc, foffset)
: new VarHandleLongs.FieldInstanceReadWrite(refc, foffset);
: new VarHandleLongs.FieldInstanceReadWrite(refc, foffset));
}
else if (type == float.class) {
return f.isFinal() && !isWriteAllowedOnFinalFields
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
? new VarHandleFloats.FieldInstanceReadOnly(refc, foffset)
: new VarHandleFloats.FieldInstanceReadWrite(refc, foffset);
: new VarHandleFloats.FieldInstanceReadWrite(refc, foffset));
}
else if (type == double.class) {
return f.isFinal() && !isWriteAllowedOnFinalFields
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
? new VarHandleDoubles.FieldInstanceReadOnly(refc, foffset)
: new VarHandleDoubles.FieldInstanceReadWrite(refc, foffset);
: new VarHandleDoubles.FieldInstanceReadWrite(refc, foffset));
}
else {
throw new UnsupportedOperationException();
@ -110,49 +118,49 @@ final class VarHandles {
Object base = MethodHandleNatives.staticFieldBase(f);
long foffset = MethodHandleNatives.staticFieldOffset(f);
if (!type.isPrimitive()) {
return f.isFinal() && !isWriteAllowedOnFinalFields
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
? new VarHandleReferences.FieldStaticReadOnly(base, foffset, type)
: new VarHandleReferences.FieldStaticReadWrite(base, foffset, type);
: new VarHandleReferences.FieldStaticReadWrite(base, foffset, type));
}
else if (type == boolean.class) {
return f.isFinal() && !isWriteAllowedOnFinalFields
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
? new VarHandleBooleans.FieldStaticReadOnly(base, foffset)
: new VarHandleBooleans.FieldStaticReadWrite(base, foffset);
: new VarHandleBooleans.FieldStaticReadWrite(base, foffset));
}
else if (type == byte.class) {
return f.isFinal() && !isWriteAllowedOnFinalFields
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
? new VarHandleBytes.FieldStaticReadOnly(base, foffset)
: new VarHandleBytes.FieldStaticReadWrite(base, foffset);
: new VarHandleBytes.FieldStaticReadWrite(base, foffset));
}
else if (type == short.class) {
return f.isFinal() && !isWriteAllowedOnFinalFields
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
? new VarHandleShorts.FieldStaticReadOnly(base, foffset)
: new VarHandleShorts.FieldStaticReadWrite(base, foffset);
: new VarHandleShorts.FieldStaticReadWrite(base, foffset));
}
else if (type == char.class) {
return f.isFinal() && !isWriteAllowedOnFinalFields
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
? new VarHandleChars.FieldStaticReadOnly(base, foffset)
: new VarHandleChars.FieldStaticReadWrite(base, foffset);
: new VarHandleChars.FieldStaticReadWrite(base, foffset));
}
else if (type == int.class) {
return f.isFinal() && !isWriteAllowedOnFinalFields
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
? new VarHandleInts.FieldStaticReadOnly(base, foffset)
: new VarHandleInts.FieldStaticReadWrite(base, foffset);
: new VarHandleInts.FieldStaticReadWrite(base, foffset));
}
else if (type == long.class) {
return f.isFinal() && !isWriteAllowedOnFinalFields
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
? new VarHandleLongs.FieldStaticReadOnly(base, foffset)
: new VarHandleLongs.FieldStaticReadWrite(base, foffset);
: new VarHandleLongs.FieldStaticReadWrite(base, foffset));
}
else if (type == float.class) {
return f.isFinal() && !isWriteAllowedOnFinalFields
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
? new VarHandleFloats.FieldStaticReadOnly(base, foffset)
: new VarHandleFloats.FieldStaticReadWrite(base, foffset);
: new VarHandleFloats.FieldStaticReadWrite(base, foffset));
}
else if (type == double.class) {
return f.isFinal() && !isWriteAllowedOnFinalFields
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
? new VarHandleDoubles.FieldStaticReadOnly(base, foffset)
: new VarHandleDoubles.FieldStaticReadWrite(base, foffset);
: new VarHandleDoubles.FieldStaticReadWrite(base, foffset));
}
else {
throw new UnsupportedOperationException();
@ -203,31 +211,31 @@ final class VarHandles {
int ashift = 31 - Integer.numberOfLeadingZeros(ascale);
if (!componentType.isPrimitive()) {
return new VarHandleReferences.Array(aoffset, ashift, arrayClass);
return maybeAdapt(new VarHandleReferences.Array(aoffset, ashift, arrayClass));
}
else if (componentType == boolean.class) {
return new VarHandleBooleans.Array(aoffset, ashift);
return maybeAdapt(new VarHandleBooleans.Array(aoffset, ashift));
}
else if (componentType == byte.class) {
return new VarHandleBytes.Array(aoffset, ashift);
return maybeAdapt(new VarHandleBytes.Array(aoffset, ashift));
}
else if (componentType == short.class) {
return new VarHandleShorts.Array(aoffset, ashift);
return maybeAdapt(new VarHandleShorts.Array(aoffset, ashift));
}
else if (componentType == char.class) {
return new VarHandleChars.Array(aoffset, ashift);
return maybeAdapt(new VarHandleChars.Array(aoffset, ashift));
}
else if (componentType == int.class) {
return new VarHandleInts.Array(aoffset, ashift);
return maybeAdapt(new VarHandleInts.Array(aoffset, ashift));
}
else if (componentType == long.class) {
return new VarHandleLongs.Array(aoffset, ashift);
return maybeAdapt(new VarHandleLongs.Array(aoffset, ashift));
}
else if (componentType == float.class) {
return new VarHandleFloats.Array(aoffset, ashift);
return maybeAdapt(new VarHandleFloats.Array(aoffset, ashift));
}
else if (componentType == double.class) {
return new VarHandleDoubles.Array(aoffset, ashift);
return maybeAdapt(new VarHandleDoubles.Array(aoffset, ashift));
}
else {
throw new UnsupportedOperationException();
@ -242,22 +250,22 @@ final class VarHandles {
Class<?> viewComponentType = viewArrayClass.getComponentType();
if (viewComponentType == long.class) {
return new VarHandleByteArrayAsLongs.ArrayHandle(be);
return maybeAdapt(new VarHandleByteArrayAsLongs.ArrayHandle(be));
}
else if (viewComponentType == int.class) {
return new VarHandleByteArrayAsInts.ArrayHandle(be);
return maybeAdapt(new VarHandleByteArrayAsInts.ArrayHandle(be));
}
else if (viewComponentType == short.class) {
return new VarHandleByteArrayAsShorts.ArrayHandle(be);
return maybeAdapt(new VarHandleByteArrayAsShorts.ArrayHandle(be));
}
else if (viewComponentType == char.class) {
return new VarHandleByteArrayAsChars.ArrayHandle(be);
return maybeAdapt(new VarHandleByteArrayAsChars.ArrayHandle(be));
}
else if (viewComponentType == double.class) {
return new VarHandleByteArrayAsDoubles.ArrayHandle(be);
return maybeAdapt(new VarHandleByteArrayAsDoubles.ArrayHandle(be));
}
else if (viewComponentType == float.class) {
return new VarHandleByteArrayAsFloats.ArrayHandle(be);
return maybeAdapt(new VarHandleByteArrayAsFloats.ArrayHandle(be));
}
throw new UnsupportedOperationException();
@ -271,22 +279,22 @@ final class VarHandles {
Class<?> viewComponentType = viewArrayClass.getComponentType();
if (viewComponentType == long.class) {
return new VarHandleByteArrayAsLongs.ByteBufferHandle(be);
return maybeAdapt(new VarHandleByteArrayAsLongs.ByteBufferHandle(be));
}
else if (viewComponentType == int.class) {
return new VarHandleByteArrayAsInts.ByteBufferHandle(be);
return maybeAdapt(new VarHandleByteArrayAsInts.ByteBufferHandle(be));
}
else if (viewComponentType == short.class) {
return new VarHandleByteArrayAsShorts.ByteBufferHandle(be);
return maybeAdapt(new VarHandleByteArrayAsShorts.ByteBufferHandle(be));
}
else if (viewComponentType == char.class) {
return new VarHandleByteArrayAsChars.ByteBufferHandle(be);
return maybeAdapt(new VarHandleByteArrayAsChars.ByteBufferHandle(be));
}
else if (viewComponentType == double.class) {
return new VarHandleByteArrayAsDoubles.ByteBufferHandle(be);
return maybeAdapt(new VarHandleByteArrayAsDoubles.ByteBufferHandle(be));
}
else if (viewComponentType == float.class) {
return new VarHandleByteArrayAsFloats.ByteBufferHandle(be);
return maybeAdapt(new VarHandleByteArrayAsFloats.ByteBufferHandle(be));
}
throw new UnsupportedOperationException();
@ -319,16 +327,257 @@ final class VarHandles {
Map<Integer, MethodHandle> carrierFactory = ADDRESS_FACTORIES.get(carrier);
MethodHandle fac = carrierFactory.computeIfAbsent(strides.length,
dims -> new AddressVarHandleGenerator(carrier, dims)
dims -> new MemoryAccessVarHandleGenerator(carrier, dims)
.generateHandleFactory());
try {
return (VarHandle)fac.invoke(be, size, offset, alignmentMask, strides);
return maybeAdapt((VarHandle)fac.invoke(be, size, offset, alignmentMask, strides));
} catch (Throwable ex) {
throw new IllegalStateException(ex);
}
}
private static VarHandle maybeAdapt(VarHandle target) {
if (!VAR_HANDLE_IDENTITY_ADAPT) return target;
target = filterValue(target,
MethodHandles.identity(target.varType()), MethodHandles.identity(target.varType()));
MethodType mtype = target.accessModeType(VarHandle.AccessMode.GET).dropParameterTypes(0, 1);
for (int i = 0 ; i < mtype.parameterCount() ; i++) {
target = filterCoordinates(target, i, MethodHandles.identity(mtype.parameterType(i)));
}
return target;
}
public static VarHandle filterValue(VarHandle target, MethodHandle filterToTarget, MethodHandle filterFromTarget) {
Objects.nonNull(target);
Objects.nonNull(filterToTarget);
Objects.nonNull(filterFromTarget);
//check that from/to filters do not throw checked exceptions
noCheckedExceptions(filterToTarget);
noCheckedExceptions(filterFromTarget);
//check that from/to filters have right signatures
if (filterFromTarget.type().parameterCount() != 1) {
throw newIllegalArgumentException("filterFromTarget filter type has wrong arity", filterFromTarget.type());
} else if (filterToTarget.type().parameterCount() != 1) {
throw newIllegalArgumentException("filterToTarget filter type has wrong arity", filterFromTarget.type());
} else if (filterFromTarget.type().parameterType(0) != filterToTarget.type().returnType() ||
filterToTarget.type().parameterType(0) != filterFromTarget.type().returnType()) {
throw newIllegalArgumentException("filterFromTarget and filterToTarget filter types do not match", filterFromTarget.type(), filterToTarget.type());
} else if (target.varType() != filterFromTarget.type().parameterType(0)) {
throw newIllegalArgumentException("filterFromTarget filter type does not match target var handle type", filterFromTarget.type(), target.varType());
} else if (target.varType() != filterToTarget.type().returnType()) {
throw newIllegalArgumentException("filterFromTarget filter type does not match target var handle type", filterToTarget.type(), target.varType());
}
return new IndirectVarHandle(target, filterFromTarget.type().returnType(), target.coordinateTypes().toArray(new Class<?>[0]),
(mode, modeHandle) -> {
int lastParameterPos = modeHandle.type().parameterCount() - 1;
return switch (mode.at) {
case GET -> MethodHandles.filterReturnValue(modeHandle, filterFromTarget);
case SET -> MethodHandles.filterArgument(modeHandle, lastParameterPos, filterToTarget);
case GET_AND_UPDATE -> {
MethodHandle adapter = MethodHandles.filterReturnValue(modeHandle, filterFromTarget);
yield MethodHandles.filterArgument(adapter, lastParameterPos, filterToTarget);
}
case COMPARE_AND_EXCHANGE -> {
MethodHandle adapter = MethodHandles.filterReturnValue(modeHandle, filterFromTarget);
adapter = MethodHandles.filterArgument(adapter, lastParameterPos, filterToTarget);
yield MethodHandles.filterArgument(adapter, lastParameterPos - 1, filterToTarget);
}
case COMPARE_AND_SET -> {
MethodHandle adapter = MethodHandles.filterArgument(modeHandle, lastParameterPos, filterToTarget);
yield MethodHandles.filterArgument(adapter, lastParameterPos - 1, filterToTarget);
}
};
});
}
public static VarHandle filterCoordinates(VarHandle target, int pos, MethodHandle... filters) {
Objects.nonNull(target);
Objects.nonNull(filters);
List<Class<?>> targetCoordinates = target.coordinateTypes();
if (pos < 0 || pos >= targetCoordinates.size()) {
throw newIllegalArgumentException("Invalid position " + pos + " for coordinate types", targetCoordinates);
} else if (pos + filters.length > targetCoordinates.size()) {
throw new IllegalArgumentException("Too many filters");
}
if (filters.length == 0) return target;
List<Class<?>> newCoordinates = new ArrayList<>(targetCoordinates);
for (int i = 0 ; i < filters.length ; i++) {
noCheckedExceptions(filters[i]);
MethodType filterType = filters[i].type();
if (filterType.parameterCount() != 1) {
throw newIllegalArgumentException("Invalid filter type " + filterType);
} else if (newCoordinates.get(pos + i) != filterType.returnType()) {
throw newIllegalArgumentException("Invalid filter type " + filterType + " for coordinate type " + newCoordinates.get(i));
}
newCoordinates.set(pos + i, filters[i].type().parameterType(0));
}
return new IndirectVarHandle(target, target.varType(), newCoordinates.toArray(new Class<?>[0]),
(mode, modeHandle) -> MethodHandles.filterArguments(modeHandle, 1 + pos, filters));
}
public static VarHandle insertCoordinates(VarHandle target, int pos, Object... values) {
Objects.nonNull(target);
Objects.nonNull(values);
List<Class<?>> targetCoordinates = target.coordinateTypes();
if (pos < 0 || pos >= targetCoordinates.size()) {
throw newIllegalArgumentException("Invalid position " + pos + " for coordinate types", targetCoordinates);
} else if (pos + values.length > targetCoordinates.size()) {
throw new IllegalArgumentException("Too many values");
}
if (values.length == 0) return target;
List<Class<?>> newCoordinates = new ArrayList<>(targetCoordinates);
for (int i = 0 ; i < values.length ; i++) {
Class<?> pt = newCoordinates.get(pos);
if (pt.isPrimitive()) {
Wrapper w = Wrapper.forPrimitiveType(pt);
w.convert(values[i], pt);
} else {
pt.cast(values[i]);
}
newCoordinates.remove(pos);
}
return new IndirectVarHandle(target, target.varType(), newCoordinates.toArray(new Class<?>[0]),
(mode, modeHandle) -> MethodHandles.insertArguments(modeHandle, 1 + pos, values));
}
public static VarHandle permuteCoordinates(VarHandle target, List<Class<?>> newCoordinates, int... reorder) {
Objects.nonNull(target);
Objects.nonNull(newCoordinates);
Objects.nonNull(reorder);
List<Class<?>> targetCoordinates = target.coordinateTypes();
MethodHandles.permuteArgumentChecks(reorder,
MethodType.methodType(void.class, newCoordinates),
MethodType.methodType(void.class, targetCoordinates));
return new IndirectVarHandle(target, target.varType(), newCoordinates.toArray(new Class<?>[0]),
(mode, modeHandle) ->
MethodHandles.permuteArguments(modeHandle,
methodTypeFor(mode.at, modeHandle.type(), targetCoordinates, newCoordinates),
reorderArrayFor(mode.at, newCoordinates, reorder)));
}
private static int numTrailingArgs(VarHandle.AccessType at) {
return switch (at) {
case GET -> 0;
case GET_AND_UPDATE, SET -> 1;
case COMPARE_AND_SET, COMPARE_AND_EXCHANGE -> 2;
};
}
private static int[] reorderArrayFor(VarHandle.AccessType at, List<Class<?>> newCoordinates, int[] reorder) {
int numTrailingArgs = numTrailingArgs(at);
int[] adjustedReorder = new int[reorder.length + 1 + numTrailingArgs];
adjustedReorder[0] = 0;
for (int i = 0 ; i < reorder.length ; i++) {
adjustedReorder[i + 1] = reorder[i] + 1;
}
for (int i = 0 ; i < numTrailingArgs ; i++) {
adjustedReorder[i + reorder.length + 1] = i + newCoordinates.size() + 1;
}
return adjustedReorder;
}
private static MethodType methodTypeFor(VarHandle.AccessType at, MethodType oldType, List<Class<?>> oldCoordinates, List<Class<?>> newCoordinates) {
int numTrailingArgs = numTrailingArgs(at);
MethodType adjustedType = MethodType.methodType(oldType.returnType(), oldType.parameterType(0));
adjustedType = adjustedType.appendParameterTypes(newCoordinates);
for (int i = 0 ; i < numTrailingArgs ; i++) {
adjustedType = adjustedType.appendParameterTypes(oldType.parameterType(1 + oldCoordinates.size() + i));
}
return adjustedType;
}
public static VarHandle collectCoordinates(VarHandle target, int pos, MethodHandle filter) {
Objects.nonNull(target);
Objects.nonNull(filter);
noCheckedExceptions(filter);
List<Class<?>> targetCoordinates = target.coordinateTypes();
if (pos < 0 || pos >= targetCoordinates.size()) {
throw newIllegalArgumentException("Invalid position " + pos + " for coordinate types", targetCoordinates);
} else if (filter.type().returnType() == void.class) {
throw newIllegalArgumentException("Invalid filter type " + filter.type() + " ; filter cannot be void");
} else if (filter.type().returnType() != targetCoordinates.get(pos)) {
throw newIllegalArgumentException("Invalid filter type " + filter.type() + " for coordinate type " + targetCoordinates.get(pos));
}
List<Class<?>> newCoordinates = new ArrayList<>(targetCoordinates);
newCoordinates.remove(pos);
newCoordinates.addAll(pos, filter.type().parameterList());
return new IndirectVarHandle(target, target.varType(), newCoordinates.toArray(new Class<?>[0]),
(mode, modeHandle) -> MethodHandles.collectArguments(modeHandle, 1 + pos, filter));
}
public static VarHandle dropCoordinates(VarHandle target, int pos, Class<?>... valueTypes) {
Objects.nonNull(target);
Objects.nonNull(valueTypes);
List<Class<?>> targetCoordinates = target.coordinateTypes();
if (pos < 0 || pos > targetCoordinates.size()) {
throw newIllegalArgumentException("Invalid position " + pos + " for coordinate types", targetCoordinates);
}
if (valueTypes.length == 0) return target;
List<Class<?>> newCoordinates = new ArrayList<>(targetCoordinates);
newCoordinates.addAll(pos, List.of(valueTypes));
return new IndirectVarHandle(target, target.varType(), newCoordinates.toArray(new Class<?>[0]),
(mode, modeHandle) -> MethodHandles.dropArguments(modeHandle, 1 + pos, valueTypes));
}
private static void noCheckedExceptions(MethodHandle handle) {
if (handle instanceof DirectMethodHandle) {
DirectMethodHandle directHandle = (DirectMethodHandle)handle;
MethodHandleInfo info = MethodHandles.Lookup.IMPL_LOOKUP.revealDirect(directHandle);
Class<?>[] exceptionTypes = switch (info.getReferenceKind()) {
case MethodHandleInfo.REF_invokeInterface, MethodHandleInfo.REF_invokeSpecial,
MethodHandleInfo.REF_invokeStatic, MethodHandleInfo.REF_invokeVirtual ->
info.reflectAs(Method.class, MethodHandles.Lookup.IMPL_LOOKUP).getExceptionTypes();
case MethodHandleInfo.REF_newInvokeSpecial ->
info.reflectAs(Constructor.class, MethodHandles.Lookup.IMPL_LOOKUP).getExceptionTypes();
case MethodHandleInfo.REF_getField, MethodHandleInfo.REF_getStatic,
MethodHandleInfo.REF_putField, MethodHandleInfo.REF_putStatic -> null;
default -> throw new AssertionError("Cannot get here");
};
if (exceptionTypes != null) {
if (Stream.of(exceptionTypes).anyMatch(VarHandles::isCheckedException)) {
throw newIllegalArgumentException("Cannot adapt a var handle with a method handle which throws checked exceptions");
}
}
} else if (handle instanceof DelegatingMethodHandle) {
noCheckedExceptions(((DelegatingMethodHandle)handle).getTarget());
} else {
//bound
BoundMethodHandle boundHandle = (BoundMethodHandle)handle;
for (int i = 0 ; i < boundHandle.fieldCount() ; i++) {
Object arg = boundHandle.arg(i);
if (arg instanceof MethodHandle){
noCheckedExceptions((MethodHandle) arg);
}
}
}
}
private static boolean isCheckedException(Class<?> clazz) {
return Throwable.class.isAssignableFrom(clazz) &&
!RuntimeException.class.isAssignableFrom(clazz) &&
!Error.class.isAssignableFrom(clazz);
}
// /**
// * A helper program to generate the VarHandleGuards class with a set of
// * static guard methods each of which corresponds to a particular shape and
@ -365,7 +614,7 @@ final class VarHandles {
// "@ForceInline\n" +
// "@LambdaForm.Compiled\n" +
// "final static <METHOD> throws Throwable {\n" +
// " if (handle.vform.methodType_table[ad.type] == ad.symbolicMethodType) {\n" +
// " if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodType) {\n" +
// " <RESULT_ERASED>MethodHandle.linkToStatic(<LINK_TO_STATIC_ARGS>);<RETURN_ERASED>\n" +
// " }\n" +
// " else {\n" +
@ -378,10 +627,10 @@ final class VarHandles {
// "@ForceInline\n" +
// "@LambdaForm.Compiled\n" +
// "final static <METHOD> throws Throwable {\n" +
// " if (handle.vform.methodType_table[ad.type] == ad.symbolicMethodType) {\n" +
// " if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodType) {\n" +
// " MethodHandle.linkToStatic(<LINK_TO_STATIC_ARGS>);\n" +
// " }\n" +
// " else if (handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodType) {\n" +
// " else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodType) {\n" +
// " MethodHandle.linkToStatic(<LINK_TO_STATIC_ARGS>);\n" +
// " }\n" +
// " else {\n" +

View File

@ -77,25 +77,29 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ get(FieldInstanceReadOnly handle, Object holder) {
static $type$ get(VarHandle ob, Object holder) {
FieldInstanceReadOnly handle = (FieldInstanceReadOnly)ob;
return UNSAFE.get$Type$(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset);
}
@ForceInline
static $type$ getVolatile(FieldInstanceReadOnly handle, Object holder) {
static $type$ getVolatile(VarHandle ob, Object holder) {
FieldInstanceReadOnly handle = (FieldInstanceReadOnly)ob;
return UNSAFE.get$Type$Volatile(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset);
}
@ForceInline
static $type$ getOpaque(FieldInstanceReadOnly handle, Object holder) {
static $type$ getOpaque(VarHandle ob, Object holder) {
FieldInstanceReadOnly handle = (FieldInstanceReadOnly)ob;
return UNSAFE.get$Type$Opaque(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset);
}
@ForceInline
static $type$ getAcquire(FieldInstanceReadOnly handle, Object holder) {
static $type$ getAcquire(VarHandle ob, Object holder) {
FieldInstanceReadOnly handle = (FieldInstanceReadOnly)ob;
return UNSAFE.get$Type$Acquire(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset);
}
@ -110,28 +114,32 @@ final class VarHandle$Type$s {
}
@ForceInline
static void set(FieldInstanceReadWrite handle, Object holder, $type$ value) {
static void set(VarHandle ob, Object holder, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
UNSAFE.put$Type$(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(value):value});
}
@ForceInline
static void setVolatile(FieldInstanceReadWrite handle, Object holder, $type$ value) {
static void setVolatile(VarHandle ob, Object holder, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
UNSAFE.put$Type$Volatile(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(value):value});
}
@ForceInline
static void setOpaque(FieldInstanceReadWrite handle, Object holder, $type$ value) {
static void setOpaque(VarHandle ob, Object holder, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
UNSAFE.put$Type$Opaque(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(value):value});
}
@ForceInline
static void setRelease(FieldInstanceReadWrite handle, Object holder, $type$ value) {
static void setRelease(VarHandle ob, Object holder, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
UNSAFE.put$Type$Release(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(value):value});
@ -139,7 +147,8 @@ final class VarHandle$Type$s {
#if[CAS]
@ForceInline
static boolean compareAndSet(FieldInstanceReadWrite handle, Object holder, $type$ expected, $type$ value) {
static boolean compareAndSet(VarHandle ob, Object holder, $type$ expected, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.compareAndSet$Type$(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(expected):expected},
@ -147,7 +156,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ compareAndExchange(FieldInstanceReadWrite handle, Object holder, $type$ expected, $type$ value) {
static $type$ compareAndExchange(VarHandle ob, Object holder, $type$ expected, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.compareAndExchange$Type$(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(expected):expected},
@ -155,7 +165,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ compareAndExchangeAcquire(FieldInstanceReadWrite handle, Object holder, $type$ expected, $type$ value) {
static $type$ compareAndExchangeAcquire(VarHandle ob, Object holder, $type$ expected, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.compareAndExchange$Type$Acquire(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(expected):expected},
@ -163,7 +174,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ compareAndExchangeRelease(FieldInstanceReadWrite handle, Object holder, $type$ expected, $type$ value) {
static $type$ compareAndExchangeRelease(VarHandle ob, Object holder, $type$ expected, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.compareAndExchange$Type$Release(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(expected):expected},
@ -171,7 +183,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static boolean weakCompareAndSetPlain(FieldInstanceReadWrite handle, Object holder, $type$ expected, $type$ value) {
static boolean weakCompareAndSetPlain(VarHandle ob, Object holder, $type$ expected, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.weakCompareAndSet$Type$Plain(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(expected):expected},
@ -179,7 +192,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static boolean weakCompareAndSet(FieldInstanceReadWrite handle, Object holder, $type$ expected, $type$ value) {
static boolean weakCompareAndSet(VarHandle ob, Object holder, $type$ expected, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.weakCompareAndSet$Type$(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(expected):expected},
@ -187,7 +201,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static boolean weakCompareAndSetAcquire(FieldInstanceReadWrite handle, Object holder, $type$ expected, $type$ value) {
static boolean weakCompareAndSetAcquire(VarHandle ob, Object holder, $type$ expected, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.weakCompareAndSet$Type$Acquire(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(expected):expected},
@ -195,7 +210,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static boolean weakCompareAndSetRelease(FieldInstanceReadWrite handle, Object holder, $type$ expected, $type$ value) {
static boolean weakCompareAndSetRelease(VarHandle ob, Object holder, $type$ expected, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.weakCompareAndSet$Type$Release(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(expected):expected},
@ -203,21 +219,24 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ getAndSet(FieldInstanceReadWrite handle, Object holder, $type$ value) {
static $type$ getAndSet(VarHandle ob, Object holder, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.getAndSet$Type$(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(value):value});
}
@ForceInline
static $type$ getAndSetAcquire(FieldInstanceReadWrite handle, Object holder, $type$ value) {
static $type$ getAndSetAcquire(VarHandle ob, Object holder, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.getAndSet$Type$Acquire(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(value):value});
}
@ForceInline
static $type$ getAndSetRelease(FieldInstanceReadWrite handle, Object holder, $type$ value) {
static $type$ getAndSetRelease(VarHandle ob, Object holder, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.getAndSet$Type$Release(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(value):value});
@ -226,21 +245,24 @@ final class VarHandle$Type$s {
#if[AtomicAdd]
@ForceInline
static $type$ getAndAdd(FieldInstanceReadWrite handle, Object holder, $type$ value) {
static $type$ getAndAdd(VarHandle ob, Object holder, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.getAndAdd$Type$(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
value);
}
@ForceInline
static $type$ getAndAddAcquire(FieldInstanceReadWrite handle, Object holder, $type$ value) {
static $type$ getAndAddAcquire(VarHandle ob, Object holder, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.getAndAdd$Type$Acquire(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
value);
}
@ForceInline
static $type$ getAndAddRelease(FieldInstanceReadWrite handle, Object holder, $type$ value) {
static $type$ getAndAddRelease(VarHandle ob, Object holder, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.getAndAdd$Type$Release(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
value);
@ -250,63 +272,72 @@ final class VarHandle$Type$s {
#if[Bitwise]
@ForceInline
static $type$ getAndBitwiseOr(FieldInstanceReadWrite handle, Object holder, $type$ value) {
static $type$ getAndBitwiseOr(VarHandle ob, Object holder, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.getAndBitwiseOr$Type$(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
value);
}
@ForceInline
static $type$ getAndBitwiseOrRelease(FieldInstanceReadWrite handle, Object holder, $type$ value) {
static $type$ getAndBitwiseOrRelease(VarHandle ob, Object holder, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.getAndBitwiseOr$Type$Release(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
value);
}
@ForceInline
static $type$ getAndBitwiseOrAcquire(FieldInstanceReadWrite handle, Object holder, $type$ value) {
static $type$ getAndBitwiseOrAcquire(VarHandle ob, Object holder, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.getAndBitwiseOr$Type$Acquire(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
value);
}
@ForceInline
static $type$ getAndBitwiseAnd(FieldInstanceReadWrite handle, Object holder, $type$ value) {
static $type$ getAndBitwiseAnd(VarHandle ob, Object holder, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.getAndBitwiseAnd$Type$(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
value);
}
@ForceInline
static $type$ getAndBitwiseAndRelease(FieldInstanceReadWrite handle, Object holder, $type$ value) {
static $type$ getAndBitwiseAndRelease(VarHandle ob, Object holder, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.getAndBitwiseAnd$Type$Release(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
value);
}
@ForceInline
static $type$ getAndBitwiseAndAcquire(FieldInstanceReadWrite handle, Object holder, $type$ value) {
static $type$ getAndBitwiseAndAcquire(VarHandle ob, Object holder, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.getAndBitwiseAnd$Type$Acquire(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
value);
}
@ForceInline
static $type$ getAndBitwiseXor(FieldInstanceReadWrite handle, Object holder, $type$ value) {
static $type$ getAndBitwiseXor(VarHandle ob, Object holder, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.getAndBitwiseXor$Type$(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
value);
}
@ForceInline
static $type$ getAndBitwiseXorRelease(FieldInstanceReadWrite handle, Object holder, $type$ value) {
static $type$ getAndBitwiseXorRelease(VarHandle ob, Object holder, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.getAndBitwiseXor$Type$Release(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
value);
}
@ForceInline
static $type$ getAndBitwiseXorAcquire(FieldInstanceReadWrite handle, Object holder, $type$ value) {
static $type$ getAndBitwiseXorAcquire(VarHandle ob, Object holder, $type$ value) {
FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
return UNSAFE.getAndBitwiseXor$Type$Acquire(Objects.requireNonNull(handle.receiverType.cast(holder)),
handle.fieldOffset,
value);
@ -359,25 +390,29 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ get(FieldStaticReadOnly handle) {
static $type$ get(VarHandle ob) {
FieldStaticReadOnly handle = (FieldStaticReadOnly)ob;
return UNSAFE.get$Type$(handle.base,
handle.fieldOffset);
}
@ForceInline
static $type$ getVolatile(FieldStaticReadOnly handle) {
static $type$ getVolatile(VarHandle ob) {
FieldStaticReadOnly handle = (FieldStaticReadOnly)ob;
return UNSAFE.get$Type$Volatile(handle.base,
handle.fieldOffset);
}
@ForceInline
static $type$ getOpaque(FieldStaticReadOnly handle) {
static $type$ getOpaque(VarHandle ob) {
FieldStaticReadOnly handle = (FieldStaticReadOnly)ob;
return UNSAFE.get$Type$Opaque(handle.base,
handle.fieldOffset);
}
@ForceInline
static $type$ getAcquire(FieldStaticReadOnly handle) {
static $type$ getAcquire(VarHandle ob) {
FieldStaticReadOnly handle = (FieldStaticReadOnly)ob;
return UNSAFE.get$Type$Acquire(handle.base,
handle.fieldOffset);
}
@ -392,28 +427,32 @@ final class VarHandle$Type$s {
}
@ForceInline
static void set(FieldStaticReadWrite handle, $type$ value) {
static void set(VarHandle ob, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
UNSAFE.put$Type$(handle.base,
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(value):value});
}
@ForceInline
static void setVolatile(FieldStaticReadWrite handle, $type$ value) {
static void setVolatile(VarHandle ob, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
UNSAFE.put$Type$Volatile(handle.base,
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(value):value});
}
@ForceInline
static void setOpaque(FieldStaticReadWrite handle, $type$ value) {
static void setOpaque(VarHandle ob, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
UNSAFE.put$Type$Opaque(handle.base,
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(value):value});
}
@ForceInline
static void setRelease(FieldStaticReadWrite handle, $type$ value) {
static void setRelease(VarHandle ob, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
UNSAFE.put$Type$Release(handle.base,
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(value):value});
@ -421,7 +460,8 @@ final class VarHandle$Type$s {
#if[CAS]
@ForceInline
static boolean compareAndSet(FieldStaticReadWrite handle, $type$ expected, $type$ value) {
static boolean compareAndSet(VarHandle ob, $type$ expected, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.compareAndSet$Type$(handle.base,
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(expected):expected},
@ -430,7 +470,8 @@ final class VarHandle$Type$s {
@ForceInline
static $type$ compareAndExchange(FieldStaticReadWrite handle, $type$ expected, $type$ value) {
static $type$ compareAndExchange(VarHandle ob, $type$ expected, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.compareAndExchange$Type$(handle.base,
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(expected):expected},
@ -438,7 +479,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ compareAndExchangeAcquire(FieldStaticReadWrite handle, $type$ expected, $type$ value) {
static $type$ compareAndExchangeAcquire(VarHandle ob, $type$ expected, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.compareAndExchange$Type$Acquire(handle.base,
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(expected):expected},
@ -446,7 +488,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ compareAndExchangeRelease(FieldStaticReadWrite handle, $type$ expected, $type$ value) {
static $type$ compareAndExchangeRelease(VarHandle ob, $type$ expected, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.compareAndExchange$Type$Release(handle.base,
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(expected):expected},
@ -454,7 +497,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static boolean weakCompareAndSetPlain(FieldStaticReadWrite handle, $type$ expected, $type$ value) {
static boolean weakCompareAndSetPlain(VarHandle ob, $type$ expected, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.weakCompareAndSet$Type$Plain(handle.base,
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(expected):expected},
@ -462,7 +506,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static boolean weakCompareAndSet(FieldStaticReadWrite handle, $type$ expected, $type$ value) {
static boolean weakCompareAndSet(VarHandle ob, $type$ expected, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.weakCompareAndSet$Type$(handle.base,
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(expected):expected},
@ -470,7 +515,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static boolean weakCompareAndSetAcquire(FieldStaticReadWrite handle, $type$ expected, $type$ value) {
static boolean weakCompareAndSetAcquire(VarHandle ob, $type$ expected, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.weakCompareAndSet$Type$Acquire(handle.base,
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(expected):expected},
@ -478,7 +524,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static boolean weakCompareAndSetRelease(FieldStaticReadWrite handle, $type$ expected, $type$ value) {
static boolean weakCompareAndSetRelease(VarHandle ob, $type$ expected, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.weakCompareAndSet$Type$Release(handle.base,
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(expected):expected},
@ -486,21 +533,24 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ getAndSet(FieldStaticReadWrite handle, $type$ value) {
static $type$ getAndSet(VarHandle ob, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.getAndSet$Type$(handle.base,
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(value):value});
}
@ForceInline
static $type$ getAndSetAcquire(FieldStaticReadWrite handle, $type$ value) {
static $type$ getAndSetAcquire(VarHandle ob, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.getAndSet$Type$Acquire(handle.base,
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(value):value});
}
@ForceInline
static $type$ getAndSetRelease(FieldStaticReadWrite handle, $type$ value) {
static $type$ getAndSetRelease(VarHandle ob, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.getAndSet$Type$Release(handle.base,
handle.fieldOffset,
{#if[Object]?handle.fieldType.cast(value):value});
@ -509,21 +559,24 @@ final class VarHandle$Type$s {
#if[AtomicAdd]
@ForceInline
static $type$ getAndAdd(FieldStaticReadWrite handle, $type$ value) {
static $type$ getAndAdd(VarHandle ob, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.getAndAdd$Type$(handle.base,
handle.fieldOffset,
value);
}
@ForceInline
static $type$ getAndAddAcquire(FieldStaticReadWrite handle, $type$ value) {
static $type$ getAndAddAcquire(VarHandle ob, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.getAndAdd$Type$Acquire(handle.base,
handle.fieldOffset,
value);
}
@ForceInline
static $type$ getAndAddRelease(FieldStaticReadWrite handle, $type$ value) {
static $type$ getAndAddRelease(VarHandle ob, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.getAndAdd$Type$Release(handle.base,
handle.fieldOffset,
value);
@ -532,63 +585,72 @@ final class VarHandle$Type$s {
#if[Bitwise]
@ForceInline
static $type$ getAndBitwiseOr(FieldStaticReadWrite handle, $type$ value) {
static $type$ getAndBitwiseOr(VarHandle ob, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.getAndBitwiseOr$Type$(handle.base,
handle.fieldOffset,
value);
}
@ForceInline
static $type$ getAndBitwiseOrRelease(FieldStaticReadWrite handle, $type$ value) {
static $type$ getAndBitwiseOrRelease(VarHandle ob, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.getAndBitwiseOr$Type$Release(handle.base,
handle.fieldOffset,
value);
}
@ForceInline
static $type$ getAndBitwiseOrAcquire(FieldStaticReadWrite handle, $type$ value) {
static $type$ getAndBitwiseOrAcquire(VarHandle ob, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.getAndBitwiseOr$Type$Acquire(handle.base,
handle.fieldOffset,
value);
}
@ForceInline
static $type$ getAndBitwiseAnd(FieldStaticReadWrite handle, $type$ value) {
static $type$ getAndBitwiseAnd(VarHandle ob, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.getAndBitwiseAnd$Type$(handle.base,
handle.fieldOffset,
value);
}
@ForceInline
static $type$ getAndBitwiseAndRelease(FieldStaticReadWrite handle, $type$ value) {
static $type$ getAndBitwiseAndRelease(VarHandle ob, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.getAndBitwiseAnd$Type$Release(handle.base,
handle.fieldOffset,
value);
}
@ForceInline
static $type$ getAndBitwiseAndAcquire(FieldStaticReadWrite handle, $type$ value) {
static $type$ getAndBitwiseAndAcquire(VarHandle ob, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.getAndBitwiseAnd$Type$Acquire(handle.base,
handle.fieldOffset,
value);
}
@ForceInline
static $type$ getAndBitwiseXor(FieldStaticReadWrite handle, $type$ value) {
static $type$ getAndBitwiseXor(VarHandle ob, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.getAndBitwiseXor$Type$(handle.base,
handle.fieldOffset,
value);
}
@ForceInline
static $type$ getAndBitwiseXorRelease(FieldStaticReadWrite handle, $type$ value) {
static $type$ getAndBitwiseXorRelease(VarHandle ob, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.getAndBitwiseXor$Type$Release(handle.base,
handle.fieldOffset,
value);
}
@ForceInline
static $type$ getAndBitwiseXorAcquire(FieldStaticReadWrite handle, $type$ value) {
static $type$ getAndBitwiseXorAcquire(VarHandle ob, $type$ value) {
FieldStaticReadWrite handle = (FieldStaticReadWrite)ob;
return UNSAFE.getAndBitwiseXor$Type$Acquire(handle.base,
handle.fieldOffset,
value);
@ -654,7 +716,8 @@ final class VarHandle$Type$s {
#end[Object]
@ForceInline
static $type$ get(Array handle, Object oarray, int index) {
static $type$ get(VarHandle ob, Object oarray, int index) {
Array handle = (Array)ob;
#if[Object]
Object[] array = (Object[]) handle.arrayType.cast(oarray);
#else[Object]
@ -664,7 +727,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static void set(Array handle, Object oarray, int index, $type$ value) {
static void set(VarHandle ob, Object oarray, int index, $type$ value) {
Array handle = (Array)ob;
#if[Object]
Object[] array = (Object[]) handle.arrayType.cast(oarray);
#else[Object]
@ -674,7 +738,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ getVolatile(Array handle, Object oarray, int index) {
static $type$ getVolatile(VarHandle ob, Object oarray, int index) {
Array handle = (Array)ob;
#if[Object]
Object[] array = (Object[]) handle.arrayType.cast(oarray);
#else[Object]
@ -685,7 +750,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static void setVolatile(Array handle, Object oarray, int index, $type$ value) {
static void setVolatile(VarHandle ob, Object oarray, int index, $type$ value) {
Array handle = (Array)ob;
#if[Object]
Object[] array = (Object[]) handle.arrayType.cast(oarray);
#else[Object]
@ -697,7 +763,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ getOpaque(Array handle, Object oarray, int index) {
static $type$ getOpaque(VarHandle ob, Object oarray, int index) {
Array handle = (Array)ob;
#if[Object]
Object[] array = (Object[]) handle.arrayType.cast(oarray);
#else[Object]
@ -708,7 +775,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static void setOpaque(Array handle, Object oarray, int index, $type$ value) {
static void setOpaque(VarHandle ob, Object oarray, int index, $type$ value) {
Array handle = (Array)ob;
#if[Object]
Object[] array = (Object[]) handle.arrayType.cast(oarray);
#else[Object]
@ -720,7 +788,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ getAcquire(Array handle, Object oarray, int index) {
static $type$ getAcquire(VarHandle ob, Object oarray, int index) {
Array handle = (Array)ob;
#if[Object]
Object[] array = (Object[]) handle.arrayType.cast(oarray);
#else[Object]
@ -731,7 +800,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static void setRelease(Array handle, Object oarray, int index, $type$ value) {
static void setRelease(VarHandle ob, Object oarray, int index, $type$ value) {
Array handle = (Array)ob;
#if[Object]
Object[] array = (Object[]) handle.arrayType.cast(oarray);
#else[Object]
@ -744,7 +814,8 @@ final class VarHandle$Type$s {
#if[CAS]
@ForceInline
static boolean compareAndSet(Array handle, Object oarray, int index, $type$ expected, $type$ value) {
static boolean compareAndSet(VarHandle ob, Object oarray, int index, $type$ expected, $type$ value) {
Array handle = (Array)ob;
#if[Object]
Object[] array = (Object[]) handle.arrayType.cast(oarray);
#else[Object]
@ -757,7 +828,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ compareAndExchange(Array handle, Object oarray, int index, $type$ expected, $type$ value) {
static $type$ compareAndExchange(VarHandle ob, Object oarray, int index, $type$ expected, $type$ value) {
Array handle = (Array)ob;
#if[Object]
Object[] array = (Object[]) handle.arrayType.cast(oarray);
#else[Object]
@ -770,7 +842,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ compareAndExchangeAcquire(Array handle, Object oarray, int index, $type$ expected, $type$ value) {
static $type$ compareAndExchangeAcquire(VarHandle ob, Object oarray, int index, $type$ expected, $type$ value) {
Array handle = (Array)ob;
#if[Object]
Object[] array = (Object[]) handle.arrayType.cast(oarray);
#else[Object]
@ -783,7 +856,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ compareAndExchangeRelease(Array handle, Object oarray, int index, $type$ expected, $type$ value) {
static $type$ compareAndExchangeRelease(VarHandle ob, Object oarray, int index, $type$ expected, $type$ value) {
Array handle = (Array)ob;
#if[Object]
Object[] array = (Object[]) handle.arrayType.cast(oarray);
#else[Object]
@ -796,7 +870,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static boolean weakCompareAndSetPlain(Array handle, Object oarray, int index, $type$ expected, $type$ value) {
static boolean weakCompareAndSetPlain(VarHandle ob, Object oarray, int index, $type$ expected, $type$ value) {
Array handle = (Array)ob;
#if[Object]
Object[] array = (Object[]) handle.arrayType.cast(oarray);
#else[Object]
@ -809,7 +884,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static boolean weakCompareAndSet(Array handle, Object oarray, int index, $type$ expected, $type$ value) {
static boolean weakCompareAndSet(VarHandle ob, Object oarray, int index, $type$ expected, $type$ value) {
Array handle = (Array)ob;
#if[Object]
Object[] array = (Object[]) handle.arrayType.cast(oarray);
#else[Object]
@ -822,7 +898,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static boolean weakCompareAndSetAcquire(Array handle, Object oarray, int index, $type$ expected, $type$ value) {
static boolean weakCompareAndSetAcquire(VarHandle ob, Object oarray, int index, $type$ expected, $type$ value) {
Array handle = (Array)ob;
#if[Object]
Object[] array = (Object[]) handle.arrayType.cast(oarray);
#else[Object]
@ -835,7 +912,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static boolean weakCompareAndSetRelease(Array handle, Object oarray, int index, $type$ expected, $type$ value) {
static boolean weakCompareAndSetRelease(VarHandle ob, Object oarray, int index, $type$ expected, $type$ value) {
Array handle = (Array)ob;
#if[Object]
Object[] array = (Object[]) handle.arrayType.cast(oarray);
#else[Object]
@ -848,7 +926,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ getAndSet(Array handle, Object oarray, int index, $type$ value) {
static $type$ getAndSet(VarHandle ob, Object oarray, int index, $type$ value) {
Array handle = (Array)ob;
#if[Object]
Object[] array = (Object[]) handle.arrayType.cast(oarray);
#else[Object]
@ -860,7 +939,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ getAndSetAcquire(Array handle, Object oarray, int index, $type$ value) {
static $type$ getAndSetAcquire(VarHandle ob, Object oarray, int index, $type$ value) {
Array handle = (Array)ob;
#if[Object]
Object[] array = (Object[]) handle.arrayType.cast(oarray);
#else[Object]
@ -872,7 +952,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ getAndSetRelease(Array handle, Object oarray, int index, $type$ value) {
static $type$ getAndSetRelease(VarHandle ob, Object oarray, int index, $type$ value) {
Array handle = (Array)ob;
#if[Object]
Object[] array = (Object[]) handle.arrayType.cast(oarray);
#else[Object]
@ -886,7 +967,8 @@ final class VarHandle$Type$s {
#if[AtomicAdd]
@ForceInline
static $type$ getAndAdd(Array handle, Object oarray, int index, $type$ value) {
static $type$ getAndAdd(VarHandle ob, Object oarray, int index, $type$ value) {
Array handle = (Array)ob;
$type$[] array = ($type$[]) oarray;
return UNSAFE.getAndAdd$Type$(array,
(((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase,
@ -894,7 +976,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ getAndAddAcquire(Array handle, Object oarray, int index, $type$ value) {
static $type$ getAndAddAcquire(VarHandle ob, Object oarray, int index, $type$ value) {
Array handle = (Array)ob;
$type$[] array = ($type$[]) oarray;
return UNSAFE.getAndAdd$Type$Acquire(array,
(((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase,
@ -902,7 +985,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ getAndAddRelease(Array handle, Object oarray, int index, $type$ value) {
static $type$ getAndAddRelease(VarHandle ob, Object oarray, int index, $type$ value) {
Array handle = (Array)ob;
$type$[] array = ($type$[]) oarray;
return UNSAFE.getAndAdd$Type$Release(array,
(((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase,
@ -912,7 +996,8 @@ final class VarHandle$Type$s {
#if[Bitwise]
@ForceInline
static $type$ getAndBitwiseOr(Array handle, Object oarray, int index, $type$ value) {
static $type$ getAndBitwiseOr(VarHandle ob, Object oarray, int index, $type$ value) {
Array handle = (Array)ob;
$type$[] array = ($type$[]) oarray;
return UNSAFE.getAndBitwiseOr$Type$(array,
(((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase,
@ -920,7 +1005,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ getAndBitwiseOrRelease(Array handle, Object oarray, int index, $type$ value) {
static $type$ getAndBitwiseOrRelease(VarHandle ob, Object oarray, int index, $type$ value) {
Array handle = (Array)ob;
$type$[] array = ($type$[]) oarray;
return UNSAFE.getAndBitwiseOr$Type$Release(array,
(((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase,
@ -928,7 +1014,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ getAndBitwiseOrAcquire(Array handle, Object oarray, int index, $type$ value) {
static $type$ getAndBitwiseOrAcquire(VarHandle ob, Object oarray, int index, $type$ value) {
Array handle = (Array)ob;
$type$[] array = ($type$[]) oarray;
return UNSAFE.getAndBitwiseOr$Type$Acquire(array,
(((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase,
@ -936,7 +1023,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ getAndBitwiseAnd(Array handle, Object oarray, int index, $type$ value) {
static $type$ getAndBitwiseAnd(VarHandle ob, Object oarray, int index, $type$ value) {
Array handle = (Array)ob;
$type$[] array = ($type$[]) oarray;
return UNSAFE.getAndBitwiseAnd$Type$(array,
(((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase,
@ -944,7 +1032,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ getAndBitwiseAndRelease(Array handle, Object oarray, int index, $type$ value) {
static $type$ getAndBitwiseAndRelease(VarHandle ob, Object oarray, int index, $type$ value) {
Array handle = (Array)ob;
$type$[] array = ($type$[]) oarray;
return UNSAFE.getAndBitwiseAnd$Type$Release(array,
(((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase,
@ -952,7 +1041,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ getAndBitwiseAndAcquire(Array handle, Object oarray, int index, $type$ value) {
static $type$ getAndBitwiseAndAcquire(VarHandle ob, Object oarray, int index, $type$ value) {
Array handle = (Array)ob;
$type$[] array = ($type$[]) oarray;
return UNSAFE.getAndBitwiseAnd$Type$Acquire(array,
(((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase,
@ -960,7 +1050,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ getAndBitwiseXor(Array handle, Object oarray, int index, $type$ value) {
static $type$ getAndBitwiseXor(VarHandle ob, Object oarray, int index, $type$ value) {
Array handle = (Array)ob;
$type$[] array = ($type$[]) oarray;
return UNSAFE.getAndBitwiseXor$Type$(array,
(((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase,
@ -968,7 +1059,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ getAndBitwiseXorRelease(Array handle, Object oarray, int index, $type$ value) {
static $type$ getAndBitwiseXorRelease(VarHandle ob, Object oarray, int index, $type$ value) {
Array handle = (Array)ob;
$type$[] array = ($type$[]) oarray;
return UNSAFE.getAndBitwiseXor$Type$Release(array,
(((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase,
@ -976,7 +1068,8 @@ final class VarHandle$Type$s {
}
@ForceInline
static $type$ getAndBitwiseXorAcquire(Array handle, Object oarray, int index, $type$ value) {
static $type$ getAndBitwiseXorAcquire(VarHandle ob, Object oarray, int index, $type$ value) {
Array handle = (Array)ob;
$type$[] array = ($type$[]) oarray;
return UNSAFE.getAndBitwiseXor$Type$Acquire(array,
(((long) Preconditions.checkIndex(index, array.length, AIOOBE_SUPPLIER)) << handle.ashift) + handle.abase,

View File

@ -26,6 +26,7 @@ package java.lang.invoke;
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.Unsafe;
import jdk.internal.util.Preconditions;
import jdk.internal.vm.annotation.ForceInline;
@ -98,7 +99,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ get(ArrayHandle handle, Object oba, int index) {
static $type$ get(VarHandle ob, Object oba, int index) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
#if[floatingPoint]
$rawType$ rawValue = UNSAFE.get$RawType$Unaligned(
@ -115,7 +117,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static void set(ArrayHandle handle, Object oba, int index, $type$ value) {
static void set(VarHandle ob, Object oba, int index, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
#if[floatingPoint]
UNSAFE.put$RawType$Unaligned(
@ -133,7 +136,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getVolatile(ArrayHandle handle, Object oba, int index) {
static $type$ getVolatile(VarHandle ob, Object oba, int index) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
return convEndian(handle.be,
UNSAFE.get$RawType$Volatile(
@ -142,7 +146,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static void setVolatile(ArrayHandle handle, Object oba, int index, $type$ value) {
static void setVolatile(VarHandle ob, Object oba, int index, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
UNSAFE.put$RawType$Volatile(
ba,
@ -151,7 +156,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAcquire(ArrayHandle handle, Object oba, int index) {
static $type$ getAcquire(VarHandle ob, Object oba, int index) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
return convEndian(handle.be,
UNSAFE.get$RawType$Acquire(
@ -160,7 +166,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static void setRelease(ArrayHandle handle, Object oba, int index, $type$ value) {
static void setRelease(VarHandle ob, Object oba, int index, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
UNSAFE.put$RawType$Release(
ba,
@ -169,7 +176,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getOpaque(ArrayHandle handle, Object oba, int index) {
static $type$ getOpaque(VarHandle ob, Object oba, int index) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
return convEndian(handle.be,
UNSAFE.get$RawType$Opaque(
@ -178,7 +186,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static void setOpaque(ArrayHandle handle, Object oba, int index, $type$ value) {
static void setOpaque(VarHandle ob, Object oba, int index, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
UNSAFE.put$RawType$Opaque(
ba,
@ -188,7 +197,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
#if[CAS]
@ForceInline
static boolean compareAndSet(ArrayHandle handle, Object oba, int index, $type$ expected, $type$ value) {
static boolean compareAndSet(VarHandle ob, Object oba, int index, $type$ expected, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
#if[Object]
return UNSAFE.compareAndSetReference(
@ -204,7 +214,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ compareAndExchange(ArrayHandle handle, Object oba, int index, $type$ expected, $type$ value) {
static $type$ compareAndExchange(VarHandle ob, Object oba, int index, $type$ expected, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
return convEndian(handle.be,
UNSAFE.compareAndExchange$RawType$(
@ -214,7 +225,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ compareAndExchangeAcquire(ArrayHandle handle, Object oba, int index, $type$ expected, $type$ value) {
static $type$ compareAndExchangeAcquire(VarHandle ob, Object oba, int index, $type$ expected, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
return convEndian(handle.be,
UNSAFE.compareAndExchange$RawType$Acquire(
@ -224,7 +236,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ compareAndExchangeRelease(ArrayHandle handle, Object oba, int index, $type$ expected, $type$ value) {
static $type$ compareAndExchangeRelease(VarHandle ob, Object oba, int index, $type$ expected, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
return convEndian(handle.be,
UNSAFE.compareAndExchange$RawType$Release(
@ -234,7 +247,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static boolean weakCompareAndSetPlain(ArrayHandle handle, Object oba, int index, $type$ expected, $type$ value) {
static boolean weakCompareAndSetPlain(VarHandle ob, Object oba, int index, $type$ expected, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
return UNSAFE.weakCompareAndSet$RawType$Plain(
ba,
@ -243,7 +257,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static boolean weakCompareAndSet(ArrayHandle handle, Object oba, int index, $type$ expected, $type$ value) {
static boolean weakCompareAndSet(VarHandle ob, Object oba, int index, $type$ expected, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
return UNSAFE.weakCompareAndSet$RawType$(
ba,
@ -252,7 +267,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static boolean weakCompareAndSetAcquire(ArrayHandle handle, Object oba, int index, $type$ expected, $type$ value) {
static boolean weakCompareAndSetAcquire(VarHandle ob, Object oba, int index, $type$ expected, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
return UNSAFE.weakCompareAndSet$RawType$Acquire(
ba,
@ -261,7 +277,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static boolean weakCompareAndSetRelease(ArrayHandle handle, Object oba, int index, $type$ expected, $type$ value) {
static boolean weakCompareAndSetRelease(VarHandle ob, Object oba, int index, $type$ expected, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
return UNSAFE.weakCompareAndSet$RawType$Release(
ba,
@ -270,7 +287,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndSet(ArrayHandle handle, Object oba, int index, $type$ value) {
static $type$ getAndSet(VarHandle ob, Object oba, int index, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
#if[Object]
return convEndian(handle.be,
@ -288,7 +306,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndSetAcquire(ArrayHandle handle, Object oba, int index, $type$ value) {
static $type$ getAndSetAcquire(VarHandle ob, Object oba, int index, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
return convEndian(handle.be,
UNSAFE.getAndSet$RawType$Acquire(
@ -298,7 +317,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndSetRelease(ArrayHandle handle, Object oba, int index, $type$ value) {
static $type$ getAndSetRelease(VarHandle ob, Object oba, int index, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
return convEndian(handle.be,
UNSAFE.getAndSet$RawType$Release(
@ -310,7 +330,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
#if[AtomicAdd]
@ForceInline
static $type$ getAndAdd(ArrayHandle handle, Object oba, int index, $type$ delta) {
static $type$ getAndAdd(VarHandle ob, Object oba, int index, $type$ delta) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
if (handle.be == BE) {
return UNSAFE.getAndAdd$RawType$(
@ -323,7 +344,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndAddAcquire(ArrayHandle handle, Object oba, int index, $type$ delta) {
static $type$ getAndAddAcquire(VarHandle ob, Object oba, int index, $type$ delta) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
if (handle.be == BE) {
return UNSAFE.getAndAdd$RawType$Acquire(
@ -336,7 +358,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndAddRelease(ArrayHandle handle, Object oba, int index, $type$ delta) {
static $type$ getAndAddRelease(VarHandle ob, Object oba, int index, $type$ delta) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
if (handle.be == BE) {
return UNSAFE.getAndAdd$RawType$Release(
@ -363,7 +386,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
#if[Bitwise]
@ForceInline
static $type$ getAndBitwiseOr(ArrayHandle handle, Object oba, int index, $type$ value) {
static $type$ getAndBitwiseOr(VarHandle ob, Object oba, int index, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
if (handle.be == BE) {
return UNSAFE.getAndBitwiseOr$RawType$(
@ -376,7 +400,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndBitwiseOrRelease(ArrayHandle handle, Object oba, int index, $type$ value) {
static $type$ getAndBitwiseOrRelease(VarHandle ob, Object oba, int index, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
if (handle.be == BE) {
return UNSAFE.getAndBitwiseOr$RawType$Release(
@ -389,7 +414,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndBitwiseOrAcquire(ArrayHandle handle, Object oba, int index, $type$ value) {
static $type$ getAndBitwiseOrAcquire(VarHandle ob, Object oba, int index, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
if (handle.be == BE) {
return UNSAFE.getAndBitwiseOr$RawType$Acquire(
@ -414,7 +440,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndBitwiseAnd(ArrayHandle handle, Object oba, int index, $type$ value) {
static $type$ getAndBitwiseAnd(VarHandle ob, Object oba, int index, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
if (handle.be == BE) {
return UNSAFE.getAndBitwiseAnd$RawType$(
@ -427,7 +454,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndBitwiseAndRelease(ArrayHandle handle, Object oba, int index, $type$ value) {
static $type$ getAndBitwiseAndRelease(VarHandle ob, Object oba, int index, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
if (handle.be == BE) {
return UNSAFE.getAndBitwiseAnd$RawType$Release(
@ -440,7 +468,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndBitwiseAndAcquire(ArrayHandle handle, Object oba, int index, $type$ value) {
static $type$ getAndBitwiseAndAcquire(VarHandle ob, Object oba, int index, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
if (handle.be == BE) {
return UNSAFE.getAndBitwiseAnd$RawType$Acquire(
@ -465,7 +494,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndBitwiseXor(ArrayHandle handle, Object oba, int index, $type$ value) {
static $type$ getAndBitwiseXor(VarHandle ob, Object oba, int index, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
if (handle.be == BE) {
return UNSAFE.getAndBitwiseXor$RawType$(
@ -478,7 +508,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndBitwiseXorRelease(ArrayHandle handle, Object oba, int index, $type$ value) {
static $type$ getAndBitwiseXorRelease(VarHandle ob, Object oba, int index, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
if (handle.be == BE) {
return UNSAFE.getAndBitwiseXor$RawType$Release(
@ -491,7 +522,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndBitwiseXorAcquire(ArrayHandle handle, Object oba, int index, $type$ value) {
static $type$ getAndBitwiseXorAcquire(VarHandle ob, Object oba, int index, $type$ value) {
ArrayHandle handle = (ArrayHandle)ob;
byte[] ba = (byte[]) oba;
if (handle.be == BE) {
return UNSAFE.getAndBitwiseXor$RawType$Acquire(
@ -533,7 +565,10 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
@ForceInline
static int index(ByteBuffer bb, int index) {
nioAccess.checkSegment(bb);
MemorySegmentProxy segmentProxy = nioAccess.bufferSegment(bb);
if (segmentProxy != null) {
segmentProxy.checkValidState();
}
return Preconditions.checkIndex(index, UNSAFE.getInt(bb, BUFFER_LIMIT) - ALIGN, null);
}
@ -553,7 +588,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ get(ByteBufferHandle handle, Object obb, int index) {
static $type$ get(VarHandle ob, Object obb, int index) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
#if[floatingPoint]
$rawType$ rawValue = UNSAFE.get$RawType$Unaligned(
@ -570,7 +606,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static void set(ByteBufferHandle handle, Object obb, int index, $type$ value) {
static void set(VarHandle ob, Object obb, int index, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
#if[floatingPoint]
UNSAFE.put$RawType$Unaligned(
@ -588,7 +625,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getVolatile(ByteBufferHandle handle, Object obb, int index) {
static $type$ getVolatile(VarHandle ob, Object obb, int index) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return convEndian(handle.be,
UNSAFE.get$RawType$Volatile(
@ -597,7 +635,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static void setVolatile(ByteBufferHandle handle, Object obb, int index, $type$ value) {
static void setVolatile(VarHandle ob, Object obb, int index, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
UNSAFE.put$RawType$Volatile(
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
@ -606,7 +645,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAcquire(ByteBufferHandle handle, Object obb, int index) {
static $type$ getAcquire(VarHandle ob, Object obb, int index) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return convEndian(handle.be,
UNSAFE.get$RawType$Acquire(
@ -615,7 +655,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static void setRelease(ByteBufferHandle handle, Object obb, int index, $type$ value) {
static void setRelease(VarHandle ob, Object obb, int index, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
UNSAFE.put$RawType$Release(
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
@ -624,7 +665,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getOpaque(ByteBufferHandle handle, Object obb, int index) {
static $type$ getOpaque(VarHandle ob, Object obb, int index) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return convEndian(handle.be,
UNSAFE.get$RawType$Opaque(
@ -633,7 +675,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static void setOpaque(ByteBufferHandle handle, Object obb, int index, $type$ value) {
static void setOpaque(VarHandle ob, Object obb, int index, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
UNSAFE.put$RawType$Opaque(
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
@ -643,7 +686,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
#if[CAS]
@ForceInline
static boolean compareAndSet(ByteBufferHandle handle, Object obb, int index, $type$ expected, $type$ value) {
static boolean compareAndSet(VarHandle ob, Object obb, int index, $type$ expected, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
#if[Object]
return UNSAFE.compareAndSetReference(
@ -659,7 +703,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ compareAndExchange(ByteBufferHandle handle, Object obb, int index, $type$ expected, $type$ value) {
static $type$ compareAndExchange(VarHandle ob, Object obb, int index, $type$ expected, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return convEndian(handle.be,
UNSAFE.compareAndExchange$RawType$(
@ -669,7 +714,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ compareAndExchangeAcquire(ByteBufferHandle handle, Object obb, int index, $type$ expected, $type$ value) {
static $type$ compareAndExchangeAcquire(VarHandle ob, Object obb, int index, $type$ expected, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return convEndian(handle.be,
UNSAFE.compareAndExchange$RawType$Acquire(
@ -679,7 +725,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ compareAndExchangeRelease(ByteBufferHandle handle, Object obb, int index, $type$ expected, $type$ value) {
static $type$ compareAndExchangeRelease(VarHandle ob, Object obb, int index, $type$ expected, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return convEndian(handle.be,
UNSAFE.compareAndExchange$RawType$Release(
@ -689,7 +736,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static boolean weakCompareAndSetPlain(ByteBufferHandle handle, Object obb, int index, $type$ expected, $type$ value) {
static boolean weakCompareAndSetPlain(VarHandle ob, Object obb, int index, $type$ expected, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return UNSAFE.weakCompareAndSet$RawType$Plain(
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
@ -698,7 +746,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static boolean weakCompareAndSet(ByteBufferHandle handle, Object obb, int index, $type$ expected, $type$ value) {
static boolean weakCompareAndSet(VarHandle ob, Object obb, int index, $type$ expected, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return UNSAFE.weakCompareAndSet$RawType$(
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
@ -707,7 +756,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static boolean weakCompareAndSetAcquire(ByteBufferHandle handle, Object obb, int index, $type$ expected, $type$ value) {
static boolean weakCompareAndSetAcquire(VarHandle ob, Object obb, int index, $type$ expected, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return UNSAFE.weakCompareAndSet$RawType$Acquire(
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
@ -716,7 +766,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static boolean weakCompareAndSetRelease(ByteBufferHandle handle, Object obb, int index, $type$ expected, $type$ value) {
static boolean weakCompareAndSetRelease(VarHandle ob, Object obb, int index, $type$ expected, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return UNSAFE.weakCompareAndSet$RawType$Release(
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
@ -725,7 +776,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndSet(ByteBufferHandle handle, Object obb, int index, $type$ value) {
static $type$ getAndSet(VarHandle ob, Object obb, int index, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
#if[Object]
return convEndian(handle.be,
@ -743,7 +795,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndSetAcquire(ByteBufferHandle handle, Object obb, int index, $type$ value) {
static $type$ getAndSetAcquire(VarHandle ob, Object obb, int index, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return convEndian(handle.be,
UNSAFE.getAndSet$RawType$Acquire(
@ -753,7 +806,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndSetRelease(ByteBufferHandle handle, Object obb, int index, $type$ value) {
static $type$ getAndSetRelease(VarHandle ob, Object obb, int index, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return convEndian(handle.be,
UNSAFE.getAndSet$RawType$Release(
@ -765,7 +819,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
#if[AtomicAdd]
@ForceInline
static $type$ getAndAdd(ByteBufferHandle handle, Object obb, int index, $type$ delta) {
static $type$ getAndAdd(VarHandle ob, Object obb, int index, $type$ delta) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndAdd$RawType$(
@ -778,7 +833,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndAddAcquire(ByteBufferHandle handle, Object obb, int index, $type$ delta) {
static $type$ getAndAddAcquire(VarHandle ob, Object obb, int index, $type$ delta) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndAdd$RawType$Acquire(
@ -791,7 +847,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndAddRelease(ByteBufferHandle handle, Object obb, int index, $type$ delta) {
static $type$ getAndAddRelease(VarHandle ob, Object obb, int index, $type$ delta) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndAdd$RawType$Release(
@ -819,7 +876,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
#if[Bitwise]
@ForceInline
static $type$ getAndBitwiseOr(ByteBufferHandle handle, Object obb, int index, $type$ value) {
static $type$ getAndBitwiseOr(VarHandle ob, Object obb, int index, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseOr$RawType$(
@ -832,7 +890,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndBitwiseOrRelease(ByteBufferHandle handle, Object obb, int index, $type$ value) {
static $type$ getAndBitwiseOrRelease(VarHandle ob, Object obb, int index, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseOr$RawType$Release(
@ -845,7 +904,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndBitwiseOrAcquire(ByteBufferHandle handle, Object obb, int index, $type$ value) {
static $type$ getAndBitwiseOrAcquire(VarHandle ob, Object obb, int index, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseOr$RawType$Acquire(
@ -871,7 +931,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndBitwiseAnd(ByteBufferHandle handle, Object obb, int index, $type$ value) {
static $type$ getAndBitwiseAnd(VarHandle ob, Object obb, int index, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseAnd$RawType$(
@ -884,7 +945,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndBitwiseAndRelease(ByteBufferHandle handle, Object obb, int index, $type$ value) {
static $type$ getAndBitwiseAndRelease(VarHandle ob, Object obb, int index, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseAnd$RawType$Release(
@ -897,7 +959,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndBitwiseAndAcquire(ByteBufferHandle handle, Object obb, int index, $type$ value) {
static $type$ getAndBitwiseAndAcquire(VarHandle ob, Object obb, int index, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseAnd$RawType$Acquire(
@ -924,7 +987,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
@ForceInline
static $type$ getAndBitwiseXor(ByteBufferHandle handle, Object obb, int index, $type$ value) {
static $type$ getAndBitwiseXor(VarHandle ob, Object obb, int index, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseXor$RawType$(
@ -937,7 +1001,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndBitwiseXorRelease(ByteBufferHandle handle, Object obb, int index, $type$ value) {
static $type$ getAndBitwiseXorRelease(VarHandle ob, Object obb, int index, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseXor$RawType$Release(
@ -950,7 +1015,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
}
@ForceInline
static $type$ getAndBitwiseXorAcquire(ByteBufferHandle handle, Object obb, int index, $type$ value) {
static $type$ getAndBitwiseXorAcquire(VarHandle ob, Object obb, int index, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseXor$RawType$Acquire(

View File

@ -33,7 +33,7 @@ import static java.lang.invoke.MethodHandleStatics.UNSAFE;
#warn
final class VarHandleMemoryAddressAs$Type$s {
final class MemoryAccessVarHandle$Type$Helper {
static final boolean BE = UNSAFE.isBigEndian();
@ -76,7 +76,7 @@ final class VarHandleMemoryAddressAs$Type$s {
static long offset(MemoryAddressProxy bb, long offset, long alignmentMask) {
long address = offsetNoVMAlignCheck(bb, offset, alignmentMask);
if ((address & VM_ALIGN) != 0) {
throw VarHandleMemoryAddressBase.newIllegalStateExceptionForMisalignedAccess(address);
throw MemoryAccessVarHandleBase.newIllegalStateExceptionForMisalignedAccess(address);
}
return address;
}
@ -87,13 +87,14 @@ final class VarHandleMemoryAddressAs$Type$s {
long address = base + offset;
//note: the offset portion has already been aligned-checked, by construction
if ((base & alignmentMask) != 0) {
throw VarHandleMemoryAddressBase.newIllegalStateExceptionForMisalignedAccess(address);
throw MemoryAccessVarHandleBase.newIllegalStateExceptionForMisalignedAccess(address);
}
return address;
}
@ForceInline
static $type$ get0(VarHandleMemoryAddressBase handle, Object obb, long base) {
static $type$ get0(VarHandle ob, Object obb, long base) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true);
#if[floatingPoint]
$rawType$ rawValue = UNSAFE.get$RawType$Unaligned(
@ -116,7 +117,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static void set0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
static void set0(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
#if[floatingPoint]
UNSAFE.put$RawType$Unaligned(
@ -141,7 +143,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static $type$ getVolatile0(VarHandleMemoryAddressBase handle, Object obb, long base) {
static $type$ getVolatile0(VarHandle ob, Object obb, long base) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true);
return convEndian(handle.be,
UNSAFE.get$RawType$Volatile(
@ -150,7 +153,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static void setVolatile0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
static void setVolatile0(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
UNSAFE.put$RawType$Volatile(
bb.unsafeGetBase(),
@ -159,7 +163,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static $type$ getAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base) {
static $type$ getAcquire0(VarHandle ob, Object obb, long base) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true);
return convEndian(handle.be,
UNSAFE.get$RawType$Acquire(
@ -168,7 +173,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static void setRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
static void setRelease0(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
UNSAFE.put$RawType$Release(
bb.unsafeGetBase(),
@ -177,7 +183,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static $type$ getOpaque0(VarHandleMemoryAddressBase handle, Object obb, long base) {
static $type$ getOpaque0(VarHandle ob, Object obb, long base) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true);
return convEndian(handle.be,
UNSAFE.get$RawType$Opaque(
@ -186,7 +193,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static void setOpaque0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
static void setOpaque0(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
UNSAFE.put$RawType$Opaque(
bb.unsafeGetBase(),
@ -196,7 +204,8 @@ final class VarHandleMemoryAddressAs$Type$s {
#if[CAS]
@ForceInline
static boolean compareAndSet0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
static boolean compareAndSet0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return UNSAFE.compareAndSet$RawType$(
bb.unsafeGetBase(),
@ -205,7 +214,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static $type$ compareAndExchange0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
static $type$ compareAndExchange0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.compareAndExchange$RawType$(
@ -215,7 +225,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static $type$ compareAndExchangeAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
static $type$ compareAndExchangeAcquire0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.compareAndExchange$RawType$Acquire(
@ -225,7 +236,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static $type$ compareAndExchangeRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
static $type$ compareAndExchangeRelease0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.compareAndExchange$RawType$Release(
@ -235,7 +247,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static boolean weakCompareAndSetPlain0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
static boolean weakCompareAndSetPlain0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return UNSAFE.weakCompareAndSet$RawType$Plain(
bb.unsafeGetBase(),
@ -244,7 +257,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static boolean weakCompareAndSet0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
static boolean weakCompareAndSet0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return UNSAFE.weakCompareAndSet$RawType$(
bb.unsafeGetBase(),
@ -253,7 +267,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static boolean weakCompareAndSetAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
static boolean weakCompareAndSetAcquire0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return UNSAFE.weakCompareAndSet$RawType$Acquire(
bb.unsafeGetBase(),
@ -262,7 +277,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static boolean weakCompareAndSetRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
static boolean weakCompareAndSetRelease0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return UNSAFE.weakCompareAndSet$RawType$Release(
bb.unsafeGetBase(),
@ -271,7 +287,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static $type$ getAndSet0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
static $type$ getAndSet0(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.getAndSet$RawType$(
@ -281,7 +298,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static $type$ getAndSetAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
static $type$ getAndSetAcquire0(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.getAndSet$RawType$Acquire(
@ -291,7 +309,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static $type$ getAndSetRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
static $type$ getAndSetRelease0(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.getAndSet$RawType$Release(
@ -303,7 +322,8 @@ final class VarHandleMemoryAddressAs$Type$s {
#if[AtomicAdd]
@ForceInline
static $type$ getAndAdd0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ delta) {
static $type$ getAndAdd0(VarHandle ob, Object obb, long base, $type$ delta) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndAdd$RawType$(
@ -316,7 +336,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static $type$ getAndAddAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ delta) {
static $type$ getAndAddAcquire0(VarHandle ob, Object obb, long base, $type$ delta) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndAdd$RawType$Acquire(
@ -329,7 +350,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static $type$ getAndAddRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ delta) {
static $type$ getAndAddRelease0(VarHandle ob, Object obb, long base, $type$ delta) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndAdd$RawType$Release(
@ -356,7 +378,8 @@ final class VarHandleMemoryAddressAs$Type$s {
#if[Bitwise]
@ForceInline
static $type$ getAndBitwiseOr0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
static $type$ getAndBitwiseOr0(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseOr$RawType$(
@ -369,7 +392,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static $type$ getAndBitwiseOrRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
static $type$ getAndBitwiseOrRelease0(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseOr$RawType$Release(
@ -382,7 +406,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static $type$ getAndBitwiseOrAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
static $type$ getAndBitwiseOrAcquire0(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseOr$RawType$Acquire(
@ -407,7 +432,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static $type$ getAndBitwiseAnd0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
static $type$ getAndBitwiseAnd0(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseAnd$RawType$(
@ -420,7 +446,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static $type$ getAndBitwiseAndRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
static $type$ getAndBitwiseAndRelease0(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseAnd$RawType$Release(
@ -433,7 +460,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static $type$ getAndBitwiseAndAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
static $type$ getAndBitwiseAndAcquire0(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseAnd$RawType$Acquire(
@ -459,7 +487,8 @@ final class VarHandleMemoryAddressAs$Type$s {
@ForceInline
static $type$ getAndBitwiseXor0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
static $type$ getAndBitwiseXor0(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseXor$RawType$(
@ -472,7 +501,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static $type$ getAndBitwiseXorRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
static $type$ getAndBitwiseXorRelease0(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseXor$RawType$Release(
@ -485,7 +515,8 @@ final class VarHandleMemoryAddressAs$Type$s {
}
@ForceInline
static $type$ getAndBitwiseXorAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
static $type$ getAndBitwiseXorAcquire0(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseXor$RawType$Acquire(

View File

@ -29,10 +29,12 @@ import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.access.foreign.UnmapperProxy;
import jdk.internal.misc.Unsafe;
import jdk.internal.misc.VM.BufferPool;
import jdk.internal.vm.annotation.ForceInline;
import java.io.FileDescriptor;
import java.util.Spliterator;
/**
@ -768,6 +770,11 @@ public abstract class Buffer {
return new DirectByteBuffer(addr, cap, obj, segment);
}
@Override
public ByteBuffer newMappedByteBuffer(UnmapperProxy unmapperProxy, long address, int cap, Object obj, MemorySegmentProxy segment) {
return new DirectByteBuffer(address, cap, obj, unmapperProxy.fileDescriptor(), unmapperProxy.isSync(), segment);
}
@Override
public ByteBuffer newHeapByteBuffer(byte[] hb, int offset, int capacity, MemorySegmentProxy segment) {
return new HeapByteBuffer(hb, offset, capacity, segment);
@ -784,8 +791,37 @@ public abstract class Buffer {
}
@Override
public void checkSegment(Buffer buffer) {
buffer.checkSegment();
public UnmapperProxy unmapper(ByteBuffer bb) {
if (bb instanceof MappedByteBuffer) {
return ((MappedByteBuffer)bb).unmapper();
} else {
return null;
}
}
@Override
public MemorySegmentProxy bufferSegment(Buffer buffer) {
return buffer.segment;
}
@Override
public void force(FileDescriptor fd, long address, boolean isSync, long offset, long size) {
MappedMemoryUtils.force(fd, address, isSync, offset, size);
}
@Override
public void load(long address, boolean isSync, long size) {
MappedMemoryUtils.load(address, isSync, size);
}
@Override
public void unload(long address, boolean isSync, long size) {
MappedMemoryUtils.unload(address, isSync, size);
}
@Override
public boolean isLoaded(long address, boolean isSync, long size) {
return MappedMemoryUtils.isLoaded(address, isSync, size);
}
});
}

View File

@ -153,6 +153,15 @@ class Direct$Type$Buffer$RW$$BO$
att = ob;
}
// 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, FileDescriptor fd, boolean isSync, MemorySegmentProxy segment) {
super(-1, 0, cap, cap, fd, isSync, segment);
address = addr;
cleaner = null;
att = ob;
}
// Invoked only by JNI: NewDirectByteBuffer(void*, long)
//

View File

@ -30,7 +30,7 @@ import java.lang.ref.Reference;
import java.util.Objects;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.Unsafe;
import jdk.internal.access.foreign.UnmapperProxy;
/**
@ -109,58 +109,29 @@ public abstract class MappedByteBuffer
this.isSync = false;
}
// Returns the distance (in bytes) of the buffer start from the
// largest page aligned address of the mapping less than or equal
// to the start address.
private long mappingOffset() {
return mappingOffset(0);
}
UnmapperProxy unmapper() {
return fd != null ?
new UnmapperProxy() {
@Override
public long address() {
return address;
}
// Returns the distance (in bytes) of the buffer element
// identified by index from the largest page aligned address of
// the mapping less than or equal to the element address. Computed
// each time to avoid storing in every direct buffer.
private long mappingOffset(int index) {
int ps = Bits.pageSize();
long indexAddress = address + index;
long baseAddress = alignDown(indexAddress, ps);
return indexAddress - baseAddress;
}
@Override
public FileDescriptor fileDescriptor() {
return fd;
}
// Given an offset previously obtained from calling
// mappingOffset() returns the largest page aligned address of the
// mapping less than or equal to the buffer start address.
private long mappingAddress(long mappingOffset) {
return mappingAddress(mappingOffset, 0);
}
@Override
public boolean isSync() {
return isSync;
}
// Given an offset previously otained from calling
// mappingOffset(index) returns the largest page aligned address
// of the mapping less than or equal to the address of the buffer
// element identified by index.
private long mappingAddress(long mappingOffset, long index) {
long indexAddress = address + index;
return indexAddress - mappingOffset;
}
// given a mappingOffset previously otained from calling
// mappingOffset() return that offset added to the buffer
// capacity.
private long mappingLength(long mappingOffset) {
return mappingLength(mappingOffset, (long)capacity());
}
// given a mappingOffset previously otained from calling
// mappingOffset(index) return that offset added to the supplied
// length.
private long mappingLength(long mappingOffset, long length) {
return length + mappingOffset;
}
// align address down to page size
private static long alignDown(long address, int pageSize) {
// pageSize must be a power of 2
return address & ~(pageSize - 1);
@Override
public void unmap() {
throw new UnsupportedOperationException();
}
} : null;
}
/**
@ -202,20 +173,9 @@ public abstract class MappedByteBuffer
if (fd == null) {
return true;
}
// a sync mapped buffer is always loaded
if (isSync()) {
return true;
}
if ((address == 0) || (capacity() == 0))
return true;
long offset = mappingOffset();
long length = mappingLength(offset);
return isLoaded0(mappingAddress(offset), length, Bits.pageCount(length));
return MappedMemoryUtils.isLoaded(address, isSync, capacity());
}
// not used, but a potential target for a store, see load() for details.
private static byte unused;
/**
* Loads this buffer's content into physical memory.
*
@ -230,37 +190,11 @@ public abstract class MappedByteBuffer
if (fd == null) {
return this;
}
// no need to load a sync mapped buffer
if (isSync()) {
return this;
}
if ((address == 0) || (capacity() == 0))
return this;
long offset = mappingOffset();
long length = mappingLength(offset);
load0(mappingAddress(offset), length);
// Read a byte from each page to bring it into memory. A checksum
// is computed as we go along to prevent the compiler from otherwise
// considering the loop as dead code.
Unsafe unsafe = Unsafe.getUnsafe();
int ps = Bits.pageSize();
int count = Bits.pageCount(length);
long a = mappingAddress(offset);
byte x = 0;
try {
for (int i=0; i<count; i++) {
// TODO consider changing to getByteOpaque thus avoiding
// dead code elimination and the need to calculate a checksum
x ^= unsafe.getByte(a);
a += ps;
}
MappedMemoryUtils.load(address, isSync, capacity());
} finally {
Reference.reachabilityFence(this);
}
if (unused != 0)
unused = x;
return this;
}
@ -293,8 +227,7 @@ public abstract class MappedByteBuffer
return force(0, limit());
}
if ((address != 0) && (capacity() != 0)) {
long offset = mappingOffset();
force0(fd, mappingAddress(offset), mappingLength(offset));
return force(0, capacity());
}
return this;
}
@ -348,22 +281,11 @@ public abstract class MappedByteBuffer
if ((address != 0) && (limit() != 0)) {
// check inputs
Objects.checkFromIndexSize(index, length, limit());
if (isSync) {
// simply force writeback of associated cache lines
Unsafe.getUnsafe().writebackMemory(address + index, length);
} else {
// force writeback via file descriptor
long offset = mappingOffset(index);
force0(fd, mappingAddress(offset, index), mappingLength(offset, length));
}
MappedMemoryUtils.force(fd, address, isSync, index, length);
}
return this;
}
private native boolean isLoaded0(long address, long length, int pageCount);
private native void load0(long address, long length);
private native void force0(FileDescriptor fd, long address, long length);
// -- Covariant return type overrides
/**

View File

@ -0,0 +1,156 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.nio;
import jdk.internal.misc.Unsafe;
import java.io.FileDescriptor;
/* package */ class MappedMemoryUtils {
static boolean isLoaded(long address, boolean isSync, long size) {
// a sync mapped buffer is always loaded
if (isSync) {
return true;
}
if ((address == 0) || (size == 0))
return true;
long offset = mappingOffset(address);
long length = mappingLength(offset, size);
return isLoaded0(mappingAddress(address, offset), length, Bits.pageCount(length));
}
static void load(long address, boolean isSync, long size) {
// no need to load a sync mapped buffer
if (isSync) {
return;
}
if ((address == 0) || (size == 0))
return;
long offset = mappingOffset(address);
long length = mappingLength(offset, size);
load0(mappingAddress(address, offset), length);
// Read a byte from each page to bring it into memory. A checksum
// is computed as we go along to prevent the compiler from otherwise
// considering the loop as dead code.
Unsafe unsafe = Unsafe.getUnsafe();
int ps = Bits.pageSize();
int count = Bits.pageCount(length);
long a = mappingAddress(address, offset);
byte x = 0;
for (int i=0; i<count; i++) {
// TODO consider changing to getByteOpaque thus avoiding
// dead code elimination and the need to calculate a checksum
x ^= unsafe.getByte(a);
a += ps;
}
if (unused != 0)
unused = x;
}
// not used, but a potential target for a store, see load() for details.
private static byte unused;
static void unload(long address, boolean isSync, long size) {
// no need to load a sync mapped buffer
if (isSync) {
return;
}
if ((address == 0) || (size == 0))
return;
long offset = mappingOffset(address);
long length = mappingLength(offset, size);
unload0(mappingAddress(address, offset), length);
}
static void force(FileDescriptor fd, long address, boolean isSync, long index, long length) {
if (isSync) {
// simply force writeback of associated cache lines
Unsafe.getUnsafe().writebackMemory(address + index, length);
} else {
// force writeback via file descriptor
long offset = mappingOffset(address, index);
force0(fd, mappingAddress(address, offset, index), mappingLength(offset, length));
}
}
// native methods
private static native boolean isLoaded0(long address, long length, int pageCount);
private static native void load0(long address, long length);
private static native void unload0(long address, long length);
private static native void force0(FileDescriptor fd, long address, long length);
// utility methods
// Returns the distance (in bytes) of the buffer start from the
// largest page aligned address of the mapping less than or equal
// to the start address.
private static long mappingOffset(long address) {
return mappingOffset(address, 0);
}
// Returns the distance (in bytes) of the buffer element
// identified by index from the largest page aligned address of
// the mapping less than or equal to the element address. Computed
// each time to avoid storing in every direct buffer.
private static long mappingOffset(long address, long index) {
int ps = Bits.pageSize();
long indexAddress = address + index;
long baseAddress = alignDown(indexAddress, ps);
return indexAddress - baseAddress;
}
// Given an offset previously obtained from calling
// mappingOffset() returns the largest page aligned address of the
// mapping less than or equal to the buffer start address.
private static long mappingAddress(long address, long mappingOffset) {
return mappingAddress(address, mappingOffset, 0);
}
// Given an offset previously otained from calling
// mappingOffset(index) returns the largest page aligned address
// of the mapping less than or equal to the address of the buffer
// element identified by index.
private static long mappingAddress(long address, long mappingOffset, long index) {
long indexAddress = address + index;
return indexAddress - mappingOffset;
}
// given a mappingOffset previously otained from calling
// mappingOffset(index) return that offset added to the supplied
// length.
private static long mappingLength(long mappingOffset, long length) {
return length + mappingOffset;
}
// align address down to page size
private static long alignDown(long address, int pageSize) {
// pageSize must be a power of 2
return address & ~(pageSize - 1);
}
}

View File

@ -25,9 +25,11 @@
package jdk.internal.access;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
import java.util.List;
import java.util.Map;
public interface JavaLangInvokeAccess {
@ -113,8 +115,14 @@ public interface JavaLangInvokeAccess {
* 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);
VarHandle memoryAccessVarHandle(Class<?> carrier, long alignmentMask,
ByteOrder order, long offset, long[] strides);
/**
* Is {@code handle} a memory access varhandle?
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
boolean isMemoryAccessVarHandle(VarHandle handle);
/**
* Returns the carrier associated with a memory access var handle.
@ -145,4 +153,40 @@ public interface JavaLangInvokeAccess {
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
long[] memoryAddressStrides(VarHandle handle);
/**
* Var handle carrier combinator.
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
VarHandle filterValue(VarHandle target, MethodHandle filterToTarget, MethodHandle filterFromTarget);
/**
* Var handle filter coordinates combinator.
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
VarHandle filterCoordinates(VarHandle target, int pos, MethodHandle... filters);
/**
* Var handle drop coordinates combinator.
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
VarHandle dropCoordinates(VarHandle target, int pos, Class<?>... valueTypes);
/**
* Var handle permute coordinates combinator.
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
VarHandle permuteCoordinates(VarHandle target, List<Class<?>> newCoordinates, int... reorder);
/**
* Var handle collect coordinates combinator.
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
VarHandle collectCoordinates(VarHandle target, int pos, MethodHandle filter);
/**
* Var handle insert coordinates combinator.
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
VarHandle insertCoordinates(VarHandle target, int pos, Object... values);
}

View File

@ -26,8 +26,10 @@
package jdk.internal.access;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.access.foreign.UnmapperProxy;
import jdk.internal.misc.VM.BufferPool;
import java.io.FileDescriptor;
import java.nio.Buffer;
import java.nio.ByteBuffer;
@ -47,6 +49,16 @@ public interface JavaNioAccess {
*/
ByteBuffer newDirectByteBuffer(long addr, int cap, Object obj, MemorySegmentProxy segment);
/**
* Constructs a mapped 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. The {@code sync} and {@code fd} parameters of the mapped
* buffer are derived from the {@code UnmapperProxy}.
* Used by {@code jdk.internal.foreignMemorySegmentImpl}.
*/
ByteBuffer newMappedByteBuffer(UnmapperProxy unmapperProxy, 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}.
@ -64,7 +76,32 @@ public interface JavaNioAccess {
long getBufferAddress(ByteBuffer bb);
/**
* Used by byte buffer var handle views.
* Used by {@code jdk.internal.foreign.Utils}.
*/
void checkSegment(Buffer buffer);
UnmapperProxy unmapper(ByteBuffer bb);
/**
* Used by {@code jdk.internal.foreign.AbstractMemorySegmentImpl} and byte buffer var handle views.
*/
MemorySegmentProxy bufferSegment(Buffer buffer);
/**
* Used by {@code jdk.internal.foreign.MappedMemorySegmentImpl} and byte buffer var handle views.
*/
void force(FileDescriptor fd, long address, boolean isSync, long offset, long size);
/**
* Used by {@code jdk.internal.foreign.MappedMemorySegmentImpl} and byte buffer var handle views.
*/
void load(long address, boolean isSync, long size);
/**
* Used by {@code jdk.internal.foreign.MappedMemorySegmentImpl}.
*/
void unload(long address, boolean isSync, long size);
/**
* Used by {@code jdk.internal.foreign.MappedMemorySegmentImpl} and byte buffer var handle views.
*/
boolean isLoaded(long address, boolean isSync, long size);
}

View File

@ -39,4 +39,61 @@ public interface MemoryAddressProxy {
void checkAccess(long offset, long length, boolean readOnly);
long unsafeGetOffset();
Object unsafeGetBase();
boolean isSmall();
/* Helper functions for offset computations. These are required so that we can avoid issuing long opcodes
* (e.g. LMUL, LADD) when we're operating on 'small' segments (segments whose length can be expressed with an int).
* C2 BCE code is very sensitive to the kind of opcode being emitted, and this workaround allows us to rescue
* BCE when working with small segments. This workaround should be dropped when JDK-8223051 is resolved.
*/
public static long addOffsets(long op1, long op2, MemoryAddressProxy addr) {
if (addr.isSmall()) {
// force ints for BCE
if (op1 > Integer.MAX_VALUE || op2 > Integer.MAX_VALUE
|| op1 < Integer.MIN_VALUE || op2 < Integer.MIN_VALUE) {
throw overflowException(Integer.MIN_VALUE, Integer.MAX_VALUE);
}
int i1 = (int)op1;
int i2 = (int)op2;
try {
return Math.addExact(i1, i2);
} catch (ArithmeticException ex) {
throw overflowException(Integer.MIN_VALUE, Integer.MAX_VALUE);
}
} else {
try {
return Math.addExact(op1, op2);
} catch (ArithmeticException ex) {
throw overflowException(Long.MIN_VALUE, Long.MAX_VALUE);
}
}
}
public static long multiplyOffsets(long op1, long op2, MemoryAddressProxy addr) {
if (addr.isSmall()) {
if (op1 > Integer.MAX_VALUE || op2 > Integer.MAX_VALUE
|| op1 < Integer.MIN_VALUE || op2 < Integer.MIN_VALUE) {
throw overflowException(Integer.MIN_VALUE, Integer.MAX_VALUE);
}
// force ints for BCE
int i1 = (int)op1;
int i2 = (int)op2;
try {
return Math.multiplyExact(i1, i2);
} catch (ArithmeticException ex) {
throw overflowException(Integer.MIN_VALUE, Integer.MAX_VALUE);
}
} else {
try {
return Math.multiplyExact(op1, op2);
} catch (ArithmeticException ex) {
throw overflowException(Long.MIN_VALUE, Long.MAX_VALUE);
}
}
}
private static IndexOutOfBoundsException overflowException(long min, long max) {
return new IndexOutOfBoundsException(String.format("Overflow occurred during offset computation ; offset exceeded range { %d .. %d }", min, max));
}
}

View File

@ -26,11 +26,15 @@
package jdk.internal.access.foreign;
import java.io.FileDescriptor;
/**
* 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();
FileDescriptor fileDescriptor();
boolean isSync();
void unmap();
}

View File

@ -229,6 +229,7 @@ module java.base {
jdk.management.agent;
exports jdk.internal.vm.annotation to
jdk.internal.vm.ci,
jdk.incubator.foreign,
jdk.unsupported;
exports jdk.internal.util.jar to
jdk.jartool;

View File

@ -892,6 +892,11 @@ public class FileChannelImpl
return address;
}
@Override
public FileDescriptor fileDescriptor() {
return fd;
}
@Override
public void run() {
unmap();
@ -945,6 +950,10 @@ public class FileChannelImpl
totalCapacity -= cap;
}
}
public boolean isSync() {
return false;
}
}
private static class SyncUnmapper extends Unmapper {
@ -974,6 +983,10 @@ public class FileChannelImpl
totalCapacity -= cap;
}
}
public boolean isSync() {
return true;
}
}
private static void unmap(MappedByteBuffer bb) {

View File

@ -27,7 +27,7 @@
#include "jni_util.h"
#include "jvm.h"
#include "jlong.h"
#include "java_nio_MappedByteBuffer.h"
#include "java_nio_MappedMemoryUtils.h"
#include <assert.h>
#include <sys/mman.h>
#include <stddef.h>
@ -55,7 +55,7 @@ static long calculate_number_of_pages_in_range(void* address, size_t len, size_t
#endif
JNIEXPORT jboolean JNICALL
Java_java_nio_MappedByteBuffer_isLoaded0(JNIEnv *env, jobject obj, jlong address,
Java_java_nio_MappedMemoryUtils_isLoaded0(JNIEnv *env, jobject obj, jlong address,
jlong len, jint numPages)
{
jboolean loaded = JNI_TRUE;
@ -104,7 +104,7 @@ Java_java_nio_MappedByteBuffer_isLoaded0(JNIEnv *env, jobject obj, jlong address
JNIEXPORT void JNICALL
Java_java_nio_MappedByteBuffer_load0(JNIEnv *env, jobject obj, jlong address,
Java_java_nio_MappedMemoryUtils_load0(JNIEnv *env, jobject obj, jlong address,
jlong len)
{
char *a = (char *)jlong_to_ptr(address);
@ -114,9 +114,19 @@ Java_java_nio_MappedByteBuffer_load0(JNIEnv *env, jobject obj, jlong address,
}
}
JNIEXPORT void JNICALL
Java_java_nio_MappedMemoryUtils_unload0(JNIEnv *env, jobject obj, jlong address,
jlong len)
{
char *a = (char *)jlong_to_ptr(address);
int result = madvise((caddr_t)a, (size_t)len, MADV_DONTNEED);
if (result == -1) {
JNU_ThrowIOExceptionWithLastError(env, "madvise failed");
}
}
JNIEXPORT void JNICALL
Java_java_nio_MappedByteBuffer_force0(JNIEnv *env, jobject obj, jobject fdo,
Java_java_nio_MappedMemoryUtils_force0(JNIEnv *env, jobject obj, jobject fdo,
jlong address, jlong len)
{
void* a = (void *)jlong_to_ptr(address);

View File

@ -27,11 +27,11 @@
#include "jni_util.h"
#include "jvm.h"
#include "jlong.h"
#include "java_nio_MappedByteBuffer.h"
#include "java_nio_MappedMemoryUtils.h"
#include <stdlib.h>
JNIEXPORT jboolean JNICALL
Java_java_nio_MappedByteBuffer_isLoaded0(JNIEnv *env, jobject obj, jlong address,
Java_java_nio_MappedMemoryUtils_isLoaded0(JNIEnv *env, jobject obj, jlong address,
jlong len, jint numPages)
{
jboolean loaded = JNI_FALSE;
@ -44,14 +44,21 @@ Java_java_nio_MappedByteBuffer_isLoaded0(JNIEnv *env, jobject obj, jlong address
}
JNIEXPORT void JNICALL
Java_java_nio_MappedByteBuffer_load0(JNIEnv *env, jobject obj, jlong address,
Java_java_nio_MappedMemoryUtils_load0(JNIEnv *env, jobject obj, jlong address,
jlong len)
{
// no madvise available
}
JNIEXPORT void JNICALL
Java_java_nio_MappedByteBuffer_force0(JNIEnv *env, jobject obj, jobject fdo,
Java_java_nio_MappedMemoryUtils_unload0(JNIEnv *env, jobject obj, jlong address,
jlong len)
{
// no madvise available
}
JNIEXPORT void JNICALL
Java_java_nio_MappedMemoryUtils_force0(JNIEnv *env, jobject obj, jobject fdo,
jlong address, jlong len)
{
void *a = (void *) jlong_to_ptr(address);

View File

@ -26,6 +26,7 @@
package jdk.incubator.foreign;
import java.lang.constant.ClassDesc;
import java.lang.constant.Constable;
import java.lang.constant.ConstantDesc;
import java.lang.constant.ConstantDescs;
import java.lang.constant.DirectMethodHandleDesc;
@ -33,41 +34,64 @@ import java.lang.constant.DynamicConstantDesc;
import java.lang.constant.MethodHandleDesc;
import java.lang.constant.MethodTypeDesc;
import java.nio.ByteOrder;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.lang.constant.ConstantDescs.BSM_INVOKE;
import static java.lang.constant.ConstantDescs.CD_String;
import static java.lang.constant.ConstantDescs.CD_long;
abstract class AbstractLayout implements MemoryLayout {
private final OptionalLong size;
final long alignment;
private final Optional<String> name;
final Map<String, Constable> attributes;
public AbstractLayout(OptionalLong size, long alignment, Optional<String> name) {
public AbstractLayout(OptionalLong size, long alignment, Map<String, Constable> attributes) {
this.size = size;
this.alignment = alignment;
this.name = name;
}
Optional<String> optName() {
return name;
this.attributes = Collections.unmodifiableMap(attributes);
}
@Override
public AbstractLayout withName(String name) {
return dup(alignment, Optional.of(name));
return withAttribute(LAYOUT_NAME, name);
}
@Override
public final Optional<String> name() {
return name;
return attribute(LAYOUT_NAME).map(String.class::cast);
}
abstract AbstractLayout dup(long alignment, Optional<String> name);
@Override
public Optional<Constable> attribute(String name) {
return Optional.ofNullable(attributes.get(name));
}
@Override
public Stream<String> attributes() {
return attributes.keySet().stream();
}
@Override
public AbstractLayout withAttribute(String name, Constable value) {
Map<String, Constable> newAttributes = new HashMap<>(attributes);
newAttributes.put(name, value);
return dup(alignment, newAttributes);
}
abstract AbstractLayout dup(long alignment, Map<String, Constable> annos);
@Override
public AbstractLayout withBitAlignment(long alignmentBits) {
checkAlignment(alignmentBits);
return dup(alignmentBits, name);
return dup(alignmentBits, attributes);
}
void checkAlignment(long alignmentBitCount) {
@ -99,34 +123,57 @@ abstract class AbstractLayout implements MemoryLayout {
@Override
public long bitSize() {
return size.orElseThrow(this::badSizeException);
return size.orElseThrow(AbstractLayout::badSizeException);
}
static OptionalLong optSize(MemoryLayout layout) {
return ((AbstractLayout)layout).size;
}
private UnsupportedOperationException badSizeException() {
private static 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 (name().isPresent()) {
s = String.format("%s(%s)", s, name().get());
}
if (!hasNaturalAlignment()) {
s = alignment + "%" + s;
}
if (!attributes.isEmpty()) {
s += attributes.entrySet().stream()
.map(e -> e.getKey() + "=" + e.getValue())
.collect(Collectors.joining(",", "[", "]"));
}
return s;
}
<T> DynamicConstantDesc<T> decorateLayoutConstant(DynamicConstantDesc<T> desc) {
if (!hasNaturalAlignment()) {
desc = DynamicConstantDesc.ofNamed(BSM_INVOKE, "withBitAlignment", desc.constantType(), MH_WITH_BIT_ALIGNMENT,
desc, bitAlignment());
}
for (var e : attributes.entrySet()) {
desc = DynamicConstantDesc.ofNamed(BSM_INVOKE, "withAttribute", desc.constantType(), MH_WITH_ATTRIBUTE,
desc, e.getKey(), e.getValue().describeConstable().orElseThrow());
}
return desc;
}
boolean hasNaturalAlignment() {
return size.isPresent() && size.getAsLong() == alignment;
}
@Override
public boolean isPadding() {
return this instanceof PaddingLayout;
}
@Override
public int hashCode() {
return name.hashCode() << Long.hashCode(alignment);
return attributes.hashCode() << Long.hashCode(alignment);
}
@Override
@ -139,8 +186,8 @@ abstract class AbstractLayout implements MemoryLayout {
return false;
}
return Objects.equals(name, ((AbstractLayout)other).name) &&
Objects.equals(alignment, ((AbstractLayout)other).alignment);
return Objects.equals(attributes, ((AbstractLayout) other).attributes) &&
Objects.equals(alignment, ((AbstractLayout) other).alignment);
}
/*** Helper constants for implementing Layout::describeConstable ***/
@ -149,7 +196,7 @@ abstract class AbstractLayout implements MemoryLayout {
= 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_MEMORY_LAYOUT = MemoryLayout.class.describeConstable().get();
static final ClassDesc CD_VALUE_LAYOUT = ValueLayout.class.describeConstable().get();
@ -159,25 +206,33 @@ abstract class AbstractLayout implements MemoryLayout {
static final ClassDesc CD_BYTEORDER = ByteOrder.class.describeConstable().get();
static final ClassDesc CD_Constable = Constable.class.describeConstable().get();
static final ConstantDesc BIG_ENDIAN = DynamicConstantDesc.ofNamed(BSM_GET_STATIC_FINAL, "BIG_ENDIAN", CD_BYTEORDER, CD_BYTEORDER);
static final ConstantDesc LITTLE_ENDIAN = DynamicConstantDesc.ofNamed(BSM_GET_STATIC_FINAL, "LITTLE_ENDIAN", CD_BYTEORDER, CD_BYTEORDER);
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_PADDING = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "ofPaddingBits",
MethodTypeDesc.of(CD_MEMORY_LAYOUT, 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_VALUE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "ofValueBits",
MethodTypeDesc.of(CD_VALUE_LAYOUT, 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_SIZED_SEQUENCE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "ofSequence",
MethodTypeDesc.of(CD_SEQUENCE_LAYOUT, CD_long, CD_MEMORY_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_UNSIZED_SEQUENCE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "ofSequence",
MethodTypeDesc.of(CD_SEQUENCE_LAYOUT, CD_MEMORY_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_STRUCT = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "ofStruct",
MethodTypeDesc.of(CD_GROUP_LAYOUT, CD_MEMORY_LAYOUT.arrayType()));
static final MethodHandleDesc MH_UNION = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_LAYOUT, "ofUnion",
MethodTypeDesc.of(CD_GROUP_LAYOUT, CD_LAYOUT.arrayType()));
static final MethodHandleDesc MH_UNION = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_MEMORY_LAYOUT, "ofUnion",
MethodTypeDesc.of(CD_GROUP_LAYOUT, CD_MEMORY_LAYOUT.arrayType()));
static final MethodHandleDesc MH_WITH_BIT_ALIGNMENT = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_VIRTUAL, CD_MEMORY_LAYOUT, "withBitAlignment",
MethodTypeDesc.of(CD_MEMORY_LAYOUT, CD_long));
static final MethodHandleDesc MH_WITH_ATTRIBUTE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_VIRTUAL, CD_MEMORY_LAYOUT, "withAttribute",
MethodTypeDesc.of(CD_MEMORY_LAYOUT, CD_String, CD_Constable));
}

View File

@ -25,12 +25,14 @@
*/
package jdk.incubator.foreign;
import java.lang.constant.Constable;
import java.lang.constant.ConstantDesc;
import java.lang.constant.ConstantDescs;
import java.lang.constant.DynamicConstantDesc;
import java.lang.constant.MethodHandleDesc;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
@ -100,11 +102,11 @@ public final class GroupLayout extends AbstractLayout {
private final List<MemoryLayout> elements;
GroupLayout(Kind kind, List<MemoryLayout> elements) {
this(kind, elements, kind.alignof(elements), Optional.empty());
this(kind, elements, kind.alignof(elements), Map.of());
}
GroupLayout(Kind kind, List<MemoryLayout> elements, long alignment, Optional<String> name) {
super(kind.sizeof(elements), alignment, name);
GroupLayout(Kind kind, List<MemoryLayout> elements, long alignment, Map<String, Constable> attributes) {
super(kind.sizeof(elements), alignment, attributes);
this.kind = kind;
this.elements = elements;
}
@ -168,8 +170,8 @@ public final class GroupLayout extends AbstractLayout {
}
@Override
GroupLayout dup(long alignment, Optional<String> name) {
return new GroupLayout(kind, elements, alignment, name);
GroupLayout dup(long alignment, Map<String, Constable> attributes) {
return new GroupLayout(kind, elements, alignment, attributes);
}
@Override
@ -184,9 +186,9 @@ public final class GroupLayout extends AbstractLayout {
for (int i = 0 ; i < elements.size() ; i++) {
constants[i + 1] = elements.get(i).describeConstable().get();
}
return Optional.of(DynamicConstantDesc.ofNamed(
return Optional.of(decorateLayoutConstant(DynamicConstantDesc.ofNamed(
ConstantDescs.BSM_INVOKE, kind.name().toLowerCase(),
CD_GROUP_LAYOUT, constants));
CD_GROUP_LAYOUT, constants)));
}
//hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
@ -207,4 +209,12 @@ public final class GroupLayout extends AbstractLayout {
public GroupLayout withBitAlignment(long alignmentBits) {
return (GroupLayout)super.withBitAlignment(alignmentBits);
}
/**
* {@inheritDoc}
*/
@Override
public GroupLayout withAttribute(String name, Constable value) {
return (GroupLayout)super.withAttribute(name, value);
}
}

View File

@ -0,0 +1,132 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.incubator.foreign;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
/**
* A mapped memory segment, that is, a memory segment backed by memory-mapped file.
*
* <p> Mapped memory segments are created via the {@link MemorySegment#mapFromPath(Path, long, FileChannel.MapMode)}.
* Mapped memory segments behave like ordinary segments, but provide additional capabilities to manipulate memory-mapped
* memory regions, such as {@link #force()} and {@link #load()}.
* <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 MappedMemorySegment} directly.
*
* <p> The content of a mapped memory segment can change at any time, for example
* if the content of the corresponding region of the mapped file is changed by
* this (or another) program. Whether or not such changes occur, and when they
* occur, is operating-system dependent and therefore unspecified.
*
* All or part of a mapped memory segment may become
* inaccessible at any time, for example if the backing mapped file is truncated. An
* attempt to access an inaccessible region of a mapped memory segment will not
* change the segment's content and will cause an unspecified exception to be
* thrown either at the time of the access or at some later time. It is
* therefore strongly recommended that appropriate precautions be taken to
* avoid the manipulation of a mapped file by this (or another) program, except to read or write
* the file's content.
*
* @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 subtypes.
*/
public interface MappedMemorySegment extends MemorySegment {
@Override
MappedMemorySegment withAccessModes(int accessModes);
@Override
MappedMemorySegment asSlice(long offset, long newSize);
/**
* Forces any changes made to this segment's content to be written to the
* storage device containing the mapped file.
*
* <p> If the file mapped into this segment resides on a local storage
* device then when this method returns it is guaranteed that all changes
* made to the segment since it was created, or since this method was last
* invoked, will have been written to that device.
*
* <p> If the file does not reside on a local device then no such guarantee
* is made.
*
* <p> If this segment was not mapped in read/write mode ({@link
* java.nio.channels.FileChannel.MapMode#READ_WRITE}) then
* invoking this method may have no effect. In particular, the
* method has no effect for segments mapped in read-only or private
* mapping modes. This method may or may not have an effect for
* implementation-specific mapping modes.
* </p>
*/
void force();
/**
* Loads this segment's content into physical memory.
*
* <p> This method makes a best effort to ensure that, when it returns,
* this segment's contents is resident in physical memory. Invoking this
* method may cause some number of page faults and I/O operations to
* occur. </p>
*/
void load();
/**
* Unloads this segment's content from physical memory.
*
* <p> This method makes a best effort to ensure that this segment's contents are
* are no longer resident in physical memory. Accessing this segment's contents
* after invoking this method may cause some number of page faults and I/O operations to
* occur (as this segment's contents might need to be paged back in). </p>
*/
void unload();
/**
* Tells whether or not this segment's content is resident in physical
* memory.
*
* <p> A return value of {@code true} implies that it is highly likely
* that all of the data in this segment is resident in physical memory and
* may therefore be accessed without incurring any virtual-memory page
* faults or I/O operations. A return value of {@code false} does not
* necessarily imply that the segment's content is not resident in physical
* memory.
*
* <p> The returned value is a hint, rather than a guarantee, because the
* underlying operating system may have paged out some of the segment's data
* by the time that an invocation of this method returns. </p>
*
* @return {@code true} if it is likely that this segment's content
* is resident in physical memory
*/
boolean isLoaded();
}

View File

@ -29,13 +29,16 @@ 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#addOffset(long)}.
* A memory address models a reference into a memory location. Memory addresses are typically obtained using the
* {@link MemorySegment#baseAddress()} method; such addresses are said to be <em>checked</em>, and can be expressed
* as <em>offsets</em> into some underlying memory segment (see {@link #segment()} and {@link #segmentOffset()}).
* Since checked memory addresses feature both spatial and temporal bounds, these addresses can <em>safely</em> be
* dereferenced using a memory access var handle (see {@link MemoryHandles}).
* <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.
* If an address does not have any associated segment, it is said to be <em>unchecked</em>. Unchecked memory
* addresses do not feature known spatial or temporal bounds; as such, attempting a memory dereference operation
* using an unchecked memory address will result in a runtime exception. Unchecked addresses can be obtained
* e.g. by calling the {@link #ofLong(long)} method.
* <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
@ -60,18 +63,35 @@ public interface MemoryAddress {
MemoryAddress addOffset(long offset);
/**
* The offset of this memory address into the underlying segment.
*
* @return the offset
* Returns the offset of this memory address into the underlying segment (if any).
* @return the offset of this memory address into the underlying segment (if any).
* @throws UnsupportedOperationException if no segment is associated with this memory address,
* e.g. if {@code segment() == null}.
*/
long offset();
long segmentOffset();
/**
* The memory segment this address belongs to.
* @return The memory segment this address belongs to.
* Returns the raw long value associated to this memory address.
* @return The raw long value associated to this memory address.
* @throws UnsupportedOperationException if this memory address is associated with an heap segment.
*/
long toRawLongValue();
/**
* Returns the memory segment (if any) this address belongs to.
* @return The memory segment this address belongs to, or {@code null} if no such segment exists.
*/
MemorySegment segment();
/**
* Reinterpret this address as an offset into the provided segment.
* @param segment the segment to be rebased
* @return a new address pointing to the same memory location through the provided segment
* @throws IllegalArgumentException if the provided segment is not a valid rebase target for this address. This
* can happen, for instance, if an heap-based addressed is rebased to an off-heap memory segment.
*/
MemoryAddress rebase(MemorySegment segment);
/**
* Compares the specified object with this address for equality. Returns {@code true} if and only if the specified
* object is also an address, and it refers to the same memory location as this address.
@ -80,7 +100,7 @@ public interface MemoryAddress {
* can happen, for instance, if the segment associated with one address is a <em>slice</em>
* (see {@link MemorySegment#asSlice(long, long)}) of the segment associated with the other address. Moreover,
* two addresses might be considered equals despite differences in the temporal bounds associated with their
* corresponding segments (this is possible, for example, as a result of calls to {@link MemorySegment#acquire()}).
* corresponding segments.
*
* @param that the object to be compared for equality with this address.
* @return {@code true} if the specified object is equal to this address.
@ -100,7 +120,13 @@ public interface MemoryAddress {
* through {@code src.addOffset(bytes - 1)} are copied into addresses {@code dst} through {@code dst.addOffset(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.addOffset(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.addOffset(bytes - 1)}.
* and then the contents of the temporary segment were copied into the bytes at addresses {@code dst} through
* {@code dst.addOffset(bytes - 1)}.
* <p>
* The result of a bulk copy is unspecified if, in the uncommon case, the source and target address ranges do not
* overlap, but refer to overlapping regions of the same backing storage using different addresses. For example,
* this may occur if the same file is {@link MemorySegment#mapFromPath mapped} to two segments.
*
* @param src the source address.
* @param dst the target address.
* @param bytes the number of bytes to be copied.
@ -108,10 +134,30 @@ public interface MemoryAddress {
* 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()}).
* @throws UnsupportedOperationException if either {@code src} or {@code dst} do not feature required access modes;
* more specifically, {@code src} should be associated with a segment with {@link MemorySegment#READ} access mode,
* while {@code dst} should be associated with a segment with {@link MemorySegment#WRITE} access mode.
*/
static void copy(MemoryAddress src, MemoryAddress dst, long bytes) {
MemoryAddressImpl.copy((MemoryAddressImpl)src, (MemoryAddressImpl)dst, bytes);
}
/**
* The <em>unchecked</em> memory address instance modelling the {@code NULL} address. This address is <em>not</em> backed by
* a memory segment and hence it cannot be dereferenced.
*/
MemoryAddress NULL = new MemoryAddressImpl( 0L);
/**
* Obtain a new <em>unchecked</em> memory address instance from given long address. The returned address is <em>not</em> backed by
* a memory segment and hence it cannot be dereferenced.
* @param value the long address.
* @return the new memory address instance.
*/
static MemoryAddress ofLong(long value) {
return value == 0 ?
NULL :
new MemoryAddressImpl(value);
}
}

View File

@ -30,8 +30,12 @@ import jdk.internal.access.SharedSecrets;
import jdk.internal.foreign.Utils;
import sun.invoke.util.Wrapper;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
import java.util.List;
/**
* This class defines several factory methods for constructing and combining memory access var handles.
@ -123,6 +127,27 @@ public final class MemoryHandles {
//sorry, just the one!
}
private static final MethodHandle LONG_TO_ADDRESS;
private static final MethodHandle ADDRESS_TO_LONG;
private static final MethodHandle ADD_OFFSET;
private static final MethodHandle ADD_STRIDE;
static {
try {
LONG_TO_ADDRESS = MethodHandles.lookup().findStatic(MemoryHandles.class, "longToAddress",
MethodType.methodType(MemoryAddress.class, long.class));
ADDRESS_TO_LONG = MethodHandles.lookup().findStatic(MemoryHandles.class, "addressToLong",
MethodType.methodType(long.class, MemoryAddress.class));
ADD_OFFSET = MethodHandles.lookup().findStatic(MemoryHandles.class, "addOffset",
MethodType.methodType(MemoryAddress.class, MemoryAddress.class, long.class));
ADD_STRIDE = MethodHandles.lookup().findStatic(MemoryHandles.class, "addStride",
MethodType.methodType(MemoryAddress.class, MemoryAddress.class, long.class, long.class));
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
/**
* Creates a memory access var handle with the given carrier type and byte order.
*
@ -174,85 +199,325 @@ public final class MemoryHandles {
throw new IllegalArgumentException("Bad alignment: " + alignmentBytes);
}
return JLI.memoryAddressViewVarHandle(carrier, alignmentBytes - 1, byteOrder, 0, new long[]{});
return Utils.fixUpVarHandle(JLI.memoryAccessVarHandle(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>.
* Returns a var handle that adds a <em>fixed</em> offset to the incoming {@link MemoryAddress} coordinate
* and then propagates such value to the target var handle. That is,
* when the returned var handle receives a memory address coordinate pointing at a memory location at
* offset <em>O</em>, a memory address coordinate pointing at a memory location at offset <em>O' + O</em>
* is created, and then passed to the target var handle.
*
* 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.
* The returned var handle will feature the same type and access coordinates as the target var handle.
*
* @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.
* @return the adapted var handle.
* @throws IllegalArgumentException if the first access coordinate type is not of type {@link MemoryAddress}.
*/
public static VarHandle withOffset(VarHandle target, long bytesOffset) {
if (bytesOffset < 0) {
throw new IllegalArgumentException("Illegal offset: " + bytesOffset);
if (bytesOffset == 0) {
return target; //nothing to do
}
long alignMask = JLI.memoryAddressAlignmentMask(target);
checkAddressFirstCoordinate(target);
if ((bytesOffset & alignMask) != 0) {
throw new IllegalArgumentException("Offset " + bytesOffset + " does not conform to alignment " + (alignMask + 1));
if (JLI.isMemoryAccessVarHandle(target) &&
(bytesOffset & JLI.memoryAddressAlignmentMask(target)) == 0) {
//flatten
return Utils.fixUpVarHandle(JLI.memoryAccessVarHandle(
JLI.memoryAddressCarrier(target),
JLI.memoryAddressAlignmentMask(target),
JLI.memoryAddressByteOrder(target),
JLI.memoryAddressOffset(target) + bytesOffset,
JLI.memoryAddressStrides(target)));
} else {
//slow path
VarHandle res = collectCoordinates(target, 0, ADD_OFFSET);
return insertCoordinates(res, 1, bytesOffset);
}
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).
* Returns a var handle which adds a <em>variable</em> offset to the incoming {@link MemoryAddress}
* access coordinate value and then propagates such value to the target var handle.
* That is, when the returned var handle receives a memory address coordinate pointing at a memory location at
* offset <em>O</em>, a new memory address coordinate pointing at a memory location at offset <em>(S * X) + O</em>
* is created, and then passed to the target var handle,
* 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}).
*
* @apiNote the resulting var handle features certain <a href="#memaccess-mode">access mode restrictions</a>,
* which are common to all memory access var handles.
* The returned var handle will feature the same type as the target var handle; an additional access coordinate
* of type {@code long} will be added to the access coordinate types of the target var handle at the position
* immediately following the leading access coordinate of type {@link MemoryAddress}.
*
* @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.
* @param bytesStride the stride, in bytes, by which to multiply the coordinate value.
* @return the adapted var handle.
* @throws IllegalArgumentException if the first access coordinate type is not of type {@link MemoryAddress}.
*/
public static VarHandle withStride(VarHandle target, long bytesStride) {
if (bytesStride == 0) {
throw new IllegalArgumentException("Stride must be positive: " + bytesStride);
return dropCoordinates(target, 1, long.class); // dummy coordinate
}
long alignMask = JLI.memoryAddressAlignmentMask(target);
checkAddressFirstCoordinate(target);
if ((bytesStride & alignMask) != 0) {
throw new IllegalArgumentException("Stride " + bytesStride + " does not conform to alignment " + (alignMask + 1));
if (JLI.isMemoryAccessVarHandle(target) &&
(bytesStride & JLI.memoryAddressAlignmentMask(target)) == 0) {
//flatten
long[] strides = JLI.memoryAddressStrides(target);
long[] newStrides = new long[strides.length + 1];
System.arraycopy(strides, 0, newStrides, 1, strides.length);
newStrides[0] = bytesStride;
return Utils.fixUpVarHandle(JLI.memoryAccessVarHandle(
JLI.memoryAddressCarrier(target),
JLI.memoryAddressAlignmentMask(target),
JLI.memoryAddressByteOrder(target),
JLI.memoryAddressOffset(target),
newStrides));
} else {
//slow path
VarHandle res = collectCoordinates(target, 0, ADD_STRIDE);
return insertCoordinates(res, 2, bytesStride);
}
}
/**
* Adapt an existing var handle into a new var handle whose carrier type is {@link MemoryAddress}.
* That is, when calling {@link VarHandle#get(Object...)} on the returned var handle,
* the read numeric value will be turned into a memory address (as if by calling {@link MemoryAddress#ofLong(long)});
* similarly, when calling {@link VarHandle#set(Object...)}, the memory address to be set will be converted
* into a numeric value, and then written into memory. The amount of bytes read (resp. written) from (resp. to)
* memory depends on the carrier of the original memory access var handle.
*
* @param target the memory access var handle to be adapted
* @return the adapted var handle.
* @throws IllegalArgumentException if the carrier type of {@code varHandle} is either {@code boolean},
* {@code float}, or {@code double}, or is not a primitive type.
*/
public static VarHandle asAddressVarHandle(VarHandle target) {
Class<?> carrier = target.varType();
if (!carrier.isPrimitive() || carrier == boolean.class ||
carrier == float.class || carrier == double.class) {
throw new IllegalArgumentException("Unsupported carrier type: " + carrier.getName());
}
long offset = JLI.memoryAddressOffset(target);
if (carrier != long.class) {
// slow-path, we need to adapt
return filterValue(target,
MethodHandles.explicitCastArguments(ADDRESS_TO_LONG, MethodType.methodType(carrier, MemoryAddress.class)),
MethodHandles.explicitCastArguments(LONG_TO_ADDRESS, MethodType.methodType(MemoryAddress.class, carrier)));
} else {
// fast-path
return filterValue(target, ADDRESS_TO_LONG, LONG_TO_ADDRESS);
}
}
long[] strides = JLI.memoryAddressStrides(target);
long[] newStrides = new long[strides.length + 1];
System.arraycopy(strides, 0, newStrides, 1, strides.length);
newStrides[0] = bytesStride;
/**
* Adapts a target var handle by pre-processing incoming and outgoing values using a pair of unary filter functions.
* <p>
* When calling e.g. {@link VarHandle#set(Object...)} on the resulting var handle, the incoming value (of type {@code T}, where
* {@code T} is the parameter type of the first filter function) is processed using the first filter and then passed
* to the target var handle.
* Conversely, when calling e.g. {@link VarHandle#get(Object...)} on the resulting var handle, the return value obtained from
* the target var handle (of type {@code T}, where {@code T} is the parameter type of the second filter function)
* is processed using the second filter and returned to the caller. More advanced access mode types, such as
* {@link java.lang.invoke.VarHandle.AccessMode#COMPARE_AND_EXCHANGE} might apply both filters at the same time.
* <p>
* For the filters to be well formed, their types must be of the form {@code S -> T} and {@code T -> S},
* respectively, where {@code T} is the type of the target var handle. If this is the case, the resulting var handle will
* have type {@code S}.
* <p>
* The resulting var handle will feature the same access modes (see {@link java.lang.invoke.VarHandle.AccessMode}) and
* atomic access guarantees as those featured by the target var handle.
*
* @param target the target var handle
* @param filterToTarget a filter to convert some type {@code S} into the type of {@code target}
* @param filterFromTarget a filter to convert the type of {@code target} to some type {@code S}
* @return an adapter var handle which accepts a new type, performing the provided boxing/unboxing conversions.
* @throws NullPointerException if either {@code target}, {@code filterToTarget} or {@code filterFromTarget} are {@code == null}.
* @throws IllegalArgumentException if {@code filterFromTarget} and {@code filterToTarget} are not well-formed, that is, they have types
* other than {@code S -> T} and {@code T -> S}, respectively, where {@code T} is the type of the target var handle,
* or if either {@code filterFromTarget} or {@code filterToTarget} throws any checked exceptions.
*/
public static VarHandle filterValue(VarHandle target, MethodHandle filterToTarget, MethodHandle filterFromTarget) {
return JLI.filterValue(target, filterToTarget, filterFromTarget);
}
return JLI.memoryAddressViewVarHandle(
JLI.memoryAddressCarrier(target),
alignMask,
JLI.memoryAddressByteOrder(target),
offset,
newStrides);
/**
* Adapts a target var handle by pre-processing incoming coordinate values using unary filter functions.
* <p>
* When calling e.g. {@link VarHandle#get(Object...)} on the resulting var handle, the incoming coordinate values
* starting at position {@code pos} (of type {@code C1, C2 ... Cn}, where {@code C1, C2 ... Cn} are the return type
* of the unary filter functions) are transformed into new values (of type {@code S1, S2 ... Sn}, where {@code S1, S2 ... Sn} are the
* parameter types of the unary filter functions), and then passed (along with any coordinate that was left unaltered
* by the adaptation) to the target var handle.
* <p>
* For the coordinate filters to be well formed, their types must be of the form {@code S1 -> T1, S2 -> T1 ... Sn -> Tn},
* where {@code T1, T2 ... Tn} are the coordinate types starting at position {@code pos} of the target var handle.
* <p>
* The resulting var handle will feature the same access modes (see {@link java.lang.invoke.VarHandle.AccessMode}) and
* atomic access guarantees as those featured by the target var handle.
*
* @param target the target var handle
* @param pos the position of the first coordinate to be transformed
* @param filters the unary functions which are used to transform coordinates starting at position {@code pos}
* @return an adapter var handle which accepts new coordinate types, applying the provided transformation
* to the new coordinate values.
* @throws NullPointerException if either {@code target}, {@code filters} are {@code == null}.
* @throws IllegalArgumentException if the handles in {@code filters} are not well-formed, that is, they have types
* other than {@code S1 -> T1, S2 -> T2, ... Sn -> Tn} where {@code T1, T2 ... Tn} are the coordinate types starting
* at position {@code pos} of the target var handle, if {@code pos} is not between 0 and the target var handle coordinate arity, inclusive,
* or if more filters are provided than the actual number of coordinate types available starting at {@code pos},
* or if any of the filters throws any checked exceptions.
*/
public static VarHandle filterCoordinates(VarHandle target, int pos, MethodHandle... filters) {
return JLI.filterCoordinates(target, pos, filters);
}
/**
* Provides a target var handle with one or more <em>bound coordinates</em>
* in advance of the var handle's invocation. As a consequence, the resulting var handle will feature less
* coordinate types than the target var handle.
* <p>
* When calling e.g. {@link VarHandle#get(Object...)} on the resulting var handle, incoming coordinate values
* are joined with bound coordinate values, and then passed to the target var handle.
* <p>
* For the bound coordinates to be well formed, their types must be {@code T1, T2 ... Tn },
* where {@code T1, T2 ... Tn} are the coordinate types starting at position {@code pos} of the target var handle.
* <p>
* The resulting var handle will feature the same access modes (see {@link java.lang.invoke.VarHandle.AccessMode}) and
* atomic access guarantees as those featured by the target var handle.
*
* @param target the var handle to invoke after the bound coordinates are inserted
* @param pos the position of the first coordinate to be inserted
* @param values the series of bound coordinates to insert
* @return an adapter var handle which inserts an additional coordinates,
* before calling the target var handle
* @throws NullPointerException if either {@code target}, {@code values} are {@code == null}.
* @throws IllegalArgumentException if {@code pos} is not between 0 and the target var handle coordinate arity, inclusive,
* or if more values are provided than the actual number of coordinate types available starting at {@code pos}.
* @throws ClassCastException if the bound coordinates in {@code values} are not well-formed, that is, they have types
* other than {@code T1, T2 ... Tn }, where {@code T1, T2 ... Tn} are the coordinate types starting at position {@code pos}
* of the target var handle.
*/
public static VarHandle insertCoordinates(VarHandle target, int pos, Object... values) {
return JLI.insertCoordinates(target, pos, values);
}
/**
* Provides a var handle which adapts the coordinate values of the target var handle, by re-arranging them
* so that the new coordinates match the provided ones.
* <p>
* The given array controls the reordering.
* Call {@code #I} the number of incoming coordinates (the value
* {@code newCoordinates.size()}, and call {@code #O} the number
* of outgoing coordinates (the number of coordinates associated with the target var handle).
* Then the length of the reordering array must be {@code #O},
* and each element must be a non-negative number less than {@code #I}.
* For every {@code N} less than {@code #O}, the {@code N}-th
* outgoing coordinate will be taken from the {@code I}-th incoming
* coordinate, where {@code I} is {@code reorder[N]}.
* <p>
* No coordinate value conversions are applied.
* The type of each incoming coordinate, as determined by {@code newCoordinates},
* must be identical to the type of the corresponding outgoing coordinate
* in the target var handle.
* <p>
* The reordering array need not specify an actual permutation.
* An incoming coordinate will be duplicated if its index appears
* more than once in the array, and an incoming coordinate will be dropped
* if its index does not appear in the array.
* <p>
* The resulting var handle will feature the same access modes (see {@link java.lang.invoke.VarHandle.AccessMode}) and
* atomic access guarantees as those featured by the target var handle.
* @param target the var handle to invoke after the coordinates have been reordered
* @param newCoordinates the new coordinate types
* @param reorder an index array which controls the reordering
* @return an adapter var handle which re-arranges the incoming coordinate values,
* before calling the target var handle
* @throws NullPointerException if either {@code target}, {@code newCoordinates} or {@code reorder} are {@code == null}.
* @throws IllegalArgumentException if the index array length is not equal to
* the number of coordinates of the target var handle, or if any index array element is not a valid index for
* a coordinate of {@code newCoordinates}, or if two corresponding coordinate types in
* the target var handle and in {@code newCoordinates} are not identical.
*/
public static VarHandle permuteCoordinates(VarHandle target, List<Class<?>> newCoordinates, int... reorder) {
return JLI.permuteCoordinates(target, newCoordinates, reorder);
}
/**
* Adapts a target var handle handle by pre-processing
* a sub-sequence of its coordinate values with a filter (a method handle).
* The pre-processed coordinates are replaced by the result (if any) of the
* filter function and the target var handle is then called on the modified (usually shortened)
* coordinate list.
* <p>
* If {@code R} is the return type of the filter (which cannot be void), the target var handle must accept a value of
* type {@code R} as its coordinate in position {@code pos}, preceded and/or followed by
* any coordinate not passed to the filter.
* No coordinates are reordered, and the result returned from the filter
* replaces (in order) the whole subsequence of coordinates originally
* passed to the adapter.
* <p>
* The argument types (if any) of the filter
* replace zero or one coordinate types of the target var handle, at position {@code pos},
* in the resulting adapted var handle.
* The return type of the filter must be identical to the
* coordinate type of the target var handle at position {@code pos}, and that target var handle
* coordinate is supplied by the return value of the filter.
* <p>
* The resulting var handle will feature the same access modes (see {@link java.lang.invoke.VarHandle.AccessMode}) and
* atomic access guarantees as those featured by the target var handle.
*
* @param target the var handle to invoke after the coordinates have been filtered
* @param pos the position of the coordinate to be filtered
* @param filter the filter method handle
* @return an adapter var handle which filters the incoming coordinate values,
* before calling the target var handle
* @throws NullPointerException if either {@code target}, {@code filter} are {@code == null}.
* @throws IllegalArgumentException if the return type of {@code filter}
* is void, or it is not the same as the {@code pos} coordinate of the target var handle,
* if {@code pos} is not between 0 and the target var handle coordinate arity, inclusive,
* if the resulting var handle's type would have <a href="MethodHandle.html#maxarity">too many coordinates</a>,
* or if {@code filter} throws any checked exceptions.
*/
public static VarHandle collectCoordinates(VarHandle target, int pos, MethodHandle filter) {
return JLI.collectCoordinates(target, pos, filter);
}
/**
* Returns a var handle which will discard some dummy coordinates before delegating to the
* target var handle. As a consequence, the resulting var handle will feature more
* coordinate types than the target var handle.
* <p>
* The {@code pos} argument may range between zero and <i>N</i>, where <i>N</i> is the arity of the
* target var handle's coordinate types. If {@code pos} is zero, the dummy coordinates will precede
* the target's real arguments; if {@code pos} is <i>N</i> they will come after.
* <p>
* The resulting var handle will feature the same access modes (see {@link java.lang.invoke.VarHandle.AccessMode}) and
* atomic access guarantees as those featured by the target var handle.
*
* @param target the var handle to invoke after the dummy coordinates are dropped
* @param pos position of first coordinate to drop (zero for the leftmost)
* @param valueTypes the type(s) of the coordinate(s) to drop
* @return an adapter var handle which drops some dummy coordinates,
* before calling the target var handle
* @throws NullPointerException if either {@code target}, {@code valueTypes} are {@code == null}.
* @throws IllegalArgumentException if {@code pos} is not between 0 and the target var handle coordinate arity, inclusive.
*/
public static VarHandle dropCoordinates(VarHandle target, int pos, Class<?>... valueTypes) {
return JLI.dropCoordinates(target, pos, valueTypes);
}
private static void checkAddressFirstCoordinate(VarHandle handle) {
if (handle.coordinateTypes().size() < 1 ||
handle.coordinateTypes().get(0) != MemoryAddress.class) {
throw new IllegalArgumentException("Expected var handle with leading coordinate of type MemoryAddress");
}
}
private static void checkCarrier(Class<?> carrier) {
@ -265,4 +530,20 @@ public final class MemoryHandles {
long bitsAlignment = Math.max(8, Wrapper.forPrimitiveType(carrier).bitWidth());
return Utils.bitsToBytesOrThrow(bitsAlignment, IllegalStateException::new);
}
private static MemoryAddress longToAddress(long value) {
return MemoryAddress.ofLong(value);
}
private static long addressToLong(MemoryAddress value) {
return value.toRawLongValue();
}
private static MemoryAddress addOffset(MemoryAddress address, long offset) {
return address.addOffset(offset);
}
private static MemoryAddress addStride(MemoryAddress address, long index, long stride) {
return address.addOffset(index * stride);
}
}

View File

@ -41,6 +41,7 @@ import java.util.OptionalLong;
import java.util.Set;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;
/**
* A memory layout can be used to describe the contents of a memory segment in a <em>language neutral</em> fashion.
@ -76,7 +77,10 @@ import java.util.function.UnaryOperator;
* <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 padding layout <em>L</em>, the natural alignment is 1, regardless of its size; that is, in the absence
* of an explicit alignment constraint, a padding layout should not affect the alignment constraint of the group
* layout it is nested into</li>
* <li>for a value 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>
@ -129,6 +133,25 @@ MemoryLayout newSeq = MemoryLayout.ofSequence(5,
MemoryLayout.ofStruct(
MemoryLayout.ofPaddingBits(32),
MemoryLayout.ofPaddingBits(32)
));
* }</pre></blockquote>
*
* Similarly, we can select the member layout named {@code value}, as follows:
* <blockquote><pre>{@code
MemoryLayout value = seq.select(PathElement.sequenceElement(), PathElement.groupElement("value"));
* }</pre></blockquote>
*
* And, we can also replace the layout named {@code value} with another layout, as follows:
* <blockquote><pre>{@code
MemoryLayout newSeq = seq.map(l -> MemoryLayout.ofPadding(32), PathElement.sequenceElement(), PathElement.groupElement("value"));
* }</pre></blockquote>
*
* That is, the above declaration is identical to the following, more verbose one:
* <blockquote><pre>{@code
MemoryLayout newSeq = MemoryLayout.ofSequence(5,
MemoryLayout.ofStruct(
MemoryLayout.ofPaddingBits(32),
MemoryLayout.ofPaddingBits(32)
));
* }</pre></blockquote>
*
@ -145,6 +168,13 @@ VarHandle valueHandle = seq.map(int.class, PathElement.sequenceElement(), PathEl
* it follows that the memory access var handle {@code valueHandle} will feature an extra {@code long}
* access coordinate.
*
* <h2>Layout attributes</h2>
*
* Layouts can be optionally associated with one or more <em>attributes</em>. A layout attribute forms a <em>name/value</em>
* pair, where the name is a {@link String} and the value is a {@link Constable}. The most common form of layout attribute
* is the <em>layout name</em> (see {@link #LAYOUT_NAME}), a custom name that can be associated to memory layouts and that can be referred to when
* constructing <a href="MemoryLayout.html#layout-paths"><em>layout paths</em></a>.
*
* @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.
@ -198,6 +228,11 @@ public interface MemoryLayout extends Constable {
/**
* Return the <em>name</em> (if any) associated with this layout.
* <p>
* This is equivalent to the following code:
* <blockquote><pre>{@code
attribute(LAYOUT_NAME).map(String.class::cast);
* }</pre></blockquote>
*
* @return the layout <em>name</em> (if any).
* @see MemoryLayout#withName(String)
@ -206,6 +241,11 @@ public interface MemoryLayout extends Constable {
/**
* Creates a new layout which features the desired layout <em>name</em>.
* <p>
* This is equivalent to the following code:
* <blockquote><pre>{@code
withAttribute(LAYOUT_NAME, name);
* }</pre></blockquote>
*
* @param name the layout name.
* @return a new layout which is the same as this layout, except for the <em>name</em> associated to it.
@ -262,6 +302,32 @@ public interface MemoryLayout extends Constable {
*/
MemoryLayout withBitAlignment(long bitAlignment);
/**
* Returns the attribute with the given name (if it exists).
*
* @param name the attribute name
* @return the attribute with the given name (if it exists).
*/
Optional<Constable> attribute(String name);
/**
* Returns a new memory layout which features the same attributes as this layout, plus the newly specified attribute.
* If this layout already contains an attribute with the same name, the existing attribute value is overwritten in the returned
* layout.
*
* @param name the attribute name.
* @param value the attribute value.
* @return a new memory layout which features the same attributes as this layout, plus the newly specified attribute.
*/
MemoryLayout withAttribute(String name, Constable value);
/**
* Returns a stream of the attribute names associated with this layout.
*
* @return a stream of the attribute names associated with this layout.
*/
Stream<String> attributes();
/**
* Computes the offset, in bits, of the layout selected by a given layout path, where the path is considered rooted in this
* layout.
@ -345,6 +411,12 @@ public interface MemoryLayout extends Constable {
return finalizer.apply(path);
}
/**
* Is this a padding layout (e.g. a layout created from {@link #ofPaddingBits(long)}) ?
* @return true, if this layout is a padding layout.
*/
boolean isPadding();
/**
* 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
@ -448,7 +520,7 @@ E * (S + I * F)
* the same kind, have the same size, name and alignment constraints. Furthermore, depending on the layout kind, additional
* conditions must be satisfied:
* <ul>
* <li>two value layouts are considered equal if they have the same endianness (see {@link ValueLayout#order()})</li>
* <li>two value layouts are considered equal if they have the same byte order (see {@link ValueLayout#order()})</li>
* <li>two sequence layouts are considered equal if they have the same element count (see {@link SequenceLayout#elementCount()}), and
* if their element layouts (see {@link SequenceLayout#elementLayout()}) are also equal</li>
* <li>two group layouts are considered equal if they are of the same kind (see {@link GroupLayout#isStruct()},
@ -543,4 +615,9 @@ E * (S + I * F)
static GroupLayout ofUnion(MemoryLayout... elements) {
return new GroupLayout(GroupLayout.Kind.UNION, List.of(elements));
}
/**
* Attribute name used to specify the <em>name</em> property of a memory layout (see {@link #name()} and {@link #withName(String)}).
*/
String LAYOUT_NAME = "layout/name";
}

View File

@ -28,11 +28,18 @@ package jdk.incubator.foreign;
import java.nio.ByteBuffer;
import jdk.internal.foreign.AbstractMemorySegmentImpl;
import jdk.internal.foreign.HeapMemorySegmentImpl;
import jdk.internal.foreign.MappedMemorySegmentImpl;
import jdk.internal.foreign.NativeMemorySegmentImpl;
import jdk.internal.foreign.Utils;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.util.Objects;
import java.util.Spliterator;
import java.util.function.Consumer;
/**
* A memory segment models a contiguous region of memory. A memory segment is associated with both spatial
@ -67,7 +74,8 @@ import java.nio.file.Path;
* 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>.
* {@link MemorySegment#mapFromPath(Path, long, FileChannel.MapMode)}. Such memory segments are called <em>mapped memory segments</em>
* (see {@link MappedMemorySegment}).
*
* <h2>Closing a memory segment</h2>
*
@ -77,8 +85,6 @@ import java.nio.file.Path;
* <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
@ -94,34 +100,61 @@ import java.nio.file.Path;
* 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.
* Memory segments support <em>serial thread confinement</em>; that is, ownership of a memory segment can change (see
* {@link #withOwnerThread(Thread)}). This allows, for instance, for two threads {@code A} and {@code B} to share
* a segment in a controlled, cooperative and race-free fashion.
* <p>
* In some cases, it might be useful for multiple threads to process the contents of the same memory segment concurrently
* (e.g. in the case of parallel processing); while memory segments provide strong confinement guarantees, it is possible
* to obtain a {@link Spliterator} from a segment, which can be used to slice the segment and allow multiple thread to
* work in parallel on disjoint segment slices (this assumes that the access mode {@link #ACQUIRE} is set).
* For instance, the following code can be used to sum all int values in a memory segment in parallel:
* <blockquote><pre>{@code
MemorySegment segment = ...
SequenceLayout SEQUENCE_LAYOUT = MemoryLayout.ofSequence(1024, MemoryLayouts.JAVA_INT);
VarHandle VH_int = SEQUENCE_LAYOUT.elementLayout().varHandle(int.class);
int sum = StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT), true)
.mapToInt(s -> (int)VH_int.get(s.baseAddress()))
.sum();
* }</pre></blockquote>
*
* <h2><a id = "access-modes">Access modes</a></h2>
*
* Memory segments supports zero or more <em>access modes</em>. Supported access modes are {@link #READ},
* {@link #WRITE}, {@link #CLOSE}, {@link #ACQUIRE} and {@link #HANDOFF}. The set of access modes supported by a segment alters the
* set of operations that are supported by that segment. For instance, attempting to call {@link #close()} on
* a segment which does not support the {@link #CLOSE} access mode will result in an exception.
* <p>
* The set of supported access modes can only be made stricter (by supporting <em>fewer</em> access modes). This means
* that restricting the set of access modes supported by a segment before sharing it with other clients
* is generally a good practice if the creator of the segment wants to retain some control over how the segment
* is going to be accessed.
*
* <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)}).
* Memory segments support <em>views</em>. For instance, it is possible to alter the set of supported access modes,
* by creating an <em>immutable</em> view of a memory segment, as follows:
* <blockquote><pre>{@code
MemorySegment segment = ...
MemorySegment roSegment = segment.withAccessModes(segment.accessModes() & ~WRITE);
* }</pre></blockquote>
* 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.
* another actor, it is the responsibility of that client to take protective measures, such as removing {@link #CLOSE}
* from the set of supported access modes, 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.
* the same spatial and temporal access restrictions associated to the memory segment 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.
* {@link MappedMemorySegment} and other explicitly permitted subtypes.
*
* @implSpec
* Implementations of this interface are immutable and thread-safe.
@ -129,20 +162,40 @@ import java.nio.file.Path;
public interface MemorySegment extends AutoCloseable {
/**
* The base memory address associated with this memory segment.
* The base memory address associated with this memory segment. The returned address is
* a <em>checked</em> memory address and can therefore be used in derefrence operations
* (see {@link MemoryAddress}).
* @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.
* Returns a spliterator for the given memory segment. The returned spliterator reports {@link Spliterator#SIZED},
* {@link Spliterator#SUBSIZED}, {@link Spliterator#IMMUTABLE}, {@link Spliterator#NONNULL} and {@link Spliterator#ORDERED}
* characteristics.
* <p>
* The returned spliterator splits the segment according to the specified sequence layout; that is,
* if the supplied layout is a sequence layout whose element count is {@code N}, then calling {@link Spliterator#trySplit()}
* will result in a spliterator serving approximatively {@code N/2} elements (depending on whether N is even or not).
* As such, splitting is possible as long as {@code N >= 2}. The spliterator returns segments that feature the same
* <a href="#access-modes">access modes</a> as the given segment less the {@link #CLOSE} access mode.
* <p>
* The returned spliterator effectively allows to slice a segment into disjoint sub-segments, which can then
* be processed in parallel by multiple threads (if the access mode {@link #ACQUIRE} is set).
* While closing the segment (see {@link #close()}) during pending concurrent execution will generally
* fail with an exception, it is possible to close a segment when a spliterator has been obtained but no thread
* is actively working on it using {@link Spliterator#tryAdvance(Consumer)}; in such cases, any subsequent call
* to {@link Spliterator#tryAdvance(Consumer)} will fail with an exception.
* @param segment the segment to be used for splitting.
* @param layout the layout to be used for splitting.
* @param <S> the memory segment type
* @return the element spliterator for this segment
* @throws IllegalStateException if the segment is not <em>alive</em>, or if access occurs from a thread other than the
* thread owning this segment
*/
MemorySegment acquire();
static <S extends MemorySegment> Spliterator<S> spliterator(S segment, SequenceLayout layout) {
return AbstractMemorySegmentImpl.spliterator(segment, layout);
}
/**
* The thread owning this segment.
@ -150,6 +203,27 @@ public interface MemorySegment extends AutoCloseable {
*/
Thread ownerThread();
/**
* Obtains a new memory segment backed by the same underlying memory region as this segment,
* but with different owner thread. As a side-effect, this segment will be marked as <em>not alive</em>,
* and subsequent operations on this segment will result in runtime errors.
* <p>
* Write accesses to the segment's content <a href="../../../java/util/concurrent/package-summary.html#MemoryVisibility"><i>happens-before</i></a>
* hand-over from the current owner thread to the new owner thread, which in turn <i>happens before</i> read accesses to the segment's contents on
* the new owner thread.
*
* @param newOwner the new owner thread.
* @return a new memory segment backed by the same underlying memory region as this segment,
* owned by {@code newOwner}.
* @throws IllegalStateException if this segment is not <em>alive</em>, or if access occurs from a thread other than the
* thread owning this segment, or if the segment cannot be closed because it is being operated upon by a different
* thread (see {@link #spliterator(MemorySegment, SequenceLayout)}).
* @throws NullPointerException if {@code newOwner == null}
* @throws IllegalArgumentException if the segment is already a confined segment owner by {@code newOnwer}.
* @throws UnsupportedOperationException if this segment does not support the {@link #HANDOFF} access mode.
*/
MemorySegment withOwnerThread(Thread newOwner);
/**
* The size (in bytes) of this memory segment.
* @return The size (in bytes) of this memory segment.
@ -157,13 +231,31 @@ public interface MemorySegment extends AutoCloseable {
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.
* Obtains a segment view with specific <a href="#access-modes">access modes</a>. Supported access modes are {@link #READ}, {@link #WRITE},
* {@link #CLOSE}, {@link #ACQUIRE} and {@link #HANDOFF}. It is generally not possible to go from a segment with stricter access modes
* to one with less strict access modes. For instance, attempting to add {@link #WRITE} access mode to a read-only segment
* will be met with an exception.
* @param accessModes an ORed mask of zero or more access modes.
* @return a segment view with specific access modes.
* @throws IllegalArgumentException when {@code mask} is an access mask which is less strict than the one supported by this
* segment, or when {@code mask} contains bits not associated with any of the supported access modes.
*/
MemorySegment asReadOnly();
MemorySegment withAccessModes(int accessModes);
/**
* Does this segment support a given set of access modes?
* @param accessModes an ORed mask of zero or more access modes.
* @return true, if the access modes in {@code accessModes} are stricter than the ones supported by this segment.
* @throws IllegalArgumentException when {@code mask} contains bits not associated with any of the supported access modes.
*/
boolean hasAccessModes(int accessModes);
/**
* Returns the <a href="#access-modes">access modes</a> associated with this segment; the result is represented as ORed values from
* {@link #READ}, {@link #WRITE}, {@link #CLOSE}, {@link #ACQUIRE} and {@link #HANDOFF}.
* @return the access modes associated with this segment.
*/
int accessModes();
/**
* Obtains a new memory segment view whose base address is the same as the base address of this segment plus a given offset,
@ -172,8 +264,6 @@ public interface MemorySegment extends AutoCloseable {
* @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);
@ -184,27 +274,22 @@ public interface MemorySegment extends AutoCloseable {
*/
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()}).
* @throws IllegalStateException if this segment is not <em>alive</em>, or if access occurs from a thread other than the
* thread owning this segment, or if the segment cannot be closed because it is being operated upon by a different
* thread (see {@link #spliterator(MemorySegment, SequenceLayout)}).
* @throws UnsupportedOperationException if this segment does not support the {@link #CLOSE} access mode.
*/
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>
* (e.g. the segment has access mode {@link #READ} but not {@link #WRITE}), 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>
@ -218,17 +303,15 @@ public interface MemorySegment extends AutoCloseable {
* @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.
* than {@link Integer#MAX_VALUE}, or if the segment does not support the {@link #READ} access mode.
*/
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 UnsupportedOperationException if this segment does not feature the {@link #READ} access mode, or 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.
*/
@ -239,6 +322,10 @@ public interface MemorySegment extends AutoCloseable {
* buffer. The segment starts relative to the buffer's position (inclusive)
* and ends relative to the buffer's limit (exclusive).
* <p>
* The segment will feature all <a href="#access-modes">access modes</a>, unless the given
* buffer is {@linkplain ByteBuffer#isReadOnly() read-only} in which case the segment will
* not feature the {@link #WRITE} access mode.
* <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.
*
@ -246,98 +333,98 @@ public interface MemorySegment extends AutoCloseable {
* @return a new buffer memory segment.
*/
static MemorySegment ofByteBuffer(ByteBuffer bb) {
return Utils.makeBufferSegment(bb);
return AbstractMemorySegmentImpl.ofBuffer(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.
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>.
*
* @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);
return HeapMemorySegmentImpl.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.
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>.
*
* @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);
return HeapMemorySegmentImpl.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.
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>.
*
* @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);
return HeapMemorySegmentImpl.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.
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>.
*
* @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);
return HeapMemorySegmentImpl.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.
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>.
*
* @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);
return HeapMemorySegmentImpl.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.
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>.
*
* @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);
return HeapMemorySegmentImpl.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.
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>.
*
* @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);
return HeapMemorySegmentImpl.makeArraySegment(arr);
}
/**
@ -365,7 +452,7 @@ public interface MemorySegment extends AutoCloseable {
* <p>
* This is equivalent to the following code:
* <blockquote><pre>{@code
allocateNative(bytesSize, 1);
allocateNative(bytesSize, 1);
* }</pre></blockquote>
*
* @implNote The block of off-heap memory associated with the returned native memory segment is initialized to zero.
@ -382,25 +469,30 @@ public interface MemorySegment extends AutoCloseable {
/**
* Creates a new mapped memory segment that models a memory-mapped region of a file from a given path.
* <p>
* The segment will feature all <a href="#access-modes">access modes</a>, unless the given mapping mode
* is {@linkplain FileChannel.MapMode#READ_ONLY READ_ONLY}, in which case the segment will not feature
* the {@link #WRITE} access mode.
*
* @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)}.
* @param mapMode a file mapping mode, see {@link FileChannel#map(FileChannel.MapMode, long, long)}; the chosen mapping mode
* might affect the behavior of the returned memory mapped segment (see {@link MappedMemorySegment#force()}).
* @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);
static MappedMemorySegment mapFromPath(Path path, long bytesSize, FileChannel.MapMode mapMode) throws IOException {
return MappedMemorySegmentImpl.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).
* alignment constraint (in bytes). The segment will feature all <a href="#access-modes">access modes</a>.
*
* @implNote The block of off-heap memory associated with the returned native memory segment is initialized to zero.
* Moreover, a client is responsible to call the {@link MemorySegment#close()} on a native memory segment,
@ -422,6 +514,80 @@ public interface MemorySegment extends AutoCloseable {
throw new IllegalArgumentException("Invalid alignment constraint : " + alignmentBytes);
}
return Utils.makeNativeSegment(bytesSize, alignmentBytes);
return NativeMemorySegmentImpl.makeNativeSegment(bytesSize, alignmentBytes);
}
/**
* Returns a new native memory segment with given base address and size; the returned segment has its own temporal
* bounds, and can therefore be closed; closing such a segment can optionally result in calling an user-provided cleanup
* action. This method can be very useful when interacting with custom native memory sources (e.g. custom allocators,
* GPU memory, etc.), where an address to some underlying memory region is typically obtained from native code
* (often as a plain {@code long} value). The segment will feature all <a href="#access-modes">access modes</a>.
* <p>
* This method is <em>restricted</em>. Restricted method are unsafe, and, if used incorrectly, their use might crash
* the JVM crash or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
* restricted methods, and use safe and supported functionalities, where possible.
*
* @param addr the desired base address
* @param bytesSize the desired size.
* @param owner the desired owner thread. If {@code owner == null}, the returned segment is <em>not</em> confined.
* @param cleanup a cleanup action to be executed when the {@link MemorySegment#close()} method is called on the
* returned segment. If {@code cleanup == null}, no cleanup action is executed.
* @param attachment an object that must be kept alive by the returned segment; this can be useful when
* the returned segment depends on memory which could be released if a certain object
* is determined to be unreacheable. In most cases this will be set to {@code null}.
* @return a new native memory segment with given base address, size, owner, cleanup action and object attachment.
* @throws IllegalArgumentException if {@code bytesSize <= 0}.
* @throws UnsupportedOperationException if {@code addr} is associated with an heap segment.
* @throws IllegalAccessError if the runtime property {@code foreign.restricted} is not set to either
* {@code permit}, {@code warn} or {@code debug} (the default value is set to {@code deny}).
* @throws NullPointerException if {@code addr == null}.
*/
static MemorySegment ofNativeRestricted(MemoryAddress addr, long bytesSize, Thread owner, Runnable cleanup, Object attachment) {
Objects.requireNonNull(addr);
if (bytesSize <= 0) {
throw new IllegalArgumentException("Invalid size : " + bytesSize);
}
Utils.checkRestrictedAccess("MemorySegment.ofNativeRestricted");
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, bytesSize, owner, cleanup, attachment);
}
// access mode masks
/**
* Read access mode; read operations are supported by a segment which supports this access mode.
* @see MemorySegment#accessModes()
* @see MemorySegment#withAccessModes(int)
*/
int READ = 1;
/**
* Write access mode; write operations are supported by a segment which supports this access mode.
* @see MemorySegment#accessModes()
* @see MemorySegment#withAccessModes(int)
*/
int WRITE = READ << 1;
/**
* Close access mode; calling {@link #close()} is supported by a segment which supports this access mode.
* @see MemorySegment#accessModes()
* @see MemorySegment#withAccessModes(int)
*/
int CLOSE = WRITE << 1;
/**
* Acquire access mode; this segment support sharing with threads other than the owner thread, via spliterator
* (see {@link #spliterator(MemorySegment, SequenceLayout)}).
* @see MemorySegment#accessModes()
* @see MemorySegment#withAccessModes(int)
*/
int ACQUIRE = CLOSE << 1;
/**
* Handoff access mode; this segment support serial thread-confinement via thread ownership changes
* (see {@link #withOwnerThread(Thread)}).
* @see MemorySegment#accessModes()
* @see MemorySegment#withAccessModes(int)
*/
int HANDOFF = ACQUIRE << 1;
}

View File

@ -25,8 +25,10 @@
*/
package jdk.incubator.foreign;
import java.lang.constant.Constable;
import java.lang.constant.ConstantDescs;
import java.lang.constant.DynamicConstantDesc;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
@ -47,11 +49,11 @@ import java.util.OptionalLong;
/* package-private */ final class PaddingLayout extends AbstractLayout implements MemoryLayout {
PaddingLayout(long size) {
this(size, size, Optional.empty());
this(size, 1, Map.of());
}
PaddingLayout(long size, long alignment, Optional<String> name) {
super(OptionalLong.of(size), alignment, name);
PaddingLayout(long size, long alignment, Map<String, Constable> attributes) {
super(OptionalLong.of(size), alignment, attributes);
}
@Override
@ -80,14 +82,19 @@ import java.util.OptionalLong;
}
@Override
PaddingLayout dup(long alignment, Optional<String> name) {
return new PaddingLayout(bitSize(), alignment, name);
PaddingLayout dup(long alignment, Map<String, Constable> attributes) {
return new PaddingLayout(bitSize(), alignment, attributes);
}
@Override
public boolean hasNaturalAlignment() {
return true;
}
@Override
public Optional<DynamicConstantDesc<MemoryLayout>> describeConstable() {
return Optional.of(DynamicConstantDesc.ofNamed(ConstantDescs.BSM_INVOKE, "padding",
CD_LAYOUT, MH_PADDING, bitSize()));
return Optional.of(decorateLayoutConstant(DynamicConstantDesc.ofNamed(ConstantDescs.BSM_INVOKE, "padding",
CD_MEMORY_LAYOUT, MH_PADDING, bitSize())));
}
//hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
@ -108,4 +115,12 @@ import java.util.OptionalLong;
public PaddingLayout withBitAlignment(long alignmentBits) {
return (PaddingLayout)super.withBitAlignment(alignmentBits);
}
/**
* {@inheritDoc}
*/
@Override
public PaddingLayout withAttribute(String name, Constable value) {
return (PaddingLayout)super.withAttribute(name, value);
}
}

View File

@ -25,11 +25,14 @@
*/
package jdk.incubator.foreign;
import java.lang.constant.Constable;
import java.lang.constant.ConstantDescs;
import java.lang.constant.DynamicConstantDesc;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.stream.LongStream;
/**
* 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>.
@ -66,13 +69,13 @@ public final class SequenceLayout extends AbstractLayout {
private final MemoryLayout elementLayout;
SequenceLayout(OptionalLong elemCount, MemoryLayout elementLayout) {
this(elemCount, elementLayout, elementLayout.bitAlignment(), Optional.empty());
this(elemCount, elementLayout, elementLayout.bitAlignment(), Map.of());
}
SequenceLayout(OptionalLong elemCount, MemoryLayout elementLayout, long alignment, Optional<String> name) {
SequenceLayout(OptionalLong elemCount, MemoryLayout elementLayout, long alignment, Map<String, Constable> attributes) {
super(elemCount.isPresent() && AbstractLayout.optSize(elementLayout).isPresent() ?
OptionalLong.of(elemCount.getAsLong() * elementLayout.bitSize()) :
OptionalLong.empty(), alignment, name);
OptionalLong.empty(), alignment, attributes);
this.elemCount = elemCount;
this.elementLayout = elementLayout;
}
@ -104,7 +107,122 @@ public final class SequenceLayout extends AbstractLayout {
*/
public SequenceLayout withElementCount(long elementCount) {
AbstractLayout.checkSize(elementCount, true);
return new SequenceLayout(OptionalLong.of(elementCount), elementLayout, alignment, name());
return new SequenceLayout(OptionalLong.of(elementCount), elementLayout, alignment, attributes);
}
/**
* Returns a new sequence layout where element layouts in the flattened projection of this
* sequence layout (see {@link #flatten()}) are re-arranged into one or more nested sequence layouts
* according to the provided element counts. This transformation preserves the layout size;
* that is, multiplying the provided element counts must yield the same element count
* as the flattened projection of this sequence layout.
* <p>
* For instance, given a sequence layout of the kind:
* <pre>{@code
var seq = MemoryLayout.ofSequence(4, MemoryLayout.ofSequence(3, MemoryLayouts.JAVA_INT));
* }</pre>
* calling {@code seq.reshape(2, 6)} will yield the following sequence layout:
* <pre>{@code
var reshapeSeq = MemoryLayout.ofSequence(2, MemoryLayout.ofSequence(6, MemoryLayouts.JAVA_INT));
* }</pre>
* <p>
* If one of the provided element count is the special value {@code -1}, then the element
* count in that position will be inferred from the remaining element counts and the
* element count of the flattened projection of this layout. For instance, a layout equivalent to
* the above {@code reshapeSeq} can also be computed in the following ways:
* <pre>{@code
var reshapeSeqImplicit1 = seq.reshape(-1, 6);
var reshapeSeqImplicit2 = seq.reshape(2, -1);
* }</pre>
* @param elementCounts an array of element counts, of which at most one can be {@code -1}.
* @return a new sequence layout where element layouts in the flattened projection of this
* sequence layout (see {@link #flatten()}) are re-arranged into one or more nested sequence layouts.
* @throws NullPointerException if {@code elementCounts == null}.
* @throws UnsupportedOperationException if this sequence layout does not have an element count.
* @throws IllegalArgumentException if two or more element counts are set to {@code -1}, or if one
* or more element count is {@code <= 0} (but other than {@code -1}) or, if, after any required inference,
* multiplying the element counts does not yield the same element count as the flattened projection of this
* sequence layout.
*/
public SequenceLayout reshape(long... elementCounts) {
Objects.requireNonNull(elementCounts);
if (elementCounts.length == 0) {
throw new IllegalArgumentException();
}
if (!elementCount().isPresent()) {
throw new UnsupportedOperationException("Cannot reshape a sequence layout whose element count is unspecified");
}
SequenceLayout flat = flatten();
long expectedCount = flat.elementCount().getAsLong();
long actualCount = 1;
int inferPosition = -1;
for (int i = 0 ; i < elementCounts.length ; i++) {
if (elementCounts[i] == -1) {
if (inferPosition == -1) {
inferPosition = i;
} else {
throw new IllegalArgumentException("Too many unspecified element counts");
}
} else if (elementCounts[i] <= 0) {
throw new IllegalArgumentException("Invalid element count: " + elementCounts[i]);
} else {
actualCount = elementCounts[i] * actualCount;
}
}
// infer an unspecified element count (if any)
if (inferPosition != -1) {
long inferredCount = expectedCount / actualCount;
elementCounts[inferPosition] = inferredCount;
actualCount = actualCount * inferredCount;
}
if (actualCount != expectedCount) {
throw new IllegalArgumentException("Element counts do not match expected size: " + expectedCount);
}
MemoryLayout res = flat.elementLayout();
for (int i = elementCounts.length - 1 ; i >= 0 ; i--) {
res = MemoryLayout.ofSequence(elementCounts[i], res);
}
return (SequenceLayout)res;
}
/**
* Returns a new, flattened sequence layout whose element layout is the first non-sequence
* element layout found by recursively traversing the element layouts of this sequence layout.
* This transformation preserves the layout size; nested sequence layout in this sequence layout will
* be dropped and their element counts will be incorporated into that of the returned sequence layout.
* For instance, given a sequence layout of the kind:
* <pre>{@code
var seq = MemoryLayout.ofSequence(4, MemoryLayout.ofSequence(3, MemoryLayouts.JAVA_INT));
* }</pre>
* calling {@code seq.flatten()} will yield the following sequence layout:
* <pre>{@code
var flattenedSeq = MemoryLayout.ofSequence(12, MemoryLayouts.JAVA_INT);
* }</pre>
* @return a new sequence layout with the same size as this layout (but, possibly, with different
* element count), whose element layout is not a sequence layout.
* @throws UnsupportedOperationException if this sequence layout, or one of the nested sequence layouts being
* flattened, does not have an element count.
*/
public SequenceLayout flatten() {
if (!elementCount().isPresent()) {
throw badUnboundSequenceLayout();
}
long count = elementCount().getAsLong();
MemoryLayout elemLayout = elementLayout();
while (elemLayout instanceof SequenceLayout) {
SequenceLayout elemSeq = (SequenceLayout)elemLayout;
count = count * elemSeq.elementCount().orElseThrow(this::badUnboundSequenceLayout);
elemLayout = elemSeq.elementLayout();
}
return MemoryLayout.ofSequence(count, elemLayout);
}
private UnsupportedOperationException badUnboundSequenceLayout() {
return new UnsupportedOperationException("Cannot flatten a sequence layout whose element count is unspecified");
}
@Override
@ -134,8 +252,8 @@ public final class SequenceLayout extends AbstractLayout {
}
@Override
SequenceLayout dup(long alignment, Optional<String> name) {
return new SequenceLayout(elementCount(), elementLayout, alignment, name);
SequenceLayout dup(long alignment, Map<String, Constable> attributes) {
return new SequenceLayout(elementCount(), elementLayout, alignment, attributes);
}
@Override
@ -145,11 +263,11 @@ public final class SequenceLayout extends AbstractLayout {
@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()));
return Optional.of(decorateLayoutConstant(elemCount.isPresent() ?
DynamicConstantDesc.ofNamed(ConstantDescs.BSM_INVOKE, "value",
CD_SEQUENCE_LAYOUT, MH_SIZED_SEQUENCE, elemCount.getAsLong(), elementLayout.describeConstable().get()) :
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
@ -170,4 +288,12 @@ public final class SequenceLayout extends AbstractLayout {
public SequenceLayout withBitAlignment(long alignmentBits) {
return (SequenceLayout)super.withBitAlignment(alignmentBits);
}
/**
* {@inheritDoc}
*/
@Override
public SequenceLayout withAttribute(String name, Constable value) {
return (SequenceLayout)super.withAttribute(name, value);
}
}

View File

@ -25,10 +25,11 @@
*/
package jdk.incubator.foreign;
import java.lang.constant.Constable;
import java.lang.constant.ConstantDescs;
import java.lang.constant.DynamicConstantDesc;
import java.lang.constant.MethodHandleDesc;
import java.nio.ByteOrder;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
@ -52,11 +53,11 @@ public final class ValueLayout extends AbstractLayout implements MemoryLayout {
private final ByteOrder order;
ValueLayout(ByteOrder order, long size) {
this(order, size, size, Optional.empty());
this(order, size, size, Map.of());
}
ValueLayout(ByteOrder order, long size, long alignment, Optional<String> name) {
super(OptionalLong.of(size), alignment, name);
ValueLayout(ByteOrder order, long size, long alignment, Map<String, Constable> attributes) {
super(OptionalLong.of(size), alignment, attributes);
this.order = order;
}
@ -76,7 +77,7 @@ public final class ValueLayout extends AbstractLayout implements MemoryLayout {
* @return a new value layout with given byte order.
*/
public ValueLayout withOrder(ByteOrder order) {
return new ValueLayout(order, bitSize(), alignment, optName());
return new ValueLayout(order, bitSize(), alignment, attributes);
}
@Override
@ -109,14 +110,14 @@ public final class ValueLayout extends AbstractLayout implements MemoryLayout {
}
@Override
ValueLayout dup(long alignment, Optional<String> name) {
return new ValueLayout(order, bitSize(), alignment, name);
ValueLayout dup(long alignment, Map<String, Constable> attributes) {
return new ValueLayout(order, bitSize(), alignment, attributes);
}
@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));
return Optional.of(decorateLayoutConstant(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
@ -137,4 +138,12 @@ public final class ValueLayout extends AbstractLayout implements MemoryLayout {
public ValueLayout withBitAlignment(long alignmentBits) {
return (ValueLayout)super.withBitAlignment(alignmentBits);
}
/**
* {@inheritDoc}
*/
@Override
public ValueLayout withAttribute(String name, Constable value) {
return (ValueLayout)super.withAttribute(name, value);
}
}

View File

@ -42,15 +42,16 @@ try (MemorySegment segment = MemorySegment.allocateNative(10 * 4)) {
* 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
* associated with the segment will be released at the end of the block, according to the semantics described in
* Section {@jls 14.20.3} of <cite>The Java&trade; Language Specification</cite>. 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
* The first models a contiguous memory region, which can reside either inside or outside the Java heap; the latter models an address - which can
* sometimes be expressed as an offset into 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
@ -74,7 +75,9 @@ try (MemorySegment segment = MemorySegment.allocateNative(10 * 4)) {
*
* 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>.
* which resides <em>outside</em> the boundaries of the memory segment it refers to. We call this guarantee <em>spatial safety</em>;
* in other words, access to memory segments is bounds-checked, in the same way as array access is, as described in
* Section {@jls 15.10.4} of <cite>The Java&trade; Language Specification</cite>.
* <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,
@ -82,7 +85,6 @@ try (MemorySegment segment = MemorySegment.allocateNative(10 * 4)) {
* 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.

View File

@ -1,68 +0,0 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. 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,461 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.foreign;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.SequenceLayout;
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.access.foreign.UnmapperProxy;
import jdk.internal.vm.annotation.ForceInline;
import sun.security.action.GetPropertyAction;
import java.lang.invoke.VarHandle;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.Spliterator;
import java.util.function.Consumer;
/**
* This abstract class provides an immutable implementation for the {@code MemorySegment} interface. This class contains information
* about the segment's spatial and temporal bounds; each memory segment implementation is associated with an owner thread which is set at creation time.
* 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. Subclasses
* are defined for each memory segment kind, see {@link NativeMemorySegmentImpl}, {@link HeapMemorySegmentImpl} and
* {@link MappedMemorySegmentImpl}.
*/
public abstract class AbstractMemorySegmentImpl implements MemorySegment, MemorySegmentProxy {
private static final boolean enableSmallSegments =
Boolean.parseBoolean(GetPropertyAction.privilegedGetProperty("jdk.incubator.foreign.SmallSegments", "true"));
final static int ACCESS_MASK = READ | WRITE | CLOSE | ACQUIRE | HANDOFF;
final static int FIRST_RESERVED_FLAG = 1 << 16; // upper 16 bits are reserved
final static int SMALL = FIRST_RESERVED_FLAG;
final static long NONCE = new Random().nextLong();
final static int DEFAULT_MASK = READ | WRITE | CLOSE | ACQUIRE | HANDOFF;
final static JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
final long length;
final int mask;
final Thread owner;
final MemoryScope scope;
@ForceInline
AbstractMemorySegmentImpl(long length, int mask, Thread owner, MemoryScope scope) {
this.length = length;
this.mask = mask;
this.owner = owner;
this.scope = scope;
}
abstract long min();
abstract Object base();
abstract AbstractMemorySegmentImpl dup(long offset, long size, int mask, Thread owner, MemoryScope scope);
abstract ByteBuffer makeByteBuffer();
static int defaultAccessModes(long size) {
return (enableSmallSegments && size < Integer.MAX_VALUE) ?
DEFAULT_MASK | SMALL :
DEFAULT_MASK;
}
@Override
public AbstractMemorySegmentImpl asSlice(long offset, long newSize) {
checkBounds(offset, newSize);
return asSliceNoCheck(offset, newSize);
}
private AbstractMemorySegmentImpl asSliceNoCheck(long offset, long newSize) {
return dup(offset, newSize, mask, owner, scope);
}
@SuppressWarnings("unchecked")
public static <S extends MemorySegment> Spliterator<S> spliterator(S segment, SequenceLayout sequenceLayout) {
((AbstractMemorySegmentImpl)segment).checkValidState();
if (sequenceLayout.byteSize() != segment.byteSize()) {
throw new IllegalArgumentException();
}
return (Spliterator<S>)new SegmentSplitter(sequenceLayout.elementLayout().byteSize(), sequenceLayout.elementCount().getAsLong(),
(AbstractMemorySegmentImpl)segment.withAccessModes(segment.accessModes() & ~CLOSE));
}
@Override
@ForceInline
public final MemoryAddress baseAddress() {
return new MemoryAddressImpl(this, 0);
}
@Override
public final ByteBuffer asByteBuffer() {
if (!isSet(READ)) {
throw unsupportedAccessMode(READ);
}
checkIntSize("ByteBuffer");
ByteBuffer _bb = makeByteBuffer();
if (!isSet(WRITE)) {
//scope is IMMUTABLE - obtain a RO byte buffer
_bb = _bb.asReadOnlyBuffer();
}
return _bb;
}
@Override
public final int accessModes() {
return mask & ACCESS_MASK;
}
@Override
public final long byteSize() {
return length;
}
@Override
public final boolean isAlive() {
return scope.isAliveThreadSafe();
}
@Override
public Thread ownerThread() {
return owner;
}
@Override
public AbstractMemorySegmentImpl withAccessModes(int accessModes) {
checkAccessModes(accessModes);
if ((~accessModes() & accessModes) != 0) {
throw new IllegalArgumentException("Cannot acquire more access modes");
}
return dup(0, length, (mask & ~ACCESS_MASK) | accessModes, owner, scope);
}
@Override
public boolean hasAccessModes(int accessModes) {
checkAccessModes(accessModes);
return (accessModes() & accessModes) == accessModes;
}
private void checkAccessModes(int accessModes) {
if ((accessModes & ~ACCESS_MASK) != 0) {
throw new IllegalArgumentException("Invalid access modes");
}
}
@Override
public MemorySegment withOwnerThread(Thread newOwner) {
Objects.requireNonNull(newOwner);
checkValidState();
if (!isSet(HANDOFF)) {
throw unsupportedAccessMode(HANDOFF);
}
if (owner == newOwner) {
throw new IllegalArgumentException("Segment already owned by thread: " + newOwner);
} else {
try {
return dup(0L, length, mask, newOwner, scope.dup());
} finally {
//flush read/writes to memory before returning the new segment
VarHandle.fullFence();
}
}
}
@Override
public final void close() {
if (!isSet(CLOSE)) {
throw unsupportedAccessMode(CLOSE);
}
checkValidState();
closeNoCheck();
}
private final void closeNoCheck() {
scope.close(true);
}
final AbstractMemorySegmentImpl acquire() {
if (Thread.currentThread() != ownerThread() && !isSet(ACQUIRE)) {
throw unsupportedAccessMode(ACQUIRE);
}
return dup(0, length, mask, Thread.currentThread(), scope.acquire());
}
@Override
public final byte[] toByteArray() {
checkIntSize("byte[]");
byte[] arr = new byte[(int)length];
MemorySegment arrSegment = MemorySegment.ofArray(arr);
MemoryAddress.copy(baseAddress(), arrSegment.baseAddress(), length);
return arr;
}
boolean isSmall() {
return isSet(SMALL);
}
void checkRange(long offset, long length, boolean writeAccess) {
checkValidState();
if (writeAccess && !isSet(WRITE)) {
throw unsupportedAccessMode(WRITE);
} else if (!writeAccess && !isSet(READ)) {
throw unsupportedAccessMode(READ);
}
checkBounds(offset, length);
}
@Override
public final void checkValidState() {
if (owner != null && owner != Thread.currentThread()) {
throw new IllegalStateException("Attempt to access segment outside owning thread");
}
scope.checkAliveConfined();
}
// Helper methods
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 (isSmall()) {
checkBoundsSmall((int)offset, (int)length);
} else {
if (length < 0 ||
offset < 0 ||
offset > this.length - length) { // careful of overflow
throw outOfBoundException(offset, length);
}
}
}
private void checkBoundsSmall(int offset, int length) {
if (length < 0 ||
offset < 0 ||
offset > (int)this.length - length) { // careful of overflow
throw outOfBoundException(offset, length);
}
}
UnsupportedOperationException unsupportedAccessMode(int expected) {
return new UnsupportedOperationException((String.format("Required access mode %s ; current access modes: %s",
modeStrings(expected).get(0), modeStrings(mask))));
}
private List<String> modeStrings(int mode) {
List<String> modes = new ArrayList<>();
if ((mode & READ) != 0) {
modes.add("READ");
}
if ((mode & WRITE) != 0) {
modes.add("WRITE");
}
if ((mode & CLOSE) != 0) {
modes.add("CLOSE");
}
if ((mode & ACQUIRE) != 0) {
modes.add("ACQUIRE");
}
if ((mode & HANDOFF) != 0) {
modes.add("HANDOFF");
}
return modes;
}
private IndexOutOfBoundsException outOfBoundException(long offset, long length) {
return new IndexOutOfBoundsException(String.format("Out of bound access on segment %s; new offset = %d; new length = %d",
this, offset, length));
}
protected int id() {
//compute a stable and random id for this memory segment
return Math.abs(Objects.hash(base(), min(), NONCE));
}
static class SegmentSplitter implements Spliterator<MemorySegment> {
AbstractMemorySegmentImpl segment;
long elemCount;
final long elementSize;
long currentIndex;
SegmentSplitter(long elementSize, long elemCount, AbstractMemorySegmentImpl segment) {
this.segment = segment;
this.elementSize = elementSize;
this.elemCount = elemCount;
}
@Override
public SegmentSplitter trySplit() {
if (currentIndex == 0 && elemCount > 1) {
AbstractMemorySegmentImpl parent = segment;
long rem = elemCount % 2;
long split = elemCount / 2;
long lobound = split * elementSize;
long hibound = lobound + (rem * elementSize);
elemCount = split + rem;
segment = parent.asSliceNoCheck(lobound, hibound);
return new SegmentSplitter(elementSize, split, parent.asSliceNoCheck(0, lobound));
} else {
return null;
}
}
@Override
public boolean tryAdvance(Consumer<? super MemorySegment> action) {
Objects.requireNonNull(action);
if (currentIndex < elemCount) {
AbstractMemorySegmentImpl acquired = segment.acquire();
try {
action.accept(acquired.asSliceNoCheck(currentIndex * elementSize, elementSize));
} finally {
acquired.closeNoCheck();
currentIndex++;
if (currentIndex == elemCount) {
segment = null;
}
}
return true;
} else {
return false;
}
}
@Override
public void forEachRemaining(Consumer<? super MemorySegment> action) {
Objects.requireNonNull(action);
if (currentIndex < elemCount) {
AbstractMemorySegmentImpl acquired = segment.acquire();
try {
if (acquired.isSmall()) {
int index = (int) currentIndex;
int limit = (int) elemCount;
int elemSize = (int) elementSize;
for (; index < limit; index++) {
action.accept(acquired.asSliceNoCheck(index * elemSize, elemSize));
}
} else {
for (long i = currentIndex ; i < elemCount ; i++) {
action.accept(acquired.asSliceNoCheck(i * elementSize, elementSize));
}
}
} finally {
acquired.closeNoCheck();
currentIndex = elemCount;
segment = null;
}
}
}
@Override
public long estimateSize() {
return elemCount;
}
@Override
public int characteristics() {
return NONNULL | SUBSIZED | SIZED | IMMUTABLE | ORDERED;
}
}
// Object methods
@Override
public String toString() {
return "MemorySegment{ id=0x" + Long.toHexString(id()) + " limit: " + length + " }";
}
public static AbstractMemorySegmentImpl ofBuffer(ByteBuffer bb) {
long bbAddress = nioAccess.getBufferAddress(bb);
Object base = nioAccess.getBufferBase(bb);
UnmapperProxy unmapper = nioAccess.unmapper(bb);
int pos = bb.position();
int limit = bb.limit();
int size = limit - pos;
AbstractMemorySegmentImpl bufferSegment = (AbstractMemorySegmentImpl)nioAccess.bufferSegment(bb);
final MemoryScope bufferScope;
int modes;
final Thread owner;
if (bufferSegment != null) {
bufferScope = bufferSegment.scope;
modes = bufferSegment.mask;
owner = bufferSegment.owner;
} else {
bufferScope = new MemoryScope(bb, null);
modes = defaultAccessModes(size);
owner = Thread.currentThread();
}
if (bb.isReadOnly()) {
modes &= ~WRITE;
}
if (base != null) {
return new HeapMemorySegmentImpl<>(bbAddress + pos, () -> (byte[])base, size, modes, owner, bufferScope);
} else if (unmapper == null) {
return new NativeMemorySegmentImpl(bbAddress + pos, size, modes, owner, bufferScope);
} else {
return new MappedMemorySegmentImpl(bbAddress + pos, unmapper, size, modes, owner, bufferScope);
}
}
public static AbstractMemorySegmentImpl NOTHING = new AbstractMemorySegmentImpl(0, 0, null, MemoryScope.GLOBAL) {
@Override
ByteBuffer makeByteBuffer() {
throw new UnsupportedOperationException();
}
@Override
long min() {
return 0;
}
@Override
Object base() {
return null;
}
@Override
AbstractMemorySegmentImpl dup(long offset, long size, int mask, Thread owner, MemoryScope scope) {
throw new UnsupportedOperationException();
}
};
}

View File

@ -0,0 +1,127 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package jdk.internal.foreign;
import jdk.incubator.foreign.MemorySegment;
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Unsafe;
import jdk.internal.vm.annotation.ForceInline;
import java.nio.ByteBuffer;
import java.util.Objects;
import java.util.function.Supplier;
/**
* Implementation for heap memory segments. An heap memory segment is composed by an offset and
* a base object (typically an array). To enhance performances, the access to the base object needs to feature
* sharp type information, as well as sharp null-check information. For this reason, the factories for heap segments
* use a lambda to implement the base object accessor, so that the type information will remain sharp (e.g.
* the static compiler will generate specialized base accessor for us).
*/
public class HeapMemorySegmentImpl<H> extends AbstractMemorySegmentImpl {
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
private static final int BYTE_ARR_BASE = UNSAFE.arrayBaseOffset(byte[].class);
final long offset;
final Supplier<H> baseProvider;
@ForceInline
HeapMemorySegmentImpl(long offset, Supplier<H> baseProvider, long length, int mask, Thread owner, MemoryScope scope) {
super(length, mask, owner, scope);
this.offset = offset;
this.baseProvider = baseProvider;
}
@Override
H base() {
return Objects.requireNonNull(baseProvider.get());
}
@Override
long min() {
return offset;
}
@Override
HeapMemorySegmentImpl<H> dup(long offset, long size, int mask, Thread owner, MemoryScope scope) {
return new HeapMemorySegmentImpl<H>(this.offset + offset, baseProvider, size, mask, owner, scope);
}
@Override
ByteBuffer makeByteBuffer() {
if (!(base() instanceof byte[])) {
throw new UnsupportedOperationException("Not an address to an heap-allocated byte array");
}
JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
return nioAccess.newHeapByteBuffer((byte[]) base(), (int)min() - BYTE_ARR_BASE, (int) byteSize(), this);
}
// factories
public static MemorySegment makeArraySegment(byte[] arr) {
return makeHeapSegment(() -> arr, arr.length,
Unsafe.ARRAY_BYTE_BASE_OFFSET, Unsafe.ARRAY_BYTE_INDEX_SCALE);
}
public static MemorySegment makeArraySegment(char[] arr) {
return makeHeapSegment(() -> arr, arr.length,
Unsafe.ARRAY_CHAR_BASE_OFFSET, Unsafe.ARRAY_CHAR_INDEX_SCALE);
}
public static MemorySegment makeArraySegment(short[] arr) {
return makeHeapSegment(() -> arr, arr.length,
Unsafe.ARRAY_SHORT_BASE_OFFSET, Unsafe.ARRAY_SHORT_INDEX_SCALE);
}
public static MemorySegment makeArraySegment(int[] arr) {
return makeHeapSegment(() -> arr, arr.length,
Unsafe.ARRAY_INT_BASE_OFFSET, Unsafe.ARRAY_INT_INDEX_SCALE);
}
public static MemorySegment makeArraySegment(long[] arr) {
return makeHeapSegment(() -> arr, arr.length,
Unsafe.ARRAY_LONG_BASE_OFFSET, Unsafe.ARRAY_LONG_INDEX_SCALE);
}
public static MemorySegment makeArraySegment(float[] arr) {
return makeHeapSegment(() -> arr, arr.length,
Unsafe.ARRAY_FLOAT_BASE_OFFSET, Unsafe.ARRAY_FLOAT_INDEX_SCALE);
}
public static MemorySegment makeArraySegment(double[] arr) {
return makeHeapSegment(() -> arr, arr.length,
Unsafe.ARRAY_DOUBLE_BASE_OFFSET, Unsafe.ARRAY_DOUBLE_INDEX_SCALE);
}
static <Z> HeapMemorySegmentImpl<Z> makeHeapSegment(Supplier<Z> obj, int length, int base, int scale) {
int byteSize = length * scale;
MemoryScope scope = new MemoryScope(null, null);
return new HeapMemorySegmentImpl<>(base, obj, byteSize, defaultAccessModes(byteSize), Thread.currentThread(), scope);
}
}

View File

@ -33,6 +33,7 @@ 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.ArrayList;
import java.util.List;
@ -41,7 +42,7 @@ 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)},
* This class provide support for constructing layout paths; that is, starting from a root path (see {@link #rootPath(MemoryLayout, ToLongFunction)},
* 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
@ -50,7 +51,7 @@ import java.util.stream.LongStream;
*/
public class LayoutPath {
private static JavaLangInvokeAccess JLI = SharedSecrets.getJavaLangInvokeAccess();
private static final JavaLangInvokeAccess JLI = SharedSecrets.getJavaLangInvokeAccess();
private final MemoryLayout layout;
private final long offset;
@ -140,12 +141,12 @@ public class LayoutPath {
checkAlignment(this);
return JLI.memoryAddressViewVarHandle(
return Utils.fixUpVarHandle(JLI.memoryAccessVarHandle(
carrier,
layout.byteAlignment() - 1, //mask
((ValueLayout) layout).order(),
Utils.bitsToBytesOrThrow(offset, IllegalStateException::new),
LongStream.of(strides).map(s -> Utils.bitsToBytesOrThrow(s, IllegalStateException::new)).toArray());
LongStream.of(strides).map(s -> Utils.bitsToBytesOrThrow(s, IllegalStateException::new)).toArray()));
}
public MemoryLayout layout() {
@ -241,7 +242,7 @@ public class LayoutPath {
return newStrides;
}
private static long[] EMPTY_STRIDES = new long[0];
private static final long[] EMPTY_STRIDES = new long[0];
/**
* This class provides an immutable implementation for the {@code PathElement} interface. A path element implementation

View File

@ -0,0 +1,125 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.foreign;
import jdk.incubator.foreign.MappedMemorySegment;
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.foreign.UnmapperProxy;
import sun.nio.ch.FileChannelImpl;
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;
/**
* Implementation for a mapped memory segments. A mapped memory segment is a native memory segment, which
* additionally features an {@link UnmapperProxy} object. This object provides detailed information about the
* memory mapped segment, such as the file descriptor associated with the mapping. This information is crucial
* in order to correctly reconstruct a byte buffer object from the segment (see {@link #makeByteBuffer()}).
*/
public class MappedMemorySegmentImpl extends NativeMemorySegmentImpl implements MappedMemorySegment {
private final UnmapperProxy unmapper;
MappedMemorySegmentImpl(long min, UnmapperProxy unmapper, long length, int mask, Thread owner, MemoryScope scope) {
super(min, length, mask, owner, scope);
this.unmapper = unmapper;
}
@Override
ByteBuffer makeByteBuffer() {
JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
return nioAccess.newMappedByteBuffer(unmapper, min, (int)length, null, this);
}
@Override
MappedMemorySegmentImpl dup(long offset, long size, int mask, Thread owner, MemoryScope scope) {
return new MappedMemorySegmentImpl(min + offset, unmapper, size, mask, owner, scope);
}
// mapped segment methods
@Override
public MappedMemorySegmentImpl asSlice(long offset, long newSize) {
return (MappedMemorySegmentImpl)super.asSlice(offset, newSize);
}
@Override
public MappedMemorySegmentImpl withAccessModes(int accessModes) {
return (MappedMemorySegmentImpl)super.withAccessModes(accessModes);
}
@Override
public void load() {
nioAccess.load(min, unmapper.isSync(), length);
}
@Override
public void unload() {
nioAccess.unload(min, unmapper.isSync(), length);
}
@Override
public boolean isLoaded() {
return nioAccess.isLoaded(min, unmapper.isSync(), length);
}
@Override
public void force() {
nioAccess.force(unmapper.fileDescriptor(), min, unmapper.isSync(), 0, length);
}
// factories
public static MappedMemorySegment 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);
int modes = defaultAccessModes(bytesSize);
if (mapMode == FileChannel.MapMode.READ_ONLY) {
modes &= ~WRITE;
}
return new MappedMemorySegmentImpl(unmapperProxy.address(), unmapperProxy, bytesSize,
modes, 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

@ -31,7 +31,6 @@ import jdk.internal.misc.Unsafe;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import java.lang.ref.Reference;
import java.util.Objects;
/**
@ -42,10 +41,15 @@ public final class MemoryAddressImpl implements MemoryAddress, MemoryAddressProx
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
private final MemorySegmentImpl segment;
private final AbstractMemorySegmentImpl segment;
private final long offset;
public MemoryAddressImpl(MemorySegmentImpl segment, long offset) {
public MemoryAddressImpl(long offset) {
this.segment = AbstractMemorySegmentImpl.NOTHING;
this.offset = offset;
}
public MemoryAddressImpl(AbstractMemorySegmentImpl segment, long offset) {
this.segment = Objects.requireNonNull(segment);
this.offset = offset;
}
@ -64,13 +68,25 @@ public final class MemoryAddressImpl implements MemoryAddress, MemoryAddressProx
// MemoryAddress methods
@Override
public long offset() {
public long segmentOffset() {
if (segment() == null) {
throw new UnsupportedOperationException("Address does not have a segment");
}
return offset;
}
@Override
public long toRawLongValue() {
if (unsafeGetBase() != null) {
throw new UnsupportedOperationException("Not a native address");
}
return unsafeGetOffset();
}
@Override
public MemorySegment segment() {
return segment;
return segment != AbstractMemorySegmentImpl.NOTHING ?
segment : null;
}
@Override
@ -78,20 +94,34 @@ public final class MemoryAddressImpl implements MemoryAddress, MemoryAddressProx
return new MemoryAddressImpl(segment, offset + bytes);
}
@Override
public MemoryAddress rebase(MemorySegment segment) {
AbstractMemorySegmentImpl segmentImpl = (AbstractMemorySegmentImpl)segment;
if (segmentImpl.base() != this.segment.base()) {
throw new IllegalArgumentException("Invalid rebase target: " + segment);
}
return new MemoryAddressImpl((AbstractMemorySegmentImpl)segment,
unsafeGetOffset() - ((MemoryAddressImpl)segment.baseAddress()).unsafeGetOffset());
}
// MemoryAddressProxy methods
public void checkAccess(long offset, long length, boolean readOnly) {
segment.checkRange(this.offset + offset, length, !readOnly);
segment.checkRange(MemoryAddressProxy.addOffsets(this.offset, offset, this), length, !readOnly);
}
public long unsafeGetOffset() {
return segment.min + offset;
return segment.min() + offset;
}
public Object unsafeGetBase() {
return segment.base();
}
@Override
public boolean isSmall() {
return segment.isSmall();
}
// Object methods
@Override

View File

@ -31,7 +31,7 @@ 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
* when the scope is closed (this operation is triggered by {@link AbstractMemorySegmentImpl#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).
*/
@ -58,6 +58,8 @@ public final class MemoryScope {
final Runnable cleanupAction;
final static MemoryScope GLOBAL = new MemoryScope(null, null);
public MemoryScope(Object ref, Runnable cleanupAction) {
this.ref = ref;
this.cleanupAction = cleanupAction;
@ -105,15 +107,20 @@ public final class MemoryScope {
} while (!COUNT_HANDLE.compareAndSet(this, value, value - 1));
}
void close() {
void close(boolean doCleanup) {
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) {
if (doCleanup && cleanupAction != null) {
cleanupAction.run();
}
}
MemoryScope dup() {
close(false);
return new MemoryScope(ref, cleanupAction);
}
}

View File

@ -1,209 +0,0 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. 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 Thread ownerThread() {
return owner;
}
@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,110 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package jdk.internal.foreign;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Unsafe;
import jdk.internal.vm.annotation.ForceInline;
import sun.security.action.GetBooleanAction;
import java.nio.ByteBuffer;
/**
* Implementation for native memory segments. A native memory segment is essentially a wrapper around
* a native long address.
*/
public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl {
private static final Unsafe unsafe = Unsafe.getUnsafe();
// The maximum alignment supported by malloc - typically 16 on
// 64-bit platforms and 8 on 32-bit platforms.
private final static long MAX_ALIGN = Unsafe.ADDRESS_SIZE == 4 ? 8 : 16;
private static final boolean skipZeroMemory = GetBooleanAction.privilegedGetProperty("jdk.internal.foreign.skipZeroMemory");
final long min;
@ForceInline
NativeMemorySegmentImpl(long min, long length, int mask, Thread owner, MemoryScope scope) {
super(length, mask, owner, scope);
this.min = min;
}
@Override
NativeMemorySegmentImpl dup(long offset, long size, int mask, Thread owner, MemoryScope scope) {
return new NativeMemorySegmentImpl(min + offset, size, mask, owner, scope);
}
@Override
ByteBuffer makeByteBuffer() {
JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
return nioAccess.newDirectByteBuffer(min(), (int) this.length, null, this);
}
@Override
long min() {
return min;
}
@Override
Object base() {
return null;
}
// 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 NativeMemorySegmentImpl(buf, alignedSize, defaultAccessModes(alignedSize),
Thread.currentThread(), scope);
if (alignedSize != bytesSize) {
long delta = alignedBuf - buf;
segment = segment.asSlice(delta, bytesSize);
}
return segment;
}
public static MemorySegment makeNativeSegmentUnchecked(MemoryAddress min, long bytesSize, Thread owner, Runnable cleanup, Object attachment) {
MemoryScope scope = new MemoryScope(attachment, cleanup);
return new NativeMemorySegmentImpl(min.toRawLongValue(), bytesSize, defaultAccessModes(bytesSize), owner, scope);
}
}

View File

@ -26,20 +26,16 @@
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 jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryHandles;
import jdk.internal.access.foreign.MemoryAddressProxy;
import jdk.internal.misc.VM;
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.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
import java.util.Optional;
import java.util.function.Supplier;
/**
@ -47,15 +43,19 @@ import java.util.function.Supplier;
*/
public final class Utils {
private static Unsafe unsafe = Unsafe.getUnsafe();
private static final String foreignRestrictedAccess = Optional.ofNullable(VM.getSavedProperty("foreign.restricted"))
.orElse("deny");
// The maximum alignment supported by malloc - typically 16 on
// 64-bit platforms and 8 on 32-bit platforms.
private final static long MAX_ALIGN = Unsafe.ADDRESS_SIZE == 4 ? 8 : 16;
private static final MethodHandle ADDRESS_FILTER;
private static final JavaNioAccess javaNioAccess = SharedSecrets.getJavaNioAccess();
private static final boolean skipZeroMemory = GetBooleanAction.privilegedGetProperty("jdk.internal.foreign.skipZeroMemory");
static {
try {
ADDRESS_FILTER = MethodHandles.lookup().findStatic(Utils.class, "filterAddress",
MethodType.methodType(MemoryAddressProxy.class, MemoryAddress.class));
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static long alignUp(long n, long alignment) {
return (n + alignment - 1) & -alignment;
@ -69,90 +69,34 @@ public final class Utils {
}
}
// 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 VarHandle fixUpVarHandle(VarHandle handle) {
// This adaptation is required, otherwise the memory access var handle will have type MemoryAddressProxy,
// and not MemoryAddress (which the user expects), which causes performance issues with asType() adaptations.
return MemoryHandles.filterCoordinates(handle, 0, ADDRESS_FILTER);
}
public static MemorySegment makeArraySegment(byte[] arr) {
return makeArraySegment(arr, arr.length, Unsafe.ARRAY_BYTE_BASE_OFFSET, Unsafe.ARRAY_BYTE_INDEX_SCALE);
private static MemoryAddressProxy filterAddress(MemoryAddress addr) {
return (MemoryAddressImpl)addr;
}
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);
public static void checkRestrictedAccess(String method) {
switch (foreignRestrictedAccess) {
case "deny" -> throwIllegalAccessError(foreignRestrictedAccess, method);
case "warn" -> System.err.println("WARNING: Accessing restricted foreign method: " + method);
case "debug" -> {
StringBuilder sb = new StringBuilder("DEBUG: restricted foreign method: \" + method");
StackWalker.getInstance().forEach(f -> sb.append(System.lineSeparator())
.append("\tat ")
.append(f));
System.err.println(sb.toString());
}
case "permit" -> {}
default -> throwIllegalAccessError(foreignRestrictedAccess, method);
}
}
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);
}
private static void throwIllegalAccessError(String value, String method) {
throw new IllegalAccessError("Illegal access to restricted foreign method: " + method +
" ; system property 'foreign.restricted' is set to '" + value + "'");
}
}

View File

@ -0,0 +1,464 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
/*
* @test
* @modules jdk.incubator.foreign
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false -Xverify:all TestAdaptVarHandles
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true -Xverify:all TestAdaptVarHandles
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false -Xverify:all TestAdaptVarHandles
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true -Xverify:all TestAdaptVarHandles
*/
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryHandles;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.ValueLayout;
import org.testng.annotations.*;
import static org.testng.Assert.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
import java.util.List;
public class TestAdaptVarHandles {
static MethodHandle S2I;
static MethodHandle I2S;
static MethodHandle S2L;
static MethodHandle S2L_EX;
static MethodHandle S2I_EX;
static MethodHandle I2S_EX;
static MethodHandle BASE_ADDR;
static MethodHandle SUM_OFFSETS;
static MethodHandle VOID_FILTER;
static {
try {
S2I = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "stringToInt", MethodType.methodType(int.class, String.class));
I2S = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "intToString", MethodType.methodType(String.class, int.class));
S2L = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "stringToLong", MethodType.methodType(long.class, String.class));
S2L_EX = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "stringToLongException", MethodType.methodType(long.class, String.class));
BASE_ADDR = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "baseAddress", MethodType.methodType(MemoryAddress.class, MemorySegment.class));
SUM_OFFSETS = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "sumOffsets", MethodType.methodType(long.class, long.class, long.class));
VOID_FILTER = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "void_filter", MethodType.methodType(void.class, String.class));
MethodHandle s2i_ex = MethodHandles.throwException(int.class, Throwable.class);
s2i_ex = MethodHandles.insertArguments(s2i_ex, 0, new Throwable());
S2I_EX = MethodHandles.dropArguments(s2i_ex, 0, String.class);
MethodHandle i2s_ex = MethodHandles.throwException(String.class, Throwable.class);
i2s_ex = MethodHandles.insertArguments(i2s_ex, 0, new Throwable());
I2S_EX = MethodHandles.dropArguments(i2s_ex, 0, int.class);
} catch (Throwable ex) {
throw new ExceptionInInitializerError();
}
}
@Test
public void testFilterValue() throws Throwable {
ValueLayout layout = MemoryLayouts.JAVA_INT;
MemorySegment segment = MemorySegment.allocateNative(layout);
VarHandle intHandle = layout.varHandle(int.class);
VarHandle i2SHandle = MemoryHandles.filterValue(intHandle, S2I, I2S);
i2SHandle.set(segment.baseAddress(), "1");
String oldValue = (String)i2SHandle.getAndAdd(segment.baseAddress(), "42");
assertEquals(oldValue, "1");
String value = (String)i2SHandle.get(segment.baseAddress());
assertEquals(value, "43");
boolean swapped = (boolean)i2SHandle.compareAndSet(segment.baseAddress(), "43", "12");
assertTrue(swapped);
oldValue = (String)i2SHandle.compareAndExchange(segment.baseAddress(), "12", "42");
assertEquals(oldValue, "12");
value = (String)i2SHandle.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment.baseAddress());
assertEquals(value, "42");
}
@Test(expectedExceptions = NullPointerException.class)
public void testBadFilterNullTarget() {
MemoryHandles.filterValue(null, S2I, I2S);
}
@Test(expectedExceptions = NullPointerException.class)
public void testBadFilterNullUnbox() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.filterValue(intHandle, null, I2S);
}
@Test(expectedExceptions = NullPointerException.class)
public void testBadFilterNullBox() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.filterValue(intHandle, S2I, null);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterCarrier() {
VarHandle floatHandle = MemoryLayouts.JAVA_FLOAT.varHandle(float.class);
MemoryHandles.filterValue(floatHandle, S2I, I2S);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterUnboxArity() {
VarHandle floatHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.filterValue(floatHandle, S2I.bindTo(""), I2S);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterBoxArity() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.filterValue(intHandle, S2I, I2S.bindTo(42));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterBoxException() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.filterValue(intHandle, I2S, S2L_EX);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterUnboxException() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.filterValue(intHandle, S2L_EX, I2S);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterBoxHandleException() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.filterValue(intHandle, S2I, I2S_EX);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterUnboxHandleException() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.filterValue(intHandle, S2I_EX, I2S);
}
@Test
public void testFilterCoordinates() throws Throwable {
ValueLayout layout = MemoryLayouts.JAVA_INT;
MemorySegment segment = MemorySegment.allocateNative(layout);
VarHandle intHandle = MemoryHandles.withStride(layout.varHandle(int.class), 4);
VarHandle intHandle_longIndex = MemoryHandles.filterCoordinates(intHandle, 0, BASE_ADDR, S2L);
intHandle_longIndex.set(segment, "0", 1);
int oldValue = (int)intHandle_longIndex.getAndAdd(segment, "0", 42);
assertEquals(oldValue, 1);
int value = (int)intHandle_longIndex.get(segment, "0");
assertEquals(value, 43);
boolean swapped = (boolean)intHandle_longIndex.compareAndSet(segment, "0", 43, 12);
assertTrue(swapped);
oldValue = (int)intHandle_longIndex.compareAndExchange(segment, "0", 12, 42);
assertEquals(oldValue, 12);
value = (int)intHandle_longIndex.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment, "0");
assertEquals(value, 42);
}
@Test(expectedExceptions = NullPointerException.class)
public void testBadFilterCoordinatesNullTarget() {
MemoryHandles.filterCoordinates(null, 0, S2I);
}
@Test(expectedExceptions = NullPointerException.class)
public void testBadFilterCoordinatesNullFilters() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.filterCoordinates(intHandle, 0, null);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterCoordinatesNegativePos() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.filterCoordinates(intHandle, -1, SUM_OFFSETS);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterCoordinatesPosTooBig() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.filterCoordinates(intHandle, 1, SUM_OFFSETS);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterCoordinatesWrongFilterType() {
VarHandle intHandle = MemoryHandles.withStride(MemoryLayouts.JAVA_INT.varHandle(int.class), 4);
MemoryHandles.filterCoordinates(intHandle, 1, S2I);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterCoordinatesWrongFilterException() {
VarHandle intHandle = MemoryHandles.withStride(MemoryLayouts.JAVA_INT.varHandle(int.class), 4);
MemoryHandles.filterCoordinates(intHandle, 1, S2L_EX);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterCoordinatesTooManyFilters() {
VarHandle intHandle = MemoryHandles.withStride(MemoryLayouts.JAVA_INT.varHandle(int.class), 4);
MemoryHandles.filterCoordinates(intHandle, 1, S2L, S2L);
}
@Test
public void testInsertCoordinates() throws Throwable {
ValueLayout layout = MemoryLayouts.JAVA_INT;
MemorySegment segment = MemorySegment.allocateNative(layout);
VarHandle intHandle = MemoryHandles.withStride(layout.varHandle(int.class), 4);
VarHandle intHandle_longIndex = MemoryHandles.insertCoordinates(intHandle, 0, segment.baseAddress(), 0L);
intHandle_longIndex.set(1);
int oldValue = (int)intHandle_longIndex.getAndAdd(42);
assertEquals(oldValue, 1);
int value = (int)intHandle_longIndex.get();
assertEquals(value, 43);
boolean swapped = (boolean)intHandle_longIndex.compareAndSet(43, 12);
assertTrue(swapped);
oldValue = (int)intHandle_longIndex.compareAndExchange(12, 42);
assertEquals(oldValue, 12);
value = (int)intHandle_longIndex.toMethodHandle(VarHandle.AccessMode.GET).invokeExact();
assertEquals(value, 42);
}
@Test(expectedExceptions = NullPointerException.class)
public void testBadInsertCoordinatesNullTarget() {
MemoryHandles.insertCoordinates(null, 0, 42);
}
@Test(expectedExceptions = NullPointerException.class)
public void testBadInsertCoordinatesNullValues() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.insertCoordinates(intHandle, 0, null);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadInsertCoordinatesNegativePos() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.insertCoordinates(intHandle, -1, 42);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadInsertCoordinatesPosTooBig() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.insertCoordinates(intHandle, 1, 42);
}
@Test(expectedExceptions = ClassCastException.class)
public void testBadInsertCoordinatesWrongCoordinateType() {
VarHandle intHandle = MemoryHandles.withStride(MemoryLayouts.JAVA_INT.varHandle(int.class), 4);
MemoryHandles.insertCoordinates(intHandle, 1, "Hello");
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadInsertCoordinatesTooManyValues() {
VarHandle intHandle = MemoryHandles.withStride(MemoryLayouts.JAVA_INT.varHandle(int.class), 4);
MemoryHandles.insertCoordinates(intHandle, 1, 0L, 0L);
}
@Test
public void testPermuteCoordinates() throws Throwable {
ValueLayout layout = MemoryLayouts.JAVA_INT;
MemorySegment segment = MemorySegment.allocateNative(layout);
VarHandle intHandle = MemoryHandles.withStride(layout.varHandle(int.class), 4);
VarHandle intHandle_swap = MemoryHandles.permuteCoordinates(intHandle,
List.of(long.class, MemoryAddress.class), 1, 0);
intHandle_swap.set(0L, segment.baseAddress(), 1);
int oldValue = (int)intHandle_swap.getAndAdd(0L, segment.baseAddress(), 42);
assertEquals(oldValue, 1);
int value = (int)intHandle_swap.get(0L, segment.baseAddress());
assertEquals(value, 43);
boolean swapped = (boolean)intHandle_swap.compareAndSet(0L, segment.baseAddress(), 43, 12);
assertTrue(swapped);
oldValue = (int)intHandle_swap.compareAndExchange(0L, segment.baseAddress(), 12, 42);
assertEquals(oldValue, 12);
value = (int)intHandle_swap.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(0L, segment.baseAddress());
assertEquals(value, 42);
}
@Test(expectedExceptions = NullPointerException.class)
public void testBadPermuteCoordinatesNullTarget() {
MemoryHandles.permuteCoordinates(null, List.of());
}
@Test(expectedExceptions = NullPointerException.class)
public void testBadPermuteCoordinatesNullCoordinates() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.permuteCoordinates(intHandle, null);
}
@Test(expectedExceptions = NullPointerException.class)
public void testBadPermuteCoordinatesNullReorder() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.permuteCoordinates(intHandle, List.of(int.class), null);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadPermuteCoordinatesTooManyCoordinates() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.permuteCoordinates(intHandle, List.of(int.class, int.class), new int[2]);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadPermuteCoordinatesTooFewCoordinates() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.permuteCoordinates(intHandle, List.of());
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadPermuteCoordinatesIndexTooBig() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.permuteCoordinates(intHandle, List.of(int.class, int.class), 3);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadPermuteCoordinatesIndexTooSmall() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.permuteCoordinates(intHandle, List.of(int.class, int.class), -1);
}
@Test
public void testCollectCoordinates() throws Throwable {
ValueLayout layout = MemoryLayouts.JAVA_INT;
MemorySegment segment = MemorySegment.allocateNative(layout);
VarHandle intHandle = MemoryHandles.withStride(layout.varHandle(int.class), 4);
VarHandle intHandle_sum = MemoryHandles.collectCoordinates(intHandle, 1, SUM_OFFSETS);
intHandle_sum.set(segment.baseAddress(), -2L, 2L, 1);
int oldValue = (int)intHandle_sum.getAndAdd(segment.baseAddress(), -2L, 2L, 42);
assertEquals(oldValue, 1);
int value = (int)intHandle_sum.get(segment.baseAddress(), -2L, 2L);
assertEquals(value, 43);
boolean swapped = (boolean)intHandle_sum.compareAndSet(segment.baseAddress(), -2L, 2L, 43, 12);
assertTrue(swapped);
oldValue = (int)intHandle_sum.compareAndExchange(segment.baseAddress(), -2L, 2L, 12, 42);
assertEquals(oldValue, 12);
value = (int)intHandle_sum.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment.baseAddress(), -2L, 2L);
assertEquals(value, 42);
}
@Test(expectedExceptions = NullPointerException.class)
public void testBadCollectCoordinatesNullTarget() {
MemoryHandles.collectCoordinates(null, 0, SUM_OFFSETS);
}
@Test(expectedExceptions = NullPointerException.class)
public void testBadCollectCoordinatesNullFilters() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.collectCoordinates(intHandle, 0, null);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadCollectCoordinatesNegativePos() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.collectCoordinates(intHandle, -1, SUM_OFFSETS);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadCollectCoordinatesPosTooBig() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.collectCoordinates(intHandle, 1, SUM_OFFSETS);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadCollectCoordinatesWrongFilterType() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.collectCoordinates(intHandle, 0, SUM_OFFSETS);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadCollectCoordinatesWrongVoidFilterType() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.collectCoordinates(intHandle, 0, VOID_FILTER);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadCollectCoordinatesWrongFilterException() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.collectCoordinates(intHandle, 0, S2L_EX);
}
@Test
public void testDropCoordinates() throws Throwable {
ValueLayout layout = MemoryLayouts.JAVA_INT;
MemorySegment segment = MemorySegment.allocateNative(layout);
VarHandle intHandle = MemoryHandles.withStride(layout.varHandle(int.class), 4);
VarHandle intHandle_dummy = MemoryHandles.dropCoordinates(intHandle, 1, float.class, String.class);
intHandle_dummy.set(segment.baseAddress(), 1f, "hello", 0L, 1);
int oldValue = (int)intHandle_dummy.getAndAdd(segment.baseAddress(), 1f, "hello", 0L, 42);
assertEquals(oldValue, 1);
int value = (int)intHandle_dummy.get(segment.baseAddress(), 1f, "hello", 0L);
assertEquals(value, 43);
boolean swapped = (boolean)intHandle_dummy.compareAndSet(segment.baseAddress(), 1f, "hello", 0L, 43, 12);
assertTrue(swapped);
oldValue = (int)intHandle_dummy.compareAndExchange(segment.baseAddress(), 1f, "hello", 0L, 12, 42);
assertEquals(oldValue, 12);
value = (int)intHandle_dummy.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment.baseAddress(), 1f, "hello", 0L);
assertEquals(value, 42);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadDropCoordinatesNegativePos() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.dropCoordinates(intHandle, -1);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadDropCoordinatesPosTooBig() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.dropCoordinates(intHandle, 2);
}
@Test(expectedExceptions = NullPointerException.class)
public void testBadDropCoordinatesNullValueTypes() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.dropCoordinates(intHandle, 1, null);
}
@Test(expectedExceptions = NullPointerException.class)
public void testBadDropCoordinatesNullTarget() {
MemoryHandles.dropCoordinates(null, 1);
}
//helper methods
static int stringToInt(String s) {
return Integer.valueOf(s);
}
static String intToString(int i) {
return String.valueOf(i);
}
static long stringToLong(String s) {
return Long.valueOf(s);
}
static long stringToLongException(String s) throws Throwable {
return Long.valueOf(s);
}
static MemoryAddress baseAddress(MemorySegment segment) {
return segment.baseAddress();
}
static long sumOffsets(long l1, long l2) {
return l1 + l2;
}
static void void_filter(String s) { }
}

View File

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

View File

@ -39,6 +39,8 @@ import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.testng.annotations.*;
import static jdk.incubator.foreign.MemorySegment.READ;
import static org.testng.Assert.*;
public class TestArrays {
@ -116,6 +118,20 @@ public class TestArrays {
segment.toByteArray();
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testArrayFromHeapSegmentWithoutAccess() {
MemorySegment segment = MemorySegment.ofArray(new byte[8]);
segment = segment.withAccessModes(segment.accessModes() & ~READ);
segment.toByteArray();
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testArrayFromNativeSegmentWithoutAccess() {
MemorySegment segment = MemorySegment.allocateNative(8);
segment = segment.withAccessModes(segment.accessModes() & ~READ);
segment.toByteArray();
}
@DataProvider(name = "arrays")
public Object[][] nativeAccessOps() {
Consumer<MemoryAddress> byteInitializer =

View File

@ -31,6 +31,7 @@
*/
import jdk.incubator.foreign.MappedMemorySegment;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryAddress;
@ -39,6 +40,7 @@ import jdk.incubator.foreign.MemoryLayout.PathElement;
import jdk.incubator.foreign.SequenceLayout;
import java.io.File;
import java.io.IOException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
@ -53,31 +55,48 @@ 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.Files;
import java.nio.file.Path;
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.function.Predicate;
import java.util.stream.Stream;
import jdk.internal.foreign.HeapMemorySegmentImpl;
import jdk.internal.foreign.MappedMemorySegmentImpl;
import jdk.internal.foreign.MemoryAddressImpl;
import jdk.internal.foreign.NativeMemorySegmentImpl;
import org.testng.SkipException;
import org.testng.annotations.*;
import sun.nio.ch.DirectBuffer;
import static jdk.incubator.foreign.MemorySegment.*;
import static org.testng.Assert.*;
public class TestByteBuffer {
static Path tempPath;
static {
try {
File file = File.createTempFile("buffer", "txt");
file.deleteOnExit();
tempPath = file.toPath();
Files.write(file.toPath(), new byte[256], StandardOpenOption.WRITE);
} catch (IOException ex) {
throw new ExceptionInInitializerError(ex);
}
}
static SequenceLayout tuples = MemoryLayout.ofSequence(500,
MemoryLayout.ofStruct(
MemoryLayouts.BITS_32_BE.withName("index"),
@ -217,6 +236,21 @@ public class TestByteBuffer {
}
}
static final int ALL_ACCESS_MODES = READ | WRITE | CLOSE | ACQUIRE | HANDOFF;
@Test
public void testDefaultAccessModesMappedSegment() throws Throwable {
try (MappedMemorySegment segment = MemorySegment.mapFromPath(tempPath, 8, FileChannel.MapMode.READ_WRITE)) {
assertTrue(segment.hasAccessModes(ALL_ACCESS_MODES));
assertEquals(segment.accessModes(), ALL_ACCESS_MODES);
}
try (MappedMemorySegment segment = MemorySegment.mapFromPath(tempPath, 8, FileChannel.MapMode.READ_ONLY)) {
assertTrue(segment.hasAccessModes(ALL_ACCESS_MODES & ~WRITE));
assertEquals(segment.accessModes(), ALL_ACCESS_MODES& ~WRITE);
}
}
@Test
public void testMappedSegment() throws Throwable {
File f = new File("test2.out");
@ -224,9 +258,10 @@ public class TestByteBuffer {
f.deleteOnExit();
//write to channel
try (MemorySegment segment = MemorySegment.mapFromPath(f.toPath(), tuples.byteSize(), FileChannel.MapMode.READ_WRITE)) {
try (MappedMemorySegment segment = MemorySegment.mapFromPath(f.toPath(), tuples.byteSize(), FileChannel.MapMode.READ_WRITE)) {
MemoryAddress base = segment.baseAddress();
initTuples(base);
segment.force();
}
//read from channel
@ -390,13 +425,16 @@ public class TestByteBuffer {
try (MemorySegment segment = MemorySegment.allocateNative(bytes)) {
leaked = segment;
}
leaked.asByteBuffer();
ByteBuffer byteBuffer = leaked.asByteBuffer(); // ok
byteBuffer.get(); // should throw
}
@Test(expectedExceptions = { UnsupportedOperationException.class,
IllegalArgumentException.class })
public void testTooBigForByteBuffer() {
MemorySegment.allocateNative((long) Integer.MAX_VALUE * 2).asByteBuffer();
try (MemorySegment segment = MemorySegment.allocateNative((long)Integer.MAX_VALUE + 10L)) {
segment.asByteBuffer();
}
}
@Test(dataProvider="resizeOps")
@ -423,6 +461,57 @@ public class TestByteBuffer {
}
}
@Test
public void testDefaultAccessModesOfBuffer() {
ByteBuffer rwBuffer = ByteBuffer.wrap(new byte[4]);
try (MemorySegment segment = MemorySegment.ofByteBuffer(rwBuffer)) {
assertTrue(segment.hasAccessModes(ALL_ACCESS_MODES));
assertEquals(segment.accessModes(), ALL_ACCESS_MODES);
}
ByteBuffer roBuffer = rwBuffer.asReadOnlyBuffer();
try (MemorySegment segment = MemorySegment.ofByteBuffer(roBuffer)) {
assertTrue(segment.hasAccessModes(ALL_ACCESS_MODES & ~WRITE));
assertEquals(segment.accessModes(), ALL_ACCESS_MODES & ~WRITE);
}
}
@Test(dataProvider="bufferSources")
public void testBufferToSegment(ByteBuffer bb, Predicate<MemorySegment> segmentChecker) {
MemorySegment segment = MemorySegment.ofByteBuffer(bb);
assertEquals(segment.hasAccessModes(MemorySegment.WRITE), !bb.isReadOnly());
assertTrue(segmentChecker.test(segment));
assertTrue(segmentChecker.test(segment.asSlice(0, segment.byteSize())));
assertTrue(segmentChecker.test(segment.withAccessModes(MemorySegment.READ)));
assertEquals(bb.capacity(), segment.byteSize());
//another round trip
segment = MemorySegment.ofByteBuffer(segment.asByteBuffer());
assertEquals(segment.hasAccessModes(MemorySegment.WRITE), !bb.isReadOnly());
assertTrue(segmentChecker.test(segment));
assertTrue(segmentChecker.test(segment.asSlice(0, segment.byteSize())));
assertTrue(segmentChecker.test(segment.withAccessModes(MemorySegment.READ)));
assertEquals(bb.capacity(), segment.byteSize());
}
@Test
public void testRoundTripAccess() {
try(MemorySegment ms = MemorySegment.allocateNative(4)) {
MemorySegment msNoAccess = ms.withAccessModes(MemorySegment.READ); // READ is required to make BB
MemorySegment msRoundTrip = MemorySegment.ofByteBuffer(msNoAccess.asByteBuffer());
assertEquals(msNoAccess.accessModes(), msRoundTrip.accessModes());
}
}
@Test(expectedExceptions = IllegalStateException.class)
public void testDeadAccessOnClosedBufferSegment() {
MemorySegment s1 = MemorySegment.allocateNative(MemoryLayouts.JAVA_INT);
MemorySegment s2 = MemorySegment.ofByteBuffer(s1.asByteBuffer());
s1.close(); // memory freed
intHandle.set(s2.baseAddress(), 0L, 10); // Dead access!
}
@DataProvider(name = "bufferOps")
public static Object[][] bufferOps() throws Throwable {
return new Object[][]{
@ -566,4 +655,27 @@ public class TestByteBuffer {
return null;
}
}
@DataProvider(name = "bufferSources")
public static Object[][] bufferSources() {
Predicate<MemorySegment> heapTest = segment -> segment instanceof HeapMemorySegmentImpl;
Predicate<MemorySegment> nativeTest = segment -> segment instanceof NativeMemorySegmentImpl;
Predicate<MemorySegment> mappedTest = segment -> segment instanceof MappedMemorySegmentImpl;
try (FileChannel channel = FileChannel.open(tempPath, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
return new Object[][]{
{ ByteBuffer.wrap(new byte[256]), heapTest },
{ ByteBuffer.allocate(256), heapTest },
{ ByteBuffer.allocateDirect(256), nativeTest },
{ channel.map(FileChannel.MapMode.READ_WRITE, 0L, 256), mappedTest },
{ ByteBuffer.wrap(new byte[256]).asReadOnlyBuffer(), heapTest },
{ ByteBuffer.allocate(256).asReadOnlyBuffer(), heapTest },
{ ByteBuffer.allocateDirect(256).asReadOnlyBuffer(), nativeTest },
{ channel.map(FileChannel.MapMode.READ_WRITE, 0L, 256).asReadOnlyBuffer(),
nativeTest /* this seems to be an existing bug in the BB implementation */ }
};
} catch (IOException ex) {
throw new ExceptionInInitializerError(ex);
}
}
}

View File

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

View File

@ -91,6 +91,9 @@ public class TestLayoutConstants {
MemoryLayout.ofStruct(
MemoryLayouts.PAD_8,
MemoryLayouts.BITS_32_BE)) },
{ MemoryLayouts.BITS_32_LE.withName("myInt") },
{ MemoryLayouts.BITS_32_LE.withBitAlignment(8) },
{ MemoryLayouts.BITS_32_LE.withAttribute("xyz", "abc") },
};
}
}

View File

@ -32,6 +32,7 @@ import jdk.incubator.foreign.MemoryLayout;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
import java.util.function.LongFunction;
import java.util.stream.Stream;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.SequenceLayout;
@ -176,6 +177,25 @@ public class TestLayouts {
assertEquals(struct.byteAlignment(), 8);
}
@Test(dataProvider="basicLayouts")
public void testPaddingNoAlign(MemoryLayout layout) {
assertEquals(MemoryLayout.ofPaddingBits(layout.bitSize()).bitAlignment(), 1);
}
@Test(dataProvider="basicLayouts")
public void testStructPaddingAndAlign(MemoryLayout layout) {
MemoryLayout struct = MemoryLayout.ofStruct(
layout, MemoryLayout.ofPaddingBits(128 - layout.bitSize()));
assertEquals(struct.bitAlignment(), layout.bitAlignment());
}
@Test(dataProvider="basicLayouts")
public void testUnionPaddingAndAlign(MemoryLayout layout) {
MemoryLayout struct = MemoryLayout.ofUnion(
layout, MemoryLayout.ofPaddingBits(128 - layout.bitSize()));
assertEquals(struct.bitAlignment(), layout.bitAlignment());
}
@Test
public void testUnionSizeAndAlign() {
MemoryLayout struct = MemoryLayout.ofUnion(
@ -188,6 +208,11 @@ public class TestLayouts {
assertEquals(struct.byteAlignment(), 8);
}
@Test(dataProvider = "layoutKinds")
public void testPadding(LayoutKind kind) {
assertEquals(kind == LayoutKind.PADDING, kind.layout.isPadding());
}
@Test(dataProvider="layoutsAndAlignments")
public void testAlignmentString(MemoryLayout layout, long bitAlign) {
long[] alignments = { 8, 16, 32, 64, 128 };
@ -236,6 +261,13 @@ public class TestLayouts {
return values;
}
@DataProvider(name = "layoutKinds")
public Object[][] layoutsKinds() {
return Stream.of(LayoutKind.values())
.map(lk -> new Object[] { lk })
.toArray(Object[][]::new);
}
enum SizedLayoutFactory {
VALUE_LE(size -> MemoryLayout.ofValueBits(size, ByteOrder.LITTLE_ENDIAN)),
VALUE_BE(size -> MemoryLayout.ofValueBits(size, ByteOrder.BIG_ENDIAN)),
@ -268,17 +300,15 @@ public class TestLayouts {
}
}
@DataProvider(name = "basicLayouts")
public Object[][] basicLayouts() {
return Stream.of(basicLayouts)
.map(l -> new Object[] { l })
.toArray(Object[][]::new);
}
@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
@ -303,4 +333,14 @@ public class TestLayouts {
}
return layoutsAndAlignments;
}
static MemoryLayout[] basicLayouts = {
MemoryLayouts.JAVA_BYTE,
MemoryLayouts.JAVA_CHAR,
MemoryLayouts.JAVA_SHORT,
MemoryLayouts.JAVA_INT,
MemoryLayouts.JAVA_FLOAT,
MemoryLayouts.JAVA_LONG,
MemoryLayouts.JAVA_DOUBLE,
};
}

View File

@ -23,7 +23,10 @@
/*
* @test
* @run testng/othervm -Xverify:all TestMemoryAccess
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false -Xverify:all TestMemoryAccess
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true -Xverify:all TestMemoryAccess
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false -Xverify:all TestMemoryAccess
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true -Xverify:all TestMemoryAccess
*/
import jdk.incubator.foreign.GroupLayout;
@ -81,14 +84,15 @@ public class TestMemoryAccess {
private void testAccessInternal(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout layout, VarHandle handle, Checker checker) {
MemoryAddress outer_address;
try (MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(layout))) {
boolean isRO = !segment.hasAccessModes(MemorySegment.WRITE);
MemoryAddress addr = segment.baseAddress();
try {
checker.check(handle, addr);
if (segment.isReadOnly()) {
if (isRO) {
throw new AssertionError(); //not ok, memory should be immutable
}
} catch (UnsupportedOperationException ex) {
if (!segment.isReadOnly()) {
if (!isRO) {
throw new AssertionError(); //we should not have failed!
}
return;
@ -112,16 +116,17 @@ public class TestMemoryAccess {
private void testArrayAccessInternal(Function<MemorySegment, MemorySegment> viewFactory, SequenceLayout seq, VarHandle handle, ArrayChecker checker) {
MemoryAddress outer_address;
try (MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(seq))) {
boolean isRO = !segment.hasAccessModes(MemorySegment.WRITE);
MemoryAddress addr = segment.baseAddress();
try {
for (int i = 0; i < seq.elementCount().getAsLong(); i++) {
checker.check(handle, addr, i);
}
if (segment.isReadOnly()) {
if (isRO) {
throw new AssertionError(); //not ok, memory should be immutable
}
} catch (UnsupportedOperationException ex) {
if (!segment.isReadOnly()) {
if (!isRO) {
throw new AssertionError(); //we should not have failed!
}
return;
@ -180,6 +185,7 @@ public class TestMemoryAccess {
private void testMatrixAccessInternal(Function<MemorySegment, MemorySegment> viewFactory, SequenceLayout seq, VarHandle handle, MatrixChecker checker) {
MemoryAddress outer_address;
try (MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(seq))) {
boolean isRO = !segment.hasAccessModes(MemorySegment.WRITE);
MemoryAddress addr = segment.baseAddress();
try {
for (int i = 0; i < seq.elementCount().getAsLong(); i++) {
@ -187,11 +193,11 @@ public class TestMemoryAccess {
checker.check(handle, addr, i, j);
}
}
if (segment.isReadOnly()) {
if (isRO) {
throw new AssertionError(); //not ok, memory should be immutable
}
} catch (UnsupportedOperationException ex) {
if (!segment.isReadOnly()) {
if (!isRO) {
throw new AssertionError(); //we should not have failed!
}
return;
@ -214,7 +220,7 @@ public class TestMemoryAccess {
}
static Function<MemorySegment, MemorySegment> ID = Function.identity();
static Function<MemorySegment, MemorySegment> IMMUTABLE = MemorySegment::asReadOnly;
static Function<MemorySegment, MemorySegment> IMMUTABLE = ms -> ms.withAccessModes(MemorySegment.READ | MemorySegment.CLOSE);
@DataProvider(name = "elements")
public Object[][] createData() {

View File

@ -25,20 +25,19 @@
/*
* @test
* @modules java.base/jdk.internal.misc
* jdk.incubator.foreign/jdk.incubator.foreign.unsafe
* @run testng TestNative
* jdk.incubator.foreign/jdk.internal.foreign
* @run testng/othervm -Dforeign.restricted=permit TestNative
*/
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayout.PathElement;
import jdk.incubator.foreign.unsafe.ForeignUnsafe;
import jdk.internal.misc.Unsafe;
import org.testng.annotations.*;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.SequenceLayout;
import jdk.internal.misc.Unsafe;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.lang.invoke.VarHandle;
import java.nio.Buffer;
@ -54,7 +53,7 @@ import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import static jdk.incubator.foreign.MemorySegment.*;
import static org.testng.Assert.*;
public class TestNative {
@ -113,12 +112,12 @@ public class TestNative {
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();
ByteBuffer bb = base.segment().asSlice(base.segmentOffset(), (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);
Object rawValue = nativeRawExtractor.apply(base.toRawLongValue(), (int)i);
if (handleValue instanceof Number) {
assertEquals(((Number)handleValue).longValue(), i);
assertEquals(((Number)bufferValue).longValue(), i);
@ -149,6 +148,9 @@ public class TestNative {
public static native long getCapacity(Buffer buffer);
public static native long allocate(int size);
public static native void free(long address);
@Test(dataProvider="nativeAccessOps")
public void testNativeAccess(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
try (MemorySegment segment = MemorySegment.allocateNative(seq)) {
@ -170,6 +172,42 @@ public class TestNative {
}
}
static final int ALL_ACCESS_MODES = READ | WRITE | CLOSE | ACQUIRE | HANDOFF;
@Test
public void testDefaultAccessModes() {
MemoryAddress addr = MemoryAddress.ofLong(allocate(12));
MemorySegment mallocSegment = MemorySegment.ofNativeRestricted(addr, 12, null,
() -> free(addr.toRawLongValue()), null);
try (MemorySegment segment = mallocSegment) {
assertTrue(segment.hasAccessModes(ALL_ACCESS_MODES));
assertEquals(segment.accessModes(), ALL_ACCESS_MODES);
}
}
@Test
public void testMallocSegment() {
MemoryAddress addr = MemoryAddress.ofLong(allocate(12));
assertNull(addr.segment());
MemorySegment mallocSegment = MemorySegment.ofNativeRestricted(addr, 12, null,
() -> free(addr.toRawLongValue()), null);
assertEquals(mallocSegment.byteSize(), 12);
mallocSegment.close(); //free here
assertTrue(!mallocSegment.isAlive());
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadResize() {
try (MemorySegment segment = MemorySegment.allocateNative(4)) {
MemorySegment.ofNativeRestricted(segment.baseAddress(), 0, null, null, null);
}
}
@Test(expectedExceptions = NullPointerException.class)
public void testNullUnsafeSegment() {
MemorySegment.ofNativeRestricted(null, 10, null, null, null);
}
static {
System.loadLibrary("NativeAccess");
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @modules java.base/jdk.internal.misc
* jdk.incubator.foreign/jdk.internal.foreign
* @run testng TestNoForeignUnsafeOverride
*/
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import org.testng.annotations.Test;
public class TestNoForeignUnsafeOverride {
static {
System.setProperty("foreign.restricted", "permit");
}
@Test(expectedExceptions = IllegalAccessError.class)
public void testUnsafeAccess() {
MemorySegment.ofNativeRestricted(MemoryAddress.ofLong(42), 10, null, null, null);
}
}

View File

@ -0,0 +1,150 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
/*
* @test
* @run testng TestRebase
*/
import jdk.incubator.foreign.MemoryAddress;
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.List;
import java.util.function.IntFunction;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
public class TestRebase {
static VarHandle BYTE_VH = MemoryLayouts.JAVA_BYTE.varHandle(byte.class);
@Test(dataProvider = "slices")
public void testRebase(SegmentSlice s1, SegmentSlice s2) {
if (s1.contains(s2)) {
//check that an address and its rebased counterpart point to same element
MemoryAddress base = s2.segment.baseAddress();
MemoryAddress rebased = base.rebase(s1.segment);
for (int i = 0; i < s2.size(); i++) {
int expected = (int) BYTE_VH.get(base.addOffset(i));
int found = (int) BYTE_VH.get(rebased.addOffset(i));
assertEquals(found, expected);
}
} else if (s1.kind != s2.kind) {
// check that rebase s1 to s2 fails
try {
s1.segment.baseAddress().rebase(s2.segment);
fail("Rebase unexpectedly passed!");
} catch (IllegalArgumentException ex) {
assertTrue(true);
}
} else if (!s2.contains(s1)) {
//disjoint segments - check that rebased address is out of bounds
MemoryAddress base = s2.segment.baseAddress();
MemoryAddress rebased = base.rebase(s1.segment);
for (int i = 0; i < s2.size(); i++) {
BYTE_VH.get(base.addOffset(i));
try {
BYTE_VH.get(rebased.addOffset(i));
fail("Rebased address on a disjoint segment is not out of bounds!");
} catch (IndexOutOfBoundsException ex) {
assertTrue(true);
}
}
}
}
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;
}
boolean contains(SegmentSlice other) {
return kind == other.kind &&
first <= other.first &&
last >= other.last;
}
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()) {
//init root segment
MemorySegment segment = kind.makeSegment(16);
for (int i = 0 ; i < 16 ; i++) {
BYTE_VH.set(segment.baseAddress().addOffset(i), (byte)i);
}
//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,198 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @run testng TestReshape
*/
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.SequenceLayout;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.stream.LongStream;
import org.testng.annotations.*;
import static org.testng.Assert.*;
public class TestReshape {
@Test(dataProvider = "shapes")
public void testReshape(MemoryLayout layout, long[] expectedShape) {
long flattenedSize = LongStream.of(expectedShape).reduce(1L, Math::multiplyExact);
SequenceLayout seq_flattened = MemoryLayout.ofSequence(flattenedSize, layout);
assertDimensions(seq_flattened, flattenedSize);
for (long[] shape : new Shape(expectedShape)) {
SequenceLayout seq_shaped = seq_flattened.reshape(shape);
assertDimensions(seq_shaped, expectedShape);
assertEquals(seq_shaped.flatten(), seq_flattened);
}
}
@Test(expectedExceptions = NullPointerException.class)
public void testNullReshape() {
SequenceLayout seq = MemoryLayout.ofSequence(4, MemoryLayouts.JAVA_INT);
seq.reshape(null);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testInvalidReshape() {
SequenceLayout seq = MemoryLayout.ofSequence(4, MemoryLayouts.JAVA_INT);
seq.reshape(3, 2);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadReshapeInference() {
SequenceLayout seq = MemoryLayout.ofSequence(4, MemoryLayouts.JAVA_INT);
seq.reshape(-1, -1);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadReshapeParameterZero() {
SequenceLayout seq = MemoryLayout.ofSequence(4, MemoryLayouts.JAVA_INT);
seq.reshape(0, 4);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadReshapeParameterNegative() {
SequenceLayout seq = MemoryLayout.ofSequence(4, MemoryLayouts.JAVA_INT);
seq.reshape(-2, 2);
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testReshapeOnUnboundSequence() {
SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT);
seq.reshape(3, 2);
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testFlattenOnUnboundSequence() {
SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT);
seq.flatten();
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testFlattenOnUnboundNestedSequence() {
SequenceLayout seq = MemoryLayout.ofSequence(4, MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT));
seq.flatten();
}
static void assertDimensions(SequenceLayout layout, long... dims) {
SequenceLayout prev = null;
for (int i = 0 ; i < dims.length ; i++) {
if (prev != null) {
layout = (SequenceLayout)prev.elementLayout();
}
assertEquals(layout.elementCount().getAsLong(), dims[i]);
prev = layout;
}
}
static class Shape implements Iterable<long[]> {
long[] shape;
Shape(long... shape) {
this.shape = shape;
}
public Iterator<long[]> iterator() {
List<long[]> shapes = new ArrayList<>();
shapes.add(shape);
for (int i = 0 ; i < shape.length ; i++) {
long[] inferredShape = shape.clone();
inferredShape[i] = -1;
shapes.add(inferredShape);
}
return shapes.iterator();
}
}
static MemoryLayout POINT = MemoryLayout.ofStruct(
MemoryLayouts.JAVA_INT,
MemoryLayouts.JAVA_INT
);
@DataProvider(name = "shapes")
Object[][] shapes() {
return new Object[][] {
{ MemoryLayouts.JAVA_BYTE, new long[] { 256 } },
{ MemoryLayouts.JAVA_BYTE, new long[] { 16, 16 } },
{ MemoryLayouts.JAVA_BYTE, new long[] { 4, 4, 4, 4 } },
{ MemoryLayouts.JAVA_BYTE, new long[] { 2, 8, 16 } },
{ MemoryLayouts.JAVA_BYTE, new long[] { 16, 8, 2 } },
{ MemoryLayouts.JAVA_BYTE, new long[] { 8, 16, 2 } },
{ MemoryLayouts.JAVA_SHORT, new long[] { 256 } },
{ MemoryLayouts.JAVA_SHORT, new long[] { 16, 16 } },
{ MemoryLayouts.JAVA_SHORT, new long[] { 4, 4, 4, 4 } },
{ MemoryLayouts.JAVA_SHORT, new long[] { 2, 8, 16 } },
{ MemoryLayouts.JAVA_SHORT, new long[] { 16, 8, 2 } },
{ MemoryLayouts.JAVA_SHORT, new long[] { 8, 16, 2 } },
{ MemoryLayouts.JAVA_CHAR, new long[] { 256 } },
{ MemoryLayouts.JAVA_CHAR, new long[] { 16, 16 } },
{ MemoryLayouts.JAVA_CHAR, new long[] { 4, 4, 4, 4 } },
{ MemoryLayouts.JAVA_CHAR, new long[] { 2, 8, 16 } },
{ MemoryLayouts.JAVA_CHAR, new long[] { 16, 8, 2 } },
{ MemoryLayouts.JAVA_CHAR, new long[] { 8, 16, 2 } },
{ MemoryLayouts.JAVA_INT, new long[] { 256 } },
{ MemoryLayouts.JAVA_INT, new long[] { 16, 16 } },
{ MemoryLayouts.JAVA_INT, new long[] { 4, 4, 4, 4 } },
{ MemoryLayouts.JAVA_INT, new long[] { 2, 8, 16 } },
{ MemoryLayouts.JAVA_INT, new long[] { 16, 8, 2 } },
{ MemoryLayouts.JAVA_INT, new long[] { 8, 16, 2 } },
{ MemoryLayouts.JAVA_LONG, new long[] { 256 } },
{ MemoryLayouts.JAVA_LONG, new long[] { 16, 16 } },
{ MemoryLayouts.JAVA_LONG, new long[] { 4, 4, 4, 4 } },
{ MemoryLayouts.JAVA_LONG, new long[] { 2, 8, 16 } },
{ MemoryLayouts.JAVA_LONG, new long[] { 16, 8, 2 } },
{ MemoryLayouts.JAVA_LONG, new long[] { 8, 16, 2 } },
{ MemoryLayouts.JAVA_FLOAT, new long[] { 256 } },
{ MemoryLayouts.JAVA_FLOAT, new long[] { 16, 16 } },
{ MemoryLayouts.JAVA_FLOAT, new long[] { 4, 4, 4, 4 } },
{ MemoryLayouts.JAVA_FLOAT, new long[] { 2, 8, 16 } },
{ MemoryLayouts.JAVA_FLOAT, new long[] { 16, 8, 2 } },
{ MemoryLayouts.JAVA_FLOAT, new long[] { 8, 16, 2 } },
{ MemoryLayouts.JAVA_DOUBLE, new long[] { 256 } },
{ MemoryLayouts.JAVA_DOUBLE, new long[] { 16, 16 } },
{ MemoryLayouts.JAVA_DOUBLE, new long[] { 4, 4, 4, 4 } },
{ MemoryLayouts.JAVA_DOUBLE, new long[] { 2, 8, 16 } },
{ MemoryLayouts.JAVA_DOUBLE, new long[] { 16, 8, 2 } },
{ MemoryLayouts.JAVA_DOUBLE, new long[] { 8, 16, 2 } },
{ POINT, new long[] { 256 } },
{ POINT, new long[] { 16, 16 } },
{ POINT, new long[] { 4, 4, 4, 4 } },
{ POINT, new long[] { 2, 8, 16 } },
{ POINT, new long[] { 16, 8, 2 } },
{ POINT, new long[] { 8, 16, 2 } },
};
}
}

View File

@ -30,6 +30,8 @@ import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
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.lang.reflect.Method;
@ -37,12 +39,13 @@ import java.lang.reflect.Modifier;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
import java.util.Spliterator;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.LongFunction;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.testng.annotations.*;
import static jdk.incubator.foreign.MemorySegment.*;
import static org.testng.Assert.*;
public class TestSegments {
@ -95,6 +98,27 @@ public class TestSegments {
}
}
@Test
public void testNothingSegmentAccess() {
VarHandle longHandle = MemoryLayouts.JAVA_LONG.varHandle(long.class);
long[] values = { 0L, Integer.MAX_VALUE - 1, (long) Integer.MAX_VALUE + 1 };
for (long value : values) {
MemoryAddress addr = MemoryAddress.ofLong(value);
try {
longHandle.get(addr);
} catch (UnsupportedOperationException ex) {
assertTrue(ex.getMessage().contains("Required access mode"));
}
}
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testNothingSegmentOffset() {
MemoryAddress addr = MemoryAddress.ofLong(42);
assertNull(addr.segment());
addr.segmentOffset();
}
@Test
public void testSlices() {
VarHandle byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE)
@ -108,7 +132,7 @@ public class TestSegments {
MemoryAddress base = segment.baseAddress();
MemoryAddress last = base.addOffset(10);
while (!base.equals(last)) {
MemorySegment slice = segment.asSlice(base.offset(), 10 - start);
MemorySegment slice = segment.asSlice(base.segmentOffset(), 10 - start);
for (long i = start ; i < 10 ; i++) {
assertEquals(
byteHandle.get(segment.baseAddress(), i),
@ -121,6 +145,71 @@ public class TestSegments {
}
}
static final int ALL_ACCESS_MODES = READ | WRITE | CLOSE | ACQUIRE | HANDOFF;
@DataProvider(name = "segmentFactories")
public Object[][] segmentFactories() {
List<Supplier<MemorySegment>> l = List.of(
() -> MemorySegment.ofArray(new byte[1]),
() -> MemorySegment.ofArray(new char[1]),
() -> MemorySegment.ofArray(new double[1]),
() -> MemorySegment.ofArray(new float[1]),
() -> MemorySegment.ofArray(new int[1]),
() -> MemorySegment.ofArray(new long[1]),
() -> MemorySegment.ofArray(new short[1]),
() -> MemorySegment.ofArray(new int[1]),
() -> MemorySegment.allocateNative(1),
() -> MemorySegment.allocateNative(1, 2),
() -> MemorySegment.allocateNative(MemoryLayout.ofValueBits(8, ByteOrder.LITTLE_ENDIAN))
);
return l.stream().map(s -> new Object[] { s }).toArray(Object[][]::new);
}
@Test(dataProvider = "segmentFactories")
public void testAccessModesOfFactories(Supplier<MemorySegment> memorySegmentSupplier) {
try (MemorySegment segment = memorySegmentSupplier.get()) {
assertTrue(segment.hasAccessModes(ALL_ACCESS_MODES));
assertEquals(segment.accessModes(), ALL_ACCESS_MODES);
}
}
@Test(dataProvider = "accessModes")
public void testAccessModes(int accessModes) {
int[] arr = new int[1];
for (AccessActions action : AccessActions.values()) {
MemorySegment segment = MemorySegment.ofArray(arr);
MemorySegment restrictedSegment = segment.withAccessModes(accessModes);
assertEquals(restrictedSegment.accessModes(), accessModes);
boolean shouldFail = !restrictedSegment.hasAccessModes(action.accessMode);
try {
action.run(restrictedSegment);
assertFalse(shouldFail);
} catch (UnsupportedOperationException ex) {
assertTrue(shouldFail);
}
}
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testWithAccessModesBadUnsupportedMode() {
int[] arr = new int[1];
MemorySegment segment = MemorySegment.ofArray(arr);
segment.withAccessModes((1 << AccessActions.values().length) + 1);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadWithAccessModesBadStrongerMode() {
int[] arr = new int[1];
MemorySegment segment = MemorySegment.ofArray(arr).withAccessModes(READ);
segment.withAccessModes(WRITE);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadHasAccessModes() {
int[] arr = new int[1];
MemorySegment segment = MemorySegment.ofArray(arr);
segment.hasAccessModes((1 << AccessActions.values().length) + 1);
}
@DataProvider(name = "badSizeAndAlignments")
public Object[][] sizesAndAlignments() {
return new Object[][] {
@ -176,16 +265,19 @@ public class TestSegments {
final Method method;
final Object[] params;
final static List<String> CONFINED_NAMES = List.of(
"close",
"toByteArray",
"withOwnerThread"
);
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");
return CONFINED_NAMES.contains(method.getName());
}
@Override
@ -219,4 +311,77 @@ public class TestSegments {
return null;
}
}
@DataProvider(name = "accessModes")
public Object[][] accessModes() {
int nActions = AccessActions.values().length;
Object[][] results = new Object[1 << nActions][];
for (int accessModes = 0 ; accessModes < results.length ; accessModes++) {
results[accessModes] = new Object[] { accessModes };
}
return results;
}
enum AccessActions {
ACQUIRE(MemorySegment.ACQUIRE) {
@Override
void run(MemorySegment segment) {
Spliterator<MemorySegment> spliterator =
MemorySegment.spliterator(segment, MemoryLayout.ofSequence(segment.byteSize(), MemoryLayouts.JAVA_BYTE));
AtomicReference<RuntimeException> exception = new AtomicReference<>();
Runnable action = () -> {
try {
spliterator.tryAdvance(s -> { });
} catch (RuntimeException e) {
exception.set(e);
}
};
Thread thread = new Thread(action);
thread.start();
try {
thread.join();
} catch (InterruptedException ex) {
throw new AssertionError(ex);
}
RuntimeException e = exception.get();
if (e != null) {
throw e;
}
}
},
CLOSE(MemorySegment.CLOSE) {
@Override
void run(MemorySegment segment) {
segment.close();
}
},
READ(MemorySegment.READ) {
@Override
void run(MemorySegment segment) {
INT_HANDLE.get(segment.baseAddress());
}
},
WRITE(MemorySegment.WRITE) {
@Override
void run(MemorySegment segment) {
INT_HANDLE.set(segment.baseAddress(), 42);
}
},
HANDOFF(MemorySegment.HANDOFF) {
@Override
void run(MemorySegment segment) {
segment.withOwnerThread(new Thread());
}
};
final int accessMode;
static VarHandle INT_HANDLE = MemoryLayouts.JAVA_INT.varHandle(int.class);
AccessActions(int accessMode) {
this.accessMode = accessMode;
}
abstract void run(MemorySegment segment);
}
}

View File

@ -24,34 +24,118 @@
/*
* @test
* @run testng TestSharedAccess
* @run testng/othervm -Dforeign.restricted=permit TestSharedAccess
*/
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayouts;
import org.testng.annotations.*;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.SequenceLayout;
import org.testng.annotations.Test;
import java.lang.invoke.VarHandle;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Spliterator;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import static org.testng.Assert.*;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
public class TestSharedAccess {
static final VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
@Test
public void testConfined() throws Throwable {
Thread owner = Thread.currentThread();
MemorySegment s = MemorySegment.allocateNative(4);
AtomicReference<MemorySegment> confined = new AtomicReference<>(s);
setInt(s.baseAddress(), 42);
assertEquals(getInt(s.baseAddress()), 42);
List<Thread> threads = new ArrayList<>();
for (int i = 0 ; i < 1000 ; i++) {
threads.add(new Thread(() -> {
assertEquals(getInt(confined.get().baseAddress()), 42);
confined.set(confined.get().withOwnerThread(owner));
}));
}
threads.forEach(t -> {
confined.set(confined.get().withOwnerThread(t));
t.start();
try {
t.join();
} catch (Throwable e) {
throw new IllegalStateException(e);
}
});
confined.get().close();
}
@Test
public void testShared() throws Throwable {
try (MemorySegment s = MemorySegment.allocateNative(4)) {
setInt(s, 42);
assertEquals(getInt(s), 42);
SequenceLayout layout = MemoryLayout.ofSequence(1024, MemoryLayouts.JAVA_INT);
try (MemorySegment s = MemorySegment.allocateNative(layout)) {
for (int i = 0 ; i < layout.elementCount().getAsLong() ; i++) {
setInt(s.baseAddress().addOffset(i * 4), 42);
}
List<Thread> threads = new ArrayList<>();
List<Spliterator<MemorySegment>> spliterators = new ArrayList<>();
spliterators.add(MemorySegment.spliterator(s, layout));
while (true) {
boolean progress = false;
List<Spliterator<MemorySegment>> newSpliterators = new ArrayList<>();
for (Spliterator<MemorySegment> spliterator : spliterators) {
Spliterator<MemorySegment> sub = spliterator.trySplit();
if (sub != null) {
progress = true;
newSpliterators.add(sub);
}
}
spliterators.addAll(newSpliterators);
if (!progress) break;
}
AtomicInteger accessCount = new AtomicInteger();
for (Spliterator<MemorySegment> spliterator : spliterators) {
threads.add(new Thread(() -> {
spliterator.tryAdvance(local -> {
assertEquals(getInt(local.baseAddress()), 42);
accessCount.incrementAndGet();
});
}));
}
threads.forEach(Thread::start);
threads.forEach(t -> {
try {
t.join();
} catch (Throwable e) {
throw new IllegalStateException(e);
}
});
assertEquals(accessCount.get(), 1024);
}
}
@Test
public void testSharedUnsafe() throws Throwable {
try (MemorySegment s = MemorySegment.allocateNative(4)) {
setInt(s.baseAddress(), 42);
assertEquals(getInt(s.baseAddress()), 42);
List<Thread> threads = new ArrayList<>();
MemorySegment sharedSegment = MemorySegment.ofNativeRestricted(
s.baseAddress(), s.byteSize(), null, null, null);
for (int i = 0 ; i < 1000 ; i++) {
threads.add(new Thread(() -> {
try (MemorySegment local = s.acquire()) {
assertEquals(getInt(local), 42);
}
assertEquals(getInt(sharedSegment.baseAddress()), 42);
}));
}
threads.forEach(Thread::start);
@ -67,16 +151,105 @@ public class TestSharedAccess {
@Test(expectedExceptions=IllegalStateException.class)
public void testBadCloseWithPendingAcquire() {
try (MemorySegment segment = MemorySegment.allocateNative(8)) {
segment.acquire();
} //should fail here!
withAcquired(MemorySegment::close);
}
static int getInt(MemorySegment handle) {
return (int)intHandle.getVolatile(handle.baseAddress());
@Test(expectedExceptions=IllegalStateException.class)
public void testBadCloseWithPendingAcquireBuffer() {
withAcquired(segment -> {
segment = MemorySegment.ofByteBuffer(segment.asByteBuffer()); // original segment is lost
segment.close(); // this should still fail
});
}
static void setInt(MemorySegment handle, int value) {
intHandle.setVolatile(handle.baseAddress(), value);
@Test(expectedExceptions=IllegalStateException.class)
public void testBadHandoffWithPendingAcquire() {
withAcquired(segment -> segment.withOwnerThread(new Thread()));
}
@Test(expectedExceptions=IllegalStateException.class)
public void testBadHandoffWithPendingAcquireBuffer() {
withAcquired(segment -> {
segment = MemorySegment.ofByteBuffer(segment.asByteBuffer()); // original segment is lost
segment.withOwnerThread(new Thread()); // this should still fail
});
}
@Test(expectedExceptions=IllegalArgumentException.class)
public void testBadHandoffSameThread() {
MemorySegment.ofArray(new int[4]).withOwnerThread(Thread.currentThread());
}
@Test(expectedExceptions=NullPointerException.class)
public void testBadHandoffNullThread() {
MemorySegment.ofArray(new int[4]).withOwnerThread(null);
}
private void withAcquired(Consumer<MemorySegment> acquiredAction) {
CountDownLatch holder = new CountDownLatch(1);
MemorySegment segment = MemorySegment.allocateNative(16);
Spliterator<MemorySegment> spliterator = MemorySegment.spliterator(segment,
MemoryLayout.ofSequence(16, MemoryLayouts.JAVA_BYTE));
CountDownLatch acquired = new CountDownLatch(1);
Runnable r = () -> spliterator.tryAdvance(s -> {
try {
acquired.countDown();
holder.await();
} catch (InterruptedException ex) {
throw new AssertionError(ex);
}
});
new Thread(r).start();
try {
acquired.await();
acquiredAction.accept(segment);
} catch (InterruptedException ex) {
throw new AssertionError(ex);
} finally {
holder.countDown();
}
}
@Test
public void testOutsideConfinementThread() throws Throwable {
CountDownLatch a = new CountDownLatch(1);
CountDownLatch b = new CountDownLatch(1);
CompletableFuture<?> r;
try (MemorySegment s1 = MemorySegment.allocateNative(MemoryLayout.ofSequence(2, MemoryLayouts.JAVA_INT))) {
r = CompletableFuture.runAsync(() -> {
try {
ByteBuffer bb = s1.asByteBuffer();
MemorySegment s2 = MemorySegment.ofByteBuffer(bb);
a.countDown();
try {
b.await();
} catch (InterruptedException e) {
}
MemoryAddress base = s2.baseAddress();
setInt(base.addOffset(4), -42);
fail();
} catch (IllegalStateException ex) {
assertTrue(ex.getMessage().contains("owning thread"));
}
});
a.await();
MemoryAddress base = s1.baseAddress();
setInt(base.addOffset(4), 42);
}
b.countDown();
r.get();
}
static int getInt(MemoryAddress address) {
return (int)intHandle.getVolatile(address);
}
static void setInt(MemoryAddress address, int value) {
intHandle.setVolatile(address, value);
}
}

View File

@ -0,0 +1,257 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @run testng TestSpliterator
*/
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.SequenceLayout;
import java.lang.invoke.VarHandle;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Spliterator;
import java.util.concurrent.CountedCompleter;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.LongStream;
import java.util.stream.StreamSupport;
import org.testng.annotations.*;
import static jdk.incubator.foreign.MemorySegment.*;
import static org.testng.Assert.*;
public class TestSpliterator {
static final VarHandle INT_HANDLE = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT)
.varHandle(int.class, MemoryLayout.PathElement.sequenceElement());
final static int CARRIER_SIZE = 4;
@Test(dataProvider = "splits")
public void testSum(int size, int threshold) {
SequenceLayout layout = MemoryLayout.ofSequence(size, MemoryLayouts.JAVA_INT);
//setup
MemorySegment segment = MemorySegment.allocateNative(layout);
for (int i = 0; i < layout.elementCount().getAsLong(); i++) {
INT_HANDLE.set(segment.baseAddress(), (long) i, i);
}
long expected = LongStream.range(0, layout.elementCount().getAsLong()).sum();
//serial
long serial = sum(0, segment);
assertEquals(serial, expected);
//parallel counted completer
long parallelCounted = new SumSegmentCounted(null, MemorySegment.spliterator(segment, layout), threshold).invoke();
assertEquals(parallelCounted, expected);
//parallel recursive action
long parallelRecursive = new SumSegmentRecursive(MemorySegment.spliterator(segment, layout), threshold).invoke();
assertEquals(parallelRecursive, expected);
//parallel stream
long streamParallel = StreamSupport.stream(MemorySegment.spliterator(segment, layout), true)
.reduce(0L, TestSpliterator::sumSingle, Long::sum);
assertEquals(streamParallel, expected);
segment.close();
}
public void testSumSameThread() {
SequenceLayout layout = MemoryLayout.ofSequence(1024, MemoryLayouts.JAVA_INT);
//setup
MemorySegment segment = MemorySegment.allocateNative(layout);
for (int i = 0; i < layout.elementCount().getAsLong(); i++) {
INT_HANDLE.set(segment.baseAddress(), (long) i, i);
}
long expected = LongStream.range(0, layout.elementCount().getAsLong()).sum();
//check that a segment w/o ACQUIRE access mode can still be used from same thread
AtomicLong spliteratorSum = new AtomicLong();
spliterator(segment.withAccessModes(MemorySegment.READ), layout)
.forEachRemaining(s -> spliteratorSum.addAndGet(sumSingle(0L, s)));
assertEquals(spliteratorSum.get(), expected);
}
static long sumSingle(long acc, MemorySegment segment) {
return acc + (int)INT_HANDLE.get(segment.baseAddress(), 0L);
}
static long sum(long start, MemorySegment segment) {
long sum = start;
MemoryAddress base = segment.baseAddress();
int length = (int)segment.byteSize();
for (int i = 0 ; i < length / CARRIER_SIZE ; i++) {
sum += (int)INT_HANDLE.get(base, (long)i);
}
return sum;
}
static class SumSegmentCounted extends CountedCompleter<Long> {
final long threshold;
long localSum = 0;
List<SumSegmentCounted> children = new LinkedList<>();
private Spliterator<MemorySegment> segmentSplitter;
SumSegmentCounted(SumSegmentCounted parent, Spliterator<MemorySegment> segmentSplitter, long threshold) {
super(parent);
this.segmentSplitter = segmentSplitter;
this.threshold = threshold;
}
@Override
public void compute() {
Spliterator<MemorySegment> sub;
while (segmentSplitter.estimateSize() > threshold &&
(sub = segmentSplitter.trySplit()) != null) {
addToPendingCount(1);
SumSegmentCounted child = new SumSegmentCounted(this, sub, threshold);
children.add(child);
child.fork();
}
segmentSplitter.forEachRemaining(slice -> {
localSum += sumSingle(0, slice);
});
tryComplete();
}
@Override
public Long getRawResult() {
long sum = localSum;
for (SumSegmentCounted c : children) {
sum += c.getRawResult();
}
return sum;
}
}
static class SumSegmentRecursive extends RecursiveTask<Long> {
final long threshold;
private final Spliterator<MemorySegment> splitter;
private long result;
SumSegmentRecursive(Spliterator<MemorySegment> splitter, long threshold) {
this.splitter = splitter;
this.threshold = threshold;
}
@Override
protected Long compute() {
if (splitter.estimateSize() > threshold) {
SumSegmentRecursive sub = new SumSegmentRecursive(splitter.trySplit(), threshold);
sub.fork();
return compute() + sub.join();
} else {
splitter.forEachRemaining(slice -> {
result += sumSingle(0, slice);
});
return result;
}
}
}
@DataProvider(name = "splits")
public Object[][] splits() {
return new Object[][] {
{ 10, 1 },
{ 100, 1 },
{ 1000, 1 },
{ 10000, 1 },
{ 10, 10 },
{ 100, 10 },
{ 1000, 10 },
{ 10000, 10 },
{ 10, 100 },
{ 100, 100 },
{ 1000, 100 },
{ 10000, 100 },
{ 10, 1000 },
{ 100, 1000 },
{ 1000, 1000 },
{ 10000, 1000 },
{ 10, 10000 },
{ 100, 10000 },
{ 1000, 10000 },
{ 10000, 10000 },
};
}
static final int ALL_ACCESS_MODES = READ | WRITE | CLOSE | ACQUIRE | HANDOFF;
@DataProvider(name = "accessScenarios")
public Object[][] accessScenarios() {
SequenceLayout layout = MemoryLayout.ofSequence(16, MemoryLayouts.JAVA_INT);
var mallocSegment = MemorySegment.allocateNative(layout);
Map<Supplier<Spliterator<MemorySegment>>,Integer> l = Map.of(
() -> spliterator(mallocSegment.withAccessModes(ALL_ACCESS_MODES), layout), ALL_ACCESS_MODES,
() -> spliterator(mallocSegment.withAccessModes(0), layout), 0,
() -> spliterator(mallocSegment.withAccessModes(READ), layout), READ,
() -> spliterator(mallocSegment.withAccessModes(CLOSE), layout), 0,
() -> spliterator(mallocSegment.withAccessModes(READ|WRITE), layout), READ|WRITE,
() -> spliterator(mallocSegment.withAccessModes(READ|WRITE|ACQUIRE), layout), READ|WRITE|ACQUIRE,
() -> spliterator(mallocSegment.withAccessModes(READ|WRITE|ACQUIRE|HANDOFF), layout), READ|WRITE|ACQUIRE|HANDOFF
);
return l.entrySet().stream().map(e -> new Object[] { e.getKey(), e.getValue() }).toArray(Object[][]::new);
}
static Consumer<MemorySegment> assertAccessModes(int accessModes) {
return segment -> {
assertTrue(segment.hasAccessModes(accessModes & ~CLOSE));
assertEquals(segment.accessModes(), accessModes & ~CLOSE);
};
}
@Test(dataProvider = "accessScenarios")
public void testAccessModes(Supplier<Spliterator<MemorySegment>> spliteratorSupplier,
int expectedAccessModes) {
Spliterator<MemorySegment> spliterator = spliteratorSupplier.get();
spliterator.forEachRemaining(assertAccessModes(expectedAccessModes));
spliterator = spliteratorSupplier.get();
do { } while (spliterator.tryAdvance(assertAccessModes(expectedAccessModes)));
splitOrConsume(spliteratorSupplier.get(), assertAccessModes(expectedAccessModes));
}
static void splitOrConsume(Spliterator<MemorySegment> spliterator,
Consumer<MemorySegment> consumer) {
var s1 = spliterator.trySplit();
if (s1 != null) {
splitOrConsume(s1, consumer);
splitOrConsume(spliterator, consumer);
} else {
spliterator.forEachRemaining(consumer);
}
}
}

View File

@ -34,6 +34,7 @@ import org.testng.annotations.Test;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
@ -53,16 +54,27 @@ public class TestVarHandleCombinators {
assertEquals((byte) vh.get(addr, 2), (byte) -1);
}
@Test(expectedExceptions = IllegalArgumentException.class)
@Test(expectedExceptions = IllegalStateException.class)
public void testUnalignedElement() {
VarHandle vh = MemoryHandles.varHandle(byte.class, 4, ByteOrder.nativeOrder());
MemoryHandles.withStride(vh, 2);
vh = MemoryHandles.withStride(vh, 2);
MemorySegment segment = MemorySegment.ofArray(new byte[4]);
vh.get(segment.baseAddress(), 1L); //should throw
}
public void testZeroStrideElement() {
VarHandle vh = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
VarHandle strided_vh = MemoryHandles.withStride(vh, 0);
MemorySegment segment = MemorySegment.ofArray(new int[] { 42 });
for (int i = 0 ; i < 100 ; i++) {
assertEquals((int)vh.get(segment.baseAddress()), strided_vh.get(segment.baseAddress(), (long)i));
}
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadStrideElement() {
VarHandle vh = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
MemoryHandles.withStride(vh, 0); //scale factor cant be zero
public void testStrideWrongHandle() {
VarHandle vh = MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.nativeOrder());
MemoryHandles.withStride(vh, 10);
}
@Test(expectedExceptions = IllegalArgumentException.class)
@ -86,7 +98,7 @@ public class TestVarHandleCombinators {
assertEquals((byte) vh.get(address), (byte) 10);
}
@Test(expectedExceptions = IllegalArgumentException.class)
@Test(expectedExceptions = IllegalStateException.class)
public void testAlignBadAccess() {
VarHandle vh = MemoryHandles.varHandle(byte.class, 2, ByteOrder.nativeOrder());
vh = MemoryHandles.withOffset(vh, 1); // offset by 1 byte
@ -97,16 +109,27 @@ public class TestVarHandleCombinators {
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);
public void testZeroOffsetElement() {
VarHandle vh = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
VarHandle offset_vh = MemoryHandles.withOffset(vh, 0);
MemorySegment segment = MemorySegment.ofArray(new int[] { 42 });
for (int i = 0 ; i < 100 ; i++) {
assertEquals((int)vh.get(segment.baseAddress()), offset_vh.get(segment.baseAddress(), (long)i));
}
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testOffsetWrongHandle() {
VarHandle vh = MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.nativeOrder());
MemoryHandles.withOffset(vh, 1);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testUnalignedOffset() {
VarHandle vh = MemoryHandles.varHandle(byte.class, 4, ByteOrder.nativeOrder());
MemoryHandles.withOffset(vh, 2);
vh = MemoryHandles.withOffset(vh, 2);
MemorySegment segment = MemorySegment.ofArray(new byte[4]);
vh.get(segment.baseAddress()); //should throw
}
@Test

View File

@ -23,6 +23,7 @@
*/
#include "jni.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
@ -114,3 +115,13 @@ JNIEXPORT jlong JNICALL
Java_TestNative_getCapacity(JNIEnv *env, jclass cls, jobject buf) {
return (*env)->GetDirectBufferCapacity(env, buf);
}
JNIEXPORT jlong JNICALL
Java_TestNative_allocate(JNIEnv *env, jclass cls, jint size) {
return (jlong)malloc(size);
}
JNIEXPORT void JNICALL
Java_TestNative_free(JNIEnv *env, jclass cls, jlong ptr) {
free((void*) ptr);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,7 +25,9 @@
* @test
* @bug 8156486
* @run testng/othervm VarHandleTestMethodTypeBoolean
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false VarHandleTestMethodTypeBoolean
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeBoolean
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false VarHandleTestMethodTypeBoolean
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeBoolean
*/
import org.testng.annotations.BeforeClass;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,7 +25,9 @@
* @test
* @bug 8156486
* @run testng/othervm VarHandleTestMethodTypeByte
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false VarHandleTestMethodTypeByte
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeByte
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false VarHandleTestMethodTypeByte
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeByte
*/
import org.testng.annotations.BeforeClass;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,7 +25,9 @@
* @test
* @bug 8156486
* @run testng/othervm VarHandleTestMethodTypeChar
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false VarHandleTestMethodTypeChar
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeChar
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false VarHandleTestMethodTypeChar
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeChar
*/
import org.testng.annotations.BeforeClass;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,7 +25,9 @@
* @test
* @bug 8156486
* @run testng/othervm VarHandleTestMethodTypeDouble
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false VarHandleTestMethodTypeDouble
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeDouble
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false VarHandleTestMethodTypeDouble
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeDouble
*/
import org.testng.annotations.BeforeClass;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,7 +25,9 @@
* @test
* @bug 8156486
* @run testng/othervm VarHandleTestMethodTypeFloat
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false VarHandleTestMethodTypeFloat
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeFloat
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false VarHandleTestMethodTypeFloat
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeFloat
*/
import org.testng.annotations.BeforeClass;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,7 +25,9 @@
* @test
* @bug 8156486
* @run testng/othervm VarHandleTestMethodTypeInt
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false VarHandleTestMethodTypeInt
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeInt
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false VarHandleTestMethodTypeInt
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeInt
*/
import org.testng.annotations.BeforeClass;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,7 +25,9 @@
* @test
* @bug 8156486
* @run testng/othervm VarHandleTestMethodTypeLong
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false VarHandleTestMethodTypeLong
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeLong
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false VarHandleTestMethodTypeLong
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeLong
*/
import org.testng.annotations.BeforeClass;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,7 +25,9 @@
* @test
* @bug 8156486
* @run testng/othervm VarHandleTestMethodTypeShort
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false VarHandleTestMethodTypeShort
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeShort
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false VarHandleTestMethodTypeShort
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeShort
*/
import org.testng.annotations.BeforeClass;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,7 +25,9 @@
* @test
* @bug 8156486
* @run testng/othervm VarHandleTestMethodTypeString
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false VarHandleTestMethodTypeString
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeString
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false VarHandleTestMethodTypeString
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeString
*/
import org.testng.annotations.BeforeClass;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,7 +25,9 @@
* @test
* @bug 8156486
* @run testng/othervm VarHandleTestMethodType$Type$
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false VarHandleTestMethodType$Type$
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodType$Type$
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false VarHandleTestMethodType$Type$
* @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodType$Type$
*/
import org.testng.annotations.BeforeClass;

View File

@ -6,3 +6,6 @@ lib.dirs = /lib/testlibrary/bootlib
# Tests that must run in othervm mode
othervm.dirs= /java/util/stream
# To help out with foreign memory access Spliterator tests
modules = jdk.incubator.foreign

View File

@ -0,0 +1,157 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.tests.java.util.stream;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment;
import java.lang.invoke.VarHandle;
import java.util.Collection;
import java.util.List;
import java.util.SpliteratorTestHelper;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.testng.annotations.DataProvider;
public class SegmentTestDataProvider {
static VarHandle BYTE_HANDLE = MemoryLayouts.JAVA_BYTE.varHandle(byte.class);
static VarHandle CHAR_HANDLE = MemoryLayouts.JAVA_CHAR.varHandle(char.class);
static VarHandle SHORT_HANDLE = MemoryLayouts.JAVA_SHORT.varHandle(short.class);
static VarHandle INT_HANDLE = MemoryLayouts.JAVA_INT.varHandle(int.class);
static VarHandle LONG_HANDLE = MemoryLayouts.JAVA_LONG.varHandle(long.class);
static VarHandle FLOAT_HANDLE = MemoryLayouts.JAVA_FLOAT.varHandle(float.class);
static VarHandle DOUBLE_HANDLE = MemoryLayouts.JAVA_DOUBLE.varHandle(double.class);
static boolean compareSegmentsByte(Collection<MemorySegment> segments1, Collection<MemorySegment> segments2, boolean isOrdered) {
Function<MemorySegment, Byte> mapper = segment -> (byte)BYTE_HANDLE.get(segment.baseAddress());
List<Byte> list1 = segments1.stream()
.map(mapper)
.collect(Collectors.toList());
List<Byte> list2 = segments2.stream()
.map(mapper)
.collect(Collectors.toList());
return list1.equals(list2);
}
static boolean compareSegmentsChar(Collection<MemorySegment> segments1, Collection<MemorySegment> segments2, boolean isOrdered) {
Function<MemorySegment, Character> mapper = segment -> (char)CHAR_HANDLE.get(segment.baseAddress());
List<Character> list1 = segments1.stream()
.map(mapper)
.collect(Collectors.toList());
List<Character> list2 = segments2.stream()
.map(mapper)
.collect(Collectors.toList());
return list1.equals(list2);
}
static boolean compareSegmentsShort(Collection<MemorySegment> segments1, Collection<MemorySegment> segments2, boolean isOrdered) {
Function<MemorySegment, Short> mapper = segment -> (short)SHORT_HANDLE.get(segment.baseAddress());
List<Short> list1 = segments1.stream()
.map(mapper)
.collect(Collectors.toList());
List<Short> list2 = segments2.stream()
.map(mapper)
.collect(Collectors.toList());
return list1.equals(list2);
}
static boolean compareSegmentsInt(Collection<MemorySegment> segments1, Collection<MemorySegment> segments2, boolean isOrdered) {
Function<MemorySegment, Integer> mapper = segment -> (int)INT_HANDLE.get(segment.baseAddress());
List<Integer> list1 = segments1.stream()
.map(mapper)
.collect(Collectors.toList());
List<Integer> list2 = segments2.stream()
.map(mapper)
.collect(Collectors.toList());
return list1.equals(list2);
}
static boolean compareSegmentsLong(Collection<MemorySegment> segments1, Collection<MemorySegment> segments2, boolean isOrdered) {
Function<MemorySegment, Long> mapper = segment -> (long)LONG_HANDLE.get(segment.baseAddress());
List<Long> list1 = segments1.stream()
.map(mapper)
.collect(Collectors.toList());
List<Long> list2 = segments2.stream()
.map(mapper)
.collect(Collectors.toList());
return list1.equals(list2);
}
static boolean compareSegmentsFloat(Collection<MemorySegment> segments1, Collection<MemorySegment> segments2, boolean isOrdered) {
Function<MemorySegment, Float> mapper = segment -> (float)FLOAT_HANDLE.get(segment.baseAddress());
List<Float> list1 = segments1.stream()
.map(mapper)
.collect(Collectors.toList());
List<Float> list2 = segments2.stream()
.map(mapper)
.collect(Collectors.toList());
return list1.equals(list2);
}
static Consumer<MemorySegment> segmentCopier(Consumer<MemorySegment> input) {
return segment -> {
MemorySegment copy = MemorySegment.ofArray(new byte[(int)segment.byteSize()]);
MemoryAddress.copy(segment.baseAddress(), copy.baseAddress(), segment.byteSize());
input.accept(copy);
};
}
static boolean compareSegmentsDouble(Collection<MemorySegment> segments1, Collection<MemorySegment> segments2, boolean isOrdered) {
Function<MemorySegment, Double> mapper = segment -> (double)DOUBLE_HANDLE.get(segment.baseAddress());
List<Double> list1 = segments1.stream()
.map(mapper)
.collect(Collectors.toList());
List<Double> list2 = segments2.stream()
.map(mapper)
.collect(Collectors.toList());
return list1.equals(list2);
}
static void initSegment(MemorySegment segment) {
for (int i = 0 ; i < segment.byteSize() ; i++) {
BYTE_HANDLE.set(segment.baseAddress(), (byte)i);
}
}
static Object[][] spliteratorTestData = {
{ "bytes", MemoryLayout.ofSequence(1024, MemoryLayouts.JAVA_BYTE), (SpliteratorTestHelper.ContentAsserter<MemorySegment>)SegmentTestDataProvider::compareSegmentsByte },
{ "chars", MemoryLayout.ofSequence(1024, MemoryLayouts.JAVA_CHAR), (SpliteratorTestHelper.ContentAsserter<MemorySegment>)SegmentTestDataProvider::compareSegmentsChar },
{ "shorts", MemoryLayout.ofSequence(1024, MemoryLayouts.JAVA_SHORT), (SpliteratorTestHelper.ContentAsserter<MemorySegment>)SegmentTestDataProvider::compareSegmentsShort },
{ "ints", MemoryLayout.ofSequence(1024, MemoryLayouts.JAVA_INT), (SpliteratorTestHelper.ContentAsserter<MemorySegment>)SegmentTestDataProvider::compareSegmentsInt },
{ "longs", MemoryLayout.ofSequence(1024, MemoryLayouts.JAVA_LONG), (SpliteratorTestHelper.ContentAsserter<MemorySegment>)SegmentTestDataProvider::compareSegmentsLong },
{ "floats", MemoryLayout.ofSequence(1024, MemoryLayouts.JAVA_FLOAT), (SpliteratorTestHelper.ContentAsserter<MemorySegment>)SegmentTestDataProvider::compareSegmentsFloat },
{ "doubles", MemoryLayout.ofSequence(1024, MemoryLayouts.JAVA_DOUBLE), (SpliteratorTestHelper.ContentAsserter<MemorySegment>)SegmentTestDataProvider::compareSegmentsDouble },
};
// returns an array of (String name, Supplier<Spliterator<MemorySegment>>, ContentAsserter<MemorySegment>)
@DataProvider(name = "SegmentSpliterator")
public static Object[][] spliteratorProvider() {
return spliteratorTestData;
}
}

View File

@ -22,11 +22,10 @@
*/
package org.openjdk.tests.java.util.stream;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.SequenceLayout;
import org.testng.annotations.Test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Supplier;
import java.util.Spliterator;
import java.util.SpliteratorTestHelper;
@ -62,4 +61,13 @@ public class SpliteratorTest {
public void testDoubleSpliterator(String name, Supplier<Spliterator.OfDouble> supplier) {
SpliteratorTestHelper.testDoubleSpliterator(supplier);
}
@Test(dataProvider = "SegmentSpliterator", dataProviderClass = SegmentTestDataProvider.class )
public void testSegmentSpliterator(String name, SequenceLayout layout, SpliteratorTestHelper.ContentAsserter<MemorySegment> contentAsserter) {
try (MemorySegment segment = MemorySegment.allocateNative(layout)) {
SegmentTestDataProvider.initSegment(segment);
SpliteratorTestHelper.testSpliterator(() -> MemorySegment.spliterator(segment, layout),
SegmentTestDataProvider::segmentCopier, contentAsserter);
}
}
}

View File

@ -0,0 +1,138 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.jdk.incubator.foreign;
import jdk.incubator.foreign.MemoryLayout;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.CompilerControl;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Warmup;
import sun.misc.Unsafe;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import java.lang.invoke.VarHandle;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.TimeUnit;
import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement;
import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT;
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@State(org.openjdk.jmh.annotations.Scope.Thread)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(3)
public class LoopOverConstant {
static final Unsafe unsafe = Utils.unsafe;
static final int ELEM_SIZE = 1_000_000;
static final int CARRIER_SIZE = (int)JAVA_INT.byteSize();
static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE;
static final long unsafe_addr = unsafe.allocateMemory(ALLOC_SIZE);
//setup unsafe address
static {
for (int i = 0; i < ELEM_SIZE; i++) {
unsafe.putInt(unsafe_addr + (i * CARRIER_SIZE) , i);
}
}
//setup native memory segment
static final MemoryAddress segment_addr = MemorySegment.allocateNative(ALLOC_SIZE).baseAddress();
static final VarHandle VH_int = MemoryLayout.ofSequence(JAVA_INT).varHandle(int.class, sequenceElement());
static {
for (int i = 0; i < ELEM_SIZE; i++) {
VH_int.set(segment_addr, (long) i, i);
}
}
//setup direct buffer
static final ByteBuffer bb = ByteBuffer.allocateDirect(ALLOC_SIZE).order(ByteOrder.nativeOrder());
static {
for (int i = 0; i < ELEM_SIZE; i++) {
bb.putInt(i * CARRIER_SIZE , i);
}
}
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public int unsafe_get() {
return unsafe.getInt(unsafe_addr);
}
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public int segment_get() {
return (int)VH_int.get(segment_addr, 0L);
}
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public int BB_get() {
return bb.getInt(0);
}
@Benchmark
public int unsafe_loop() {
int res = 0;
for (int i = 0; i < ELEM_SIZE; i ++) {
res += unsafe.getInt(unsafe_addr + (i * CARRIER_SIZE));
}
return res;
}
@Benchmark
public int segment_loop() {
int res = 0;
for (int i = 0; i < ELEM_SIZE; i++) {
res += (int) VH_int.get(segment_addr, (long)i);
}
return res;
}
@Benchmark
public int BB_loop() {
int res = 0;
for (int i = 0; i < ELEM_SIZE; i++) {
res += bb.getInt(i * CARRIER_SIZE);
}
return res;
}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.jdk.incubator.foreign;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Warmup;
import sun.misc.Unsafe;
import java.lang.invoke.VarHandle;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.TimeUnit;
import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement;
import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT;
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@State(org.openjdk.jmh.annotations.Scope.Thread)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(3)
public class LoopOverNew {
static final Unsafe unsafe = Utils.unsafe;
static final int ELEM_SIZE = 1_000_000;
static final int CARRIER_SIZE = (int)JAVA_INT.byteSize();
static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE;
static final VarHandle VH_int = MemoryLayout.ofSequence(JAVA_INT).varHandle(int.class, sequenceElement());
@Benchmark
public void unsafe_loop() {
long unsafe_addr = unsafe.allocateMemory(ALLOC_SIZE);
for (int i = 0; i < ELEM_SIZE; i++) {
unsafe.putInt(unsafe_addr + (i * CARRIER_SIZE) , i);
}
unsafe.freeMemory(unsafe_addr);
}
@Benchmark
public void segment_loop() {
MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE);
for (int i = 0; i < ELEM_SIZE; i++) {
VH_int.set(segment.baseAddress(), (long) i, i);
}
segment.close();
}
@Benchmark
public void buffer_loop() {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(ALLOC_SIZE).order(ByteOrder.nativeOrder());
for (int i = 0; i < ELEM_SIZE; i++) {
byteBuffer.putInt(i * CARRIER_SIZE , i);
}
unsafe.invokeCleaner(byteBuffer);
}
}

View File

@ -0,0 +1,158 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.jdk.incubator.foreign;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Warmup;
import sun.misc.Unsafe;
import java.lang.invoke.VarHandle;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.TimeUnit;
import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement;
import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT;
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@State(org.openjdk.jmh.annotations.Scope.Thread)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(3)
public class LoopOverNonConstant {
static final Unsafe unsafe = Utils.unsafe;
static final int ELEM_SIZE = 1_000_000;
static final int CARRIER_SIZE = (int)JAVA_INT.byteSize();
static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE;
static final VarHandle VH_int = MemoryLayout.ofSequence(JAVA_INT).varHandle(int.class, sequenceElement());
MemorySegment segment;
long unsafe_addr;
ByteBuffer byteBuffer;
@Setup
public void setup() {
unsafe_addr = unsafe.allocateMemory(ALLOC_SIZE);
for (int i = 0; i < ELEM_SIZE; i++) {
unsafe.putInt(unsafe_addr + (i * CARRIER_SIZE) , i);
}
segment = MemorySegment.allocateNative(ALLOC_SIZE);
for (int i = 0; i < ELEM_SIZE; i++) {
VH_int.set(segment.baseAddress(), (long) i, i);
}
byteBuffer = ByteBuffer.allocateDirect(ALLOC_SIZE).order(ByteOrder.nativeOrder());
for (int i = 0; i < ELEM_SIZE; i++) {
byteBuffer.putInt(i * CARRIER_SIZE , i);
}
}
@TearDown
public void tearDown() {
segment.close();
unsafe.invokeCleaner(byteBuffer);
unsafe.freeMemory(unsafe_addr);
}
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public int unsafe_get() {
return unsafe.getInt(unsafe_addr);
}
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public int segment_get() {
return (int) VH_int.get(segment.baseAddress(), 0L);
}
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public int BB_get() {
return byteBuffer.getInt(0);
}
@Benchmark
public int unsafe_loop() {
int res = 0;
for (int i = 0; i < ELEM_SIZE; i ++) {
res += unsafe.getInt(unsafe_addr + (i * CARRIER_SIZE));
}
return res;
}
@Benchmark
public int segment_loop() {
int sum = 0;
MemoryAddress base = segment.baseAddress();
for (int i = 0; i < ELEM_SIZE; i++) {
sum += (int) VH_int.get(base, (long) i);
}
return sum;
}
@Benchmark
public int segment_loop_slice() {
int sum = 0;
MemoryAddress base = segment.asSlice(0, segment.byteSize()).baseAddress();
for (int i = 0; i < ELEM_SIZE; i++) {
sum += (int) VH_int.get(base, (long) i);
}
return sum;
}
@Benchmark
public int segment_loop_readonly() {
int sum = 0;
MemoryAddress base = segment.withAccessModes(MemorySegment.READ).baseAddress();
for (int i = 0; i < ELEM_SIZE; i++) {
sum += (int) VH_int.get(base, (long) i);
}
return sum;
}
@Benchmark
public int BB_loop() {
int sum = 0;
ByteBuffer bb = byteBuffer;
for (int i = 0; i < ELEM_SIZE; i++) {
sum += bb.getInt(i * CARRIER_SIZE);
}
return sum;
}
}

View File

@ -0,0 +1,151 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.jdk.incubator.foreign;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Warmup;
import sun.misc.Unsafe;
import java.lang.invoke.VarHandle;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.TimeUnit;
import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement;
import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT;
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@State(org.openjdk.jmh.annotations.Scope.Thread)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(3)
public class LoopOverNonConstantHeap {
static final Unsafe unsafe = Utils.unsafe;
static final int ELEM_SIZE = 1_000_000;
static final int CARRIER_SIZE = (int)JAVA_INT.byteSize();
static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE;
static final int UNSAFE_BYTE_BASE = unsafe.arrayBaseOffset(byte[].class);
static final VarHandle VH_int = MemoryLayout.ofSequence(JAVA_INT).varHandle(int.class, sequenceElement());
MemorySegment segment;
byte[] base;
ByteBuffer byteBuffer;
@Setup
public void setup() {
base = new byte[ALLOC_SIZE];
for (int i = 0; i < ELEM_SIZE; i++) {
unsafe.putInt(base, UNSAFE_BYTE_BASE + (i * CARRIER_SIZE) , i);
}
segment = MemorySegment.ofArray(base);
byteBuffer = ByteBuffer.wrap(base).order(ByteOrder.nativeOrder());
}
@TearDown
public void tearDown() {
segment.close();
}
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public int unsafe_get() {
return unsafe.getInt(base, UNSAFE_BYTE_BASE);
}
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public int segment_get() {
return (int) VH_int.get(segment.baseAddress(), 0L);
}
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public int BB_get() {
return byteBuffer.getInt(0);
}
@Benchmark
public int unsafe_loop() {
int res = 0;
for (int i = 0; i < ELEM_SIZE; i ++) {
res += unsafe.getInt(base, UNSAFE_BYTE_BASE + (i * CARRIER_SIZE));
}
return res;
}
@Benchmark
public int segment_loop() {
int sum = 0;
MemoryAddress base = segment.baseAddress();
for (int i = 0; i < ELEM_SIZE; i++) {
sum += (int) VH_int.get(base, (long) i);
}
return sum;
}
@Benchmark
public int segment_loop_slice() {
int sum = 0;
MemoryAddress base = segment.asSlice(0, segment.byteSize()).baseAddress();
for (int i = 0; i < ELEM_SIZE; i++) {
sum += (int) VH_int.get(base, (long) i);
}
return sum;
}
@Benchmark
public int segment_loop_readonly() {
int sum = 0;
MemoryAddress base = segment.withAccessModes(MemorySegment.READ).baseAddress();
for (int i = 0; i < ELEM_SIZE; i++) {
sum += (int) VH_int.get(base, (long) i);
}
return sum;
}
@Benchmark
public int BB_loop() {
int sum = 0;
ByteBuffer bb = byteBuffer;
for (int i = 0; i < ELEM_SIZE; i++) {
sum += bb.getInt(i * CARRIER_SIZE);
}
return sum;
}
}

View File

@ -0,0 +1,175 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.jdk.incubator.foreign;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Warmup;
import sun.misc.Unsafe;
import java.io.File;
import java.io.IOException;
import java.lang.invoke.VarHandle;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.TimeUnit;
import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement;
import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT;
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@State(org.openjdk.jmh.annotations.Scope.Thread)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(3)
public class LoopOverNonConstantMapped {
static final Unsafe unsafe = Utils.unsafe;
static final int ELEM_SIZE = 1_000_000;
static final int CARRIER_SIZE = (int)JAVA_INT.byteSize();
static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE;
static final Path tempPath;
static {
try {
File file = File.createTempFile("buffer", "txt");
file.deleteOnExit();
tempPath = file.toPath();
Files.write(file.toPath(), new byte[ALLOC_SIZE], StandardOpenOption.WRITE);
} catch (IOException ex) {
throw new ExceptionInInitializerError(ex);
}
}
static final VarHandle VH_int = MemoryLayout.ofSequence(JAVA_INT).varHandle(int.class, sequenceElement());
MemorySegment segment;
long unsafe_addr;
ByteBuffer byteBuffer;
@Setup
public void setup() throws IOException {
try (FileChannel channel = FileChannel.open(tempPath, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
byteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, ALLOC_SIZE).order(ByteOrder.nativeOrder());
for (int i = 0; i < ELEM_SIZE; i++) {
byteBuffer.putInt(i * CARRIER_SIZE, i);
}
((MappedByteBuffer)byteBuffer).force();
}
segment = MemorySegment.mapFromPath(tempPath, ALLOC_SIZE, FileChannel.MapMode.READ_WRITE);
unsafe_addr = segment.baseAddress().toRawLongValue();
}
@TearDown
public void tearDown() {
segment.close();
unsafe.invokeCleaner(byteBuffer);
}
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public int unsafe_get() {
return unsafe.getInt(unsafe_addr);
}
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public int segment_get() {
return (int) VH_int.get(segment.baseAddress(), 0L);
}
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public int BB_get() {
return byteBuffer.getInt(0);
}
@Benchmark
public int unsafe_loop() {
int res = 0;
for (int i = 0; i < ELEM_SIZE; i ++) {
res += unsafe.getInt(unsafe_addr + (i * CARRIER_SIZE));
}
return res;
}
@Benchmark
public int segment_loop() {
int sum = 0;
MemoryAddress base = segment.baseAddress();
for (int i = 0; i < ELEM_SIZE; i++) {
sum += (int) VH_int.get(base, (long) i);
}
return sum;
}
@Benchmark
public int segment_loop_slice() {
int sum = 0;
MemoryAddress base = segment.asSlice(0, segment.byteSize()).baseAddress();
for (int i = 0; i < ELEM_SIZE; i++) {
sum += (int) VH_int.get(base, (long) i);
}
return sum;
}
@Benchmark
public int segment_loop_readonly() {
int sum = 0;
MemoryAddress base = segment.withAccessModes(MemorySegment.READ).baseAddress();
for (int i = 0; i < ELEM_SIZE; i++) {
sum += (int) VH_int.get(base, (long) i);
}
return sum;
}
@Benchmark
public int BB_loop() {
int sum = 0;
ByteBuffer bb = byteBuffer;
for (int i = 0; i < ELEM_SIZE; i++) {
sum += bb.getInt(i * CARRIER_SIZE);
}
return sum;
}
}

View File

@ -0,0 +1,276 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.jdk.incubator.foreign;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.SequenceLayout;
import sun.misc.Unsafe;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Warmup;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import java.lang.invoke.VarHandle;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.Spliterator;
import java.util.concurrent.CountedCompleter;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.TimeUnit;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.function.ToIntFunction;
import java.util.stream.StreamSupport;
import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement;
import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT;
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@State(org.openjdk.jmh.annotations.Scope.Thread)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(3)
public class ParallelSum {
final static int CARRIER_SIZE = 4;
final static int ALLOC_SIZE = CARRIER_SIZE * 1024 * 1024 * 256;
final static int ELEM_SIZE = ALLOC_SIZE / CARRIER_SIZE;
static final VarHandle VH_int = MemoryLayout.ofSequence(JAVA_INT).varHandle(int.class, sequenceElement());
final static SequenceLayout SEQUENCE_LAYOUT = MemoryLayout.ofSequence(ELEM_SIZE, MemoryLayouts.JAVA_INT);
final static int BULK_FACTOR = 512;
final static SequenceLayout SEQUENCE_LAYOUT_BULK = SEQUENCE_LAYOUT.reshape(-1, BULK_FACTOR);
static final Unsafe unsafe = Utils.unsafe;
MemorySegment segment;
long address;
@Setup
public void setup() {
address = unsafe.allocateMemory(ALLOC_SIZE);
for (int i = 0; i < ELEM_SIZE; i++) {
unsafe.putInt(address + (i * CARRIER_SIZE), i);
}
segment = MemorySegment.allocateNative(ALLOC_SIZE);
for (int i = 0; i < ELEM_SIZE; i++) {
VH_int.set(segment.baseAddress(), (long) i, i);
}
}
@TearDown
public void tearDown() throws Throwable {
unsafe.freeMemory(address);
segment.close();
}
@Benchmark
public int segment_serial() {
int res = 0;
MemoryAddress base = segment.baseAddress();
for (int i = 0; i < ELEM_SIZE; i++) {
res += (int)VH_int.get(base, (long) i);
}
return res;
}
@Benchmark
public int unsafe_serial() {
int res = 0;
for (int i = 0; i < ELEM_SIZE; i++) {
res += unsafe.getInt(address + (i * CARRIER_SIZE));
}
return res;
}
@Benchmark
public int segment_parallel() {
return new SumSegment(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT), SEGMENT_TO_INT).invoke();
}
@Benchmark
public int segment_parallel_bulk() {
return new SumSegment(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT_BULK), SEGMENT_TO_INT_BULK).invoke();
}
@Benchmark
public int segment_stream_parallel() {
return StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT), true)
.mapToInt(SEGMENT_TO_INT).sum();
}
@Benchmark
public int segment_stream_parallel_bulk() {
return StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT_BULK), true)
.mapToInt(SEGMENT_TO_INT_BULK).sum();
}
final static ToIntFunction<MemorySegment> SEGMENT_TO_INT = slice ->
(int) VH_int.get(slice.baseAddress(), 0L);
final static ToIntFunction<MemorySegment> SEGMENT_TO_INT_BULK = slice -> {
int res = 0;
MemoryAddress base = slice.baseAddress();
for (int i = 0; i < BULK_FACTOR ; i++) {
res += (int)VH_int.get(base, (long) i);
}
return res;
};
@Benchmark
public Optional<MemorySegment> segment_stream_findany_serial() {
return StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT), false)
.filter(FIND_SINGLE)
.findAny();
}
@Benchmark
public Optional<MemorySegment> segment_stream_findany_parallel() {
return StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT), true)
.filter(FIND_SINGLE)
.findAny();
}
@Benchmark
public Optional<MemorySegment> segment_stream_findany_serial_bulk() {
return StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT_BULK), false)
.filter(FIND_BULK)
.findAny();
}
@Benchmark
public Optional<MemorySegment> segment_stream_findany_parallel_bulk() {
return StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT_BULK), true)
.filter(FIND_BULK)
.findAny();
}
final static Predicate<MemorySegment> FIND_SINGLE = slice ->
(int)VH_int.get(slice.baseAddress(), 0L) == (ELEM_SIZE - 1);
final static Predicate<MemorySegment> FIND_BULK = slice -> {
MemoryAddress base = slice.baseAddress();
for (int i = 0; i < BULK_FACTOR ; i++) {
if ((int)VH_int.get(base, (long)i) == (ELEM_SIZE - 1)) {
return true;
}
}
return false;
};
@Benchmark
public int unsafe_parallel() {
return new SumUnsafe(address, 0, ALLOC_SIZE).invoke();
}
static class SumUnsafe extends RecursiveTask<Integer> {
final static int SPLIT_THRESHOLD = 4 * 1024 * 8;
private final long address;
private final int start, length;
SumUnsafe(long address, int start, int length) {
this.address = address;
this.start = start;
this.length = length;
}
@Override
protected Integer compute() {
if (length > SPLIT_THRESHOLD) {
SumUnsafe s1 = new SumUnsafe(address, start, length / 2);
SumUnsafe s2 = new SumUnsafe(address, length / 2, length / 2);
s1.fork();
s2.fork();
return s1.join() + s2.join();
} else {
int res = 0;
for (int i = 0; i < length; i += CARRIER_SIZE) {
res += unsafe.getInt(start + address + i);
}
return res;
}
}
}
static class SumSegment extends CountedCompleter<Integer> {
final static int SPLIT_THRESHOLD = 1024 * 8;
int localSum = 0;
private final ToIntFunction<MemorySegment> mapper;
List<SumSegment> children = new LinkedList<>();
private Spliterator<MemorySegment> segmentSplitter;
SumSegment(Spliterator<MemorySegment> segmentSplitter, ToIntFunction<MemorySegment> mapper) {
this(null, segmentSplitter, mapper);
}
SumSegment(SumSegment parent, Spliterator<MemorySegment> segmentSplitter, ToIntFunction<MemorySegment> mapper) {
super(parent);
this.segmentSplitter = segmentSplitter;
this.mapper = mapper;
}
@Override
public void compute() {
Spliterator<MemorySegment> sub;
while (segmentSplitter.estimateSize() > SPLIT_THRESHOLD &&
(sub = segmentSplitter.trySplit()) != null) {
addToPendingCount(1);
SumSegment child = new SumSegment(this, sub, mapper);
children.add(child);
child.fork();
}
segmentSplitter.forEachRemaining(s -> {
localSum += mapper.applyAsInt(s);
});
propagateCompletion();
}
@Override
public Integer getRawResult() {
int sum = localSum;
for (SumSegment c : children) {
sum += c.getRawResult();
}
children = null;
return sum;
}
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.jdk.incubator.foreign;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class Utils {
static final Unsafe unsafe;
//setup unsafe
static {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
unsafe = (Unsafe) f.get(null);
} catch (ReflectiveOperationException ex) {
throw new IllegalStateException();
}
}
}

View File

@ -0,0 +1,126 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.jdk.incubator.foreign.points;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.bench.jdk.incubator.foreign.points.support.BBPoint;
import org.openjdk.bench.jdk.incubator.foreign.points.support.JNIPoint;
import org.openjdk.bench.jdk.incubator.foreign.points.support.PanamaPoint;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@State(org.openjdk.jmh.annotations.Scope.Thread)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(3)
public class PointsAccess {
BBPoint BBPoint;
PanamaPoint panamaPoint;
JNIPoint JNIPoint;
@Setup
public void setup() {
BBPoint = new BBPoint(0, 0);
panamaPoint = new PanamaPoint(0, 0);
JNIPoint = new JNIPoint(0, 0);
}
@TearDown
public void tearDown() {
JNIPoint.free();
panamaPoint.close();
}
@Benchmark
public void BB_set() throws Throwable {
BBPoint.setX(10);
}
@Benchmark
public int BB_get() throws Throwable {
return BBPoint.getX();
}
@Benchmark
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public int BB_get_loop() throws Throwable {
int sum = 0;
for (int i = 0; i < 1_000_000; i++) {
sum += BBPoint.getX();
}
return sum;
}
@Benchmark
public void jni_set() throws Throwable {
JNIPoint.setX(10);
}
@Benchmark
public int jni_get() throws Throwable {
return JNIPoint.getX();
}
@Benchmark
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public int jni_get_loop() throws Throwable {
int sum = 0;
for (int i = 0; i < 1_000_000; i++) {
sum += JNIPoint.getX();
}
return sum;
}
@Benchmark
public void panama_set() throws Throwable {
panamaPoint.setX(10);
}
@Benchmark
public int panama_get() throws Throwable {
return panamaPoint.getX();
}
@Benchmark
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public int panama_get_loop() throws Throwable {
int sum = 0;
for (int i = 0; i < 1_000_000; i++) {
sum += panamaPoint.getX();
}
return sum;
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.jdk.incubator.foreign.points;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.bench.jdk.incubator.foreign.points.support.BBPoint;
import org.openjdk.bench.jdk.incubator.foreign.points.support.JNIPoint;
import org.openjdk.bench.jdk.incubator.foreign.points.support.PanamaPoint;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@State(org.openjdk.jmh.annotations.Scope.Thread)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(3)
public class PointsAlloc {
@Benchmark
public Object jni_ByteBuffer_alloc() throws Throwable {
return new BBPoint(0, 0);
}
@Benchmark
public Object jni_long_alloc() throws Throwable {
return new JNIPoint(0, 0);
}
@Benchmark
public Object panama_alloc() throws Throwable {
return new PanamaPoint(0, 0);
}
}

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.jdk.incubator.foreign.points;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.bench.jdk.incubator.foreign.points.support.JNIPoint;
import org.openjdk.bench.jdk.incubator.foreign.points.support.PanamaPoint;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@State(org.openjdk.jmh.annotations.Scope.Thread)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(3)
public class PointsFree {
JNIPoint jniPoint;
PanamaPoint panamaPoint;
@Setup(Level.Invocation)
public void setup() {
jniPoint = new JNIPoint(0, 0);
panamaPoint = new PanamaPoint(0, 0);
}
@Benchmark
public void jni_long_free() throws Throwable {
jniPoint.close();
}
@Benchmark
public void panama_free() throws Throwable {
panamaPoint.close();
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.jdk.incubator.foreign.points.support;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class BBPoint {
private final ByteBuffer buff;
public BBPoint(int x, int y) {
this.buff = ByteBuffer.allocateDirect(4 * 2).order(ByteOrder.nativeOrder());
setX(x);
setY(y);
}
public void setX(int x) {
buff.putInt(0, x);
}
public int getX() {
return buff.getInt(0);
}
public int getY() {
return buff.getInt(1);
}
public void setY(int y) {
buff.putInt(0, y);
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.jdk.incubator.foreign.points.support;
public class JNIPoint implements AutoCloseable {
static {
System.loadLibrary("JNIPoint");
}
private final long peer;
public JNIPoint(int x, int y) {
peer = allocate();
setX(peer, x);
setY(peer, y);
}
public void free() {
free(peer);
}
public int getX() {
return getX(peer);
}
public void setX(int value) {
setX(peer, value);
}
public int getY() {
return getY(peer);
}
public void setY(int value) {
setY(peer, value);
}
private static native long allocate();
private static native void free(long ptr);
private static native int getX(long ptr);
private static native void setX(long ptr, int x);
private static native int getY(long ptr);
private static native void setY(long ptr, int y);
@Override
public void close() {
free();
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.jdk.incubator.foreign.points.support;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
import static jdk.incubator.foreign.MemoryLayout.PathElement.groupElement;
public class PanamaPoint implements AutoCloseable {
public static final MemoryLayout LAYOUT = MemoryLayout.ofStruct(
MemoryLayouts.JAVA_INT.withOrder(ByteOrder.nativeOrder()).withName("x"),
MemoryLayouts.JAVA_INT.withOrder(ByteOrder.nativeOrder()).withName("y")
);
private static final VarHandle VH_x = LAYOUT.varHandle(int.class, groupElement("x"));
private static final VarHandle VH_y = LAYOUT.varHandle(int.class, groupElement("y"));
private final MemorySegment segment;
public PanamaPoint(int x, int y) {
this(MemorySegment.allocateNative(LAYOUT), x, y);
}
public PanamaPoint(MemorySegment segment, int x, int y) {
this(segment);
setX(x);
setY(y);
}
public PanamaPoint(MemorySegment segment) {
this.segment = segment;
}
public void setX(int x) {
VH_x.set(segment.baseAddress(), x);
}
public int getX() {
return (int) VH_x.get(segment.baseAddress());
}
public void setY(int y) {
VH_y.set(segment.baseAddress(), y);
}
public int getY() {
return (int) VH_y.get(segment.baseAddress());
}
@Override
public void close() {
segment.close();
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <jni.h>
#include <stdlib.h>
#include "points.h"
JNIEXPORT jlong JNICALL Java_org_openjdk_bench_jdk_incubator_foreign_points_support_JNIPoint_allocate
(JNIEnv *env, jclass nativePointClass) {
Point* p = malloc(sizeof *p);
return (jlong) p;
}
JNIEXPORT void JNICALL Java_org_openjdk_bench_jdk_incubator_foreign_points_support_JNIPoint_free
(JNIEnv *env, jclass cls, jlong thisPoint) {
free((Point*) thisPoint);
}
JNIEXPORT jint JNICALL Java_org_openjdk_bench_jdk_incubator_foreign_points_support_JNIPoint_getX
(JNIEnv *env, jclass cls, jlong thisPoint) {
Point* point = (Point*) thisPoint;
return point->x;
}
JNIEXPORT void JNICALL Java_org_openjdk_bench_jdk_incubator_foreign_points_support_JNIPoint_setX
(JNIEnv *env, jclass cls, jlong thisPoint, jint value) {
Point* point = (Point*) thisPoint;
point->x = value;
}
JNIEXPORT jint JNICALL Java_org_openjdk_bench_jdk_incubator_foreign_points_support_JNIPoint_getY
(JNIEnv *env, jclass cls, jlong thisPoint) {
Point* point = (Point*) thisPoint;
return point->y;
}
JNIEXPORT void JNICALL Java_org_openjdk_bench_jdk_incubator_foreign_points_support_JNIPoint_setY
(JNIEnv *env, jclass cls, jlong thisPoint, jint value) {
Point* point = (Point*) thisPoint;
point->y = value;
}

View File

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