Use ASM to extract the class name from class files

The implementation using ClassLoader.defineClass only worked in
simple cases.

Change-Id: Ib253a32e860b26147f6490702146ce190f787d04
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
index a846ea7..a8ec999 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -368,7 +368,7 @@
     assert name.endsWith(CLASS_EXTENSION) :
         "Name " + name + " must have " + CLASS_EXTENSION + " suffix";
     String descriptor = name.substring(0, name.length() - CLASS_EXTENSION.length());
-    if (descriptor.contains(".")) {
+    if (descriptor.indexOf(JAVA_PACKAGE_SEPARATOR) != -1) {
       throw new CompilationError("Unexpected class file name: " + name);
     }
     return 'L' + descriptor + ';';
@@ -383,6 +383,6 @@
 
   public static String getPathFromJavaType(String typeName) {
     assert isValidJavaType(typeName);
-    return typeName.replace(".", "/") + ".class";
+    return typeName.replace(JAVA_PACKAGE_SEPARATOR, DESCRIPTOR_PACKAGE_SEPARATOR) + ".class";
   }
 }
diff --git a/src/test/java/com/android/tools/r8/AsmTestBase.java b/src/test/java/com/android/tools/r8/AsmTestBase.java
index cb4a4af..35767fb 100644
--- a/src/test/java/com/android/tools/r8/AsmTestBase.java
+++ b/src/test/java/com/android/tools/r8/AsmTestBase.java
@@ -144,14 +144,6 @@
     byte[] dump() throws Exception;
   }
 
-  protected static Class loadClassFromAsmClass(AsmDump asmDump) {
-    try {
-      return loadClassFromDump(asmDump.dump());
-    } catch (Exception e) {
-      throw new ClassFormatError(e.toString());
-    }
-  }
-
   protected static byte[] getBytesFromAsmClass(AsmDump asmDump) {
     try {
       return asmDump.dump();
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 182326e..bf900a9 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -6,6 +6,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.objectweb.asm.Opcodes.ASM6;
 
 import com.android.tools.r8.DataResourceProvider.Visitor;
 import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
@@ -52,6 +53,8 @@
 import java.util.zip.ZipOutputStream;
 import org.junit.Rule;
 import org.junit.rules.TemporaryFolder;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
 
 public class TestBase {
   public enum Backend {
@@ -602,7 +605,7 @@
 
   protected ProcessResult runOnJavaRawNoVerify(String main, List<byte[]> classes, List<String> args)
       throws IOException {
-    return ToolHelper.runJavaNoVerify(Collections.singletonList(writeToZip(classes)), main, args);
+    return ToolHelper.runJavaNoVerify(Collections.singletonList(writeToJar(classes)), main, args);
   }
 
   protected ProcessResult runOnJavaRaw(String main, byte[]... classes) throws IOException {
@@ -615,7 +618,7 @@
     mainAndArgs.add(main);
     mainAndArgs.addAll(args);
     return ToolHelper.runJava(
-        Collections.singletonList(writeToZip(classes)), mainAndArgs.toArray(new String[0]));
+        Collections.singletonList(writeToJar(classes)), mainAndArgs.toArray(new String[0]));
   }
 
   protected ProcessResult runOnJavaRaw(AndroidApp app, String mainClass, List<String> args)
@@ -628,33 +631,57 @@
     return ToolHelper.runJava(out, mainAndArgs.toArray(new String[0]));
   }
 
-  protected Path writeToZip(List<byte[]> classes) throws IOException {
-    File result = temp.newFile("tmp.zip");
-    try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(result.toPath(),
-        StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))) {
+  private String extractClassName(byte[] ccc) {
+    class ClassNameExtractor extends ClassVisitor {
+      private String className;
+
+      private ClassNameExtractor() {
+        super(ASM6);
+      }
+
+      @Override
+      public void visit(
+          int version,
+          int access,
+          String name,
+          String signature,
+          String superName,
+          String[] interfaces) {
+        className =
+            name.replace(
+                DescriptorUtils.DESCRIPTOR_PACKAGE_SEPARATOR,
+                DescriptorUtils.JAVA_PACKAGE_SEPARATOR);
+      }
+
+      String getClassName() {
+        return className;
+      }
+    }
+
+    ClassReader reader = new ClassReader(ccc);
+    ClassNameExtractor extractor = new ClassNameExtractor();
+    reader.accept(
+        extractor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+    return extractor.getClassName();
+  }
+
+  protected Path writeToJar(List<byte[]> classes) throws IOException {
+    Path result = File.createTempFile("junit", ".jar", temp.getRoot()).toPath();
+    try (ZipOutputStream out =
+        new ZipOutputStream(
+            Files.newOutputStream(
+                result, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))) {
       for (byte[] clazz : classes) {
-        String name = loadClassFromDump(clazz).getTypeName();
+        String name = extractClassName(clazz);
         ZipUtils.writeToZipStream(
             out, DescriptorUtils.getPathFromJavaType(name), clazz, ZipEntry.STORED);
       }
     }
-    return result.toPath();
+    return result;
   }
 
-  protected Path writeToZip(JasminBuilder jasminBuilder) throws Exception {
-    return writeToZip(jasminBuilder.buildClasses());
-  }
-
-  protected static Class loadClassFromDump(byte[] dump) {
-    return new DumpLoader().loadClass(dump);
-  }
-
-  private static class DumpLoader extends ClassLoader {
-
-    @SuppressWarnings("deprecation")
-    public Class loadClass(byte[] clazz) {
-      return defineClass(clazz, 0, clazz.length);
-    }
+  protected Path writeToJar(JasminBuilder jasminBuilder) throws Exception {
+    return writeToJar(jasminBuilder.buildClasses());
   }
 
   /**
diff --git a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
index 4405565..060d87b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -324,7 +324,7 @@
         "return"
     );
 
-    Path output = writeToZip(builder);
+    Path output = writeToJar(builder);
     addExtraClasspath(output);
     return JASMIN_MAIN_CLASS;
   }
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
index a3aa5e1..036d960 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -476,7 +476,7 @@
         "return"
     );
 
-    Path javaOutput = writeToZip(jasminBuilder);
+    Path javaOutput = writeToJar(jasminBuilder);
     ProcessResult javaResult = ToolHelper.runJava(javaOutput, "Foo");
     if (javaResult.exitCode != 0) {
       System.err.println(javaResult.stderr);
diff --git a/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java b/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java
index a93db7a..acf4488 100644
--- a/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java
@@ -55,7 +55,7 @@
 
   @Before
   public void setUp() throws Exception {
-    libJar = writeToZip(ImmutableList.of(ToolHelper.getClassAsBytes(GoingToBeMissed.class)));
+    libJar = writeToJar(ImmutableList.of(ToolHelper.getClassAsBytes(GoingToBeMissed.class)));
     libDex = temp.getRoot().toPath().resolve("lib.zip");
     AndroidApp libApp = ToolHelper.runD8(readClasses(GoingToBeMissed.class));
     libApp.writeToZip(libDex, OutputMode.DexIndexed);