Publicize inner class attribute access flags
Bug: 136236245, 136079178
Change-Id: Ia38a40b7b999289e7fad7dfb13ff87c8a9be8d55
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 3100f02..7e88ec4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -20,6 +20,7 @@
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
+import java.util.ListIterator;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
@@ -825,6 +826,18 @@
return null;
}
+ public void replaceInnerClassAttributeForThisClass(InnerClassAttribute newInnerClassAttribute) {
+ ListIterator<InnerClassAttribute> iterator = getInnerClasses().listIterator();
+ while (iterator.hasNext()) {
+ InnerClassAttribute innerClassAttribute = iterator.next();
+ if (type == innerClassAttribute.getInner()) {
+ iterator.set(newInnerClassAttribute);
+ return;
+ }
+ }
+ throw new Unreachable();
+ }
+
public boolean isLocalClass() {
InnerClassAttribute innerClass = getInnerClassAttributeForThisClass();
// The corresponding enclosing-method attribute might be not available, e.g., CF version 50.
diff --git a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
index e6584a8..a1006fe 100644
--- a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
@@ -3,12 +3,17 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.optimize;
+import static com.android.tools.r8.dex.Constants.ACC_PRIVATE;
+import static com.android.tools.r8.dex.Constants.ACC_PROTECTED;
+import static com.android.tools.r8.dex.Constants.ACC_PUBLIC;
+
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.ir.optimize.MethodPoolCollection;
import com.android.tools.r8.optimize.PublicizerLense.PublicizedLenseBuilder;
@@ -70,7 +75,11 @@
DexClass clazz = application.definitionFor(type);
if (clazz != null && clazz.isProgramClass()) {
clazz.accessFlags.promoteToPublic();
+
+ // Publicize fields.
clazz.forEachField(field -> field.accessFlags.promoteToPublic());
+
+ // Publicize methods.
Set<DexEncodedMethod> privateInstanceEncodedMethods = new LinkedHashSet<>();
clazz.forEachMethod(encodedMethod -> {
if (publicizeMethod(clazz, encodedMethod)) {
@@ -80,6 +89,15 @@
if (!privateInstanceEncodedMethods.isEmpty()) {
clazz.virtualizeMethods(privateInstanceEncodedMethods);
}
+
+ // Publicize inner class attribute.
+ InnerClassAttribute attr = clazz.getInnerClassAttributeForThisClass();
+ if (attr != null) {
+ int accessFlags = ((attr.getAccess() | ACC_PUBLIC) & ~ACC_PRIVATE) & ~ACC_PROTECTED;
+ clazz.replaceInnerClassAttributeForThisClass(
+ new InnerClassAttribute(
+ accessFlags, attr.getInner(), attr.getOuter(), attr.getInnerName()));
+ }
}
appView.appInfo().forAllExtendsSubtypes(type, this::publicizeType);
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/InnerClassAttributePublicizerTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/InnerClassAttributePublicizerTest.java
new file mode 100644
index 0000000..e3647b1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/InnerClassAttributePublicizerTest.java
@@ -0,0 +1,88 @@
+// 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.accessrelaxation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.ClassAccessFlags;
+import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class InnerClassAttributePublicizerTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().build();
+ }
+
+ public InnerClassAttributePublicizerTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(InnerClassAttributePublicizerTest.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepRules("-allowaccessmodification")
+ .addKeepAttributes("EnclosingMethod", "InnerClasses")
+ .setMinApi(parameters.getRuntime())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject classSubject = inspector.clazz(Outer.Inner.class);
+ assertThat(classSubject, isPresent());
+
+ InnerClassAttribute innerClassAttribute =
+ classSubject.getDexClass().getInnerClassAttributeForThisClass();
+ assertNotNull(innerClassAttribute);
+
+ ClassAccessFlags accessFlags =
+ ClassAccessFlags.fromSharedAccessFlags(innerClassAttribute.getAccess());
+ assertTrue(accessFlags.isPublic());
+ assertFalse(accessFlags.isPrivate());
+ assertFalse(accessFlags.isProtected());
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ new Outer();
+ new Outer.Inner();
+ }
+ }
+
+ static class Outer {
+
+ static {
+ System.out.print("Hello");
+ }
+
+ static class Inner {
+
+ static {
+ System.out.println(" world!");
+ }
+ }
+ }
+}