Reduce JSNI usage for Arrays.

Change-Id: Ic73f052d1aa3d0688968d4eb40f9759ebf086fe6
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 f3e1e4e..a8ed4ce 100644
--- a/user/super/com/google/gwt/emul/java/util/Arrays.java
+++ b/user/super/com/google/gwt/emul/java/util/Arrays.java
@@ -41,7 +41,8 @@
 import java.util.stream.StreamSupport;
 
 import javaemul.internal.ArrayHelper;
-import jsinterop.annotations.JsFunction;
+import javaemul.internal.JsUtils;
+import javaemul.internal.NativeArray.CompareFunction;
 
 /**
  * Utility methods related to native arrays.
@@ -1279,7 +1280,7 @@
   }
 
   public static void sort(double[] array) {
-    nativeSort(array, getDoubleComparator());
+    ArrayHelper.asNativeArray(array).sort(getDoubleComparator());
   }
 
   public static void sort(double[] array, int fromIndex, int toIndex) {
@@ -1288,7 +1289,7 @@
   }
 
   public static void sort(float[] array) {
-    nativeSort(array, getDoubleComparator());
+    ArrayHelper.asNativeArray(array).sort(getDoubleComparator());
   }
 
   public static void sort(float[] array, int fromIndex, int toIndex) {
@@ -1306,7 +1307,7 @@
   }
 
   public static void sort(long[] array) {
-    nativeSort(array, getLongComparator());
+    ArrayHelper.asNativeArray(array).sort(getLongComparator());
   }
 
   public static void sort(long[] array, int fromIndex, int toIndex) {
@@ -1734,18 +1735,11 @@
   }
 
   /**
-   * Sort an entire array using the given comparator
-   */
-  private static native void nativeSort(Object array, Object compareFunction) /*-{
-    array.sort(compareFunction);
-  }-*/;
-
-  /**
    * Sort a subset of an array using the given comparator
    */
-  private static void nativeSort(Object array, int fromIndex, int toIndex, Object compareFunction) {
+  private static void nativeSort(Object array, int fromIndex, int toIndex, CompareFunction fn) {
     Object temp = ArrayHelper.unsafeClone(array, fromIndex, toIndex);
-    nativeSort(temp, compareFunction);
+    ArrayHelper.asNativeArray(temp).sort(fn);
     ArrayHelper.copy(temp, 0, array, fromIndex, toIndex - fromIndex);
   }
 
@@ -1753,7 +1747,7 @@
    * Sort an entire array of number primitives of integral type.
    */
   private static void nativeIntegerSort(Object array) {
-    nativeSort(array, getIntComparator());
+    ArrayHelper.asNativeArray(array).sort(getIntComparator());
   }
 
   /**
@@ -1763,32 +1757,16 @@
     nativeSort(array, fromIndex, toIndex, getIntComparator());
   }
 
-  @JsFunction
-  private interface CompareDoubleFunction {
-    int compare(double d1, double d2);
+  private static CompareFunction getIntComparator() {
+    return (a, b) -> JsUtils.unsafeCastToDouble(a) - JsUtils.unsafeCastToDouble(b);
   }
 
-  private static CompareDoubleFunction getDoubleComparator() {
-    return Double::compare;
+  private static CompareFunction getDoubleComparator() {
+    return (a, b) -> Double.compare(JsUtils.unsafeCastToDouble(a), JsUtils.unsafeCastToDouble(b));
   }
 
-  @JsFunction
-  private interface CompareLongFunction {
-    @SuppressWarnings("unusable-by-js")
-    int compare(long d1, long d2);
-  }
-
-  private static CompareLongFunction getLongComparator() {
-    return Long::compare;
-  }
-
-  @JsFunction
-  private interface CompareIntFunction {
-    int compare(int d1, int d2);
-  }
-
-  private static CompareIntFunction getIntComparator() {
-    return (a, b) -> a - b;
+  private static CompareFunction getLongComparator() {
+    return (a, b) -> Long.compare(JsUtils.unsafeCastToLong(a), JsUtils.unsafeCastToLong(b));
   }
 
   private Arrays() { }
diff --git a/user/super/com/google/gwt/emul/javaemul/internal/ArrayHelper.java b/user/super/com/google/gwt/emul/javaemul/internal/ArrayHelper.java
index 1fc2a0e..c1a66f5 100644
--- a/user/super/com/google/gwt/emul/javaemul/internal/ArrayHelper.java
+++ b/user/super/com/google/gwt/emul/javaemul/internal/ArrayHelper.java
@@ -15,6 +15,10 @@
  */
 package javaemul.internal;
 
+import jsinterop.annotations.JsPackage;
+import jsinterop.annotations.JsProperty;
+import jsinterop.annotations.JsType;
+
 /**
  * Provides utilities to perform operations on Arrays.
  */
@@ -31,34 +35,29 @@
    * Unlike clone, this method returns a copy of the array that is not type marked. This is only
    * safe for temp arrays as returned array will not do any type checks.
    */
-  public static native Object[] unsafeClone(Object array, int fromIndex, int toIndex) /*-{
-    return array.slice(fromIndex, toIndex);
-  }-*/;
-
-  public static <T> T[] createFrom(T[] array, int length) {
-    Object result = createNativeArray(length);
-    return ArrayStamper.stampJavaTypeInfo(result, array);
+  public static Object[] unsafeClone(Object array, int fromIndex, int toIndex) {
+    return asNativeArray(array).slice(fromIndex, toIndex);
   }
 
-  private static native Object createNativeArray(int length)/*-{
-    return new Array(length);
-  }-*/;
+  public static <T> T[] createFrom(T[] array, int length) {
+    return ArrayStamper.stampJavaTypeInfo(new NativeArray(length), array);
+  }
 
-  public static native int getLength(Object array) /*-{
-    return array.length;
-  }-*/;
+  public static int getLength(Object array) {
+    return asNativeArray(array).length;
+  }
 
-  public static native void setLength(Object array, int length)/*-{
-    array.length = length;
-  }-*/;
+  public static void setLength(Object array, int length) {
+    asNativeArray(array).length = length;
+  }
 
-  public static native void removeFrom(Object array, int index, int deleteCount) /*-{
-    array.splice(index, deleteCount);
-  }-*/;
+  public static void removeFrom(Object array, int index, int deleteCount) {
+    asNativeArray(array).splice(index, deleteCount);
+  }
 
-  public static native void insertTo(Object array, int index, Object value) /*-{
-    array.splice(index, 0, value);
-  }-*/;
+  public static void insertTo(Object array, int index, Object value) {
+    asNativeArray(array).splice(index, 0, value);
+  }
 
   public static void insertTo(Object array, int index, Object[] values) {
     copy(values, 0, array, index, values.length, false);
@@ -80,19 +79,29 @@
       src = unsafeClone(src, srcOfs, srcOfs + len);
       srcOfs = 0;
     }
+    NativeArray destArray = asNativeArray(dest);
     for (int batchStart = srcOfs, end = srcOfs + len; batchStart < end;) {
       // increment in block
       int batchEnd = Math.min(batchStart + ARRAY_PROCESS_BATCH_SIZE, end);
       len = batchEnd - batchStart;
-      applySplice(dest, destOfs, overwrite ? len : 0, unsafeClone(src, batchStart, batchEnd));
+      Object[] spliceArgs = unsafeClone(src, batchStart, batchEnd);
+      asNativeArray(spliceArgs).splice(0, 0, destOfs, overwrite ? len : 0);
+      getSpliceFunction().apply(destArray, spliceArgs);
       batchStart = batchEnd;
       destOfs += len;
     }
   }
 
-  private static native void applySplice(Object array, int index, int deleteCount,
-      Object arrayToAdd) /*-{
-    Array.prototype.splice.apply(array, [index, deleteCount].concat(arrayToAdd));
-  }-*/;
+  @JsType(isNative = true, name = "Function", namespace = JsPackage.GLOBAL)
+  private static class NativeFunction {
+    public native String apply(Object thisContext, Object[] argsArray);
+  }
+
+  @JsProperty(name = "Array.prototype.splice", namespace = "<window>")
+  private static native NativeFunction getSpliceFunction();
+
+  public static NativeArray asNativeArray(Object array) {
+    return JsUtils.unsafeCastToNativeArray(array);
+  }
 }
 
