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;