Backport Java 9 Objects methods
Test: tools/test.py --dex_vm all --no-internal -v *Backport* *corelibjdktests*
Change-Id: Icea5518fb116dea894cae253d8e4e9b30ee78b6d
diff --git a/build.gradle b/build.gradle
index 2edc245..889620f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -130,6 +130,12 @@
include '**ExactArithTests.java'
}
}
+ jdk11ObjectsTests {
+ java {
+ srcDirs = ['third_party/openjdk/jdk-11-test/java/util/Objects']
+ include '**BasicObjectsTest.java'
+ }
+ }
jdk11StrictMathTests {
java {
srcDirs = ['third_party/openjdk/jdk-11-test/java/lang/StrictMath']
@@ -553,6 +559,7 @@
setJava11Compilation(sourceSets.examplesJava11.compileJavaTaskName)
setJava11Compilation(sourceSets.jdk11MathTests.compileJavaTaskName)
+setJava11Compilation(sourceSets.jdk11ObjectsTests.compileJavaTaskName)
setJava11Compilation(sourceSets.jdk11StrictMathTests.compileJavaTaskName)
task compileMainWithJava11 (type: JavaCompile) {
@@ -1861,6 +1868,7 @@
dependsOn buildPreNJdwpTestsJar
dependsOn buildPreNJdwpTestsDex
dependsOn sourceSets.jdk11MathTests.output
+ dependsOn sourceSets.jdk11ObjectsTests.output
dependsOn sourceSets.jdk11StrictMathTests.output
} else {
logger.lifecycle("WARNING: Testing in not supported on your platform. Testing is only fully supported on " +
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 2adbb70..ee9d9ff 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -215,6 +215,7 @@
public final DexString listDescriptor = createString("Ljava/util/List;");
public final DexString comparatorDescriptor = createString("Ljava/util/Comparator;");
public final DexString callableDescriptor = createString("Ljava/util/concurrent/Callable;");
+ public final DexString supplierDescriptor = createString("Ljava/util/function/Supplier;");
public final DexString throwableDescriptor = createString(throwableDescriptorString);
public final DexString illegalAccessErrorDescriptor =
@@ -294,6 +295,7 @@
public final DexType listType = createType(listDescriptor);
public final DexType comparatorType = createType(comparatorDescriptor);
public final DexType callableType = createType(callableDescriptor);
+ public final DexType supplierType = createType(supplierDescriptor);
public final DexType throwableType = createType(throwableDescriptor);
public final DexType illegalAccessErrorType = createType(illegalAccessErrorDescriptor);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 7a830e8..4b617f3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -800,6 +800,36 @@
method = factory.createString("compareUnsigned");
proto = factory.createProto(factory.intType, factory.shortType, factory.shortType);
addProvider(new MethodGenerator(clazz, method, proto, ShortMethods::new));
+
+ // Objects
+ clazz = factory.objectsDescriptor;
+
+ // T Objects.requireNonNullElse(T, T)
+ method = factory.createString("requireNonNullElse");
+ proto = factory.createProto(factory.objectType, factory.objectType, factory.objectType);
+ addProvider(new MethodGenerator(clazz, method, proto, ObjectsMethods::new));
+
+ // T Objects.requireNonNullElseGet(T, Supplier<? extends T>)
+ method = factory.createString("requireNonNullElseGet");
+ proto = factory.createProto(factory.objectType, factory.objectType, factory.supplierType);
+ addProvider(new MethodGenerator(clazz, method, proto, ObjectsMethods::new));
+
+ // int Objects.checkIndex(int, int)
+ method = factory.createString("checkIndex");
+ proto = factory.createProto(factory.intType, factory.intType, factory.intType);
+ addProvider(new MethodGenerator(clazz, method, proto, ObjectsMethods::new));
+
+ // int Objects.checkFromToIndex(int, int, int)
+ method = factory.createString("checkFromToIndex");
+ proto =
+ factory.createProto(factory.intType, factory.intType, factory.intType, factory.intType);
+ addProvider(new MethodGenerator(clazz, method, proto, ObjectsMethods::new));
+
+ // int Objects.checkFromIndexSize(int, int, int)
+ method = factory.createString("checkFromIndexSize");
+ proto =
+ factory.createProto(factory.intType, factory.intType, factory.intType, factory.intType);
+ addProvider(new MethodGenerator(clazz, method, proto, ObjectsMethods::new));
}
private void warnMissingRetargetCoreLibraryMember(DexType type, AppView<?> appView) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethods.java
index 44e8dca..4fc3cb1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethods.java
@@ -10,6 +10,7 @@
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
+import java.util.function.Supplier;
public final class ObjectsMethods extends TemplateMethodCode {
public ObjectsMethods(InternalOptions options, DexMethod method, String methodName) {
@@ -80,6 +81,17 @@
return obj;
}
+ public static <T> T requireNonNullElse(T obj, T defaultObj) {
+ if (obj != null) return obj;
+ return Objects.requireNonNull(defaultObj, "defaultObj");
+ }
+
+ public static <T> T requireNonNullElseGet(T obj, Supplier<? extends T> supplier) {
+ if (obj != null) return obj;
+ T defaultObj = Objects.requireNonNull(supplier, "supplier").get();
+ return Objects.requireNonNull(defaultObj, "supplier.get()");
+ }
+
public static String toString(Object o) {
return Objects.toString(o, "null");
}
@@ -87,4 +99,33 @@
public static String toStringDefault(Object o, String nullDefault) {
return o == null ? nullDefault : o.toString();
}
+
+ public static int checkIndex(int index, int length) {
+ if (index < 0 || index >= length) {
+ throw new IndexOutOfBoundsException("Index " + index + " out of bounds for length " + length);
+ }
+ return index;
+ }
+
+ public static int checkFromToIndex(int fromIndex, int toIndex, int length) {
+ if (fromIndex < 0 || fromIndex > toIndex || toIndex > length) {
+ throw new IndexOutOfBoundsException(
+ "Range [" + fromIndex + ", " + toIndex + ") out of bounds for length " + length);
+ }
+ return fromIndex;
+ }
+
+ public static int checkFromIndexSize(int fromIndex, int size, int length) {
+ if (fromIndex < 0 || size < 0 || length < 0 || fromIndex > length - size) {
+ throw new IndexOutOfBoundsException("Range ["
+ + fromIndex
+ + ", "
+ + fromIndex
+ + " + "
+ + size
+ + ") out of bounds for length "
+ + length);
+ }
+ return fromIndex;
+ }
}
diff --git a/src/test/examplesJava9/backport/ObjectsBackportJava9Main.java b/src/test/examplesJava9/backport/ObjectsBackportJava9Main.java
new file mode 100644
index 0000000..483b176
--- /dev/null
+++ b/src/test/examplesJava9/backport/ObjectsBackportJava9Main.java
@@ -0,0 +1,155 @@
+package backport;
+
+import java.util.Objects;
+
+public final class ObjectsBackportJava9Main {
+ public static void main(String[] args) {
+ boolean isAndroid = "Dalvik".equals(System.getProperty("java.vm.name"));
+ String majorVersion = System.getProperty("java.vm.version").split("\\.", -1)[0];
+
+ testRequireNonNullElse();
+ if (!isAndroid || Integer.parseInt(majorVersion) >= 7) {
+ // TODO desugaring corelib is blocked by https://issuetracker.google.com/issues/114481425
+ testRequireNonNullElseGet();
+ }
+ testCheckIndex();
+ testCheckFromToIndex();
+ testCheckFromIndexSize();
+ }
+
+ private static void testRequireNonNullElse() {
+ Object one = new Object();
+ Object two = new Object();
+
+ assertSame(one, Objects.requireNonNullElse(one, two));
+ assertSame(two, Objects.requireNonNullElse(null, two));
+
+ try {
+ throw new AssertionError(Objects.requireNonNullElse(null, null));
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ private static void testRequireNonNullElseGet() {
+ Object one = new Object();
+ Object two = new Object();
+
+ assertSame(one, Objects.requireNonNullElseGet(one, () -> two));
+ assertSame(two, Objects.requireNonNullElseGet(null, () -> two));
+
+ try {
+ throw new AssertionError(Objects.requireNonNullElseGet(null, null));
+ } catch (NullPointerException expected) {
+ }
+ try {
+ throw new AssertionError(Objects.requireNonNullElseGet(null, () -> null));
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ private static void testCheckIndex() {
+ for (int i = 0; i < 10; i++) {
+ assertEquals(i, Objects.checkIndex(i, 10));
+ }
+
+ try {
+ throw new AssertionError(Objects.checkIndex(-1, 10));
+ } catch (IndexOutOfBoundsException expected) {
+ }
+ try {
+ throw new AssertionError(Objects.checkIndex(10, 0));
+ } catch (IndexOutOfBoundsException expected) {
+ }
+ try {
+ throw new AssertionError(Objects.checkIndex(0, 0));
+ } catch (IndexOutOfBoundsException expected) {
+ }
+ }
+
+ private static void testCheckFromToIndex() {
+ for (int i = 0; i <= 10; i++) {
+ for (int j = i; j <= 10; j++) {
+ assertEquals(i, Objects.checkFromToIndex(i, j, 10));
+ }
+ }
+ assertEquals(0, Objects.checkFromToIndex(0, 0, 0));
+
+ try {
+ throw new AssertionError(Objects.checkFromToIndex(4, 2, 10));
+ } catch (IndexOutOfBoundsException expected) {
+ }
+ try {
+ throw new AssertionError(Objects.checkFromToIndex(-1, 5, 10));
+ } catch (IndexOutOfBoundsException expected) {
+ }
+ try {
+ throw new AssertionError(Objects.checkFromToIndex(0, -1, 10));
+ } catch (IndexOutOfBoundsException expected) {
+ }
+ try {
+ throw new AssertionError(Objects.checkFromToIndex(11, 11, 10));
+ } catch (IndexOutOfBoundsException expected) {
+ }
+ try {
+ throw new AssertionError(Objects.checkFromToIndex(0, 1, 0));
+ } catch (IndexOutOfBoundsException expected) {
+ }
+ try {
+ throw new AssertionError(Objects.checkFromToIndex(1, 1, 0));
+ } catch (IndexOutOfBoundsException expected) {
+ }
+ }
+
+ private static void testCheckFromIndexSize() {
+ for (int i = 0; i <= 10; i++) {
+ for (int j = 10 - i; j >= 0; j--) {
+ assertEquals(i, Objects.checkFromIndexSize(i, j, 10));
+ }
+ }
+ assertEquals(0, Objects.checkFromIndexSize(0, 0, 0));
+
+ try {
+ throw new AssertionError(Objects.checkFromIndexSize(8, 4, 10));
+ } catch (IndexOutOfBoundsException expected) {
+ }
+ try {
+ throw new AssertionError(Objects.checkFromIndexSize(-1, 5, 10));
+ } catch (IndexOutOfBoundsException expected) {
+ }
+ try {
+ throw new AssertionError(Objects.checkFromIndexSize(11, 0, 10));
+ } catch (IndexOutOfBoundsException expected) {
+ }
+ try {
+ throw new AssertionError(Objects.checkFromIndexSize(0, 1, 0));
+ } catch (IndexOutOfBoundsException expected) {
+ }
+ try {
+ throw new AssertionError(Objects.checkFromIndexSize(1, 1, 0));
+ } catch (IndexOutOfBoundsException expected) {
+ }
+
+ // Check for cases where overflow might occur producing incorrect results.
+ try {
+ throw new AssertionError(Objects.checkFromIndexSize(Integer.MAX_VALUE, 1, Integer.MAX_VALUE));
+ } catch (IndexOutOfBoundsException expected) {
+ }
+ try {
+ throw new AssertionError(Objects.checkFromIndexSize(0, 1, Integer.MIN_VALUE));
+ } catch (IndexOutOfBoundsException expected) {
+ }
+ }
+
+ private static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new AssertionError("Expected <" + expected + "> but was <" + actual + '>');
+ }
+ }
+
+ private static void assertSame(Object expected, Object actual) {
+ if (expected != actual) {
+ throw new AssertionError(
+ "Expected <" + expected + "> to be same instance as <" + actual + '>');
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java
new file mode 100644
index 0000000..da50daf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.backports;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
+@RunWith(Parameterized.class)
+public final class ObjectsBackportJava9Test extends AbstractBackportTest {
+ @Parameters(name = "{0}")
+ public static Iterable<?> data() {
+ return getTestParameters()
+ .withDexRuntimes()
+ .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+ .build();
+ }
+
+ private static final Path TEST_JAR =
+ Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
+
+ public ObjectsBackportJava9Test(TestParameters parameters) {
+ super(parameters, Short.class, TEST_JAR, "backport.ObjectsBackportJava9Main");
+ // TODO Once shipped in an actual API level, migrate to ObjectsBackportTest
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11ObjectsTests.java b/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11ObjectsTests.java
new file mode 100644
index 0000000..00e85da
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/corelibjdktests/Jdk11ObjectsTests.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.corelib.corelibjdktests;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Jdk11ObjectsTests extends TestBase {
+
+ private static final Path JDK_11_OBJECTS_TESTS_DIR =
+ Paths.get(ToolHelper.JAVA_CLASSES_DIR + "jdk11ObjectsTests");
+ private static final String BASICOBJECTSTEST = "BasicObjectsTest";
+ private static final String CLASS_SUFFIX = ".class";
+ private static final Path[] JDK_11_OBJECTS_TEST_CLASS_FILES =
+ new Path[] {
+ JDK_11_OBJECTS_TESTS_DIR.resolve(BASICOBJECTSTEST + CLASS_SUFFIX),
+ };
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ // TODO desugaring corelib is blocked by https://issuetracker.google.com/issues/114481425
+ .withDexRuntimesStartingFromIncluding(DexVm.Version.V8_1_0)
+ .withCfRuntime(CfVm.JDK11)
+ .build();
+ }
+
+ public Jdk11ObjectsTests(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testD8Objects() throws Exception {
+ Assume.assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .addProgramFiles(JDK_11_OBJECTS_TEST_CLASS_FILES)
+ .setMinApi(parameters.getRuntime())
+ .run(parameters.getRuntime(), BASICOBJECTSTEST)
+ .assertSuccessWithOutput("");
+ }
+
+ @Test
+ public void testR8Objects() throws Exception {
+ Path libraryJar;
+ if (parameters.isDexRuntime()) {
+ libraryJar = ToolHelper.getAndroidJar(parameters.getRuntime().asDex().getMinApiLevel());
+ } else {
+ libraryJar = ToolHelper.getJava8RuntimeJar();
+ }
+ testForR8(parameters.getBackend())
+ .addLibraryFiles(libraryJar)
+ .addKeepMainRule(BASICOBJECTSTEST)
+ .addProgramFiles(JDK_11_OBJECTS_TEST_CLASS_FILES)
+ .setMinApi(parameters.getRuntime())
+ .run(parameters.getRuntime(), BASICOBJECTSTEST)
+ .assertSuccessWithOutput("");
+ }
+}