Option to test completeness of startup configuration
Change-Id: Ifbb433b7c95c52f7ac4c7955ccfe09185a28b459
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 4e0ceeb..d5995f4 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexItemFactory;
@@ -24,7 +25,9 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
+import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.graph.ThrowNullCode;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.ClassNameMapper;
@@ -43,6 +46,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterators;
import com.google.common.collect.Sets;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.io.IOException;
@@ -1085,6 +1089,7 @@
public void run() {
addStartupClasses();
+ enableStartupCompletenessCheckForTesting();
List<DexProgramClass> nonPackageClasses = addNonStartupClasses();
addNonPackageClasses(cycler, nonPackageClasses);
}
@@ -1131,6 +1136,31 @@
}
}
+ /**
+ * Replaces the code of each method of a non-startup class by {@code throw null}. If the
+ * application fails on launch with this enabled this points to the startup configuration being
+ * incomplete.
+ */
+ private void enableStartupCompletenessCheckForTesting() {
+ if (!options.getStartupOptions().isStartupCompletenessCheckForTesting()) {
+ return;
+ }
+ for (DexProgramClass clazz : classPartioning.getNonStartupClasses()) {
+ clazz.forEachProgramMethodMatching(
+ DexEncodedMethod::hasCode,
+ method ->
+ method.getDefinition().setCode(ThrowNullCode.get(), Int2ReferenceMaps.emptyMap()));
+ if (!clazz.hasClassInitializer()) {
+ clazz.addDirectMethod(
+ DexEncodedMethod.syntheticBuilder()
+ .setAccessFlags(MethodAccessFlags.createForClassInitializer())
+ .setCode(ThrowNullCode.get())
+ .setMethod(dexItemFactory.createClassInitializer(clazz.getType()))
+ .build());
+ }
+ }
+ }
+
private List<DexProgramClass> addNonStartupClasses() {
int prefixLength = MINIMUM_PREFIX_LENGTH;
int transactionStartIndex = 0;
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
index a0e0f03..9bab1cd 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
@@ -7,6 +7,7 @@
public class StartupOptions {
private boolean enableMinimalStartupDex = false;
+ private boolean enableStartupCompletenessCheckForTesting = false;
private StartupConfiguration startupConfiguration;
@@ -18,6 +19,10 @@
enableMinimalStartupDex = true;
}
+ public boolean isStartupCompletenessCheckForTesting() {
+ return enableStartupCompletenessCheckForTesting;
+ }
+
public boolean hasStartupConfiguration() {
return startupConfiguration != null;
}