--- old/src/java.base/share/classes/jdk/internal/misc/Unsafe.java 2016-03-01 08:25:36.122002863 -0800 +++ new/src/java.base/share/classes/jdk/internal/misc/Unsafe.java 2016-03-01 08:25:36.018001059 -0800 @@ -40,6 +40,15 @@ * Although the class and all methods are public, use of this class is * limited because only trusted code can obtain instances of it. * + * Note: It is the resposibility of the caller to make sure + * arguments are checked before methods of this class are + * called. While some rudimentary checks are performed on the input, + * the checks are best effort and when performance is an overriding + * priority, as when methods of this class are optimized by the + * runtime compiler, some or all checks (if any) may be elided. Hence, + * the caller must not rely on the checks and corresponding + * exceptions! + * * @author John R. Rose * @see #getUnsafe */ @@ -358,6 +367,169 @@ @HotSpotIntrinsicCandidate public native void putAddress(long address, long x); + + + /// helper methods for validating various types of objects/values + + /** + * Create an exception reflecting that some of the input was invalid + * + * Note: It is the resposibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @return an exception object + */ + private RuntimeException invalidInput() { + return new IllegalArgumentException(); + } + + /** + * Check if a value is 32-bit clean (32 MSB are all zero) + * + * @param value the 64-bit value to check + * + * @return true if the value is 32-bit clean + */ + private boolean is32BitClean(long value) { + return value >>> 32 == 0; + } + + /** + * Check the validity of a size (the equivalent of a size_t) + * + * @throws RuntimeException if the size is invalid + * (Note: after optimization, invalid inputs may + * go undetected, which will lead to unpredictable + * behavior) + */ + private void checkSize(long size) { + if (ADDRESS_SIZE == 4) { + // Note: this will also check for negative sizes + if (!is32BitClean(size)) { + throw invalidInput(); + } + } else if (size < 0) { + throw invalidInput(); + } + } + + /** + * Check the validity of a native address (the equivalent of void*) + * + * @throws RuntimeException if the address is invalid + * (Note: after optimization, invalid inputs may + * go undetected, which will lead to unpredictable + * behavior) + */ + private void checkNativeAddress(long address) { + if (ADDRESS_SIZE == 4) { + // Accept both zero and sign extended pointers. A valid + // pointer will, after the +1 below, either have produced + // the value 0x0 or 0x1. Masking off the low bit allows + // for testing against 0. + if ((((address >> 32) + 1) & ~1) != 0) { + throw invalidInput(); + } + } + } + + /** + * Check the validity of an offset, relative to a base object + * + * @param o the base object + * @param offset the offset to check + * + * @throws RuntimeException if the size is invalid + * (Note: after optimization, invalid inputs may + * go undetected, which will lead to unpredictable + * behavior) + */ + private void checkOffset(Object o, long offset) { + if (ADDRESS_SIZE == 4) { + // Note: this will also check for negative offsets + if (!is32BitClean(offset)) { + throw invalidInput(); + } + } else if (offset < 0) { + throw invalidInput(); + } + } + + /** + * Check the validity of a double-register pointer + * + * Note: This code deliberately does *not* check for NPE for (at + * least) three reasons: + * + * 1) NPE is not just NULL/0 - there is a range of values all + * resulting in an NPE, which is not trivial to check for + * + * 2) It is the responsibility of the callers of Unsafe methods + * to verify the input, so throwing an exception here is not really + * useful - passing in a NULL pointer is a critical error and the + * must not expect an exception to be thrown anyway. + * + * 3) the actual operations will detect NULL pointers anyway by + * means of traps and signals (like SIGSEGV). + * + * @param o Java heap object, or null + * @param offset indication of where the variable resides in a Java heap + * object, if any, else a memory address locating the variable + * statically + * + * @throws RuntimeException if the pointer is invalid + * (Note: after optimization, invalid inputs may + * go undetected, which will lead to unpredictable + * behavior) + */ + private void checkPointer(Object o, long offset) { + if (o == null) { + checkNativeAddress(offset); + } else { + checkOffset(o, offset); + } + } + + /** + * Check if a type is a primitive array type + * + * @param c the type to check + * + * @return true if the type is a primitive array type + */ + private void checkPrimitiveArray(Class c) { + Class componentType = c.getComponentType(); + if (componentType == null || !componentType.isPrimitive()) { + throw invalidInput(); + } + } + + /** + * Check that a pointer is a valid primitive array type pointer + * + * Note: pointers off-heap are considered to be primitive arrays + * + * @throws RuntimeException if the pointer is invalid + * (Note: after optimization, invalid inputs may + * go undetected, which will lead to unpredictable + * behavior) + */ + private void checkPrimitivePointer(Object o, long offset) { + checkPointer(o, offset); + + if (o != null) { + // If on heap, it it must be a primitive array + checkPrimitiveArray(o.getClass()); + } + } + + /// wrappers for malloc, realloc, free: /** @@ -367,7 +539,16 @@ * aligned for all value types. Dispose of this memory by calling {@link * #freeMemory}, or resize it with {@link #reallocateMemory}. * - * @throws IllegalArgumentException if the size is negative or too large + * Note: It is the resposibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @throws RuntimeException if the size is negative or too large * for the native size_t type * * @throws OutOfMemoryError if the allocation is refused by the system @@ -375,7 +556,32 @@ * @see #getByte(long) * @see #putByte(long, byte) */ - public native long allocateMemory(long bytes); + public long allocateMemory(long bytes) { + allocateMemoryChecks(bytes); + + if (bytes == 0) { + return 0; + } + + long p = allocateMemory0(bytes); + if (p == 0) { + throw new OutOfMemoryError(); + } + + return p; + } + + /** + * Validate the arguments to allocateMemory + * + * @throws RuntimeException if the arguments are invalid + * (Note: after optimization, invalid inputs may + * go undetected, which will lead to unpredictable + * behavior) + */ + private void allocateMemoryChecks(long bytes) { + checkSize(bytes); + } /** * Resizes a new block of native memory, to the given size in bytes. The @@ -387,14 +593,50 @@ * #reallocateMemory}. The address passed to this method may be null, in * which case an allocation will be performed. * - * @throws IllegalArgumentException if the size is negative or too large + * Note: It is the resposibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @throws RuntimeException if the size is negative or too large * for the native size_t type * * @throws OutOfMemoryError if the allocation is refused by the system * * @see #allocateMemory */ - public native long reallocateMemory(long address, long bytes); + public long reallocateMemory(long address, long bytes) { + reallocateMemoryChecks(address, bytes); + + if (bytes == 0) { + freeMemory(address); + return 0; + } + + long p = (address == 0) ? allocateMemory0(bytes) : reallocateMemory0(address, bytes); + if (p == 0) { + throw new OutOfMemoryError(); + } + + return p; + } + + /** + * Validate the arguments to reallocateMemory + * + * @throws RuntimeException if the arguments are invalid + * (Note: after optimization, invalid inputs may + * go undetected, which will lead to unpredictable + * behavior) + */ + private void reallocateMemoryChecks(long address, long bytes) { + checkPointer(null, address); + checkSize(bytes); + } /** * Sets all bytes in a given block of memory to a fixed value @@ -411,9 +653,28 @@ * If the effective address and length are (resp.) even modulo 4 or 2, * the stores take place in units of 'int' or 'short'. * + * Note: It is the resposibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @throws RuntimeException if any of the arguments is invalid + * * @since 1.7 */ - public native void setMemory(Object o, long offset, long bytes, byte value); + public void setMemory(Object o, long offset, long bytes, byte value) { + setMemoryChecks(o, offset, bytes, value); + + if (bytes == 0) { + return; + } + + setMemory0(o, offset, bytes, value); + } /** * Sets all bytes in a given block of memory to a fixed value @@ -427,6 +688,19 @@ } /** + * Validate the arguments to setMemory + * + * @throws RuntimeException if the arguments are invalid + * (Note: after optimization, invalid inputs may + * go undetected, which will lead to unpredictable + * behavior) + */ + private void setMemoryChecks(Object o, long offset, long bytes, byte value) { + checkPrimitivePointer(o, offset); + checkSize(bytes); + } + + /** * Sets all bytes in a given block of memory to a copy of another * block. * @@ -441,12 +715,31 @@ * If the effective addresses and length are (resp.) even modulo 4 or 2, * the transfer takes place in units of 'int' or 'short'. * + * Note: It is the resposibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @throws RuntimeException if any of the arguments is invalid + * * @since 1.7 */ - @HotSpotIntrinsicCandidate - public native void copyMemory(Object srcBase, long srcOffset, - Object destBase, long destOffset, - long bytes); + public void copyMemory(Object srcBase, long srcOffset, + Object destBase, long destOffset, + long bytes) { + copyMemoryChecks(srcBase, srcOffset, destBase, destOffset, bytes); + + if (bytes == 0) { + return; + } + + copyMemory0(srcBase, srcOffset, destBase, destOffset, bytes); + } + /** * Sets all bytes in a given block of memory to a copy of another * block. This provides a single-register addressing mode, @@ -458,15 +751,22 @@ copyMemory(null, srcAddress, null, destAddress, bytes); } - private boolean isPrimitiveArray(Class c) { - Class componentType = c.getComponentType(); - return componentType != null && componentType.isPrimitive(); + /** + * Validate the arguments to copyMemory + * + * @throws RuntimeException if any of the arguments is invalid + * (Note: after optimization, invalid inputs may + * go undetected, which will lead to unpredictable + * behavior) + */ + private void copyMemoryChecks(Object srcBase, long srcOffset, + Object destBase, long destOffset, + long bytes) { + checkSize(bytes); + checkPrimitivePointer(srcBase, srcOffset); + checkPrimitivePointer(destBase, destOffset); } - private native void copySwapMemory0(Object srcBase, long srcOffset, - Object destBase, long destOffset, - long bytes, long elemSize); - /** * Copies all elements from one block of memory to another block, * *unconditionally* byte swapping the elements on the fly. @@ -476,39 +776,23 @@ * as discussed in {@link #getInt(Object,long)}. When the object reference is null, * the offset supplies an absolute base address. * + * Note: It is the resposibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @throws RuntimeException if any of the arguments is invalid + * * @since 9 */ public void copySwapMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes, long elemSize) { - if (bytes < 0) { - throw new IllegalArgumentException(); - } - if (elemSize != 2 && elemSize != 4 && elemSize != 8) { - throw new IllegalArgumentException(); - } - if (bytes % elemSize != 0) { - throw new IllegalArgumentException(); - } - if ((srcBase == null && srcOffset == 0) || - (destBase == null && destOffset == 0)) { - throw new NullPointerException(); - } - - // Must be off-heap, or primitive heap arrays - if (srcBase != null && (srcOffset < 0 || !isPrimitiveArray(srcBase.getClass()))) { - throw new IllegalArgumentException(); - } - if (destBase != null && (destOffset < 0 || !isPrimitiveArray(destBase.getClass()))) { - throw new IllegalArgumentException(); - } - - // Sanity check size and offsets on 32-bit platforms. Most - // significant 32 bits must be zero. - if (ADDRESS_SIZE == 4 && - (bytes >>> 32 != 0 || srcOffset >>> 32 != 0 || destOffset >>> 32 != 0)) { - throw new IllegalArgumentException(); - } + copySwapMemoryChecks(srcBase, srcOffset, destBase, destOffset, bytes, elemSize); if (bytes == 0) { return; @@ -517,6 +801,22 @@ copySwapMemory0(srcBase, srcOffset, destBase, destOffset, bytes, elemSize); } + private void copySwapMemoryChecks(Object srcBase, long srcOffset, + Object destBase, long destOffset, + long bytes, long elemSize) { + checkSize(bytes); + + if (elemSize != 2 && elemSize != 4 && elemSize != 8) { + throw invalidInput(); + } + if (bytes % elemSize != 0) { + throw invalidInput(); + } + + checkPrimitivePointer(srcBase, srcOffset); + checkPrimitivePointer(destBase, destOffset); + } + /** * Copies all elements from one block of memory to another block, byte swapping the * elements on the fly. @@ -535,9 +835,40 @@ * #allocateMemory} or {@link #reallocateMemory}. The address passed to * this method may be null, in which case no action is taken. * + * Note: It is the resposibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @throws RuntimeException if any of the arguments is invalid + * * @see #allocateMemory */ - public native void freeMemory(long address); + public void freeMemory(long address) { + freeMemoryChecks(address); + + if (address == 0) { + return; + } + + freeMemory0(address); + } + + /** + * Validate the arguments to freeMemory + * + * @throws RuntimeException if the arguments are invalid + * (Note: after optimization, invalid inputs may + * go undetected, which will lead to unpredictable + * behavior) + */ + private void freeMemoryChecks(long address) { + checkPointer(null, address); + } /// random queries @@ -546,7 +877,7 @@ * {@link #staticFieldOffset}, {@link #objectFieldOffset}, * or {@link #arrayBaseOffset}. */ - public static final int INVALID_FIELD_OFFSET = -1; + public static final int INVALID_FIELD_OFFSET = -1; /** * Reports the location of a given field in the storage allocation of its @@ -566,7 +897,13 @@ * must preserve all bits of static field offsets. * @see #getInt(Object, long) */ - public native long objectFieldOffset(Field f); + public long objectFieldOffset(Field f) { + if (f == null) { + throw new NullPointerException(); + } + + return objectFieldOffset0(f); + } /** * Reports the location of a given static field, in conjunction with {@link @@ -585,7 +922,13 @@ * this method reports its result as a long value. * @see #getInt(Object, long) */ - public native long staticFieldOffset(Field f); + public long staticFieldOffset(Field f) { + if (f == null) { + throw new NullPointerException(); + } + + return staticFieldOffset0(f); + } /** * Reports the location of a given static field, in conjunction with {@link @@ -597,7 +940,13 @@ * not be used in any way except as argument to the get and put routines in * this class. */ - public native Object staticFieldBase(Field f); + public Object staticFieldBase(Field f) { + if (f == null) { + throw new NullPointerException(); + } + + return staticFieldBase0(f); + } /** * Detects if the given class may need to be initialized. This is often @@ -605,14 +954,26 @@ * class. * @return false only if a call to {@code ensureClassInitialized} would have no effect */ - public native boolean shouldBeInitialized(Class c); + public boolean shouldBeInitialized(Class c) { + if (c == null) { + throw new NullPointerException(); + } + + return shouldBeInitialized0(c); + } /** * Ensures the given class has been initialized. This is often * needed in conjunction with obtaining the static field base of a * class. */ - public native void ensureClassInitialized(Class c); + public void ensureClassInitialized(Class c) { + if (c == null) { + throw new NullPointerException(); + } + + ensureClassInitialized0(c); + } /** * Reports the offset of the first element in the storage allocation of a @@ -624,7 +985,14 @@ * @see #getInt(Object, long) * @see #putInt(Object, long, int) */ - public native int arrayBaseOffset(Class arrayClass); + public int arrayBaseOffset(Class arrayClass) { + if (arrayClass == null) { + throw new NullPointerException(); + } + + return arrayBaseOffset0(arrayClass); + } + /** The value of {@code arrayBaseOffset(boolean[].class)} */ public static final int ARRAY_BOOLEAN_BASE_OFFSET @@ -673,7 +1041,14 @@ * @see #getInt(Object, long) * @see #putInt(Object, long, int) */ - public native int arrayIndexScale(Class arrayClass); + public int arrayIndexScale(Class arrayClass) { + if (arrayClass == null) { + throw new NullPointerException(); + } + + return arrayIndexScale0(arrayClass); + } + /** The value of {@code arrayIndexScale(boolean[].class)} */ public static final int ARRAY_BOOLEAN_INDEX_SCALE @@ -717,10 +1092,12 @@ * other primitive types (as stored in native memory blocks) is determined * fully by their information content. */ - public native int addressSize(); + public int addressSize() { + return ADDRESS_SIZE; + } /** The value of {@code addressSize()} */ - public static final int ADDRESS_SIZE = theUnsafe.addressSize(); + public static final int ADDRESS_SIZE = theUnsafe.addressSize0(); /** * Reports the size in bytes of a native memory page (whatever that is). @@ -735,9 +1112,22 @@ * Tells the VM to define a class, without security checks. By default, the * class loader and protection domain come from the caller's class. */ - public native Class defineClass(String name, byte[] b, int off, int len, - ClassLoader loader, - ProtectionDomain protectionDomain); + public Class defineClass(String name, byte[] b, int off, int len, + ClassLoader loader, + ProtectionDomain protectionDomain) { + if (b == null) { + throw new NullPointerException(); + } + if (len < 0) { + throw new ArrayIndexOutOfBoundsException(); + } + + return defineClass0(name, b, off, len, loader, protectionDomain); + } + + public native Class defineClass0(String name, byte[] b, int off, int len, + ClassLoader loader, + ProtectionDomain protectionDomain); /** * Defines a class but does not make it known to the class loader or system dictionary. @@ -755,7 +1145,13 @@ * @param data bytes of a class file * @param cpPatches where non-null entries exist, they replace corresponding CP entries in data */ - public native Class defineAnonymousClass(Class hostClass, byte[] data, Object[] cpPatches); + public Class defineAnonymousClass(Class hostClass, byte[] data, Object[] cpPatches) { + if (hostClass == null || data == null) { + throw new NullPointerException(); + } + + return defineAnonymousClass0(hostClass, data, cpPatches); + } /** * Allocates an instance but does not run any constructor. @@ -1290,7 +1686,13 @@ * @return the number of samples actually retrieved; or -1 * if the load average is unobtainable. */ - public native int getLoadAverage(double[] loadavg, int nelems); + public int getLoadAverage(double[] loadavg, int nelems) { + if (nelems < 0 || nelems > 3 || nelems > loadavg.length) { + throw new ArrayIndexOutOfBoundsException(); + } + + return getLoadAverage0(loadavg, nelems); + } // The following contain CAS-based Java implementations used on // platforms not supporting native instructions @@ -1718,9 +2120,6 @@ } // JVM interface methods - private native boolean unalignedAccess0(); - private native boolean isBigEndian0(); - // BE is true iff the native endianness of this platform is big. private static final boolean BE = theUnsafe.isBigEndian0(); @@ -1820,4 +2219,26 @@ private static short convEndian(boolean big, short n) { return big == BE ? n : Short.reverseBytes(n) ; } private static int convEndian(boolean big, int n) { return big == BE ? n : Integer.reverseBytes(n) ; } private static long convEndian(boolean big, long n) { return big == BE ? n : Long.reverseBytes(n) ; } + + + + private native long allocateMemory0(long bytes); + private native long reallocateMemory0(long address, long bytes); + private native void freeMemory0(long address); + private native void setMemory0(Object o, long offset, long bytes, byte value); + @HotSpotIntrinsicCandidate + private native void copyMemory0(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes); + private native void copySwapMemory0(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes, long elemSize); + private native long objectFieldOffset0(Field f); + private native long staticFieldOffset0(Field f); + private native Object staticFieldBase0(Field f); + private native boolean shouldBeInitialized0(Class c); + private native void ensureClassInitialized0(Class c); + private native int arrayBaseOffset0(Class arrayClass); + private native int arrayIndexScale0(Class arrayClass); + private native int addressSize0(); + private native Class defineAnonymousClass0(Class hostClass, byte[] data, Object[] cpPatches); + private native int getLoadAverage0(double[] loadavg, int nelems); + private native boolean unalignedAccess0(); + private native boolean isBigEndian0(); } --- old/src/java.base/share/classes/sun/misc/Unsafe.java 2016-03-01 08:25:36.126002933 -0800 +++ new/src/java.base/share/classes/sun/misc/Unsafe.java 2016-03-01 08:25:36.018001059 -0800 @@ -25,7 +25,7 @@ package sun.misc; -import jdk.internal.HotSpotIntrinsicCandidate; +import jdk.internal.vm.annotation.ForceInline; import jdk.internal.misc.VM; import sun.reflect.CallerSensitive; import sun.reflect.Reflection; @@ -39,21 +39,29 @@ * Although the class and all methods are public, use of this class is * limited because only trusted code can obtain instances of it. * + * Note: It is the resposibility of the caller to make sure + * arguments are checked before methods of this class are + * called. While some rudimentary checks are performed on the input, + * the checks are best effort and when performance is an overriding + * priority, as when methods of this class are optimized by the + * runtime compiler, some or all checks (if any) may be elided. Hence, + * the caller must not rely on the checks and corresponding + * exceptions! + * * @author John R. Rose * @see #getUnsafe */ public final class Unsafe { - private static native void registerNatives(); static { - registerNatives(); sun.reflect.Reflection.registerMethodsToFilter(Unsafe.class, "getUnsafe"); } private Unsafe() {} private static final Unsafe theUnsafe = new Unsafe(); + private static final jdk.internal.misc.Unsafe theInternalUnsafe = jdk.internal.misc.Unsafe.getUnsafe(); /** * Provides the caller with the capability of performing unsafe @@ -150,8 +158,10 @@ * @throws RuntimeException No defined exceptions are thrown, not even * {@link NullPointerException} */ - @HotSpotIntrinsicCandidate - public native int getInt(Object o, long offset); + @ForceInline + public int getInt(Object o, long offset) { + return theInternalUnsafe.getInt(o, offset); + } /** * Stores a value into a given Java variable. @@ -173,15 +183,19 @@ * @throws RuntimeException No defined exceptions are thrown, not even * {@link NullPointerException} */ - @HotSpotIntrinsicCandidate - public native void putInt(Object o, long offset, int x); + @ForceInline + public void putInt(Object o, long offset, int x) { + theInternalUnsafe.putInt(o, offset, x); + } /** * Fetches a reference value from a given Java variable. * @see #getInt(Object, long) */ - @HotSpotIntrinsicCandidate - public native Object getObject(Object o, long offset); + @ForceInline + public Object getObject(Object o, long offset) { + return theInternalUnsafe.getObject(o, offset); + } /** * Stores a reference value into a given Java variable. @@ -193,51 +207,95 @@ * are updated. * @see #putInt(Object, long, int) */ - @HotSpotIntrinsicCandidate - public native void putObject(Object o, long offset, Object x); + @ForceInline + public void putObject(Object o, long offset, Object x) { + theInternalUnsafe.putObject(o, offset, x); + } /** @see #getInt(Object, long) */ - @HotSpotIntrinsicCandidate - public native boolean getBoolean(Object o, long offset); + @ForceInline + public boolean getBoolean(Object o, long offset) { + return theInternalUnsafe.getBoolean(o, offset); + } + /** @see #putInt(Object, long, int) */ - @HotSpotIntrinsicCandidate - public native void putBoolean(Object o, long offset, boolean x); + @ForceInline + public void putBoolean(Object o, long offset, boolean x) { + theInternalUnsafe.putBoolean(o, offset, x); + } + /** @see #getInt(Object, long) */ - @HotSpotIntrinsicCandidate - public native byte getByte(Object o, long offset); + @ForceInline + public byte getByte(Object o, long offset) { + return theInternalUnsafe.getByte(o, offset); + } + /** @see #putInt(Object, long, int) */ - @HotSpotIntrinsicCandidate - public native void putByte(Object o, long offset, byte x); + @ForceInline + public void putByte(Object o, long offset, byte x) { + theInternalUnsafe.putByte(o, offset, x); + } + /** @see #getInt(Object, long) */ - @HotSpotIntrinsicCandidate - public native short getShort(Object o, long offset); + @ForceInline + public short getShort(Object o, long offset) { + return theInternalUnsafe.getShort(o, offset); + } + /** @see #putInt(Object, long, int) */ - @HotSpotIntrinsicCandidate - public native void putShort(Object o, long offset, short x); + @ForceInline + public void putShort(Object o, long offset, short x) { + theInternalUnsafe.putShort(o, offset, x); + } + /** @see #getInt(Object, long) */ - @HotSpotIntrinsicCandidate - public native char getChar(Object o, long offset); + @ForceInline + public char getChar(Object o, long offset) { + return theInternalUnsafe.getChar(o, offset); + } + /** @see #putInt(Object, long, int) */ - @HotSpotIntrinsicCandidate - public native void putChar(Object o, long offset, char x); + @ForceInline + public void putChar(Object o, long offset, char x) { + theInternalUnsafe.putChar(o, offset, x); + } + /** @see #getInt(Object, long) */ - @HotSpotIntrinsicCandidate - public native long getLong(Object o, long offset); + @ForceInline + public long getLong(Object o, long offset) { + return theInternalUnsafe.getLong(o, offset); + } + /** @see #putInt(Object, long, int) */ - @HotSpotIntrinsicCandidate - public native void putLong(Object o, long offset, long x); + @ForceInline + public void putLong(Object o, long offset, long x) { + theInternalUnsafe.putLong(o, offset, x); + } + /** @see #getInt(Object, long) */ - @HotSpotIntrinsicCandidate - public native float getFloat(Object o, long offset); + @ForceInline + public float getFloat(Object o, long offset) { + return theInternalUnsafe.getFloat(o, offset); + } + /** @see #putInt(Object, long, int) */ - @HotSpotIntrinsicCandidate - public native void putFloat(Object o, long offset, float x); + @ForceInline + public void putFloat(Object o, long offset, float x) { + theInternalUnsafe.putFloat(o, offset, x); + } + /** @see #getInt(Object, long) */ - @HotSpotIntrinsicCandidate - public native double getDouble(Object o, long offset); + @ForceInline + public double getDouble(Object o, long offset) { + return theInternalUnsafe.getDouble(o, offset); + } + /** @see #putInt(Object, long, int) */ - @HotSpotIntrinsicCandidate - public native void putDouble(Object o, long offset, double x); + @ForceInline + public void putDouble(Object o, long offset, double x) { + theInternalUnsafe.putDouble(o, offset, x); + } + // These read VM internal data. @@ -248,7 +306,10 @@ * @param address a memory address locating the variable * @return the value fetched from the indicated native variable */ - public native Object getUncompressedObject(long address); + @ForceInline + public Object getUncompressedObject(long address) { + return theInternalUnsafe.getUncompressedObject(address); + } /** * Fetches the {@link java.lang.Class} Java mirror for the given native @@ -257,7 +318,10 @@ * @param metaspaceKlass a native metaspace {@code Klass} pointer * @return the {@link java.lang.Class} Java mirror */ - public native Class getJavaMirror(long metaspaceKlass); + @ForceInline + public Class getJavaMirror(long metaspaceKlass) { + return theInternalUnsafe.getJavaMirror(metaspaceKlass); + } /** * Fetches a native metaspace {@code Klass} pointer for the given Java @@ -266,7 +330,10 @@ * @param o Java heap object for which to fetch the class pointer * @return a native metaspace {@code Klass} pointer */ - public native long getKlassPointer(Object o); + @ForceInline + public long getKlassPointer(Object o) { + return theInternalUnsafe.getKlassPointer(o); + } // These work on values in the C heap. @@ -277,8 +344,10 @@ * * @see #allocateMemory */ - @HotSpotIntrinsicCandidate - public native byte getByte(long address); + @ForceInline + public byte getByte(long address) { + return theInternalUnsafe.getByte(address); + } /** * Stores a value into a given memory address. If the address is zero, or @@ -287,45 +356,83 @@ * * @see #getByte(long) */ - @HotSpotIntrinsicCandidate - public native void putByte(long address, byte x); + @ForceInline + public void putByte(long address, byte x) { + theInternalUnsafe.putByte(address, x); + } /** @see #getByte(long) */ - @HotSpotIntrinsicCandidate - public native short getShort(long address); + @ForceInline + public short getShort(long address) { + return theInternalUnsafe.getShort(address); + } + /** @see #putByte(long, byte) */ - @HotSpotIntrinsicCandidate - public native void putShort(long address, short x); + @ForceInline + public void putShort(long address, short x) { + theInternalUnsafe.putShort(address, x); + } + /** @see #getByte(long) */ - @HotSpotIntrinsicCandidate - public native char getChar(long address); + @ForceInline + public char getChar(long address) { + return theInternalUnsafe.getChar(address); + } + /** @see #putByte(long, byte) */ - @HotSpotIntrinsicCandidate - public native void putChar(long address, char x); + @ForceInline + public void putChar(long address, char x) { + theInternalUnsafe.putChar(address, x); + } + /** @see #getByte(long) */ - @HotSpotIntrinsicCandidate - public native int getInt(long address); + @ForceInline + public int getInt(long address) { + return theInternalUnsafe.getInt(address); + } + /** @see #putByte(long, byte) */ - @HotSpotIntrinsicCandidate - public native void putInt(long address, int x); + @ForceInline + public void putInt(long address, int x) { + theInternalUnsafe.putInt(address, x); + } + /** @see #getByte(long) */ - @HotSpotIntrinsicCandidate - public native long getLong(long address); + @ForceInline + public long getLong(long address) { + return theInternalUnsafe.getLong(address); + } + /** @see #putByte(long, byte) */ - @HotSpotIntrinsicCandidate - public native void putLong(long address, long x); + @ForceInline + public void putLong(long address, long x) { + theInternalUnsafe.putLong(address, x); + } + /** @see #getByte(long) */ - @HotSpotIntrinsicCandidate - public native float getFloat(long address); + @ForceInline + public float getFloat(long address) { + return theInternalUnsafe.getFloat(address); + } + /** @see #putByte(long, byte) */ - @HotSpotIntrinsicCandidate - public native void putFloat(long address, float x); + @ForceInline + public void putFloat(long address, float x) { + theInternalUnsafe.putFloat(address, x); + } + /** @see #getByte(long) */ - @HotSpotIntrinsicCandidate - public native double getDouble(long address); + @ForceInline + public double getDouble(long address) { + return theInternalUnsafe.getDouble(address); + } + /** @see #putByte(long, byte) */ - @HotSpotIntrinsicCandidate - public native void putDouble(long address, double x); + @ForceInline + public void putDouble(long address, double x) { + theInternalUnsafe.putDouble(address, x); + } + /** * Fetches a native pointer from a given memory address. If the address is @@ -341,8 +448,10 @@ * * @see #allocateMemory */ - @HotSpotIntrinsicCandidate - public native long getAddress(long address); + @ForceInline + public long getAddress(long address) { + return theInternalUnsafe.getAddress(address); + } /** * Stores a native pointer into a given memory address. If the address is @@ -354,8 +463,11 @@ * * @see #getAddress(long) */ - @HotSpotIntrinsicCandidate - public native void putAddress(long address, long x); + @ForceInline + public void putAddress(long address, long x) { + theInternalUnsafe.putAddress(address, x); + } + /// wrappers for malloc, realloc, free: @@ -366,7 +478,16 @@ * aligned for all value types. Dispose of this memory by calling {@link * #freeMemory}, or resize it with {@link #reallocateMemory}. * - * @throws IllegalArgumentException if the size is negative or too large + * Note: It is the resposibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @throws RuntimeException if the size is negative or too large * for the native size_t type * * @throws OutOfMemoryError if the allocation is refused by the system @@ -374,7 +495,10 @@ * @see #getByte(long) * @see #putByte(long, byte) */ - public native long allocateMemory(long bytes); + @ForceInline + public long allocateMemory(long bytes) { + return theInternalUnsafe.allocateMemory(bytes); + } /** * Resizes a new block of native memory, to the given size in bytes. The @@ -386,14 +510,26 @@ * #reallocateMemory}. The address passed to this method may be null, in * which case an allocation will be performed. * - * @throws IllegalArgumentException if the size is negative or too large + * Note: It is the resposibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @throws RuntimeException if the size is negative or too large * for the native size_t type * * @throws OutOfMemoryError if the allocation is refused by the system * * @see #allocateMemory */ - public native long reallocateMemory(long address, long bytes); + @ForceInline + public long reallocateMemory(long address, long bytes) { + return theInternalUnsafe.reallocateMemory(address, bytes); + } /** * Sets all bytes in a given block of memory to a fixed value @@ -410,9 +546,23 @@ * If the effective address and length are (resp.) even modulo 4 or 2, * the stores take place in units of 'int' or 'short'. * + * Note: It is the resposibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @throws RuntimeException if any of the arguments is invalid + * * @since 1.7 */ - public native void setMemory(Object o, long offset, long bytes, byte value); + @ForceInline + public void setMemory(Object o, long offset, long bytes, byte value) { + theInternalUnsafe.setMemory(o, offset, bytes, value); + } /** * Sets all bytes in a given block of memory to a fixed value @@ -421,8 +571,9 @@ * *

