Merge commit 'a882c7b8db707b0e1b0fc86047976a3f5b46dd82' into dev-release
diff --git a/build.gradle b/build.gradle
index d3005a8..0812f9b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -329,6 +329,7 @@
"android_jar/lib-v29",
"android_jar/lib-v30",
"android_jar/lib-v31",
+ "android_jar/lib-v32",
"android_jar/api-versions",
"android_jar/api-database",
"api-outlining/simple-app-dump",
diff --git a/scripts/add-android-jar.sh b/scripts/add-android-jar.sh
new file mode 100755
index 0000000..3eb0d21
--- /dev/null
+++ b/scripts/add-android-jar.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+#
+# Copyright (c) 2021, 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.
+
+set -e
+set -x
+
+echo "Update this script manually before using"
+exit -1
+
+# Download Platform SDK in @SDK_HOME
+SDK_HOME=$HOME/Android/Sdk
+
+# Modify these to match the SDK android.jar to add.
+SDK_DIR_NAME=android-Sv2
+SDK_VERSION=32
+
+SDK_DIR=$SDK_HOME/platforms/$SDK_DIR_NAME
+THIRD_PARTY_ANDROID_JAR=third_party/android_jar
+THIRD_PARTY_ANDROID_JAR_LIB=$THIRD_PARTY_ANDROID_JAR/lib-v$SDK_VERSION
+
+rm -rf $THIRD_PARTY_ANDROID_JAR_LIB
+rm -f ${THIRD_PARTY_ANDROID_JAR_LIB}.tar.gz
+rm -f ${THIRD_PARTY_ANDROID_JAR_LIB}.tar.sha1
+
+mkdir -p $THIRD_PARTY_ANDROID_JAR_LIB/optional
+cp $SDK_DIR/android.jar $THIRD_PARTY_ANDROID_JAR_LIB/android.jar
+cp $SDK_DIR/optional/*.jar $THIRD_PARTY_ANDROID_JAR_LIB/optional
+cp $SDK_DIR/optional/optional.json $THIRD_PARTY_ANDROID_JAR_LIB/optional
+cp $THIRD_PARTY_ANDROID_JAR/lib-v31/README.google $THIRD_PARTY_ANDROID_JAR_LIB
+vi $THIRD_PARTY_ANDROID_JAR_LIB/README.google
+
+(cd $THIRD_PARTY_ANDROID_JAR \
+ && upload_to_google_storage.py -a --bucket r8-deps lib-v$SDK_VERSION)
+rm -rf $THIRD_PARTY_ANDROID_JAR_LIB
+rm ${THIRD_PARTY_ANDROID_JAR_LIB}.tar.gz
+git add ${THIRD_PARTY_ANDROID_JAR_LIB}.tar.gz.sha1
+
+echo "Update build.gradle with this new cloud dependency, and verify with tools/gradle.py downloadDeps"
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/r8/graph/ImmediateProgramSubtypingInfo.java b/src/main/java/com/android/tools/r8/graph/ImmediateProgramSubtypingInfo.java
index e57f798..cec5083 100644
--- a/src/main/java/com/android/tools/r8/graph/ImmediateProgramSubtypingInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/ImmediateProgramSubtypingInfo.java
@@ -45,6 +45,13 @@
return new ImmediateProgramSubtypingInfo(appView, immediateSubtypes);
}
+ public void forEachImmediateSuperClass(DexClass clazz, Consumer<? super DexClass> consumer) {
+ forEachImmediateSuperClassMatching(
+ clazz,
+ (supertype, superclass) -> superclass != null,
+ (supertype, superclass) -> consumer.accept(superclass));
+ }
+
public void forEachImmediateSuperClass(
DexClass clazz, BiConsumer<? super DexType, ? super DexClass> consumer) {
forEachImmediateSuperClassMatching(clazz, (supertype, superclass) -> true, consumer);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoWeakerAccessPrivileges.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoWeakerAccessPrivileges.java
index 399e807..600b5a8 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoWeakerAccessPrivileges.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoWeakerAccessPrivileges.java
@@ -31,6 +31,8 @@
new IdentityHashMap<>();
private final Map<Set<DexProgramClass>, DexMethodSignatureSet>
nonPublicVirtualMethodSignaturesCache = new IdentityHashMap<>();
+ private final Map<DexClass, DexMethodSignatureSet> nonPublicVirtualLibraryMethodSignaturesCache =
+ new IdentityHashMap<>();
private final Map<DexProgramClass, Set<DexProgramClass>> stronglyConnectedComponentsCache =
new IdentityHashMap<>();
@@ -152,12 +154,37 @@
clazz.forEachProgramVirtualMethodMatching(
method -> method.getAccessFlags().isPackagePrivateOrProtected(),
nonPublicVirtualMethodSignatures::add);
+ immediateSubtypingInfo.forEachImmediateSuperClassMatching(
+ clazz,
+ superclass -> !superclass.isProgramClass(),
+ superclass ->
+ nonPublicVirtualMethodSignatures.addAll(
+ getOrComputeNonPublicVirtualLibraryMethodSignatures(superclass)));
}
nonPublicVirtualMethodSignaturesCache.put(
stronglyConnectedComponent, nonPublicVirtualMethodSignatures);
return nonPublicVirtualMethodSignatures;
}
+ private DexMethodSignatureSet getOrComputeNonPublicVirtualLibraryMethodSignatures(
+ DexClass clazz) {
+ if (nonPublicVirtualLibraryMethodSignaturesCache.containsKey(clazz)) {
+ return nonPublicVirtualLibraryMethodSignaturesCache.get(clazz);
+ }
+ DexMethodSignatureSet nonPublicVirtualLibraryMethodSignatures = DexMethodSignatureSet.create();
+ clazz.forEachClassMethodMatching(
+ method -> method.getAccessFlags().isPackagePrivateOrProtected(),
+ nonPublicVirtualLibraryMethodSignatures::add);
+ immediateSubtypingInfo.forEachImmediateSuperClass(
+ clazz,
+ superclass ->
+ nonPublicVirtualLibraryMethodSignatures.addAll(
+ getOrComputeNonPublicVirtualLibraryMethodSignatures(superclass)));
+ nonPublicVirtualLibraryMethodSignaturesCache.put(
+ clazz, nonPublicVirtualLibraryMethodSignatures);
+ return nonPublicVirtualLibraryMethodSignatures;
+ }
+
@Override
public void clear() {
inheritedInterfaceMethodsCache.clear();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
index 9b549d5..3bca6db 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
@@ -1242,7 +1242,9 @@
int maxOffset = getMaxOffset();
int maxTargetOffset = targetInfo.getMaxOffset();
if (maxTargetOffset < maxOffset) {
- return getOffset() - targetInfo.getOffset() < Short.MIN_VALUE;
+ int relativeJumpOffset = targetInfo.getOffset() - getOffset();
+ assert relativeJumpOffset < 0;
+ return relativeJumpOffset < Short.MIN_VALUE;
}
// Forward branch: over estimate the distance, but take into account the sizes
// of instructions generated so far. That way the over estimation is only for the
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 5b0ff74..494cf92 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -3264,10 +3264,10 @@
EnqueuerEvent preconditionEvent = UnconditionalKeepInfoEvent.get();
KeepFieldInfo.Joiner minimumKeepInfoForField =
dependentMinimumKeepInfo.remove(preconditionEvent, field.getReference());
- if (minimumKeepInfoForField != null) {
- keepInfo.joinField(field, info -> info.merge(minimumKeepInfoForField));
- enqueueFieldIfShrinkingIsDisallowed(field, preconditionEvent, minimumKeepInfoForField);
- }
+ if (minimumKeepInfoForField != null) {
+ keepInfo.joinField(field, info -> info.merge(minimumKeepInfoForField));
+ enqueueFieldIfShrinkingIsDisallowed(field, preconditionEvent, minimumKeepInfoForField);
+ }
}
private void applyMinimumKeepInfoWhenLive(
@@ -3309,10 +3309,10 @@
EnqueuerEvent preconditionEvent = UnconditionalKeepInfoEvent.get();
KeepMethodInfo.Joiner minimumKeepInfoForMethod =
dependentMinimumKeepInfo.remove(preconditionEvent, method.getReference());
- if (minimumKeepInfoForMethod != null) {
- keepInfo.joinMethod(method, info -> info.merge(minimumKeepInfoForMethod));
- enqueueMethodIfShrinkingIsDisallowed(method, preconditionEvent, minimumKeepInfoForMethod);
- }
+ if (minimumKeepInfoForMethod != null) {
+ keepInfo.joinMethod(method, info -> info.merge(minimumKeepInfoForMethod));
+ enqueueMethodIfShrinkingIsDisallowed(method, preconditionEvent, minimumKeepInfoForMethod);
+ }
}
private void applyMinimumKeepInfoWhenLiveOrTargeted(
@@ -4613,6 +4613,7 @@
return;
}
+ WorkList<DexProgramClass> worklist = WorkList.newIdentityWorkList();
for (Instruction user : interfacesValue.uniqueUsers()) {
if (!user.isArrayPut()) {
continue;
@@ -4624,23 +4625,41 @@
continue;
}
- DexProgramClass clazz = getProgramClassOrNullFromReflectiveAccess(type, method);
- if (clazz != null && clazz.isInterface()) {
- // Add this interface to the set of pinned items to ensure that we do not merge the
- // interface into its unique subtype, if any.
- // TODO(b/145344105): This should be superseded by the unknown interface hierarchy.
- keepInfo.joinClass(clazz, joiner -> joiner.disallowOptimization().disallowShrinking());
- KeepReason reason = KeepReason.reflectiveUseIn(method);
- markInterfaceAsInstantiated(clazz, graphReporter.registerClass(clazz, reason));
+ DexProgramClass clazz = asProgramClassOrNull(definitionFor(type, method));
+ if (clazz != null) {
+ worklist.addIfNotSeen(clazz);
+ }
+ }
- // Also pin all of its virtual methods to ensure that the devirtualizer does not perform
- // illegal rewritings of invoke-interface instructions into invoke-virtual instructions.
- clazz.forEachProgramVirtualMethod(
- virtualMethod -> {
- keepInfo.joinMethod(
- virtualMethod, joiner -> joiner.disallowOptimization().disallowShrinking());
- markVirtualMethodAsReachable(virtualMethod.getReference(), true, method, reason);
- });
+ while (worklist.hasNext()) {
+ DexProgramClass clazz = worklist.next();
+ if (!clazz.isInterface()) {
+ continue;
+ }
+
+ // Add this interface to the set of pinned items to ensure that we do not merge the
+ // interface into its unique subtype, if any.
+ // TODO(b/145344105): This should be superseded by the unknown interface hierarchy.
+ keepInfo.joinClass(clazz, joiner -> joiner.disallowOptimization().disallowShrinking());
+ KeepReason reason = KeepReason.reflectiveUseIn(method);
+ markInterfaceAsInstantiated(clazz, graphReporter.registerClass(clazz, reason));
+
+ // Also pin all of its virtual methods to ensure that the devirtualizer does not perform
+ // illegal rewritings of invoke-interface instructions into invoke-virtual instructions.
+ clazz.forEachProgramVirtualMethod(
+ virtualMethod -> {
+ keepInfo.joinMethod(
+ virtualMethod, joiner -> joiner.disallowOptimization().disallowShrinking());
+ markVirtualMethodAsReachable(virtualMethod.getReference(), true, method, reason);
+ });
+
+ // Repeat for all super interfaces.
+ for (DexType implementedType : clazz.getInterfaces()) {
+ DexProgramClass implementedClass =
+ asProgramClassOrNull(definitionFor(implementedType, clazz));
+ if (implementedClass != null) {
+ worklist.addIfNotSeen(implementedClass);
+ }
}
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
index c2f78cf..e1a79db 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
@@ -42,12 +42,14 @@
Q(29),
R(30),
S(31),
+ Sv2(32),
UNKNOWN(10000),
NOT_SET(10001);
// When updating LATEST and a new version goes stable, add a new api-versions.xml to third_party
// and update the version and generated jar in AndroidApiDatabaseBuilderGeneratorTest.
- public static final AndroidApiLevel LATEST = S;
+ // TODO(b/204738868): Update API database for Sv2.
+ public static final AndroidApiLevel LATEST = Sv2;
public static final int magicApiLevelUsedByAndroidPlatformBuild = 10000;
@@ -169,9 +171,11 @@
return R;
case 31:
return S;
+ case 32:
+ return Sv2;
default:
// This has to be updated when we add new api levels.
- assert S == LATEST;
+ assert Sv2 == LATEST;
return UNKNOWN;
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/DexVersion.java b/src/main/java/com/android/tools/r8/utils/DexVersion.java
index 65ac80a..24d374e 100644
--- a/src/main/java/com/android/tools/r8/utils/DexVersion.java
+++ b/src/main/java/com/android/tools/r8/utils/DexVersion.java
@@ -41,6 +41,7 @@
// UNKNOWN is an unknown higher api version we therefore choose the highest known
// version.
case UNKNOWN:
+ case Sv2:
case S:
case R:
case Q:
diff --git a/src/test/java/com/android/tools/r8/BackportedMethodListTest.java b/src/test/java/com/android/tools/r8/BackportedMethodListTest.java
index 4ffaaa7..706126c 100644
--- a/src/test/java/com/android/tools/r8/BackportedMethodListTest.java
+++ b/src/test/java/com/android/tools/r8/BackportedMethodListTest.java
@@ -79,10 +79,18 @@
mode == Mode.LIBRARY_DESUGAR || apiLevel >= AndroidApiLevel.N.getLevel(),
backports.contains("java/util/OptionalLong#isEmpty()Z"));
- // Java 9, 10 and 11 methods.
- assertTrue(backports.contains("java/lang/StrictMath#multiplyExact(JI)J"));
- assertTrue(backports.contains("java/util/List#copyOf(Ljava/util/Collection;)Ljava/util/List;"));
+ // Java 9, 10 and 11 methods added at API level S.
+ assertEquals(
+ apiLevel < AndroidApiLevel.S.getLevel(),
+ backports.contains("java/lang/StrictMath#multiplyExact(JI)J"));
+ assertEquals(
+ apiLevel < AndroidApiLevel.S.getLevel(),
+ backports.contains("java/util/List#copyOf(Ljava/util/Collection;)Ljava/util/List;"));
+
+ // Java 9, 10 and 11 methods not yet added.
+ assertTrue(backports.contains("java/lang/Integer#parseInt(Ljava/lang/CharSequence;III)I"));
assertTrue(backports.contains("java/lang/Character#toString(I)Ljava/lang/String;"));
+ assertTrue(backports.contains("java/lang/String#repeat(I)Ljava/lang/String;"));
}
private void addLibraryDesugaring(BackportedMethodListCommand.Builder builder) {
diff --git a/src/test/java/com/android/tools/r8/androidapi/AndroidJarSv2Test.java b/src/test/java/com/android/tools/r8/androidapi/AndroidJarSv2Test.java
new file mode 100644
index 0000000..ca29ab5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/androidapi/AndroidJarSv2Test.java
@@ -0,0 +1,58 @@
+// Copyright (c) 2021, 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.androidapi;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class AndroidJarSv2Test extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
+ }
+
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("Hello, world!");
+
+ @Test
+ public void testD8() throws Exception {
+ testForD8(parameters.getBackend())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.Sv2))
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.Sv2))
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/LibraryMemberRebindingInterfaceTest.java b/src/test/java/com/android/tools/r8/memberrebinding/LibraryMemberRebindingInterfaceTest.java
new file mode 100644
index 0000000..7df0c10
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/memberrebinding/LibraryMemberRebindingInterfaceTest.java
@@ -0,0 +1,133 @@
+// Copyright (c) 2021, 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.memberrebinding;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class LibraryMemberRebindingInterfaceTest extends TestBase {
+
+ private static Path oldRuntimeJar;
+ private static Path newRuntimeJar;
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return TestBase.getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @BeforeClass
+ public static void compileCustomLib() throws Exception {
+ oldRuntimeJar =
+ createJar(
+ ImmutableList.of(LibraryI.class, LibraryB.class, LibraryC.class),
+ getProgramClassFileDataWithoutMethods(LibraryA.class));
+ newRuntimeJar =
+ createJar(ImmutableList.of(LibraryI.class, LibraryA.class, LibraryB.class, LibraryC.class));
+ }
+
+ // Tests old android.jar with old runtime.
+ @Test
+ public void testEmptyAAtCompileTimeAndRuntime() throws Exception {
+ test(oldRuntimeJar, oldRuntimeJar);
+ }
+
+ // Tests new android.jar with new runtime.
+ @Test
+ public void testNonEmptyAAtCompileTimeAndRuntime() throws Exception {
+ test(newRuntimeJar, newRuntimeJar);
+ }
+
+ // Tests old android.jar with new runtime.
+ @Test
+ public void testEmptyAAtCompileTime() throws Exception {
+ test(oldRuntimeJar, newRuntimeJar);
+ }
+
+ // Tests new android.jar with old runtime.
+ @Test
+ public void testEmptyAAtRuntimeTime() throws Exception {
+ test(newRuntimeJar, oldRuntimeJar);
+ }
+
+ private void test(Path compileTimeLibrary, Path runtimeLibrary) throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class)
+ .addKeepClassAndMembersRules(TestClass.class)
+ .addLibraryFiles(compileTimeLibrary)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addRunClasspathFiles(buildOnDexRuntime(parameters, runtimeLibrary))
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("42");
+ }
+
+ private static Path createJar(Collection<Class<?>> programClasses, byte[]... programClassFileData)
+ throws Exception {
+ return testForR8(getStaticTemp(), Backend.CF)
+ .addProgramClasses(programClasses)
+ .addProgramClassFileData(programClassFileData)
+ .addKeepAllClassesRule()
+ .compile()
+ .writeToZip();
+ }
+
+ private static byte[] getProgramClassFileDataWithoutMethods(Class<?> clazz) throws IOException {
+ return transformer(clazz)
+ .removeMethods(
+ (int access, String name, String descriptor, String signature, String[] exceptions) ->
+ !name.equals("<init>"))
+ .transform();
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ test(new LibraryC());
+ }
+
+ static void test(LibraryB b) {
+ System.out.println(b.m());
+ }
+ }
+
+ interface LibraryI {
+
+ int m();
+ }
+
+ static class LibraryA {
+
+ // Added in API level X, so we can't rebind to this in APIs < X.
+ public int m() {
+ return 42;
+ }
+ }
+
+ abstract static class LibraryB extends LibraryA implements LibraryI {}
+
+ static class LibraryC extends LibraryB {
+
+ @Override
+ public int m() {
+ return 42;
+ }
+ }
+}
diff --git a/third_party/android_jar/lib-v32.tar.gz.sha1 b/third_party/android_jar/lib-v32.tar.gz.sha1
new file mode 100644
index 0000000..1d92e7e
--- /dev/null
+++ b/third_party/android_jar/lib-v32.tar.gz.sha1
@@ -0,0 +1 @@
+9b07cb997fcedd7314bb9cf35b9ddacbe817c51c
\ No newline at end of file
diff --git a/third_party/gmscore/latest.tar.gz.sha1 b/third_party/gmscore/latest.tar.gz.sha1
index 48535ae..47a77db 100644
--- a/third_party/gmscore/latest.tar.gz.sha1
+++ b/third_party/gmscore/latest.tar.gz.sha1
@@ -1 +1 @@
-1721de4354b943dcaf629932ca204bfc73551a7a
\ No newline at end of file
+218810e079a80c7810c3bc5c68a4448c076eddfd
\ No newline at end of file