Merge commit '9284765f' into dev-release
diff --git a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
index 4864e0b..c6d8571 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
@@ -226,21 +226,7 @@
       DexLibraryClass libraryClass,
       ThrowExceptionCode throwExceptionCode) {
     DexItemFactory factory = appView.dexItemFactory();
-    if (libraryClass.getType() == factory.objectType
-        || libraryClass.getType().toDescriptorString().startsWith("Ljava/")) {
-      return;
-    }
-    // We cannot reliably create a stub that will have the same throwing
-    // behavior for all VMs. We only create stubs for exceptions to allow them being present in
-    // catch handlers. See b/b/258270051 for more information.
-    if (!isThrowable(libraryClass)
-        || appView.options().apiModelingOptions().stubNonThrowableClasses) {
-      return;
-    }
-    if (appView
-        .options()
-        .machineDesugaredLibrarySpecification
-        .isSupported(libraryClass.getType())) {
+    if (!appView.options().apiModelingOptions().stubbingEnabledFor(appView, libraryClass)) {
       return;
     }
     Set<ProgramDefinition> contexts = referencingContexts.get(libraryClass);
@@ -277,16 +263,4 @@
             },
             ignored -> {});
   }
-
-  private boolean isThrowable(DexLibraryClass libraryClass) {
-    DexClass current = libraryClass;
-    while (current.getSuperType() != null) {
-      DexType superType = current.getSuperType();
-      if (superType == factory.throwableType) {
-        return true;
-      }
-      current = appView.definitionFor(current.getSuperType());
-    }
-    return false;
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 5f0d225..4300df0 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -769,7 +769,8 @@
     List<InnerClassAttribute> innerClasses = clazz.getInnerClasses();
     if (enclosingMethod == null
         && innerClasses.isEmpty()
-        && clazz.getClassSignature().hasNoSignature()) {
+        && clazz.getClassSignature().hasNoSignature()
+        && !clazz.isInANest()) {
       return;
     }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index d838596..411e5ee 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -142,6 +142,7 @@
   public final DexString shortDescriptor = createString("S");
   public final DexString voidDescriptor = createString("V");
   public final DexString descriptorSeparator = createString("/");
+  public final DexString javaDescriptorPrefix = createString("Ljava/");
 
   private final DexString booleanArrayDescriptor = createString("[Z");
   private final DexString byteArrayDescriptor = createString("[B");
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
index 100d61a..810f2de 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
@@ -20,6 +20,8 @@
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.Reporter;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -157,7 +159,6 @@
             keepByteCode,
             extensionInformation,
             originalMembersWithKotlinInfo);
-    String companionObjectName = setCompanionObject(kmClass, hostClass, reporter);
     KotlinTypeReference anonymousObjectOrigin = getAnonymousObjectOrigin(kmClass, factory);
     boolean nameCanBeDeducedFromClassOrOrigin =
         kmClass.name.equals(
@@ -176,7 +177,7 @@
         getSuperTypes(kmClass.getSupertypes(), factory, reporter),
         getSealedSubClasses(kmClass.getSealedSubclasses(), factory),
         getNestedClasses(hostClass, kmClass.getNestedClasses(), factory),
-        kmClass.getEnumEntries(),
+        setEnumEntries(kmClass, hostClass),
         KotlinVersionRequirementInfo.create(kmClass.getVersionRequirements()),
         anonymousObjectOrigin,
         packageName,
@@ -187,7 +188,7 @@
         KotlinTypeInfo.create(kmClass.getInlineClassUnderlyingType(), factory, reporter),
         originalMembersWithKotlinInfo,
         JvmExtensionsKt.getJvmFlags(kmClass),
-        companionObjectName);
+        setCompanionObject(kmClass, hostClass, reporter));
   }
 
   private static KotlinTypeReference getAnonymousObjectOrigin(
@@ -248,6 +249,25 @@
     return companionObjectName;
   }
 
+  private static List<String> setEnumEntries(KmClass kmClass, DexClass hostClass) {
+    List<String> enumEntries = kmClass.getEnumEntries();
+    if (enumEntries.isEmpty()) {
+      return enumEntries;
+    }
+    Collection<String> enumEntryStrings =
+        enumEntries.size() < 16 ? enumEntries : Sets.newHashSet(enumEntries);
+    hostClass
+        .fields()
+        .forEach(
+            field -> {
+              String fieldName = field.getName().toString();
+              if (enumEntryStrings.contains(fieldName)) {
+                field.setKotlinMemberInfo(new KotlinEnumEntryInfo(fieldName));
+              }
+            });
+    return enumEntries;
+  }
+
   @Override
   public boolean isClass() {
     return true;
@@ -290,14 +310,24 @@
     }
     // Find a companion object.
     boolean foundCompanion = false;
+    int numberOfEnumEntries = 0;
     for (DexEncodedField field : clazz.fields()) {
-      if (field.getKotlinInfo().isCompanion()) {
+      KotlinFieldLevelInfo kotlinInfo = field.getKotlinInfo();
+      if (kotlinInfo.isCompanion()) {
         rewritten |=
-            field
-                .getKotlinInfo()
+            kotlinInfo
                 .asCompanion()
                 .rewrite(kmClass, field.getReference(), appView.getNamingLens());
         foundCompanion = true;
+      } else if (kotlinInfo.isEnumEntry()) {
+        KotlinEnumEntryInfo kotlinEnumEntryInfo = kotlinInfo.asEnumEntry();
+        rewritten |=
+            kotlinEnumEntryInfo.rewrite(kmClass, field.getReference(), appView.getNamingLens());
+        if (numberOfEnumEntries >= enumEntries.size()
+            || !enumEntries.get(numberOfEnumEntries).equals(kotlinEnumEntryInfo.getEnumEntry())) {
+          rewritten = true;
+        }
+        numberOfEnumEntries += 1;
       }
     }
     // If we did not find a companion but it was there on input we have to emit a new metadata
@@ -305,6 +335,11 @@
     if (!foundCompanion && companionObjectName != null) {
       rewritten = true;
     }
+    // If we could remove enum entries but were unable to rename them we still have to emit a new
+    // metadata object.
+    if (numberOfEnumEntries < enumEntries.size()) {
+      rewritten = true;
+    }
     // Take all not backed constructors because we will never find them in definitions.
     for (KotlinConstructorInfo constructorInfo : constructorsWithNoBacking) {
       rewritten |= constructorInfo.rewrite(kmClass, null, appView);
@@ -373,8 +408,6 @@
               appView,
               null);
     }
