Don't hardcode object layout in Unsafe and space_test.

Add a test for Unsafe.arrayBaseOffset() and Unsafe.arrayIndexScale().

Change-Id: I9cbdb79a4a7ee055129f41811a117910c8b2febd
diff --git a/build/Android.libarttest.mk b/build/Android.libarttest.mk
index f946d91..65b78c9 100644
--- a/build/Android.libarttest.mk
+++ b/build/Android.libarttest.mk
@@ -17,7 +17,8 @@
 LIBARTTEST_COMMON_SRC_FILES := \
 	test/JniTest/jni_test.cc \
 	test/ReferenceMap/stack_walk_refmap_jni.cc \
-	test/StackWalk/stack_walk_jni.cc
+	test/StackWalk/stack_walk_jni.cc \
+	test/UnsafeTest/unsafe_test.cc
 
 # $(1): target or host
 define build-libarttest
diff --git a/runtime/gc/space/space_test.cc b/runtime/gc/space/space_test.cc
index 9989ffe..6d07a60 100644
--- a/runtime/gc/space/space_test.cc
+++ b/runtime/gc/space/space_test.cc
@@ -39,20 +39,23 @@
     Runtime::Current()->GetHeap()->AddSpace(space);
   }
   void InstallClass(mirror::Object* o, size_t size) NO_THREAD_SAFETY_ANALYSIS {
-    // Note the minimum size, which is the size of a zero-length byte array, is 12.
-    EXPECT_GE(size, static_cast<size_t>(12));
+    // Note the minimum size, which is the size of a zero-length byte array.
+    EXPECT_GE(size, SizeOfZeroLengthByteArray());
     SirtRef<mirror::ClassLoader> null_loader(Thread::Current(), NULL);
     mirror::Class* byte_array_class = Runtime::Current()->GetClassLinker()->FindClass("[B", null_loader);
     EXPECT_TRUE(byte_array_class != NULL);
     o->SetClass(byte_array_class);
     mirror::Array* arr = o->AsArray();
-    // size_t header_size = sizeof(mirror::Object) + 4;
-    size_t header_size = arr->DataOffset(1).Uint32Value();
+    size_t header_size = SizeOfZeroLengthByteArray();
     int32_t length = size - header_size;
     arr->SetLength(length);
     EXPECT_EQ(arr->SizeOf(), size);
   }
 
+  static size_t SizeOfZeroLengthByteArray() {
+    return mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimByte)).Uint32Value();
+  }
+
   static MallocSpace* CreateDlMallocSpace(const std::string& name, size_t initial_size, size_t growth_limit,
                                           size_t capacity, byte* requested_begin) {
     return DlMallocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin);
@@ -355,9 +358,10 @@
   mirror::Object* lots_of_objects[1024];
   for (size_t i = 0; i < arraysize(lots_of_objects); i++) {
     size_t allocation_size = 0;
-    lots_of_objects[i] = space->Alloc(self, 16, &allocation_size);
+    size_t size_of_zero_length_byte_array = SizeOfZeroLengthByteArray();
+    lots_of_objects[i] = space->Alloc(self, size_of_zero_length_byte_array, &allocation_size);
     EXPECT_TRUE(lots_of_objects[i] != nullptr);
-    InstallClass(lots_of_objects[i], 16);
+    InstallClass(lots_of_objects[i], size_of_zero_length_byte_array);
     EXPECT_EQ(allocation_size, space->AllocationSize(lots_of_objects[i]));
   }
 
