Reproduce illegal member value propagation in presence of feature splits
Bug: 155249941
Change-Id: Id554e9797da82921c4ab9811e901f5e624d19d17
diff --git a/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java b/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
new file mode 100644
index 0000000..0979b27
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, 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.utils;
+
+public class ConsumerUtils {
+
+ public static <T> ThrowingConsumer<T, RuntimeException> emptyThrowingConsumer() {
+ return ignore -> {};
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterInlineRegression.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterInlineRegression.java
index 14d1e63..8f6a2d6 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterInlineRegression.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterInlineRegression.java
@@ -4,7 +4,10 @@
package com.android.tools.r8.dexsplitter;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
@@ -16,13 +19,13 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ThrowingConsumer;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
-import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
-import java.util.function.Predicate;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -46,15 +49,11 @@
@Test
public void testInliningFromFeature() throws Exception {
- Predicate<R8TestCompileResult> ensureGetFromFeatureGone =
+ ThrowingConsumer<R8TestCompileResult, Exception> ensureGetFromFeatureGone =
r8TestCompileResult -> {
// Ensure that getFromFeature from FeatureClass is inlined into the run method.
- try {
- ClassSubject clazz = r8TestCompileResult.inspector().clazz(FeatureClass.class);
- return clazz.uniqueMethodWithName("getFromFeature").isAbsent();
- } catch (IOException | ExecutionException ex) {
- throw new RuntimeException("Found getFromFeature in FeatureClass");
- }
+ ClassSubject clazz = r8TestCompileResult.inspector().clazz(FeatureClass.class);
+ assertThat(clazz.uniqueMethodWithName("getFromFeature"), not(isPresent()));
};
Consumer<R8FullTestBuilder> configurator =
r8FullTestBuilder -> r8FullTestBuilder.enableMergeAnnotations().noMinification();
@@ -83,8 +82,7 @@
ImmutableSet.of(BaseSuperClass.class),
ImmutableSet.of(FeatureClass.class),
FeatureClass.class,
- EXPECTED,
- a -> true,
+ ConsumerUtils.emptyThrowingConsumer(),
configurator);
assertEquals(processResult.exitCode, 0);
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMemberValuePropagationRegression.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMemberValuePropagationRegression.java
new file mode 100644
index 0000000..b177200
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMemberValuePropagationRegression.java
@@ -0,0 +1,113 @@
+// 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.dexsplitter;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.ConsumerUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ThrowingConsumer;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class DexSplitterMemberValuePropagationRegression extends SplitterTestBase {
+
+ public static final String EXPECTED = StringUtils.lines("42");
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection params() {
+ return getTestParameters().withAllRuntimes().build();
+ }
+
+ private final TestParameters parameters;
+
+ public DexSplitterMemberValuePropagationRegression(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testPropagationFromFeature() throws Exception {
+ ThrowingConsumer<R8TestCompileResult, Exception> ensureGetFromFeatureGone =
+ r8TestCompileResult -> {
+ // Ensure that getFromFeature from FeatureClass is inlined into the run method.
+ ClassSubject clazz = r8TestCompileResult.inspector().clazz(FeatureClass.class);
+ assertThat(clazz.uniqueMethodWithName("getFromFeature"), not(isPresent()));
+ };
+ ProcessResult processResult =
+ testDexSplitter(
+ parameters,
+ ImmutableSet.of(BaseSuperClass.class),
+ ImmutableSet.of(FeatureClass.class, FeatureEnum.class),
+ FeatureClass.class,
+ EXPECTED,
+ ensureGetFromFeatureGone,
+ builder -> builder.enableInliningAnnotations().noMinification());
+ // We expect art to fail on this with the dex splitter, see b/122902374
+ assertNotEquals(processResult.exitCode, 0);
+ assertTrue(processResult.stderr.contains("NoClassDefFoundError"));
+ }
+
+ @Test
+ public void testOnR8Splitter() throws IOException, CompilationFailedException {
+ assumeTrue(parameters.isDexRuntime());
+ ProcessResult processResult =
+ testR8Splitter(
+ parameters,
+ ImmutableSet.of(BaseSuperClass.class),
+ ImmutableSet.of(FeatureClass.class, FeatureEnum.class),
+ FeatureClass.class,
+ ConsumerUtils.emptyThrowingConsumer(),
+ R8TestBuilder::enableInliningAnnotations);
+ // TODO(b/155249941): Should succeed with `EXPECTED` as output.
+ assertNotEquals(processResult.exitCode, 0);
+ }
+
+ public abstract static class BaseSuperClass implements RunInterface {
+
+ @NeverInline
+ @Override
+ public void run() {
+ System.out.println(getFromFeature());
+ }
+
+ public abstract Enum<?> getFromFeature();
+ }
+
+ public static class FeatureClass extends BaseSuperClass {
+
+ @NeverInline
+ @Override
+ public Enum<?> getFromFeature() {
+ return FeatureEnum.A;
+ }
+ }
+
+ public enum FeatureEnum {
+ A;
+
+ @Override
+ public String toString() {
+ return "42";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
index 25cbe9a..067f920 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
@@ -4,13 +4,14 @@
package com.android.tools.r8.dexsplitter;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NeverMerge;
import com.android.tools.r8.R8FullTestBuilder;
@@ -18,13 +19,13 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ThrowingConsumer;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
-import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
-import java.util.function.Predicate;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -49,16 +50,12 @@
@Test
public void testInliningFromFeature() throws Exception {
// Static merging is based on sorting order, we assert that we merged to the feature.
- Predicate<R8TestCompileResult> ensureMergingToFeature =
+ ThrowingConsumer<R8TestCompileResult, Exception> ensureMergingToFeature =
r8TestCompileResult -> {
- try {
- ClassSubject clazz = r8TestCompileResult.inspector().clazz(AFeatureWithStatic.class);
- return clazz.allMethods().size() == 2
- && clazz.uniqueMethodWithName("getBase42").isPresent()
- && clazz.uniqueMethodWithName("getFoobar").isPresent();
- } catch (IOException | ExecutionException ex) {
- throw new RuntimeException("Failed lookup up AFeatureWithStatic");
- }
+ ClassSubject clazz = r8TestCompileResult.inspector().clazz(AFeatureWithStatic.class);
+ assertEquals(2, clazz.allMethods().size());
+ assertThat(clazz.uniqueMethodWithName("getBase42"), isPresent());
+ assertThat(clazz.uniqueMethodWithName("getFoobar"), isPresent());
};
Consumer<R8FullTestBuilder> configurator =
r8FullTestBuilder ->
@@ -82,8 +79,7 @@
}
@Test
- public void testOnR8Splitter() throws IOException, CompilationFailedException,
- ExecutionException {
+ public void testOnR8Splitter() throws IOException, CompilationFailedException {
assumeTrue(parameters.isDexRuntime());
Consumer<R8FullTestBuilder> configurator =
r8FullTestBuilder -> r8FullTestBuilder.enableMergeAnnotations().noMinification();
@@ -93,8 +89,7 @@
ImmutableSet.of(BaseClass.class, BaseWithStatic.class),
ImmutableSet.of(FeatureClass.class, AFeatureWithStatic.class),
FeatureClass.class,
- EXPECTED,
- a -> true,
+ ConsumerUtils.emptyThrowingConsumer(),
configurator);
assertEquals(processResult.exitCode, 0);
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/R8SplitterInlineToFeature.java b/src/test/java/com/android/tools/r8/dexsplitter/R8SplitterInlineToFeature.java
index dae7746..331403a 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/R8SplitterInlineToFeature.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/R8SplitterInlineToFeature.java
@@ -4,10 +4,12 @@
package com.android.tools.r8.dexsplitter;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.NeverMerge;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.R8TestCompileResult;
@@ -15,12 +17,10 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ThrowingConsumer;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.google.common.collect.ImmutableSet;
-import java.io.IOException;
-import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
-import java.util.function.Predicate;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -47,19 +47,15 @@
}
@Test
- public void testInlining() throws IOException, CompilationFailedException {
+ public void testInlining() throws Exception {
assumeTrue(parameters.isDexRuntime());
Consumer<R8FullTestBuilder> configurator =
r8FullTestBuilder -> r8FullTestBuilder.enableMergeAnnotations().noMinification();
- Predicate<R8TestCompileResult> ensureInlined =
+ ThrowingConsumer<R8TestCompileResult, Exception> ensureInlined =
r8TestCompileResult -> {
// Ensure that isEarly from BaseUtilClass is inlined into the feature
- try {
- ClassSubject clazz = r8TestCompileResult.inspector().clazz(BaseUtilClass.class);
- return clazz.uniqueMethodWithName("isEarly").isAbsent();
- } catch (IOException | ExecutionException ex) {
- throw new RuntimeException("Found isEarly in BaseUtilClass");
- }
+ ClassSubject clazz = r8TestCompileResult.inspector().clazz(BaseUtilClass.class);
+ assertThat(clazz.uniqueMethodWithName("isEarly"), not(isPresent()));
};
ProcessResult processResult =
testR8Splitter(
@@ -67,7 +63,6 @@
ImmutableSet.of(BaseSuperClass.class, BaseUtilClass.class),
ImmutableSet.of(FeatureClass.class),
FeatureClass.class,
- EXPECTED,
ensureInlined,
configurator);
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java b/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
index 1e31022..16291c4 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.dexsplitter.DexSplitter.Options;
import com.android.tools.r8.utils.ArchiveResourceProvider;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.ThrowingConsumer;
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams;
@@ -32,7 +33,6 @@
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
-import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
@@ -121,20 +121,19 @@
TemporaryFolder temp,
Collection<String> nonJavaFiles,
boolean ensureClassesInOutput,
- Class... classes) {
+ Class<?>... classes) {
addConsumers(builder, outputPath, temp, nonJavaFiles, true, Arrays.asList(classes));
return builder.build();
}
- protected ProcessResult testR8Splitter(
+ protected <E extends Throwable> ProcessResult testR8Splitter(
TestParameters parameters,
Set<Class<?>> baseClasses,
Set<Class<?>> featureClasses,
- Class toRun,
- String expectedOutput,
- Predicate<R8TestCompileResult> predicate,
+ Class<?> toRun,
+ ThrowingConsumer<R8TestCompileResult, E> compileResultConsumer,
Consumer<R8FullTestBuilder> r8TestConfigurator)
- throws IOException, CompilationFailedException {
+ throws IOException, CompilationFailedException, E {
Path featureOutput = temp.newFile("feature.zip").toPath();
R8FullTestBuilder r8FullTestBuilder = testForR8(parameters.getBackend());
@@ -158,7 +157,7 @@
r8TestConfigurator.accept(r8FullTestBuilder);
R8TestCompileResult r8TestCompileResult = r8FullTestBuilder.compile();
- assertTrue(predicate.test(r8TestCompileResult));
+ compileResultConsumer.accept(r8TestCompileResult);
Path baseOutput = r8TestCompileResult.writeToZip();
return runFeatureOnArt(toRun, baseOutput, featureOutput, parameters.getRuntime());
@@ -166,15 +165,15 @@
// Compile the passed in classes plus RunInterface and SplitRunner using R8, then split
// based on the base/feature sets. toRun must implement the BaseRunInterface
- protected ProcessResult testDexSplitter(
+ protected <E extends Throwable> ProcessResult testDexSplitter(
TestParameters parameters,
Set<Class<?>> baseClasses,
Set<Class<?>> featureClasses,
- Class toRun,
+ Class<?> toRun,
String expectedOutput,
- Predicate<R8TestCompileResult> predicate,
+ ThrowingConsumer<R8TestCompileResult, E> compileResultConsumer,
Consumer<R8FullTestBuilder> r8TestConfigurator)
- throws Exception {
+ throws Exception, E {
List<Class<?>> baseClassesWithRunner =
ImmutableList.<Class<?>>builder()
.add(RunInterface.class, SplitRunner.class)
@@ -229,7 +228,7 @@
.addKeepClassRules(toRun);
r8TestConfigurator.accept(r8FullTestBuilder);
R8TestCompileResult r8TestCompileResult = r8FullTestBuilder.compile();
- assertTrue(predicate.test(r8TestCompileResult));
+ compileResultConsumer.accept(r8TestCompileResult);
Path fullFiles = r8TestCompileResult.writeToZip();
// Ensure that we can run the program as a unit (i.e., without splitting)