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;
   }