Add rewriting of PermittedSubclasses
Bug: b/227160052
Change-Id: I3ee4a058b728020ffe72ace2890b926656644585
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index cc8e00f..a87febd 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -1043,6 +1043,10 @@
permittedSubclasses.clear();
}
+ public void removePermittedSubclassAttribute(Predicate<PermittedSubclassAttribute> predicate) {
+ permittedSubclasses.removeIf(predicate);
+ }
+
public boolean isLocalClass() {
InnerClassAttribute innerClass = getInnerClassAttributeForThisClass();
// The corresponding enclosing-method attribute might be not available, e.g., CF version 50.
@@ -1139,6 +1143,10 @@
return permittedSubclasses;
}
+ public void setPermittedSubclassAttributes(List<PermittedSubclassAttribute> permittedSubclasses) {
+ this.permittedSubclasses = permittedSubclasses;
+ }
+
public List<RecordComponentInfo> getRecordComponents() {
return recordComponents;
}
diff --git a/src/main/java/com/android/tools/r8/graph/fixup/TreeFixerBase.java b/src/main/java/com/android/tools/r8/graph/fixup/TreeFixerBase.java
index a84af1a..035ab26 100644
--- a/src/main/java/com/android/tools/r8/graph/fixup/TreeFixerBase.java
+++ b/src/main/java/com/android/tools/r8/graph/fixup/TreeFixerBase.java
@@ -28,6 +28,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.IdentityHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
@@ -297,15 +298,23 @@
return permittedSubclassAttributes;
}
boolean changed = false;
- List<PermittedSubclassAttribute> newPermittedSubclassAttributes =
- new ArrayList<>(permittedSubclassAttributes.size());
+ LinkedHashSet<DexType> newPermittedSubclassTypes =
+ new LinkedHashSet<>(permittedSubclassAttributes.size());
for (PermittedSubclassAttribute permittedSubclassAttribute : permittedSubclassAttributes) {
- DexType permittedSubclassType = permittedSubclassAttribute.getPermittedSubclass();
- DexType newPermittedSubclassType = fixupType(permittedSubclassType);
- newPermittedSubclassAttributes.add(new PermittedSubclassAttribute(newPermittedSubclassType));
- changed |= newPermittedSubclassType != permittedSubclassType;
+ DexType permittedSubClassType = permittedSubclassAttribute.getPermittedSubclass();
+ DexType newPermittedSubClassType = fixupType(permittedSubClassType);
+ newPermittedSubclassTypes.add(newPermittedSubClassType);
+ changed |= newPermittedSubClassType != permittedSubClassType;
}
- return changed ? newPermittedSubclassAttributes : permittedSubclassAttributes;
+ if (!changed) {
+ return permittedSubclassAttributes;
+ }
+ List<PermittedSubclassAttribute> newPermittedSubclassAttributes =
+ new ArrayList<>(newPermittedSubclassTypes.size());
+ for (DexType newPermittedSubclassType : newPermittedSubclassTypes) {
+ newPermittedSubclassAttributes.add(new PermittedSubclassAttribute(newPermittedSubclassType));
+ }
+ return newPermittedSubclassAttributes;
}
protected List<RecordComponentInfo> fixupRecordComponents(
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
index 804b505..ee56cc7 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -157,6 +157,8 @@
clazz.setInnerClasses(fixupInnerClassAttributes(clazz.getInnerClasses()));
clazz.setNestHostAttribute(fixupNestHost(clazz.getNestHostClassAttribute()));
clazz.setNestMemberAttributes(fixupNestMemberAttributes(clazz.getNestMembersClassAttributes()));
+ clazz.setPermittedSubclassAttributes(
+ fixupPermittedSubclassAttribute(clazz.getPermittedSubclassAttributes()));
}
private void fixupProgramClassSuperTypes(DexProgramClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 3688415..ec15263 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.graph.EnclosingMethodAttribute;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.NestMemberClassAttribute;
+import com.android.tools.r8.graph.PermittedSubclassAttribute;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.RecordComponentInfo;
@@ -226,6 +227,7 @@
PredicateUtils.not(isReachableInstanceField(reachableInstanceFields)));
}
}
+ clazz.removePermittedSubclassAttribute(this::isAttributeReferencingPrunedType);
unusedItemsPrinter.visited();
assert verifyNoDeadFields(clazz);
}
@@ -319,6 +321,10 @@
return context == null || isTypeMissing(context) || !isTypeLive(context);
}
+ private boolean isAttributeReferencingPrunedType(PermittedSubclassAttribute attr) {
+ return !isTypeLive(attr.getPermittedSubclass());
+ }
+
private <D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> int firstUnreachableIndex(
List<D> items, Predicate<D> live) {
for (int i = 0; i < items.size(); i++) {
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesEnumJdk17CompiledTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesEnumJdk17CompiledTest.java
index 07d4ef7..e00e194 100644
--- a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesEnumJdk17CompiledTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesEnumJdk17CompiledTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.desugar.sealed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static junit.framework.Assert.assertEquals;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assume.assumeTrue;
@@ -18,7 +19,7 @@
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.Matchers;
+import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -70,14 +71,15 @@
}
private void inspect(CodeInspector inspector) {
- ClassSubject clazz = inspector.clazz("enum_sealed.Enum");
- assertThat(clazz, Matchers.isPresentAndRenamed());
- if (!parameters.isCfRuntime()) {
- return;
- }
+ ClassSubject clazz = inspector.clazz(EnumSealed.Enum.typeName());
+ assertThat(clazz, isPresentAndRenamed());
+ ClassSubject sub1 = inspector.clazz(EnumSealed.EnumB.typeName());
+ assertThat(sub1, isPresentAndRenamed());
assertEquals(
- keepPermittedSubclassesAttribute ? 1 : 0,
- clazz.getFinalPermittedSubclassAttributes().size());
+ parameters.isCfRuntime() && keepPermittedSubclassesAttribute
+ ? ImmutableList.of(sub1.asTypeSubject())
+ : ImmutableList.of(),
+ clazz.getFinalPermittedSubclassAttributes());
}
@Test
@@ -90,6 +92,7 @@
keepPermittedSubclassesAttribute,
TestShrinkerBuilder::addKeepAttributePermittedSubclasses)
.addKeepMainRule(EnumSealed.Main.typeName())
+ .addKeepClassRulesWithAllowObfuscation(EnumSealed.Enum.typeName())
.compile()
.inspect(this::inspect)
.run(parameters.getRuntime(), EnumSealed.Main.typeName())
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesIllegalSubclassTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesIllegalSubclassTest.java
index 87baa3f..240b7a9 100644
--- a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesIllegalSubclassTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesIllegalSubclassTest.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.desugar.sealed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static junit.framework.Assert.assertEquals;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -20,7 +22,7 @@
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.Matchers;
+import com.google.common.collect.ImmutableList;
import java.util.List;
import org.hamcrest.Matcher;
import org.junit.Test;
@@ -37,6 +39,7 @@
@Parameter(1)
public boolean keepPermittedSubclassesAttribute;
+
static final Matcher<String> EXPECTED = containsString("cannot inherit from sealed class");
static final String EXPECTED_WITHOUT_PERMITTED_SUBCLASSES_ATTRIBUTE =
StringUtils.lines("Success!");
@@ -80,14 +83,19 @@
}
private void inspect(CodeInspector inspector) {
- ClassSubject clazz = inspector.clazz(C.class);
- assertThat(clazz, Matchers.isPresentAndRenamed());
- if (!parameters.isCfRuntime()) {
- return;
- }
+ ClassSubject clazz = inspector.clazz(Super.class);
+ assertThat(clazz, isPresentAndRenamed());
+ ClassSubject sub1 = inspector.clazz(Sub1.class);
+ ClassSubject sub2 = inspector.clazz(Sub2.class);
+ ClassSubject sub3 = inspector.clazz(Sub3.class);
+ assertThat(sub1, isPresentAndNotRenamed());
+ assertThat(sub2, isPresentAndNotRenamed());
+ assertThat(sub3, isPresentAndNotRenamed());
assertEquals(
- keepPermittedSubclassesAttribute ? 2 : 0,
- clazz.getFinalPermittedSubclassAttributes().size());
+ parameters.isCfRuntime() && keepPermittedSubclassesAttribute
+ ? ImmutableList.of(sub1.asTypeSubject(), sub2.asTypeSubject())
+ : ImmutableList.of(),
+ clazz.getFinalPermittedSubclassAttributes());
}
@Test
@@ -100,10 +108,8 @@
.applyIf(
keepPermittedSubclassesAttribute,
TestShrinkerBuilder::addKeepAttributePermittedSubclasses)
- // Keep the sealed class to ensure the PermittedSubclasses attribute stays live.
- .addKeepPermittedSubclasses(C.class)
- // Keep subclasses as the PermittedSubclasses attribute is not rewritten.
- .addKeepRules("-keep class * extends " + C.class.getTypeName())
+ .addKeepPermittedSubclasses(Super.class)
+ .addKeepRules("-keep class * extends " + Super.class.getTypeName())
.addKeepMainRule(TestClass.class)
.compile()
.inspect(this::inspect)
@@ -121,7 +127,9 @@
}
public byte[] getTransformedClasses() throws Exception {
- return transformer(C.class).setPermittedSubclasses(C.class, Sub1.class, Sub2.class).transform();
+ return transformer(Super.class)
+ .setPermittedSubclasses(Super.class, Sub1.class, Sub2.class)
+ .transform();
}
static class TestClass {
@@ -134,11 +142,11 @@
}
}
- abstract static class C /* permits Sub1, Sub2 */ {}
+ abstract static class Super /* permits Sub1, Sub2 */ {}
- static class Sub1 extends C {}
+ static class Sub1 extends Super {}
- static class Sub2 extends C {}
+ static class Sub2 extends Super {}
- static class Sub3 extends C {}
+ static class Sub3 extends Super {}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesJdk17CompiledTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesJdk17CompiledTest.java
index 2b7d981..c51bb03 100644
--- a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesJdk17CompiledTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesJdk17CompiledTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.desugar.sealed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static junit.framework.Assert.assertEquals;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assume.assumeTrue;
@@ -18,7 +19,7 @@
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.Matchers;
+import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -70,14 +71,17 @@
}
private void inspect(CodeInspector inspector) {
- ClassSubject clazz = inspector.clazz("sealed.Compiler");
- assertThat(clazz, Matchers.isPresentAndRenamed());
- if (!parameters.isCfRuntime()) {
- return;
- }
+ ClassSubject clazz = inspector.clazz(Sealed.Compiler.typeName());
+ assertThat(clazz, isPresentAndRenamed());
+ ClassSubject sub1 = inspector.clazz(Sealed.R8Compiler.typeName());
+ ClassSubject sub2 = inspector.clazz(Sealed.D8Compiler.typeName());
+ assertThat(sub1, isPresentAndRenamed());
+ assertThat(sub2, isPresentAndRenamed());
assertEquals(
- keepPermittedSubclassesAttribute ? 2 : 0,
- clazz.getFinalPermittedSubclassAttributes().size());
+ parameters.isCfRuntime() && keepPermittedSubclassesAttribute
+ ? ImmutableList.of(sub1.asTypeSubject(), sub2.asTypeSubject())
+ : ImmutableList.of(),
+ clazz.getFinalPermittedSubclassAttributes());
}
@Test
@@ -89,8 +93,8 @@
.applyIf(
keepPermittedSubclassesAttribute,
TestShrinkerBuilder::addKeepAttributePermittedSubclasses)
- // Keep the sealed class to ensure the PermittedSubclasses attribute stays live.
.addKeepPermittedSubclasses(Sealed.Compiler.typeName())
+ .addKeepRules("-keep,allowobfuscation class * extends " + Sealed.Compiler.typeName())
.addKeepMainRule(Sealed.Main.typeName())
.compile()
.inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesMergeTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesMergeTest.java
new file mode 100644
index 0000000..bcd774a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesMergeTest.java
@@ -0,0 +1,99 @@
+// Copyright (c) 2023, 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.sealed;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static junit.framework.Assert.assertEquals;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+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 SealedClassesMergeTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ static final String EXPECTED = StringUtils.lines("Success!");
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ private void addTestClasses(TestBuilder<?, ?> builder) throws Exception {
+ builder
+ .addProgramClasses(TestClass.class, Sub1.class, Sub2.class)
+ .addProgramClassFileData(getTransformedClasses());
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject clazz = inspector.clazz(Super.class);
+ assertThat(clazz, isPresentAndRenamed());
+ ClassSubject sub1 = inspector.clazz(Sub1.class);
+ assertThat(sub1, isPresentAndRenamed());
+ assertEquals(
+ parameters.isCfRuntime() ? ImmutableList.of(sub1.asTypeSubject()) : ImmutableList.of(),
+ clazz.getFinalPermittedSubclassAttributes());
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ parameters.assumeR8TestParameters();
+ testForR8(parameters.getBackend())
+ .apply(this::addTestClasses)
+ .setMinApi(parameters)
+ .addKeepAttributePermittedSubclasses()
+ .addKeepClassRulesWithAllowObfuscation(Super.class)
+ .addKeepMainRule(TestClass.class)
+ .addHorizontallyMergedClassesInspector(
+ inspector -> {
+ inspector
+ .assertIsCompleteMergeGroup(Sub2.class, Sub1.class)
+ .assertNoOtherClassesMerged();
+ })
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .applyIf(
+ !parameters.isCfRuntime() || parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK17),
+ r -> r.assertSuccessWithOutput(EXPECTED),
+ r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class));
+ }
+
+ public byte[] getTransformedClasses() throws Exception {
+ return transformer(Super.class)
+ .setPermittedSubclasses(Super.class, Sub1.class, Sub2.class)
+ .transform();
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ new Sub1();
+ new Sub2();
+ System.out.println("Success!");
+ }
+ }
+
+ abstract static class Super /* permits Sub1, Sub2 */ {}
+
+ static class Sub1 extends Super {}
+
+ static class Sub2 extends Super {}
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesShrinkingTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesShrinkingTest.java
new file mode 100644
index 0000000..1893e3b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesShrinkingTest.java
@@ -0,0 +1,92 @@
+// Copyright (c) 2023, 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.sealed;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static junit.framework.Assert.assertEquals;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+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 SealedClassesShrinkingTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ static final String EXPECTED = StringUtils.lines("Success!");
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ private void addTestClasses(TestBuilder<?, ?> builder) throws Exception {
+ builder
+ .addProgramClasses(TestClass.class, UsedSub.class, UnusedSub.class)
+ .addProgramClassFileData(getTransformedClasses());
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject clazz = inspector.clazz(Super.class);
+ assertThat(clazz, isPresentAndRenamed());
+ ClassSubject sub1 = inspector.clazz(UsedSub.class);
+ assertThat(sub1, isPresentAndRenamed());
+ assertEquals(
+ parameters.isCfRuntime() ? ImmutableList.of(sub1.asTypeSubject()) : ImmutableList.of(),
+ clazz.getFinalPermittedSubclassAttributes());
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ parameters.assumeR8TestParameters();
+ testForR8(parameters.getBackend())
+ .apply(this::addTestClasses)
+ .setMinApi(parameters)
+ .addKeepAttributePermittedSubclasses()
+ .addKeepClassRulesWithAllowObfuscation(Super.class, UsedSub.class)
+ .addKeepMainRule(TestClass.class)
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .applyIf(
+ !parameters.isCfRuntime() || parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK17),
+ r -> r.assertSuccessWithOutput(EXPECTED),
+ r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class));
+ }
+
+ public byte[] getTransformedClasses() throws Exception {
+ return transformer(Super.class)
+ .setPermittedSubclasses(Super.class, UsedSub.class, UnusedSub.class)
+ .transform();
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ new UsedSub();
+ System.out.println("Success!");
+ }
+ }
+
+ abstract static class Super /* permits UsedSub, UnusedSub */ {}
+
+ static class UsedSub extends Super {}
+
+ static class UnusedSub extends Super {}
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesTest.java
index 3aa1a38..997e4fb 100644
--- a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesTest.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.desugar.sealed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assume.assumeTrue;
@@ -18,7 +20,7 @@
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.Matchers;
+import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -35,12 +37,16 @@
@Parameter(1)
public boolean keepPermittedSubclassesAttribute;
+ @Parameter(2)
+ public boolean repackage;
+
static final String EXPECTED = StringUtils.lines("Success!");
- @Parameters(name = "{0}, keepPermittedSubclasses = {1}")
+ @Parameters(name = "{0}, keepPermittedSubclasses = {1}, repackage = {2}")
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(),
+ BooleanUtils.values(),
BooleanUtils.values());
}
@@ -76,14 +82,22 @@
}
private void inspect(CodeInspector inspector) {
- ClassSubject clazz = inspector.clazz(C.class);
- assertThat(clazz, Matchers.isPresentAndRenamed());
- if (!parameters.isCfRuntime()) {
- return;
+ ClassSubject clazz = inspector.clazz(Super.class);
+ assertThat(clazz, isPresentAndRenamed());
+ ClassSubject sub1 = inspector.clazz(Sub1.class);
+ ClassSubject sub2 = inspector.clazz(Sub2.class);
+ assertThat(sub1, isPresentAndRenamed());
+ assertThat(sub2, isPresentAndRenamed());
+ if (repackage) {
+ assertEquals(-1, sub1.getFinalName().indexOf('.'));
+ } else {
+ assertTrue(sub1.getFinalName().startsWith(getClass().getPackage().getName()));
}
assertEquals(
- keepPermittedSubclassesAttribute ? 2 : 0,
- clazz.getFinalPermittedSubclassAttributes().size());
+ parameters.isCfRuntime() && keepPermittedSubclassesAttribute
+ ? ImmutableList.of(sub1.asTypeSubject(), sub2.asTypeSubject())
+ : ImmutableList.of(),
+ clazz.getFinalPermittedSubclassAttributes());
}
@Test
@@ -96,8 +110,9 @@
keepPermittedSubclassesAttribute,
TestShrinkerBuilder::addKeepAttributePermittedSubclasses)
// Keep the sealed class to ensure the PermittedSubclasses attribute stays live.
- .addKeepPermittedSubclasses(C.class)
+ .addKeepPermittedSubclasses(Super.class, Sub1.class, Sub2.class)
.addKeepMainRule(TestClass.class)
+ .applyIf(repackage, b -> b.addKeepRules("-repackageclasses"))
.compile()
.inspect(this::inspect)
.run(parameters.getRuntime(), TestClass.class)
@@ -108,7 +123,9 @@
}
public byte[] getTransformedClasses() throws Exception {
- return transformer(C.class).setPermittedSubclasses(C.class, Sub1.class, Sub2.class).transform();
+ return transformer(Super.class)
+ .setPermittedSubclasses(Super.class, Sub1.class, Sub2.class)
+ .transform();
}
static class TestClass {
@@ -120,9 +137,9 @@
}
}
- abstract static class C /* permits Sub1, Sub2 */ {}
+ public abstract static class Super /* permits Sub1, Sub2 */ {}
- static class Sub1 extends C {}
+ public static class Sub1 extends Super {}
- static class Sub2 extends C {}
+ public static class Sub2 extends Super {}
}