Merge "Avoid adding static / private interface methods to the root set."
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberPoolCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberPoolCollection.java
new file mode 100644
index 0000000..f593f45
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberPoolCollection.java
@@ -0,0 +1,212 @@
+// 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.ir.optimize;
+
+import com.android.tools.r8.graph.Descriptor;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.base.Equivalence;
+import com.google.common.base.Equivalence.Wrapper;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.function.Predicate;
+
+// Per-class collection of member signatures.
+public abstract class MemberPoolCollection<T extends Descriptor> {
+
+ final Equivalence<T> equivalence;
+ final DexApplication application;
+ final Map<DexClass, MemberPool<T>> memberPools = new ConcurrentHashMap<>();
+
+ MemberPoolCollection(DexApplication application, Equivalence<T> equivalence) {
+ this.application = application;
+ this.equivalence = equivalence;
+ }
+
+ public void buildAll(ExecutorService executorService, Timing timing) throws ExecutionException {
+ timing.begin("Building member pool collection");
+ try {
+ List<Future<?>> futures = new ArrayList<>();
+ List<? extends DexClass> classes = application.classes();
+ submitAll(classes, futures, executorService);
+ ThreadUtils.awaitFutures(futures);
+ } finally {
+ timing.end();
+ }
+ }
+
+ public MemberPool<T> buildForHierarchy(
+ DexClass clazz, ExecutorService executorService, Timing timing) throws ExecutionException {
+ timing.begin("Building member pool collection");
+ try {
+ List<Future<?>> futures = new ArrayList<>();
+ submitAll(
+ getAllSuperTypesInclusive(clazz, memberPools::containsKey), futures, executorService);
+ submitAll(getAllSubTypesExclusive(clazz, memberPools::containsKey), futures, executorService);
+ ThreadUtils.awaitFutures(futures);
+ } finally {
+ timing.end();
+ }
+ return get(clazz);
+ }
+
+ public boolean hasPool(DexClass clazz) {
+ return memberPools.containsKey(clazz);
+ }
+
+ public MemberPool<T> get(DexClass clazz) {
+ assert hasPool(clazz);
+ return memberPools.get(clazz);
+ }
+
+ public boolean markIfNotSeen(DexClass clazz, T reference) {
+ MemberPool<T> memberPool = get(clazz);
+ Wrapper<T> key = equivalence.wrap(reference);
+ if (memberPool.hasSeen(key)) {
+ return true;
+ }
+ memberPool.seen(key);
+ return false;
+ }
+
+ private void submitAll(
+ Iterable<? extends DexClass> classes,
+ List<Future<?>> futures,
+ ExecutorService executorService) {
+ for (DexClass clazz : classes) {
+ futures.add(executorService.submit(computeMemberPoolForClass(clazz)));
+ }
+ }
+
+ abstract Runnable computeMemberPoolForClass(DexClass clazz);
+
+ // TODO(jsjeon): maybe be part of AppInfoWithSubtyping?
+ private Set<DexClass> getAllSuperTypesInclusive(
+ DexClass subject, Predicate<DexClass> stoppingCriterion) {
+ Set<DexClass> superTypes = new HashSet<>();
+ Deque<DexClass> worklist = new ArrayDeque<>();
+ worklist.add(subject);
+ while (!worklist.isEmpty()) {
+ DexClass clazz = worklist.pop();
+ if (stoppingCriterion.test(clazz)) {
+ continue;
+ }
+ if (superTypes.add(clazz)) {
+ if (clazz.superType != null) {
+ addNonNull(worklist, application.definitionFor(clazz.superType));
+ }
+ for (DexType interfaceType : clazz.interfaces.values) {
+ addNonNull(worklist, application.definitionFor(interfaceType));
+ }
+ }
+ }
+ return superTypes;
+ }
+
+ // TODO(jsjeon): maybe be part of AppInfoWithSubtyping?
+ private Set<DexClass> getAllSubTypesExclusive(
+ DexClass subject, Predicate<DexClass> stoppingCriterion) {
+ Set<DexClass> subTypes = new HashSet<>();
+ Deque<DexClass> worklist = new ArrayDeque<>();
+ subject.type.forAllExtendsSubtypes(
+ type -> addNonNull(worklist, application.definitionFor(type)));
+ subject.type.forAllImplementsSubtypes(
+ type -> addNonNull(worklist, application.definitionFor(type)));
+ while (!worklist.isEmpty()) {
+ DexClass clazz = worklist.pop();
+ if (stoppingCriterion.test(clazz)) {
+ continue;
+ }
+ if (subTypes.add(clazz)) {
+ clazz.type.forAllExtendsSubtypes(
+ type -> addNonNull(worklist, application.definitionFor(type)));
+ clazz.type.forAllImplementsSubtypes(
+ type -> addNonNull(worklist, application.definitionFor(type)));
+ }
+ }
+ return subTypes;
+ }
+
+ public static class MemberPool<T> {
+ private Equivalence<T> equivalence;
+ private MemberPool<T> superType;
+ private final Set<MemberPool<T>> interfaces = new HashSet<>();
+ private final Set<MemberPool<T>> subTypes = new HashSet<>();
+ private final Set<Wrapper<T>> memberPool = new HashSet<>();
+
+ MemberPool(Equivalence<T> equivalence) {
+ this.equivalence = equivalence;
+ }
+
+ synchronized void linkSupertype(MemberPool<T> superType) {
+ assert this.superType == null;
+ this.superType = superType;
+ }
+
+ synchronized void linkSubtype(MemberPool<T> subType) {
+ boolean added = subTypes.add(subType);
+ assert added;
+ }
+
+ synchronized void linkInterface(MemberPool<T> itf) {
+ boolean added = interfaces.add(itf);
+ assert added;
+ }
+
+ public void seen(T descriptor) {
+ seen(equivalence.wrap(descriptor));
+ }
+
+ public synchronized void seen(Wrapper<T> descriptor) {
+ boolean added = memberPool.add(descriptor);
+ assert added;
+ }
+
+ public boolean hasSeen(T descriptor) {
+ return hasSeen(equivalence.wrap(descriptor));
+ }
+
+ public boolean hasSeen(Wrapper<T> descriptor) {
+ return hasSeenUpwardRecursive(descriptor) || hasSeenDownwardRecursive(descriptor);
+ }
+
+ public boolean hasSeenDirectly(T descriptor) {
+ return hasSeenDirectly(equivalence.wrap(descriptor));
+ }
+
+ public boolean hasSeenDirectly(Wrapper<T> descriptor) {
+ return memberPool.contains(descriptor);
+ }
+
+ private boolean hasSeenUpwardRecursive(Wrapper<T> descriptor) {
+ return memberPool.contains(descriptor)
+ || (superType != null && superType.hasSeenUpwardRecursive(descriptor))
+ || interfaces.stream().anyMatch(itf -> itf.hasSeenUpwardRecursive(descriptor));
+ }
+
+ private boolean hasSeenDownwardRecursive(Wrapper<T> reference) {
+ return memberPool.contains(reference)
+ || subTypes.stream().anyMatch(subType -> subType.hasSeenDownwardRecursive(reference));
+ }
+ }
+
+ private static <T> void addNonNull(Collection<T> collection, T item) {
+ if (item != null) {
+ collection.add(item);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java
index ca38f09..68b32ea 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java
@@ -7,93 +7,30 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
-import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.Timing;
-import com.google.common.base.Equivalence;
-import com.google.common.base.Equivalence.Wrapper;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Deque;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.function.Predicate;
// Per-class collection of method signatures.
//
-// Example use case: to determine if a certain method can be publicized or not.
-public class MethodPoolCollection {
-
- private static final Equivalence<DexMethod> equivalence = MethodSignatureEquivalence.get();
-
- private final DexApplication application;
- private final Map<DexClass, MethodPool> methodPools = new ConcurrentHashMap<>();
+// Example use cases:
+// *) in publicizer,
+// to determine if a private method does not collide with methods in that class hierarchy.
+// *) in vertical class merger,
+// before moving a default interface method to its subtype, check if it does not collide with one
+// in the given class hierarchy.
+// *) in uninstantiated type optimizer,
+// to avoid signature collisions while discarding unused return type or parameters.
+// TODO(b/66369976): to determine if a certain method can be made `final`.
+public class MethodPoolCollection extends MemberPoolCollection<DexMethod> {
public MethodPoolCollection(DexApplication application) {
- this.application = application;
+ super(application, MethodSignatureEquivalence.get());
}
- public void buildAll(ExecutorService executorService, Timing timing) throws ExecutionException {
- timing.begin("Building method pool collection");
- try {
- List<Future<?>> futures = new ArrayList<>();
- @SuppressWarnings("unchecked")
- List<DexClass> classes = (List) application.classes();
- submitAll(classes, futures, executorService);
- ThreadUtils.awaitFutures(futures);
- } finally {
- timing.end();
- }
- }
-
- public MethodPool buildForHierarchy(
- DexClass clazz, ExecutorService executorService, Timing timing) throws ExecutionException {
- timing.begin("Building method pool collection");
- try {
- List<Future<?>> futures = new ArrayList<>();
- submitAll(
- getAllSuperTypesInclusive(clazz, methodPools::containsKey), futures, executorService);
- submitAll(getAllSubTypesExclusive(clazz, methodPools::containsKey), futures, executorService);
- ThreadUtils.awaitFutures(futures);
- } finally {
- timing.end();
- }
- return get(clazz);
- }
-
- public MethodPool get(DexClass clazz) {
- assert methodPools.containsKey(clazz);
- return methodPools.get(clazz);
- }
-
- public boolean markIfNotSeen(DexClass clazz, DexMethod method) {
- MethodPool methodPool = get(clazz);
- Wrapper<DexMethod> key = equivalence.wrap(method);
- if (methodPool.hasSeen(key)) {
- return true;
- }
- methodPool.seen(key);
- return false;
- }
-
- private void submitAll(
- Iterable<DexClass> classes, List<Future<?>> futures, ExecutorService executorService) {
- for (DexClass clazz : classes) {
- futures.add(executorService.submit(computeMethodPoolPerClass(clazz)));
- }
- }
-
- private Runnable computeMethodPoolPerClass(DexClass clazz) {
+ @Override
+ Runnable computeMemberPoolForClass(DexClass clazz) {
return () -> {
- MethodPool methodPool = methodPools.computeIfAbsent(clazz, k -> new MethodPool());
+ MemberPool<DexMethod> methodPool =
+ memberPools.computeIfAbsent(clazz, k -> new MemberPool<>(equivalence));
clazz.forEachMethod(
encodedMethod -> {
// We will add private instance methods when we promote them.
@@ -104,7 +41,8 @@
if (clazz.superType != null) {
DexClass superClazz = application.definitionFor(clazz.superType);
if (superClazz != null) {
- MethodPool superPool = methodPools.computeIfAbsent(superClazz, k -> new MethodPool());
+ MemberPool<DexMethod> superPool =
+ memberPools.computeIfAbsent(superClazz, k -> new MemberPool<>(equivalence));
superPool.linkSubtype(methodPool);
methodPool.linkSupertype(superPool);
}
@@ -114,7 +52,8 @@
implementer -> {
DexClass subClazz = application.definitionFor(implementer);
if (subClazz != null) {
- MethodPool childPool = methodPools.computeIfAbsent(subClazz, k -> new MethodPool());
+ MemberPool<DexMethod> childPool =
+ memberPools.computeIfAbsent(subClazz, k -> new MemberPool<>(equivalence));
childPool.linkInterface(methodPool);
}
});
@@ -122,106 +61,4 @@
};
}
- private Set<DexClass> getAllSuperTypesInclusive(
- DexClass subject, Predicate<DexClass> stoppingCriterion) {
- Set<DexClass> superTypes = new HashSet<>();
- Deque<DexClass> worklist = new ArrayDeque<>();
- worklist.add(subject);
- while (!worklist.isEmpty()) {
- DexClass clazz = worklist.pop();
- if (stoppingCriterion.test(clazz)) {
- continue;
- }
- if (superTypes.add(clazz)) {
- if (clazz.superType != null) {
- addNonNull(worklist, application.definitionFor(clazz.superType));
- }
- for (DexType interfaceType : clazz.interfaces.values) {
- addNonNull(worklist, application.definitionFor(interfaceType));
- }
- }
- }
- return superTypes;
- }
-
- private Set<DexClass> getAllSubTypesExclusive(
- DexClass subject, Predicate<DexClass> stoppingCriterion) {
- Set<DexClass> subTypes = new HashSet<>();
- Deque<DexClass> worklist = new ArrayDeque<>();
- subject.type.forAllExtendsSubtypes(
- type -> addNonNull(worklist, application.definitionFor(type)));
- subject.type.forAllImplementsSubtypes(
- type -> addNonNull(worklist, application.definitionFor(type)));
- while (!worklist.isEmpty()) {
- DexClass clazz = worklist.pop();
- if (stoppingCriterion.test(clazz)) {
- continue;
- }
- if (subTypes.add(clazz)) {
- clazz.type.forAllExtendsSubtypes(
- type -> addNonNull(worklist, application.definitionFor(type)));
- clazz.type.forAllImplementsSubtypes(
- type -> addNonNull(worklist, application.definitionFor(type)));
- }
- }
- return subTypes;
- }
-
- public static class MethodPool {
- private MethodPool superType;
- private final Set<MethodPool> interfaces = new HashSet<>();
- private final Set<MethodPool> subTypes = new HashSet<>();
- private final Set<Wrapper<DexMethod>> methodPool = new HashSet<>();
-
- private MethodPool() {}
-
- synchronized void linkSupertype(MethodPool superType) {
- assert this.superType == null;
- this.superType = superType;
- }
-
- synchronized void linkSubtype(MethodPool subType) {
- boolean added = subTypes.add(subType);
- assert added;
- }
-
- synchronized void linkInterface(MethodPool itf) {
- boolean added = interfaces.add(itf);
- assert added;
- }
-
- public void seen(DexMethod method) {
- seen(MethodSignatureEquivalence.get().wrap(method));
- }
-
- public synchronized void seen(Wrapper<DexMethod> method) {
- boolean added = methodPool.add(method);
- assert added;
- }
-
- public boolean hasSeen(Wrapper<DexMethod> method) {
- return hasSeenUpwardRecursive(method) || hasSeenDownwardRecursive(method);
- }
-
- public boolean hasSeenDirectly(Wrapper<DexMethod> method) {
- return methodPool.contains(method);
- }
-
- private boolean hasSeenUpwardRecursive(Wrapper<DexMethod> method) {
- return methodPool.contains(method)
- || (superType != null && superType.hasSeenUpwardRecursive(method))
- || interfaces.stream().anyMatch(itf -> itf.hasSeenUpwardRecursive(method));
- }
-
- private boolean hasSeenDownwardRecursive(Wrapper<DexMethod> method) {
- return methodPool.contains(method)
- || subTypes.stream().anyMatch(subType -> subType.hasSeenDownwardRecursive(method));
- }
- }
-
- private static <T> void addNonNull(Collection<T> collection, T item) {
- if (item != null) {
- collection.add(item);
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index e41593f..4cc3207 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -37,7 +37,7 @@
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Throw;
import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.optimize.MethodPoolCollection.MethodPool;
+import com.android.tools.r8.ir.optimize.MemberPoolCollection.MemberPool;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
@@ -139,7 +139,7 @@
appView,
appView.appInfo().classes(),
clazz -> {
- MethodPool methodPool = methodPoolCollection.get(clazz);
+ MemberPool<DexMethod> methodPool = methodPoolCollection.get(clazz);
if (clazz.isInterface()) {
// Do not allow changing the prototype of methods that override an interface method.
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index fd90187..fe10045 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -36,8 +36,8 @@
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
+import com.android.tools.r8.ir.optimize.MemberPoolCollection.MemberPool;
import com.android.tools.r8.ir.optimize.MethodPoolCollection;
-import com.android.tools.r8.ir.optimize.MethodPoolCollection.MethodPool;
import com.android.tools.r8.ir.synthetic.AbstractSynthesizedCode;
import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
import com.android.tools.r8.logging.Log;
@@ -941,7 +941,7 @@
// due to the way invoke-super works on default interface methods. In order to be able
// to hit this method directly after the merge, we need to make it public, and find a
// method name that does not collide with one in the hierarchy of this class.
- MethodPool methodPoolForTarget =
+ MemberPool<DexMethod> methodPoolForTarget =
methodPoolCollection.buildForHierarchy(target, executorService, timing);
resultingDirectMethod =
renameMethod(
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithReprocessingTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithReprocessingTest.java
new file mode 100644
index 0000000..31db483
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithReprocessingTest.java
@@ -0,0 +1,27 @@
+// 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.kotlin;
+
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.function.Consumer;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class KotlinLambdaMergingWithReprocessingTest extends AbstractR8KotlinTestBase {
+ private Consumer<InternalOptions> optionsModifier =
+ o -> {
+ o.enableTreeShaking = true;
+ o.enableMinification = false;
+ o.enableInlining = true;
+ o.enableClassInlining = true;
+ o.enableLambdaMerging = true;
+ };
+
+ @Ignore("b/123737770")
+ @Test
+ public void testMergingKStyleLambdasAndReprocessing() throws Exception {
+ final String mainClassName = "reprocess_merged_lambdas_kstyle.MainKt";
+ runTest("reprocess_merged_lambdas_kstyle", mainClassName, optionsModifier, null);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/b123068484/FieldRenamingTest.java b/src/test/java/com/android/tools/r8/naming/b123068484/FieldRenamingTest.java
index 119cb98..6908fda 100644
--- a/src/test/java/com/android/tools/r8/naming/b123068484/FieldRenamingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/b123068484/FieldRenamingTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.naming.b123068484;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
@@ -14,6 +15,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.FieldSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.nio.file.Path;
@@ -74,6 +76,9 @@
}
private void inspect(CodeInspector inspector) {
+ FieldSubject fld = inspector.clazz(CONCRETE1.getTypeName().replace("Concrete1", "Abs"))
+ .uniqueFieldWithName("strField");
+
ClassSubject main = inspector.clazz(MAIN);
assertThat(main, isPresent());
MethodSubject methodSubject = main.mainMethod();
@@ -83,7 +88,9 @@
.iterateInstructions(InstructionSubject::isInstanceGet)
.forEachRemaining(instructionSubject -> {
String fieldName = instructionSubject.getField().name.toString();
+ // All of those field references will be renamed.
assertNotEquals("strField", fieldName);
+ assertEquals(fld.getFinalName(), fieldName);
});
}
diff --git a/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTest.java b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTest.java
index 7733339..8d35333 100644
--- a/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTest.java
@@ -1,22 +1,16 @@
// 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.resolution;
-public class PublicFieldInnerClassTest {
- public static final Class<?>[] CLASSES = {
- PrivateBase.class,
- PrivateSubclass.class,
- PackageBase.class,
- PackageSubclass.class,
- ProtectedBase.class,
- ProtectedSubclass.class,
- PublicBase.class,
- PublicSubclass.class,
- PublicFieldInnerClassTest.class
- };
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+class PublicFieldInnerClassTestMain {
private static class PrivateBase {
public int value;
}
@@ -68,3 +62,30 @@
System.out.println(getPublic(new PublicSubclass()));
}
}
+
+@RunWith(Parameterized.class)
+public class PublicFieldInnerClassTest extends TestBase {
+ private static final Class CLASS = PublicFieldInnerClassTestMain.class;
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("0", "0", "0", "0");
+
+ private final Backend backend;
+
+ @Parameterized.Parameters(name = "Backend: {0}")
+ public static Object[] data() {
+ return Backend.values();
+ }
+
+ public PublicFieldInnerClassTest(Backend backend) {
+ this.backend = backend;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(backend)
+ .setMode(CompilationMode.DEBUG)
+ .addProgramClassesAndInnerClasses(CLASS)
+ .addKeepMainRule(CLASS)
+ .run(CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java b/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java
deleted file mode 100644
index d1fc83e..0000000
--- a/src/test/java/com/android/tools/r8/resolution/PublicFieldInnerClassTestRunner.java
+++ /dev/null
@@ -1,74 +0,0 @@
-// 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.resolution;
-
-import static org.junit.Assert.assertEquals;
-
-import com.android.tools.r8.ClassFileConsumer;
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.DexIndexedConsumer;
-import com.android.tools.r8.ProgramConsumer;
-import com.android.tools.r8.R8Command;
-import com.android.tools.r8.R8Command.Builder;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.origin.Origin;
-import java.nio.file.Path;
-import java.util.Arrays;
-import java.util.List;
-import org.junit.Test;
-
-public class PublicFieldInnerClassTestRunner extends TestBase {
- static final Class CLASS = PublicFieldInnerClassTest.class;
- static final Class<?>[] CLASSES = PublicFieldInnerClassTest.CLASSES;
-
- @Test
- public void testCf() throws Exception {
- ProcessResult runInput =
- ToolHelper.runJava(ToolHelper.getClassPathForTests(), CLASS.getCanonicalName());
- assertEquals(0, runInput.exitCode);
- Path outCf = temp.getRoot().toPath().resolve("cf.jar");
- build(new ClassFileConsumer.ArchiveConsumer(outCf));
- ProcessResult runCf = ToolHelper.runJava(outCf, CLASS.getCanonicalName());
- assertEquals(runInput.toString(), runCf.toString());
- assertEquals(
- -1,
- runCf.stderr.indexOf("java.lang.NoSuchFieldError"));
- }
-
- @Test
- public void testDex() throws Exception {
- ProcessResult runInput =
- ToolHelper.runJava(ToolHelper.getClassPathForTests(), CLASS.getCanonicalName());
- assertEquals(0, runInput.exitCode);
- Path outDex = temp.getRoot().toPath().resolve("dex.zip");
- build(new DexIndexedConsumer.ArchiveConsumer(outDex));
- // TODO(b/76191597): Change to runArtNoVerificationErrors + assertEquals when bug is fixed
- ProcessResult runDex = ToolHelper.runArtNoVerificationErrorsRaw(
- outDex.toString(), CLASS.getCanonicalName());
- assertEquals(runInput.stdout, runDex.stdout);
- assertEquals(runInput.exitCode, runDex.exitCode);
- assertEquals(
- -1,
- runDex.stderr.indexOf("java.lang.NoSuchFieldError"));
- }
-
- private void build(ProgramConsumer consumer) throws Exception {
- List<String> config = Arrays.asList(
- "-keep public class " + CLASS.getCanonicalName() + " {",
- " public static void main(...);",
- "}"
- );
- Builder builder = R8Command.builder()
- .setMode(CompilationMode.DEBUG)
- .addLibraryFiles(ToolHelper.getAndroidJar(ToolHelper.getMinApiLevelForDexVm()))
- .setProgramConsumer(consumer)
- .addProguardConfiguration(config, Origin.unknown());
- for (Class<?> c : CLASSES) {
- builder.addClassProgramData(ToolHelper.getClassAsBytes(c), Origin.unknown());
- }
- ToolHelper.runR8(builder.build());
- }
-}
diff --git a/src/test/kotlinR8TestResources/reprocess_merged_lambdas_kstyle/main.kt b/src/test/kotlinR8TestResources/reprocess_merged_lambdas_kstyle/main.kt
new file mode 100644
index 0000000..a71b9af
--- /dev/null
+++ b/src/test/kotlinR8TestResources/reprocess_merged_lambdas_kstyle/main.kt
@@ -0,0 +1,15 @@
+// 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 reprocess_merged_lambdas_kstyle
+
+private var COUNT = 11
+
+private fun next() = "${COUNT++}"
+
+fun consumeOne(l: (x: String) -> String): String = l(next())
+
+fun main(args: Array<String>) {
+ println(consumeOne { consumeOne { consumeOne { _ -> "A" } } })
+ println(consumeOne { consumeOne { consumeOne { _ -> "B" } } })
+}
\ No newline at end of file