From 79a4983fb5e92798168a8e146280fb1975ec2be0 Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Sun, 9 Mar 2014 14:51:08 +0400 Subject: [PATCH] Completes implementation of java.util.Arrays and java.util.Objects --- .../java/lang/ClassNativeGenerator.java | 4 +- .../java/lang/LongNativeGenerator.java | 38 + .../org/teavm/classlib/java/lang/TClass.java | 6 +- .../org/teavm/classlib/java/lang/TEnum.java | 2 +- .../org/teavm/classlib/java/lang/TLong.java | 4 + .../org/teavm/classlib/java/util/TArrays.java | 1257 ++++++++++++++++- .../teavm/classlib/java/util/TObjects.java | 48 + .../classlib/java/util/TServiceLoader.java | 4 + .../teavm/classlib/java/util/ArraysTest.java | 20 + 9 files changed, 1373 insertions(+), 10 deletions(-) create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/lang/LongNativeGenerator.java diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java index 731414374..f2f9deb60 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java @@ -98,7 +98,7 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug case "doubleClass": context.getWriter().append("$rt_cls($rt_doublecls())"); break; - case "wrap": + case "wrapClass": context.writeExpr(context.getArgument(0)); break; case "getEnumConstantsImpl": @@ -193,7 +193,7 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug case "intClass": case "floatClass": case "doubleClass": - case "wrap": + case "wrapClass": case "getSuperclass": case "getComponentType0": case "forNameImpl": diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/LongNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/LongNativeGenerator.java new file mode 100644 index 000000000..54f324dd4 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/LongNativeGenerator.java @@ -0,0 +1,38 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * 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 org.teavm.classlib.java.lang; + +import java.io.IOException; +import org.teavm.codegen.SourceWriter; +import org.teavm.javascript.ni.Generator; +import org.teavm.javascript.ni.GeneratorContext; +import org.teavm.model.MethodReference; + +/** + * + * @author Alexey Andreev + */ +public class LongNativeGenerator implements Generator { + @Override + public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { + switch (methodRef.getName()) { + case "compare": + writer.append("Long_compare(").append(context.getParameterName(1)).append(", ") + .append(context.getParameterName(2)).append(");").softNewLine(); + break; + } + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java index 0acbc72ef..d89fa5d9a 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java @@ -103,7 +103,7 @@ public class TClass extends TObject { @InjectedBy(ClassNativeGenerator.class) @PluggableDependency(ClassNativeGenerator.class) - public static native TClass wrap(Class cls); + public static native TClass wrapClass(Class cls); public boolean desiredAssertionStatus() { return true; @@ -122,9 +122,9 @@ public class TClass extends TObject { @SuppressWarnings("unchecked") public T cast(TObject obj) { - if (obj != null && !isAssignableFrom(TClass.wrap(obj.getClass()))) { + if (obj != null && !isAssignableFrom(TClass.wrapClass(obj.getClass()))) { throw new TClassCastException(TString.wrap(new TStringBuilder() - .append(TClass.wrap(obj.getClass()).getName()) + .append(TClass.wrapClass(obj.getClass()).getName()) .append(TString.wrap(" is not subtype of ")).append(name).toString())); } return (T)obj; diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TEnum.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TEnum.java index e04be9053..0ab5cb8db 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TEnum.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TEnum.java @@ -62,7 +62,7 @@ public abstract class TEnum> extends TObject implements TComp @SuppressWarnings("unchecked") public final TClass getDeclaringClass() { - return (TClass)TClass.wrap(getClass()); + return (TClass)TClass.wrapClass(getClass()); } @Override diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TLong.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TLong.java index cfe447483..dcb3c166b 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TLong.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TLong.java @@ -15,6 +15,7 @@ */ package org.teavm.classlib.java.lang; +import org.teavm.javascript.ni.GeneratedBy; import org.teavm.javascript.ni.Rename; /** @@ -69,4 +70,7 @@ public class TLong extends TNumber { } return other instanceof TLong && ((TLong)other).value == value; } + + @GeneratedBy(LongNativeGenerator.class) + public static native int compare(long a, long b); } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TArrays.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TArrays.java index c0b0ced22..410ccfa31 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TArrays.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TArrays.java @@ -15,7 +15,10 @@ */ package org.teavm.classlib.java.util; +import java.lang.reflect.Array; +import java.util.Objects; import org.teavm.classlib.java.lang.*; +import org.teavm.classlib.java.lang.reflect.TArray; /** * @@ -40,9 +43,63 @@ public class TArrays extends TObject { return result; } + public static short[] copyOf(short[] array, int length) { + short[] result = new short[length]; + int sz = TMath.min(length, array.length); + for (int i = 0; i < sz; ++i) { + result[i] = array[i]; + } + return result; + } + + public static int[] copyOf(int[] array, int length) { + int[] result = new int[length]; + int sz = TMath.min(length, array.length); + for (int i = 0; i < sz; ++i) { + result[i] = array[i]; + } + return result; + } + + public static long[] copyOf(long[] array, int length) { + long[] result = new long[length]; + int sz = TMath.min(length, array.length); + for (int i = 0; i < sz; ++i) { + result[i] = array[i]; + } + return result; + } + + public static float[] copyOf(float[] array, int length) { + float[] result = new float[length]; + int sz = TMath.min(length, array.length); + for (int i = 0; i < sz; ++i) { + result[i] = array[i]; + } + return result; + } + + public static double[] copyOf(double[] array, int length) { + double[] result = new double[length]; + int sz = TMath.min(length, array.length); + for (int i = 0; i < sz; ++i) { + result[i] = array[i]; + } + return result; + } + + public static boolean[] copyOf(boolean[] array, int length) { + boolean[] result = new boolean[length]; + int sz = TMath.min(length, array.length); + for (int i = 0; i < sz; ++i) { + result[i] = array[i]; + } + return result; + } + public static T[] copyOf(T[] original, int newLength) { @SuppressWarnings("unchecked") - T[] result = (T[])new Object[newLength]; + T[] result = (T[])Array.newInstance(original.getClass().getComponentType(), newLength); int sz = TMath.min(newLength, original.length); for (int i = 0; i < sz; ++i) { result[i] = original[i]; @@ -50,6 +107,100 @@ public class TArrays extends TObject { return result; } + @SuppressWarnings("unchecked") + public static T[] copyOf(U[] original, int newLength, TClass cls) { + TClass componentType = cls.getComponentType(); + T[] result = (T[])(Object)TArray.newInstance(componentType, newLength); + int sz = TMath.min(newLength, original.length); + for (int i = 0; i < sz; ++i) { + result[i] = (T)componentType.cast(TObject.wrap(original[i])); + } + return result; + } + + public static boolean[] copyOfRange(boolean[] array, int from, int to) { + boolean[] result = new boolean[to - from]; + for (int i = from; i < to; ++i) { + result[i - from] = array[i]; + } + return result; + } + + public static byte[] copyOfRange(byte[] array, int from, int to) { + byte[] result = new byte[to - from]; + for (int i = from; i < to; ++i) { + result[i - from] = array[i]; + } + return result; + } + + public static char[] copyOfRange(char[] array, int from, int to) { + char[] result = new char[to - from]; + for (int i = from; i < to; ++i) { + result[i - from] = array[i]; + } + return result; + } + + public static short[] copyOfRange(short[] array, int from, int to) { + short[] result = new short[to - from]; + for (int i = from; i < to; ++i) { + result[i - from] = array[i]; + } + return result; + } + + public static int[] copyOfRange(int[] array, int from, int to) { + int[] result = new int[to - from]; + for (int i = from; i < to; ++i) { + result[i - from] = array[i]; + } + return result; + } + + public static long[] copyOfRange(long[] array, int from, int to) { + long[] result = new long[to - from]; + for (int i = from; i < to; ++i) { + result[i - from] = array[i]; + } + return result; + } + + public static float[] copyOfRange(float[] array, int from, int to) { + float[] result = new float[to - from]; + for (int i = from; i < to; ++i) { + result[i - from] = array[i]; + } + return result; + } + + public static double[] copyOfRange(double[] array, int from, int to) { + double[] result = new double[to - from]; + for (int i = from; i < to; ++i) { + result[i - from] = array[i]; + } + return result; + } + + public static T[] copyOfRange(T[] original, int from, int to) { + @SuppressWarnings("unchecked") + T[] result = (T[])Array.newInstance(original.getClass().getComponentType(), to - from); + for (int i = from; i < to; ++i) { + result[i - from] = original[i]; + } + return result; + } + + @SuppressWarnings("unchecked") + public static T[] copyOfRange(U[] original, int from, int to, TClass newType) { + TClass componentType = newType.getComponentType(); + T[] result = (T[])(Object)TArray.newInstance(componentType, to - from); + for (int i = from; i < to; ++i) { + result[i - from] = (T)newType.getComponentType().cast(TObject.wrap(original[i])); + } + return result; + } + public static TString toString(TObject[] a) { TStringBuilder sb = new TStringBuilder(); sb.append(TString.wrap("[")); @@ -167,12 +318,113 @@ public class TArrays extends TObject { return TString.wrap(sb.toString()); } - public static void fill(TObject[] a, int fromIndex, int toIndex, TObject val) { + public static void fill(long[] a, int fromIndex, int toIndex, long val) { if (fromIndex > toIndex) { throw new TIllegalArgumentException(); } - if (fromIndex < 0 || toIndex > a.length) { - throw new TIndexOutOfBoundsException(); + while (fromIndex < toIndex) { + a[fromIndex++] = val; + } + } + + public static void fill(long[] a, long val) { + fill(a, 0, a.length, val); + } + + public static void fill(int[] a, int fromIndex, int toIndex, int val) { + if (fromIndex > toIndex) { + throw new TIllegalArgumentException(); + } + while (fromIndex < toIndex) { + a[fromIndex++] = val; + } + } + + public static void fill(int[] a, int val) { + fill(a, 0, a.length, val); + } + + public static void fill(short[] a, int fromIndex, int toIndex, short val) { + if (fromIndex > toIndex) { + throw new TIllegalArgumentException(); + } + while (fromIndex < toIndex) { + a[fromIndex++] = val; + } + } + + public static void fill(short[] a, short val) { + fill(a, 0, a.length, val); + } + + public static void fill(char[] a, int fromIndex, int toIndex, char val) { + if (fromIndex > toIndex) { + throw new TIllegalArgumentException(); + } + while (fromIndex < toIndex) { + a[fromIndex++] = val; + } + } + + public static void fill(char[] a, char val) { + fill(a, 0, a.length, val); + } + + public static void fill(byte[] a, int fromIndex, int toIndex, byte val) { + if (fromIndex > toIndex) { + throw new TIllegalArgumentException(); + } + while (fromIndex < toIndex) { + a[fromIndex++] = val; + } + } + + public static void fill(byte[] a, byte val) { + fill(a, 0, a.length, val); + } + + public static void fill(boolean[] a, int fromIndex, int toIndex, boolean val) { + if (fromIndex > toIndex) { + throw new TIllegalArgumentException(); + } + while (fromIndex < toIndex) { + a[fromIndex++] = val; + } + } + + public static void fill(boolean[] a, boolean val) { + fill(a, 0, a.length, val); + } + + public static void fill(float[] a, int fromIndex, int toIndex, float val) { + if (fromIndex > toIndex) { + throw new TIllegalArgumentException(); + } + while (fromIndex < toIndex) { + a[fromIndex++] = val; + } + } + + public static void fill(float[] a, float val) { + fill(a, 0, a.length, val); + } + + public static void fill(double[] a, int fromIndex, int toIndex, double val) { + if (fromIndex > toIndex) { + throw new TIllegalArgumentException(); + } + while (fromIndex < toIndex) { + a[fromIndex++] = val; + } + } + + public static void fill(double[] a, double val) { + fill(a, 0, a.length, val); + } + + public static void fill(TObject[] a, int fromIndex, int toIndex, TObject val) { + if (fromIndex > toIndex) { + throw new TIllegalArgumentException(); } while (fromIndex < toIndex) { a[fromIndex++] = val; @@ -183,6 +435,439 @@ public class TArrays extends TObject { fill(a, 0, a.length, val); } + public static void sort(int[] a, int fromIndex, int toIndex) { + int[] subarray = new int[toIndex - fromIndex]; + for (int i = fromIndex; i < toIndex; ++i) { + subarray[i - fromIndex] = a[i]; + } + sort(subarray); + for (int i = fromIndex; i < toIndex; ++i) { + a[i] = subarray[i - fromIndex]; + } + } + + public static void sort(int[] a) { + if (a.length == 0) { + return; + } + int[] first = a; + int[] second = new int[a.length]; + int chunkSize = 1; + while (chunkSize < a.length) { + for (int i = 0; i < first.length; i += chunkSize * 2) { + merge(first, second, i, Math.min(first.length, i + chunkSize), + Math.min(first.length, i + 2 * chunkSize)); + } + int[] tmp = first; + first = second; + second = tmp; + chunkSize *= 2; + } + if (first != a) { + for (int i = 0; i < first.length; ++i) { + second[i] = first[i]; + } + } + } + + private static void merge(int[] a, int[] b, int from, int split, int to) { + int index = from; + int from2 = split; + while (true) { + if (from == split) { + while (from2 < to) { + b[index++] = a[from2++]; + } + break; + } else if (from2 == to) { + while (from < split) { + b[index++] = a[from++]; + } + break; + } + int p = a[from]; + int q = a[from2]; + if (p <= q) { + b[index++] = p; + ++from; + } else { + b[index++] = q; + ++from2; + } + } + } + + public static void sort(long[] a, int fromIndex, int toIndex) { + long[] subarray = new long[toIndex - fromIndex]; + for (int i = fromIndex; i < toIndex; ++i) { + subarray[i - fromIndex] = a[i]; + } + sort(subarray); + for (int i = fromIndex; i < toIndex; ++i) { + a[i] = subarray[i - fromIndex]; + } + } + + public static void sort(long[] a) { + if (a.length == 0) { + return; + } + long[] first = a; + long[] second = new long[a.length]; + int chunkSize = 1; + while (chunkSize < a.length) { + for (int i = 0; i < first.length; i += chunkSize * 2) { + merge(first, second, i, Math.min(first.length, i + chunkSize), + Math.min(first.length, i + 2 * chunkSize)); + } + long[] tmp = first; + first = second; + second = tmp; + chunkSize *= 2; + } + if (first != a) { + for (int i = 0; i < first.length; ++i) { + second[i] = first[i]; + } + } + } + + private static void merge(long[] a, long[] b, int from, int split, int to) { + int index = from; + int from2 = split; + while (true) { + if (from == split) { + while (from2 < to) { + b[index++] = a[from2++]; + } + break; + } else if (from2 == to) { + while (from < split) { + b[index++] = a[from++]; + } + break; + } + long p = a[from]; + long q = a[from2]; + if (p <= q) { + b[index++] = p; + ++from; + } else { + b[index++] = q; + ++from2; + } + } + } + + public static void sort(short[] a, int fromIndex, int toIndex) { + short[] subarray = new short[toIndex - fromIndex]; + for (int i = fromIndex; i < toIndex; ++i) { + subarray[i - fromIndex] = a[i]; + } + sort(subarray); + for (int i = fromIndex; i < toIndex; ++i) { + a[i] = subarray[i - fromIndex]; + } + } + + public static void sort(short[] a) { + if (a.length == 0) { + return; + } + short[] first = a; + short[] second = new short[a.length]; + int chunkSize = 1; + while (chunkSize < a.length) { + for (int i = 0; i < first.length; i += chunkSize * 2) { + merge(first, second, i, Math.min(first.length, i + chunkSize), + Math.min(first.length, i + 2 * chunkSize)); + } + short[] tmp = first; + first = second; + second = tmp; + chunkSize *= 2; + } + if (first != a) { + for (int i = 0; i < first.length; ++i) { + second[i] = first[i]; + } + } + } + + private static void merge(short[] a, short[] b, int from, int split, int to) { + int index = from; + int from2 = split; + while (true) { + if (from == split) { + while (from2 < to) { + b[index++] = a[from2++]; + } + break; + } else if (from2 == to) { + while (from < split) { + b[index++] = a[from++]; + } + break; + } + short p = a[from]; + short q = a[from2]; + if (p <= q) { + b[index++] = p; + ++from; + } else { + b[index++] = q; + ++from2; + } + } + } + + public static void sort(char[] a, int fromIndex, int toIndex) { + char[] subarray = new char[toIndex - fromIndex]; + for (int i = fromIndex; i < toIndex; ++i) { + subarray[i - fromIndex] = a[i]; + } + sort(subarray); + for (int i = fromIndex; i < toIndex; ++i) { + a[i] = subarray[i - fromIndex]; + } + } + + public static void sort(char[] a) { + if (a.length == 0) { + return; + } + char[] first = a; + char[] second = new char[a.length]; + int chunkSize = 1; + while (chunkSize < a.length) { + for (int i = 0; i < first.length; i += chunkSize * 2) { + merge(first, second, i, Math.min(first.length, i + chunkSize), + Math.min(first.length, i + 2 * chunkSize)); + } + char[] tmp = first; + first = second; + second = tmp; + chunkSize *= 2; + } + if (first != a) { + for (int i = 0; i < first.length; ++i) { + second[i] = first[i]; + } + } + } + + private static void merge(char[] a, char[] b, int from, int split, int to) { + int index = from; + int from2 = split; + while (true) { + if (from == split) { + while (from2 < to) { + b[index++] = a[from2++]; + } + break; + } else if (from2 == to) { + while (from < split) { + b[index++] = a[from++]; + } + break; + } + char p = a[from]; + char q = a[from2]; + if (p <= q) { + b[index++] = p; + ++from; + } else { + b[index++] = q; + ++from2; + } + } + } + + public static void sort(byte[] a, int fromIndex, int toIndex) { + byte[] subarray = new byte[toIndex - fromIndex]; + for (int i = fromIndex; i < toIndex; ++i) { + subarray[i - fromIndex] = a[i]; + } + sort(subarray); + for (int i = fromIndex; i < toIndex; ++i) { + a[i] = subarray[i - fromIndex]; + } + } + + public static void sort(byte[] a) { + if (a.length == 0) { + return; + } + byte[] first = a; + byte[] second = new byte[a.length]; + int chunkSize = 1; + while (chunkSize < a.length) { + for (int i = 0; i < first.length; i += chunkSize * 2) { + merge(first, second, i, Math.min(first.length, i + chunkSize), + Math.min(first.length, i + 2 * chunkSize)); + } + byte[] tmp = first; + first = second; + second = tmp; + chunkSize *= 2; + } + if (first != a) { + for (int i = 0; i < first.length; ++i) { + second[i] = first[i]; + } + } + } + + private static void merge(byte[] a, byte[] b, int from, int split, int to) { + int index = from; + int from2 = split; + while (true) { + if (from == split) { + while (from2 < to) { + b[index++] = a[from2++]; + } + break; + } else if (from2 == to) { + while (from < split) { + b[index++] = a[from++]; + } + break; + } + byte p = a[from]; + byte q = a[from2]; + if (p <= q) { + b[index++] = p; + ++from; + } else { + b[index++] = q; + ++from2; + } + } + } + + public static void sort(float[] a, int fromIndex, int toIndex) { + float[] subarray = new float[toIndex - fromIndex]; + for (int i = fromIndex; i < toIndex; ++i) { + subarray[i - fromIndex] = a[i]; + } + sort(subarray); + for (int i = fromIndex; i < toIndex; ++i) { + a[i] = subarray[i - fromIndex]; + } + } + + public static void sort(float[] a) { + if (a.length == 0) { + return; + } + float[] first = a; + float[] second = new float[a.length]; + int chunkSize = 1; + while (chunkSize < a.length) { + for (int i = 0; i < first.length; i += chunkSize * 2) { + merge(first, second, i, Math.min(first.length, i + chunkSize), + Math.min(first.length, i + 2 * chunkSize)); + } + float[] tmp = first; + first = second; + second = tmp; + chunkSize *= 2; + } + if (first != a) { + for (int i = 0; i < first.length; ++i) { + second[i] = first[i]; + } + } + } + + private static void merge(float[] a, float[] b, int from, int split, int to) { + int index = from; + int from2 = split; + while (true) { + if (from == split) { + while (from2 < to) { + b[index++] = a[from2++]; + } + break; + } else if (from2 == to) { + while (from < split) { + b[index++] = a[from++]; + } + break; + } + float p = a[from]; + float q = a[from2]; + if (p <= q) { + b[index++] = p; + ++from; + } else { + b[index++] = q; + ++from2; + } + } + } + + public static void sort(double[] a, int fromIndex, int toIndex) { + double[] subarray = new double[toIndex - fromIndex]; + for (int i = fromIndex; i < toIndex; ++i) { + subarray[i - fromIndex] = a[i]; + } + sort(subarray); + for (int i = fromIndex; i < toIndex; ++i) { + a[i] = subarray[i - fromIndex]; + } + } + + public static void sort(double[] a) { + if (a.length == 0) { + return; + } + double[] first = a; + double[] second = new double[a.length]; + int chunkSize = 1; + while (chunkSize < a.length) { + for (int i = 0; i < first.length; i += chunkSize * 2) { + merge(first, second, i, Math.min(first.length, i + chunkSize), + Math.min(first.length, i + 2 * chunkSize)); + } + double[] tmp = first; + first = second; + second = tmp; + chunkSize *= 2; + } + if (first != a) { + for (int i = 0; i < first.length; ++i) { + second[i] = first[i]; + } + } + } + + private static void merge(double[] a, double[] b, int from, int split, int to) { + int index = from; + int from2 = split; + while (true) { + if (from == split) { + while (from2 < to) { + b[index++] = a[from2++]; + } + break; + } else if (from2 == to) { + while (from < split) { + b[index++] = a[from++]; + } + break; + } + double p = a[from]; + double q = a[from2]; + if (p <= q) { + b[index++] = p; + ++from; + } else { + b[index++] = q; + ++from2; + } + } + } public static void sort(Object[] a) { sort(a, new NaturalOrder()); @@ -269,6 +954,538 @@ public class TArrays extends TObject { } } + public static int binarySearch(int[] a, int key) { + return binarySearch(a, 0, a.length, key); + } + + public static int binarySearch(int[] a, int fromIndex, int toIndex, int key) { + if (fromIndex > toIndex) { + throw new TIllegalArgumentException(); + } + int l = fromIndex; + int u = toIndex - 1; + while (true) { + int i = (l + u) / 2; + int e = a[i]; + if (e == key) { + return i; + } else if (e < key) { + u = i - 1; + if (u < l) { + return -i - 1; + } + } else { + l = i + 1; + if (l > u) { + return -i - 2; + } + } + } + } + + public static int binarySearch(long[] a, long key) { + return binarySearch(a, 0, a.length, key); + } + + public static int binarySearch(long[] a, int fromIndex, int toIndex, long key) { + if (fromIndex > toIndex) { + throw new TIllegalArgumentException(); + } + int l = fromIndex; + int u = toIndex - 1; + while (true) { + int i = (l + u) / 2; + long e = a[i]; + if (e == key) { + return i; + } else if (e < key) { + u = i - 1; + if (u < l) { + return -i - 1; + } + } else { + l = i + 1; + if (l > u) { + return -i - 2; + } + } + } + } + + public static int binarySearch(short[] a, short key) { + return binarySearch(a, 0, a.length, key); + } + + public static int binarySearch(short[] a, int fromIndex, int toIndex, short key) { + if (fromIndex > toIndex) { + throw new TIllegalArgumentException(); + } + int l = fromIndex; + int u = toIndex - 1; + while (true) { + int i = (l + u) / 2; + short e = a[i]; + if (e == key) { + return i; + } else if (e < key) { + u = i - 1; + if (u < l) { + return -i - 1; + } + } else { + l = i + 1; + if (l > u) { + return -i - 2; + } + } + } + } + + public static int binarySearch(char[] a, char key) { + return binarySearch(a, 0, a.length, key); + } + + public static int binarySearch(char[] a, int fromIndex, int toIndex, char key) { + if (fromIndex > toIndex) { + throw new TIllegalArgumentException(); + } + int l = fromIndex; + int u = toIndex - 1; + while (true) { + int i = (l + u) / 2; + char e = a[i]; + if (e == key) { + return i; + } else if (e < key) { + u = i - 1; + if (u < l) { + return -i - 1; + } + } else { + l = i + 1; + if (l > u) { + return -i - 2; + } + } + } + } + + public static int binarySearch(byte[] a, byte key) { + return binarySearch(a, 0, a.length, key); + } + + public static int binarySearch(byte[] a, int fromIndex, int toIndex, byte key) { + if (fromIndex > toIndex) { + throw new TIllegalArgumentException(); + } + int l = fromIndex; + int u = toIndex - 1; + while (true) { + int i = (l + u) / 2; + byte e = a[i]; + if (e == key) { + return i; + } else if (e < key) { + u = i - 1; + if (u < l) { + return -i - 1; + } + } else { + l = i + 1; + if (l > u) { + return -i - 2; + } + } + } + } + + public static int binarySearch(double[] a, double key) { + return binarySearch(a, 0, a.length, key); + } + + public static int binarySearch(double[] a, int fromIndex, int toIndex, double key) { + if (fromIndex > toIndex) { + throw new TIllegalArgumentException(); + } + int l = fromIndex; + int u = toIndex - 1; + while (true) { + int i = (l + u) / 2; + double e = a[i]; + if (e == key) { + return i; + } else if (e < key) { + u = i - 1; + if (u < l) { + return -i - 1; + } + } else { + l = i + 1; + if (l > u) { + return -i - 2; + } + } + } + } + + public static int binarySearch(float[] a, float key) { + return binarySearch(a, 0, a.length, key); + } + + public static int binarySearch(float[] a, int fromIndex, int toIndex, float key) { + if (fromIndex > toIndex) { + throw new TIllegalArgumentException(); + } + int l = fromIndex; + int u = toIndex - 1; + while (true) { + int i = (l + u) / 2; + float e = a[i]; + if (e == key) { + return i; + } else if (e < key) { + u = i - 1; + if (u < l) { + return -i - 1; + } + } else { + l = i + 1; + if (l > u) { + return -i - 2; + } + } + } + } + + public static int binarySearch(Object[] a, Object key) { + return binarySearch(a, 0, a.length, key); + } + + public static int binarySearch(Object[] a, int fromIndex, int toIndex, Object key) { + return binarySearch(a, fromIndex, toIndex, key, new NaturalOrder()); + } + + public static int binarySearch(T[] a, T key, TComparator c) { + return binarySearch(a, 0, a.length, key, c); + } + + public static int binarySearch(T[] a, int fromIndex, int toIndex, T key, TComparator c) { + if (fromIndex > toIndex) { + throw new TIllegalArgumentException(); + } + int l = fromIndex; + int u = toIndex - 1; + while (true) { + int i = (l + u) / 2; + T e = a[i]; + int cmp = c.compare(key, e); + if (cmp == 0) { + return i; + } else if (cmp < 0) { + u = i - 1; + if (u < l) { + return -i - 1; + } + } else { + l = i + 1; + if (l > u) { + return -i - 2; + } + } + } + } + + public static boolean equals(long[] a, long[] a2) { + if (a == a2) { + return true; + } + if (a == null || a2 == null || a.length != a2.length) { + return false; + } + for (int i = 0; i < a.length; ++i) { + if (a[i] != a2[i]) { + return false; + } + } + return true; + } + + public static boolean equals(int[] a, int[] a2) { + if (a == a2) { + return true; + } + if (a == null || a2 == null || a.length != a2.length) { + return false; + } + for (int i = 0; i < a.length; ++i) { + if (a[i] != a2[i]) { + return false; + } + } + return true; + } + + public static boolean equals(short[] a, short[] a2) { + if (a == a2) { + return true; + } + if (a == null || a2 == null || a.length != a2.length) { + return false; + } + for (int i = 0; i < a.length; ++i) { + if (a[i] != a2[i]) { + return false; + } + } + return true; + } + + public static boolean equals(char[] a, char[] a2) { + if (a == a2) { + return true; + } + if (a == null || a2 == null || a.length != a2.length) { + return false; + } + for (int i = 0; i < a.length; ++i) { + if (a[i] != a2[i]) { + return false; + } + } + return true; + } + + public static boolean equals(byte[] a, byte[] a2) { + if (a == a2) { + return true; + } + if (a == null || a2 == null || a.length != a2.length) { + return false; + } + for (int i = 0; i < a.length; ++i) { + if (a[i] != a2[i]) { + return false; + } + } + return true; + } + + public static boolean equals(float[] a, float[] a2) { + if (a == a2) { + return true; + } + if (a == null || a2 == null || a.length != a2.length) { + return false; + } + for (int i = 0; i < a.length; ++i) { + if (a[i] != a2[i]) { + return false; + } + } + return true; + } + + public static boolean equals(double[] a, double[] a2) { + if (a == a2) { + return true; + } + if (a == null || a2 == null || a.length != a2.length) { + return false; + } + for (int i = 0; i < a.length; ++i) { + if (a[i] != a2[i]) { + return false; + } + } + return true; + } + + public static boolean equals(boolean[] a, boolean[] a2) { + if (a == a2) { + return true; + } + if (a == null || a2 == null || a.length != a2.length) { + return false; + } + for (int i = 0; i < a.length; ++i) { + if (a[i] != a2[i]) { + return false; + } + } + return true; + } + + public static boolean equals(Object[] a, Object[] a2) { + if (a == a2) { + return true; + } + if (a == null || a2 == null || a.length != a2.length) { + return false; + } + for (int i = 0; i < a.length; ++i) { + if (!Objects.equals(a[i], a2[i])) { + return false; + } + } + return true; + } + + public static int hashCode(boolean[] a) { + if (a == null) { + return 0; + } + int hash = 0xA5A537FC; + for (int i = 0; i < a.length; ++i) { + int h = a[i] ? 0x12345678 : 0x87654321; + hash = TInteger.rotateLeft(h, 4) ^ TInteger.rotateRight(h, 7) ^ TInteger.rotateLeft(hash, 13); + } + return hash; + } + + public static int hashCode(long[] a) { + if (a == null) { + return 0; + } + int hash = 0xA5A537FC; + for (int i = 0; i < a.length; ++i) { + int h = (int)(a[i] >>> 32) ^ (int)a[i]; + hash = TInteger.rotateLeft(h, 4) ^ TInteger.rotateRight(h, 7) ^ TInteger.rotateLeft(hash, 13); + } + return hash; + } + + public static int hashCode(int[] a) { + if (a == null) { + return 0; + } + int hash = 0xA5A537FC; + for (int i = 0; i < a.length; ++i) { + hash = TInteger.rotateLeft(a[i], 4) ^ TInteger.rotateRight(a[i], 7) ^ TInteger.rotateLeft(hash, 13); + } + return hash; + } + + public static int hashCode(byte[] a) { + if (a == null) { + return 0; + } + int hash = 0xA5A537FC; + for (int i = 0; i < a.length; ++i) { + hash = TInteger.rotateLeft(a[i], 4) ^ TInteger.rotateRight(a[i], 7) ^ TInteger.rotateLeft(hash, 13); + } + return hash; + } + + public static int hashCode(short[] a) { + if (a == null) { + return 0; + } + int hash = 0xA5A537FC; + for (int i = 0; i < a.length; ++i) { + hash = TInteger.rotateLeft(a[i], 4) ^ TInteger.rotateRight(a[i], 7) ^ TInteger.rotateLeft(hash, 13); + } + return hash; + } + + public static int hashCode(char[] a) { + if (a == null) { + return 0; + } + int hash = 0xA5A537FC; + for (int i = 0; i < a.length; ++i) { + hash = TInteger.rotateLeft(a[i], 4) ^ TInteger.rotateRight(a[i], 7) ^ TInteger.rotateLeft(hash, 13); + } + return hash; + } + + public static int hashCode(float[] a) { + if (a == null) { + return 0; + } + int hash = 0xA5A537FC; + for (int i = 0; i < a.length; ++i) { + int h = TFloat.floatToIntBits(a[i]); + hash = TInteger.rotateLeft(h, 4) ^ TInteger.rotateRight(h, 7) ^ TInteger.rotateLeft(hash, 13); + } + return hash; + } + + public static int hashCode(double[] a) { + if (a == null) { + return 0; + } + int hash = 0xA5A537FC; + for (int i = 0; i < a.length; ++i) { + long lh = TDouble.doubleToLongBits(a[i]); + int h = (int)lh ^ (int)(lh >> 32); + hash = TInteger.rotateLeft(h, 4) ^ TInteger.rotateRight(h, 7) ^ TInteger.rotateLeft(hash, 13); + } + return hash; + } + + public static int hashCode(Object[] a) { + if (a == null) { + return 0; + } + int hash = 0xA5A537FC; + for (int i = 0; i < a.length; ++i) { + int h = TObjects.hashCode(a[i]) ^ 0x1F7A58E0; + hash = TInteger.rotateLeft(h, 4) ^ TInteger.rotateRight(h, 7) ^ TInteger.rotateLeft(hash, 13); + } + return hash; + } + + public static int deepHashCode(Object[] a) { + if (a == null) { + return 0; + } + int hash = 0xA5A537FC; + for (int i = 0; i < a.length; ++i) { + Object el = a[i]; + int h; + if (a[i] instanceof boolean[]) { + h = hashCode((boolean[])el); + } else if (a[i] instanceof byte[]) { + h = hashCode((byte[])el); + } else if (a[i] instanceof short[]) { + h = hashCode((short[])el); + } else if (a[i] instanceof char[]) { + h = hashCode((char[])el); + } else if (a[i] instanceof int[]) { + h = hashCode((int[])el); + } else if (a[i] instanceof long[]) { + h = hashCode((long[])el); + } else if (a[i] instanceof float[]) { + h = hashCode((float[])el); + } else if (a[i] instanceof double[]) { + h = hashCode((double[])el); + } else if (a[i] instanceof Object[]) { + h = deepHashCode((Object[])el); + } else { + h = TObjects.hashCode(el) ^ 0x1F7A58E0; + } + hash = TInteger.rotateLeft(h, 4) ^ TInteger.rotateRight(h, 7) ^ TInteger.rotateLeft(hash, 13); + } + return hash; + } + + public static boolean deepEquals(Object[] a1, Object[] a2) { + if (a1 == a2) { + return true; + } + if (a1 == null || a2 == null || a1.length != a2.length) { + return false; + } + for (int i = 0; i < a1.length; ++i) { + Object e1 = a1[i]; + Object e2 = a2[i]; + if (!TObjects.deepEquals(e1, e2)) { + return false; + } + } + return true; + } + @SafeVarargs public static TList asList(final T... a) { return new TAbstractList() { @@ -280,4 +1497,36 @@ public class TArrays extends TObject { } }; } + + public static TString deepToString(Object[] a) { + TStringBuilder sb = new TStringBuilder(); + deepToString(a, sb, new TArrayList()); + return TString.wrap(sb.toString()); + } + + private static void deepToString(Object[] a, TStringBuilder out, TList visited) { + out.append('['); + if (visited.contains(a)) { + out.append(TString.wrap("...")); + } else { + visited.add(a); + if (a.length > 0) { + deepToString(a[0], out, visited); + for (int i = 1; i < a.length; ++i) { + out.append(TString.wrap(", ")); + deepToString(a[0], out, visited); + } + } + visited.remove(visited.size() - 1); + } + out.append(']'); + } + + private static void deepToString(Object a, TStringBuilder out, TList visited) { + if (a instanceof Object[]) { + deepToString((Object[])a, out, visited); + } else { + out.append(TObject.wrap(a)); + } + } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TObjects.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TObjects.java index 24ce4c006..609cbb1e6 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TObjects.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TObjects.java @@ -55,4 +55,52 @@ public final class TObjects extends TObject { } return obj; } + + public static boolean deepEquals(Object a, Object b) { + if (a == b) { + return true; + } + if (a == null) { + return b == null; + } + if (a instanceof boolean[]) { + return b instanceof boolean[] && TArrays.equals((boolean[])a, (boolean[])b); + } else if (b instanceof boolean[]) { + return false; + } else if (a instanceof byte[]) { + return b instanceof byte[] && TArrays.equals((byte[])a, (byte[])b); + } else if (b instanceof byte[]) { + return false; + } else if (a instanceof short[]) { + return b instanceof short[] && TArrays.equals((short[])a, (short[])b); + } else if (b instanceof short[]) { + return false; + } else if (a instanceof int[]) { + return b instanceof int[] && TArrays.equals((int[])a, (int[])b); + } else if (b instanceof int[]) { + return false; + } else if (a instanceof char[]) { + return b instanceof char[] && TArrays.equals((char[])a, (char[])b); + } else if (b instanceof char[]) { + return false; + } else if (a instanceof float[]) { + return b instanceof float[] && TArrays.equals((float[])a, (float[])b); + } else if (b instanceof float[]) { + return false; + } else if (a instanceof double[]) { + return b instanceof double[] && TArrays.equals((double[])a, (double[])b); + } else if (b instanceof double[]) { + return false; + } else if (a instanceof Object[]) { + return b instanceof Object[] && TArrays.deepEquals((Object[])a, (Object[])b); + } else if (b instanceof Object[]) { + return false; + } else { + return a.equals(b); + } + } + + public static int hash(Object... values) { + return TArrays.hashCode(values); + } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TServiceLoader.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TServiceLoader.java index 2d323d79c..6299fed4c 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TServiceLoader.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TServiceLoader.java @@ -60,4 +60,8 @@ public final class TServiceLoader extends TObject implements TIterable { } private static native T[] loadServices(TClass serviceType); + + public void reload() { + // Do nothing, services are bound at build time + } } diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/ArraysTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/util/ArraysTest.java index 773178c47..6c6caaa72 100644 --- a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/ArraysTest.java +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/util/ArraysTest.java @@ -37,6 +37,19 @@ public class ArraysTest { assertEquals(Integer.valueOf(7), array[5]); } + @Test + public void binarySearchWorks() { + Integer[] array = { 2, 4, 6, 8, 10, 12, 14, 16 }; + assertEquals(3, Arrays.binarySearch(array, 8)); + assertEquals(7, Arrays.binarySearch(array, 16)); + assertEquals(0, Arrays.binarySearch(array, 2)); + assertEquals(-1, Arrays.binarySearch(array, 1)); + assertEquals(-2, Arrays.binarySearch(array, 3)); + assertEquals(-3, Arrays.binarySearch(array, 5)); + assertEquals(-8, Arrays.binarySearch(array, 15)); + assertEquals(-9, Arrays.binarySearch(array, 17)); + } + @Test public void arrayExposedAsList() { Integer[] array = { 2, 3, 4 }; @@ -44,4 +57,11 @@ public class ArraysTest { assertEquals(3, list.size()); assertEquals(Integer.valueOf(4), list.get(2)); } + + @Test + public void arrayExposedAsString() { + Object[] array = { 1, 2, null, null, "foo" }; + array[3] = array; + assertEquals("[1, 2, null, [...], foo]", Arrays.deepToString(array)); + } }