Add unpadded array allocations.

Unpadded array allocations avoid unused bytes following arrays. API conventions
follow those of newNonMovableArray.
Dalvik implementation assume dlmalloc allocation properties, ART implementation
is handled by resizing the array post-allocation:
https://android-review.googlesource.com/83670
Add VMRuntimeTest to check the properties of the array allocation routines.
Change java.lang.reflect.Array.newInstance to compare more probable array types
before less probable ones.

Bug: 13028925.

Change-Id: Icf6bfb1aa5ad4faa062b393082c689b6118a84b5
diff --git a/libart/src/main/java/dalvik/system/VMRuntime.java b/libart/src/main/java/dalvik/system/VMRuntime.java
index 39cd326..7bbb93e 100644
--- a/libart/src/main/java/dalvik/system/VMRuntime.java
+++ b/libart/src/main/java/dalvik/system/VMRuntime.java
@@ -223,6 +223,13 @@
     public native Object newNonMovableArray(Class<?> componentType, int length);
 
     /**
+     * Returns an array of at least minLength, but potentially larger. The increased size comes from
+     * avoiding any padding after the array. The amount of padding varies depending on the
+     * componentType and the memory allocator implementation.
+     */
+    public native Object newUnpaddedArray(Class<?> componentType, int minLength);
+
+    /**
      * Returns the address of array[0]. This differs from using JNI in that JNI might lie and
      * give you the address of a copy of the array when in forcecopy mode.
      */
diff --git a/libdvm/src/main/java/dalvik/system/VMRuntime.java b/libdvm/src/main/java/dalvik/system/VMRuntime.java
index f4acf47..47823f0 100644
--- a/libdvm/src/main/java/dalvik/system/VMRuntime.java
+++ b/libdvm/src/main/java/dalvik/system/VMRuntime.java
@@ -223,6 +223,57 @@
     public native Object newNonMovableArray(Class<?> componentType, int length);
 
     /**
+     * Returns an array of at least minLength, but potentially larger. The increased size comes from
+     * avoiding any padding after the array. The amount of padding varies depending on the
+     * componentType and the memory allocator implementation.
+     */
+    public Object newUnpaddedArray(Class<?> componentType, int minLength) {
+        // Dalvik has 32bit pointers, the array header is 16bytes plus 4bytes for dlmalloc,
+        // allocations are 8byte aligned so having 4bytes of array data avoids padding.
+        if (!componentType.isPrimitive()) {
+            int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
+            return java.lang.reflect.Array.newInstance(componentType, size);
+        } else if (componentType == char.class) {
+            int bytes = 20 + (2 * minLength);
+            int alignedUpBytes = (bytes + 7) & -8;
+            int dataBytes = alignedUpBytes - 20;
+            int size = dataBytes / 2;
+            return new char[size];
+        } else if (componentType == int.class) {
+            int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
+            return new int[size];
+        } else if (componentType == byte.class) {
+            int bytes = 20 + minLength;
+            int alignedUpBytes = (bytes + 7) & -8;
+            int dataBytes = alignedUpBytes - 20;
+            int size = dataBytes;
+            return new byte[size];
+        } else if (componentType == boolean.class) {
+            int bytes = 20 + minLength;
+            int alignedUpBytes = (bytes + 7) & -8;
+            int dataBytes = alignedUpBytes - 20;
+            int size = dataBytes;
+            return new boolean[size];
+        } else if (componentType == short.class) {
+            int bytes = 20 + (2 * minLength);
+            int alignedUpBytes = (bytes + 7) & -8;
+            int dataBytes = alignedUpBytes - 20;
+            int size = dataBytes / 2;
+            return new short[size];
+        } else if (componentType == float.class) {
+            int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
+            return new float[size];
+        } else if (componentType == long.class) {
+            return new long[minLength];
+        } else if (componentType == double.class) {
+            return new double[minLength];
+        } else {
+            assert componentType == void.class;
+            throw new IllegalArgumentException("Can't allocate an array of void");
+        }
+    }
+
+    /**
      * Returns the address of array[0]. This differs from using JNI in that JNI might lie and
      * give you the address of a copy of the array when in forcecopy mode.
      */
