Add emulation for java.lang.reflect.Array.
Change-Id: Ie6c4b232d2d4c582cc0b8bfd6d00f86700f03c6d
Review-Link: https://gwt-review.googlesource.com/#/c/19260/
diff --git a/user/super/com/google/gwt/emul/java/lang/reflect/Array.java b/user/super/com/google/gwt/emul/java/lang/reflect/Array.java
new file mode 100644
index 0000000..f9a3a6c
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/lang/reflect/Array.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * 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 java.lang.reflect;
+
+import static javaemul.internal.InternalPreconditions.checkArgument;
+import static javaemul.internal.InternalPreconditions.checkNotNull;
+
+import javaemul.internal.ArrayHelper;
+
+/**
+ * See <a
+ * href="http://java.sun.com/javase/6/docs/api/java/lang/reflect/Array.html">the
+ * official Java API doc</a> for details.
+ */
+public final class Array {
+
+ public static Object get(Object array, int index) {
+ if (array instanceof boolean[]) {
+ return getBooleanImpl(array, index);
+ } else if (array instanceof byte[]) {
+ return getByteImpl(array, index);
+ } else if (array instanceof char[]) {
+ return getCharImpl(array, index);
+ } else if (array instanceof double[]) {
+ return getDoubleImpl(array, index);
+ } else if (array instanceof float[]) {
+ return getFloatImpl(array, index);
+ } else if (array instanceof int[]) {
+ return getIntImpl(array, index);
+ } else if (array instanceof long[]) {
+ return getLongImpl(array, index);
+ } else if (array instanceof short[]) {
+ return getShortImpl(array, index);
+ } else {
+ checkArgument(array instanceof Object[]);
+ Object[] typedArray = (Object[]) array;
+ return typedArray[index];
+ }
+ }
+
+ public static boolean getBoolean(Object array, int index) {
+ checkArgument(array instanceof boolean[]);
+ return getBooleanImpl(array, index);
+ }
+
+ private static boolean getBooleanImpl(Object array, int index) {
+ boolean[] typedArray = (boolean[]) array;
+ return typedArray[index];
+ }
+
+ public static byte getByte(Object array, int index) {
+ checkArgument(array instanceof byte[]);
+ return getByteImpl(array, index);
+ }
+
+ private static byte getByteImpl(Object array, int index) {
+ byte[] typedArray = (byte[]) array;
+ return typedArray[index];
+ }
+
+ public static char getChar(Object array, int index) {
+ checkArgument(array instanceof char[]);
+ return getCharImpl(array, index);
+ }
+
+ private static char getCharImpl(Object array, int index) {
+ char[] typedArray = (char[]) array;
+ return typedArray[index];
+ }
+
+ public static double getDouble(Object array, int index) {
+ checkArgument(array instanceof double[]);
+ return getDoubleImpl(array, index);
+ }
+
+ private static double getDoubleImpl(Object array, int index) {
+ double[] typedArray = (double[]) array;
+ return typedArray[index];
+ }
+
+ public static float getFloat(Object array, int index) {
+ checkArgument(array instanceof float[]);
+ return getFloatImpl(array, index);
+ }
+
+ private static float getFloatImpl(Object array, int index) {
+ float[] typedArray = (float[]) array;
+ return typedArray[index];
+ }
+
+ public static int getInt(Object array, int index) {
+ checkArgument(array instanceof int[]);
+ return getIntImpl(array, index);
+ }
+
+ private static int getIntImpl(Object array, int index) {
+ int[] typedArray = (int[]) array;
+ return typedArray[index];
+ }
+
+ public static int getLength(Object array) {
+ checkNotNull(array);
+ return ArrayHelper.getLength(array);
+ }
+
+ public static long getLong(Object array, int index) {
+ checkArgument(array instanceof long[]);
+ return getLongImpl(array, index);
+ }
+
+ private static long getLongImpl(Object array, int index) {
+ long[] typedArray = (long[]) array;
+ return typedArray[index];
+ }
+
+ public static short getShort(Object array, int index) {
+ checkArgument(array instanceof short[]);
+ return getShortImpl(array, index);
+ }
+
+ private static short getShortImpl(Object array, int index) {
+ short[] typedArray = (short[]) array;
+ return typedArray[index];
+ }
+
+ public static void set(Object array, int index, Object value) {
+ if (array instanceof boolean[]) {
+ setBooleanImpl(array, index, (Boolean) value);
+ } else if (array instanceof byte[]) {
+ setByteImpl(array, index, (Byte) value);
+ } else if (array instanceof char[]) {
+ setCharImpl(array, index, (Character) value);
+ } else if (array instanceof double[]) {
+ setDoubleImpl(array, index, (Double) value);
+ } else if (array instanceof float[]) {
+ setFloatImpl(array, index, (Float) value);
+ } else if (array instanceof int[]) {
+ setIntImpl(array, index, (Integer) value);
+ } else if (array instanceof long[]) {
+ setLongImpl(array, index, (Long) value);
+ } else if (array instanceof short[]) {
+ setShortImpl(array, index, (Short) value);
+ } else {
+ checkArgument(array instanceof Object[]);
+ Object[] typedArray = (Object[]) array;
+ typedArray[index] = value;
+ }
+ }
+
+ public static void setBoolean(Object array, int index, boolean value) {
+ checkArgument(array instanceof boolean[]);
+ setBooleanImpl(array, index, value);
+ }
+
+ private static void setBooleanImpl(Object array, int index, boolean value) {
+ boolean[] typedArray = (boolean[]) array;
+ typedArray[index] = value;
+ }
+
+ public static void setByte(Object array, int index, byte value) {
+ checkArgument(array instanceof byte[]);
+ setByteImpl(array, index, value);
+ }
+
+ private static void setByteImpl(Object array, int index, byte value) {
+ byte[] typedArray = (byte[]) array;
+ typedArray[index] = value;
+ }
+
+ public static void setChar(Object array, int index, char value) {
+ checkArgument(array instanceof char[]);
+ setCharImpl(array, index, value);
+ }
+
+ private static void setCharImpl(Object array, int index, char value) {
+ char[] typedArray = (char[]) array;
+ typedArray[index] = value;
+ }
+
+ public static void setDouble(Object array, int index, double value) {
+ checkArgument(array instanceof double[]);
+ setDoubleImpl(array, index, value);
+ }
+
+ private static void setDoubleImpl(Object array,int index, double value) {
+ double[] typedArray = (double[]) array;
+ typedArray[index] = value;
+ }
+
+ public static void setFloat(Object array, int index, float value) {
+ checkArgument(array instanceof float[]);
+ setFloatImpl(array, index, value);
+ }
+
+ private static void setFloatImpl(Object array, int index, float value) {
+ float[] typedArray = (float[]) array;
+ typedArray[index] = value;
+ }
+
+ public static void setInt(Object array, int index, int value) {
+ checkArgument(array instanceof int[]);
+ setIntImpl(array, index, value);
+ }
+
+ private static void setIntImpl(Object array, int index, int value) {
+ int[] typedArray = (int[]) array;
+ typedArray[index] = value;
+ }
+
+ public static void setLong(Object array, int index, long value) {
+ checkArgument(array instanceof long[]);
+ setLongImpl(array, index, value);
+ }
+
+ private static void setLongImpl(Object array, int index, long value) {
+ long[] typedArray = (long[]) array;
+ typedArray[index] = value;
+ }
+
+ public static void setShort(Object array, int index, short value) {
+ checkArgument(array instanceof short[]);
+ setShortImpl(array, index, value);
+ }
+
+ private static void setShortImpl(Object array, int index, short value) {
+ short[] typedArray = (short[]) array;
+ typedArray[index] = value;
+ }
+
+ // There is a good reason to not implement the generic newInstance methods:
+ //
+ // In GWT we would need access from a class literal to the castable type map, which
+ // could be done, but would require some serious work.
+ // But it would also mean that we can not compute all Array types being instantiated anymore and
+ // thus would have to retain more castable type information (bloat).
+ //
+ // In J2CL the problem is slightly different. Most checking is deferred to runtime in J2CL and
+ // we retain constructor functions of leaf types. To ensure that we are not just retaining
+ // all constructors in a program we make sure that access to these always gets inlined with
+ // array creation. By allowing a generic method we would not be able to ensure this anymore
+ // and might cause serious bloat in a program.
+ // A middle ground for J2CL could be to treat this method special in the compiler and fail the
+ // compile if its not called with a compile time constant for the class literal.
+
+ // Not implemented:
+ // public static Object newInstance(Class<?> componentType, int... dimensions)
+ // public static Object newInstance(Class<?> componentType, int length)
+
+ private Array() {
+ }
+}
diff --git a/user/test/com/google/gwt/emultest/EmulSuite.java b/user/test/com/google/gwt/emultest/EmulSuite.java
index 483ae7a..155d866 100644
--- a/user/test/com/google/gwt/emultest/EmulSuite.java
+++ b/user/test/com/google/gwt/emultest/EmulSuite.java
@@ -41,6 +41,7 @@
import com.google.gwt.emultest.java.lang.ThrowableStackTraceEmulTest;
import com.google.gwt.emultest.java.lang.ThrowableTest;
import com.google.gwt.emultest.java.lang.TypeTest;
+import com.google.gwt.emultest.java.lang.reflect.ArrayTest;
import com.google.gwt.emultest.java.math.MathContextTest;
import com.google.gwt.emultest.java.math.MathContextWithObfuscatedEnumsTest;
import com.google.gwt.emultest.java.math.RoundingModeTest;
@@ -91,6 +92,9 @@
ThrowableStackTraceEmulTest.class,
TypeTest.class,
+ // java.lang.reflect
+ ArrayTest.class,
+
//-- java.math
// BigDecimal is tested in {@link BigDecimalSuite}
// BigInteger is tested in {@link BigIntegerSuite}
diff --git a/user/test/com/google/gwt/emultest/java/lang/reflect/ArrayTest.java b/user/test/com/google/gwt/emultest/java/lang/reflect/ArrayTest.java
new file mode 100644
index 0000000..420248c
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/lang/reflect/ArrayTest.java
@@ -0,0 +1,450 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * 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 com.google.gwt.emultest.java.lang.reflect;
+
+import com.google.gwt.junit.client.GWTTestCase;
+
+import java.lang.reflect.Array;
+
+/** Tests for java.lang.reflect.Array. */
+public final class ArrayTest extends GWTTestCase {
+
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.emultest.EmulSuite";
+ }
+
+ public void testGet() {
+ try {
+ Array.get(null, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.get(new Object(), 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ assertEquals("1", Array.get(new Object[] {"1"}, 0));
+ assertEquals(Boolean.TRUE, Array.get(new boolean[] {true}, 0));
+ assertEquals(new Byte((byte) 1), Array.get(new byte[] {1}, 0));
+ assertEquals(new Character('1'), Array.get(new char[] {'1'}, 0));
+ assertEquals(new Double(1.0d), Array.get(new double[] {1}, 0));
+ assertEquals(new Float(1.0f), Array.get(new float[] {1.0f}, 0));
+ assertEquals(new Integer(1), Array.get(new int[] {1}, 0));
+ assertEquals(new Long(1L), Array.get(new long[] {1}, 0));
+ assertEquals(new Short((short) 1), Array.get(new short[] {1}, 0));
+ }
+
+ public void testGetBoolean() {
+ try {
+ Array.getBoolean(null, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.getBoolean(new Object(), 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.getBoolean(new Boolean[] {true}, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ assertTrue(Array.getBoolean(new boolean[] {true}, 0));
+ assertFalse(Array.getBoolean(new boolean[] {false}, 0));
+ assertTrue(Array.getBoolean(new boolean[] {Boolean.TRUE}, 0));
+ assertFalse(Array.getBoolean(new boolean[] {Boolean.FALSE}, 0));
+ }
+
+ public void testGetByte() {
+ try {
+ Array.getByte(null, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.getByte(new Object(), 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.getByte(new Byte[] {(byte) 0}, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ assertEquals((byte) 1, Array.getByte(new byte[] {(byte) 1}, 0));
+ assertEquals((byte) 1, Array.getByte(new byte[] {(byte) 1}, 0));
+ assertEquals((byte) 1, Array.getByte(new byte[] {(byte) 1}, 0));
+ assertEquals((byte) 1, Array.getByte(new byte[] {(byte) 1}, 0));
+ }
+
+ public void testGetChar() {
+ try {
+ Array.getChar(null, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.getChar(new Object(), 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.getChar(new Character[] {'0'}, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ assertEquals('1', Array.getChar(new char[] {'1'}, 0));
+ }
+
+ public void testGetDouble() {
+ try {
+ Array.getDouble(null, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.getDouble(new Object(), 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.getDouble(new Double[] {0d}, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ assertEquals(1.0d, Array.getDouble(new double[] {1.0d}, 0));
+ }
+
+ public void testGetFloat() {
+ try {
+ Array.getFloat(null, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.getFloat(new Object(), 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.getFloat(new Float[] {0f}, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ assertEquals(1.0f, Array.getFloat(new float[] {1.0f}, 0));
+ }
+
+ public void testGetInt() {
+ try {
+ Array.getInt(null, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.getInt(new Object(), 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.getInt(new Integer[] {0}, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ assertEquals(1, Array.getInt(new int[] {1}, 0));
+ }
+
+ public void testGetLength() {
+ try {
+ Array.getLength(null);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+ assertEquals(0, Array.getLength(new Object[0]));
+ assertEquals(1, Array.getLength(new Object[1]));
+
+ assertEquals(0, Array.getLength(new int[0]));
+ assertEquals(1, Array.getLength(new int[1]));
+ }
+
+ public void testGetLong() {
+ try {
+ Array.getLong(null, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.getLong(new Object(), 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.getLong(new Long[] {0L}, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ assertEquals(1L, Array.getLong(new long[] {1L}, 0));
+ }
+
+ public void testGetShort() {
+ try {
+ Array.getShort(null, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.getShort(new Object(), 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.getShort(new Short[] {(short) 1}, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ assertEquals((short) 1, Array.getShort(new short[] {(short) 1}, 0));
+ }
+
+ public void testSet() {
+ try {
+ Array.set(null, 0, true);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.set(new Object(), 0, true);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ Object[] objectArray = new Object[1];
+ Array.set(objectArray, 0, "1");
+ assertEquals("1", objectArray[0]);
+
+ boolean[] booleanArray = new boolean[1];
+ Array.set(booleanArray, 0, true);
+ assertTrue(booleanArray[0]);
+
+ byte[] byteArray = new byte[1];
+ Array.set(byteArray, 0, (byte) 1);
+ assertEquals((byte) 1, byteArray[0]);
+
+ char[] charArray = new char[1];
+ Array.set(charArray, 0, 'a');
+ assertEquals('a', charArray[0]);
+
+ double[] doubleArray = new double[1];
+ Array.set(doubleArray, 0, 1.0d);
+ assertEquals(1.0d, doubleArray[0]);
+
+ float[] floatArray = new float[1];
+ Array.set(floatArray, 0, 1.0f);
+ assertEquals(1.0f, floatArray[0]);
+
+ int[] intArray = new int[1];
+ Array.set(intArray, 0, 1);
+ assertEquals(1, intArray[0]);
+
+ long[] longArray = new long[1];
+ Array.set(longArray, 0, 1L);
+ assertEquals(1L, longArray[0]);
+
+ short[] shortArray = new short[1];
+ Array.set(shortArray, 0, (short) 1);
+ assertEquals((short) 1, shortArray[0]);
+ }
+
+ public void testSetBoolean() {
+ try {
+ Array.setBoolean(null, 0, true);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.setBoolean(new Object(), 0, true);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ boolean[] array = new boolean[1];
+
+ Array.setBoolean(array, 0, true);
+ assertTrue(array[0]);
+ }
+
+ public void testSetByte() {
+ try {
+ Array.setByte(null, 0, (byte) 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.setByte(new Object(), 0, (byte) 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ byte[] array = new byte[1];
+
+ Array.setByte(array, 0, (byte) 1);
+ assertEquals((byte) 1, array[0]);
+ }
+
+ public void testSetChar() {
+ try {
+ Array.setChar(null, 0, 'a');
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.setChar(new Object(), 0, 'a');
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ char[] array = new char[1];
+
+ Array.setChar(array, 0, 'a');
+ assertEquals('a', array[0]);
+ }
+
+ public void testSetDouble() {
+ try {
+ Array.setDouble(null, 0, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.setDouble(new Object(), 0, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ double[] array = new double[1];
+
+ Array.setDouble(array, 0, 1d);
+ assertEquals(1d, array[0]);
+ }
+
+ public void testSetFloat() {
+ try {
+ Array.setFloat(null, 0, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.setFloat(new Object(), 0, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ float[] array = new float[1];
+
+ Array.setFloat(array, 0, 1.0f);
+ assertEquals(1.0f, array[0]);
+ }
+
+ public void testSetInt() {
+ try {
+ Array.setInt(null, 0, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.setInt(new Object(), 0, 0);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ int[] array = new int[1];
+
+ Array.setInt(array, 0, 1);
+ assertEquals(1, array[0]);
+ }
+
+ public void testSetLong() {
+ try {
+ Array.setLong(null, 0, 0L);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.setLong(new Object(), 0, 0L);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ long[] array = new long[1];
+
+ Array.setLong(array, 0, 1L);
+ assertEquals(1L, array[0]);
+ }
+
+ public void testSetShort() {
+ try {
+ Array.setShort(null, 0, (short) 1);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ try {
+ Array.setShort(new Object(), 0, (short) 1);
+ fail();
+ } catch (RuntimeException expected) {
+ }
+
+ short[] array = new short[1];
+
+ Array.setShort(array, 0, (short) 1);
+ assertEquals((short) 1, array[0]);
+ }
+}