Adding Unsafe.copyMemory for arrays
Test: art/test/testrunner/testrunner.py -t 2235-JdkUnsafeTest
Bug: 203534219
Change-Id: Ibcf5c0f124907b630e5290fd62c4ccdebe71176f
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 135b6b4..041176e 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -1983,6 +1983,14 @@
UnstartedJNIJdkUnsafeGetArrayIndexScaleForComponentType(self, method, receiver, args, result);
}
+void UnstartedRuntime::UnstartedJNIJdkUnsafeAddressSize(
+ Thread* self ATTRIBUTE_UNUSED,
+ ArtMethod* method ATTRIBUTE_UNUSED,
+ mirror::Object* receiver ATTRIBUTE_UNUSED,
+ uint32_t* args ATTRIBUTE_UNUSED,
+ JValue* result) {
+ result->SetI(sizeof(void*));
+}
void UnstartedRuntime::UnstartedJNIJdkUnsafeCompareAndSwapInt(
Thread* self,
diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h
index 16d29b8..906160f 100644
--- a/runtime/interpreter/unstarted_runtime_list.h
+++ b/runtime/interpreter/unstarted_runtime_list.h
@@ -110,6 +110,7 @@
V(UnsafePutObject, "Lsun/misc/Unsafe;", "putObject", "(Ljava/lang/Object;JLjava/lang/Object;)V") \
V(UnsafeGetArrayBaseOffsetForComponentType, "Lsun/misc/Unsafe;", "getArrayBaseOffsetForComponentType", "(Ljava/lang/Class;)I") \
V(UnsafeGetArrayIndexScaleForComponentType, "Lsun/misc/Unsafe;", "getArrayIndexScaleForComponentType", "(Ljava/lang/Class;)I") \
+ V(JdkUnsafeAddressSize, "Ljdk/internal/misc/Unsafe;", "addressSize", "()I") \
V(JdkUnsafeCompareAndSwapInt, "Ljdk/internal/misc/Unsafe;", "compareAndSwapInt", "(Ljava/lang/Object;JII)Z") \
V(JdkUnsafeGetIntVolatile, "Ljdk/internal/misc/Unsafe;", "getIntVolatile", "(Ljava/lang/Object;J)I") \
V(JdkUnsafePutObject, "Ljdk/internal/misc/Unsafe;", "putObject", "(Ljava/lang/Object;JLjava/lang/Object;)V") \
diff --git a/runtime/native/jdk_internal_misc_Unsafe.cc b/runtime/native/jdk_internal_misc_Unsafe.cc
index 6935332..307a2fa 100644
--- a/runtime/native/jdk_internal_misc_Unsafe.cc
+++ b/runtime/native/jdk_internal_misc_Unsafe.cc
@@ -43,6 +43,7 @@
// jdk/internal/misc/Unsafe.java comments).
bool ValidJniSizeArgument(jlong size) REQUIRES_SHARED(Locks::mutator_lock_) {
const jlong maybe_truncated_size = static_cast<jlong>(static_cast<size_t>(size));
+ // size is nonnegative and fits into size_t
if (LIKELY(size >= 0 && size == maybe_truncated_size)) {
return true;
}
@@ -346,19 +347,26 @@
*reinterpret_cast<jdouble*>(address) = value;
}
-static void Unsafe_copyMemory(JNIEnv *env, jobject unsafe ATTRIBUTE_UNUSED, jlong src,
- jlong dst, jlong size) {
+static void Unsafe_copyMemory0(JNIEnv *env, jobject unsafe ATTRIBUTE_UNUSED,
+ jobject srcObj, jlong srcOffset,
+ jobject dstObj, jlong dstOffset,
+ jlong size) {
ScopedFastNativeObjectAccess soa(env);
if (size == 0) {
return;
}
- // size is nonnegative and fits into size_t
if (!ValidJniSizeArgument(size)) {
DCHECK(soa.Self()->IsExceptionPending());
return;
}
const size_t memcpy_size = static_cast<size_t>(size);
- memcpy(reinterpret_cast<void *>(dst), reinterpret_cast<void *>(src), memcpy_size);
+ const size_t src_offset = static_cast<size_t>(srcOffset);
+ ObjPtr<mirror::Object> src = soa.Decode<mirror::Object>(srcObj);
+ const size_t dst_offset = static_cast<size_t>(dstOffset);
+ ObjPtr<mirror::Object> dst = soa.Decode<mirror::Object>(dstObj);
+ memcpy(reinterpret_cast<uint8_t*>(dst.Ptr()) + dst_offset,
+ reinterpret_cast<uint8_t*>(src.Ptr()) + src_offset,
+ memcpy_size);
}
static jboolean Unsafe_getBoolean(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
@@ -515,7 +523,7 @@
FAST_NATIVE_METHOD(Unsafe, allocateMemory, "(J)J"),
FAST_NATIVE_METHOD(Unsafe, freeMemory, "(J)V"),
FAST_NATIVE_METHOD(Unsafe, setMemory, "(JJB)V"),
- FAST_NATIVE_METHOD(Unsafe, copyMemory, "(JJJ)V"),
+ FAST_NATIVE_METHOD(Unsafe, copyMemory0, "(Ljava/lang/Object;JLjava/lang/Object;JJ)V"),
FAST_NATIVE_METHOD(Unsafe, getBoolean, "(Ljava/lang/Object;J)Z"),
FAST_NATIVE_METHOD(Unsafe, getByte, "(Ljava/lang/Object;J)B"),
diff --git a/test/2235-JdkUnsafeTest/src/Main.java b/test/2235-JdkUnsafeTest/src/Main.java
index 88eded3..1c644c7 100644
--- a/test/2235-JdkUnsafeTest/src/Main.java
+++ b/test/2235-JdkUnsafeTest/src/Main.java
@@ -53,6 +53,11 @@
}
}
+ private static void expectThrow(Class<?> exceptionClass, String msg) {
+ System.out.println(msg + " : expected " + exceptionClass.getName());
+ System.exit(1);
+ }
+
private static Unsafe getUnsafe() throws NoSuchFieldException, IllegalAccessException {
Class<?> unsafeClass = Unsafe.class;
Field f = unsafeClass.getDeclaredField("theUnsafe");
@@ -70,6 +75,7 @@
testCompareAndSet(unsafe);
testGetAndPutVolatile(unsafe);
testGetAcquireAndPutRelease(unsafe);
+ testCopyMemory(unsafe);
}
private static void testArrayBaseOffset(Unsafe unsafe) {
@@ -379,6 +385,103 @@
"Unsafe.getObjectAcquire(Object, long)");
}
+ private static void testCopyMemory(Unsafe unsafe) {
+ int size = 4 * 1024;
+
+ int intSize = 4;
+ int[] inputInts = new int[size / intSize];
+ for (int i = 0; i != inputInts.length; ++i) {
+ inputInts[i] = ((int)i) + 1;
+ }
+ int[] outputInts = new int[size / intSize];
+ unsafe.copyMemory(inputInts, Unsafe.ARRAY_INT_BASE_OFFSET,
+ outputInts, Unsafe.ARRAY_INT_BASE_OFFSET,
+ size);
+ for (int i = 0; i != inputInts.length; ++i) {
+ check(inputInts[i], outputInts[i], "unsafe.copyMemory/int");
+ }
+
+ int longSize = 8;
+ long[] inputLongs = new long[size / longSize];
+ for (int i = 0; i != inputLongs.length; ++i) {
+ inputLongs[i] = ((long)i) + 1L;
+ }
+ long[] outputLongs = new long[size / longSize];
+ unsafe.copyMemory(inputLongs, 0, outputLongs, 0, size);
+ unsafe.copyMemory(inputLongs, Unsafe.ARRAY_LONG_BASE_OFFSET,
+ outputLongs, Unsafe.ARRAY_LONG_BASE_OFFSET,
+ size);
+ for (int i = 0; i != inputLongs.length; ++i) {
+ check(inputLongs[i], outputLongs[i], "unsafe.copyMemory/long");
+ }
+
+ int floatSize = 4;
+ float[] inputFloats = new float[size / floatSize];
+ for (int i = 0; i != inputFloats.length; ++i) {
+ inputFloats[i] = ((float)i) + 0.5f;
+ }
+ float[] outputFloats = new float[size / floatSize];
+ unsafe.copyMemory(inputFloats, 0, outputFloats, 0, size);
+ unsafe.copyMemory(inputFloats, Unsafe.ARRAY_FLOAT_BASE_OFFSET,
+ outputFloats, Unsafe.ARRAY_FLOAT_BASE_OFFSET,
+ size);
+ for (int i = 0; i != inputFloats.length; ++i) {
+ check(inputFloats[i], outputFloats[i], "unsafe.copyMemory/float");
+ }
+
+ int doubleSize = 8;
+ double[] inputDoubles = new double[size / doubleSize];
+ for (int i = 0; i != inputDoubles.length; ++i) {
+ inputDoubles[i] = ((double)i) + 0.5;
+ }
+ double[] outputDoubles = new double[size / doubleSize];
+ unsafe.copyMemory(inputDoubles, Unsafe.ARRAY_DOUBLE_BASE_OFFSET,
+ outputDoubles, Unsafe.ARRAY_DOUBLE_BASE_OFFSET,
+ size);
+ for (int i = 0; i != inputDoubles.length; ++i) {
+ check(inputDoubles[i], outputDoubles[i], "unsafe.copyMemory/double");
+ }
+
+ // check the version that works with memory pointers
+ long srcMemory = jdkUnsafeTestMalloc(size);
+ // use the integer array to fill the source
+ unsafe.copyMemory(inputInts, Unsafe.ARRAY_INT_BASE_OFFSET,
+ null, srcMemory,
+ size);
+ long dstMemory = jdkUnsafeTestMalloc(size);
+
+ unsafe.copyMemory(srcMemory, dstMemory, size);
+ for (int i = 0; i != size; ++i) {
+ check(unsafe.getByte(srcMemory + i),
+ unsafe.getByte(dstMemory + i),
+ "unsafe.copyMemory/memoryAddress");
+ }
+
+ jdkUnsafeTestFree(dstMemory);
+ jdkUnsafeTestFree(srcMemory);
+
+ try {
+ TestClass srcObj = new TestClass();
+ srcObj.intVar = 12345678;
+ int[] dstArray = new int[1];
+ unsafe.copyMemory(srcObj, unsafe.objectFieldOffset(TestClass.class, "intVar"),
+ dstArray, Unsafe.ARRAY_INT_BASE_OFFSET,
+ 4);
+ expectThrow(RuntimeException.class, "unsafe.copyMemory/non-array-src");
+ } catch (RuntimeException e) {
+ }
+
+ try {
+ int[] srcArray = { 12345678 };
+ TestClass dstObj = new TestClass();
+ unsafe.copyMemory(srcArray, Unsafe.ARRAY_INT_BASE_OFFSET,
+ dstObj, unsafe.objectFieldOffset(TestClass.class, "intVar"),
+ 4);
+ expectThrow(RuntimeException.class, "unsafe.copyMemory/non-array-dst");
+ } catch (RuntimeException e) {
+ }
+ }
+
private static class TestClass {
public int intVar = 0;
public long longVar = 0;