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() {