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:
parent
9b94b9d1a1
commit
f3eb44a94d
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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" +
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -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(
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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)
|
||||
//
|
||||
|
@ -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
|
||||
|
||||
/**
|
||||
|
156
src/java.base/share/classes/java/nio/MappedMemoryUtils.java
Normal file
156
src/java.base/share/classes/java/nio/MappedMemoryUtils.java
Normal 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
@ -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);
|
@ -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));
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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™ 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™ 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.
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
};
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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 + "'");
|
||||
}
|
||||
}
|
||||
|
464
test/jdk/java/foreign/TestAdaptVarHandles.java
Normal file
464
test/jdk/java/foreign/TestAdaptVarHandles.java
Normal 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) { }
|
||||
}
|
162
test/jdk/java/foreign/TestAddressHandle.java
Normal file
162
test/jdk/java/foreign/TestAddressHandle.java
Normal 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);
|
||||
}
|
||||
}
|
@ -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 =
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
83
test/jdk/java/foreign/TestLayoutAttributes.java
Normal file
83
test/jdk/java/foreign/TestLayoutAttributes.java
Normal 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));
|
||||
}
|
||||
|
||||
}
|
@ -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") },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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");
|
||||
}
|
||||
|
45
test/jdk/java/foreign/TestNoForeignUnsafeOverride.java
Normal file
45
test/jdk/java/foreign/TestNoForeignUnsafeOverride.java
Normal 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);
|
||||
}
|
||||
}
|
150
test/jdk/java/foreign/TestRebase.java
Normal file
150
test/jdk/java/foreign/TestRebase.java
Normal 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;
|
||||
}
|
||||
}
|
198
test/jdk/java/foreign/TestReshape.java
Normal file
198
test/jdk/java/foreign/TestReshape.java
Normal 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 } },
|
||||
};
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
257
test/jdk/java/foreign/TestSpliterator.java
Normal file
257
test/jdk/java/foreign/TestSpliterator.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user