-    // TODO(b/154347404): Understand enum entries.
-    kmClass.getEnumEntries().addAll(enumEntries);
     rewritten |= versionRequirements.rewrite(kmClass::visitVersionRequirement);
     if (inlineClassUnderlyingPropertyName != null && inlineClassUnderlyingType != null) {
       kmClass.setInlineClassUnderlyingPropertyName(inlineClassUnderlyingPropertyName);
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinEnumEntryInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinEnumEntryInfo.java
new file mode 100644
index 0000000..25e4ca7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinEnumEntryInfo.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2022, 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.kotlin;
+
+import com.android.tools.r8.graph.DexDefinitionSupplier;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.naming.NamingLens;
+import kotlinx.metadata.KmClassVisitor;
+
+// Structure around a kotlin enum value that can be assigned to a field.
+public class KotlinEnumEntryInfo implements KotlinFieldLevelInfo {
+
+  private final String enumEntry;
+
+  public KotlinEnumEntryInfo(String enumEntry) {
+    this.enumEntry = enumEntry;
+  }
+
+  @Override
+  public boolean isEnumEntry() {
+    return true;
+  }
+
+  @Override
+  public KotlinEnumEntryInfo asEnumEntry() {
+    return this;
+  }
+
+  boolean rewrite(KmClassVisitor visitor, DexField field, NamingLens lens) {
+    DexString dexString = lens.lookupName(field);
+    String finalName = dexString.toString();
+    visitor.visitEnumEntry(finalName);
+    return !finalName.equals(enumEntry);
+  }
+
+  @Override
+  public void trace(DexDefinitionSupplier definitionSupplier) {
+    // Do nothing.
+  }
+
+  public String getEnumEntry() {
+    return enumEntry;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMemberLevelInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMemberLevelInfo.java
index 41f23c1..7c6a6c1 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMemberLevelInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMemberLevelInfo.java
@@ -43,4 +43,12 @@
   default KotlinPropertyInfo asProperty() {
     return null;
   }
+
+  default boolean isEnumEntry() {
+    return false;
+  }
+
+  default KotlinEnumEntryInfo asEnumEntry() {
+    return null;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index b726531..b4099ee 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1832,6 +1832,40 @@
     public void disableStubbingOfClasses() {
       enableStubbingOfClasses = false;
     }
+
+    public boolean stubbingEnabledFor(AppView<?> appView, DexLibraryClass libraryClass) {
+      if (libraryClass.getType() == appView.dexItemFactory().objectType
+          || libraryClass
+              .getType()
+              .getDescriptor()
+              .startsWith(appView.dexItemFactory().javaDescriptorPrefix)) {
+        return false;
+      }
+      // Check if desugared library will bridge the type.
+      if (appView
+          .options()
+          .machineDesugaredLibrarySpecification
+          .isSupported(libraryClass.getType())) {
+        return false;
+      }
+      // We cannot reliably create a stub that will have the same throwing behavior for all VMs. We
+      // only create stubs for exceptions to allow them being present in catch handlers. See
+      // b/258270051 for more information. Note that throwables in the java namespace are not
+      // stubbed (bailout above).
+      return isThrowable(appView, libraryClass) || stubNonThrowableClasses;
+    }
+
+    private boolean isThrowable(AppView<?> appView, DexLibraryClass libraryClass) {
+      DexClass current = libraryClass;
+      while (current.getSuperType() != null) {
+        DexType superType = current.getSuperType();
+        if (superType == appView.dexItemFactory().throwableType) {
+          return true;
+        }
+        current = appView.definitionFor(current.getSuperType());
+      }
+      return false;
+    }
   }
 
   public static class ProtoShrinkingOptions {
diff --git a/src/test/java/com/android/tools/r8/JvmTestBuilder.java b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
index 8b56639..49ce60e 100644
--- a/src/test/java/com/android/tools/r8/JvmTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.FileUtils;
 import com.google.common.collect.ObjectArrays;
@@ -189,7 +190,11 @@
   }
 
   public JvmTestBuilder addAndroidBuildVersion() {
-    addVmArguments("-D" + AndroidBuildVersion.PROPERTY + "=10000");
+    return addAndroidBuildVersion(AndroidApiLevel.ANDROID_PLATFORM);
+  }
+
+  public JvmTestBuilder addAndroidBuildVersion(AndroidApiLevel apiLevel) {
+    addVmArguments("-D" + AndroidBuildVersion.PROPERTY + "=" + apiLevel.getLevel());
     return addProgramClasses(AndroidBuildVersion.class);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelIndirectTargetWithDifferentApiLevelTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelIndirectTargetWithDifferentApiLevelTest.java
new file mode 100644
index 0000000..f859e30
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelIndirectTargetWithDifferentApiLevelTest.java
@@ -0,0 +1,199 @@
+// Copyright (c) 2022, 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.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompileResult;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.util.Collections;
+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 ApiModelIndirectTargetWithDifferentApiLevelTest extends TestBase {
+
+  private final AndroidApiLevel ifaceApiLevel = AndroidApiLevel.M;
+  private final AndroidApiLevel classApiLevel = AndroidApiLevel.M;
+  private final AndroidApiLevel classMethodApiLevel = AndroidApiLevel.O_MR1;
+
+  @Parameter public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+  }
+
+  private boolean isGreaterOrEqualToIfaceMockLevel() {
+    return parameters.getApiLevel().isGreaterThanOrEqualTo(ifaceApiLevel);
+  }
+
+  private boolean isGreaterOrEqualToClassMethodMockLevel() {
+    return parameters.getApiLevel().isGreaterThanOrEqualTo(classMethodApiLevel);
+  }
+
+  private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception {
+    testBuilder
+        .addProgramClasses(Main.class, ProgramJoiner.class)
+        .addLibraryClasses(LibraryClass.class, LibraryInterface.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .setMinApi(parameters.getApiLevel())
+        .addAndroidBuildVersion(parameters.getApiLevel())
+        .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+        .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, classApiLevel))
+        .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
+        .apply(
+            setMockApiLevelForMethod(
+                LibraryClass.class.getDeclaredMethod("foo"), classMethodApiLevel))
+        .apply(setMockApiLevelForClass(LibraryInterface.class, ifaceApiLevel))
+        .apply(
+            setMockApiLevelForMethod(
+                LibraryInterface.class.getDeclaredMethod("foo"), ifaceApiLevel));
+  }
+
+  private void setupRunEnvironment(TestCompileResult<?, ?> compileResult) {
+    compileResult
+        .applyIf(
+            isGreaterOrEqualToClassMethodMockLevel(),
+            b -> b.addRunClasspathClasses(LibraryClass.class, LibraryInterface.class))
+        .applyIf(
+            !isGreaterOrEqualToClassMethodMockLevel() && isGreaterOrEqualToIfaceMockLevel(),
+            b ->
+                b.addRunClasspathClasses(LibraryInterface.class)
+                    .addRunClasspathClassFileData(
+                        transformer(LibraryClass.class).removeMethodsWithName("foo").transform()));
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    assumeTrue(parameters.isCfRuntime() && parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
+    testForJvm()
+        .addProgramClasses(Main.class, ProgramJoiner.class)
+        .addAndroidBuildVersion(parameters.getApiLevel())
+        .addLibraryClasses(LibraryClass.class, LibraryInterface.class)
+        .run(parameters.getRuntime(), Main.class)
+        .apply(result -> checkOutput(result, false));
+  }
+
+  @Test
+  public void testD8Debug() throws Exception {
+    testForD8(parameters.getBackend())
+        .setMode(CompilationMode.DEBUG)
+        .apply(this::setupTestBuilder)
+        .compile()
+        .apply(this::setupRunEnvironment)
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), Main.class)
+        .apply(result -> checkOutput(result, false));
+  }
+
+  @Test
+  public void testD8Release() throws Exception {
+    testForD8(parameters.getBackend())
+        .setMode(CompilationMode.RELEASE)
+        .apply(this::setupTestBuilder)
+        .compile()
+        .apply(this::setupRunEnvironment)
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), Main.class)
+        .apply(result -> checkOutput(result, false));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .apply(this::setupTestBuilder)
+        .addKeepMainRule(Main.class)
+        .addKeepClassAndMembersRules(ProgramJoiner.class)
+        .compile()
+        .inspect(this::inspect)
+        .apply(this::setupRunEnvironment)
+        .run(parameters.getRuntime(), Main.class)
+        .apply(result -> checkOutput(result, true));
+  }
+
+  private void checkOutput(SingleTestRunResult<?> runResult, boolean isR8) {
+    if (isGreaterOrEqualToClassMethodMockLevel()) {
+      runResult.assertSuccessWithOutputLines("LibraryClass::foo");
+    } else if (isGreaterOrEqualToIfaceMockLevel()) {
+      if (isR8) {
+        runResult.assertFailureWithErrorThatThrows(NoSuchMethodError.class);
+      } else {
+        runResult.assertFailureWithErrorThatThrows(AbstractMethodError.class);
+      }
+    } else if (isR8 && parameters.isCfRuntime()) {
+      // TODO(b/254510678): R8 should not rebind to the library method.
+      runResult.assertFailureWithErrorThatThrows(NoClassDefFoundError.class);
+    } else {
+      runResult.assertSuccessWithOutputLines("Hello World");
+    }
+    runResult.applyIf(
+        !isGreaterOrEqualToIfaceMockLevel()
+            && parameters.isDexRuntime()
+            && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V7_0_0),
+        result -> result.assertStderrMatches(not(containsString("This dex file is invalid"))));
+  }
+
+  private void inspect(CodeInspector inspector) throws Exception {
+    // TODO(b/254510678): We should outline the call to ProgramJoiner.foo.
+    verifyThat(
+            inspector,
+            parameters,
+            Reference.method(
+                Reference.classFromClass(ProgramJoiner.class),
+                "foo",
+                Collections.emptyList(),
+                null))
+        .isNotOutlinedFrom(Main.class.getDeclaredMethod("main", String[].class));
+  }
+
+  // Only present from api level 23.
+  public static class LibraryClass {
+
+    // Only present from api level 27;
+    public void foo() {
+      System.out.println("LibraryClass::foo");
+    }
+  }
+
+  // Present from 23
+  public interface LibraryInterface {
+
+    // Present from 23
+    void foo();
+  }
+
+  public static class ProgramJoiner extends LibraryClass implements LibraryInterface {}
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      if (AndroidBuildVersion.VERSION >= 23) {
+        new ProgramJoiner().foo();
+      } else {
+        System.out.println("Hello World");
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelIndirectTargetWithSameApiLevelTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelIndirectTargetWithSameApiLevelTest.java
new file mode 100644
index 0000000..a0cc571
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelIndirectTargetWithSameApiLevelTest.java
@@ -0,0 +1,181 @@
+// Copyright (c) 2022, 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.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompileResult;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.util.Collections;
+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;
+
+// The difference between this test and the ApiModelIndirectTargetWithDifferentApiLevelTest is
+// what we should rebind to. If there is a method definition in the class hierarchy and it has
+// the same api-level as one in an interface we should still pick the class.
+@RunWith(Parameterized.class)
+public class ApiModelIndirectTargetWithSameApiLevelTest extends TestBase {
+
+  private final AndroidApiLevel mockApiLevel = AndroidApiLevel.M;
+
+  @Parameter public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+  }
+
+  private boolean isGreaterOrEqualToMockLevel() {
+    return parameters.getApiLevel().isGreaterThanOrEqualTo(mockApiLevel);
+  }
+
+  private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception {
+    testBuilder
+        .addProgramClasses(Main.class, ProgramJoiner.class)
+        .addLibraryClasses(LibraryClass.class, LibraryInterface.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .setMinApi(parameters.getApiLevel())
+        .addAndroidBuildVersion(parameters.getApiLevel())
+        .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+        .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, mockApiLevel))
+        .apply(setMockApiLevelForClass(LibraryClass.class, mockApiLevel))
+        .apply(setMockApiLevelForMethod(LibraryClass.class.getDeclaredMethod("foo"), mockApiLevel))
+        .apply(setMockApiLevelForClass(LibraryInterface.class, mockApiLevel))
+        .apply(
+            setMockApiLevelForMethod(
+                LibraryInterface.class.getDeclaredMethod("foo"), mockApiLevel));
+  }
+
+  private void setupRunEnvironment(TestCompileResult<?, ?> compileResult) {
+    compileResult.applyIf(
+        isGreaterOrEqualToMockLevel(),
+        b -> b.addRunClasspathClasses(LibraryClass.class, LibraryInterface.class));
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    assumeTrue(parameters.isCfRuntime() && parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
+    testForJvm()
+        .addProgramClasses(Main.class, ProgramJoiner.class)
+        .addAndroidBuildVersion(parameters.getApiLevel())
+        .addLibraryClasses(LibraryClass.class, LibraryInterface.class)
+        .run(parameters.getRuntime(), Main.class)
+        .apply(result -> checkOutput(result, false));
+  }
+
+  @Test
+  public void testD8Debug() throws Exception {
+    testForD8(parameters.getBackend())
+        .setMode(CompilationMode.DEBUG)
+        .apply(this::setupTestBuilder)
+        .compile()
+        .apply(this::setupRunEnvironment)
+        .run(parameters.getRuntime(), Main.class)
+        .apply(result -> checkOutput(result, false))
+        .inspect(this::inspect);
+  }
+
+  @Test
+  public void testD8Release() throws Exception {
+    testForD8(parameters.getBackend())
+        .setMode(CompilationMode.RELEASE)
+        .apply(this::setupTestBuilder)
+        .compile()
+        .apply(this::setupRunEnvironment)
+        .run(parameters.getRuntime(), Main.class)
+        .apply(result -> checkOutput(result, false))
+        .inspect(this::inspect);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .apply(this::setupTestBuilder)
+        .addKeepMainRule(Main.class)
+        .addKeepClassAndMembersRules(ProgramJoiner.class)
+        .compile()
+        .inspect(this::inspect)
+        .apply(this::setupRunEnvironment)
+        .run(parameters.getRuntime(), Main.class)
+        .apply(result -> checkOutput(result, true));
+  }
+
+  private void checkOutput(SingleTestRunResult<?> runResult, boolean isR8) {
+    if (isGreaterOrEqualToMockLevel()) {
+      runResult.assertSuccessWithOutputLines("LibraryClass::foo");
+    } else if (isR8 && parameters.isCfRuntime()) {
+      // TODO(b/254510678): R8 should not rebind to the library method.
+      runResult.assertFailureWithErrorThatThrows(NoClassDefFoundError.class);
+    } else {
+      runResult.assertSuccessWithOutputLines("Hello World");
+    }
+    runResult.applyIf(
+        !isGreaterOrEqualToMockLevel()
+            && parameters.isDexRuntime()
+            && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V7_0_0),
+        result -> result.assertStderrMatches(not(containsString("This dex file is invalid"))));
+  }
+
+  private void inspect(CodeInspector inspector) throws Exception {
+    // TODO(b/254510678): We should outline the call to ProgramClass.foo.
+    verifyThat(
+            inspector,
+            parameters,
+            Reference.method(
+                Reference.classFromClass(ProgramJoiner.class),
+                "foo",
+                Collections.emptyList(),
+                null))
+        .isNotOutlinedFrom(Main.class.getDeclaredMethod("main", String[].class));
+  }
+
+  // Only present from api level 23.
+  public static class LibraryClass {
+
+    // Only present from api level 27;
+    public void foo() {
+      System.out.println("LibraryClass::foo");
+    }
+  }
+
+  // Present from 23
+  public interface LibraryInterface {
+
+    // Present from 23
+    void foo();
+  }
+
+  public static class ProgramJoiner extends LibraryClass implements LibraryInterface {}
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      if (AndroidBuildVersion.VERSION >= 23) {
+        new ProgramJoiner().foo();
+      } else {
+        System.out.println("Hello World");
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
index 016a147..20d345a 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -213,6 +213,11 @@
   }
 
   static ApiModelingMethodVerificationHelper verifyThat(
+      CodeInspector inspector, TestParameters parameters, MethodReference method) {
+    return new ApiModelingMethodVerificationHelper(inspector, parameters, method);
+  }
+
+  static ApiModelingMethodVerificationHelper verifyThat(
       CodeInspector inspector, TestParameters parameters, Constructor method) {
     return new ApiModelingMethodVerificationHelper(
         inspector, parameters, Reference.methodFromMethod(method));
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesInDexShrinkingTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesInDexShrinkingTest.java
new file mode 100644
index 0000000..9829235
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesInDexShrinkingTest.java
@@ -0,0 +1,349 @@
+// Copyright (c) 2022, 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.desugar.nestaccesscontrol;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.TypeSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+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;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class NestAttributesInDexShrinkingTest extends NestAttributesInDexTestBase
+    implements Opcodes {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
+  }
+
+  private static final String EXPECTED_OUTPUT = StringUtils.lines("Hello, world!");
+
+  @Test
+  public void testRuntime() throws Exception {
+    assumeTrue(
+        parameters.isCfRuntime()
+            && isRuntimeWithNestSupport(parameters.asCfRuntime())
+            && parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
+    testForJvm()
+        .addProgramClassFileData(
+            dumpHost(ACC_PRIVATE), dumpMember1(ACC_PRIVATE), dumpMember2(ACC_PRIVATE))
+        .run(parameters.getRuntime(), "Host")
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  private void inspect(CodeInspector inspector, boolean expectNestAttributes) {
+    ClassSubject host = inspector.clazz("Host");
+    ClassSubject member1 = inspector.clazz("Host$Member1");
+    ClassSubject member2 = inspector.clazz("Host$Member2");
+    if (expectNestAttributes) {
+      assertEquals(
+          ImmutableList.of(member2.asTypeSubject(), member1.asTypeSubject()),
+          host.getFinalNestMembersAttribute());
+    } else {
+      assertEquals(0, host.getFinalNestMembersAttribute().size());
+    }
+    TypeSubject expectedNestHostAttribute = expectNestAttributes ? host.asTypeSubject() : null;
+    assertEquals(expectedNestHostAttribute, member1.getFinalNestHostAttribute());
+    assertEquals(expectedNestHostAttribute, member2.getFinalNestHostAttribute());
+  }
+
+  private void expectNestAttributes(CodeInspector inspector) {
+    inspect(inspector, true);
+  }
+
+  private void expectNoNestAttributes(CodeInspector inspector) {
+    inspect(inspector, false);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    assumeTrue(parameters.isDexRuntime() || isRuntimeWithNestSupport(parameters.asCfRuntime()));
+    // TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
+    assertFalse(parameters.getApiLevel().getLevel() > 33);
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(
+            dumpHost(ACC_PRIVATE), dumpMember1(ACC_PRIVATE), dumpMember2(ACC_PRIVATE))
+        .addKeepMainRule("Host")
+        .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(options -> options.emitNestAnnotationsInDex = true)
+        .compile()
+        .inspect(inspector -> assertEquals(1, inspector.allClasses().size()))
+        .run(parameters.getRuntime(), "Host")
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testR8KeepHostWithPrivateMembers() throws Exception {
+    assumeTrue(parameters.isDexRuntime() || isRuntimeWithNestSupport(parameters.asCfRuntime()));
+    // TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
+    assertFalse(parameters.getApiLevel().getLevel() > 33);
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(
+            dumpHost(ACC_PRIVATE), dumpMember1(ACC_PRIVATE), dumpMember2(ACC_PRIVATE))
+        .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(options -> options.emitNestAnnotationsInDex = true)
+        .addKeepMainRule("Host")
+        .addKeepClassAndMembersRules("Host", "Host$Member1", "Host$Member2")
+        .compile()
+        .inspect(this::expectNestAttributes)
+        .run(parameters.getRuntime(), "Host")
+        .applyIf(
+            parameters.isCfRuntime(),
+            r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT),
+            r -> r.assertFailureWithErrorThatThrows(IllegalAccessError.class));
+  }
+
+  @Test
+  public void testR8KeepHostWithPublicMembers() throws Exception {
+    assumeTrue(parameters.isDexRuntime() || isRuntimeWithNestSupport(parameters.asCfRuntime()));
+    // TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
+    assertFalse(parameters.getApiLevel().getLevel() > 33);
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(
+            dumpHost(ACC_PUBLIC), dumpMember1(ACC_PUBLIC), dumpMember2(ACC_PUBLIC))
+        .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(options -> options.emitNestAnnotationsInDex = true)
+        .addKeepMainRule("Host")
+        .addKeepClassAndMembersRules("Host", "Host$Member1", "Host$Member2")
+        .compile()
+        .inspect(this::expectNoNestAttributes)
+        .run(parameters.getRuntime(), "Host")
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  /*
+    Dump of:
+
+    public class Host {
+      public static void main(String[] args) {
+        new Host().h1();
+        System.out.println();
+      }
+
+      static class Member1 {
+        private void m(Host host) {  // private or public
+          host.h2("Hello");
+        }
+      }
+
+      static class Member2 {
+        private void m(Host host) {  // private or public
+          host.h2(", world!");
+        }
+      }
+
+      private void h1() {  // private or public
+        new Member1().m(this);
+        new Member2().m(this);
+      }
+
+      private void h2(String message) {  // private or public
+        System.out.print(message);
+      }
+    }
+
+    compiled with `-target 11`. Not a transformer here, as transforming the javac post nest
+    access methods is not feasible.
+  */
+
+  public static byte[] dumpHost(int methodAccess) throws Exception {
+    assert methodAccess == ACC_PUBLIC || methodAccess == ACC_PRIVATE;
+
+    ClassWriter classWriter = new ClassWriter(0);
+    MethodVisitor methodVisitor;
+
+    classWriter.visit(V11, ACC_PUBLIC | ACC_SUPER, "Host", null, "java/lang/Object", null);
+    classWriter.visitSource("Host.java", null);
+    classWriter.visitNestMember("Host$Member2");
+    classWriter.visitNestMember("Host$Member1");
+    classWriter.visitInnerClass("Host$Member2", "Host", "Member2", ACC_STATIC);
+    classWriter.visitInnerClass("Host$Member1", "Host", "Member1", ACC_STATIC);
+    {
+      methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(1, label0);
+      methodVisitor.visitVarInsn(ALOAD, 0);
+      methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+      methodVisitor.visitInsn(RETURN);
+      methodVisitor.visitMaxs(1, 1);
+      methodVisitor.visitEnd();
+    }
+    {
+      methodVisitor =
+          classWriter.visitMethod(
+              ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(3, label0);
+      methodVisitor.visitTypeInsn(NEW, "Host");
+      methodVisitor.visitInsn(DUP);
+      methodVisitor.visitMethodInsn(INVOKESPECIAL, "Host", "<init>", "()V", false);
+      methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "Host", "h1", "()V", false);
+      Label label1 = new Label();
+      methodVisitor.visitLabel(label1);
+      methodVisitor.visitLineNumber(4, label1);
+      methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+      methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "()V", false);
+      Label label2 = new Label();
+      methodVisitor.visitLabel(label2);
+      methodVisitor.visitLineNumber(5, label2);
+      methodVisitor.visitInsn(RETURN);
+      methodVisitor.visitMaxs(2, 1);
+      methodVisitor.visitEnd();
+    }
+    {
+      methodVisitor = classWriter.visitMethod(methodAccess, "h1", "()V", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(18, label0);
+      methodVisitor.visitTypeInsn(NEW, "Host$Member1");
+      methodVisitor.visitInsn(DUP);
+      methodVisitor.visitMethodInsn(INVOKESPECIAL, "Host$Member1", "<init>", "()V", false);
+      methodVisitor.visitVarInsn(ALOAD, 0);
+      methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "Host$Member1", "m", "(LHost;)V", false);
+      Label label1 = new Label();
+      methodVisitor.visitLabel(label1);
+      methodVisitor.visitLineNumber(19, label1);
+      methodVisitor.visitTypeInsn(NEW, "Host$Member2");
+      methodVisitor.visitInsn(DUP);
+      methodVisitor.visitMethodInsn(INVOKESPECIAL, "Host$Member2", "<init>", "()V", false);
+      methodVisitor.visitVarInsn(ALOAD, 0);
+      methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "Host$Member2", "m", "(LHost;)V", false);
+      Label label2 = new Label();
+      methodVisitor.visitLabel(label2);
+      methodVisitor.visitLineNumber(20, label2);
+      methodVisitor.visitInsn(RETURN);
+      methodVisitor.visitMaxs(2, 1);
+      methodVisitor.visitEnd();
+    }
+    {
+      methodVisitor =
+          classWriter.visitMethod(methodAccess, "h2", "(Ljava/lang/String;)V", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(23, label0);
+      methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+      methodVisitor.visitVarInsn(ALOAD, 1);
+      methodVisitor.visitMethodInsn(
+          INVOKEVIRTUAL, "java/io/PrintStream", "print", "(Ljava/lang/String;)V", false);
+      Label label1 = new Label();
+      methodVisitor.visitLabel(label1);
+      methodVisitor.visitLineNumber(24, label1);
+      methodVisitor.visitInsn(RETURN);
+      methodVisitor.visitMaxs(2, 2);
+      methodVisitor.visitEnd();
+    }
+    classWriter.visitEnd();
+
+    return classWriter.toByteArray();
+  }
+
+  public static byte[] dumpMember1(int methodAccess) throws Exception {
+    assert methodAccess == ACC_PUBLIC || methodAccess == ACC_PRIVATE;
+
+    ClassWriter classWriter = new ClassWriter(0);
+    MethodVisitor methodVisitor;
+
+    classWriter.visit(V11, ACC_SUPER, "Host$Member1", null, "java/lang/Object", null);
+    classWriter.visitSource("Host.java", null);
+    classWriter.visitNestHost("Host");
+    classWriter.visitInnerClass("Host$Member1", "Host", "Member1", ACC_STATIC);
+    {
+      methodVisitor = classWriter.visitMethod(0, "<init>", "()V", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(7, label0);
+      methodVisitor.visitVarInsn(ALOAD, 0);
+      methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+      methodVisitor.visitInsn(RETURN);
+      methodVisitor.visitMaxs(1, 1);
+      methodVisitor.visitEnd();
+    }
+    {
+      methodVisitor = classWriter.visitMethod(methodAccess, "m", "(LHost;)V", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(9, label0);
+      methodVisitor.visitVarInsn(ALOAD, 1);
+      methodVisitor.visitLdcInsn("Hello");
+      methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "Host", "h2", "(Ljava/lang/String;)V", false);
+      Label label1 = new Label();
+      methodVisitor.visitLabel(label1);
+      methodVisitor.visitLineNumber(10, label1);
+      methodVisitor.visitInsn(RETURN);
+      methodVisitor.visitMaxs(2, 2);
+      methodVisitor.visitEnd();
+    }
+    classWriter.visitEnd();
+
+    return classWriter.toByteArray();
+  }
+
+  public static byte[] dumpMember2(int methodAccess) throws Exception {
+    assert methodAccess == ACC_PUBLIC || methodAccess == ACC_PRIVATE;
+
+    ClassWriter classWriter = new ClassWriter(0);
+    MethodVisitor methodVisitor;
+
+    classWriter.visit(V11, ACC_SUPER, "Host$Member2", null, "java/lang/Object", null);
+    classWriter.visitSource("Host.java", null);
+    classWriter.visitNestHost("Host");
+    classWriter.visitInnerClass("Host$Member2", "Host", "Member2", ACC_STATIC);
+    {
+      methodVisitor = classWriter.visitMethod(0, "<init>", "()V", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(13, label0);
+      methodVisitor.visitVarInsn(ALOAD, 0);
+      methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+      methodVisitor.visitInsn(RETURN);
+      methodVisitor.visitMaxs(1, 1);
+      methodVisitor.visitEnd();
+    }
+    {
+      methodVisitor = classWriter.visitMethod(methodAccess, "m", "(LHost;)V", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(14, label0);
+      methodVisitor.visitVarInsn(ALOAD, 1);
+      methodVisitor.visitLdcInsn(", world!");
+      methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "Host", "h2", "(Ljava/lang/String;)V", false);
+      methodVisitor.visitInsn(RETURN);
+      methodVisitor.visitMaxs(2, 2);
+      methodVisitor.visitEnd();
+    }
+    classWriter.visitEnd();
+
+    return classWriter.toByteArray();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesInDexTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesInDexTest.java
index b5ad377..25f211f 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesInDexTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesInDexTest.java
@@ -9,8 +9,8 @@
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
 
-import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRunResult;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.desugar.nestaccesscontrol.NestAttributesInDexTest.Host.Member1;
 import com.android.tools.r8.desugar.nestaccesscontrol.NestAttributesInDexTest.Host.Member2;
@@ -36,7 +36,7 @@
 import org.objectweb.asm.Opcodes;
 
 @RunWith(Parameterized.class)
-public class NestAttributesInDexTest extends TestBase {
+public class NestAttributesInDexTest extends NestAttributesInDexTestBase {
 
   @Parameter() public TestParameters parameters;
 
@@ -52,11 +52,35 @@
           "true", "true", "true", "false", "false", "true", "true", "true", "false", "false",
           "true", "true", "true");
 
+  // Right now R8 removes the nest attributes if they are not required for runtime execution, so
+  // most reflective calls will return false.
+  private static final String R8_EXPECTED_OUTPUT =
+      StringUtils.lines(
+          "false", "false", "false", "true", "false", "false", "true", "false", "false", "false",
+          "false", "false", "true", "false", "false", "false", "false", "false", "true", "false",
+          "false", "true", "true", "true");
+
+  private void checkResult(TestRunResult<?> result) {
+    if (isRuntimeWithNestSupport(parameters.getRuntime())) {
+      result.assertSuccessWithOutput(EXPECTED_OUTPUT);
+    } else {
+      result.assertFailureWithErrorThatThrows(NoSuchMethodError.class);
+    }
+  }
+
+  private void checkResultR8(TestRunResult<?> result) {
+    if (isRuntimeWithNestSupport(parameters.getRuntime())) {
+      result.assertSuccessWithOutput(R8_EXPECTED_OUTPUT);
+    } else {
+      result.assertFailureWithErrorThatThrows(NoSuchMethodError.class);
+    }
+  }
+
   @Test
   public void testRuntime() throws Exception {
     assumeTrue(
         parameters.isCfRuntime()
-            && parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK11)
+            && isRuntimeWithNestSupport(parameters.asCfRuntime())
             && parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
     testForJvm()
         .addProgramClassFileData(getTransformedClasses())
@@ -93,8 +117,7 @@
         .compile()
         .inspect(inspector -> inspect(inspector, true))
         .run(parameters.getRuntime(), TestClass.class)
-        // No Art versions have support for nest attributes yet.
-        .assertFailureWithErrorThatThrows(NoSuchMethodError.class);
+        .apply(this::checkResult);
   }
 
   @Test
@@ -109,7 +132,84 @@
         .compile()
         .inspect(inspector -> inspect(inspector, false))
         .run(parameters.getRuntime(), TestClass.class)
-        .assertFailureWithErrorThatThrows(NoSuchMethodError.class);
+        .apply(this::checkResult);
+  }
+
+  @Test
+  public void testR8NoKeep() throws Exception {
+    assumeTrue(parameters.isDexRuntime() || isRuntimeWithNestSupport(parameters.asCfRuntime()));
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(getTransformedClasses())
+        .addProgramClasses(OtherHost.class)
+        .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(options -> options.emitNestAnnotationsInDex = true)
+        .addKeepMainRule(TestClass.class)
+        .compile()
+        // Don't expect any nest info. The classes Host, Member1, Member2 and OtherHost remains
+        // due to the use of class constants in the code, but they have no methods so no nest
+        // attributes are required for runtime execution.
+        .inspect(inspector -> inspect(inspector, false))
+        .run(parameters.getRuntime(), TestClass.class)
+        .apply(this::checkResultR8);
+  }
+
+  @Test
+  public void testR8KeepHost() throws Exception {
+    assumeTrue(parameters.isDexRuntime() || isRuntimeWithNestSupport(parameters.asCfRuntime()));
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(getTransformedClasses())
+        .addProgramClasses(OtherHost.class)
+        .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(options -> options.emitNestAnnotationsInDex = true)
+        .addKeepMainRule(TestClass.class)
+        .addKeepClassRules(Host.class)
+        .compile()
+        // Don't expect any nest info. Class Host is kept and the classes Member1, Members and
+        // OtherHost remains due to the use of class constants in the code, but they have no methods
+        // so no nest attributes are required for runtime execution.
+        // TODO(b/130716228#comment5): How to keep nest attributes?
+        .inspect(inspector -> inspect(inspector, false))
+        .run(parameters.getRuntime(), TestClass.class)
+        .apply(this::checkResultR8);
+  }
+
+  @Test
+  public void testR8KeepMembers() throws Exception {
+    assumeTrue(parameters.isDexRuntime() || isRuntimeWithNestSupport(parameters.asCfRuntime()));
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(getTransformedClasses())
+        .addProgramClasses(OtherHost.class)
+        .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(options -> options.emitNestAnnotationsInDex = true)
+        .addKeepMainRule(TestClass.class)
+        .addKeepClassRules(Member1.class, Member2.class)
+        .compile()
+        // Don't expect any nest info. Member1 and Member2 are kept and the classes Host and
+        // OtherHost remains due to the use of class constants in the code, but they have no
+        // methods so no nest attributes are required for runtime execution.
+        // TODO(b/130716228#comment5): How to keep nest attributes?
+        .inspect(inspector -> inspect(inspector, false))
+        .run(parameters.getRuntime(), TestClass.class)
+        .apply(this::checkResultR8);
+  }
+
+  @Test
+  public void testR8KeepBoth() throws Exception {
+    assumeTrue(parameters.isDexRuntime() || isRuntimeWithNestSupport(parameters.asCfRuntime()));
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(getTransformedClasses())
+        .addProgramClasses(OtherHost.class)
+        .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(options -> options.emitNestAnnotationsInDex = true)
+        .addKeepMainRule(TestClass.class)
+        .addKeepClassRules(Host.class, Member1.class, Member2.class)
+        .compile()
+        // Don't expect any nest info. All of Host, Member1 and Member2 are kept,
+        // but they have no methods so no nest attributes are required for runtime execution.
+        // TODO(b/130716228#comment5): How to keep nest attributes?
+        .inspect(inspector -> inspect(inspector, false))
+        .run(parameters.getRuntime(), TestClass.class)
+        .apply(this::checkResultR8);
   }
 
   public Collection<byte[]> getTransformedClasses() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesInDexTestBase.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesInDexTestBase.java
new file mode 100644
index 0000000..9f67110
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesInDexTestBase.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2022, 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.desugar.nestaccesscontrol;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.TestRuntime.DexRuntime;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public abstract class NestAttributesInDexTestBase extends TestBase {
+
+  protected boolean isRuntimeWithNestSupport(TestRuntime runtime) {
+    if (runtime.isCf()) {
+      return isRuntimeWithNestSupport(runtime.asCf());
+    } else {
+      return isRuntimeWithNestSupport(runtime.asDex());
+    }
+  }
+
+  protected boolean isRuntimeWithNestSupport(CfRuntime runtime) {
+    return runtime.isNewerThanOrEqual(CfVm.JDK11);
+  }
+
+  protected boolean isRuntimeWithNestSupport(DexRuntime runtime) {
+    // No Art versions have support for nest attributes yet.
+    return false;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteEnumTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteEnumTest.java
index 939eb30..b7c8c16 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteEnumTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteEnumTest.java
@@ -3,16 +3,23 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
+import static com.android.tools.r8.cf.methodhandles.fields.ClassFieldMethodHandleTest.Main.assertEquals;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
 import java.nio.file.Path;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.List;
+import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -58,7 +65,7 @@
   }
 
   @Test
-  public void testR8() throws Exception {
+  public void testKotlincFailsRenamed() throws Exception {
     R8TestCompileResult r8libResult =
         testForR8(parameters.getBackend())
             .addProgramFiles(jarMap.getForConfiguration(kotlinc, targetVersion))
@@ -74,24 +81,60 @@
                   assertThat(direction, isPresentAndNotRenamed());
                   direction.allFields().forEach(field -> assertTrue(field.isRenamed()));
                 });
-    Path libJar = r8libResult.writeToZip();
+    ProcessResult kotlinCompileResult =
+        kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+            .addClasspathFiles(r8libResult.writeToZip())
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/enum_app", "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compileRaw();
+    assertEquals(1, kotlinCompileResult.exitCode);
+    assertThat(kotlinCompileResult.stderr, containsString("unresolved reference"));
+    assertThat(kotlinCompileResult.stderr, containsString("Direction.UP"));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    R8TestCompileResult r8libResult =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(jarMap.getForConfiguration(kotlinc, targetVersion))
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar())
+            .addKeepKotlinMetadata()
+            .addKeepEnumsRule()
+            .addKeepClassRules(DIRECTION_TYPE_NAME)
+            .addKeepClassAndMembersRulesWithAllowObfuscation(DIRECTION_TYPE_NAME)
+            .compile()
+            .inspect(
+                inspector -> {
+                  ClassSubject direction = inspector.clazz(DIRECTION_TYPE_NAME);
+                  assertThat(direction, isPresentAndNotRenamed());
+                  direction.allFields().forEach(field -> assertTrue(field.isRenamed()));
+                  KmClassSubject kmClass = direction.getKmClass();
+                  List<String> expectedEnumNames = Arrays.asList("a", "b", "c", "d");
+                  Assert.assertEquals(expectedEnumNames, kmClass.getEnumEntries());
+                });
     Path output =
         kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
-            .addClasspathFiles(libJar)
+            .addClasspathFiles(jarMap.getForConfiguration(kotlinc, targetVersion))
             .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/enum_app", "main"))
             .compile();
     Path path =
         testForR8(parameters.getBackend())
-            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
+            .addClasspathFiles(
+                kotlinc.getKotlinStdlibJar(),
+                kotlinc.getKotlinReflectJar(),
+                jarMap.getForConfiguration(kotlinc, targetVersion))
             .addProgramFiles(output)
             .addKeepAllClassesRule()
             .addApplyMapping(r8libResult.getProguardMap())
             .compile()
             .writeToZip();
-    // TODO(b/259389417): We should rename enum values in metadata.
     testForRuntime(parameters)
-        .addProgramFiles(libJar, kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), path)
+        .addProgramFiles(
+            r8libResult.writeToZip(),
+            kotlinc.getKotlinStdlibJar(),
+            kotlinc.getKotlinReflectJar(),
+            path)
         .run(parameters.getRuntime(), PKG + ".enum_app.MainKt")
-        .assertFailureWithErrorThatThrows(NoSuchFieldError.class);
+        .assertSuccessWithOutputLines(EXPECTED);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/NewArrayInCatchRangeTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/NewArrayInCatchRangeTest.java
new file mode 100644
index 0000000..8cc0379
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/arrays/NewArrayInCatchRangeTest.java
@@ -0,0 +1,69 @@
+// Copyright (c) 2022, 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.rewrite.arrays;
+
+import static org.junit.Assume.assumeTrue;
+
+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 org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+// Regression test for issue found in b/259986613
+@RunWith(Parameterized.class)
+public class NewArrayInCatchRangeTest extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("1");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+  }
+
+  public NewArrayInCatchRangeTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForRuntime(parameters)
+        .addInnerClasses(NewArrayInCatchRangeTest.class)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testReleaseD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8(parameters.getBackend())
+        .release()
+        .setMinApi(parameters.getApiLevel())
+        .addInnerClasses(NewArrayInCatchRangeTest.class)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  static class TestClass {
+
+    public static int foo() {
+      int value = 1;
+      int[] array = new int[1];
+      try {
+        array[0] = value;
+      } catch (RuntimeException e) {
+        return array[0];
+      }
+      return array[0];
+    }
+
+    public static void main(String[] args) {
+      System.out.println(foo());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/NewArrayInTwoCatchRangesTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/NewArrayInTwoCatchRangesTest.java
new file mode 100644
index 0000000..ab11b3d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/arrays/NewArrayInTwoCatchRangesTest.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2022, 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.rewrite.arrays;
+
+import static org.junit.Assume.assumeTrue;
+
+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 org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class NewArrayInTwoCatchRangesTest extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("1");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+  }
+
+  public NewArrayInTwoCatchRangesTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForRuntime(parameters)
+        .addInnerClasses(NewArrayInTwoCatchRangesTest.class)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testReleaseD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8(parameters.getBackend())
+        .release()
+        .setMinApi(parameters.getApiLevel())
+        .addInnerClasses(NewArrayInTwoCatchRangesTest.class)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  static class TestClass {
+
+    public static int foo() {
+      int value = 1;
+      try {
+        int[] array = new int[2];
+        try {
+          array[0] = value;
+          try {
+            array[1] = value;
+          } catch (RuntimeException e) {
+            return array[1];
+          }
+        } catch (RuntimeException e) {
+          return array[0];
+        }
+        return array[0];
+      } catch (RuntimeException e) {
+        return 42;
+      }
+    }
+
+    public static void main(String[] args) {
+      System.out.println(foo());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/librarymethodoverride/LibraryMethodOverrideOfClassMethodWithInterfaceTest.java b/src/test/java/com/android/tools/r8/shaking/librarymethodoverride/LibraryMethodOverrideOfClassMethodWithInterfaceTest.java
new file mode 100644
index 0000000..247b780
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/librarymethodoverride/LibraryMethodOverrideOfClassMethodWithInterfaceTest.java
@@ -0,0 +1,101 @@
+// Copyright (c) 2022, 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.shaking.librarymethodoverride;
+
+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.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.Enqueuer;
+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 LibraryMethodOverrideOfClassMethodWithInterfaceTest extends TestBase {
+
+  private final String EXPECTED = "ProgramClass::foo";
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testRuntime() throws Exception {
+    testForRuntime(parameters)
+        .addProgramClasses(ProgramClass.class, ProgramInterface.class, Main.class)
+        .addLibraryClasses(LibraryClass.class)
+        .addRunClasspathFiles(buildOnDexRuntime(parameters, LibraryClass.class))
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(ProgramClass.class, ProgramInterface.class, Main.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .addLibraryClasses(LibraryClass.class)
+        .addOptionsModification(
+            options -> options.testing.enqueuerInspector = this::verifyLibraryOverrideInformation)
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(Main.class)
+        .compile()
+        .addBootClasspathClasses(LibraryClass.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  private void verifyLibraryOverrideInformation(AppInfoWithLiveness appInfo, Enqueuer.Mode mode) {
+    DexItemFactory dexItemFactory = appInfo.dexItemFactory();
+    DexProgramClass clazz =
+        appInfo
+            .definitionFor(dexItemFactory.createType(descriptor(ProgramInterface.class)))
+            .asProgramClass();
+    DexEncodedMethod method =
+        clazz.lookupVirtualMethod(m -> m.getReference().name.toString().equals("foo"));
+    // TODO(b/259531498): We should not mark the interface method as overriding.
+    assertTrue(method.isLibraryMethodOverride().isTrue());
+  }
+
+  public abstract static class LibraryClass {
+
+    abstract void foo();
+  }
+
+  public interface ProgramInterface {
+
+    void foo();
+  }
+
+  public static class ProgramClass extends LibraryClass implements ProgramInterface {
+
+    @Override
+    public void foo() {
+      System.out.println("ProgramClass::foo");
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      callFoo(new ProgramClass());
+    }
+
+    public static void callFoo(ProgramInterface i) {
+      i.foo();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmClassSubject.java
index d87e74e..1da8375 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmClassSubject.java
@@ -141,6 +141,11 @@
   }
 
   @Override
+  public List<String> getEnumEntries() {
+    return null;
+  }
+
+  @Override
   public String getCompanionObject() {
     return null;
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java
index cc9bfd0..34f7c4c 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java
@@ -127,6 +127,11 @@
   }
 
   @Override
+  public List<String> getEnumEntries() {
+    return kmClass.getEnumEntries();
+  }
+
+  @Override
   public List<KmTypeParameter> getKmTypeParameters() {
     return kmClass.getTypeParameters();
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmClassSubject.java
index 9d776d3..84b493a 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/KmClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmClassSubject.java
@@ -26,5 +26,7 @@
 
   public abstract List<ClassSubject> getSealedSubclasses();
 
+  public abstract List<String> getEnumEntries();
+
   public abstract String getCompanionObject();
 }