Don't rebind to protected library members when generating class files

Fixes: b/367915233
Change-Id: Iedee3cd60d668810a9ba073e23234e5097ff6bec
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingHelper.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingHelper.java
index a2d0f00..649b64f 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingHelper.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingHelper.java
@@ -161,6 +161,9 @@
     //  `original.holder` on all API levels, in which case it is not OK to rebind to the resolved
     //  method.
     return resolvedMethod.isLibraryMethod()
+        // Only rebind to protected methods in library when generating DEX. Dalvik/ART does not
+        // implement the full instruction verification.
+        && (options.isGeneratingDex() || !resolvedMethod.getAccessFlags().isProtected())
         && isAccessibleInAllContexts(resolvedMethod, resolutionResult, contexts)
         && !isInvokeSuperToInterfaceMethod(resolvedMethod, invokeType)
         && !isInvokeSuperToAbstractMethod(resolvedMethod, invokeType)
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/MemberRebindingProtectedInLibraryHierarchyTest.java b/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/MemberRebindingProtectedInLibraryHierarchyTest.java
new file mode 100644
index 0000000..de3d6bb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/MemberRebindingProtectedInLibraryHierarchyTest.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2024, 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.memberrebinding.protectedaccess;
+
+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.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.InvokeInstructionSubject;
+import java.util.LinkedHashMap;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+// Test for B/367915233.
+@RunWith(Parameterized.class)
+public class MemberRebindingProtectedInLibraryHierarchyTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  private static final String EXPECTED_OUTPUT = StringUtils.lines("Hello, world!");
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT)
+        .inspect(
+            inspector ->
+                assertTrue(
+                    inspector
+                        .clazz(TestClass.class)
+                        .mainMethod()
+                        .streamInstructions()
+                        .filter(InstructionSubject::isInvokeVirtual)
+                        .map(
+                            instruction -> ((InvokeInstructionSubject) instruction).invokedMethod())
+                        .anyMatch(
+                            method ->
+                                (parameters.isCfRuntime()
+                                        ? method
+                                            .getHolderType()
+                                            .toSourceString()
+                                            .equals("java.util.HashMap")
+                                        : method
+                                            .getHolderType()
+                                            .toSourceString()
+                                            .equals("java.lang.Object"))
+                                    && method.getName().toString().equals("clone"))));
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      new LinkedHashMap<>().clone();
+      System.out.println("Hello, world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/B367915233Test.java b/src/test/java/com/android/tools/r8/resolution/B367915233Test.java
deleted file mode 100644
index c01e188..0000000
--- a/src/test/java/com/android/tools/r8/resolution/B367915233Test.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) 2024, 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.resolution;
-
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.StringUtils;
-import java.util.LinkedHashMap;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameter;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class B367915233Test extends TestBase {
-
-  @Parameter(0)
-  public TestParameters parameters;
-
-  @Parameters(name = "{0}")
-  public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimesAndApiLevels().build();
-  }
-
-  private static final String EXPECTED_OUTPUT = StringUtils.lines("Hello, world!");
-
-  @Test
-  public void testR8() throws Exception {
-    testForR8(parameters.getBackend())
-        .addInnerClasses(getClass())
-        .addKeepMainRule(TestClass.class)
-        .setMinApi(parameters)
-        .run(parameters.getRuntime(), TestClass.class)
-        .applyIf(
-            parameters.isCfRuntime(),
-            r -> r.assertFailureWithErrorThatThrows(VerifyError.class),
-            r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
-  }
-
-  static class TestClass {
-
-    public static void main(String[] args) {
-      new LinkedHashMap<>().clone();
-      System.out.println("Hello, world!");
-    }
-  }
-}