Warn when startup items are not in program input
Change-Id: I9abcaf92f84c4b64bc627a43fe8b8a819327d970
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 55b4e69..3bd5968 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -25,7 +25,6 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.experimental.startup.StartupCompleteness;
import com.android.tools.r8.experimental.startup.StartupOrder;
-import com.android.tools.r8.experimental.startup.profile.art.ARTProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
import com.android.tools.r8.features.FeatureSplitConfiguration.DataResourceProvidersAndConsumer;
import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.graph.AppView;
@@ -226,8 +225,7 @@
StartupOrder startupOrder =
appView.appInfo().hasClassHierarchy()
? appView.appInfoWithClassHierarchy().getStartupOrder()
- : StartupOrder.createInitialStartupOrder(
- options, SyntheticToSyntheticContextGeneralization.createForD8());
+ : StartupOrder.createInitialStartupOrderForD8(appView);
distributor =
new VirtualFile.FillFilesDistributor(
this, classes, options, executorService, startupOrder);
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java
index 9a44961..291e8f2 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java
@@ -37,7 +37,7 @@
appView.hasClassHierarchy()
? appView.appInfoWithClassHierarchy().getStartupOrder()
: StartupOrder.createInitialStartupOrder(
- appView.options(), syntheticToSyntheticContextGeneralization);
+ appView.options(), null, syntheticToSyntheticContextGeneralization);
}
/**
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
index 5fba5e9..d80396d 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
@@ -8,6 +8,8 @@
import com.android.tools.r8.experimental.startup.profile.StartupProfile;
import com.android.tools.r8.experimental.startup.profile.art.ARTProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.PrunedItems;
@@ -22,15 +24,27 @@
public static StartupOrder createInitialStartupOrder(
InternalOptions options,
+ DexDefinitionSupplier definitions,
SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
StartupProfile startupProfile =
- StartupProfile.parseStartupProfile(options, syntheticToSyntheticContextGeneralization);
+ StartupProfile.parseStartupProfile(
+ options, definitions, syntheticToSyntheticContextGeneralization);
if (startupProfile == null || startupProfile.getStartupItems().isEmpty()) {
return empty();
}
return new NonEmptyStartupOrder(new LinkedHashSet<>(startupProfile.getStartupItems()));
}
+ public static StartupOrder createInitialStartupOrderForD8(AppView<?> appView) {
+ return createInitialStartupOrder(
+ appView.options(), appView, SyntheticToSyntheticContextGeneralization.createForD8());
+ }
+
+ public static StartupOrder createInitialStartupOrderForR8(DexApplication application) {
+ return createInitialStartupOrder(
+ application.options, application, SyntheticToSyntheticContextGeneralization.createForR8());
+ }
+
public static StartupOrder empty() {
return new EmptyStartupOrder();
}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfile.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfile.java
index f23f459..842e181 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfile.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfile.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.experimental.startup.profile.art.ARTProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
import com.android.tools.r8.experimental.startup.profile.art.AlwaysTrueARTProfileRulePredicate;
import com.android.tools.r8.experimental.startup.profile.art.HumanReadableARTProfileParser;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.startup.ARTProfileRulePredicate;
import com.android.tools.r8.startup.HumanReadableARTProfileParserBuilder;
@@ -17,6 +18,8 @@
import com.android.tools.r8.startup.StartupProfileBuilder;
import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.startup.SyntheticStartupMethodBuilder;
+import com.android.tools.r8.startup.diagnostic.MissingStartupProfileItemsDiagnostic;
+import com.android.tools.r8.startup.diagnostic.MissingStartupProfileItemsDiagnostic.Builder;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.InternalOptions;
import java.util.ArrayList;
@@ -35,9 +38,14 @@
public static Builder builder(
InternalOptions options,
+ MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder,
StartupProfileProvider startupProfileProvider,
SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
- return new Builder(options, startupProfileProvider, syntheticToSyntheticContextGeneralization);
+ return new Builder(
+ options,
+ missingItemsDiagnosticBuilder,
+ startupProfileProvider,
+ syntheticToSyntheticContextGeneralization);
}
public static StartupProfile merge(Collection<StartupProfile> startupProfiles) {
@@ -65,6 +73,7 @@
*/
public static StartupProfile parseStartupProfile(
InternalOptions options,
+ DexDefinitionSupplier definitions,
SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
if (!options.getStartupOptions().hasStartupProfileProviders()) {
return null;
@@ -73,11 +82,20 @@
options.getStartupOptions().getStartupProfileProviders();
List<StartupProfile> startupProfiles = new ArrayList<>(startupProfileProviders.size());
for (StartupProfileProvider startupProfileProvider : startupProfileProviders) {
+ MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder =
+ new MissingStartupProfileItemsDiagnostic.Builder(definitions)
+ .setOrigin(startupProfileProvider.getOrigin());
StartupProfile.Builder startupProfileBuilder =
StartupProfile.builder(
- options, startupProfileProvider, syntheticToSyntheticContextGeneralization);
+ options,
+ missingItemsDiagnosticBuilder,
+ startupProfileProvider,
+ syntheticToSyntheticContextGeneralization);
startupProfileProvider.getStartupProfile(startupProfileBuilder);
startupProfiles.add(startupProfileBuilder.build());
+ if (missingItemsDiagnosticBuilder.hasMissingStartupItems()) {
+ options.reporter.warning(missingItemsDiagnosticBuilder.build());
+ }
}
return StartupProfile.merge(startupProfiles);
}
@@ -89,6 +107,7 @@
public static class Builder implements StartupProfileBuilder {
private final DexItemFactory dexItemFactory;
+ private final MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder;
private final InternalOptions options;
private final StartupProfileProvider startupProfileProvider;
private final SyntheticToSyntheticContextGeneralization
@@ -98,9 +117,11 @@
Builder(
InternalOptions options,
+ MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder,
StartupProfileProvider startupProfileProvider,
SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
this.dexItemFactory = options.dexItemFactory();
+ this.missingItemsDiagnosticBuilder = missingItemsDiagnosticBuilder;
this.options = options;
this.startupProfileProvider = startupProfileProvider;
this.syntheticToSyntheticContextGeneralization = syntheticToSyntheticContextGeneralization;
@@ -110,14 +131,22 @@
public Builder addStartupClass(Consumer<StartupClassBuilder> startupClassBuilderConsumer) {
StartupClass.Builder startupClassBuilder = StartupClass.builder(dexItemFactory);
startupClassBuilderConsumer.accept(startupClassBuilder);
- return addStartupItem(startupClassBuilder.build());
+ StartupClass startupClass = startupClassBuilder.build();
+ if (missingItemsDiagnosticBuilder.registerStartupClass(startupClass)) {
+ return this;
+ }
+ return addStartupItem(startupClass);
}
@Override
public Builder addStartupMethod(Consumer<StartupMethodBuilder> startupMethodBuilderConsumer) {
StartupMethod.Builder startupMethodBuilder = StartupMethod.builder(dexItemFactory);
startupMethodBuilderConsumer.accept(startupMethodBuilder);
- return addStartupItem(startupMethodBuilder.build());
+ StartupMethod startupMethod = startupMethodBuilder.build();
+ if (missingItemsDiagnosticBuilder.registerStartupMethod(startupMethod)) {
+ return this;
+ }
+ return addStartupItem(startupMethod);
}
@Override
@@ -126,7 +155,11 @@
SyntheticStartupMethod.Builder syntheticStartupMethodBuilder =
SyntheticStartupMethod.builder(dexItemFactory);
syntheticStartupMethodBuilderConsumer.accept(syntheticStartupMethodBuilder);
- return addStartupItem(syntheticStartupMethodBuilder.build());
+ SyntheticStartupMethod syntheticStartupMethod = syntheticStartupMethodBuilder.build();
+ if (missingItemsDiagnosticBuilder.registerSyntheticStartupMethod(syntheticStartupMethod)) {
+ return this;
+ }
+ return addStartupItem(syntheticStartupMethod);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 7651bd5..3633506 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
import com.android.tools.r8.errors.dontwarn.DontWarnConfiguration;
import com.android.tools.r8.experimental.startup.StartupOrder;
-import com.android.tools.r8.experimental.startup.profile.art.ARTProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
@@ -209,9 +208,7 @@
DexApplication application, MainDexInfo mainDexInfo) {
ClassToFeatureSplitMap classToFeatureSplitMap =
ClassToFeatureSplitMap.createInitialClassToFeatureSplitMap(application.options);
- StartupOrder startupOrder =
- StartupOrder.createInitialStartupOrder(
- application.options, SyntheticToSyntheticContextGeneralization.createForR8());
+ StartupOrder startupOrder = StartupOrder.createInitialStartupOrderForR8(application);
AppInfoWithClassHierarchy appInfo =
AppInfoWithClassHierarchy.createInitialAppInfoWithClassHierarchy(
application,
diff --git a/src/main/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnostic.java b/src/main/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnostic.java
new file mode 100644
index 0000000..716480d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnostic.java
@@ -0,0 +1,121 @@
+// Copyright (c) 2022, 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.startup.diagnostic;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.experimental.startup.profile.StartupClass;
+import com.android.tools.r8.experimental.startup.profile.StartupMethod;
+import com.android.tools.r8.experimental.startup.profile.SyntheticStartupMethod;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+@Keep
+public class MissingStartupProfileItemsDiagnostic implements Diagnostic {
+
+ private final List<DexReference> missingStartupItems;
+ private final Origin origin;
+
+ MissingStartupProfileItemsDiagnostic(List<DexReference> missingStartupItems, Origin origin) {
+ assert !missingStartupItems.isEmpty();
+ this.missingStartupItems = missingStartupItems;
+ this.origin = origin;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ @Override
+ public Position getPosition() {
+ return Position.UNKNOWN;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ StringBuilder builder = new StringBuilder();
+ Iterator<DexReference> missingStartupItemIterator = missingStartupItems.iterator();
+
+ // Write first missing startup item.
+ writeMissingStartupItem(builder, missingStartupItemIterator.next());
+
+ // Write remaining missing startup items with line separator before.
+ while (missingStartupItemIterator.hasNext()) {
+ writeMissingStartupItem(
+ builder.append(System.lineSeparator()), missingStartupItemIterator.next());
+ }
+
+ return builder.toString();
+ }
+
+ private void writeMissingStartupItem(StringBuilder builder, DexReference missingStartupItem) {
+ missingStartupItem.apply(
+ missingStartupClass -> builder.append("Startup class not found: "),
+ missingStartupField -> builder.append("Startup field not found: "),
+ missingStartupMethod -> builder.append("Startup method not found: "));
+ builder.append(missingStartupItem.toSourceString());
+ }
+
+ public static class Builder {
+
+ private final DexDefinitionSupplier definitions;
+ private final Set<DexReference> missingStartupItems = Sets.newIdentityHashSet();
+
+ private Origin origin;
+
+ public Builder(DexDefinitionSupplier definitions) {
+ this.definitions = definitions;
+ }
+
+ public boolean hasMissingStartupItems() {
+ return !missingStartupItems.isEmpty();
+ }
+
+ public boolean registerStartupClass(StartupClass startupClass) {
+ if (definitions != null && definitions.definitionFor(startupClass.getReference()) == null) {
+ missingStartupItems.add(startupClass.getReference());
+ return true;
+ }
+ return false;
+ }
+
+ public boolean registerStartupMethod(StartupMethod startupMethod) {
+ if (definitions != null && definitions.definitionFor(startupMethod.getReference()) == null) {
+ missingStartupItems.add(startupMethod.getReference());
+ return true;
+ }
+ return false;
+ }
+
+ public boolean registerSyntheticStartupMethod(SyntheticStartupMethod syntheticStartupMethod) {
+ if (definitions != null
+ && definitions.definitionFor(syntheticStartupMethod.getSyntheticContextType()) == null) {
+ missingStartupItems.add(syntheticStartupMethod.getSyntheticContextType());
+ return true;
+ }
+ return false;
+ }
+
+ public Builder setOrigin(Origin origin) {
+ this.origin = origin;
+ return this;
+ }
+
+ public MissingStartupProfileItemsDiagnostic build() {
+ assert hasMissingStartupItems();
+ List<DexReference> sortedMissingStartupItems = new ArrayList<>(missingStartupItems);
+ sortedMissingStartupItems.sort(DexReference::compareTo);
+ return new MissingStartupProfileItemsDiagnostic(sortedMissingStartupItems, origin);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnosticTest.java b/src/test/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnosticTest.java
new file mode 100644
index 0000000..f687df4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnosticTest.java
@@ -0,0 +1,118 @@
+// Copyright (c) 2022, 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.startup.diagnostic;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.startup.StartupProfileBuilder;
+import com.android.tools.r8.startup.StartupProfileProvider;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Collection;
+import java.util.Collections;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MissingStartupProfileItemsDiagnosticTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ testForD8(Backend.DEX)
+ .addProgramClasses(Main.class)
+ .addOptionsModification(
+ options ->
+ options
+ .getStartupOptions()
+ .setStartupProfileProviders(getStartupProfileProviders()))
+ .release()
+ .setIntermediate(true)
+ .setMinApi(AndroidApiLevel.LATEST)
+ .compileWithExpectedDiagnostics(this::inspectDiagnostics);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(Backend.DEX)
+ .addProgramClasses(Main.class)
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options ->
+ options
+ .getStartupOptions()
+ .setStartupProfileProviders(getStartupProfileProviders()))
+ .allowDiagnosticWarningMessages()
+ .setMinApi(AndroidApiLevel.LATEST)
+ .compileWithExpectedDiagnostics(this::inspectDiagnostics);
+ }
+
+ private static Collection<StartupProfileProvider> getStartupProfileProviders() {
+ StartupProfileProvider startupProfileProvider =
+ new StartupProfileProvider() {
+ @Override
+ public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+ ClassReference fooClassReference = Reference.classFromTypeName("Foo");
+ ClassReference barClassReference = Reference.classFromTypeName("Bar");
+ ClassReference bazClassReference = Reference.classFromTypeName("Baz");
+ startupProfileBuilder
+ .addStartupClass(
+ startupClassBuilder -> startupClassBuilder.setClassReference(fooClassReference))
+ .addStartupMethod(
+ startupMethodBuilder ->
+ startupMethodBuilder.setMethodReference(
+ MethodReferenceUtils.mainMethod(barClassReference)))
+ .addSyntheticStartupMethod(
+ syntheticStartupMethodBuilder ->
+ syntheticStartupMethodBuilder.setSyntheticContextReference(
+ bazClassReference));
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ };
+ return Collections.singleton(startupProfileProvider);
+ }
+
+ private void inspectDiagnostics(TestDiagnosticMessages diagnostics) {
+ diagnostics.assertWarningsMatch(
+ allOf(
+ diagnosticType(MissingStartupProfileItemsDiagnostic.class),
+ diagnosticMessage(
+ equalTo(
+ StringUtils.joinLines(
+ "Startup method not found: void Bar.main(java.lang.String[])",
+ "Startup class not found: Baz",
+ "Startup class not found: Foo")))));
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {}
+ }
+}