Backport List/Map/Set.copyOf factories
Test: tools/test.py --dex_vm all --no-internal -v *Backport*Test*
Test: tools/test.py --no-internal -v *GenerateBackportMethods*
Change-Id: I18521bbdfd8b72eaaa78b4cc09ea5d06a44ec6f3
diff --git a/build.gradle b/build.gradle
index 4699982..1fd0955 100644
--- a/build.gradle
+++ b/build.gradle
@@ -125,6 +125,11 @@
srcDirs = ['src/test/examplesJava9']
}
}
+ examplesJava10 {
+ java {
+ srcDirs = ['src/test/examplesJava10']
+ }
+ }
examplesJava11 {
java {
srcDirs = ['src/test/examplesJava11']
@@ -550,7 +555,7 @@
targetCompatibility = JavaVersion.VERSION_1_9
}
-def setJava11Compilation(sourceSet) {
+def setJdk11CompilationWithCompatibility(String sourceSet, JavaVersion compatibility) {
tasks.named(sourceSet).get().configure {
def jdkDir = 'third_party/openjdk/jdk-11/'
options.fork = true
@@ -562,14 +567,15 @@
} else {
options.forkOptions.javaHome = file(jdkDir + 'Windows')
}
- sourceCompatibility = JavaVersion.VERSION_11
- targetCompatibility = JavaVersion.VERSION_11
+ sourceCompatibility = compatibility
+ targetCompatibility = compatibility
}
}
-setJava11Compilation(sourceSets.examplesJava11.compileJavaTaskName)
-setJava11Compilation(sourceSets.examplesTestNGRunner.compileJavaTaskName)
-setJava11Compilation(sourceSets.jdk11TimeTests.compileJavaTaskName)
+setJdk11CompilationWithCompatibility(sourceSets.examplesJava10.compileJavaTaskName, JavaVersion.VERSION_1_10)
+setJdk11CompilationWithCompatibility(sourceSets.examplesJava11.compileJavaTaskName, JavaVersion.VERSION_11)
+setJdk11CompilationWithCompatibility(sourceSets.examplesTestNGRunner.compileJavaTaskName, JavaVersion.VERSION_11)
+setJdk11CompilationWithCompatibility(sourceSets.jdk11TimeTests.compileJavaTaskName, JavaVersion.VERSION_11)
task compileMainWithJava11 (type: JavaCompile) {
dependsOn downloadDeps
@@ -1434,6 +1440,22 @@
}
}
+task buildExampleJava10Jars {
+ def examplesDir = file("src/test/examplesJava10")
+ examplesDir.eachDir { dir ->
+ def name = dir.getName();
+ def exampleOutputDir = file("build/test/examplesJava10");
+ def jarName = "${name}.jar"
+ dependsOn "jar_examplesJava10_${name}"
+ task "jar_examplesJava10_${name}"(type: Jar) {
+ archiveName = jarName
+ destinationDir = exampleOutputDir
+ from sourceSets.examplesJava10.output
+ include "**/" + name + "/**/*.class"
+ }
+ }
+}
+
task buildExampleJava11Jars {
def examplesDir = file("src/test/examplesJava11")
examplesDir.eachDir { dir ->
@@ -1566,6 +1588,7 @@
dependsOn buildExampleAndroidOJars
dependsOn buildExampleAndroidPJars
dependsOn buildExampleJava9Jars
+ dependsOn buildExampleJava10Jars
dependsOn buildExampleJava11Jars
dependsOn buildExampleAndroidApi
def examplesDir = file("src/test/examples")
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 8b44470..f2e7147 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
@@ -513,6 +513,7 @@
// These are currently not implemented at any API level in Android.
initializeJava9MethodProviders(factory);
+ initializeJava10MethodProviders(factory);
initializeJava11MethodProviders(factory);
if (!options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()) {
@@ -1367,6 +1368,41 @@
addProvider(new MethodGenerator(method, BackportedMethods::CollectionMethods_mapEntry));
}
+ private void initializeJava10MethodProviders(DexItemFactory factory) {
+ // List
+ DexType type = factory.listType;
+
+ // List List.copyOf(Collection)
+ DexString name = factory.createString("copyOf");
+ DexProto proto = factory.createProto(factory.listType, factory.collectionType);
+ DexMethod method = factory.createMethod(type, proto, name);
+ addProvider(
+ new MethodGenerator(
+ method, BackportedMethods::CollectionsMethods_copyOfList, "copyOfList"));
+
+ // Set
+ type = factory.setType;
+
+ // Set Set.copyOf(Collection)
+ name = factory.createString("copyOf");
+ proto = factory.createProto(factory.setType, factory.collectionType);
+ method = factory.createMethod(type, proto, name);
+ addProvider(
+ new MethodGenerator(
+ method, BackportedMethods::CollectionsMethods_copyOfSet, "copyOfSet"));
+
+ // Set
+ type = factory.mapType;
+
+ // Map Map.copyOf(Map)
+ name = factory.createString("copyOf");
+ proto = factory.createProto(factory.mapType, factory.mapType);
+ method = factory.createMethod(type, proto, name);
+ addProvider(
+ new MethodGenerator(
+ method, BackportedMethods::CollectionsMethods_copyOfMap, "copyOfMap"));
+ }
+
private void initializeJava11MethodProviders(DexItemFactory factory) {
// Character
DexType type = factory.boxedCharType;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
index 5ca3dbd..3494e60 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -958,6 +958,363 @@
ImmutableList.of());
}
+ public static CfCode CollectionsMethods_copyOfList(InternalOptions options, DexMethod method) {
+ CfLabel label0 = new CfLabel();
+ CfLabel label1 = new CfLabel();
+ CfLabel label2 = new CfLabel();
+ CfLabel label3 = new CfLabel();
+ CfLabel label4 = new CfLabel();
+ CfLabel label5 = new CfLabel();
+ CfLabel label6 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 3,
+ 4,
+ ImmutableList.of(
+ label0,
+ new CfNew(options.itemFactory.createType("Ljava/util/ArrayList;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInvoke(
+ 185,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Collection;"),
+ options.itemFactory.createProto(options.itemFactory.createType("I")),
+ options.itemFactory.createString("size")),
+ true),
+ new CfInvoke(
+ 183,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/ArrayList;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("V"), options.itemFactory.createType("I")),
+ options.itemFactory.createString("<init>")),
+ false),
+ new CfStore(ValueType.OBJECT, 1),
+ label1,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInvoke(
+ 185,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Collection;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/util/Iterator;")),
+ options.itemFactory.createString("iterator")),
+ true),
+ new CfStore(ValueType.OBJECT, 2),
+ label2,
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfInvoke(
+ 185,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Iterator;"),
+ options.itemFactory.createProto(options.itemFactory.createType("Z")),
+ options.itemFactory.createString("hasNext")),
+ true),
+ new CfIf(If.Type.EQ, ValueType.INT, label5),
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfInvoke(
+ 185,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Iterator;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("next")),
+ true),
+ new CfStore(ValueType.OBJECT, 3),
+ label3,
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfLoad(ValueType.OBJECT, 3),
+ new CfInvoke(
+ 184,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Objects;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/Object;"),
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("requireNonNull")),
+ false),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/ArrayList;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Z"),
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("add")),
+ false),
+ new CfStackInstruction(CfStackInstruction.Opcode.Pop),
+ label4,
+ new CfGoto(label2),
+ label5,
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfInvoke(
+ 184,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Collections;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/util/List;"),
+ options.itemFactory.createType("Ljava/util/List;")),
+ options.itemFactory.createString("unmodifiableList")),
+ false),
+ new CfReturn(ValueType.OBJECT),
+ label6),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
+ public static CfCode CollectionsMethods_copyOfMap(InternalOptions options, DexMethod method) {
+ CfLabel label0 = new CfLabel();
+ CfLabel label1 = new CfLabel();
+ CfLabel label2 = new CfLabel();
+ CfLabel label3 = new CfLabel();
+ CfLabel label4 = new CfLabel();
+ CfLabel label5 = new CfLabel();
+ CfLabel label6 = new CfLabel();
+ CfLabel label7 = new CfLabel();
+ CfLabel label8 = new CfLabel();
+ CfLabel label9 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 3,
+ 4,
+ ImmutableList.of(
+ label0,
+ new CfNew(options.itemFactory.createType("Ljava/util/HashMap;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInvoke(
+ 185,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Map;"),
+ options.itemFactory.createProto(options.itemFactory.createType("I")),
+ options.itemFactory.createString("size")),
+ true),
+ new CfInvoke(
+ 183,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/HashMap;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("V"), options.itemFactory.createType("I")),
+ options.itemFactory.createString("<init>")),
+ false),
+ new CfStore(ValueType.OBJECT, 1),
+ label1,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInvoke(
+ 185,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Map;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/util/Set;")),
+ options.itemFactory.createString("entrySet")),
+ true),
+ new CfInvoke(
+ 185,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Set;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/util/Iterator;")),
+ options.itemFactory.createString("iterator")),
+ true),
+ new CfStore(ValueType.OBJECT, 2),
+ label2,
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfInvoke(
+ 185,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Iterator;"),
+ options.itemFactory.createProto(options.itemFactory.createType("Z")),
+ options.itemFactory.createString("hasNext")),
+ true),
+ new CfIf(If.Type.EQ, ValueType.INT, label8),
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfInvoke(
+ 185,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Iterator;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("next")),
+ true),
+ new CfCheckCast(options.itemFactory.createType("Ljava/util/Map$Entry;")),
+ new CfStore(ValueType.OBJECT, 3),
+ label3,
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfLoad(ValueType.OBJECT, 3),
+ label4,
+ new CfInvoke(
+ 185,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Map$Entry;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("getKey")),
+ true),
+ new CfInvoke(
+ 184,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Objects;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/Object;"),
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("requireNonNull")),
+ false),
+ new CfLoad(ValueType.OBJECT, 3),
+ label5,
+ new CfInvoke(
+ 185,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Map$Entry;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("getValue")),
+ true),
+ new CfInvoke(
+ 184,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Objects;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/Object;"),
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("requireNonNull")),
+ false),
+ label6,
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/HashMap;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/Object;"),
+ options.itemFactory.createType("Ljava/lang/Object;"),
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("put")),
+ false),
+ new CfStackInstruction(CfStackInstruction.Opcode.Pop),
+ label7,
+ new CfGoto(label2),
+ label8,
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfInvoke(
+ 184,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Collections;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/util/Map;"),
+ options.itemFactory.createType("Ljava/util/Map;")),
+ options.itemFactory.createString("unmodifiableMap")),
+ false),
+ new CfReturn(ValueType.OBJECT),
+ label9),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
+ public static CfCode CollectionsMethods_copyOfSet(InternalOptions options, DexMethod method) {
+ CfLabel label0 = new CfLabel();
+ CfLabel label1 = new CfLabel();
+ CfLabel label2 = new CfLabel();
+ CfLabel label3 = new CfLabel();
+ CfLabel label4 = new CfLabel();
+ CfLabel label5 = new CfLabel();
+ CfLabel label6 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 3,
+ 4,
+ ImmutableList.of(
+ label0,
+ new CfNew(options.itemFactory.createType("Ljava/util/HashSet;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInvoke(
+ 185,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Collection;"),
+ options.itemFactory.createProto(options.itemFactory.createType("I")),
+ options.itemFactory.createString("size")),
+ true),
+ new CfInvoke(
+ 183,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/HashSet;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("V"), options.itemFactory.createType("I")),
+ options.itemFactory.createString("<init>")),
+ false),
+ new CfStore(ValueType.OBJECT, 1),
+ label1,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInvoke(
+ 185,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Collection;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/util/Iterator;")),
+ options.itemFactory.createString("iterator")),
+ true),
+ new CfStore(ValueType.OBJECT, 2),
+ label2,
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfInvoke(
+ 185,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Iterator;"),
+ options.itemFactory.createProto(options.itemFactory.createType("Z")),
+ options.itemFactory.createString("hasNext")),
+ true),
+ new CfIf(If.Type.EQ, ValueType.INT, label5),
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfInvoke(
+ 185,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Iterator;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("next")),
+ true),
+ new CfStore(ValueType.OBJECT, 3),
+ label3,
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfLoad(ValueType.OBJECT, 3),
+ new CfInvoke(
+ 184,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Objects;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/Object;"),
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("requireNonNull")),
+ false),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/HashSet;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Z"),
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("add")),
+ false),
+ new CfStackInstruction(CfStackInstruction.Opcode.Pop),
+ label4,
+ new CfGoto(label2),
+ label5,
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfInvoke(
+ 184,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Collections;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/util/Set;"),
+ options.itemFactory.createType("Ljava/util/Set;")),
+ options.itemFactory.createString("unmodifiableSet")),
+ false),
+ new CfReturn(ValueType.OBJECT),
+ label6),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
public static CfCode CollectionsMethods_emptyEnumeration(
InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
diff --git a/src/test/examplesJava10/backport/ListBackportJava10Main.java b/src/test/examplesJava10/backport/ListBackportJava10Main.java
new file mode 100644
index 0000000..376f67a
--- /dev/null
+++ b/src/test/examplesJava10/backport/ListBackportJava10Main.java
@@ -0,0 +1,67 @@
+// 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 backport;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class ListBackportJava10Main {
+
+ public static void main(String[] args) {
+ testCopyOf();
+ }
+
+ private static void testCopyOf() {
+ Object anObject0 = new Object();
+ Object anObject1 = new Object();
+ List<Object> original = Arrays.asList(anObject0, anObject1);
+ List<Object> copy = List.copyOf(original);
+ assertEquals(2, copy.size());
+ assertEquals(original, copy);
+ assertSame(anObject0, copy.get(0));
+ assertSame(anObject1, copy.get(1));
+ assertMutationNotAllowed(copy);
+
+ // Mutate the original backing collection and ensure it's not reflected in copy.
+ original.set(0, new Object());
+ assertSame(anObject0, copy.get(0));
+
+ try {
+ List.copyOf(null);
+ throw new AssertionError();
+ } catch (NullPointerException expected) {
+ }
+ try {
+ List.copyOf(Arrays.asList(1, null, 2));
+ throw new AssertionError();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ private static void assertMutationNotAllowed(List<Object> ofObject) {
+ try {
+ ofObject.add(new Object());
+ throw new AssertionError();
+ } catch (UnsupportedOperationException expected) {
+ }
+ try {
+ ofObject.set(0, new Object());
+ throw new AssertionError();
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
+
+ private static void assertSame(Object expected, Object actual) {
+ if (expected != actual) {
+ throw new AssertionError("Expected <" + expected + "> but was <" + actual + ">");
+ }
+ }
+
+ private static void assertEquals(Object expected, Object actual) {
+ if (expected != actual && !expected.equals(actual)) {
+ throw new AssertionError("Expected <" + expected + "> but was <" + actual + ">");
+ }
+ }
+}
diff --git a/src/test/examplesJava10/backport/MapBackportJava10Main.java b/src/test/examplesJava10/backport/MapBackportJava10Main.java
new file mode 100644
index 0000000..95413e9
--- /dev/null
+++ b/src/test/examplesJava10/backport/MapBackportJava10Main.java
@@ -0,0 +1,82 @@
+// 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 backport;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class MapBackportJava10Main {
+
+ public static void main(String[] args) {
+ testCopyOf();
+ }
+
+ private static void testCopyOf() {
+ Object key0 = new Object();
+ Object value0 = new Object();
+ Object key1 = new Object();
+ Object value1 = new Object();
+ Map<Object, Object> original = new HashMap<>();
+ original.put(key0, value0);
+ original.put(key1, value1);
+ Map<Object, Object> copy = Map.copyOf(original);
+ assertEquals(2, copy.size());
+ assertEquals(original, copy);
+ assertSame(value0, copy.get(key0));
+ assertSame(value1, copy.get(key1));
+ assertMutationNotAllowed(copy);
+
+ // Mutate the original backing collection and ensure it's not reflected in copy.
+ original.put(key0, new Object());
+ assertSame(value0, copy.get(key0));
+
+ try {
+ Map.copyOf(null);
+ throw new AssertionError();
+ } catch (NullPointerException expected) {
+ }
+ try {
+ Map<Object, Object> map = new HashMap<>();
+ map.put(null, new Object());
+ Map.copyOf(map);
+ throw new AssertionError();
+ } catch (NullPointerException expected) {
+ }
+ try {
+ Map<Object, Object> map = new HashMap<>();
+ map.put(new Object(), null);
+ Map.copyOf(map);
+ throw new AssertionError();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ private static void assertMutationNotAllowed(Map<Object, Object> ofObject) {
+ try {
+ ofObject.put(new Object(), new Object());
+ throw new AssertionError();
+ } catch (UnsupportedOperationException expected) {
+ }
+ for (Map.Entry<Object, Object> entry : ofObject.entrySet()) {
+ try {
+ entry.setValue(new Object());
+ throw new AssertionError();
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
+ }
+
+ private static void assertSame(Object expected, Object actual) {
+ if (expected != actual) {
+ throw new AssertionError("Expected <" + expected + "> but was <" + actual + ">");
+ }
+ }
+
+ private static void assertEquals(Object expected, Object actual) {
+ if (expected != actual && !expected.equals(actual)) {
+ throw new AssertionError("Expected <" + expected + "> but was <" + actual + ">");
+ }
+ }
+}
diff --git a/src/test/examplesJava10/backport/SetBackportJava10Main.java b/src/test/examplesJava10/backport/SetBackportJava10Main.java
new file mode 100644
index 0000000..b8fb356
--- /dev/null
+++ b/src/test/examplesJava10/backport/SetBackportJava10Main.java
@@ -0,0 +1,74 @@
+// 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 backport;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class SetBackportJava10Main {
+
+ public static void main(String[] args) {
+ testCopyOf();
+ }
+
+ private static void testCopyOf() {
+ Object anObject0 = new Object();
+ Object anObject1 = new Object();
+ List<Object> original = Arrays.asList(anObject0, anObject1);
+ Set<Object> copy = Set.copyOf(original);
+ assertEquals(2, copy.size());
+ assertEquals(new HashSet<>(original), copy);
+ assertTrue(copy.contains(anObject0));
+ assertTrue(copy.contains(anObject1));
+ assertMutationNotAllowed(copy);
+
+ // Mutate the original backing collection and ensure it's not reflected in copy.
+ Object newObject = new Object();
+ original.set(0, newObject);
+ assertFalse(copy.contains(newObject));
+
+ // Ensure duplicates are allowed and are de-duped.
+ assertEquals(Set.of(1, 2), Set.copyOf(List.of(1, 2, 1, 2)));
+
+ try {
+ Set.copyOf(null);
+ throw new AssertionError();
+ } catch (NullPointerException expected) {
+ }
+ try {
+ Set.copyOf(Arrays.asList(1, null, 2));
+ throw new AssertionError();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ private static void assertMutationNotAllowed(Set<Object> ofObject) {
+ try {
+ ofObject.add(new Object());
+ throw new AssertionError();
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
+
+ private static void assertTrue(boolean value) {
+ if (!value) {
+ throw new AssertionError("Expected <true> but was <false>");
+ }
+ }
+
+ private static void assertFalse(boolean value) {
+ if (value) {
+ throw new AssertionError("Expected <false> but was <true>");
+ }
+ }
+
+ private static void assertEquals(Object expected, Object actual) {
+ if (expected != actual && !expected.equals(actual)) {
+ throw new AssertionError("Expected <" + expected + "> but was <" + actual + ">");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/TestRuntime.java b/src/test/java/com/android/tools/r8/TestRuntime.java
index 6347853..99f9a71 100644
--- a/src/test/java/com/android/tools/r8/TestRuntime.java
+++ b/src/test/java/com/android/tools/r8/TestRuntime.java
@@ -25,7 +25,9 @@
public enum CfVm {
JDK8("jdk8", 52),
JDK9("jdk9", 53),
- JDK11("jdk11", 55);
+ JDK10("jdk10", 54),
+ JDK11("jdk11", 55),
+ ;
private final String name;
private final int classfileVersion;
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index c0a5ee0..87b2806 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -107,6 +107,7 @@
public static final String EXAMPLES_ANDROID_O_BUILD_DIR = TESTS_BUILD_DIR + "examplesAndroidO/";
public static final String EXAMPLES_ANDROID_P_BUILD_DIR = TESTS_BUILD_DIR + "examplesAndroidP/";
public static final String EXAMPLES_JAVA9_BUILD_DIR = TESTS_BUILD_DIR + "examplesJava9/";
+ public static final String EXAMPLES_JAVA10_BUILD_DIR = TESTS_BUILD_DIR + "examplesJava10/";
public static final String EXAMPLES_JAVA11_JAR_DIR = TESTS_BUILD_DIR + "examplesJava11/";
public static final String EXAMPLES_JAVA11_BUILD_DIR = BUILD_DIR + "classes/java/examplesJava11/";
public static final String EXAMPLES_PROTO_BUILD_DIR = TESTS_BUILD_DIR + "examplesProto/";
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportJava9Test.java
index 28eefe3..37aa606 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportJava9Test.java
@@ -31,6 +31,7 @@
public ByteBackportJava9Test(TestParameters parameters) {
super(parameters, Byte.class, TEST_JAR, "backport.ByteBackportJava9Main");
- // TODO Once shipped in an actual API level, migrate to ByteBackportTest
+ // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
+ // an actual API level, migrate these tests to ByteBackportTest.
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportJava11Test.java
index f1d51fc..88be606 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportJava11Test.java
@@ -31,6 +31,7 @@
public CharacterBackportJava11Test(TestParameters parameters) {
super(parameters, Short.class, TEST_JAR, "backport.CharacterBackportJava11Main");
- // TODO Once shipped in an actual API level, migrate to CharacterBackportTest
+ // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
+ // an actual API level, migrate these tests to CharacterBackportTest.
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava10Test.java
new file mode 100644
index 0000000..309b319
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava10Test.java
@@ -0,0 +1,44 @@
+// 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 java.util.List;
+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 class ListBackportJava10Test extends AbstractBackportTest {
+ @Parameters(name = "{0}")
+ public static Iterable<?> data() {
+ return getTestParameters()
+ .withCfRuntimesStartingFromIncluding(CfVm.JDK10)
+ .withDexRuntimes()
+ .withAllApiLevels()
+ .build();
+ }
+
+ private static final Path TEST_JAR =
+ Paths.get(ToolHelper.EXAMPLES_JAVA10_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
+
+ public ListBackportJava10Test(TestParameters parameters) {
+ super(parameters, List.class, TEST_JAR, "backport.ListBackportJava10Main");
+ // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
+ // an actual API level, migrate these tests to ListBackportTest.
+
+ // Available since API 1 and used to test created lists.
+ ignoreInvokes("add");
+ ignoreInvokes("get");
+ ignoreInvokes("set");
+ ignoreInvokes("size");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava9Test.java
index a861d1f..03843cc0 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava9Test.java
@@ -32,7 +32,8 @@
public ListBackportJava9Test(TestParameters parameters) {
super(parameters, List.class, TEST_JAR, "backport.ListBackportJava9Main");
- // TODO Once shipped in an actual API level, migrate to ListBackportTest
+ // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
+ // an actual API level, migrate these tests to ListBackportTest.
// Available since API 1 and used to test created lists.
ignoreInvokes("add");
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava10Test.java
new file mode 100644
index 0000000..e54d7d8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava10Test.java
@@ -0,0 +1,44 @@
+// 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 java.util.Map;
+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 class MapBackportJava10Test extends AbstractBackportTest {
+ @Parameters(name = "{0}")
+ public static Iterable<?> data() {
+ return getTestParameters()
+ .withCfRuntimesStartingFromIncluding(CfVm.JDK10)
+ .withDexRuntimes()
+ .withAllApiLevels()
+ .build();
+ }
+
+ private static final Path TEST_JAR =
+ Paths.get(ToolHelper.EXAMPLES_JAVA10_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
+
+ public MapBackportJava10Test(TestParameters parameters) {
+ super(parameters, Map.class, TEST_JAR, "backport.MapBackportJava10Main");
+ // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
+ // an actual API level, migrate these tests to MapBackportTest.
+
+ // Available since API 1 and used to test created maps.
+ ignoreInvokes("entrySet");
+ ignoreInvokes("get");
+ ignoreInvokes("put");
+ ignoreInvokes("size");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava9Test.java
index 06e41f8..fb39b49 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava9Test.java
@@ -32,7 +32,8 @@
public MapBackportJava9Test(TestParameters parameters) {
super(parameters, Map.class, TEST_JAR, "backport.MapBackportJava9Main");
- // TODO Once shipped in an actual API level, migrate to MapBackportTest
+ // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
+ // an actual API level, migrate these tests to MapBackportTest.
// Available since API 1 and used to test created maps.
ignoreInvokes("entrySet");
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava9Test.java
index 439ef38..387697e 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava9Test.java
@@ -31,6 +31,7 @@
public MathBackportJava9Test(TestParameters parameters) {
super(parameters, Math.class, TEST_JAR, "backport.MathBackportJava9Main");
- // TODO Once shipped in an actual API level, migrate to MathBackportTest
+ // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
+ // an actual API level, migrate these tests to MathBackportTest.
}
}
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
index 7544e87..ef8b990 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java
@@ -31,6 +31,7 @@
public ObjectsBackportJava9Test(TestParameters parameters) {
super(parameters, Short.class, TEST_JAR, "backport.ObjectsBackportJava9Main");
- // TODO Once shipped in an actual API level, migrate to ObjectsBackportTest
+ // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
+ // an actual API level, migrate these tests to ObjectsBackportTest.
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java
index d77052a..0429fe5 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java
@@ -33,5 +33,7 @@
public OptionalBackportJava9Test(TestParameters parameters) {
super(parameters, Short.class, TEST_JAR, "backport.OptionalBackportJava9Main");
+ // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
+ // an actual API level, migrate these tests to OptionalBackportTest.
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava10Test.java
new file mode 100644
index 0000000..5c08b8f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava10Test.java
@@ -0,0 +1,43 @@
+// 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 java.util.Set;
+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 class SetBackportJava10Test extends AbstractBackportTest {
+ @Parameters(name = "{0}")
+ public static Iterable<?> data() {
+ return getTestParameters()
+ .withCfRuntimesStartingFromIncluding(CfVm.JDK10)
+ .withDexRuntimes()
+ .withAllApiLevels()
+ .build();
+ }
+
+ private static final Path TEST_JAR =
+ Paths.get(ToolHelper.EXAMPLES_JAVA10_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
+
+ public SetBackportJava10Test(TestParameters parameters) {
+ super(parameters, Set.class, TEST_JAR, "backport.SetBackportJava10Main");
+ // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
+ // an actual API level, migrate these tests to SetBackportTest.
+
+ // Available since API 1 and used to test created sets.
+ ignoreInvokes("add");
+ ignoreInvokes("contains");
+ ignoreInvokes("size");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava9Test.java
index b248e0b..6947ff2 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava9Test.java
@@ -32,7 +32,8 @@
public SetBackportJava9Test(TestParameters parameters) {
super(parameters, Set.class, TEST_JAR, "backport.SetBackportJava9Main");
- // TODO Once shipped in an actual API level, migrate to SetBackportTest
+ // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
+ // an actual API level, migrate these tests to SetBackportTest.
// Available since API 1 and used to test created sets.
ignoreInvokes("add");
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportJava9Test.java
index d56ef5b..beffd8a 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportJava9Test.java
@@ -31,6 +31,7 @@
public ShortBackportJava9Test(TestParameters parameters) {
super(parameters, Short.class, TEST_JAR, "backport.ShortBackportJava9Main");
- // TODO Once shipped in an actual API level, migrate to ShortBackportTest
+ // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
+ // an actual API level, migrate these tests to ShortBackportTest.
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StreamBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/StreamBackportJava9Test.java
index 0f08ff9..073c179 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/StreamBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StreamBackportJava9Test.java
@@ -34,6 +34,9 @@
public StreamBackportJava9Test(TestParameters parameters) {
super(parameters, Stream.class, TEST_JAR, "backport.StreamBackportJava9Main");
+ // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
+ // an actual API level, migrate these tests to StreamBackportTest.
+
// Available since N as part of library desugaring.
ignoreInvokes("of");
ignoreInvokes("empty");
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava9Test.java
index 43cbf48..2f124e7 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava9Test.java
@@ -31,6 +31,7 @@
public StrictMathBackportJava9Test(TestParameters parameters) {
super(parameters, Math.class, TEST_JAR, "backport.StrictMathBackportJava9Main");
- // TODO Once shipped in an actual API level, migrate to MathBackportTest
+ // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
+ // an actual API level, migrate these tests to StrictMathBackportTest.
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/CollectionsMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/CollectionsMethods.java
index dd300da..eac9b74 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/CollectionsMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/CollectionsMethods.java
@@ -4,10 +4,18 @@
package com.android.tools.r8.ir.desugar.backports;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
import java.util.ListIterator;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
public final class CollectionsMethods {
@@ -22,4 +30,30 @@
public static <T> ListIterator<T> emptyListIterator() {
return Collections.<T>emptyList().listIterator();
}
+
+ public static <T> List<T> copyOfList(Collection<? extends T> other) {
+ ArrayList<T> list = new ArrayList<>(other.size());
+ for (T item : other) {
+ list.add(Objects.requireNonNull(item));
+ }
+ return Collections.unmodifiableList(list);
+ }
+
+ public static <T> Set<T> copyOfSet(Collection<? extends T> other) {
+ HashSet<T> set = new HashSet<>(other.size());
+ for (T item : other) {
+ set.add(Objects.requireNonNull(item));
+ }
+ return Collections.unmodifiableSet(set);
+ }
+
+ public static <K, V> Map<K, V> copyOfMap(Map<? extends K, ? extends V> other) {
+ HashMap<K, V> map = new HashMap<>(other.size());
+ for (Map.Entry<? extends K, ? extends V> entry : other.entrySet()) {
+ map.put(
+ Objects.requireNonNull(entry.getKey()),
+ Objects.requireNonNull(entry.getValue()));
+ }
+ return Collections.unmodifiableMap(map);
+ }
}