@@ -436,9 +440,10 @@
         alloc_size = object_size;
       } else {
         alloc_size = test_rand(&rand_seed) % static_cast<size_t>(-object_size);
-        // Note the minimum size, which is the size of a zero-length byte array, is 12.
-        if (alloc_size < 12) {
-          alloc_size = 12;
+        // Note the minimum size, which is the size of a zero-length byte array.
+        size_t size_of_zero_length_byte_array = SizeOfZeroLengthByteArray();
+        if (alloc_size < size_of_zero_length_byte_array) {
+          alloc_size = size_of_zero_length_byte_array;
         }
       }
       mirror::Object* object;
@@ -562,6 +567,10 @@
 }
 
 void SpaceTest::SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size, CreateSpaceFn create_space) {
+  if (object_size < SizeOfZeroLengthByteArray()) {
+    // Too small for the object layout/model.
+    return;
+  }
   size_t initial_size = 4 * MB;
   size_t growth_limit = 8 * MB;
   size_t capacity = 16 * MB;
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 922e642..c6faf44 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -80,6 +80,14 @@
     Object* obj = reinterpret_cast<Object*>(args[0]);
     Object* newValue = reinterpret_cast<Object*>(args[3]);
     obj->SetFieldObject(MemberOffset((static_cast<uint64_t>(args[2]) << 32) | args[1]), newValue, false);
+  } else if (name == "int sun.misc.Unsafe.getArrayBaseOffsetForComponentType(java.lang.Class)") {
+    mirror::Class* component = reinterpret_cast<Object*>(args[0])->AsClass();
+    Primitive::Type primitive_type = component->GetPrimitiveType();
+    result->SetI(mirror::Array::DataOffset(Primitive::ComponentSize(primitive_type)).Int32Value());
+  } else if (name == "int sun.misc.Unsafe.getArrayIndexScaleForComponentType(java.lang.Class)") {
+    mirror::Class* component = reinterpret_cast<Object*>(args[0])->AsClass();
+    Primitive::Type primitive_type = component->GetPrimitiveType();
+    result->SetI(Primitive::ComponentSize(primitive_type));
   } else {
     LOG(FATAL) << "Attempt to invoke native method in non-started runtime: " << name;
   }
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index 6c22003..6727862 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -16,6 +16,7 @@
 
 #include "gc/accounting/card_table-inl.h"
 #include "jni_internal.h"
+#include "mirror/array.h"
 #include "mirror/object.h"
 #include "mirror/object-inl.h"
 #include "scoped_fast_native_object_access.h"
@@ -153,6 +154,20 @@
   obj->SetFieldObject(MemberOffset(offset), newValue, false);
 }
 
+static jint Unsafe_getArrayBaseOffsetForComponentType(JNIEnv* env, jclass, jobject component_class) {
+  ScopedFastNativeObjectAccess soa(env);
+  mirror::Class* component = soa.Decode<mirror::Class*>(component_class);
+  Primitive::Type primitive_type = component->GetPrimitiveType();
+  return mirror::Array::DataOffset(Primitive::ComponentSize(primitive_type)).Int32Value();
+}
+
+static jint Unsafe_getArrayIndexScaleForComponentType(JNIEnv* env, jclass, jobject component_class) {
+  ScopedFastNativeObjectAccess soa(env);
+  mirror::Class* component = soa.Decode<mirror::Class*>(component_class);
+  Primitive::Type primitive_type = component->GetPrimitiveType();
+  return Primitive::ComponentSize(primitive_type);
+}
+
 static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(Unsafe, compareAndSwapInt, "!(Ljava/lang/Object;JII)Z"),
   NATIVE_METHOD(Unsafe, compareAndSwapLong, "!(Ljava/lang/Object;JJJ)Z"),
@@ -172,6 +187,8 @@
   NATIVE_METHOD(Unsafe, getObject, "!(Ljava/lang/Object;J)Ljava/lang/Object;"),
   NATIVE_METHOD(Unsafe, putObject, "!(Ljava/lang/Object;JLjava/lang/Object;)V"),
   NATIVE_METHOD(Unsafe, putOrderedObject, "!(Ljava/lang/Object;JLjava/lang/Object;)V"),