diff --git a/luni/src/main/java/java/lang/reflect/Array.java b/luni/src/main/java/java/lang/reflect/Array.java
index 088a434..a7dacfe 100644
--- a/luni/src/main/java/java/lang/reflect/Array.java
+++ b/luni/src/main/java/java/lang/reflect/Array.java
@@ -352,16 +352,16 @@
     public static Object newInstance(Class<?> componentType, int size) throws NegativeArraySizeException {
         if (!componentType.isPrimitive()) {
             return createObjectArray(componentType, size);
-        } else if (componentType == boolean.class) {
-            return new boolean[size];
-        } else if (componentType == byte.class) {
-            return new byte[size];
         } else if (componentType == char.class) {
             return new char[size];
-        } else if (componentType == short.class) {
-            return new short[size];
         } else if (componentType == int.class) {
             return new int[size];
+        } else if (componentType == byte.class) {
+            return new byte[size];
+        } else if (componentType == boolean.class) {
+            return new boolean[size];
+        } else if (componentType == short.class) {
+            return new short[size];
         } else if (componentType == long.class) {
             return new long[size];
         } else if (componentType == float.class) {
diff --git a/luni/src/test/java/dalvik/system/VMRuntimeTest.java b/luni/src/test/java/dalvik/system/VMRuntimeTest.java
new file mode 100644
index 0000000..44af461
--- /dev/null
+++ b/luni/src/test/java/dalvik/system/VMRuntimeTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+
+package dalvik.system;
+
+import java.lang.reflect.Array;
+import junit.framework.TestCase;
+
+/**
+ * Test VMRuntime behavior.
+ */
+public final class VMRuntimeTest extends TestCase {
+
+    private void doTestNewNonMovableArray(Class<?> componentType, int step, int maxLength) {
+        // Can't create negative sized arrays.
+        try {
+            Object array = VMRuntime.getRuntime().newNonMovableArray(componentType, -1);
+            assertTrue(false);
+        } catch (NegativeArraySizeException expected) {
+        }
+
+        try {
+            Object array = VMRuntime.getRuntime().newNonMovableArray(componentType, Integer.MIN_VALUE);
+            assertTrue(false);
+        } catch (NegativeArraySizeException expected) {
+        }
+
+        // Allocate arrays in a loop and check their properties.
+        for (int i = 0; i <= maxLength; i += step) {
+            Object array = VMRuntime.getRuntime().newNonMovableArray(componentType, i);
+            assertTrue(array.getClass().isArray());
+            assertEquals(array.getClass().getComponentType(), componentType);
+            assertEquals(Array.getLength(array), i);
+        }
+    }
+
+    public void testNewNonMovableArray() {
+        // Can't create arrays with no component type.
+        try {
+            Object array = VMRuntime.getRuntime().newNonMovableArray(null, 0);
+            assertTrue(false);
+        } catch (NullPointerException expected) {
+        }
+
+        // Can't create arrays of void.
+        try {
+            Object array = VMRuntime.getRuntime().newNonMovableArray(void.class, 0);
+            assertTrue(false);
+        } catch (IllegalArgumentException expected) {
+        }
+
+        int maxLengthForLoop = 16 * 1024;
+        int step = 67;
+        doTestNewNonMovableArray(boolean.class, step, maxLengthForLoop);
+        doTestNewNonMovableArray(byte.class, step, maxLengthForLoop);
+        doTestNewNonMovableArray(char.class, step, maxLengthForLoop);
+        doTestNewNonMovableArray(short.class, step, maxLengthForLoop);
+        doTestNewNonMovableArray(int.class, step, maxLengthForLoop);
+        doTestNewNonMovableArray(long.class, step, maxLengthForLoop);
+        doTestNewNonMovableArray(float.class, step, maxLengthForLoop);
+        doTestNewNonMovableArray(double.class, step, maxLengthForLoop);
+        doTestNewNonMovableArray(Object.class, step, maxLengthForLoop);
+        doTestNewNonMovableArray(Number.class, step, maxLengthForLoop);
+        doTestNewNonMovableArray(String.class, step, maxLengthForLoop);
+        doTestNewNonMovableArray(Runnable.class, step, maxLengthForLoop);
+    }
+
+    private void doTestNewUnpaddedArray(Class<?> componentType, int step, int maxLength) {
+         // Can't create negative sized arrays.
+        try {
+            Object array = VMRuntime.getRuntime().newUnpaddedArray(componentType, -1);
+            assertTrue(false);
+        } catch (NegativeArraySizeException expected) {
+        }
+
+        try {
+            Object array = VMRuntime.getRuntime().newUnpaddedArray(componentType, Integer.MIN_VALUE);
+            assertTrue(false);
+        } catch (NegativeArraySizeException expected) {
+        }
+
+        // Allocate arrays in a loop and check their properties.
+        for (int i = 0; i <= maxLength; i += step) {
+            Object array = VMRuntime.getRuntime().newUnpaddedArray(componentType, i);
+            assertTrue(array.getClass().isArray());
+            assertEquals(array.getClass().getComponentType(), componentType);
+            assertTrue(Array.getLength(array) >= i);
+        }
+    }
+
+    public void testNewUnpaddedArray() {
+        // Can't create arrays with no component type.
+        try {
+            Object array = VMRuntime.getRuntime().newUnpaddedArray(null, 0);
+            assertTrue(false);
+        } catch (NullPointerException expected) {
+        }
+
+        // Can't create arrays of void.
+        try {
+            Object array = VMRuntime.getRuntime().newUnpaddedArray(void.class, 0);
+            assertTrue(false);
+        } catch (IllegalArgumentException expected) {
+        }
+
+        int maxLengthForLoop = 16 * 1024;
+        int step = 67;
+        doTestNewUnpaddedArray(boolean.class, step, maxLengthForLoop);
+        doTestNewUnpaddedArray(byte.class, step, maxLengthForLoop);
+        doTestNewUnpaddedArray(char.class, step, maxLengthForLoop);
+        doTestNewUnpaddedArray(short.class, step, maxLengthForLoop);
+        doTestNewUnpaddedArray(int.class, step, maxLengthForLoop);
+        doTestNewUnpaddedArray(long.class, step, maxLengthForLoop);
+        doTestNewUnpaddedArray(float.class, step, maxLengthForLoop);
+        doTestNewUnpaddedArray(double.class, step, maxLengthForLoop);
+        doTestNewUnpaddedArray(Object.class, step, maxLengthForLoop);
+        doTestNewUnpaddedArray(Number.class, step, maxLengthForLoop);
+        doTestNewUnpaddedArray(String.class, step, maxLengthForLoop);
+        doTestNewUnpaddedArray(Runnable.class, step, maxLengthForLoop);
+    }
+}
+