diff --git a/user/super/com/google/gwt/emul/javaemul/internal/JsUtils.java b/user/super/com/google/gwt/emul/javaemul/internal/JsUtils.java
index 8796b61..c0477ac 100644
--- a/user/super/com/google/gwt/emul/javaemul/internal/JsUtils.java
+++ b/user/super/com/google/gwt/emul/javaemul/internal/JsUtils.java
@@ -52,6 +52,14 @@
    return bool;
   }-*/;
 
+  public static native long unsafeCastToLong(Object l) /*-{
+    return l;
+  }-*/;
+
+  public static native NativeArray unsafeCastToNativeArray(Object array) /*-{
+    return array;
+  }-*/;
+
   public static native <T> T getProperty(Object map, String key) /*-{
     return map[key];
   }-*/;
diff --git a/user/super/com/google/gwt/emul/javaemul/internal/NativeArray.java b/user/super/com/google/gwt/emul/javaemul/internal/NativeArray.java
new file mode 100644
index 0000000..7f0ea99
--- /dev/null
+++ b/user/super/com/google/gwt/emul/javaemul/internal/NativeArray.java
@@ -0,0 +1,42 @@
+/*
+ * 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;
+
+import javaemul.internal.annotations.DoNotAutobox;
+import jsinterop.annotations.JsFunction;
+import jsinterop.annotations.JsType;
+
+/**
+ * Simple class to work with native array API.
+ */
+@JsType(isNative = true, name = "Array", namespace = "<window>")
+public class NativeArray {
+  /**
+   * Compare function for sort.
+   */
+  @JsFunction
+  public interface CompareFunction {
+    double compare(Object d1, Object d2);
+  }
+
+  public int length;
+  public NativeArray() { }
+  public NativeArray(int length) { }
+  public native Object concat(Object arrayToAdd);
+  public native Object[] slice(int fromIndex, int toIndex);
+  public native void splice(int index, int deleteCount, @DoNotAutobox Object... value);
+  public native <T> void sort(CompareFunction compareFunction);
+}