Add java.lang Java 8 API
This patch does not include new functional methods
and unsigned methods in Number implementations.
Some code and tests adapted from google guava libraries.
Change-Id: I5f2ca3069de73ad8d8801d8e7bca7ebb1ed36e43
diff --git a/user/super/com/google/gwt/emul/java/lang/Byte.java b/user/super/com/google/gwt/emul/java/lang/Byte.java
index 2d9a99b..bc87eed 100644
--- a/user/super/com/google/gwt/emul/java/lang/Byte.java
+++ b/user/super/com/google/gwt/emul/java/lang/Byte.java
@@ -23,6 +23,7 @@
public static final byte MIN_VALUE = (byte) 0x80;
public static final byte MAX_VALUE = (byte) 0x7F;
public static final int SIZE = 8;
+ public static final int BYTES = SIZE / Byte.SIZE;
public static final Class<Byte> TYPE = byte.class;
/**
@@ -41,11 +42,6 @@
return Byte.valueOf((byte) __decodeAndValidateInt(s, MIN_VALUE, MAX_VALUE));
}
- /**
- * @skip
- *
- * Here for shared implementation with Arrays.hashCode
- */
public static int hashCode(byte b) {
return b;
}
diff --git a/user/super/com/google/gwt/emul/java/lang/Character.java b/user/super/com/google/gwt/emul/java/lang/Character.java
index 5ebeccc..ca0c93d 100644
--- a/user/super/com/google/gwt/emul/java/lang/Character.java
+++ b/user/super/com/google/gwt/emul/java/lang/Character.java
@@ -128,6 +128,7 @@
public static final int MAX_CODE_POINT = 0x10FFFF;
public static final int SIZE = 16;
+ public static final int BYTES = SIZE / Byte.SIZE;
public static int charCount(int codePoint) {
return codePoint >= MIN_SUPPLEMENTARY_CODE_POINT ? 2 : 1;
@@ -217,11 +218,6 @@
return forDigit(digit);
}
- /**
- * @skip
- *
- * public for shared implementation with Arrays.hashCode
- */
public static int hashCode(char c) {
return c;
}
diff --git a/user/super/com/google/gwt/emul/java/lang/Double.java b/user/super/com/google/gwt/emul/java/lang/Double.java
index 390f694..27f1534 100644
--- a/user/super/com/google/gwt/emul/java/lang/Double.java
+++ b/user/super/com/google/gwt/emul/java/lang/Double.java
@@ -37,6 +37,7 @@
public static final double NEGATIVE_INFINITY = -1d / 0d;
public static final double POSITIVE_INFINITY = 1d / 0d;
public static final int SIZE = 64;
+ public static final int BYTES = SIZE / Byte.SIZE;
public static final Class<Double> TYPE = double.class;
// 2^512, 2^-512
@@ -196,13 +197,14 @@
return (ihi << 32) | ilo;
}
- /**
- * @skip Here for shared implementation with Arrays.hashCode
- */
public static int hashCode(double d) {
return (int) d;
}
+ public static boolean isFinite(double x) {
+ return NEGATIVE_INFINITY < x && x < POSITIVE_INFINITY;
+ }
+
public static boolean isInfinite(double x) {
return x == POSITIVE_INFINITY || x == NEGATIVE_INFINITY;
}
@@ -263,10 +265,22 @@
return negative ? -d : d;
}
+ public static double max(double a, double b) {
+ return Math.max(a, b);
+ }
+
+ public static double min(double a, double b) {
+ return Math.min(a, b);
+ }
+
public static double parseDouble(String s) throws NumberFormatException {
return __parseAndValidateDouble(s);
}
+ public static double sum(double a, double b) {
+ return a + b;
+ }
+
public static String toString(double b) {
return String.valueOf(b);
}
diff --git a/user/super/com/google/gwt/emul/java/lang/Float.java b/user/super/com/google/gwt/emul/java/lang/Float.java
index 5d3647b..565e2df 100644
--- a/user/super/com/google/gwt/emul/java/lang/Float.java
+++ b/user/super/com/google/gwt/emul/java/lang/Float.java
@@ -28,6 +28,7 @@
public static final float NEGATIVE_INFINITY = -1f / 0f;
public static final float POSITIVE_INFINITY = 1f / 0f;
public static final int SIZE = 32;
+ public static final int BYTES = SIZE / Byte.SIZE;
public static final Class<Float> TYPE = float.class;
private static final long POWER_31_INT = 2147483648L;
@@ -85,7 +86,6 @@
}
/**
- * @skip Here for shared implementation with Arrays.hashCode.
* @param f
* @return hash value of float (currently just truncated to int)
*/
@@ -130,6 +130,10 @@
return (float) Double.longBitsToDouble(bits64);
}
+ public static boolean isFinite(float x) {
+ return Double.isFinite(x);
+ }
+
public static boolean isInfinite(float x) {
return Double.isInfinite(x);
}
@@ -138,6 +142,14 @@
return Double.isNaN(x);
}
+ public static float max(float a, float b) {
+ return Math.max(a, b);
+ }
+
+ public static float min(float a, float b) {
+ return Math.min(a, b);
+ }
+
public static float parseFloat(String s) throws NumberFormatException {
double doubleValue = __parseAndValidateDouble(s);
if (doubleValue > Float.MAX_VALUE) {
@@ -148,6 +160,10 @@
return (float) doubleValue;
}
+ public static float sum(float a, float b) {
+ return a + b;
+ }
+
public static String toString(float b) {
return String.valueOf(b);
}
diff --git a/user/super/com/google/gwt/emul/java/lang/Integer.java b/user/super/com/google/gwt/emul/java/lang/Integer.java
index 948bf75..bda0338 100644
--- a/user/super/com/google/gwt/emul/java/lang/Integer.java
+++ b/user/super/com/google/gwt/emul/java/lang/Integer.java
@@ -23,6 +23,7 @@
public static final int MAX_VALUE = 0x7fffffff;
public static final int MIN_VALUE = 0x80000000;
public static final int SIZE = 32;
+ public static final int BYTES = SIZE / Byte.SIZE;
public static final Class<Integer> TYPE = int.class;
/**
@@ -71,11 +72,6 @@
return Integer.valueOf(__decodeAndValidateInt(s, MIN_VALUE, MAX_VALUE));
}
- /**
- * @skip
- *
- * Here for shared implementation with Arrays.hashCode
- */
public static int hashCode(int i) {
return i;
}
@@ -98,6 +94,14 @@
return i & -i;
}
+ public static int max(int a, int b) {
+ return Math.max(a, b);
+ }
+
+ public static int min(int a, int b) {
+ return Math.min(a, b);
+ }
+
public static int numberOfLeadingZeros(int i) {
// Based on Henry S. Warren, Jr: "Hacker's Delight", p. 80.
if (i < 0) {
@@ -197,6 +201,10 @@
}
}
+ public static int sum(int a, int b) {
+ return a + b;
+ }
+
public static String toBinaryString(int value) {
return toUnsignedRadixString(value, 2);
}
diff --git a/user/super/com/google/gwt/emul/java/lang/Long.java b/user/super/com/google/gwt/emul/java/lang/Long.java
index 7115944..0654fa4 100644
--- a/user/super/com/google/gwt/emul/java/lang/Long.java
+++ b/user/super/com/google/gwt/emul/java/lang/Long.java
@@ -31,6 +31,7 @@
public static final long MAX_VALUE = 0x7fffffffffffffffL;
public static final long MIN_VALUE = 0x8000000000000000L;
public static final int SIZE = 64;
+ public static final int BYTES = SIZE / Byte.SIZE;
public static final Class<Long> TYPE = long.class;
public static int bitCount(long i) {
@@ -54,9 +55,6 @@
return valueOf(decode.payload, decode.radix);
}
- /**
- * @skip Here for shared implementation with Arrays.hashCode
- */
public static int hashCode(long l) {
return (int) l;
}
@@ -74,6 +72,14 @@
return i & -i;
}
+ public static long max(long a, long b) {
+ return Math.max(a, b);
+ }
+
+ public static long min(long a, long b) {
+ return Math.min(a, b);
+ }
+
public static int numberOfLeadingZeros(long i) {
int high = (int) (i >> 32);
if (high != 0) {
@@ -145,6 +151,10 @@
}
}
+ public static long sum(long a, long b) {
+ return a + b;
+ }
+
public static String toBinaryString(long value) {
return toPowerOfTwoUnsignedString(value, 1);
}
diff --git a/user/super/com/google/gwt/emul/java/lang/Math.java b/user/super/com/google/gwt/emul/java/lang/Math.java
index fce3019..f996964 100644
--- a/user/super/com/google/gwt/emul/java/lang/Math.java
+++ b/user/super/com/google/gwt/emul/java/lang/Math.java
@@ -71,6 +71,20 @@
return NativeMath.asin(x);
}
+ public static int addExact(int x, int y) {
+ int r = x + y;
+ // "Hacker's Delight" 2-12 Overflow if both arguments have the opposite sign of the result
+ throwOverflowIf(((x ^ r) & (y ^ r)) < 0);
+ return r;
+ }
+
+ public static long addExact(long x, long y) {
+ long r = x + y;
+ // "Hacker's Delight" 2-12 Overflow if both arguments have the opposite sign of the result
+ throwOverflowIf(((x ^ r) & (y ^ r)) < 0);
+ return r;
+ }
+
public static double atan(double x) {
return NativeMath.atan(x);
}
@@ -107,6 +121,16 @@
return (Math.exp(x) + Math.exp(-x)) / 2.0;
}
+ public static int decrementExact(int x) {
+ throwOverflowIf(x == Integer.MIN_VALUE);
+ return x - 1;
+ }
+
+ public static long decrementExact(long x) {
+ throwOverflowIf(x == Long.MIN_VALUE);
+ return x - 1;
+ }
+
public static double exp(double x) {
return NativeMath.exp(x);
}
@@ -128,10 +152,48 @@
return NativeMath.floor(x);
}
+ public static int floorDiv(int dividend, int divisor) {
+ throwDivByZeroIf(divisor == 0);
+ int r = dividend / divisor;
+ // if the signs are different and modulo not zero, round down
+ if ((dividend ^ divisor) < 0 && (r * divisor != dividend)) {
+ r--;
+ }
+ return r;
+ }
+
+ public static long floorDiv(long dividend, long divisor) {
+ throwDivByZeroIf(divisor == 0);
+ long r = dividend / divisor;
+ // if the signs are different and modulo not zero, round down
+ if ((dividend ^ divisor) < 0 && (r * divisor != dividend)) {
+ r--;
+ }
+ return r;
+ }
+
+ public static int floorMod(int dividend, int divisor) {
+ return dividend - floorDiv(dividend, divisor) * divisor;
+ }
+
+ public static long floorMod(long dividend, long divisor) {
+ return dividend - floorDiv(dividend, divisor) * divisor;
+ }
+
public static double hypot(double x, double y) {
return sqrt(x * x + y * y);
}
+ public static int incrementExact(int x) {
+ throwOverflowIf(x == Integer.MAX_VALUE);
+ return x + 1;
+ }
+
+ public static long incrementExact(long x) {
+ throwOverflowIf(x == Long.MAX_VALUE);
+ return x + 1;
+ }
+
public static double log(double x) {
return NativeMath.log(x);
}
@@ -176,6 +238,29 @@
return x < y ? x : y;
}
+ public static int multiplyExact(int x, int y) {
+ long r = (long) x * (long) y;
+ int ir = (int) r;
+ throwOverflowIf(ir != r);
+ return ir;
+ }
+
+ public static long multiplyExact(long x, long y) {
+ long r = x * y;
+ throwOverflowIf((x == Long.MIN_VALUE && y == -1) || (y != 0 && (r / y != x)));
+ return r;
+ }
+
+ public static int negateExact(int x) {
+ throwOverflowIf(x == Integer.MIN_VALUE);
+ return -x;
+ }
+
+ public static long negateExact(long x) {
+ throwOverflowIf(x == Long.MIN_VALUE);
+ return -x;
+ }
+
public static double pow(double x, double exp) {
return NativeMath.pow(x, exp);
}
@@ -209,6 +294,22 @@
return d;
}-*/;
+ public static int subtractExact(int x, int y) {
+ int r = x - y;
+ // "Hacker's Delight" Overflow if the arguments have different signs and
+ // the sign of the result is different than the sign of x
+ throwOverflowIf(((x ^ y) & (x ^ r)) < 0);
+ return r;
+ }
+
+ public static long subtractExact(long x, long y) {
+ long r = x - y;
+ // "Hacker's Delight" Overflow if the arguments have different signs and
+ // the sign of the result is different than the sign of x
+ throwOverflowIf(((x ^ y) & (x ^ r)) < 0);
+ return r;
+ }
+
public static double scalb(double d, int scaleFactor) {
if (scaleFactor >= 31 || scaleFactor <= -31) {
return d * Math.pow(2, scaleFactor);
@@ -268,10 +369,28 @@
return x * PI_UNDER_180;
}
+ public static int toIntExact(long x) {
+ int ix = (int) x;
+ throwOverflowIf(ix != x);
+ return ix;
+ }
+
public static double toRadians(double x) {
return x * PI_OVER_180;
}
+ private static void throwDivByZeroIf(boolean condition) {
+ if (condition) {
+ throw new ArithmeticException("div by zero");
+ }
+ }
+
+ private static void throwOverflowIf(boolean condition) {
+ if (condition) {
+ throw new ArithmeticException("overflow");
+ }
+ }
+
@JsType(isNative = true, name = "Math", namespace = JsPackage.GLOBAL)
private static class NativeMath {
public static double LOG10E;
diff --git a/user/super/com/google/gwt/emul/java/lang/Runnable.java b/user/super/com/google/gwt/emul/java/lang/Runnable.java
index 31e49e5..4a51e4b 100644
--- a/user/super/com/google/gwt/emul/java/lang/Runnable.java
+++ b/user/super/com/google/gwt/emul/java/lang/Runnable.java
@@ -16,15 +16,16 @@
package java.lang;
/**
- * Encapsulates an action for later execution. <a
- * href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Runnable.html">[Sun
- * docs]</a>
- *
+ * Encapsulates an action for later execution.
+ * See <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html">
+ * the official Java API doc</a> for details.
+ *
* <p>
* This interface is provided only for JRE compatibility. GWT does not support
* multithreading.
* </p>
*/
+@FunctionalInterface
public interface Runnable {
void run();
}
diff --git a/user/super/com/google/gwt/emul/java/lang/Short.java b/user/super/com/google/gwt/emul/java/lang/Short.java
index 6b03f9c..c42d03c 100644
--- a/user/super/com/google/gwt/emul/java/lang/Short.java
+++ b/user/super/com/google/gwt/emul/java/lang/Short.java
@@ -23,6 +23,7 @@
public static final short MIN_VALUE = (short) 0x8000;
public static final short MAX_VALUE = (short) 0x7fff;
public static final int SIZE = 16;
+ public static final int BYTES = SIZE / Byte.SIZE;
public static final Class<Short> TYPE = short.class;
/**
@@ -41,9 +42,6 @@
return Short.valueOf((short) __decodeAndValidateInt(s, MIN_VALUE, MAX_VALUE));
}
- /**
- * @skip Here for shared implementation with Arrays.hashCode
- */
public static int hashCode(short s) {
return s;
}
diff --git a/user/super/com/google/gwt/emul/java/lang/String.java b/user/super/com/google/gwt/emul/java/lang/String.java
index 2a31e81..5e106c2 100644
--- a/user/super/com/google/gwt/emul/java/lang/String.java
+++ b/user/super/com/google/gwt/emul/java/lang/String.java
@@ -16,6 +16,7 @@
package java.lang;
+import static javaemul.internal.InternalPreconditions.checkNotNull;
import static javaemul.internal.InternalPreconditions.checkStringBounds;
import java.io.Serializable;
@@ -24,6 +25,7 @@
import java.nio.charset.UnsupportedCharsetException;
import java.util.Comparator;
import java.util.Locale;
+import java.util.StringJoiner;
import javaemul.internal.ArrayHelper;
import javaemul.internal.EmulatedCharset;
@@ -110,6 +112,26 @@
return valueOf(v, offset, count);
}
+ public static String join(CharSequence delimiter, CharSequence... elements) {
+ checkNotNull(delimiter, "delimiter");
+ checkNotNull(elements, "elements");
+ StringJoiner joiner = new StringJoiner(delimiter);
+ for (CharSequence e : elements) {
+ joiner.add(e);
+ }
+ return joiner.toString();
+ }
+
+ public static String join(CharSequence delimiter, Iterable<? extends CharSequence> elements) {
+ checkNotNull(delimiter, "delimiter");
+ checkNotNull(elements, "elements");
+ StringJoiner joiner = new StringJoiner(delimiter);
+ for (CharSequence e : elements) {
+ joiner.add(e);
+ }
+ return joiner.toString();
+ }
+
public static String valueOf(boolean x) {
return "" + x;
}
diff --git a/user/super/com/google/gwt/emul/java/math/BigInteger.java b/user/super/com/google/gwt/emul/java/math/BigInteger.java
index 545b861..840cb3c 100644
--- a/user/super/com/google/gwt/emul/java/math/BigInteger.java
+++ b/user/super/com/google/gwt/emul/java/math/BigInteger.java
@@ -615,7 +615,7 @@
* does not fit in a {@code byte}.
*/
public byte byteValueExact() {
- if (numberLength <= 1 && bitLength() <= 7) {
+ if (numberLength <= 1 && bitLength() < Byte.SIZE) {
return byteValue();
}
throw new ArithmeticException("out of byte range");
@@ -923,7 +923,7 @@
* does not fit in an {@code int}.
*/
public int intValueExact() {
- if (numberLength <= 1 && bitLength() <= 31) {
+ if (numberLength <= 1 && bitLength() < Integer.SIZE) {
return intValue();
}
throw new ArithmeticException("out of int range");
@@ -965,7 +965,7 @@
* does not fit in a {@code long}.
*/
public long longValueExact() {
- if (numberLength <= 2 && bitLength() <= 63) {
+ if (numberLength <= 2 && bitLength() < Long.SIZE) {
return longValue();
}
throw new ArithmeticException("out of long range");
@@ -1304,7 +1304,7 @@
* does not fit in a {@code short}.
*/
public short shortValueExact() {
- if (numberLength <= 1 && bitLength() <= 15) {
+ if (numberLength <= 1 && bitLength() < Short.SIZE) {
return shortValue();
}
throw new ArithmeticException("out of short range");
diff --git a/user/test/com/google/gwt/emultest/java/lang/DoubleTest.java b/user/test/com/google/gwt/emultest/java/lang/DoubleTest.java
index 7fb15e5..8d84e2d 100644
--- a/user/test/com/google/gwt/emultest/java/lang/DoubleTest.java
+++ b/user/test/com/google/gwt/emultest/java/lang/DoubleTest.java
@@ -164,6 +164,38 @@
assertFalse(Double.isInfinite(Double.NaN));
}
+ public void testIsFinite() {
+ final double[] nonfiniteNumbers = {
+ Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN,
+ };
+ for (double value : nonfiniteNumbers) {
+ assertFalse(Double.isFinite(value));
+ }
+
+ final double[] finiteNumbers = {
+ -Double.MAX_VALUE, Double.MAX_VALUE, Double.MIN_VALUE,
+ -1.0, -0.5, -0.1, -0.0, 0.0, 0.1, 0.5, 1.0,
+ };
+ for (double value : finiteNumbers) {
+ assertTrue(Double.isFinite(value));
+ }
+ }
+
+ public void testIsInfinite() {
+ assertTrue(Double.isInfinite(Double.NEGATIVE_INFINITY));
+ assertTrue(Double.isInfinite(Double.POSITIVE_INFINITY));
+
+ assertFalse(Double.isInfinite(Double.NaN));
+
+ final double[] finiteNumbers = {
+ -Double.MAX_VALUE, Double.MAX_VALUE, Double.MIN_VALUE,
+ -1.0, -0.5, -0.1, -0.0, 0.0, 0.1, 0.5, 1.0,
+ };
+ for (double value : finiteNumbers) {
+ assertFalse(Double.isInfinite(value));
+ }
+ }
+
public void testParse() {
assertTrue(0 == Double.parseDouble("0"));
assertTrue(100 == Double.parseDouble("1e2"));
diff --git a/user/test/com/google/gwt/emultest/java/lang/FloatTest.java b/user/test/com/google/gwt/emultest/java/lang/FloatTest.java
index f12efcd..87dc276 100644
--- a/user/test/com/google/gwt/emultest/java/lang/FloatTest.java
+++ b/user/test/com/google/gwt/emultest/java/lang/FloatTest.java
@@ -97,6 +97,38 @@
assertFalse(Float.isInfinite(Float.NaN));
}
+ public void testIsFinite() {
+ final float[] nonfiniteNumbers = {
+ Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, Float.NaN,
+ };
+ for (float value : nonfiniteNumbers) {
+ assertFalse(Float.isFinite(value));
+ }
+
+ final float[] finiteNumbers = {
+ -Float.MAX_VALUE, Float.MAX_VALUE, Float.MIN_VALUE,
+ -1.0f, -0.5f, -0.1f, -0.0f, 0.0f, 0.1f, 0.5f, 1.0f,
+ };
+ for (float value : finiteNumbers) {
+ assertTrue(Float.isFinite(value));
+ }
+ }
+
+ public void testIsInfinite() {
+ assertTrue(Float.isInfinite(Float.NEGATIVE_INFINITY));
+ assertTrue(Float.isInfinite(Float.POSITIVE_INFINITY));
+
+ assertFalse(Float.isInfinite(Float.NaN));
+
+ final float[] finiteNumbers = {
+ -Float.MAX_VALUE, Float.MAX_VALUE, Float.MIN_VALUE,
+ -1.0f, -0.5f, -0.1f, -0.0f, 0.0f, 0.1f, 0.5f, 1.0f,
+ };
+ for (float value : finiteNumbers) {
+ assertFalse(Float.isInfinite(value));
+ }
+ }
+
public void testParse() {
/*
* Note: we must use appropriate deltas for a somewhat subtle reason.
diff --git a/user/test/com/google/gwt/emultest/java/lang/MathTest.java b/user/test/com/google/gwt/emultest/java/lang/MathTest.java
index 55d295a..9b8bb5d 100644
--- a/user/test/com/google/gwt/emultest/java/lang/MathTest.java
+++ b/user/test/com/google/gwt/emultest/java/lang/MathTest.java
@@ -18,6 +18,9 @@
import com.google.gwt.junit.client.GWTTestCase;
+import java.math.BigInteger;
+import java.util.ArrayList;
+
/**
* Tests for JRE emulation of java.lang.Math.
*
@@ -25,6 +28,9 @@
*/
public class MathTest extends GWTTestCase {
+ private static final Integer[] ALL_INTEGER_CANDIDATES = getAllIntegerCandidates();
+ private static final Long[] ALL_LONG_CANDIDATES = getAllLongCandidates();
+
private static boolean isNegativeZero(double x) {
return Double.doubleToLongBits(-0.0) == Double.doubleToLongBits(x);
}
@@ -58,6 +64,36 @@
assertTrue(Double.isNaN(v));
}
+ public void testAddExact() {
+ for (int a : ALL_INTEGER_CANDIDATES) {
+ for (int b : ALL_INTEGER_CANDIDATES) {
+ BigInteger expectedResult = BigInteger.valueOf(a).add(BigInteger.valueOf(b));
+ boolean expectedSuccess = fitsInInt(expectedResult);
+ try {
+ assertEquals(a + b, Math.addExact(a, b));
+ assertTrue(expectedSuccess);
+ } catch (ArithmeticException e) {
+ assertFalse(expectedSuccess);
+ }
+ }
+ }
+ }
+
+ public void testAddExactLongs() {
+ for (long a : ALL_LONG_CANDIDATES) {
+ for (long b : ALL_LONG_CANDIDATES) {
+ BigInteger expectedResult = BigInteger.valueOf(a).add(BigInteger.valueOf(b));
+ boolean expectedSuccess = fitsInLong(expectedResult);
+ try {
+ assertEquals(a + b, Math.addExact(a, b));
+ assertTrue(expectedSuccess);
+ } catch (ArithmeticException e) {
+ assertFalse(expectedSuccess);
+ }
+ }
+ }
+ }
+
public void testCbrt() {
double v = Math.cbrt(1000.0);
assertEquals(10.0, v, 1e-7);
@@ -97,6 +133,120 @@
assertEquals(Double.POSITIVE_INFINITY, v);
}
+ public void testDecrementExact() {
+ for (int a : ALL_INTEGER_CANDIDATES) {
+ BigInteger expectedResult = BigInteger.valueOf(a).subtract(BigInteger.ONE);
+ boolean expectedSuccess = fitsInInt(expectedResult);
+ try {
+ assertEquals(a - 1, Math.decrementExact(a));
+ assertTrue(expectedSuccess);
+ } catch (ArithmeticException e) {
+ assertFalse(expectedSuccess);
+ }
+ }
+ }
+
+ public void testDecrementExactLong() {
+ for (long a : ALL_LONG_CANDIDATES) {
+ BigInteger expectedResult = BigInteger.valueOf(a).subtract(BigInteger.ONE);
+ boolean expectedSuccess = fitsInLong(expectedResult);
+ try {
+ assertEquals(a - 1, Math.decrementExact(a));
+ assertTrue(expectedSuccess);
+ } catch (ArithmeticException e) {
+ assertFalse(expectedSuccess);
+ }
+ }
+ }
+
+ public void testFloorDiv() {
+ assertEquals(0, Math.floorDiv(0, 1));
+ assertEquals(1, Math.floorDiv(4, 3));
+ assertEquals(-2, Math.floorDiv(4, -3));
+ assertEquals(-2, Math.floorDiv(-4, 3));
+ assertEquals(1, Math.floorDiv(-4, -3));
+
+ // special case
+ assertEquals(Integer.MIN_VALUE, Math.floorDiv(Integer.MIN_VALUE, -1));
+
+ try {
+ Math.floorDiv(1, 0);
+ fail();
+ } catch (ArithmeticException expected) {
+ }
+ }
+
+ public void testFloorDivLongs() {
+ assertEquals(0L, Math.floorDiv(0L, 1L));
+ assertEquals(1L, Math.floorDiv(4L, 3L));
+ assertEquals(-2L, Math.floorDiv(4L, -3L));
+ assertEquals(-2L, Math.floorDiv(-4L, 3L));
+ assertEquals(1L, Math.floorDiv(-4L, -3L));
+
+ // special case
+ assertEquals(Long.MIN_VALUE, Math.floorDiv(Long.MIN_VALUE, -1));
+
+ try {
+ Math.floorDiv(1L, 0L);
+ fail();
+ } catch (ArithmeticException expected) {
+ }
+ }
+
+ public void testFloorMod() {
+ assertEquals(0, Math.floorMod(0, 1));
+ assertEquals(1, Math.floorMod(4, 3));
+ assertEquals(-2, Math.floorMod(4, -3));
+ assertEquals(2, Math.floorMod(-4, 3));
+ assertEquals(-1, Math.floorMod(-4, -3));
+
+ try {
+ Math.floorMod(1, 0);
+ fail();
+ } catch (ArithmeticException expected) {
+ }
+ }
+
+ public void testFloorModLongs() {
+ assertEquals(0L, Math.floorMod(0L, 1L));
+ assertEquals(1L, Math.floorMod(4L, 3L));
+ assertEquals(-2L, Math.floorMod(4L, -3L));
+ assertEquals(2L, Math.floorMod(-4L, 3L));
+ assertEquals(-1L, Math.floorMod(-4L, -3L));
+
+ try {
+ Math.floorMod(1L, 0L);
+ fail();
+ } catch (ArithmeticException expected) {
+ }
+ }
+
+ public void testIncrementExact() {
+ for (int a : ALL_INTEGER_CANDIDATES) {
+ BigInteger expectedResult = BigInteger.valueOf(a).add(BigInteger.ONE);
+ boolean expectedSuccess = fitsInInt(expectedResult);
+ try {
+ assertEquals(a + 1, Math.incrementExact(a));
+ assertTrue(expectedSuccess);
+ } catch (ArithmeticException e) {
+ assertFalse(expectedSuccess);
+ }
+ }
+ }
+
+ public void testIncrementExactLong() {
+ for (long a : ALL_LONG_CANDIDATES) {
+ BigInteger expectedResult = BigInteger.valueOf(a).add(BigInteger.ONE);
+ boolean expectedSuccess = fitsInLong(expectedResult);
+ try {
+ assertEquals(a + 1, Math.incrementExact(a));
+ assertTrue(expectedSuccess);
+ } catch (ArithmeticException e) {
+ assertFalse(expectedSuccess);
+ }
+ }
+ }
+
public void testMax() {
assertEquals(2d, Math.max(1d, 2d));
assertEquals(2d, Math.max(2d, 1d));
@@ -171,6 +321,62 @@
assertEquals(3.0, v, 1e-15);
}
+ public void testMultiplyExact() {
+ for (int a : ALL_INTEGER_CANDIDATES) {
+ for (int b : ALL_INTEGER_CANDIDATES) {
+ BigInteger expectedResult = BigInteger.valueOf(a).multiply(BigInteger.valueOf(b));
+ boolean expectedSuccess = fitsInInt(expectedResult);
+ try {
+ assertEquals(a * b, Math.multiplyExact(a, b));
+ assertTrue(expectedSuccess);
+ } catch (ArithmeticException e) {
+ assertFalse(expectedSuccess);
+ }
+ }
+ }
+ }
+
+ public void testMultiplyExactLongs() {
+ for (long a : ALL_LONG_CANDIDATES) {
+ for (long b : ALL_LONG_CANDIDATES) {
+ BigInteger expectedResult = BigInteger.valueOf(a).multiply(BigInteger.valueOf(b));
+ boolean expectedSuccess = fitsInLong(expectedResult);
+ try {
+ assertEquals(a * b, Math.multiplyExact(a, b));
+ assertTrue(expectedSuccess);
+ } catch (ArithmeticException e) {
+ assertFalse(expectedSuccess);
+ }
+ }
+ }
+ }
+
+ public void testNegateExact() {
+ for (int a : ALL_INTEGER_CANDIDATES) {
+ BigInteger expectedResult = BigInteger.valueOf(a).negate();
+ boolean expectedSuccess = fitsInInt(expectedResult);
+ try {
+ assertEquals(-a, Math.negateExact(a));
+ assertTrue(expectedSuccess);
+ } catch (ArithmeticException e) {
+ assertFalse(expectedSuccess);
+ }
+ }
+ }
+
+ public void testNegateExactLong() {
+ for (long a : ALL_LONG_CANDIDATES) {
+ BigInteger expectedResult = BigInteger.valueOf(a).negate();
+ boolean expectedSuccess = fitsInLong(expectedResult);
+ try {
+ assertEquals(-a, Math.negateExact(a));
+ assertTrue(expectedSuccess);
+ } catch (ArithmeticException e) {
+ assertFalse(expectedSuccess);
+ }
+ }
+ }
+
public void testSin() {
double v = Math.sin(0.0);
assertEquals(0.0, v, 1e-7);
@@ -254,4 +460,97 @@
assertEquals(4294967296.0f, Math.scalb(1f, 32));
assertEquals(2.3283064e-10f, Math.scalb(1f, -32), 1e-7f);
}
+
+ public void testSubtractExact() {
+ for (int a : ALL_INTEGER_CANDIDATES) {
+ for (int b : ALL_INTEGER_CANDIDATES) {
+ BigInteger expectedResult = BigInteger.valueOf(a).subtract(BigInteger.valueOf(b));
+ boolean expectedSuccess = fitsInInt(expectedResult);
+ try {
+ assertEquals(a - b, Math.subtractExact(a, b));
+ assertTrue(expectedSuccess);
+ } catch (ArithmeticException e) {
+ assertFalse(expectedSuccess);
+ }
+ }
+ }
+ }
+
+ public void testSubtractExactLongs() {
+ for (long a : ALL_LONG_CANDIDATES) {
+ for (long b : ALL_LONG_CANDIDATES) {
+ BigInteger expectedResult = BigInteger.valueOf(a).subtract(BigInteger.valueOf(b));
+ boolean expectedSuccess = fitsInLong(expectedResult);
+ try {
+ assertEquals(a - b, Math.subtractExact(a, b));
+ assertTrue(expectedSuccess);
+ } catch (ArithmeticException e) {
+ assertFalse(expectedSuccess);
+ }
+ }
+ }
+ }
+
+ public void testToIntExact() {
+ final long[] longs = {0, -1, 1, Integer.MIN_VALUE, Integer.MAX_VALUE,
+ Integer.MIN_VALUE - 1L, Integer.MAX_VALUE + 1L, Long.MIN_VALUE, Long.MAX_VALUE};
+ for (long a : longs) {
+ boolean expectedSuccess = (int) a == a;
+ try {
+ assertEquals((int) a, Math.toIntExact(a));
+ assertTrue(expectedSuccess);
+ } catch (ArithmeticException e) {
+ assertFalse(expectedSuccess);
+ }
+ }
+ }
+
+ private static boolean fitsInInt(BigInteger big) {
+ return big.bitLength() < Integer.SIZE;
+ }
+
+ private static boolean fitsInLong(BigInteger big) {
+ return big.bitLength() < Long.SIZE;
+ }
+
+ private static Integer[] getAllIntegerCandidates() {
+ ArrayList<Integer> candidates = new ArrayList<Integer>();
+ candidates.add(0);
+ candidates.add(-1);
+ candidates.add(1);
+ candidates.add(Integer.MAX_VALUE / 2);
+ candidates.add(Integer.MAX_VALUE / 2 - 1);
+ candidates.add(Integer.MAX_VALUE / 2 + 1);
+ candidates.add(Integer.MIN_VALUE / 2);
+ candidates.add(Integer.MIN_VALUE / 2 - 1);
+ candidates.add(Integer.MIN_VALUE / 2 + 1);
+ candidates.add(Integer.MAX_VALUE - 1);
+ candidates.add(Integer.MAX_VALUE);
+ candidates.add(Integer.MIN_VALUE + 1);
+ candidates.add(Integer.MIN_VALUE);
+ return candidates.toArray(new Integer[candidates.size()]);
+ }
+
+ private static Long[] getAllLongCandidates() {
+ ArrayList<Long> candidates = new ArrayList<Long>();
+
+ for (Integer x : getAllIntegerCandidates()) {
+ candidates.add(x.longValue());
+ }
+
+ candidates.add(Long.MAX_VALUE / 2);
+ candidates.add(Long.MAX_VALUE / 2 - 1);
+ candidates.add(Long.MAX_VALUE / 2 + 1);
+ candidates.add(Long.MIN_VALUE / 2);
+ candidates.add(Long.MIN_VALUE / 2 - 1);
+ candidates.add(Long.MIN_VALUE / 2 + 1);
+ candidates.add(Integer.MAX_VALUE + 1L);
+ candidates.add(Long.MAX_VALUE - 1L);
+ candidates.add(Long.MAX_VALUE);
+ candidates.add(Integer.MIN_VALUE - 1L);
+ candidates.add(Long.MIN_VALUE + 1L);
+ candidates.add(Long.MIN_VALUE);
+
+ return candidates.toArray(new Long[candidates.size()]);
+ }
}
diff --git a/user/test/com/google/gwt/emultest/java/lang/StringTest.java b/user/test/com/google/gwt/emultest/java/lang/StringTest.java
index b9f06aa..e897f9d 100644
--- a/user/test/com/google/gwt/emultest/java/lang/StringTest.java
+++ b/user/test/com/google/gwt/emultest/java/lang/StringTest.java
@@ -20,6 +20,7 @@
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
+import java.util.Arrays;
import java.util.Locale;
/**
@@ -460,6 +461,24 @@
assertSame("interns are not the same reference", s1.intern(), s2.intern());
}
+ public void testJoin() {
+ assertEquals("", String.join("", ""));
+ assertEquals("", String.join(",", ""));
+ assertEquals("", String.join(",", Arrays.<String>asList()));
+
+ assertEquals("a", String.join("", "a"));
+ assertEquals("a", String.join(",", "a"));
+ assertEquals("a", String.join(",", Arrays.asList("a")));
+
+ assertEquals("ab", String.join("", "a", "b"));
+ assertEquals("a,b", String.join(",", "a", "b"));
+ assertEquals("a,b", String.join(",", Arrays.asList("a", "b")));
+
+ assertEquals("abc", String.join("", "a", "b", "c"));
+ assertEquals("a,b,c", String.join(",", "a", "b", "c"));
+ assertEquals("a,b,c", String.join(",", Arrays.asList("a", "b", "c")));
+ }
+
public void testLastIndexOf() {
String x = "abcdeabcdef";
assertEquals(9, x.lastIndexOf("e"));