Fail early if malformed inner names are found.

Bug: 120597515
Change-Id: I2969308985e3ca9b2caf5c7f19eafc57e8da45b4
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 4aa5b7c..78a0ba9 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.ProgramResource.Kind;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexValue.DexValueAnnotation;
 import com.android.tools.r8.graph.DexValue.DexValueArray;
@@ -30,6 +31,7 @@
 import com.android.tools.r8.jar.CfApplicationWriter;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.FieldSignatureEquivalence;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.MethodSignatureEquivalence;
@@ -209,6 +211,14 @@
 
     @Override
     public void visitInnerClass(String name, String outerName, String innerName, int access) {
+      if (outerName != null && innerName != null) {
+        String separator =
+            DescriptorUtils.computeInnerClassSeparator(
+                outerName, name, innerName, application.options);
+        if (separator == null) {
+          throw new InternalCompilerError("Malformed class name: " + name);
+        }
+      }
       innerClasses.add(
           new InnerClassAttribute(
               access,
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 40c0236..21c7903 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -311,18 +311,26 @@
 
   public static String computeInnerClassSeparator(
       DexType outerClass, DexType innerClass, String innerName, InternalOptions options) {
-    String outerInternalRaw = outerClass.getInternalName();
-    String innerInternalRaw = innerClass.getInternalName();
+    return computeInnerClassSeparator(
+        outerClass.getInternalName(), innerClass.getInternalName(), innerName, options);
+  }
+
+  public static String computeInnerClassSeparator(
+      String outerDescriptor, String innerDescriptor, String innerName, InternalOptions options) {
     // outer-internal<separator>inner-name == inner-internal
-    if (outerInternalRaw.length() + innerName.length() > innerInternalRaw.length()) {
+    if (outerDescriptor.length() + innerName.length() > innerDescriptor.length()) {
+      // But, if the minification is enabled, we can choose $ separator.
+      if (options.isMinifying()) {
+        return String.valueOf(INNER_CLASS_SEPARATOR);
+      }
       return null;
     }
     String separator =
-        innerInternalRaw.substring(
-            outerInternalRaw.length(), innerInternalRaw.length() - innerName.length());
+        innerDescriptor.substring(
+            outerDescriptor.length(), innerDescriptor.length() - innerName.length());
     // Any non-$ separator results in a runtime exception in getCanonicalName.
     if (!separator.startsWith(String.valueOf(INNER_CLASS_SEPARATOR))) {
-      // But, if the minification is enabled, we can choose $ separator.
+      // Again, if the minification is enabled, we can choose $ separator.
       if (options.isMinifying()) {
         return String.valueOf(INNER_CLASS_SEPARATOR);
       }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java
index 7d44d4b..4ac1d9a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java
@@ -4,6 +4,8 @@
 package com.android.tools.r8.ir.optimize.reflection;
 
 import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.CompilationFailedException;
@@ -14,6 +16,7 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -171,9 +174,15 @@
             .minification(minify)
             .setMinApi(parameters.getRuntime())
             .compile();
-    } catch (CompilationFailedException e) {
-      assertTrue("b/120597515", config == TestNamingConfig.NON_NESTED_INNER);
-      assertTrue("b/120597515", minify);
+    } catch (InternalCompilerError e) {
+      assertThat(e.getMessage(), containsString("Malformed class name"));
+      assertThat(e.getMessage(), containsString(config.getInnerTypeRaw()));
+      assertTrue(
+          "b/120597515",
+          config == TestNamingConfig.EMTPY_SEPARATOR
+              || config == TestNamingConfig.UNDERBAR_SEPARATOR
+              || config == TestNamingConfig.NON_NESTED_INNER);
+      assertFalse("b/120597515", minify);
       return;
     }
     CodeInspector inspector = r8CompileResult.inspector();
@@ -205,32 +214,9 @@
         break;
       case EMTPY_SEPARATOR:
       case UNDERBAR_SEPARATOR:
-        if (!minify
-            && parameters.isCfRuntime()
-            && parameters.getRuntime().asCf().getVm().lessThanOrEqual(CfVm.JDK8)) {
-          // Any non-$ separator results in a runtime exception in getCanonicalName.
-          r8RunResult.assertFailureWithErrorThatMatches(containsString("Malformed class name"));
-        } else {
-          assert minify
-              || parameters.isDexRuntime()
-              || !parameters.getRuntime().asCf().getVm().lessThanOrEqual(CfVm.JDK8);
-          r8RunResult.assertSuccessWithOutput(getExpectedMinified(inspector));
-        }
-        break;
       case NON_NESTED_INNER:
-        if (minify) {
-          // TODO(b/120597515): better fail early while reading class file.
-          throw new Unreachable("Expect to see compilation error: " + config);
-        }
-        if (parameters.isCfRuntime()
-            && parameters.getRuntime().asCf().getVm().lessThanOrEqual(CfVm.JDK8)) {
-          // Any non-$ separator results in a runtime exception in getCanonicalName.
-          r8RunResult.assertFailureWithErrorThatMatches(containsString("Malformed class name"));
-        } else {
-          assert parameters.isDexRuntime()
-              || !parameters.getRuntime().asCf().getVm().lessThanOrEqual(CfVm.JDK8);
-          r8RunResult.assertSuccessWithOutput(getExpectedMinified(inspector));
-        }
+        assertTrue("Expect to see compilation error", minify);
+        r8RunResult.assertSuccessWithOutput(getExpectedMinified(inspector));
         break;
       default:
         throw new Unreachable("Unexpected test configuration: " + config);