Add flag for asm verification of inputs

Change-Id: Ic3f3019435c818de6d44f440c463848eab30a7e7
diff --git a/src/main/java/com/android/tools/r8/cf/CfVerifierTool.java b/src/main/java/com/android/tools/r8/cf/CfVerifierTool.java
index 7bf11e0..524b6bc 100644
--- a/src/main/java/com/android/tools/r8/cf/CfVerifierTool.java
+++ b/src/main/java/com/android/tools/r8/cf/CfVerifierTool.java
@@ -25,6 +25,7 @@
       builder.addProgramFile(Paths.get(arg));
     }
     InternalOptions options = new InternalOptions();
+    options.testing.verifyInputs = true;
     DexApplication dexApplication =
         new ApplicationReader(builder.build(), options, Timing.empty()).read();
     AppView<AppInfo> appView = AppView.createForD8(AppInfo.createInitialAppInfo(dexApplication));
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index 31d40bb..b231b6e 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -142,6 +142,11 @@
     if (shouldDump) {
       dumpApplication();
     }
+
+    if (options.testing.verifyInputs) {
+      inputApp.validateInputs();
+    }
+
     timing.begin("DexApplication.read");
     final LazyLoadedDexApplication.Builder builder =
         DexApplication.builder(options, timing, resolver);
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index d58387f..b1a2757 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -75,7 +75,11 @@
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 import java.util.zip.ZipOutputStream;
+import org.objectweb.asm.ClassReader;
 import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.util.CheckClassAdapter;
 
 /**
  * Collection of program files needed for processing.
@@ -762,6 +766,30 @@
     return extractor.getDescriptor();
   }
 
+  public void validateInputs() {
+    for (ProgramResourceProvider programResourceProvider : getProgramResourceProviders()) {
+      try {
+        for (ProgramResource programResource : programResourceProvider.getProgramResources()) {
+          try {
+            Kind kind = programResource.getKind();
+            if (kind == Kind.DEX) {
+              continue;
+            }
+            byte[] bytes = programResource.getBytes();
+            ClassReader classReader = new ClassReader(bytes);
+            classReader.accept(
+                new CheckClassAdapter(Opcodes.ASM9, new ClassNode(), true) {},
+                ClassReader.EXPAND_FRAMES);
+          } catch (Throwable e) {
+            throw new CompilationError("Failed validating " + programResource.getOrigin(), e);
+          }
+        }
+      } catch (ResourceException e) {
+        throw new CompilationError("Resource exception in validation", e);
+      }
+    }
+  }
+
   /**
    * Builder interface for constructing an AndroidApp.
    */
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 c0c5504..2334765 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1571,6 +1571,7 @@
     public boolean disableMappingToOriginalProgramVerification = false;
     public boolean allowInvalidCfAccessFlags =
         System.getProperty("com.android.tools.r8.allowInvalidCfAccessFlags") != null;
+    public boolean verifyInputs = System.getProperty("com.android.tools.r8.verifyInputs") != null;
     // TODO(b/177333791): Set to true
     public boolean checkForNotExpandingMainDexTracingResult = false;
     public Set<String> allowedUnusedDontWarnPatterns = new HashSet<>();
diff --git a/src/test/java/com/android/tools/r8/cf/stackmap/InvalidStackHeightTest.java b/src/test/java/com/android/tools/r8/cf/stackmap/InvalidStackHeightTest.java
index 71b6637..9fffa07 100644
--- a/src/test/java/com/android/tools/r8/cf/stackmap/InvalidStackHeightTest.java
+++ b/src/test/java/com/android/tools/r8/cf/stackmap/InvalidStackHeightTest.java
@@ -6,6 +6,8 @@
 
 import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
 import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.CompilationFailedException;
@@ -88,6 +90,23 @@
         .assertSuccessWithOutputLines(EXPECTED);
   }
 
+  @Test()
+  public void testR8InputVerification() throws Exception {
+    try {
+      testForR8(parameters.getBackend())
+          .addProgramClassFileData(getMainWithChangedMaxStackHeight())
+          .enableInliningAnnotations()
+          .addKeepMainRule(Main.class)
+          .setMinApi(parameters.getApiLevel())
+          .addOptionsModification(options -> options.testing.verifyInputs = true)
+          .compile();
+    } catch (CompilationFailedException e) {
+      assertTrue(e.getCause().getMessage().contains("Insufficient maximum stack size"));
+      return;
+    }
+    fail("Should always throw");
+  }
+
   public byte[] getMainWithChangedMaxStackHeight() throws Exception {
     return transformer(Main.class).setMaxStackHeight(MethodPredicate.onName("main"), 1).transform();
   }