+  NATIVE_METHOD(Unsafe, getArrayBaseOffsetForComponentType, "!(Ljava/lang/Class;)I"),
+  NATIVE_METHOD(Unsafe, getArrayIndexScaleForComponentType, "!(Ljava/lang/Class;)I"),
 };
 
 void register_sun_misc_Unsafe(JNIEnv* env) {
diff --git a/test/Android.mk b/test/Android.mk
index d716f9b..4d47651 100644
--- a/test/Android.mk
+++ b/test/Android.mk
@@ -50,7 +50,8 @@
 	ParallelGC \
 	ReferenceMap \
 	StackWalk \
-	ThreadStress
+	ThreadStress \
+	UnsafeTest
 
 # TODO: Enable when the StackWalk2 tests are passing
 #	StackWalk2 \
diff --git a/test/UnsafeTest/UnsafeTest.java b/test/UnsafeTest/UnsafeTest.java
new file mode 100644
index 0000000..f3d52896
--- /dev/null
+++ b/test/UnsafeTest/UnsafeTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Field;
+import sun.misc.Unsafe;
+
+public class UnsafeTest {
+  static {
+    System.loadLibrary("arttest");
+  }
+
+  private static void check(int actual, int expected, String msg) {
+    if (actual != expected) {
+      System.logE(msg + " : " + actual + " != " + expected);
+      System.exit(-1);
+    }
+  }
+
+  private static Unsafe getUnsafe() throws Exception {
+    Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
+    Field f = unsafeClass.getDeclaredField("theUnsafe");
+    f.setAccessible(true);
+    return (Unsafe) f.get(null);
+  }
+
+  public static void main(String[] args) throws Exception {
+    Unsafe unsafe = getUnsafe();
+    check(unsafe.arrayBaseOffset(boolean[].class), vmArrayBaseOffset(boolean[].class),
+        "Unsafe.arrayBaseOffset(boolean[])");
+    check(unsafe.arrayBaseOffset(byte[].class), vmArrayBaseOffset(byte[].class),
+        "Unsafe.arrayBaseOffset(byte[])");
+    check(unsafe.arrayBaseOffset(char[].class), vmArrayBaseOffset(char[].class),
+        "Unsafe.arrayBaseOffset(char[])");
+    check(unsafe.arrayBaseOffset(double[].class), vmArrayBaseOffset(double[].class),
+        "Unsafe.arrayBaseOffset(double[])");
+    check(unsafe.arrayBaseOffset(float[].class), vmArrayBaseOffset(float[].class),
+        "Unsafe.arrayBaseOffset(float[])");
+    check(unsafe.arrayBaseOffset(int[].class), vmArrayBaseOffset(int[].class),
+        "Unsafe.arrayBaseOffset(int[])");
+    check(unsafe.arrayBaseOffset(long[].class), vmArrayBaseOffset(long[].class),
+        "Unsafe.arrayBaseOffset(long[])");
+    check(unsafe.arrayBaseOffset(Object[].class), vmArrayBaseOffset(Object[].class),
+        "Unsafe.arrayBaseOffset(Object[])");
+
+    check(unsafe.arrayIndexScale(boolean[].class), vmArrayIndexScale(boolean[].class),
+        "Unsafe.arrayIndexScale(boolean[])");
+    check(unsafe.arrayIndexScale(byte[].class), vmArrayIndexScale(byte[].class),
+        "Unsafe.arrayIndexScale(byte[])");
+    check(unsafe.arrayIndexScale(char[].class), vmArrayIndexScale(char[].class),
+        "Unsafe.arrayIndexScale(char[])");
+    check(unsafe.arrayIndexScale(double[].class), vmArrayIndexScale(double[].class),
+        "Unsafe.arrayIndexScale(double[])");
+    check(unsafe.arrayIndexScale(float[].class), vmArrayIndexScale(float[].class),
+        "Unsafe.arrayIndexScale(float[])");
+    check(unsafe.arrayIndexScale(int[].class), vmArrayIndexScale(int[].class),
+        "Unsafe.arrayIndexScale(int[])");
+    check(unsafe.arrayIndexScale(long[].class), vmArrayIndexScale(long[].class),
+        "Unsafe.arrayIndexScale(long[])");
+    check(unsafe.arrayIndexScale(Object[].class), vmArrayIndexScale(Object[].class),
+        "Unsafe.arrayIndexScale(Object[])");
+  }
+
+  private static native int vmArrayBaseOffset(Class clazz);
+  private static native int vmArrayIndexScale(Class clazz);
+}
diff --git a/test/UnsafeTest/unsafe_test.cc b/test/UnsafeTest/unsafe_test.cc
new file mode 100644
index 0000000..e36ee14
--- /dev/null
+++ b/test/UnsafeTest/unsafe_test.cc
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni.h"
+#include "mirror/array.h"
+#include "mirror/art_method-inl.h"
+#include "mirror/class.h"
+#include "mirror/class-inl.h"
+#include "mirror/object-inl.h"
+#include "scoped_thread_state_change.h"
+
+namespace art {
+
+extern "C" JNIEXPORT jint JNICALL Java_UnsafeTest_vmArrayBaseOffset(JNIEnv* env, jclass, jobject classObj) {
+  ScopedObjectAccess soa(env);
+  mirror::Class* klass = soa.Decode<mirror::Class*>(classObj);
+  return mirror::Array::DataOffset(
+      Primitive::ComponentSize(klass->GetComponentType()->GetPrimitiveType())).Int32Value();
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_UnsafeTest_vmArrayIndexScale(JNIEnv* env, jclass, jobject classObj) {
+  ScopedObjectAccess soa(env);
+  mirror::Class* klass = soa.Decode<mirror::Class*>(classObj);
+  return Primitive::ComponentSize(klass->GetComponentType()->GetPrimitiveType());
+}
+
+}  // namespace art