blob: 7c4046210b381b9a224bd4617eeef842ce78dba9 [file] [log] [blame]
// 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.compose;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.ZipUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.android.tools.r8.utils.codeinspector.MinificationInspector;
import com.android.tools.r8.utils.codeinspector.RepackagingInspector;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.EnumMap;
import java.util.function.BiFunction;
import org.junit.BeforeClass;
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 NestedComposableArgumentPropagationTest extends TestBase {
enum ComposableFunction {
A,
B,
C
}
static class CodeStats {
final int numberOfIfInstructions;
CodeStats(MethodSubject methodSubject) {
this.numberOfIfInstructions =
(int) methodSubject.streamInstructions().filter(InstructionSubject::isIf).count();
}
}
private static Path dump;
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withNoneRuntime().build();
}
@BeforeClass
public static void setup() throws IOException {
dump = getStaticTemp().newFolder().toPath();
ZipUtils.unzip(
Paths.get(
ToolHelper.THIRD_PARTY_DIR,
"opensource-apps/compose-examples/changed-bitwise-value-propagation/dump.zip"),
dump);
}
public NestedComposableArgumentPropagationTest(TestParameters parameters) {
parameters.assertNoneRuntime();
}
@Test
public void test() throws Exception {
EnumMap<ComposableFunction, CodeStats> defaultCodeStats = build(false);
EnumMap<ComposableFunction, CodeStats> optimizedCodeStats = build(true);
for (ComposableFunction composableFunction : ComposableFunction.values()) {
CodeStats defaultCodeStatsForFunction = defaultCodeStats.get(composableFunction);
CodeStats optimizedCodeStatsForFunction = optimizedCodeStats.get(composableFunction);
assertTrue(
composableFunction
+ ": "
+ defaultCodeStatsForFunction.numberOfIfInstructions
+ " vs "
+ optimizedCodeStatsForFunction.numberOfIfInstructions,
defaultCodeStatsForFunction.numberOfIfInstructions
> optimizedCodeStatsForFunction.numberOfIfInstructions);
}
}
private EnumMap<ComposableFunction, CodeStats> build(boolean enableComposeOptimizations)
throws Exception {
Box<ClassReference> mainActivityKtClassReference =
new Box<>(Reference.classFromTypeName("com.example.MainActivityKt"));
R8TestCompileResult compileResult =
testForR8(Backend.DEX)
.addProgramFiles(dump.resolve("program.jar"))
.addClasspathFiles(dump.resolve("classpath.jar"))
.addLibraryFiles(dump.resolve("library.jar"))
.addKeepRuleFiles(dump.resolve("proguard.config"))
.addHorizontallyMergedClassesInspector(
updateMainActivityKt(
HorizontallyMergedClassesInspector::getTarget,
mainActivityKtClassReference,
false))
.addRepackagingInspector(
updateMainActivityKt(
RepackagingInspector::getTarget, mainActivityKtClassReference, true))
.addMinificationInspector(
updateMainActivityKt(
MinificationInspector::getTarget, mainActivityKtClassReference, true))
.addOptionsModification(
options -> {
options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces();
options
.getJetpackComposeOptions()
.enableAllOptimizations(enableComposeOptimizations);
})
.setMinApi(AndroidApiLevel.N)
.allowDiagnosticMessages()
.allowUnnecessaryDontWarnWildcards()
.allowUnusedDontWarnPatterns()
.allowUnusedProguardConfigurationRules()
.compile();
return createCodeStats(compileResult.inspector().clazz(mainActivityKtClassReference.get()));
}
private EnumMap<ComposableFunction, CodeStats> createCodeStats(
ClassSubject mainActivityKtClassSubject) {
EnumMap<ComposableFunction, CodeStats> result = new EnumMap<>(ComposableFunction.class);
result.put(
ComposableFunction.A,
new CodeStats(mainActivityKtClassSubject.uniqueMethodWithOriginalName("A")));
result.put(
ComposableFunction.B,
new CodeStats(mainActivityKtClassSubject.uniqueMethodWithOriginalName("B")));
result.put(
ComposableFunction.C,
new CodeStats(mainActivityKtClassSubject.uniqueMethodWithOriginalName("C")));
return result;
}
private static <T> ThrowableConsumer<T> updateMainActivityKt(
BiFunction<T, ClassReference, ClassReference> targetFn,
Box<ClassReference> mainActivityKtClassReference,
boolean failIfUnchanged) {
return inspector -> {
ClassReference targetClass = targetFn.apply(inspector, mainActivityKtClassReference.get());
if (failIfUnchanged) {
assertNotEquals(mainActivityKtClassReference.get(), targetClass);
}
mainActivityKtClassReference.set(targetClass);
};
}
}