Merge "Add CF backend to KeepAttributesTest."
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepAttributesTest.java b/src/test/java/com/android/tools/r8/shaking/KeepAttributesTest.java
index b518683..96ce54a 100644
--- a/src/test/java/com/android/tools/r8/shaking/KeepAttributesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/KeepAttributesTest.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -13,23 +13,36 @@
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.debuginfo.DebugInfoInspector;
-import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.forceproguardcompatibility.keepattributes.TestKeepAttributes;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.AndroidAppConsumers;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
-import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 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 KeepAttributesTest extends TestBase {
 
-  public static final Class CLASS = TestKeepAttributes.class;
+  private static final Class CLASS = TestKeepAttributes.class;
+
+  @Parameters(name = "{0}")
+  public static Backend[] parameters() {
+    return new Backend[] { Backend.CF, Backend.DEX};
+  }
+
+  private final Backend backend;
+
+  public KeepAttributesTest(Backend backend) {
+    this.backend = backend;
+  }
 
   @Test
   public void keepAllAttributesInDebugMode()
@@ -37,10 +50,9 @@
     List<String> keepRules = ImmutableList.of(
         "-keep class ** { *; }"
     );
-    CodeInspector inspector = compile(keepRules, CompilationMode.DEBUG);
-    DebugInfoInspector debugInfo = debugInfoForMain(inspector);
-    checkLineNumbers(true, debugInfo);
-    checkLocals(true, debugInfo);
+    MethodSubject mainMethod = compileRunAndGetMain(keepRules, CompilationMode.DEBUG);
+    assertTrue(mainMethod.hasLineNumberTable());
+    assertTrue(mainMethod.hasLocalVariableTable());
   }
 
   @Test
@@ -49,10 +61,9 @@
     List<String> keepRules = ImmutableList.of(
         "-keep class ** { *; }"
     );
-    CodeInspector inspector = compile(keepRules, CompilationMode.RELEASE);
-    DebugInfoInspector debugInfo = debugInfoForMain(inspector);
-    checkLineNumbers(false, debugInfo);
-    checkLocals(false, debugInfo);
+    MethodSubject mainMethod = compileRunAndGetMain(keepRules, CompilationMode.RELEASE);
+    assertFalse(mainMethod.hasLineNumberTable());
+    assertFalse(mainMethod.hasLocalVariableTable());
   }
 
   @Test
@@ -62,10 +73,9 @@
         "-keep class ** { *; }",
         "-keepattributes " + ProguardKeepAttributes.LINE_NUMBER_TABLE
     );
-    CodeInspector inspector = compile(keepRules, CompilationMode.RELEASE);
-    DebugInfoInspector debugInfo = debugInfoForMain(inspector);
-    checkLineNumbers(true, debugInfo);
-    checkLocals(false, debugInfo);
+    MethodSubject mainMethod = compileRunAndGetMain(keepRules, CompilationMode.RELEASE);
+    assertTrue(mainMethod.hasLineNumberTable());
+    assertFalse(mainMethod.hasLocalVariableTable());
   }
 
   @Test
@@ -78,11 +88,10 @@
             + ", "
             + ProguardKeepAttributes.LOCAL_VARIABLE_TABLE
     );
-    CodeInspector inspector = compile(keepRules, CompilationMode.RELEASE);
-    DebugInfoInspector debugInfo = debugInfoForMain(inspector);
-    checkLineNumbers(true, debugInfo);
+    MethodSubject mainMethod = compileRunAndGetMain(keepRules, CompilationMode.RELEASE);
+    assertTrue(mainMethod.hasLineNumberTable());
     // Locals are never included in release builds.
-    checkLocals(false, debugInfo);
+    assertFalse(mainMethod.hasLocalVariableTable());
   }
 
   @Test
@@ -93,7 +102,7 @@
     );
     // Compiling with a keep rule for locals but no line results in an error in R8.
     try {
-      compile(keepRules, CompilationMode.RELEASE);
+      compileRunAndGetMain(keepRules, CompilationMode.RELEASE);
     } catch (CompilationFailedException e) {
       assertTrue(e.getCause().getMessage().contains(ProguardKeepAttributes.LOCAL_VARIABLE_TABLE));
       assertTrue(e.getCause().getMessage().contains(ProguardKeepAttributes.LINE_NUMBER_TABLE));
@@ -102,7 +111,7 @@
     fail("Expected error");
   }
 
