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