Don't do ClassForName rewriting if the class is in a feature.

Bug: 144363371,144752954
Change-Id: I6558bf7fb8921911dd702fa0410519254f1e432d
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
index 11f0a08..fa41f21 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
@@ -187,6 +187,14 @@
     if (baseClazz == null || !baseClazz.isResolvable(appView)) {
       return null;
     }
+
+    // Don't allow the instantiated class to be in a feature, if it is, we can get a
+    // NoClassDefFoundError from dalvik/art.
+    if (baseClazz.isProgramClass()
+        && appView.options().featureSplitConfiguration != null
+        && appView.options().featureSplitConfiguration.isInFeature(baseClazz.asProgramClass())) {
+      return null;
+    }
     // Make sure the (base) type is visible.
     ConstraintWithTarget constraints =
         ConstraintWithTarget.classIsVisible(context, baseType, appView);
diff --git a/src/test/java/com/android/tools/r8/reflection/TestClassForNameWhenSplit.java b/src/test/java/com/android/tools/r8/reflection/TestClassForNameWhenSplit.java
new file mode 100644
index 0000000..50305fd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/reflection/TestClassForNameWhenSplit.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.reflection;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.dexsplitter.SplitterTestBase;
+import com.android.tools.r8.references.Reference;
+import com.google.common.collect.ImmutableSet;
+import java.nio.file.Path;
+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 TestClassForNameWhenSplit extends TestBase {
+
+  private final TestParameters parameters;
+  private final String EXPECTED = "caught";
+
+  @Parameters(name="{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+  }
+
+  public TestClassForNameWhenSplit(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testClassNotFound() throws Exception {
+    testForRuntime(parameters)
+        .addProgramClasses(Main.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  @Test
+  public void testClassForNameIsKeept() throws Exception {
+    Path featurePath = temp.newFile("feature1.zip").toPath();
+    testForR8(parameters.getBackend())
+        .addProgramClasses(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .addFeatureSplit(
+            builder ->
+                SplitterTestBase.simpleSplitProvider(
+                    builder, featurePath, temp, Foobar.class))
+        .addKeepMainRule(Main.class)
+        .addKeepClassRules(Foobar.class)
+        .compile()
+        .disassemble()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      try {
+        foo();
+      } catch (ClassNotFoundException e) {
+        System.out.println("caught");
+      } catch (IllegalAccessException e) {
+        System.out.println("illegal access");
+      } catch (InstantiationException e) {
+        System.out.println("instantiation exception");
+      }
+    }
+
+    private static void foo()
+        throws ClassNotFoundException, IllegalAccessException, InstantiationException {
+      try {
+        // Ensure cl init has been assumed triggered
+        new Foobar();
+      } catch (NoClassDefFoundError e) { }
+      // It is not valid to replace this with just classForName, even if we see that there is only
+      // a trivial clinit or the fact that we have already triggered it above.
+      Class<?> foobar =
+          Class.forName("com.android.tools.r8.reflection.TestClassForNameWhenSplit$Foobar");
+      foobar.newInstance();
+    }
+  }
+
+  public static class Foobar {
+    public Foobar() {
+      if (System.currentTimeMillis() < 2) {
+        System.out.println("A long time ago, in a galaxy far far away");
+      }
+    }
+  }
+}