Equivalent to {@code setMemory(null, address, bytes, value)}. */ + @ForceInline public void setMemory(long address, long bytes, byte value) { - setMemory(null, address, bytes, value); + theInternalUnsafe.setMemory(address, bytes, value); } /** @@ -440,12 +591,26 @@ * If the effective addresses and length are (resp.) even modulo 4 or 2, * the transfer takes place in units of 'int' or 'short'. * + * Note: It is the resposibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @throws RuntimeException if any of the arguments is invalid + * * @since 1.7 */ - @HotSpotIntrinsicCandidate - public native void copyMemory(Object srcBase, long srcOffset, - Object destBase, long destOffset, - long bytes); + @ForceInline + public void copyMemory(Object srcBase, long srcOffset, + Object destBase, long destOffset, + long bytes) { + theInternalUnsafe.copyMemory(srcBase, srcOffset, destBase, destOffset, bytes); + } + /** * Sets all bytes in a given block of memory to a copy of another * block. This provides a single-register addressing mode, @@ -453,8 +618,9 @@ * * Equivalent to {@code copyMemory(null, srcAddress, null, destAddress, bytes)}. */ + @ForceInline public void copyMemory(long srcAddress, long destAddress, long bytes) { - copyMemory(null, srcAddress, null, destAddress, bytes); + theInternalUnsafe.copyMemory(srcAddress, destAddress, bytes); } /** @@ -462,9 +628,23 @@ * #allocateMemory} or {@link #reallocateMemory}. The address passed to * this method may be null, in which case no action is taken. * + * Note: It is the resposibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @throws RuntimeException if any of the arguments is invalid + * * @see #allocateMemory */ - public native void freeMemory(long address); + @ForceInline + public void freeMemory(long address) { + theInternalUnsafe.freeMemory(address); + } /// random queries @@ -473,7 +653,7 @@ * {@link #staticFieldOffset}, {@link #objectFieldOffset}, * or {@link #arrayBaseOffset}. */ - public static final int INVALID_FIELD_OFFSET = -1; + public static final int INVALID_FIELD_OFFSET = jdk.internal.misc.Unsafe.INVALID_FIELD_OFFSET; /** * Reports the location of a given field in the storage allocation of its @@ -493,7 +673,10 @@ * must preserve all bits of static field offsets. * @see #getInt(Object, long) */ - public native long objectFieldOffset(Field f); + @ForceInline + public long objectFieldOffset(Field f) { + return theInternalUnsafe.objectFieldOffset(f); + } /** * Reports the location of a given static field, in conjunction with {@link @@ -512,7 +695,10 @@ * this method reports its result as a long value. * @see #getInt(Object, long) */ - public native long staticFieldOffset(Field f); + @ForceInline + public long staticFieldOffset(Field f) { + return theInternalUnsafe.staticFieldOffset(f); + } /** * Reports the location of a given static field, in conjunction with {@link @@ -524,7 +710,10 @@ * not be used in any way except as argument to the get and put routines in * this class. */ - public native Object staticFieldBase(Field f); + @ForceInline + public Object staticFieldBase(Field f) { + return theInternalUnsafe.staticFieldBase(f); + } /** * Detects if the given class may need to be initialized. This is often @@ -532,14 +721,20 @@ * class. * @return false only if a call to {@code ensureClassInitialized} would have no effect */ - public native boolean shouldBeInitialized(Class c); + @ForceInline + public boolean shouldBeInitialized(Class c) { + return theInternalUnsafe.shouldBeInitialized(c); + } /** * Ensures the given class has been initialized. This is often * needed in conjunction with obtaining the static field base of a * class. */ - public native void ensureClassInitialized(Class c); + @ForceInline + public void ensureClassInitialized(Class c) { + theInternalUnsafe.ensureClassInitialized(c); + } /** * Reports the offset of the first element in the storage allocation of a @@ -551,43 +746,37 @@ * @see #getInt(Object, long) * @see #putInt(Object, long, int) */ - public native int arrayBaseOffset(Class arrayClass); + @ForceInline + public int arrayBaseOffset(Class arrayClass) { + return theInternalUnsafe.arrayBaseOffset(arrayClass); + } /** The value of {@code arrayBaseOffset(boolean[].class)} */ - public static final int ARRAY_BOOLEAN_BASE_OFFSET - = theUnsafe.arrayBaseOffset(boolean[].class); + public static final int ARRAY_BOOLEAN_BASE_OFFSET = jdk.internal.misc.Unsafe.ARRAY_BOOLEAN_BASE_OFFSET; /** The value of {@code arrayBaseOffset(byte[].class)} */ - public static final int ARRAY_BYTE_BASE_OFFSET - = theUnsafe.arrayBaseOffset(byte[].class); + public static final int ARRAY_BYTE_BASE_OFFSET = jdk.internal.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET; /** The value of {@code arrayBaseOffset(short[].class)} */ - public static final int ARRAY_SHORT_BASE_OFFSET - = theUnsafe.arrayBaseOffset(short[].class); + public static final int ARRAY_SHORT_BASE_OFFSET = jdk.internal.misc.Unsafe.ARRAY_SHORT_BASE_OFFSET; /** The value of {@code arrayBaseOffset(char[].class)} */ - public static final int ARRAY_CHAR_BASE_OFFSET - = theUnsafe.arrayBaseOffset(char[].class); + public static final int ARRAY_CHAR_BASE_OFFSET = jdk.internal.misc.Unsafe.ARRAY_CHAR_BASE_OFFSET; /** The value of {@code arrayBaseOffset(int[].class)} */ - public static final int ARRAY_INT_BASE_OFFSET - = theUnsafe.arrayBaseOffset(int[].class); + public static final int ARRAY_INT_BASE_OFFSET = jdk.internal.misc.Unsafe.ARRAY_INT_BASE_OFFSET; /** The value of {@code arrayBaseOffset(long[].class)} */ - public static final int ARRAY_LONG_BASE_OFFSET - = theUnsafe.arrayBaseOffset(long[].class); + public static final int ARRAY_LONG_BASE_OFFSET = jdk.internal.misc.Unsafe.ARRAY_LONG_BASE_OFFSET; /** The value of {@code arrayBaseOffset(float[].class)} */ - public static final int ARRAY_FLOAT_BASE_OFFSET - = theUnsafe.arrayBaseOffset(float[].class); + public static final int ARRAY_FLOAT_BASE_OFFSET = jdk.internal.misc.Unsafe.ARRAY_FLOAT_BASE_OFFSET; /** The value of {@code arrayBaseOffset(double[].class)} */ - public static final int ARRAY_DOUBLE_BASE_OFFSET - = theUnsafe.arrayBaseOffset(double[].class); + public static final int ARRAY_DOUBLE_BASE_OFFSET = jdk.internal.misc.Unsafe.ARRAY_DOUBLE_BASE_OFFSET; /** The value of {@code arrayBaseOffset(Object[].class)} */ - public static final int ARRAY_OBJECT_BASE_OFFSET - = theUnsafe.arrayBaseOffset(Object[].class); + public static final int ARRAY_OBJECT_BASE_OFFSET = jdk.internal.misc.Unsafe.ARRAY_OBJECT_BASE_OFFSET; /** * Reports the scale factor for addressing elements in the storage @@ -600,43 +789,37 @@ * @see #getInt(Object, long) * @see #putInt(Object, long, int) */ - public native int arrayIndexScale(Class arrayClass); + @ForceInline + public int arrayIndexScale(Class arrayClass) { + return theInternalUnsafe.arrayIndexScale(arrayClass); + } /** The value of {@code arrayIndexScale(boolean[].class)} */ - public static final int ARRAY_BOOLEAN_INDEX_SCALE - = theUnsafe.arrayIndexScale(boolean[].class); + public static final int ARRAY_BOOLEAN_INDEX_SCALE = jdk.internal.misc.Unsafe.ARRAY_BOOLEAN_INDEX_SCALE; /** The value of {@code arrayIndexScale(byte[].class)} */ - public static final int ARRAY_BYTE_INDEX_SCALE - = theUnsafe.arrayIndexScale(byte[].class); + public static final int ARRAY_BYTE_INDEX_SCALE = jdk.internal.misc.Unsafe.ARRAY_BYTE_INDEX_SCALE; /** The value of {@code arrayIndexScale(short[].class)} */ - public static final int ARRAY_SHORT_INDEX_SCALE - = theUnsafe.arrayIndexScale(short[].class); + public static final int ARRAY_SHORT_INDEX_SCALE = jdk.internal.misc.Unsafe.ARRAY_SHORT_INDEX_SCALE; /** The value of {@code arrayIndexScale(char[].class)} */ - public static final int ARRAY_CHAR_INDEX_SCALE - = theUnsafe.arrayIndexScale(char[].class); + public static final int ARRAY_CHAR_INDEX_SCALE = jdk.internal.misc.Unsafe.ARRAY_CHAR_INDEX_SCALE; /** The value of {@code arrayIndexScale(int[].class)} */ - public static final int ARRAY_INT_INDEX_SCALE - = theUnsafe.arrayIndexScale(int[].class); + public static final int ARRAY_INT_INDEX_SCALE = jdk.internal.misc.Unsafe.ARRAY_INT_INDEX_SCALE; /** The value of {@code arrayIndexScale(long[].class)} */ - public static final int ARRAY_LONG_INDEX_SCALE - = theUnsafe.arrayIndexScale(long[].class); + public static final int ARRAY_LONG_INDEX_SCALE = jdk.internal.misc.Unsafe.ARRAY_LONG_INDEX_SCALE; /** The value of {@code arrayIndexScale(float[].class)} */ - public static final int ARRAY_FLOAT_INDEX_SCALE - = theUnsafe.arrayIndexScale(float[].class); + public static final int ARRAY_FLOAT_INDEX_SCALE = jdk.internal.misc.Unsafe.ARRAY_FLOAT_INDEX_SCALE; /** The value of {@code arrayIndexScale(double[].class)} */ - public static final int ARRAY_DOUBLE_INDEX_SCALE - = theUnsafe.arrayIndexScale(double[].class); + public static final int ARRAY_DOUBLE_INDEX_SCALE = jdk.internal.misc.Unsafe.ARRAY_DOUBLE_INDEX_SCALE; /** The value of {@code arrayIndexScale(Object[].class)} */ - public static final int ARRAY_OBJECT_INDEX_SCALE - = theUnsafe.arrayIndexScale(Object[].class); + public static final int ARRAY_OBJECT_INDEX_SCALE = jdk.internal.misc.Unsafe.ARRAY_OBJECT_INDEX_SCALE; /** * Reports the size in bytes of a native pointer, as stored via {@link @@ -644,16 +827,22 @@ * other primitive types (as stored in native memory blocks) is determined * fully by their information content. */ - public native int addressSize(); + @ForceInline + public int addressSize() { + return theInternalUnsafe.addressSize(); + } /** The value of {@code addressSize()} */ - public static final int ADDRESS_SIZE = theUnsafe.addressSize(); + public static final int ADDRESS_SIZE = theInternalUnsafe.addressSize(); /** * Reports the size in bytes of a native memory page (whatever that is). * This value will always be a power of two. */ - public native int pageSize(); + @ForceInline + public int pageSize() { + return theInternalUnsafe.pageSize(); + } /// random trusted operations from JNI: @@ -662,9 +851,12 @@ * Tells the VM to define a class, without security checks. By default, the * class loader and protection domain come from the caller's class. */ - public native Class defineClass(String name, byte[] b, int off, int len, - ClassLoader loader, - ProtectionDomain protectionDomain); + @ForceInline + public Class defineClass(String name, byte[] b, int off, int len, + ClassLoader loader, + ProtectionDomain protectionDomain) { + return theInternalUnsafe.defineClass(name, b, off, len, loader, protectionDomain); + } /** * Defines a class but does not make it known to the class loader or system dictionary. @@ -682,18 +874,26 @@ * @param data bytes of a class file * @param cpPatches where non-null entries exist, they replace corresponding CP entries in data */ - public native Class defineAnonymousClass(Class hostClass, byte[] data, Object[] cpPatches); + @ForceInline + public Class defineAnonymousClass(Class hostClass, byte[] data, Object[] cpPatches) { + return theInternalUnsafe.defineAnonymousClass(hostClass, data, cpPatches); + } /** * Allocates an instance but does not run any constructor. * Initializes the class if it has not yet been. */ - @HotSpotIntrinsicCandidate - public native Object allocateInstance(Class cls) - throws InstantiationException; + @ForceInline + public Object allocateInstance(Class cls) + throws InstantiationException { + return theInternalUnsafe.allocateInstance(cls); + } /** Throws the exception without telling the verifier. */ - public native void throwException(Throwable ee); + @ForceInline + public void throwException(Throwable ee) { + theInternalUnsafe.throwException(ee); + } /** * Atomically updates Java variable to {@code x} if it is currently @@ -704,10 +904,12 @@ * * @return {@code true} if successful */ - @HotSpotIntrinsicCandidate - public final native boolean compareAndSwapObject(Object o, long offset, - Object expected, - Object x); + @ForceInline + public final boolean compareAndSwapObject(Object o, long offset, + Object expected, + Object x) { + return theInternalUnsafe.compareAndSwapObject(o, offset, expected, x); + } /** * Atomically updates Java variable to {@code x} if it is currently @@ -718,10 +920,12 @@ * * @return {@code true} if successful */ - @HotSpotIntrinsicCandidate - public final native boolean compareAndSwapInt(Object o, long offset, - int expected, - int x); + @ForceInline + public final boolean compareAndSwapInt(Object o, long offset, + int expected, + int x) { + return theInternalUnsafe.compareAndSwapInt(o, offset, expected, x); + } /** * Atomically updates Java variable to {@code x} if it is currently @@ -732,88 +936,126 @@ * * @return {@code true} if successful */ - @HotSpotIntrinsicCandidate - public final native boolean compareAndSwapLong(Object o, long offset, - long expected, - long x); + @ForceInline + public final boolean compareAndSwapLong(Object o, long offset, + long expected, + long x) { + return theInternalUnsafe.compareAndSwapLong(o, offset, expected, x); + } /** * Fetches a reference value from a given Java variable, with volatile * load semantics. Otherwise identical to {@link #getObject(Object, long)} */ - @HotSpotIntrinsicCandidate - public native Object getObjectVolatile(Object o, long offset); + @ForceInline + public Object getObjectVolatile(Object o, long offset) { + return theInternalUnsafe.getObjectVolatile(o, offset); + } /** * Stores a reference value into a given Java variable, with * volatile store semantics. Otherwise identical to {@link #putObject(Object, long, Object)} */ - @HotSpotIntrinsicCandidate - public native void putObjectVolatile(Object o, long offset, Object x); + @ForceInline + public void putObjectVolatile(Object o, long offset, Object x) { + theInternalUnsafe.putObjectVolatile(o, offset, x); + } /** Volatile version of {@link #getInt(Object, long)} */ - @HotSpotIntrinsicCandidate - public native int getIntVolatile(Object o, long offset); + @ForceInline + public int getIntVolatile(Object o, long offset) { + return theInternalUnsafe.getIntVolatile(o, offset); + } /** Volatile version of {@link #putInt(Object, long, int)} */ - @HotSpotIntrinsicCandidate - public native void putIntVolatile(Object o, long offset, int x); + @ForceInline + public void putIntVolatile(Object o, long offset, int x) { + theInternalUnsafe.putIntVolatile(o, offset, x); + } /** Volatile version of {@link #getBoolean(Object, long)} */ - @HotSpotIntrinsicCandidate - public native boolean getBooleanVolatile(Object o, long offset); + @ForceInline + public boolean getBooleanVolatile(Object o, long offset) { + return theInternalUnsafe.getBooleanVolatile(o, offset); + } /** Volatile version of {@link #putBoolean(Object, long, boolean)} */ - @HotSpotIntrinsicCandidate - public native void putBooleanVolatile(Object o, long offset, boolean x); + @ForceInline + public void putBooleanVolatile(Object o, long offset, boolean x) { + theInternalUnsafe.putBooleanVolatile(o, offset, x); + } /** Volatile version of {@link #getByte(Object, long)} */ - @HotSpotIntrinsicCandidate - public native byte getByteVolatile(Object o, long offset); + @ForceInline + public byte getByteVolatile(Object o, long offset) { + return theInternalUnsafe.getByteVolatile(o, offset); + } /** Volatile version of {@link #putByte(Object, long, byte)} */ - @HotSpotIntrinsicCandidate - public native void putByteVolatile(Object o, long offset, byte x); + @ForceInline + public void putByteVolatile(Object o, long offset, byte x) { + theInternalUnsafe.putByteVolatile(o, offset, x); + } /** Volatile version of {@link #getShort(Object, long)} */ - @HotSpotIntrinsicCandidate - public native short getShortVolatile(Object o, long offset); + @ForceInline + public short getShortVolatile(Object o, long offset) { + return theInternalUnsafe.getShortVolatile(o, offset); + } /** Volatile version of {@link #putShort(Object, long, short)} */ - @HotSpotIntrinsicCandidate - public native void putShortVolatile(Object o, long offset, short x); + @ForceInline + public void putShortVolatile(Object o, long offset, short x) { + theInternalUnsafe.putShortVolatile(o, offset, x); + } /** Volatile version of {@link #getChar(Object, long)} */ - @HotSpotIntrinsicCandidate - public native char getCharVolatile(Object o, long offset); + @ForceInline + public char getCharVolatile(Object o, long offset) { + return theInternalUnsafe.getCharVolatile(o, offset); + } /** Volatile version of {@link #putChar(Object, long, char)} */ - @HotSpotIntrinsicCandidate - public native void putCharVolatile(Object o, long offset, char x); + @ForceInline + public void putCharVolatile(Object o, long offset, char x) { + theInternalUnsafe.putCharVolatile(o, offset, x); + } /** Volatile version of {@link #getLong(Object, long)} */ - @HotSpotIntrinsicCandidate - public native long getLongVolatile(Object o, long offset); + @ForceInline + public long getLongVolatile(Object o, long offset) { + return theInternalUnsafe.getLongVolatile(o, offset); + } /** Volatile version of {@link #putLong(Object, long, long)} */ - @HotSpotIntrinsicCandidate - public native void putLongVolatile(Object o, long offset, long x); + @ForceInline + public void putLongVolatile(Object o, long offset, long x) { + theInternalUnsafe.putLongVolatile(o, offset, x); + } /** Volatile version of {@link #getFloat(Object, long)} */ - @HotSpotIntrinsicCandidate - public native float getFloatVolatile(Object o, long offset); + @ForceInline + public float getFloatVolatile(Object o, long offset) { + return theInternalUnsafe.getFloatVolatile(o, offset); + } /** Volatile version of {@link #putFloat(Object, long, float)} */ - @HotSpotIntrinsicCandidate - public native void putFloatVolatile(Object o, long offset, float x); + @ForceInline + public void putFloatVolatile(Object o, long offset, float x) { + theInternalUnsafe.putFloatVolatile(o, offset, x); + } /** Volatile version of {@link #getDouble(Object, long)} */ - @HotSpotIntrinsicCandidate - public native double getDoubleVolatile(Object o, long offset); + @ForceInline + public double getDoubleVolatile(Object o, long offset) { + return theInternalUnsafe.getDoubleVolatile(o, offset); + } /** Volatile version of {@link #putDouble(Object, long, double)} */ - @HotSpotIntrinsicCandidate - public native void putDoubleVolatile(Object o, long offset, double x); + @ForceInline + public void putDoubleVolatile(Object o, long offset, double x) { + theInternalUnsafe.putDoubleVolatile(o, offset, x); + } /** * Version of {@link #putObjectVolatile(Object, long, Object)} @@ -824,16 +1066,22 @@ * * Corresponds to C11 atomic_store_explicit(..., memory_order_release). */ - @HotSpotIntrinsicCandidate - public native void putOrderedObject(Object o, long offset, Object x); + @ForceInline + public void putOrderedObject(Object o, long offset, Object x) { + theInternalUnsafe.putOrderedObject(o, offset, x); + } /** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)} */ - @HotSpotIntrinsicCandidate - public native void putOrderedInt(Object o, long offset, int x); + @ForceInline + public void putOrderedInt(Object o, long offset, int x) { + theInternalUnsafe.putOrderedInt(o, offset, x); + } /** Ordered/Lazy version of {@link #putLongVolatile(Object, long, long)} */ - @HotSpotIntrinsicCandidate - public native void putOrderedLong(Object o, long offset, long x); + @ForceInline + public void putOrderedLong(Object o, long offset, long x) { + theInternalUnsafe.putOrderedLong(o, offset, x); + } /** * Unblocks the given thread blocked on {@code park}, or, if it is @@ -847,8 +1095,10 @@ * * @param thread the thread to unpark. */ - @HotSpotIntrinsicCandidate - public native void unpark(Object thread); + @ForceInline + public void unpark(Object thread) { + theInternalUnsafe.unpark(thread); + } /** * Blocks current thread, returning when a balancing @@ -861,8 +1111,10 @@ * because {@code unpark} is, so it would be strange to place it * elsewhere. */ - @HotSpotIntrinsicCandidate - public native void park(boolean isAbsolute, long time); + @ForceInline + public void park(boolean isAbsolute, long time) { + theInternalUnsafe.park(isAbsolute, time); + } /** * Gets the load average in the system run queue assigned @@ -879,7 +1131,10 @@ * @return the number of samples actually retrieved; or -1 * if the load average is unobtainable. */ - public native int getLoadAverage(double[] loadavg, int nelems); + @ForceInline + public int getLoadAverage(double[] loadavg, int nelems) { + return theInternalUnsafe.getLoadAverage(loadavg, nelems); + } // The following contain CAS-based Java implementations used on // platforms not supporting native instructions @@ -895,13 +1150,9 @@ * @return the previous value * @since 1.8 */ - @HotSpotIntrinsicCandidate + @ForceInline public final int getAndAddInt(Object o, long offset, int delta) { - int v; - do { - v = getIntVolatile(o, offset); - } while (!compareAndSwapInt(o, offset, v, v + delta)); - return v; + return theInternalUnsafe.getAndAddInt(o, offset, delta); } /** @@ -915,13 +1166,9 @@ * @return the previous value * @since 1.8 */ - @HotSpotIntrinsicCandidate + @ForceInline public final long getAndAddLong(Object o, long offset, long delta) { - long v; - do { - v = getLongVolatile(o, offset); - } while (!compareAndSwapLong(o, offset, v, v + delta)); - return v; + return theInternalUnsafe.getAndAddLong(o, offset, delta); } /** @@ -935,13 +1182,9 @@ * @return the previous value * @since 1.8 */ - @HotSpotIntrinsicCandidate + @ForceInline public final int getAndSetInt(Object o, long offset, int newValue) { - int v; - do { - v = getIntVolatile(o, offset); - } while (!compareAndSwapInt(o, offset, v, newValue)); - return v; + return theInternalUnsafe.getAndSetInt(o, offset, newValue); } /** @@ -955,13 +1198,9 @@ * @return the previous value * @since 1.8 */ - @HotSpotIntrinsicCandidate + @ForceInline public final long getAndSetLong(Object o, long offset, long newValue) { - long v; - do { - v = getLongVolatile(o, offset); - } while (!compareAndSwapLong(o, offset, v, newValue)); - return v; + return theInternalUnsafe.getAndSetLong(o, offset, newValue); } /** @@ -975,13 +1214,9 @@ * @return the previous value * @since 1.8 */ - @HotSpotIntrinsicCandidate + @ForceInline public final Object getAndSetObject(Object o, long offset, Object newValue) { - Object v; - do { - v = getObjectVolatile(o, offset); - } while (!compareAndSwapObject(o, offset, v, newValue)); - return v; + return theInternalUnsafe.getAndSetObject(o, offset, newValue); } @@ -997,8 +1232,10 @@ * provide a LoadLoad barrier also provide a LoadStore barrier for free. * @since 1.8 */ - @HotSpotIntrinsicCandidate - public native void loadFence(); + @ForceInline + public void loadFence() { + theInternalUnsafe.loadFence(); + } /** * Ensures that loads and stores before the fence will not be reordered with @@ -1012,8 +1249,10 @@ * provide a StoreStore barrier also provide a LoadStore barrier for free. * @since 1.8 */ - @HotSpotIntrinsicCandidate - public native void storeFence(); + @ForceInline + public void storeFence() { + theInternalUnsafe.storeFence(); + } /** * Ensures that loads and stores before the fence will not be reordered @@ -1024,15 +1263,8 @@ * Corresponds to C11 atomic_thread_fence(memory_order_seq_cst). * @since 1.8 */ - @HotSpotIntrinsicCandidate - public native void fullFence(); - - /** - * Throws IllegalAccessError; for use by the VM for access control - * error support. - * @since 1.8 - */ - private static void throwIllegalAccessError() { - throw new IllegalAccessError(); + @ForceInline + public void fullFence() { + theInternalUnsafe.fullFence(); } } --- old/test/jdk/internal/misc/Unsafe/CopySwap.java 2016-03-01 08:25:36.182003903 -0800 +++ new/test/jdk/internal/misc/Unsafe/CopySwap.java 2016-03-01 08:25:36.038001406 -0800 @@ -587,22 +587,6 @@ } try { - // Check that a NULL source throws NPE - UNSAFE.copySwapMemory(null, 0, null, buf, 16, 2); - throw new RuntimeException("copySwapMemory failed to throw NPE"); - } catch (NullPointerException e) { - // good - } - - try { - // Check that a NULL destination throws NPE - UNSAFE.copySwapMemory(null, buf, null, 0, 16, 2); - throw new RuntimeException("copySwapMemory failed to throw NPE"); - } catch (NullPointerException e) { - // good - } - - try { // Check that a reference array destination throws IAE UNSAFE.copySwapMemory(null, buf, new Object[16], UNSAFE.arrayBaseOffset(Object[].class), 16, 8); throw new RuntimeException("copySwapMemory failed to throw NPE"); --- /dev/null 2015-12-08 13:01:58.111436025 -0800 +++ new/test/jdk/internal/misc/Unsafe/CopyMemory.java 2016-03-01 08:25:36.038001406 -0800 @@ -0,0 +1,667 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.internal.misc.Unsafe; +import java.lang.reflect.Field; + +/* + * @test + * @summary Test Unsafe.copyMemory + * @modules java.base/jdk.internal.misc + */ +public class CopyMemory { + private static final boolean DEBUG = Boolean.getBoolean("CopyMemory.DEBUG"); + + public static final long KB = 1024; + public static final long MB = KB * 1024; + public static final long GB = MB * 1024; + + private static final Unsafe UNSAFE; + private static final int SMALL_COPY_SIZE = 32; + private static final int BASE_ALIGNMENT = 16; + + static { + try { + Field f = jdk.internal.misc.Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + UNSAFE = (jdk.internal.misc.Unsafe) f.get(null); + } catch (Exception e) { + throw new RuntimeException("Unable to get Unsafe instance.", e); + } + } + + private static long alignDown(long value, long alignment) { + return value & ~(alignment - 1); + } + + private static long alignUp(long value, long alignment) { + return (value + alignment - 1) & ~(alignment - 1); + } + + private static boolean isAligned(long value, long alignment) { + return value == alignDown(value, alignment); + } + + private CopyMemory() { + } + + /** + * Generate verification data for a given offset + * + * The verification data is used to verify that the correct bytes + * have indeed been copied and byte swapped. + * + * The data is generated based on the offset (in bytes) into the + * source buffer. For a native buffer the offset is relative to + * the base pointer. For a heap array it is relative to the + * address of the first array element. + * + * This method will return the result of doing an elementSize byte + * read starting at offset (in bytes). + * + * @param offset offset into buffer + * @param elemSize size (in bytes) of the element + * + * @return the verification data, only the least significant + * elemSize*8 bits are set, zero extended + */ + private long getVerificationDataForOffset(long offset, long elemSize) { + byte[] bytes = new byte[(int)elemSize]; + + for (long i = 0; i < elemSize; i++) { + bytes[(int)i] = (byte)(offset + i); + } + + long o = UNSAFE.arrayBaseOffset(byte[].class); + + switch ((int)elemSize) { + case 1: return Byte.toUnsignedLong(UNSAFE.getByte(bytes, o)); + case 2: return Short.toUnsignedLong(UNSAFE.getShortUnaligned(bytes, o)); + case 4: return Integer.toUnsignedLong(UNSAFE.getIntUnaligned(bytes, o)); + case 8: return UNSAFE.getLongUnaligned(bytes, o); + default: throw new IllegalArgumentException("Invalid element size: " + elemSize); + } + } + + /** + * Verify byte swapped data + * + * @param ptr the data to verify + * @param srcOffset the srcOffset (in bytes) from which the copy started, + * used as key to regenerate the verification data + * @param dstOffset the offset (in bytes) in the array at which to start + * the verification, relative to the first element in the array + * @param size size (in bytes) of data to to verify + * @param elemSize size (in bytes) of the individual array elements + * + * @throws RuntimeException if an error is found + */ + private void verifySwappedData(GenericPointer ptr, long srcOffset, long dstOffset, long size, long elemSize) { + for (long offset = 0; offset < size; offset += elemSize) { + long expectedUnswapped = getVerificationDataForOffset(srcOffset + offset, elemSize); + long expected = byteSwap(expectedUnswapped, elemSize); + + long actual = getArrayElem(ptr, dstOffset + offset, elemSize); + + if (expected != actual) { + throw new RuntimeException("srcOffset: 0x" + Long.toHexString(srcOffset) + + " dstOffset: 0x" + Long.toHexString(dstOffset) + + " size: 0x" + Long.toHexString(size) + + " offset: 0x" + Long.toHexString(offset) + + " expectedUnswapped: 0x" + Long.toHexString(expectedUnswapped) + + " expected: 0x" + Long.toHexString(expected) + + " != actual: 0x" + Long.toHexString(actual)); + } + } + } + + /** + * Initialize an array with verification friendly data + * + * @param ptr pointer to the data to initialize + * @param size size (in bytes) of the data + * @param elemSize size (in bytes) of the individual elements + */ + private void initVerificationData(GenericPointer ptr, long size, long elemSize) { + for (long offset = 0; offset < size; offset++) { + byte data = (byte)getVerificationDataForOffset(offset, 1); + + if (ptr.isOnHeap()) { + UNSAFE.putByte(ptr.getObject(), ptr.getOffset() + offset, data); + } else { + UNSAFE.putByte(ptr.getOffset() + offset, data); + } + } + } + + /** + * Allocate a primitive array + * + * @param size size (in bytes) of all the array elements (elemSize * length) + * @param elemSize the size of the array elements + * + * @return a newly allocated primitive array + */ + Object allocArray(long size, long elemSize) { + int length = (int)(size / elemSize); + + switch ((int)elemSize) { + case 1: return new byte[length]; + case 2: return new short[length]; + case 4: return new int[length]; + case 8: return new long[length]; + default: + throw new IllegalArgumentException("Invalid element size: " + elemSize); + } + } + + /** + * Get the value of a primitive array entry + * + * @param ptr pointer to the data + * @param offset offset (in bytes) of the array element, relative to the first element in the array + * + * @return the array element, as an unsigned long + */ + private long getArrayElem(GenericPointer ptr, long offset, long elemSize) { + if (ptr.isOnHeap()) { + Object o = ptr.getObject(); + int index = (int)(offset / elemSize); + + if (o instanceof short[]) { + short[] arr = (short[])o; + return Short.toUnsignedLong(arr[index]); + } else if (o instanceof int[]) { + int[] arr = (int[])o; + return Integer.toUnsignedLong(arr[index]); + } else if (o instanceof long[]) { + long[] arr = (long[])o; + return arr[index]; + } else { + throw new IllegalArgumentException("Invalid object type: " + o.getClass().getName()); + } + } else { + long addr = ptr.getOffset() + offset; + + switch ((int)elemSize) { + case 1: return Byte.toUnsignedLong(UNSAFE.getByte(addr)); + case 2: return Short.toUnsignedLong(UNSAFE.getShortUnaligned(null, addr)); + case 4: return Integer.toUnsignedLong(UNSAFE.getIntUnaligned(null, addr)); + case 8: return UNSAFE.getLongUnaligned(null, addr); + default: throw new IllegalArgumentException("Invalid element size: " + elemSize); + } + } + } + + private void putValue(long addr, long elemSize, long value) { + switch ((int)elemSize) { + case 1: UNSAFE.putByte(addr, (byte)value); break; + case 2: UNSAFE.putShortUnaligned(null, addr, (short)value); break; + case 4: UNSAFE.putIntUnaligned(null, addr, (int)value); break; + case 8: UNSAFE.putLongUnaligned(null, addr, value); break; + default: throw new IllegalArgumentException("Invalid element size: " + elemSize); + } + } + + /** + * Get the size of the elements for an array + * + * @param o a primitive heap array + * + * @return the size (in bytes) of the individual array elements + */ + private long getArrayElemSize(Object o) { + if (o instanceof short[]) { + return 2; + } else if (o instanceof int[]) { + return 4; + } else if (o instanceof long[]) { + return 8; + } else { + throw new IllegalArgumentException("Invalid object type: " + o.getClass().getName()); + } + } + + /** + * Byte swap a value + * + * @param value the value to swap, only the bytes*8 least significant bits are used + * @param size size (in bytes) of the value + * + * @return the byte swapped value in the bytes*8 least significant bits + */ + private long byteSwap(long value, long size) { + switch ((int)size) { + case 2: return Short.toUnsignedLong(Short.reverseBytes((short)value)); + case 4: return Integer.toUnsignedLong(Integer.reverseBytes((int)value)); + case 8: return Long.reverseBytes(value); + default: throw new IllegalArgumentException("Invalid element size: " + size); + } + } + + /** + * Verify data which has *not* been byte swapped + * + * @param ptr the data to verify + * @param startOffset the offset (in bytes) at which to start the verification + * @param size size (in bytes) of the data to verify + * + * @throws RuntimeException if an error is found + */ + private void verifyUnswappedData(GenericPointer ptr, long startOffset, long srcOffset, long size) { + for (long i = 0; i < size; i++) { + byte expected = (byte)getVerificationDataForOffset(srcOffset + i, 1); + + byte actual; + if (ptr.isOnHeap()) { + actual = UNSAFE.getByte(ptr.getObject(), ptr.getOffset() + startOffset + i); + } else { + actual = UNSAFE.getByte(ptr.getOffset() + startOffset + i); + } + + if (expected != actual) { + throw new RuntimeException("startOffset: 0x" + Long.toHexString(startOffset) + + " srcOffset: 0x" + Long.toHexString(srcOffset) + + " size: 0x" + Long.toHexString(size) + + " i: 0x" + Long.toHexString(i) + + " expected: 0x" + Long.toHexString(expected) + + " != actual: 0x" + Long.toHexString(actual)); + } + } + } + + + /** + * Copy and byte swap data from the source to the destination + * + * This method will pre-populate the whole source and destination + * buffers with verification friendly data. It will then use + * copypMemory to fill part of the destination buffer with + * data from the source. Some space (padding) will be + * left before and after the data in the destination buffer, which + * should not be touched/overwritten by the copy call. + * + * Note: Both source and destination buffers will be overwritten! + * + * @param src source buffer to copy from + * @param srcOffset the offset (in bytes) in the source buffer, relative to + * the first array element, at which to start reading data + * @param dst destination buffer to copy to + * @param dstOffset the offset (in bytes) in the destination + * buffer, relative to the first array element, at which to + * start writing data + * @param bufSize the size (in bytes) of the src and dst arrays + * @param copyBytes the size (in bytes) of the copy to perform, + * must be a multiple of elemSize + * + * @throws RuntimeException if an error is found + */ + private void testCopy(GenericPointer src, long srcOffset, + GenericPointer dst, long dstOffset, + long bufSize, long copyBytes) { + if (srcOffset + copyBytes > bufSize) { + throw new IllegalArgumentException( + "srcOffset (" + srcOffset + ") + copyBytes (" + copyBytes + ") > bufSize (" + bufSize + ")"); + } + if (dstOffset + copyBytes > bufSize) { + throw new IllegalArgumentException( + "dstOffset (" + dstOffset + ") + copyBytes (" + copyBytes + ") > bufSize (" + bufSize + ")"); + } + + // Initialize the whole source buffer with a verification friendly pattern (no 0x00 bytes) + initVerificationData(src, bufSize, 1); + if (!src.equals(dst)) { + initVerificationData(dst, bufSize, 1); + } + + if (DEBUG) { + System.out.println("===before==="); + for (int offset = 0; offset < bufSize; offset++) { + long srcValue = getArrayElem(src, offset, 1); + long dstValue = getArrayElem(dst, offset, 1); + + System.out.println("offs=0x" + Long.toHexString(Integer.toUnsignedLong(offset)) + + " src=0x" + Long.toHexString(srcValue) + + " dst=0x" + Long.toHexString(dstValue)); + } + } + + // Copy & swap data into the middle of the destination buffer + UNSAFE.copyMemory(src.getObject(), + src.getOffset() + srcOffset, + dst.getObject(), + dst.getOffset() + dstOffset, + copyBytes); + + if (DEBUG) { + System.out.println("===after==="); + for (int offset = 0; offset < bufSize; offset++) { + long srcValue = getArrayElem(src, offset, 1); + long dstValue = getArrayElem(dst, offset, 1); + + System.out.println("offs=0x" + Long.toHexString(Integer.toUnsignedLong(offset)) + + " src=0x" + Long.toHexString(srcValue) + + " dst=0x" + Long.toHexString(dstValue)); + } + } + + // Verify the the front padding is unchanged + verifyUnswappedData(dst, 0, 0, dstOffset); + + // Verify copied data + verifyUnswappedData(dst, dstOffset, srcOffset, copyBytes); + + // Verify that the back back padding is unchanged + long frontAndDataBytes = dstOffset + copyBytes; + long trailingBytes = bufSize - frontAndDataBytes; + verifyUnswappedData(dst, frontAndDataBytes, frontAndDataBytes, trailingBytes); + } + + /** + * Test various configurations copying from one buffer to the other + * + * @param src the source buffer to copy from + * @param dst the destination buffer to copy to + * @param size size (in bytes) of the buffers + * @param elemSize size (in bytes) of the individual elements + * + * @throws RuntimeException if an error is found + */ + public void testBufferPair(GenericPointer src, GenericPointer dst, long size, long elemSize) { + // offset in source from which to start reading data + for (long srcOffset = 0; srcOffset < size; srcOffset += (src.isOnHeap() ? elemSize : 1)) { + + // offset in destination at which to start writing data + for (int dstOffset = 0; dstOffset < size; dstOffset += (dst.isOnHeap() ? elemSize : 1)) { + + // number of bytes to copy + long maxCopyBytes = Math.min(size - srcOffset, size - dstOffset); + for (long copyBytes = 0; copyBytes < maxCopyBytes; copyBytes += elemSize) { + try { + testCopy(src, srcOffset, dst, dstOffset, size, copyBytes); + } catch (RuntimeException e) { + // Wrap the exception in another exception to catch the relevant configuration data + throw new RuntimeException("testBufferPair: " + + "src=" + src + + " dst=" + dst + + " elemSize=0x" + Long.toHexString(elemSize) + + " copyBytes=0x" + Long.toHexString(copyBytes) + + " srcOffset=0x" + Long.toHexString(srcOffset) + + " dstOffset=0x" + Long.toHexString(dstOffset), + e); + } + } + } + } + } + + /** + * Test copying between various permutations of buffers + * + * @param buffers buffers to permute (src x dst) + * @param size size (in bytes) of buffers + * @param elemSize size (in bytes) of individual elements + * + * @throws RuntimeException if an error is found + */ + public void testPermuteBuffers(GenericPointer[] buffers, long size, long elemSize) { + for (int srcIndex = 0; srcIndex < buffers.length; srcIndex++) { + for (int dstIndex = 0; dstIndex < buffers.length; dstIndex++) { + testBufferPair(buffers[srcIndex], buffers[dstIndex], size, elemSize); + } + } + } + + /** + * Test copying of a specific element size + * + * @param size size (in bytes) of buffers to allocate + * @param elemSize size (in bytes) of individual elements + * + * @throws RuntimeException if an error is found + */ + private void testElemSize(long size, long elemSize) { + long buf1Raw = 0; + long buf2Raw = 0; + + try { + buf1Raw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT); + long buf1 = alignUp(buf1Raw, BASE_ALIGNMENT); + + buf2Raw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT); + long buf2 = alignUp(buf2Raw, BASE_ALIGNMENT); + + GenericPointer[] buffers = { + new GenericPointer(buf1), + new GenericPointer(buf2), + new GenericPointer(allocArray(size, elemSize)), + new GenericPointer(allocArray(size, elemSize)) + }; + + testPermuteBuffers(buffers, size, elemSize); + } finally { + if (buf1Raw != 0) { + UNSAFE.freeMemory(buf1Raw); + } + if (buf2Raw != 0) { + UNSAFE.freeMemory(buf2Raw); + } + } + } + + /** + * Verify that small copies work + */ + private void testSmallCopy() { + int smallBufSize = SMALL_COPY_SIZE; + + testElemSize(smallBufSize, 1); + } + + + /** + * Verify that large copies work + */ + private void testLargeCopy() { + long size = 2 * GB + 8; + long bufRaw = 0; + + // Check that a large native copy succeeds + try { + try { + bufRaw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT); + } catch (OutOfMemoryError e) { + // Accept failure, skip test + return; + } + + long buf = alignUp(bufRaw, BASE_ALIGNMENT); + + UNSAFE.copyMemory(null, buf, null, buf, size); + } catch (Exception e) { + throw new RuntimeException("copyMemory of large buffer failed"); + } finally { + if (bufRaw != 0) { + UNSAFE.freeMemory(bufRaw); + } + } + } + + /** + * Run positive tests + * + * @throws RuntimeException if an error is found + */ + private void testPositive() { + testSmallCopy(); + testLargeCopy(); + } + + /** + * Run negative tests, testing corner cases and the various exceptions + * + * @throws RuntimeException if an error is found + */ + private void testNegative() { + long bufRaw = 0; + + try { + bufRaw = UNSAFE.allocateMemory(1024); + long buf = alignUp(bufRaw, BASE_ALIGNMENT); + short[] arr = new short[16]; + + // Check illegal sizes + System.out.println("Testing negative size"); + try { + UNSAFE.copyMemory(null, buf, null, buf, -1); + throw new RuntimeException("copyMemory failed to throw IAE for size=-1"); + } catch (IllegalArgumentException e) { + // good + } + + System.out.println("Testing negative srcOffset"); + try { + // Check that negative srcOffset throws an IAE + UNSAFE.copyMemory(arr, -1, arr, UNSAFE.arrayBaseOffset(arr.getClass()), 16); + throw new RuntimeException("copyMemory failed to throw IAE for srcOffset=-1"); + } catch (IllegalArgumentException e) { + // good + } + + System.out.println("Testing negative destOffset"); + try { + // Check that negative dstOffset throws an IAE + UNSAFE.copyMemory(arr, UNSAFE.arrayBaseOffset(arr.getClass()), arr, -1, 16); + throw new RuntimeException("copyMemory failed to throw IAE for destOffset=-1"); + } catch (IllegalArgumentException e) { + // good + } + + System.out.println("Testing reference array"); + try { + // Check that a reference array destination throws IAE + UNSAFE.copyMemory(null, buf, new Object[16], UNSAFE.arrayBaseOffset(Object[].class), 16); + throw new RuntimeException("copyMemory failed to throw IAE"); + } catch (IllegalArgumentException e) { + // good + } + + // Check that invalid source & dest pointers throw IAEs (only relevant on 32-bit platforms) + if (UNSAFE.addressSize() == 4) { + long invalidPtr = (long)1 << 35; // Pick a random bit in upper 32 bits + + try { + // Check that an invalid (not 32-bit clean) source pointer throws IAE + UNSAFE.copyMemory(null, invalidPtr, null, buf, 16); + throw new RuntimeException("copyMemory failed to throw IAE for srcOffset 0x" + + Long.toHexString(invalidPtr)); + } catch (IllegalArgumentException e) { + // good + } + + try { + // Check that an invalid (not 32-bit clean) source pointer throws IAE + UNSAFE.copyMemory(null, buf, null, invalidPtr, 16); + throw new RuntimeException("copyMemory failed to throw IAE for destOffset 0x" + + Long.toHexString(invalidPtr)); + } catch (IllegalArgumentException e) { + // good + } + } + } finally { + if (bufRaw != 0) { + UNSAFE.freeMemory(bufRaw); + } + } + } + + /** + * Run all tests + * + * @throws RuntimeException if an error is found + */ + private void test() { + testPositive(); + testNegative(); + } + + public static void main(String[] args) { + CopyMemory cs = new CopyMemory(); + cs.test(); + } + + /** + * Helper class to represent a "pointer" - either a heap array or + * a pointer to a native buffer. + * + * In the case of a native pointer, the Object is null and the offset is + * the absolute address of the native buffer. + * + * In the case of a heap object, the Object is a primitive array, and + * the offset will be set to the base offset to the first element, meaning + * the object and the offset together form a double-register pointer. + */ + static class GenericPointer { + private final Object o; + private final long offset; + + private GenericPointer(Object o, long offset) { + this.o = o; + this.offset = offset; + } + + public String toString() { + return "GenericPointer(o={" + o + "}, offset=0x" + Long.toHexString(offset) + ")"; + } + + public boolean equals(Object other) { + if (!(other instanceof GenericPointer)) { + return false; + } + + GenericPointer otherp = (GenericPointer)other; + + return o == otherp.o && offset == otherp.offset; + } + + GenericPointer(Object o) { + this(o, UNSAFE.arrayBaseOffset(o.getClass())); + } + + GenericPointer(long offset) { + this(null, offset); + } + + public boolean isOnHeap() { + return o != null; + } + + public Object getObject() { + return o; + } + + public long getOffset() { + return offset; + } + } +}