Merge "Change internal tests to run on r8lib with dependencies"
diff --git a/build.gradle b/build.gradle
index 93d7e1b..7ff46eb 100644
--- a/build.gradle
+++ b/build.gradle
@@ -702,6 +702,11 @@
workingDir = projectDir
}
+task R8LibApiOnly {
+ dependsOn r8LibCreateTask("Api", "src/main/keep.txt", R8NoManifest, r8LibPath)
+ outputs.file r8LibPath
+}
+
task R8LibNoDeps {
dependsOn r8LibCreateTask(
"NoDeps",
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 228c0e5..ee45cf4 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -48,12 +48,15 @@
import com.android.tools.r8.shaking.MainDexListBuilder;
import com.android.tools.r8.shaking.ProguardClassFilter;
import com.android.tools.r8.shaking.ProguardConfiguration;
+import com.android.tools.r8.shaking.ProguardConfigurationRule;
+import com.android.tools.r8.shaking.ProguardConfigurationUtils;
import com.android.tools.r8.shaking.RootSetBuilder;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.shaking.StaticClassMerger;
import com.android.tools.r8.shaking.TreePruner;
import com.android.tools.r8.shaking.VerticalClassMerger;
import com.android.tools.r8.shaking.WhyAreYouKeepingConsumer;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.ExceptionUtils;
@@ -67,6 +70,7 @@
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.VersionProperties;
+import com.google.common.collect.Iterables;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
import java.io.ByteArrayOutputStream;
@@ -77,8 +81,10 @@
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -252,6 +258,7 @@
AppView<AppInfoWithSubtyping> appView =
new AppView<>(
new AppInfoWithSubtyping(application), GraphLense.getIdentityLense(), options);
+ List<ProguardConfigurationRule> synthesizedProguardRules = new ArrayList<>();
RootSet rootSet;
String proguardSeedsData = null;
timing.begin("Strip unused code");
@@ -278,10 +285,26 @@
ProguardConfiguration.Builder compatibility =
ProguardConfiguration.builder(application.dexItemFactory, options.reporter);
+ // Add synthesized -assumevalues from min api if relevant.
+ if (options.isGeneratingDex()) {
+ if (!ProguardConfigurationUtils.hasExplicitAssumeValuesRuleForMinSdk(
+ options.itemFactory,
+ options.getProguardConfiguration().getRules())) {
+ synthesizedProguardRules.add(
+ ProguardConfigurationUtils.buildAssumeValuesForApiLevel(
+ options.itemFactory,
+ AndroidApiLevel.getAndroidApiLevel(options.minApiLevel)));
+ }
+ }
+
rootSet =
new RootSetBuilder(
- appView, application, options.getProguardConfiguration().getRules(), options)
- .run(executorService);
+ appView,
+ application,
+ Iterables.concat(
+ options.getProguardConfiguration().getRules(), synthesizedProguardRules),
+ options
+ ).run(executorService);
Enqueuer enqueuer = new Enqueuer(appView, options, null, compatibility);
appView.setAppInfo(
@@ -608,6 +631,12 @@
assert application.classes().stream().allMatch(DexClass::isValid);
assert rootSet.verifyKeptItemsAreKept(application, appView.appInfo(), options);
+ // Report synthetic rules (only for testing).
+ // TODO(b/120959039): Move this to being reported through the graph consumer.
+ if (options.syntheticProguardRulesConsumer != null) {
+ options.syntheticProguardRulesConsumer.accept(synthesizedProguardRules);
+ }
+
// Generate the resulting application resources.
writeApplication(
executorService,
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index cca17bf..e4d1496 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -79,6 +79,7 @@
private final List<ProguardConfigurationSource> mainDexRules = new ArrayList<>();
private Consumer<ProguardConfiguration.Builder> proguardConfigurationConsumer = null;
+ private Consumer<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer = null;
private final List<ProguardConfigurationSource> proguardConfigs = new ArrayList<>();
private boolean disableTreeShaking = false;
private boolean disableMinification = false;
@@ -453,6 +454,7 @@
proguardCompatibilityRulesOutput,
keptGraphConsumer,
mainDexKeptGraphConsumer,
+ syntheticProguardRulesConsumer,
isOptimizeMultidexForLinearAlloc());
return command;
@@ -470,6 +472,15 @@
};
}
+ void addSyntheticProguardRulesConsumerForTesting(
+ Consumer<List<ProguardConfigurationRule>> consumer) {
+ syntheticProguardRulesConsumer =
+ syntheticProguardRulesConsumer == null
+ ? consumer
+ : syntheticProguardRulesConsumer.andThen(consumer);
+
+ }
+
// Internal for-testing method to add post-processors of the proguard configuration.
void allowPartiallyImplementedProguardOptions() {
allowPartiallyImplementedProguardOptions = true;
@@ -520,6 +531,7 @@
private final Path proguardCompatibilityRulesOutput;
private final GraphConsumer keptGraphConsumer;
private final GraphConsumer mainDexKeptGraphConsumer;
+ private final Consumer<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer;
/** Get a new {@link R8Command.Builder}. */
public static Builder builder() {
@@ -586,6 +598,7 @@
Path proguardCompatibilityRulesOutput,
GraphConsumer keptGraphConsumer,
GraphConsumer mainDexKeptGraphConsumer,
+ Consumer<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer,
boolean optimizeMultidexForLinearAlloc) {
super(inputApp, mode, programConsumer, mainDexListConsumer, minApiLevel, reporter,
enableDesugaring, optimizeMultidexForLinearAlloc);
@@ -601,6 +614,7 @@
this.proguardCompatibilityRulesOutput = proguardCompatibilityRulesOutput;
this.keptGraphConsumer = keptGraphConsumer;
this.mainDexKeptGraphConsumer = mainDexKeptGraphConsumer;
+ this.syntheticProguardRulesConsumer = syntheticProguardRulesConsumer;
}
private R8Command(boolean printHelp, boolean printVersion) {
@@ -615,6 +629,7 @@
proguardCompatibilityRulesOutput = null;
keptGraphConsumer = null;
mainDexKeptGraphConsumer = null;
+ syntheticProguardRulesConsumer = null;
}
/** Get the enable-tree-shaking state. */
@@ -723,6 +738,8 @@
internal.proguardCompatibilityRulesOutput = proguardCompatibilityRulesOutput;
internal.dataResourceConsumer = internal.programConsumer.getDataResourceConsumer();
+ internal.syntheticProguardRulesConsumer = syntheticProguardRulesConsumer;
+
// Default is to remove Java assertion code as Dalvik and Art does not reliable support
// Java assertions. When generation class file output always keep the Java assertions code.
assert internal.disableAssertions;
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index b500296..7686444 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "1.4.21-dev";
+ public static final String LABEL = "1.4.22-dev";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
index 668c490..9ae4986 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
@@ -80,8 +80,9 @@
return memberRules;
}
- public void setMemberRules(List<ProguardMemberRule> memberRules) {
+ public B setMemberRules(List<ProguardMemberRule> memberRules) {
this.memberRules = memberRules;
+ return self();
}
public boolean getInheritanceIsExtends() {
@@ -116,16 +117,18 @@
return classNames;
}
- public void setClassNames(ProguardClassNameList classNames) {
+ public B setClassNames(ProguardClassNameList classNames) {
this.classNames = classNames;
+ return self();
}
public ProguardClassType getClassType() {
return classType;
}
- public void setClassType(ProguardClassType classType) {
+ public B setClassType(ProguardClassType classType) {
this.classType = classType;
+ return self();
}
public boolean getClassTypeNegated() {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
index 0f1588f..163c60b 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
@@ -8,11 +8,14 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.LongInterval;
import com.google.common.collect.ImmutableList;
import java.util.Arrays;
import java.util.List;
@@ -156,4 +159,93 @@
builder.getMemberRules().add(memberRuleBuilder.build());
return builder.build();
}
+
+ public static ProguardAssumeValuesRule buildAssumeValuesForApiLevel(
+ DexItemFactory factory, AndroidApiLevel apiLevel) {
+ Origin synthesizedFromApiLevel =
+ new Origin(Origin.root()) {
+ @Override
+ public String part() {
+ return "<SYNTHESIZED_FROM_API_LEVEL_" + apiLevel.getLevel() + ">";
+ }
+ };
+
+ ProguardAccessFlags publicStaticFinalFlags = new ProguardAccessFlags();
+ publicStaticFinalFlags.setPublic();
+ publicStaticFinalFlags.setStatic();
+ publicStaticFinalFlags.setFinal();
+
+ return ProguardAssumeValuesRule
+ .builder()
+ .setOrigin(synthesizedFromApiLevel)
+ .setClassType(ProguardClassType.CLASS)
+ .setClassNames(
+ ProguardClassNameList.singletonList(
+ ProguardTypeMatcher.create(factory.createType("Landroid/os/Build$VERSION;"))))
+ .setMemberRules(ImmutableList.of(
+ ProguardMemberRule.builder()
+ .setAccessFlags(publicStaticFinalFlags)
+ .setRuleType(ProguardMemberType.FIELD)
+ .setTypeMatcher(ProguardTypeMatcher.create(factory.intType))
+ .setName(IdentifierPatternWithWildcards.withoutWildcards("SDK_INT"))
+ .setReturnValue(
+ new ProguardMemberRuleReturnValue(
+ new LongInterval(apiLevel.getLevel(), Integer.MAX_VALUE)))
+ .build()
+ ))
+ .build();
+ }
+
+ /**
+ * Check if an explicit rule matching the field
+ * public static final int android.os.Build$VERSION.SDK_INT
+ * is present.
+ */
+ public static boolean hasExplicitAssumeValuesRuleForMinSdk(
+ DexItemFactory factory, List<ProguardConfigurationRule> rules) {
+ for (ProguardConfigurationRule rule : rules) {
+ if (!(rule instanceof ProguardAssumeValuesRule)) {
+ continue;
+ }
+ if (rule.getClassType() != ProguardClassType.CLASS) {
+ continue;
+ }
+ if (rule.hasInheritanceClassName()
+ && !rule.getInheritanceClassName().matches(factory.objectType)) {
+ continue;
+ }
+ if (!rule.getClassNames().matches(factory.createType("Landroid/os/Build$VERSION;"))) {
+ continue;
+ }
+ for (ProguardMemberRule memberRule : rule.getMemberRules()) {
+ if (memberRule.getRuleType() == ProguardMemberType.ALL
+ || memberRule.getRuleType() == ProguardMemberType.ALL_FIELDS) {
+ return true;
+ }
+ if (memberRule.getRuleType() != ProguardMemberType.FIELD) {
+ continue;
+ }
+ if (memberRule.getAccessFlags().isProtected()
+ || memberRule.getAccessFlags().isPrivate()
+ || memberRule.getAccessFlags().isAbstract()
+ || memberRule.getAccessFlags().isTransient()
+ || memberRule.getAccessFlags().isVolatile()) {
+ continue;
+ }
+ if (memberRule.getNegatedAccessFlags().isPublic()
+ || memberRule.getNegatedAccessFlags().isStatic()
+ || memberRule.getNegatedAccessFlags().isFinal()) {
+ continue;
+ }
+ if (!memberRule.getType().matches(factory.intType)) {
+ continue;
+ }
+ if (!memberRule.getName().matches("SDK_INT")) {
+ continue;
+ }
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
index ae54a80..86b29a8 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
@@ -42,8 +42,9 @@
return accessFlags;
}
- public void setAccessFlags(ProguardAccessFlags flags) {
+ public Builder setAccessFlags(ProguardAccessFlags flags) {
accessFlags = flags;
+ return this;
}
public ProguardAccessFlags getNegatedAccessFlags() {
@@ -54,28 +55,32 @@
negatedAccessFlags = flags;
}
- public void setRuleType(ProguardMemberType ruleType) {
+ public Builder setRuleType(ProguardMemberType ruleType) {
this.ruleType = ruleType;
+ return this;
}
public ProguardTypeMatcher getTypeMatcher() {
return type;
}
- public void setTypeMatcher(ProguardTypeMatcher type) {
+ public Builder setTypeMatcher(ProguardTypeMatcher type) {
this.type = type;
+ return this;
}
- public void setName(IdentifierPatternWithWildcards identifierPatternWithWildcards) {
+ public Builder setName(IdentifierPatternWithWildcards identifierPatternWithWildcards) {
this.name = ProguardNameMatcher.create(identifierPatternWithWildcards);
+ return this;
}
public void setArguments(List<ProguardTypeMatcher> arguments) {
this.arguments = arguments;
}
- public void setReturnValue(ProguardMemberRuleReturnValue value) {
+ public Builder setReturnValue(ProguardMemberRuleReturnValue value) {
returnValue = value;
+ return this;
}
public boolean isValid() {
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index e86966f..46d3a68 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -60,7 +60,7 @@
private final AppView<? extends AppInfo> appView;
private final DirectMappedDexApplication application;
- private final Collection<ProguardConfigurationRule> rules;
+ private final Iterable<? extends ProguardConfigurationRule> rules;
private final Map<DexDefinition, Set<ProguardKeepRule>> noShrinking = new IdentityHashMap<>();
private final Set<DexDefinition> noOptimization = Sets.newIdentityHashSet();
private final Set<DexDefinition> noObfuscation = Sets.newIdentityHashSet();
@@ -89,11 +89,11 @@
public RootSetBuilder(
AppView<? extends AppInfo> appView,
DexApplication application,
- Collection<? extends ProguardConfigurationRule> rules,
+ Iterable<? extends ProguardConfigurationRule> rules,
InternalOptions options) {
this.appView = appView;
this.application = application.asDirect();
- this.rules = rules == null ? null : Collections.unmodifiableCollection(rules);
+ this.rules = rules;
this.options = options;
}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 1e39494..4921c79 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -34,6 +34,7 @@
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
+import java.util.function.Consumer;
public class InternalOptions {
@@ -355,6 +356,7 @@
public GraphConsumer mainDexKeptGraphConsumer = null;
public Path proguardCompatibilityRulesOutput = null;
+ public Consumer<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer = null;
public static boolean assertionsEnabled() {
boolean assertionsEnabled = false;
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 1c39971..bb5677f 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -9,6 +9,8 @@
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.CollectingGraphConsumer;
+import com.android.tools.r8.shaking.ProguardConfiguration;
+import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import java.nio.file.Path;
@@ -67,9 +69,26 @@
builder.setDisableTreeShaking(!enableTreeShaking);
builder.setDisableMinification(!enableMinification);
builder.setProguardMapConsumer((string, ignore) -> proguardMapBuilder.append(string));
- ToolHelper.runR8WithoutResult(builder.build(), optionsConsumer);
+
+ class Box {
+ private List<ProguardConfigurationRule> syntheticProguardRules;
+ private ProguardConfiguration proguardConfiguration;
+ }
+ Box box = new Box();
+ ToolHelper.addSyntheticProguardRulesConsumerForTesting(
+ builder, rules -> box.syntheticProguardRules = rules);
+ ToolHelper.runR8WithoutResult(
+ builder.build(),
+ optionsConsumer.andThen(
+ options -> box.proguardConfiguration = options.getProguardConfiguration()));
return new R8TestCompileResult(
- getState(), backend, app.get(), proguardMapBuilder.toString(), graphConsumer);
+ getState(),
+ backend,
+ app.get(),
+ box.proguardConfiguration,
+ box.syntheticProguardRules,
+ proguardMapBuilder.toString(),
+ graphConsumer);
}
public R8TestBuilder addDataResources(List<DataEntryResource> resources) {
diff --git a/src/test/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
index 8a4c44d..a9c02ff 100644
--- a/src/test/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
@@ -6,15 +6,21 @@
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.shaking.CollectingGraphConsumer;
+import com.android.tools.r8.shaking.ProguardConfiguration;
+import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.graphinspector.GraphInspector;
import java.io.IOException;
+import java.util.List;
import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
public class R8TestCompileResult extends TestCompileResult<R8TestCompileResult, R8TestRunResult> {
private final Backend backend;
+ private final ProguardConfiguration proguardConfiguration;
+ private final List<ProguardConfigurationRule> syntheticProguardRules;
private final String proguardMap;
private final CollectingGraphConsumer graphConsumer;
@@ -22,10 +28,14 @@
TestState state,
Backend backend,
AndroidApp app,
+ ProguardConfiguration proguardConfiguration,
+ List<ProguardConfigurationRule> syntheticProguardRules,
String proguardMap,
CollectingGraphConsumer graphConsumer) {
super(state, app);
this.backend = backend;
+ this.proguardConfiguration = proguardConfiguration;
+ this.syntheticProguardRules = syntheticProguardRules;
this.proguardMap = proguardMap;
this.graphConsumer = graphConsumer;
}
@@ -55,6 +65,26 @@
return new GraphInspector(graphConsumer, inspector());
}
+ public ProguardConfiguration getProguardConfiguration() {
+ return proguardConfiguration;
+ }
+
+ public R8TestCompileResult inspectProguardConfiguration(
+ Consumer<ProguardConfiguration> consumer) {
+ consumer.accept(getProguardConfiguration());
+ return self();
+ }
+
+ public List<ProguardConfigurationRule> getSyntheticProguardRules() {
+ return syntheticProguardRules;
+ }
+
+ public R8TestCompileResult inspectSyntheticProguardRules(
+ Consumer<List<ProguardConfigurationRule>> consumer) {
+ consumer.accept(getSyntheticProguardRules());
+ return self();
+ }
+
@Override
public R8TestRunResult createRunResult(ProcessResult result) {
return new R8TestRunResult(app, result, proguardMap, this::graphInspector);
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index 5d5229b..0c84e7c 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -15,13 +15,17 @@
import com.android.tools.r8.debug.DebugTestConfig;
import com.android.tools.r8.debug.DexDebugTestConfig;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.invokesuper.Consumer;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
import org.hamcrest.Matcher;
public abstract class TestCompileResult<
@@ -29,6 +33,7 @@
final TestState state;
public final AndroidApp app;
+ final List<Path> additionalRunClassPath = new ArrayList<>();
TestCompileResult(TestState state, AndroidApp app) {
this.state = state;
@@ -50,14 +55,19 @@
public RR run(String mainClass) throws IOException {
switch (getBackend()) {
case DEX:
- return runArt(mainClass);
+ return runArt(additionalRunClassPath, mainClass);
case CF:
- return runJava(mainClass);
+ return runJava(additionalRunClassPath, mainClass);
default:
throw new Unreachable();
}
}
+ public CR addRunClasspath(List<Path> classpath) {
+ additionalRunClassPath.addAll(classpath);
+ return self();
+ }
+
public CR writeToZip(Path file) throws IOException {
app.writeToZip(file, getBackend() == DEX ? OutputMode.DexIndexed : OutputMode.ClassFile);
return self();
@@ -160,17 +170,25 @@
}
}
- private RR runJava(String mainClass) throws IOException {
+ private RR runJava(List<Path> additionalClassPath, String mainClass) throws IOException {
Path out = state.getNewTempFolder().resolve("out.zip");
app.writeToZip(out, OutputMode.ClassFile);
- ProcessResult result = ToolHelper.runJava(out, mainClass);
+ List<Path> classPath = ImmutableList.<Path>builder()
+ .addAll(additionalClassPath)
+ .add(out)
+ .build();
+ ProcessResult result = ToolHelper.runJava(classPath, mainClass);
return createRunResult(result);
}
- private RR runArt(String mainClass) throws IOException {
+ private RR runArt(List<Path> additionalClassPath, String mainClass) throws IOException {
Path out = state.getNewTempFolder().resolve("out.zip");
app.writeToZip(out, OutputMode.DexIndexed);
- ProcessResult result = ToolHelper.runArtRaw(out.toString(), mainClass);
+ List<String> classPath = ImmutableList.<String>builder()
+ .addAll(additionalClassPath.stream().map(Path::toString).collect(Collectors.toList()))
+ .add(out.toString())
+ .build();
+ ProcessResult result = ToolHelper.runArtRaw(classPath, mainClass, dummy -> {});
return createRunResult(result);
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 6f42792..64c735d 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.shaking.FilteredClassPath;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.ProguardConfigurationParser;
+import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.AndroidAppConsumers;
@@ -1695,6 +1696,12 @@
return builder;
}
+ public static R8Command.Builder addSyntheticProguardRulesConsumerForTesting(
+ R8Command.Builder builder, Consumer<List<ProguardConfigurationRule>> consumer) {
+ builder.addSyntheticProguardRulesConsumerForTesting(consumer);
+ return builder;
+ }
+
public static R8Command.Builder allowPartiallyImplementedProguardOptions(
R8Command.Builder builder) {
builder.allowPartiallyImplementedProguardOptions();
diff --git a/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java b/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
new file mode 100644
index 0000000..dd11dc1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
@@ -0,0 +1,327 @@
+// 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.shaking.assumevalues;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
+import com.android.tools.r8.shaking.ProguardAssumeValuesRule;
+import com.android.tools.r8.shaking.ProguardConfiguration;
+import com.android.tools.r8.shaking.ProguardConfigurationRule;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.function.Consumer;
+import org.junit.Assume;
+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 SynthesizedRulesFromApiLevelTest extends TestBase {
+
+ private final Backend backend;
+ private final String mainClassName = "MainClass";
+ private final String compatLibraryClassName = "CompatLibrary";
+
+ public SynthesizedRulesFromApiLevelTest(Backend backend) {
+ this.backend = backend;
+ }
+
+ @Parameters(name = "Backend: {0}")
+ public static Backend[] data() {
+ return Backend.values();
+ }
+
+ // Simple mock implementation of class android.os.Build$VERSION with just the SDK_INT field.
+ private Path mockAndroidRuntimeLibrary(int sdkInt) throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ ClassBuilder classBuilder;
+
+ classBuilder = builder.addClass("android.os.Build$VERSION");
+ classBuilder.addStaticFinalField("SDK_INT", "I", Integer.toString(sdkInt));
+
+ classBuilder = builder.addClass("android.os.Native");
+ classBuilder.addStaticMethod("method", ImmutableList.of(), "V",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc \" Native\"",
+ " invokevirtual java/io/PrintStream/print(Ljava.lang.String;)V",
+ " return"
+ );
+
+ return writeToJar(builder);
+ }
+
+ private Path buildMockAndroidRuntimeLibrary(AndroidApiLevel apiLevel) throws Exception {
+ // Build the mock library containing android.os.Build.VERSION with D8.
+ Path library = temp.newFolder().toPath().resolve("library.jar");
+ D8.run(
+ D8Command
+ .builder()
+ .addProgramFiles(mockAndroidRuntimeLibrary(apiLevel.getLevel()))
+ .setOutput(library, OutputMode.DexIndexed)
+ .build());
+ return library;
+ }
+
+ private Path buildApp(AndroidApiLevel apiLevelForNative) throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ ClassBuilder classBuilder;
+
+ classBuilder = builder.addClass(compatLibraryClassName);
+
+ classBuilder.addStaticMethod("compatMethod", ImmutableList.of(), "V",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc \" Compat\"",
+ " invokevirtual java/io/PrintStream/print(Ljava.lang.String;)V",
+ " return"
+ );
+
+ classBuilder.addStaticMethod("method", ImmutableList.of(), "V",
+ ".limit stack 2",
+ " getstatic android/os/Build$VERSION/SDK_INT I",
+ " ldc " + apiLevelForNative.getLevel(),
+ "if_icmpge Native",
+ " invokestatic " + compatLibraryClassName +"/compatMethod()V",
+ " return",
+ "Native:",
+ " invokestatic android.os.Native/method()V",
+ " return"
+ );
+
+ classBuilder = builder.addClass(mainClassName);
+
+ classBuilder.addStaticMethod("getSdkInt", ImmutableList.of(), "I",
+ ".limit stack 1",
+ "getstatic android/os/Build$VERSION/SDK_INT I",
+ "ireturn"
+ );
+
+ classBuilder.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+
+ "getstatic java/lang/System/out Ljava/io/PrintStream;",
+ "invokestatic " + mainClassName + "/getSdkInt()I",
+ "invokevirtual java/io/PrintStream/print(I)V",
+
+ "invokestatic " + compatLibraryClassName + "/method()V",
+
+ "return");
+
+ return writeToJar(builder);
+ }
+
+ private enum SynthesizedRule {
+ PRESENT,
+ NOT_PRESENT
+ }
+
+ private void checkSynthesizedRuleExpectation(
+ List<ProguardConfigurationRule> synthesizedRules, SynthesizedRule expected) {
+ for (ProguardConfigurationRule rule : synthesizedRules) {
+ if (rule instanceof ProguardAssumeValuesRule
+ && rule.getOrigin().part().contains("SYNTHESIZED_FROM_API_LEVEL")) {
+ assertEquals(expected, SynthesizedRule.PRESENT);
+ return;
+ }
+ }
+ assertEquals(expected, SynthesizedRule.NOT_PRESENT);
+ }
+
+ private void noSynthesizedRules(ProguardConfiguration proguardConfiguration) {
+ for (ProguardConfigurationRule rule : proguardConfiguration.getRules()) {
+ if (rule instanceof ProguardAssumeValuesRule) {
+ assertFalse(rule.getOrigin().part().contains("SYNTHESIZED_FROM_API_LEVEL"));
+ }
+ }
+ }
+
+ private void runTest(
+ AndroidApiLevel buildApiLevel,
+ AndroidApiLevel runtimeApiLevel,
+ AndroidApiLevel nativeApiLevel,
+ String expectedOutput,
+ Consumer<CodeInspector> inspector,
+ List<String> additionalKeepRules,
+ SynthesizedRule synthesizedRule) throws Exception{
+ assertTrue(runtimeApiLevel.getLevel() >= buildApiLevel.getLevel());
+ if (backend == Backend.DEX) {
+ testForR8(backend)
+ .setMinApi(buildApiLevel)
+ .addProgramFiles(buildApp(nativeApiLevel))
+ .enableProguardTestOptions()
+ .addKeepRules("-neverinline class " + compatLibraryClassName + " { *; }")
+ .addKeepMainRule(mainClassName)
+ .addKeepRules(additionalKeepRules)
+ .compile()
+ .inspectSyntheticProguardRules(
+ syntheticProguardRules ->
+ checkSynthesizedRuleExpectation(syntheticProguardRules, synthesizedRule))
+ .inspect(inspector)
+ .addRunClasspath(ImmutableList.of(buildMockAndroidRuntimeLibrary(runtimeApiLevel)))
+ .run(mainClassName)
+ .assertSuccessWithOutput(expectedOutput);
+ } else {
+ assert backend == Backend.CF;
+ testForR8(backend)
+ .addProgramFiles(buildApp(nativeApiLevel))
+ .addKeepMainRule(mainClassName)
+ .addKeepRules(additionalKeepRules)
+ .compile()
+ .inspectProguardConfiguration(this::noSynthesizedRules)
+ .addRunClasspath(
+ ImmutableList.of(mockAndroidRuntimeLibrary(AndroidApiLevel.D.getLevel())))
+ .run(mainClassName)
+ .assertSuccessWithOutput(expectedResultForCompat(AndroidApiLevel.D));
+ }
+ }
+
+ private void runTest(
+ AndroidApiLevel buildApiLevel,
+ AndroidApiLevel runtimeApiLevel,
+ AndroidApiLevel nativeApiLevel,
+ String expectedOutput,
+ Consumer<CodeInspector> inspector) throws Exception{
+ runTest(
+ buildApiLevel,
+ runtimeApiLevel,
+ nativeApiLevel,
+ expectedOutput,
+ inspector,
+ ImmutableList.of(),
+ SynthesizedRule.PRESENT);
+ }
+
+ private String expectedResultForNative(AndroidApiLevel runtimeApiLevel) {
+ return runtimeApiLevel.getLevel() + " Native";
+ }
+
+ private String expectedResultForCompat(AndroidApiLevel runtimeApiLevel) {
+ return runtimeApiLevel.getLevel() + " Compat";
+ }
+
+ private void compatCodePresent(CodeInspector inspector) {
+ ClassSubject compatLibrary = inspector.clazz(compatLibraryClassName);
+ assertThat(compatLibrary, isPresent());
+ assertThat(compatLibrary.uniqueMethodWithName("compatMethod"), isPresent());
+ }
+
+ private void compatCodeNotPresent(CodeInspector inspector) {
+ ClassSubject compatLibrary = inspector.clazz(compatLibraryClassName);
+ assertThat(compatLibrary, isPresent());
+ assertThat(compatLibrary.uniqueMethodWithName("compatMethod"), not(isPresent()));
+ }
+
+ @Test
+ public void testNoExplicitAssumeValuesRuleNative() throws Exception {
+ Assume.assumeTrue(ToolHelper.getDexVm().isNewerThan(DexVm.ART_7_0_0_HOST));
+ runTest(
+ AndroidApiLevel.O_MR1,
+ AndroidApiLevel.O_MR1,
+ AndroidApiLevel.O_MR1,
+ expectedResultForNative(AndroidApiLevel.O_MR1),
+ this::compatCodeNotPresent);
+ }
+
+ @Test
+ public void testNoExplicitAssumeValuesRuleCompatPresent() throws Exception {
+ Assume.assumeTrue(ToolHelper.getDexVm().isNewerThan(DexVm.ART_7_0_0_HOST));
+ runTest(
+ AndroidApiLevel.O,
+ AndroidApiLevel.O_MR1,
+ AndroidApiLevel.O_MR1,
+ expectedResultForNative(AndroidApiLevel.O_MR1),
+ this::compatCodePresent);
+ }
+
+ @Test
+ public void testNoExplicitAssumeValuesRuleCompatUsed() throws Exception {
+ Assume.assumeTrue(ToolHelper.getDexVm().isNewerThan(DexVm.ART_7_0_0_HOST));
+ runTest(
+ AndroidApiLevel.O,
+ AndroidApiLevel.O,
+ AndroidApiLevel.O_MR1,
+ expectedResultForCompat(AndroidApiLevel.O),
+ this::compatCodePresent);
+ }
+
+ @Test
+ public void testExplicitAssumeValuesRuleWhichMatchAndDontKeepCompat() throws Exception {
+ Assume.assumeTrue(ToolHelper.getDexVm().isNewerThan(DexVm.ART_7_0_0_HOST));
+ runTest(
+ AndroidApiLevel.O_MR1,
+ AndroidApiLevel.O_MR1,
+ AndroidApiLevel.O_MR1,
+ expectedResultForNative(AndroidApiLevel.O_MR1),
+ this::compatCodeNotPresent,
+ ImmutableList.of(
+ "-assumevalues class android.os.Build$VERSION { public static final int SDK_INT return "
+ + AndroidApiLevel.O_MR1.getLevel() + "..1000; }"),
+ SynthesizedRule.NOT_PRESENT);
+ }
+
+ @Test
+ public void testExplicitAssumeValuesRulesWhichMatchAndKeepCompat() throws Exception {
+ Assume.assumeTrue(ToolHelper.getDexVm().isNewerThan(DexVm.ART_7_0_0_HOST));
+ String[] rules = new String[] {
+ "-assumevalues class * { int SDK_INT return 1..1000; }",
+ "-assumevalues class * { % SDK_INT return 1..1000; }",
+ "-assumevalues class * { int * return 1..1000; }",
+ "-assumevalues class * extends java.lang.Object { int SDK_INT return 1..1000; }",
+ "-assumevalues class * { <fields>; }",
+ "-assumevalues class * { *; }"
+ };
+
+ for (String rule : rules) {
+ runTest(
+ AndroidApiLevel.O_MR1,
+ AndroidApiLevel.O_MR1,
+ AndroidApiLevel.O_MR1,
+ expectedResultForNative(AndroidApiLevel.O_MR1),
+ this::compatCodePresent,
+ ImmutableList.of(rule),
+ SynthesizedRule.NOT_PRESENT);
+ }
+ }
+
+ @Test
+ public void testExplicitAssumeValuesRulesWhichDoesNotMatch() throws Exception {
+ Assume.assumeTrue(ToolHelper.getDexVm().isNewerThan(DexVm.ART_7_0_0_HOST));
+ String[] rules = new String[] {
+ "-assumevalues class * { !public int SDK_INT return 1..1000; }",
+ "-assumevalues class * { !static int SDK_INT return 1..1000; }",
+ "-assumevalues class * { !final int SDK_INT return 1..1000; }",
+ "-assumevalues class * { protected int SDK_INT return 1..1000; }",
+ "-assumevalues class * { private int SDK_INT return 1..1000; }"
+ };
+ for (String rule : rules) {
+ runTest(
+ AndroidApiLevel.O_MR1,
+ AndroidApiLevel.O_MR1,
+ AndroidApiLevel.O_MR1,
+ expectedResultForNative(AndroidApiLevel.O_MR1),
+ this::compatCodeNotPresent,
+ ImmutableList.of(rule),
+ SynthesizedRule.PRESENT);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/fields/FieldsTestBase.java b/src/test/java/com/android/tools/r8/shaking/fields/FieldsTestBase.java
index e5f9799..ce9b84d 100644
--- a/src/test/java/com/android/tools/r8/shaking/fields/FieldsTestBase.java
+++ b/src/test/java/com/android/tools/r8/shaking/fields/FieldsTestBase.java
@@ -6,12 +6,12 @@
import com.android.tools.r8.NeverMerge;
import com.android.tools.r8.TestBase;
-import com.android.tools.r8.graph.invokesuper.Consumer;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import java.util.List;
+import java.util.function.Consumer;
public abstract class FieldsTestBase extends TestBase {
diff --git a/tools/golem_build.py b/tools/golem_build.py
index d55def1..30d46bc 100755
--- a/tools/golem_build.py
+++ b/tools/golem_build.py
@@ -8,8 +8,8 @@
import gradle
import sys
-GRADLE_ARGS = ['--no-daemon', '-Pno_internal']
-BUILD_TARGETS = ['R8', 'D8', 'R8Lib', 'buildExampleJars', 'CompatDx',
+GRADLE_ARGS = ['--no-daemon']
+BUILD_TARGETS = ['R8', 'D8', 'R8LibApiOnly', 'buildExampleJars', 'CompatDx',
'downloadAndroidCts', 'downloadDx']
def Main():