-  private CodeInspector compile(List<String> keepRules, CompilationMode mode)
+  private MethodSubject compileRunAndGetMain(List<String> keepRules, CompilationMode mode)
       throws CompilationFailedException, IOException, ExecutionException {
     AndroidAppConsumers sink = new AndroidAppConsumers();
     R8.run(
@@ -111,30 +120,14 @@
             .addProgramFiles(
                 ToolHelper.getClassFilesForTestDirectory(
                     ToolHelper.getClassFileForTestClass(CLASS).getParent()))
-            .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
+            .addLibraryFiles(runtimeJar(backend))
             .addProguardConfiguration(keepRules, Origin.unknown())
-            .setProgramConsumer(sink.wrapProgramConsumer(emptyConsumer(Backend.DEX)))
+            .setProgramConsumer(sink.wrapProgramConsumer(emptyConsumer(backend)))
             .build());
     AndroidApp app = sink.build();
     CodeInspector codeInspector = new CodeInspector(app);
-    runOnArt(app, CLASS.getTypeName());
-    return codeInspector;
+    runOnVM(app, CLASS.getTypeName(), backend);
+    return codeInspector.clazz(CLASS).mainMethod();
   }
 
-  private DebugInfoInspector debugInfoForMain(CodeInspector inspector) {
-    return new DebugInfoInspector(
-        inspector,
-        CLASS.getTypeName(),
-        new MethodSignature("main", "void", Collections.singleton("java.lang.String[]")));
-  }
-
-  private void checkLineNumbers(boolean expected, DebugInfoInspector debugInfo) {
-    assertEquals("Expected " + (expected ? "line entries" : "no line entries"),
-        expected, debugInfo.getEntries().stream().anyMatch(e -> e.lineEntry));
-  }
-
-  private void checkLocals(boolean expected, DebugInfoInspector debugInfo) {
-    assertEquals("Expected " + (expected ? "locals" : "no locals"),
-        expected, debugInfo.getEntries().stream().anyMatch(e -> !e.locals.isEmpty()));
-  }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
index 1396be9..adee8cf 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
@@ -78,4 +78,14 @@
   public String getFinalSignatureAttribute() {
     return null;
   }
+
+  @Override
+  public boolean hasLineNumberTable() {
+    return false;
+  }
+
+  @Override
+  public boolean hasLocalVariableTable() {
+    return false;
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index 3fe8186..f205594 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -4,12 +4,22 @@
 
 package com.android.tools.r8.utils.codeinspector;
 
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfPosition;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexDebugEvent;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.naming.MemberNaming;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.naming.signature.GenericSignatureParser;
 import java.util.Iterator;
+import java.util.ListIterator;
 import java.util.function.Predicate;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.LineNumberNode;
 
 public class FoundMethodSubject extends MethodSubject {
 
@@ -134,6 +144,69 @@
   }
 
   @Override
+  public boolean hasLineNumberTable() {
+    Code code = getMethod().getCode();
+    if (code.isDexCode()) {
+      DexCode dexCode = code.asDexCode();
+      if (dexCode.getDebugInfo() != null) {
+        for (DexDebugEvent event : dexCode.getDebugInfo().events) {
+          if (event instanceof DexDebugEvent.Default) {
+            return true;
+          }
+        }
+      }
+      return false;
+    }
+    if (code.isCfCode()) {
+      for (CfInstruction insn : code.asCfCode().getInstructions()) {
+        if (insn instanceof CfPosition) {
+          return true;
+        }
+      }
+      return false;
+    }
+    if (code.isJarCode()) {
+      ListIterator<AbstractInsnNode> it = code.asJarCode().getNode().instructions.iterator();
+      while (it.hasNext()) {
+        if (it.next() instanceof LineNumberNode) {
+          return true;
+        }
+      }
+      return false;
+    }
+    throw new Unreachable("Unexpected code type: " + code.getClass().getSimpleName());
+  }
+
+  @Override
+  public boolean hasLocalVariableTable() {
+    Code code = getMethod().getCode();
+    if (code.isDexCode()) {
+      DexCode dexCode = code.asDexCode();
+      if (dexCode.getDebugInfo() != null) {
+        for (DexString parameter : dexCode.getDebugInfo().parameters) {
+          if (parameter != null) {
+            return true;
+          }
+        }
+        for (DexDebugEvent event : dexCode.getDebugInfo().events) {
+          if (event instanceof DexDebugEvent.StartLocal) {
+            return true;
+          }
+        }
+      }
+      return false;
+    }
+    if (code.isCfCode()) {
+      return !code.asCfCode().getLocalVariables().isEmpty();
+    }
+    if (code.isJarCode()) {
+      return code.asJarCode().getNode().localVariables != null
+          && !code.asJarCode().getNode().localVariables.isEmpty();
+    }
+    throw new Unreachable("Unexpected code type: " + code.getClass().getSimpleName());
+  }
+
+  @Override
   public String toString() {
     return dexMethod.toSourceString();
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
index 6eaaf5f..55030ca 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
@@ -32,4 +32,8 @@
       Predicate<InstructionSubject> filter) {
     return null;
   }
+
+  public abstract boolean hasLineNumberTable();
+
+  public abstract boolean hasLocalVariableTable();
 }