This commit is contained in:
Alejandro Murillo 2016-03-14 14:28:53 -07:00
commit 58fcd079ce
4 changed files with 1680 additions and 323 deletions

View File

@ -40,6 +40,15 @@ import jdk.internal.HotSpotIntrinsicCandidate;
* Although the class and all methods are public, use of this class is
* limited because only trusted code can obtain instances of it.
*
* <em>Note:</em> 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 @@ public final class Unsafe {
@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
*
* <em>Note:</em> 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
* (<em>Note:</em> 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
* (<em>Note:</em> 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
* (<em>Note:</em> 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
* (<em>Note:</em> 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
* (<em>Note:</em> 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 @@ public final class Unsafe {
* 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
* <em>Note:</em> 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 @@ public final class Unsafe {
* @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
* (<em>Note:</em> 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 @@ public final class Unsafe {
* #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
* <em>Note:</em> 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
* (<em>Note:</em> 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 @@ public final class Unsafe {
* If the effective address and length are (resp.) even modulo 4 or 2,
* the stores take place in units of 'int' or 'short'.
*
* <em>Note:</em> 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
@ -426,6 +687,19 @@ public final class Unsafe {
setMemory(null, address, bytes, value);
}
/**
* Validate the arguments to setMemory
*
* @throws RuntimeException if the arguments are invalid
* (<em>Note:</em> 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 @@ public final class Unsafe {
* If the effective addresses and length are (resp.) even modulo 4 or 2,
* the transfer takes place in units of 'int' or 'short'.
*
* <em>Note:</em> 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 <em>single-register</em> addressing mode,
@ -458,15 +751,22 @@ public final class Unsafe {
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
* (<em>Note:</em> 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 @@ public final class Unsafe {
* as discussed in {@link #getInt(Object,long)}. When the object reference is null,
* the offset supplies an absolute base address.
*
* <em>Note:</em> 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 @@ public final class Unsafe {
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 @@ public final class Unsafe {
* #allocateMemory} or {@link #reallocateMemory}. The address passed to
* this method may be null, in which case no action is taken.
*
* <em>Note:</em> 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
* (<em>Note:</em> 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 @@ public final class Unsafe {
* {@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 @@ public final class Unsafe {
* 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 @@ public final class Unsafe {
* 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 @@ public final class Unsafe {
* 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 @@ public final class Unsafe {
* 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 @@ public final class Unsafe {
* @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 @@ public final class Unsafe {
* @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 @@ public final class Unsafe {
* 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 @@ public final class Unsafe {
* 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 @@ public final class Unsafe {
* @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.
@ -765,6 +1161,59 @@ public final class Unsafe {
public native Object allocateInstance(Class<?> cls)
throws InstantiationException;
/**
* Allocates an array of a given type, but does not do zeroing.
* <p>
* This method should only be used in the very rare cases where a high-performance code
* overwrites the destination array completely, and compilers cannot assist in zeroing elimination.
* In an overwhelming majority of cases, a normal Java allocation should be used instead.
* <p>
* Users of this method are <b>required</b> to overwrite the initial (garbage) array contents
* before allowing untrusted code, or code in other threads, to observe the reference
* to the newly allocated array. In addition, the publication of the array reference must be
* safe according to the Java Memory Model requirements.
* <p>
* The safest approach to deal with an uninitialized array is to keep the reference to it in local
* variable at least until the initialization is complete, and then publish it <b>once</b>, either
* by writing it to a <em>volatile</em> field, or storing it into a <em>final</em> field in constructor,
* or issuing a {@link #storeFence} before publishing the reference.
* <p>
* @implnote This method can only allocate primitive arrays, to avoid garbage reference
* elements that could break heap integrity.
*
* @param componentType array component type to allocate
* @param length array size to allocate
* @throws IllegalArgumentException if component type is null, or not a primitive class;
* or the length is negative
*/
public Object allocateUninitializedArray(Class<?> componentType, int length) {
if (componentType == null) {
throw new IllegalArgumentException("Component type is null");
}
if (!componentType.isPrimitive()) {
throw new IllegalArgumentException("Component type is not primitive");
}
if (length < 0) {
throw new IllegalArgumentException("Negative length");
}
return allocateUninitializedArray0(componentType, length);
}
@HotSpotIntrinsicCandidate
private Object allocateUninitializedArray0(Class<?> componentType, int length) {
// These fallbacks provide zeroed arrays, but intrinsic is not required to
// return the zeroed arrays.
if (componentType == byte.class) return new byte[length];
if (componentType == boolean.class) return new boolean[length];
if (componentType == short.class) return new short[length];
if (componentType == char.class) return new char[length];
if (componentType == int.class) return new int[length];
if (componentType == float.class) return new float[length];
if (componentType == long.class) return new long[length];
if (componentType == double.class) return new double[length];
return null;
}
/** Throws the exception without telling the verifier. */
public native void throwException(Throwable ee);
@ -1290,7 +1739,13 @@ public final class Unsafe {
* @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 +2173,6 @@ public final class Unsafe {
}
// 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 +2272,26 @@ public final class Unsafe {
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();
}

File diff suppressed because it is too large Load Diff

View File

@ -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;
}
}
}

View File

@ -586,22 +586,6 @@ public class CopySwap {
}
}
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);