Make Array.sort(float[]/double[]) JDK compliant
This adds a test for double and float sorting that includes NaNs
and infinite values, and corrects the implementation of Arrays.sort()
to use Double.compare() and Float.compare(), which properly handle
NaN values.
Bug: #9504
Bug-Link: https://github.com/gwtproject/gwt/issues/9504
Change-Id: I2ad9abffbd38e70ba7d5a20011392d879383b7b8
diff --git a/user/super/com/google/gwt/emul/java/util/Arrays.java b/user/super/com/google/gwt/emul/java/util/Arrays.java
index 6fddd5d..cc08e51 100644
--- a/user/super/com/google/gwt/emul/java/util/Arrays.java
+++ b/user/super/com/google/gwt/emul/java/util/Arrays.java
@@ -41,6 +41,7 @@
import java.util.stream.StreamSupport;
import javaemul.internal.ArrayHelper;
+import javaemul.internal.DoubleCompareHolder;
import javaemul.internal.LongCompareHolder;
/**
@@ -1261,57 +1262,57 @@
}
public static void sort(byte[] array) {
- nativeNumberSort(array);
+ nativeIntegerSort(array);
}
public static void sort(byte[] array, int fromIndex, int toIndex) {
checkCriticalArrayBounds(fromIndex, toIndex, array.length);
- nativeNumberSort(array, fromIndex, toIndex);
+ nativeIntegerSort(array, fromIndex, toIndex);
}
public static void sort(char[] array) {
- nativeNumberSort(array);
+ nativeIntegerSort(array);
}
public static void sort(char[] array, int fromIndex, int toIndex) {
checkCriticalArrayBounds(fromIndex, toIndex, array.length);
- nativeNumberSort(array, fromIndex, toIndex);
+ nativeIntegerSort(array, fromIndex, toIndex);
}
public static void sort(double[] array) {
- nativeNumberSort(array);
+ nativeSort(array, DoubleCompareHolder.getDoubleComparator());
}
public static void sort(double[] array, int fromIndex, int toIndex) {
checkCriticalArrayBounds(fromIndex, toIndex, array.length);
- nativeNumberSort(array, fromIndex, toIndex);
+ nativeSort(array, fromIndex, toIndex, DoubleCompareHolder.getDoubleComparator());
}
public static void sort(float[] array) {
- nativeNumberSort(array);
+ nativeSort(array, DoubleCompareHolder.getDoubleComparator());
}
public static void sort(float[] array, int fromIndex, int toIndex) {
checkCriticalArrayBounds(fromIndex, toIndex, array.length);
- nativeNumberSort(array, fromIndex, toIndex);
+ nativeSort(array, fromIndex, toIndex, DoubleCompareHolder.getDoubleComparator());
}
public static void sort(int[] array) {
- nativeNumberSort(array);
+ nativeIntegerSort(array);
}
public static void sort(int[] array, int fromIndex, int toIndex) {
checkCriticalArrayBounds(fromIndex, toIndex, array.length);
- nativeNumberSort(array, fromIndex, toIndex);
+ nativeIntegerSort(array, fromIndex, toIndex);
}
public static void sort(long[] array) {
- nativeLongSort(array, LongCompareHolder.getLongComparator());
+ nativeSort(array, LongCompareHolder.getLongComparator());
}
public static void sort(long[] array, int fromIndex, int toIndex) {
checkCriticalArrayBounds(fromIndex, toIndex, array.length);
- nativeLongSort(array, fromIndex, toIndex);
+ nativeSort(array, fromIndex, toIndex, LongCompareHolder.getLongComparator());
}
public static void sort(Object[] array) {
@@ -1323,12 +1324,12 @@
}
public static void sort(short[] array) {
- nativeNumberSort(array);
+ nativeIntegerSort(array);
}
public static void sort(short[] array, int fromIndex, int toIndex) {
checkCriticalArrayBounds(fromIndex, toIndex, array.length);
- nativeNumberSort(array, fromIndex, toIndex);
+ nativeIntegerSort(array, fromIndex, toIndex);
}
public static <T> void sort(T[] x, Comparator<? super T> c) {
@@ -1734,36 +1735,36 @@
}
/**
- * Sort an entire array of number primitives.
+ * Sort an entire array using the given comparator
*/
- private static native void nativeLongSort(Object array, Object compareFunction) /*-{
+ private static native void nativeSort(Object array, Object compareFunction) /*-{
array.sort(compareFunction);
}-*/;
/**
- * Sort a subset of an array of number primitives.
+ * Sort a subset of an array using the given comparator
*/
- private static void nativeLongSort(Object array, int fromIndex, int toIndex) {
+ private static void nativeSort(Object array, int fromIndex, int toIndex, Object compareFunction) {
Object temp = ArrayHelper.unsafeClone(array, fromIndex, toIndex);
- nativeLongSort(temp, LongCompareHolder.getLongComparator());
+ nativeSort(temp, compareFunction);
ArrayHelper.copy(temp, 0, array, fromIndex, toIndex - fromIndex);
}
/**
- * Sort an entire array of number primitives.
+ * Sort an entire array of number primitives of integral type.
*/
- private static native void nativeNumberSort(Object array) /*-{
+ private static native void nativeIntegerSort(Object array) /*-{
array.sort(function(a, b) {
return a - b;
});
}-*/;
/**
- * Sort a subset of an array of number primitives.
+ * Sort a subset of an array of primitives of integral type.
*/
- private static void nativeNumberSort(Object array, int fromIndex, int toIndex) {
+ private static void nativeIntegerSort(Object array, int fromIndex, int toIndex) {
Object temp = ArrayHelper.unsafeClone(array, fromIndex, toIndex);
- nativeNumberSort(temp);
+ nativeIntegerSort(temp);
ArrayHelper.copy(temp, 0, array, fromIndex, toIndex - fromIndex);
}
diff --git a/user/super/com/google/gwt/emul/javaemul/internal/DoubleCompareHolder.java b/user/super/com/google/gwt/emul/javaemul/internal/DoubleCompareHolder.java
new file mode 100644
index 0000000..1fe9d58
--- /dev/null
+++ b/user/super/com/google/gwt/emul/javaemul/internal/DoubleCompareHolder.java
@@ -0,0 +1,27 @@
+/*
+ * 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 javaemul.internal;
+
+/**
+ * A helper class for double comparison.
+ */
+public class DoubleCompareHolder {
+ // TODO(dankurka): replace this with @JsFunction once its available in GWT.
+ public static native Object getDoubleComparator() /*-{
+ return @java.lang.Double::compare(*);
+ }-*/;
+}
+
diff --git a/user/test/com/google/gwt/emultest/java/util/ArraysTest.java b/user/test/com/google/gwt/emultest/java/util/ArraysTest.java
index 3c65b15..9580db9 100644
--- a/user/test/com/google/gwt/emultest/java/util/ArraysTest.java
+++ b/user/test/com/google/gwt/emultest/java/util/ArraysTest.java
@@ -1321,6 +1321,56 @@
}
/**
+ * Tests sorting of doubles
+ */
+ public void testDoubleSort() {
+ double[] array = new double[] {
+ 41.5, Double.NaN, Double.NaN, 3, 932535, 1, Double.NaN,
+ -3, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN};
+ Arrays.sort(array);
+
+ // Test the array element-by-element
+ // as GWT's Arrays.equal() does not consider NaNs "equal"
+ assertEquals(Double.NEGATIVE_INFINITY, array[0]);
+ assertEquals(-3.0, array[1]);
+ assertEquals(1.0, array[2]);
+ assertEquals(3.0, array[3]);
+ assertEquals(41.5, array[4]);
+ assertEquals(932535.0, array[5]);
+ assertEquals(Double.POSITIVE_INFINITY, array[6]);
+ assertTrue(Double.isNaN(array[7]));
+ assertTrue(Double.isNaN(array[8]));
+ assertTrue(Double.isNaN(array[9]));
+ assertTrue(Double.isNaN(array[10]));
+ }
+
+ /**
+ * Tests sorting of floats
+ */
+ public void testFloatSort() {
+ float[] array = new float[] {
+ 41.5f, Float.NaN, Float.NaN, 3, 932535, 1, Float.NaN,
+ -3, Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NaN};
+ Arrays.sort(array);
+
+ // Test the array element-by-element
+ // as GWT's Arrays.equal() does not consider NaNs "equal"
+ assertEquals(Float.NEGATIVE_INFINITY, array[0]);
+ assertEquals(-3.0f, array[1]);
+ assertEquals(1.0f, array[2]);
+ assertEquals(3.0f, array[3]);
+ assertEquals(41.5f, array[4]);
+ assertEquals(932535.0f, array[5]);
+ assertEquals(Float.POSITIVE_INFINITY, array[6]);
+ assertTrue(Float.isNaN(array[7]));
+ assertTrue(Float.isNaN(array[8]));
+ assertTrue(Float.isNaN(array[9]));
+ assertTrue(Float.isNaN(array[10]));
+ }
+
+
+
+ /**
* Tests sorting a subrange of a primitive array.
*/
public void testPrimitiveSubrangeSort() {