Merge "Gmscore: comment out 'checkdiscard' from proguard config files."
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 92dc9cd..1c7e92d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -158,6 +158,14 @@
}
+ /**
+ * Returns true if this method is synthetic.
+ */
+ public boolean isSyntheticMethod() {
+ return accessFlags.isSynthetic();
+ }
+
+
public boolean isInliningCandidate(DexEncodedMethod container, Reason inliningReason,
AppInfoWithSubtyping appInfo) {
if (isClassInitializer()) {
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 1ae38e9..b1a2102 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -705,6 +705,22 @@
if (!liveMethods.contains(encodedMethod)) {
markTypeAsLive(encodedMethod.method.holder);
markMethodAsTargeted(encodedMethod, reason);
+ // For granting inner/outer classes access to their private constructors, javac generates
+ // additional synthetic constructors. These constructors take a synthetic class
+ // as argument. As it is not possible to express a keep rule for these synthetic classes
+ // always keep synthetic arguments to synthetic constructors. See b/69825683.
+ if (encodedMethod.isInstanceInitializer() && encodedMethod.isSyntheticMethod()) {
+ for (DexType type : encodedMethod.method.proto.parameters.values) {
+ type = type.isArrayType() ? type.toBaseType(appInfo.dexItemFactory) : type;
+ if (type.isPrimitiveType()) {
+ continue;
+ }
+ DexClass clazz = appInfo.definitionFor(type);
+ if (clazz != null && clazz.accessFlags.isSynthetic()) {
+ markTypeAsLive(type);
+ }
+ }
+ }
if (Log.ENABLED) {
Log.verbose(getClass(), "Method `%s` has become live due to direct invoke",
encodedMethod.method);
diff --git a/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
new file mode 100644
index 0000000..9026740
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2018, 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.regress.b69825683;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.FoundClassSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+
+public class Regress69825683Test extends TestBase {
+
+ @Test
+ public void outerConstructsInner() throws Exception {
+ Class mainClass = com.android.tools.r8.regress.b69825683.outerconstructsinner.Outer.class;
+ R8Command.Builder builder = R8Command.builder();
+ builder.addProgramFiles(ToolHelper.getClassFilesForTestPackage(mainClass.getPackage()));
+ builder.addProguardConfiguration(ImmutableList.of(
+ "-keep class " + mainClass.getCanonicalName() + " {",
+ " public static void main(java.lang.String[]);",
+ "}",
+ "-dontobfuscate"),
+ Origin.unknown());
+ builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+ AndroidApp app = ToolHelper.runR8(builder.build());
+ DexInspector inspector = new DexInspector(app);
+ List<FoundClassSubject> classes = inspector.allClasses();
+
+ // Check that the synthetic class is still present.
+ assertEquals(3, classes.size());
+ assertEquals(1,
+ classes.stream()
+ .map(FoundClassSubject::getOriginalName)
+ .filter(name -> name.endsWith("$1"))
+ .count());
+
+ // Run code to check that the constructor with synthetic class as argument is present.
+ Class innerClass =
+ com.android.tools.r8.regress.b69825683.outerconstructsinner.Outer.Inner.class;
+ String innerName = innerClass.getCanonicalName();
+ int index = innerName.lastIndexOf('.');
+ innerName = innerName.substring(0, index) + "$" + innerName.substring(index + 1);
+ assertTrue(runOnArt(app, mainClass).startsWith(innerName));
+ }
+
+ @Test
+ public void innerConstructsOuter() throws Exception {
+ Class mainClass = com.android.tools.r8.regress.b69825683.innerconstructsouter.Outer.class;
+ R8Command.Builder builder = R8Command.builder();
+ builder.addProgramFiles(ToolHelper.getClassFilesForTestPackage(mainClass.getPackage()));
+ builder.addProguardConfiguration(ImmutableList.of(
+ "-keep class " + mainClass.getCanonicalName() + " {",
+ " public static void main(java.lang.String[]);",
+ "}",
+ "-dontobfuscate"),
+ Origin.unknown());
+ builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+ AndroidApp app = ToolHelper.runR8(builder.build());
+ DexInspector inspector = new DexInspector(app);
+ List<FoundClassSubject> classes = inspector.allClasses();
+
+ // Check that the synthetic class is still present.
+ assertEquals(3, classes.size());
+ assertEquals(1,
+ classes.stream()
+ .map(FoundClassSubject::getOriginalName)
+ .filter(name -> name.endsWith("$1"))
+ .count());
+
+ // Run code to check that the constructor with synthetic class as argument is present.
+ assertTrue(runOnArt(app, mainClass).startsWith(mainClass.getCanonicalName()));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b69825683/innerconstructsouter/Outer.java b/src/test/java/com/android/tools/r8/regress/b69825683/innerconstructsouter/Outer.java
new file mode 100644
index 0000000..8241c18
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b69825683/innerconstructsouter/Outer.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2018, 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.regress.b69825683.innerconstructsouter;
+
+public class Outer {
+
+ private Outer() {
+ }
+
+ public static class Inner {
+ public Outer build() {
+ return new Outer();
+ }
+ }
+
+ public static void main(String args[]) {
+ Inner builder = new Inner();
+ builder.build();
+ for (java.lang.reflect.Constructor m : Outer.class.getDeclaredConstructors()) {
+ System.out.println(m);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b69825683/outerconstructsinner/Outer.java b/src/test/java/com/android/tools/r8/regress/b69825683/outerconstructsinner/Outer.java
new file mode 100644
index 0000000..79bccc5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b69825683/outerconstructsinner/Outer.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2018, 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.regress.b69825683.outerconstructsinner;
+
+public class Outer {
+
+ public Outer() {
+ new Inner();
+ }
+
+ public class Inner {
+
+ private Inner() {
+ }
+ }
+
+ public static void main(String args[]) {
+ new Outer();
+ for (java.lang.reflect.Constructor m : Outer.Inner.class.getDeclaredConstructors()) {
+ System.out.println(m);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/DexInspector.java b/src/test/java/com/android/tools/r8/utils/DexInspector.java
index bbd6fb5..b8ca32e 100644
--- a/src/test/java/com/android/tools/r8/utils/DexInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/DexInspector.java
@@ -189,18 +189,19 @@
}
public void forAllClasses(Consumer<FoundClassSubject> inspection) {
- forAll(application.classes(), clazz -> {
- ClassNamingForNameMapper naming = null;
- if (mapping != null) {
- String obfuscated = originalToObfuscatedMapping.get(clazz.type.toSourceString());
- if (obfuscated != null) {
- naming = mapping.getClassNaming(obfuscated);
- }
- }
- return new FoundClassSubject(clazz, naming);
+ forAll(application.classes(), cls -> {
+ ClassSubject found = clazz(cls.type.toSourceString());
+ assert found.isPresent();
+ return (FoundClassSubject) found;
}, inspection);
}
+ public List<FoundClassSubject> allClasses() {
+ ImmutableList.Builder<FoundClassSubject> builder = ImmutableList.builder();
+ forAllClasses(builder::add);
+ return builder.build();
+ }
+
public MethodSubject method(Method method) {
ClassSubject clazz = clazz(method.getDeclaringClass());
if (!clazz.isPresent()) {
@@ -310,6 +311,8 @@
public abstract AnnotationSubject annotation(String name);
+ public abstract String getOriginalName();
+
public abstract String getOriginalDescriptor();
public abstract String getFinalDescriptor();
@@ -364,6 +367,11 @@
}
@Override
+ public String getOriginalName() {
+ return null;
+ }
+
+ @Override
public String getOriginalDescriptor() {
return null;
}
@@ -518,6 +526,15 @@
}
@Override
+ public String getOriginalName() {
+ if (naming != null) {
+ return naming.originalName;
+ } else {
+ return DescriptorUtils.descriptorToJavaType(getFinalDescriptor());
+ }
+ }
+
+ @Override
public String getOriginalDescriptor() {
if (naming != null) {
return DescriptorUtils.javaTypeToDescriptor(naming.originalName);