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