Merge commit 'e114917379e8f59a61d321d01dc6f5145d0e929e' into dev-release
diff --git a/.gitignore b/.gitignore
index 17af682..dbe9747 100644
--- a/.gitignore
+++ b/.gitignore
@@ -135,32 +135,14 @@
 third_party/openjdk/desugar_jdk_libs_releases/1.1.1.tar.gz
 third_party/openjdk/desugar_jdk_libs_releases/1.1.5
 third_party/openjdk/desugar_jdk_libs_releases/1.1.5.tar.gz
-third_party/openjdk/jdk-17/linux
-third_party/openjdk/jdk-17/linux.tar.gz
-third_party/openjdk/jdk-17/osx
-third_party/openjdk/jdk-17/osx.tar.gz
-third_party/openjdk/jdk-17/windows
-third_party/openjdk/jdk-17/windows.tar.gz
-third_party/openjdk/jdk-16/linux
-third_party/openjdk/jdk-16/linux.tar.gz
-third_party/openjdk/jdk-16/osx
-third_party/openjdk/jdk-16/osx.tar.gz
-third_party/openjdk/jdk-16/windows
-third_party/openjdk/jdk-16/windows.tar.gz
-third_party/openjdk/jdk-15/linux
-third_party/openjdk/jdk-15/linux.tar.gz
-third_party/openjdk/jdk-15/osx
-third_party/openjdk/jdk-15/osx.tar.gz
-third_party/openjdk/jdk-15/windows
-third_party/openjdk/jdk-15/windows.tar.gz
-third_party/openjdk/jdk-11-test
+third_party/openjdk/jdk-[0-9][0-9]/linux
+third_party/openjdk/jdk-[0-9][0-9]/linux.tar.gz
+third_party/openjdk/jdk-[0-9][0-9]/osx
+third_party/openjdk/jdk-[0-9][0-9]/osx.tar.gz
+third_party/openjdk/jdk-[0-9][0-9]/windows
+third_party/openjdk/jdk-[0-9][0-9]/windows.tar.gz
 third_party/openjdk/jdk-11-test.tar.gz
-third_party/openjdk/jdk-11/linux
-third_party/openjdk/jdk-11/linux.tar.gz
-third_party/openjdk/jdk-11/osx
-third_party/openjdk/jdk-11/osx.tar.gz
-third_party/openjdk/jdk-11/windows
-third_party/openjdk/jdk-11/windows.tar.gz
+third_party/openjdk/jdk-11-test/
 third_party/openjdk/jdk8/darwin-x86
 third_party/openjdk/jdk8/darwin-x86.tar.gz
 third_party/openjdk/jdk8/linux-x86
@@ -278,6 +260,8 @@
 tools/*/art-10.0.0.tar.gz
 tools/*/host/art-12.0.0-beta4
 tools/*/host/art-12.0.0-beta4.tar.gz
+tools/*/host/art-13-master
+tools/*/host/art-13-master.tar.gz
 tools/*/art.tar.gz
 tools/*/dalvik
 tools/*/dalvik-4.0.4
diff --git a/build.gradle b/build.gradle
index 919c260..560c1de 100644
--- a/build.gradle
+++ b/build.gradle
@@ -140,6 +140,11 @@
             srcDirs = ['src/test/examplesJava17']
         }
     }
+    examplesJava18 {
+        java {
+            srcDirs = ['src/test/examplesJava18']
+        }
+    }
     examplesTestNGRunner {
         java {
             srcDirs = ['src/test/testngrunner']
@@ -333,6 +338,7 @@
                 "android_jar/lib-v30",
                 "android_jar/lib-v31",
                 "android_jar/lib-v32",
+                "android_jar/lib-v33",
                 "api-outlining/simple-app-dump",
                 "binary_compatibility_tests/compiler_api_tests",
                 "core-lambda-stubs",
@@ -386,6 +392,7 @@
                 "linux/art-9.0.0",
                 "linux/art-10.0.0",
                 "linux/host/art-12.0.0-beta4",
+                "linux/host/art-13-master",
                 "linux/dalvik",
                 "linux/dalvik-4.0.4",
                 "${osString}/dx",
@@ -397,18 +404,21 @@
                 "third_party": ["openjdk/openjdk-9.0.4/linux",
                                 "openjdk/jdk8/linux-x86",
                                 "openjdk/jdk-11/linux",
-                                "openjdk/jdk-17/linux"],
+                                "openjdk/jdk-17/linux",
+                                "openjdk/jdk-18/linux"],
         ],
         osx: [
                 "third_party": ["openjdk/openjdk-9.0.4/osx",
                                 "openjdk/jdk8/darwin-x86",
                                 "openjdk/jdk-11/osx",
-                                "openjdk/jdk-17/osx"],
+                                "openjdk/jdk-17/osx",
+                                "openjdk/jdk-18/osx"],
         ],
         windows: [
                 "third_party": ["openjdk/openjdk-9.0.4/windows",
                                 "openjdk/jdk-11/windows",
-                                "openjdk/jdk-17/windows"],
+                                "openjdk/jdk-17/windows",
+                                "openjdk/jdk-18/windows"],
         ],
 ]
 
@@ -646,6 +656,12 @@
         'jdk-17',
         JavaVersion.VERSION_17,
         false)
+setJdkCompilationWithCompatibility(
+        sourceSets.examplesJava18.compileJavaTaskName,
+        'jdk-18',
+        // TODO(b/218293990): Update Gradle to get JavaVersion.VERSION_18.
+        JavaVersion.VERSION_17,
+        false)
 
 task provideJdk11TestsDependencies(type: org.gradle.api.tasks.Copy) {
     from sourceSets.examplesTestNGRunner.compileClasspath
@@ -1595,6 +1611,7 @@
 buildExampleJarsCreateTask("Java10", sourceSets.examplesJava10)
 buildExampleJarsCreateTask("Java11", sourceSets.examplesJava11)
 buildExampleJarsCreateTask("Java17", sourceSets.examplesJava17)
+buildExampleJarsCreateTask("Java18", sourceSets.examplesJava18)
 
 task provideArtFrameworksDependencies {
     cloudDependencies.tools.forEach({ art ->
@@ -1656,6 +1673,7 @@
     dependsOn buildExampleJava10Jars
     dependsOn buildExampleJava11Jars
     dependsOn buildExampleJava17Jars
+    dependsOn buildExampleJava18Jars
     dependsOn buildExampleAndroidApi
     def examplesDir = file("src/test/examples")
     def noDexTests = [
diff --git a/infra/config/global/generated/cr-buildbucket.cfg b/infra/config/global/generated/cr-buildbucket.cfg
index 9dae144..6661615 100644
--- a/infra/config/global/generated/cr-buildbucket.cfg
+++ b/infra/config/global/generated/cr-buildbucket.cfg
@@ -46,10 +46,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "archive_release"
@@ -79,10 +75,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "desugared_library-head"
@@ -116,10 +108,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "desugared_library-jdk11_head"
@@ -155,10 +143,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "lib_desugar-archive"
@@ -188,10 +172,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-android-10.0.0"
@@ -229,10 +209,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-android-10.0.0_release"
@@ -270,10 +246,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-android-12.0.0"
@@ -311,10 +283,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-android-12.0.0_release"
@@ -352,8 +320,78 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
+    }
+    builders {
+      name: "linux-android-13.0.0"
+      swarming_host: "chrome-swarming.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "normal:true"
+      dimensions: "os:Ubuntu-16.04"
+      dimensions: "pool:luci.r8.ci"
+      exe {
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
+        cipd_version: "refs/heads/master"
+        cmd: "luciexe"
+      }
+      properties:
+        '{'
+        '  "builder_group": "internal.client.r8",'
+        '  "recipe": "rex",'
+        '  "test_options": ['
+        '    "--dex_vm=13.0.0",'
+        '    "--all_tests",'
+        '    "--tool=r8",'
+        '    "--no_internal",'
+        '    "--one_line_per_test",'
+        '    "--archive_failures"'
+        '  ]'
+        '}'
+      priority: 26
+      execution_timeout_secs: 21600
+      expiration_secs: 126000
+      build_numbers: YES
+      service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
-        key: "luci.use_realms"
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+    }
+    builders {
+      name: "linux-android-13.0.0_release"
+      swarming_host: "chrome-swarming.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "normal:true"
+      dimensions: "os:Ubuntu-16.04"
+      dimensions: "pool:luci.r8.ci"
+      exe {
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
+        cipd_version: "refs/heads/master"
+        cmd: "luciexe"
+      }
+      properties:
+        '{'
+        '  "builder_group": "internal.client.r8",'
+        '  "recipe": "rex",'
+        '  "test_options": ['
+        '    "--dex_vm=13.0.0",'
+        '    "--all_tests",'
+        '    "--tool=r8",'
+        '    "--no_internal",'
+        '    "--one_line_per_test",'
+        '    "--archive_failures"'
+        '  ]'
+        '}'
+      priority: 26
+      execution_timeout_secs: 21600
+      expiration_secs: 126000
+      build_numbers: YES
+      service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "luci.recipes.use_python3"
         value: 100
       }
     }
@@ -393,10 +431,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-android-4.0.4_release"
@@ -434,10 +468,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-android-4.4.4"
@@ -475,10 +505,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-android-4.4.4_release"
@@ -516,10 +542,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-android-5.1.1"
@@ -557,10 +579,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-android-5.1.1_release"
@@ -598,10 +616,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-android-6.0.1"
@@ -639,10 +653,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-android-6.0.1_release"
@@ -680,10 +690,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-android-7.0.0"
@@ -721,10 +727,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-android-7.0.0_release"
@@ -762,10 +764,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-android-8.1.0"
@@ -803,10 +801,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-android-8.1.0_release"
@@ -844,10 +838,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-android-9.0.0"
@@ -885,10 +875,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-android-9.0.0_release"
@@ -926,10 +912,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-d8_jctf"
@@ -967,10 +949,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-d8_jctf_release"
@@ -1008,10 +986,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-dex_default"
@@ -1048,10 +1022,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-dex_default_release"
@@ -1088,10 +1058,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-internal"
@@ -1125,10 +1091,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-internal_release"
@@ -1162,10 +1124,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-jdk11"
@@ -1202,10 +1160,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-jdk11_release"
@@ -1242,10 +1196,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-jdk8"
@@ -1282,10 +1232,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-jdk8_release"
@@ -1322,10 +1268,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-jdk9"
@@ -1362,10 +1304,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-jdk9_release"
@@ -1402,10 +1340,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-kotlin_dev"
@@ -1443,10 +1377,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-kotlin_old"
@@ -1484,10 +1414,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-none"
@@ -1524,10 +1450,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-none_release"
@@ -1564,10 +1486,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-r8cf_jctf"
@@ -1605,10 +1523,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-r8cf_jctf_release"
@@ -1646,10 +1560,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-run-on-app-dump"
@@ -1682,10 +1592,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "linux-run-on-app-dump_release"
@@ -1718,10 +1624,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "windows"
@@ -1757,10 +1659,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
     builders {
       name: "windows_release"
@@ -1796,10 +1694,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
     }
   }
 }
diff --git a/infra/config/global/generated/luci-milo.cfg b/infra/config/global/generated/luci-milo.cfg
index 6151ce6..8bbf1b1 100644
--- a/infra/config/global/generated/luci-milo.cfg
+++ b/infra/config/global/generated/luci-milo.cfg
@@ -86,6 +86,11 @@
     short_name: "12.0.0"
   }
   builders {
+    name: "buildbucket/luci.r8.ci/linux-android-13.0.0"
+    category: "R8"
+    short_name: "13.0.0"
+  }
+  builders {
     name: "buildbucket/luci.r8.ci/windows"
     category: "R8"
     short_name: "windows"
@@ -211,6 +216,11 @@
     short_name: "12.0.0"
   }
   builders {
+    name: "buildbucket/luci.r8.ci/linux-android-13.0.0_release"
+    category: "Release|R8"
+    short_name: "13.0.0"
+  }
+  builders {
     name: "buildbucket/luci.r8.ci/windows_release"
     category: "Release|R8"
     short_name: "windows"
diff --git a/infra/config/global/generated/luci-notify.cfg b/infra/config/global/generated/luci-notify.cfg
index e697c32..d98d266 100644
--- a/infra/config/global/generated/luci-notify.cfg
+++ b/infra/config/global/generated/luci-notify.cfg
@@ -108,6 +108,30 @@
   }
   builders {
     bucket: "ci"
+    name: "linux-android-13.0.0"
+    repository: "https://r8.googlesource.com/r8"
+  }
+}
+notifiers {
+  notifications {
+    on_failure: true
+    on_new_failure: true
+    notify_blamelist {}
+  }
+  builders {
+    bucket: "ci"
+    name: "linux-android-13.0.0_release"
+    repository: "https://r8.googlesource.com/r8"
+  }
+}
+notifiers {
+  notifications {
+    on_failure: true
+    on_new_failure: true
+    notify_blamelist {}
+  }
+  builders {
+    bucket: "ci"
     name: "linux-android-4.0.4"
     repository: "https://r8.googlesource.com/r8"
   }
diff --git a/infra/config/global/generated/luci-scheduler.cfg b/infra/config/global/generated/luci-scheduler.cfg
index 6d92824..629ee37 100644
--- a/infra/config/global/generated/luci-scheduler.cfg
+++ b/infra/config/global/generated/luci-scheduler.cfg
@@ -15,7 +15,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "archive"
   }
 }
@@ -30,7 +30,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "archive_release"
   }
 }
@@ -44,7 +44,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "desugared_library-head"
   }
 }
@@ -58,7 +58,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "desugared_library-jdk11_head"
   }
 }
@@ -73,7 +73,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "lib_desugar-archive"
   }
 }
@@ -87,7 +87,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-android-10.0.0"
   }
 }
@@ -102,7 +102,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-android-10.0.0_release"
   }
 }
@@ -116,7 +116,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-android-12.0.0"
   }
 }
@@ -131,11 +131,40 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-android-12.0.0_release"
   }
 }
 job {
+  id: "linux-android-13.0.0"
+  realm: "ci"
+  acl_sets: "ci"
+  triggering_policy {
+    kind: GREEDY_BATCHING
+    max_concurrent_invocations: 4
+  }
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "linux-android-13.0.0"
+  }
+}
+job {
+  id: "linux-android-13.0.0_release"
+  realm: "ci"
+  acl_sets: "ci"
+  triggering_policy {
+    kind: GREEDY_BATCHING
+    max_concurrent_invocations: 4
+    max_batch_size: 1
+  }
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "linux-android-13.0.0_release"
+  }
+}
+job {
   id: "linux-android-4.0.4"
   realm: "ci"
   acl_sets: "ci"
@@ -145,7 +174,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-android-4.0.4"
   }
 }
@@ -160,7 +189,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-android-4.0.4_release"
   }
 }
@@ -174,7 +203,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-android-4.4.4"
   }
 }
@@ -189,7 +218,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-android-4.4.4_release"
   }
 }
@@ -203,7 +232,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-android-5.1.1"
   }
 }
@@ -218,7 +247,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-android-5.1.1_release"
   }
 }
@@ -232,7 +261,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-android-6.0.1"
   }
 }
@@ -247,7 +276,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-android-6.0.1_release"
   }
 }
@@ -261,7 +290,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-android-7.0.0"
   }
 }
@@ -276,7 +305,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-android-7.0.0_release"
   }
 }
@@ -290,7 +319,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-android-8.1.0"
   }
 }
@@ -305,7 +334,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-android-8.1.0_release"
   }
 }
@@ -319,7 +348,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-android-9.0.0"
   }
 }
@@ -334,7 +363,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-android-9.0.0_release"
   }
 }
@@ -348,7 +377,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-d8_jctf"
   }
 }
@@ -363,7 +392,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-d8_jctf_release"
   }
 }
@@ -377,7 +406,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-dex_default"
   }
 }
@@ -392,7 +421,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-dex_default_release"
   }
 }
@@ -406,7 +435,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-internal"
   }
 }
@@ -420,7 +449,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-internal_release"
   }
 }
@@ -434,7 +463,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-jdk11"
   }
 }
@@ -449,7 +478,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-jdk11_release"
   }
 }
@@ -463,7 +492,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-jdk8"
   }
 }
@@ -478,7 +507,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-jdk8_release"
   }
 }
@@ -492,7 +521,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-jdk9"
   }
 }
@@ -507,7 +536,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-jdk9_release"
   }
 }
@@ -521,7 +550,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-kotlin_dev"
   }
 }
@@ -535,7 +564,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-kotlin_old"
   }
 }
@@ -549,7 +578,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-none"
   }
 }
@@ -564,7 +593,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-none_release"
   }
 }
@@ -578,7 +607,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-r8cf_jctf"
   }
 }
@@ -593,7 +622,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-r8cf_jctf_release"
   }
 }
@@ -607,7 +636,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-run-on-app-dump"
   }
 }
@@ -622,7 +651,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "linux-run-on-app-dump_release"
   }
 }
@@ -636,7 +665,7 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "windows"
   }
 }
@@ -651,17 +680,38 @@
   }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
-    bucket: "luci.r8.ci"
+    bucket: "ci"
     builder: "windows_release"
   }
 }
 trigger {
+  id: "branch-gitiles-3.2-forward"
+  realm: "ci"
+  acl_sets: "ci"
+  triggers: "linux-android-12.0.0_release"
+  gitiles {
+    repo: "https://r8.googlesource.com/r8"
+    refs: "regexp:refs/heads//([3]\\.[2-9]+(\\.[0-9]+)?|[4-9]\\.[0-9]+(\\.[0-9]+)?)"
+    path_regexps: "src/main/java/com/android/tools/r8/Version.java"
+  }
+}
+trigger {
+  id: "branch-gitiles-3.3-forward"
+  realm: "ci"
+  acl_sets: "ci"
+  triggers: "linux-android-13.0.0_release"
+  gitiles {
+    repo: "https://r8.googlesource.com/r8"
+    refs: "regexp:refs/heads//([3]\\.[3-9]+(\\.[0-9]+)?|[4-9]\\.[0-9]+(\\.[0-9]+)?)"
+    path_regexps: "src/main/java/com/android/tools/r8/Version.java"
+  }
+}
+trigger {
   id: "branch-gitiles-trigger"
   realm: "ci"
   acl_sets: "ci"
   triggers: "archive_release"
   triggers: "linux-android-10.0.0_release"
-  triggers: "linux-android-12.0.0_release"
   triggers: "linux-android-4.0.4_release"
   triggers: "linux-android-4.4.4_release"
   triggers: "linux-android-5.1.1_release"
@@ -681,7 +731,7 @@
   triggers: "windows_release"
   gitiles {
     repo: "https://r8.googlesource.com/r8"
-    refs: "regexp:refs/heads/(?:d8-)?[0-9]+\\.[0-9]+(\\.[0-9]+)?"
+    refs: "regexp:refs/heads/[0-9]+\\.[0-9]+(\\.[0-9]+)?"
     path_regexps: "src/main/java/com/android/tools/r8/Version.java"
   }
 }
@@ -694,6 +744,7 @@
   triggers: "desugared_library-jdk11_head"
   triggers: "linux-android-10.0.0"
   triggers: "linux-android-12.0.0"
+  triggers: "linux-android-13.0.0"
   triggers: "linux-android-4.0.4"
   triggers: "linux-android-4.4.4"
   triggers: "linux-android-5.1.1"
diff --git a/infra/config/global/generated/project.cfg b/infra/config/global/generated/project.cfg
index b87c601..974c534 100644
--- a/infra/config/global/generated/project.cfg
+++ b/infra/config/global/generated/project.cfg
@@ -7,8 +7,9 @@
 name: "r8"
 access: "group:all"
 lucicfg {
-  version: "1.30.5"
+  version: "1.30.9"
   package_dir: ".."
   config_dir: "generated"
   entry_point: "main.star"
+  experiments: "crbug.com/1182002"
 }
diff --git a/infra/config/global/main.star b/infra/config/global/main.star
index 02b45d5..6c59f26 100755
--- a/infra/config/global/main.star
+++ b/infra/config/global/main.star
@@ -1,13 +1,11 @@
 #!/usr/bin/env lucicfg
 
-lucicfg.check_version("1.28.0", "Please use newer `lucicfg` binary")
+lucicfg.check_version("1.30.9", "Please use newer `lucicfg` binary")
 
-# Enable LUCI Realms support.
-lucicfg.enable_experiment("crbug.com/1085650")
+# Use LUCI Scheduler BBv2 names and add Scheduler realms configs.
+lucicfg.enable_experiment("crbug.com/1182002")
 
-# Launch 0% of Builds in "realms-aware mode"
 luci.builder.defaults.experiments.set({
-    "luci.use_realms": 100,
     "luci.recipes.use_python3": 100
 })
 
@@ -111,11 +109,26 @@
 )
 
 luci.gitiles_poller(
+  name = "branch-gitiles-3.3-forward",
+  bucket = "ci",
+  repo = "https://r8.googlesource.com/r8",
+  refs = ["refs/heads//([3]\\.[3-9]+(\\.[0-9]+)?|[4-9]\\.[0-9]+(\\.[0-9]+)?)"],
+  path_regexps = ["src/main/java/com/android/tools/r8/Version.java"]
+)
+
+luci.gitiles_poller(
+  name = "branch-gitiles-3.2-forward",
+  bucket = "ci",
+  repo = "https://r8.googlesource.com/r8",
+  refs = ["refs/heads//([3]\\.[2-9]+(\\.[0-9]+)?|[4-9]\\.[0-9]+(\\.[0-9]+)?)"],
+  path_regexps = ["src/main/java/com/android/tools/r8/Version.java"]
+)
+
+luci.gitiles_poller(
   name = "branch-gitiles-trigger",
   bucket = "ci",
   repo = "https://r8.googlesource.com/r8",
-  # Version branches are named d8-x.y (up until d8-1.5) or just x.y (from 1.6)
-  refs = ["refs/heads/(?:d8-)?[0-9]+\\.[0-9]+(\\.[0-9]+)?"],
+  refs = ["refs/heads/[0-9]+\\.[0-9]+(\\.[0-9]+)?"],
   path_regexps = ["src/main/java/com/android/tools/r8/Version.java"]
 )
 
@@ -167,10 +180,14 @@
   return dimensions
 
 def r8_builder(name, priority=26, trigger=True, category=None,
-               triggering_policy=None, **kwargs):
+               triggering_policy=None, release_trigger=None, **kwargs):
   release = name.endswith("release")
-  triggered = None if not trigger else ["branch-gitiles-trigger"] if release\
-      else ["main-gitiles-trigger"]
+  triggered = None
+  if trigger:
+    if release:
+      triggered = release_trigger if release_trigger else ["branch-gitiles-trigger"]
+    else:
+      triggered = ["main-gitiles-trigger"]
   triggering_policy = triggering_policy or scheduler.policy(
       kind = scheduler.GREEDY_BATCHING_KIND,
       max_batch_size = 1 if release else None,
@@ -199,7 +216,8 @@
     dimensions = None,
     execution_timeout = time.hour * 6,
     expiration_timeout = time.hour * 35,
-    category=None):
+    category=None,
+    release_trigger=None):
   dimensions = dimensions if dimensions else get_dimensions(normal=True)
   for name in [name, name + "_release"]:
     r8_builder(
@@ -208,15 +226,20 @@
         execution_timeout = execution_timeout,
         expiration_timeout = expiration_timeout,
         dimensions = dimensions,
+        release_trigger=release_trigger,
         properties = {
             "test_options" : test_options,
             "builder_group" : "internal.client.r8"
         }
     )
 
-def r8_tester_with_default(name, test_options, dimensions=None, category=None):
+def r8_tester_with_default(name,
+    test_options,
+    dimensions=None,
+    category=None,
+    release_trigger=None):
   r8_tester(name, test_options + common_test_options,
-            dimensions = dimensions, category = category)
+            dimensions = dimensions, category = category, release_trigger=release_trigger)
 
 def archivers():
   for name in ["archive", "archive_release", "lib_desugar-archive"]:
@@ -265,7 +288,11 @@
 r8_tester_with_default("linux-android-10.0.0",
     ["--dex_vm=10.0.0", "--all_tests"])
 r8_tester_with_default("linux-android-12.0.0",
-    ["--dex_vm=12.0.0", "--all_tests"])
+    ["--dex_vm=12.0.0", "--all_tests"],
+    release_trigger=["branch-gitiles-3.2-forward"])
+r8_tester_with_default("linux-android-13.0.0",
+    ["--dex_vm=13.0.0", "--all_tests"],
+    release_trigger=["branch-gitiles-3.3-forward"])
 
 r8_tester_with_default("windows", ["--all_tests"],
     dimensions=get_dimensions(windows=True))
@@ -396,4 +423,4 @@
           category = v[1],
           short_name = v[2]
       )
-add_view_entries()
\ No newline at end of file
+add_view_entries()
diff --git a/scripts/add-openjdk.sh b/scripts/add-openjdk.sh
index 6f8bada..f6b3284 100755
--- a/scripts/add-openjdk.sh
+++ b/scripts/add-openjdk.sh
@@ -14,13 +14,16 @@
 # Create directory third_party/openjdk/jdk-X
 # cd into third_party/openjdk/jdk-X
 # Prepare README.google
-# Update JDK_VERSION below
+# Update JDK_VERSION and JDK_VERSION_FULL below
 
 # Now run script with fingers crossed!
 
-JDK_VERSION=17
+JDK_VERSION="18"
+JDK_VERSION_FULL=${JDK_VERSION}
+# For ea versions the full version name has a postfix.
+# JDK_VERSION_FULL="${JDK_VERSION}-ea+33"
 
-tar xf ~/Downloads/openjdk-${JDK_VERSION}_linux-x64_bin.tar.gz
+tar xf ~/Downloads/openjdk-${JDK_VERSION_FULL}_linux-x64_bin.tar.gz
 cp -rL jdk-${JDK_VERSION} linux
 cp README.google linux
 upload_to_google_storage.py -a --bucket r8-deps linux
@@ -28,7 +31,7 @@
 rm -rf linux
 rm linux.tar.gz
 
-tar xf ~/Downloads/openjdk-${JDK_VERSION}_macos-x64_bin.tar.gz
+tar xf ~/Downloads/openjdk-${JDK_VERSION_FULL}_macos-x64_bin.tar.gz
 cp -rL jdk-${JDK_VERSION}.jdk osx
 cp README.google osx
 upload_to_google_storage.py -a --bucket r8-deps osx
@@ -36,7 +39,7 @@
 rm -rf jdk-${JDK_VERSION}.jdk
 rm osx.tar.gz
 
-unzip ~/Downloads/openjdk-${JDK_VERSION}_windows-x64_bin.zip
+unzip ~/Downloads/openjdk-${JDK_VERSION_FULL}_windows-x64_bin.zip
 cp -rL jdk-${JDK_VERSION} windows
 cp README.google windows
 upload_to_google_storage.py -a --bucket r8-deps windows
diff --git a/src/library_desugar/jdk11/chm_only_desugar_jdk_libs.json b/src/library_desugar/jdk11/chm_only_desugar_jdk_libs.json
index 565ffd9..f3a4daa 100644
--- a/src/library_desugar/jdk11/chm_only_desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/chm_only_desugar_jdk_libs.json
@@ -3,7 +3,7 @@
   "group_id" : "com.tools.android",
   "artifact_id" : "chm_only_desugar_jdk_libs",
   "version": "1.0.12",
-  "required_compilation_api_level": 31,
+  "required_compilation_api_level": 30,
   "synthesized_library_classes_package_prefix": "j$.",
   "support_all_callbacks_from_library": false,
   "common_flags": [
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json
index fb03dbb..b075745 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -3,7 +3,7 @@
   "group_id" : "com.tools.android",
   "artifact_id" : "desugar_jdk_libs",
   "version": "2.0.0",
-  "required_compilation_api_level": 31,
+  "required_compilation_api_level": 30,
   "synthesized_library_classes_package_prefix": "j$.",
   "support_all_callbacks_from_library": true,
   "common_flags": [
@@ -205,7 +205,7 @@
         "java.nio.charset.StandardCharsets": "j$.nio.charset.StandardCharsets"
       },
       "retarget_lib_member": {
-        "java.lang.Character#isBmpCodePoint": "j$.lang.DesugarCharacter"
+        "java.lang.Character#isBmpCodePoint": "java.lang.DesugarCharacter"
       }
     }
   ],
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json b/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json
index fac2ec0..1482909 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json
@@ -3,7 +3,7 @@
   "group_id" : "com.tools.android",
   "artifact_id" : "desugar_jdk_libs_alternative_3",
   "version": "2.0.0",
-  "required_compilation_api_level": 31,
+  "required_compilation_api_level": 30,
   "synthesized_library_classes_package_prefix": "j$.",
   "support_all_callbacks_from_library": false,
   "common_flags": [
@@ -209,7 +209,7 @@
         "java.nio.charset.StandardCharsets": "j$.nio.charset.StandardCharsets"
       },
       "retarget_lib_member": {
-        "java.lang.Character#isBmpCodePoint": "j$.lang.DesugarCharacter"
+        "java.lang.Character#isBmpCodePoint": "java.lang.DesugarCharacter"
       }
     }
   ],
diff --git a/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java b/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
index bce73f0..7ef1569 100644
--- a/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
+++ b/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
@@ -105,7 +105,7 @@
   InternalOptions getInternalOptions() {
     InternalOptions options = new InternalOptions(factory, getReporter());
     options.setMinApiLevel(AndroidApiLevel.getAndroidApiLevel(minApiLevel));
-    options.desugaredLibrarySpecification = desugaredLibrarySpecification;
+    options.setDesugaredLibrarySpecification(desugaredLibrarySpecification, getInputApp());
     return options;
   }
 
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
index 1316be8..631b5b3 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
@@ -3,8 +3,11 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
-import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
@@ -38,7 +41,16 @@
           "  --force-pa[:[<class name>|<package name>...]]",
           "                          # Don't change javac generated assertion code. This",
           "                          # is the default handling of javac assertion code when",
-          "                          # generating class file format.");
+          "                          # generating class file format.",
+          "  --force-assertions-handler:<handler method>[:[<class name>|<package name>...]]",
+          "  --force-ah:<handler method>[:[<class name>|<package name>...]]",
+          "                          # Change javac and kotlinc generated assertion code to invoke",
+          "                          # the method <handler method> with each assertion error",
+          "                          # instead of throwing it. The <handler method> is specified"
+              + " as",
+          "                          # a class name followed by a dot and the method name. The",
+          "                          # handler method must take a single argument of type",
+          "                          # java.lang.Throwable and have return type void.");
 
   static final Iterable<String> THREAD_COUNT_USAGE_MESSAGE =
       Arrays.asList(
@@ -82,23 +94,54 @@
 
   private static String PACKAGE_ASSERTION_POSTFIX = "...";
 
+  private enum AssertionTransformationType {
+    ENABLE,
+    DISABLE,
+    PASSTHROUGH,
+    HANDLER
+  }
+
+  private AssertionsConfiguration.Builder prepareBuilderForScope(
+      AssertionsConfiguration.Builder builder,
+      AssertionTransformationType transformation,
+      MethodReference assertionHandler) {
+    switch (transformation) {
+      case ENABLE:
+        return builder.setCompileTimeEnable();
+      case DISABLE:
+        return builder.setCompileTimeDisable();
+      case PASSTHROUGH:
+        return builder.setPassthrough();
+      case HANDLER:
+        return builder.setAssertionHandler(assertionHandler);
+      default:
+        throw new Unreachable();
+    }
+  }
+
   private void addAssertionTransformation(
-      B builder, AssertionTransformation transformation, String scope) {
+      B builder,
+      AssertionTransformationType transformation,
+      MethodReference assertionHandler,
+      String scope) {
     if (scope == null) {
       builder.addAssertionsConfiguration(
-          b -> b.setTransformation(transformation).setScopeAll().build());
+          b -> prepareBuilderForScope(b, transformation, assertionHandler).setScopeAll().build());
     } else {
       assert scope.length() > 0;
       if (scope.endsWith(PACKAGE_ASSERTION_POSTFIX)) {
         builder.addAssertionsConfiguration(
             b ->
-                b.setTransformation(transformation)
+                prepareBuilderForScope(b, transformation, assertionHandler)
                     .setScopePackage(
                         scope.substring(0, scope.length() - PACKAGE_ASSERTION_POSTFIX.length()))
                     .build());
       } else {
         builder.addAssertionsConfiguration(
-            b -> b.setTransformation(transformation).setScopeClass(scope).build());
+            b ->
+                prepareBuilderForScope(b, transformation, assertionHandler)
+                    .setScopeClass(scope)
+                    .build());
       }
     }
   }
@@ -110,34 +153,78 @@
     String FORCE_DA = "--force-da";
     String FORCE_PASSTHROUGH_ASSERTIONS = "--force-passthrough-assertions";
     String FORCE_PA = "--force-pa";
+    String FORCE_ASSERTIONS_HANDLER = "--force-assertions-handler";
+    String FORCE_AH = "--force-ah";
 
-    AssertionTransformation transformation = null;
+    AssertionTransformationType transformation = null;
+    MethodReference assertionsHandler = null;
     String remaining = null;
     if (arg.startsWith(FORCE_ENABLE_ASSERTIONS)) {
-      transformation = AssertionTransformation.ENABLE;
+      transformation = AssertionTransformationType.ENABLE;
       remaining = arg.substring(FORCE_ENABLE_ASSERTIONS.length());
     } else if (arg.startsWith(FORCE_EA)) {
-      transformation = AssertionTransformation.ENABLE;
+      transformation = AssertionTransformationType.ENABLE;
       remaining = arg.substring(FORCE_EA.length());
     } else if (arg.startsWith(FORCE_DISABLE_ASSERTIONS)) {
-      transformation = AssertionTransformation.DISABLE;
+      transformation = AssertionTransformationType.DISABLE;
       remaining = arg.substring(FORCE_DISABLE_ASSERTIONS.length());
     } else if (arg.startsWith(FORCE_DA)) {
-      transformation = AssertionTransformation.DISABLE;
+      transformation = AssertionTransformationType.DISABLE;
       remaining = arg.substring(FORCE_DA.length());
     } else if (arg.startsWith(FORCE_PASSTHROUGH_ASSERTIONS)) {
-      transformation = AssertionTransformation.PASSTHROUGH;
+      transformation = AssertionTransformationType.PASSTHROUGH;
       remaining = arg.substring(FORCE_PASSTHROUGH_ASSERTIONS.length());
     } else if (arg.startsWith(FORCE_PA)) {
-      transformation = AssertionTransformation.PASSTHROUGH;
+      transformation = AssertionTransformationType.PASSTHROUGH;
       remaining = arg.substring(FORCE_PA.length());
+    } else if (arg.startsWith(FORCE_ASSERTIONS_HANDLER)) {
+      transformation = AssertionTransformationType.HANDLER;
+      remaining = arg.substring(FORCE_ASSERTIONS_HANDLER.length());
+    } else if (arg.startsWith(FORCE_AH)) {
+      transformation = AssertionTransformationType.HANDLER;
+      remaining = arg.substring(FORCE_AH.length());
+    }
+    if (transformation == AssertionTransformationType.HANDLER) {
+      if (remaining.length() == 0 || (remaining.length() == 1 && remaining.charAt(0) == ':')) {
+        throw builder.fatalError(
+            new StringDiagnostic("Missing required argument <handler method>", origin));
+      }
+      if (remaining.charAt(0) != ':') {
+        return false;
+      }
+      remaining = remaining.substring(1);
+      int index = remaining.indexOf(':');
+      if (index == 0) {
+        throw builder.fatalError(
+            new StringDiagnostic("Missing required argument <handler method>", origin));
+      }
+      String assertionsHandlerString = index > 0 ? remaining.substring(0, index) : remaining;
+      int lastDotIndex = assertionsHandlerString.lastIndexOf('.');
+      if (assertionsHandlerString.length() < 3
+          || lastDotIndex <= 0
+          || lastDotIndex == assertionsHandlerString.length() - 1
+          || !DescriptorUtils.isValidJavaType(assertionsHandlerString.substring(0, lastDotIndex))) {
+        throw builder.fatalError(
+            new StringDiagnostic(
+                "Invalid argument <handler method>: " + assertionsHandlerString, origin));
+      }
+      assertionsHandler =
+          Reference.methodFromDescriptor(
+              DescriptorUtils.javaTypeToDescriptor(
+                  assertionsHandlerString.substring(0, lastDotIndex)),
+              assertionsHandlerString.substring(lastDotIndex + 1),
+              "(Ljava/lang/Throwable;)V");
+      remaining = remaining.substring(assertionsHandlerString.length());
     }
     if (transformation != null) {
       if (remaining.length() == 0) {
-        addAssertionTransformation(builder, transformation, null);
+        addAssertionTransformation(builder, transformation, assertionsHandler, null);
         return true;
       } else {
-        if (remaining.length() == 1 || remaining.charAt(0) != ':') {
+        if (remaining.length() == 1 && remaining.charAt(0) == ':') {
+          throw builder.fatalError(new StringDiagnostic("Missing optional argument", origin));
+        }
+        if (remaining.charAt(0) != ':') {
           return false;
         }
         String classOrPackageScope = remaining.substring(1);
@@ -147,7 +234,8 @@
           builder.error(
               new StringDiagnostic("Illegal assertion scope: " + classOrPackageScope, origin));
         }
-        addAssertionTransformation(builder, transformation, remaining.substring(1));
+        addAssertionTransformation(
+            builder, transformation, assertionsHandler, remaining.substring(1));
         return true;
       }
     } else {
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 9232563..10c5c5f 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -23,7 +23,7 @@
 import com.android.tools.r8.graph.analysis.ClassInitializerAssertionEnablingAnalysis;
 import com.android.tools.r8.inspector.internal.InspectorImpl;
 import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
+import com.android.tools.r8.ir.desugar.TypeRewriter;
 import com.android.tools.r8.ir.optimize.AssertionsRewriter;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
 import com.android.tools.r8.jar.CfApplicationWriter;
@@ -168,11 +168,11 @@
   private static AppView<AppInfo> readApp(
       AndroidApp inputApp, InternalOptions options, ExecutorService executor, Timing timing)
       throws IOException {
-    PrefixRewritingMapper rewritePrefix = options.getPrefixRewritingMapper();
+    TypeRewriter typeRewriter = options.getTypeRewriter();
     ApplicationReader applicationReader = new ApplicationReader(inputApp, options, timing);
     LazyLoadedDexApplication app = applicationReader.read(executor);
     AppInfo appInfo = AppInfo.createInitialAppInfo(app, applicationReader.readMainDexClasses(app));
-    return AppView.createForD8(appInfo, rewritePrefix);
+    return AppView.createForD8(appInfo, typeRewriter);
   }
 
   private static void run(AndroidApp inputApp, InternalOptions options, ExecutorService executor)
@@ -269,7 +269,7 @@
         new CfApplicationWriter(appView, marker, GraphLens.getIdentityLens(), namingLens)
             .write(options.getClassFileConsumer(), inputApp);
       } else {
-        if (!hasDexResources || !hasClassResources || !appView.rewritePrefix.isRewriting()) {
+        if (!hasDexResources || !hasClassResources || !appView.typeRewriter.isRewriting()) {
           // All inputs are either dex or cf, or there is nothing to rewrite.
           namingLens = hasDexResources ? NamingLens.getIdentityLens() : namingLens;
           new GenericSignatureRewriter(appView, namingLens)
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 8dd34a8..0967f55 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -498,8 +498,7 @@
     internal.encodeChecksums = getIncludeClassesChecksum();
     internal.dexClassChecksumFilter = getDexClassChecksumFilter();
     internal.enableInheritanceClassInDexDistributor = isOptimizeMultidexForLinearAlloc();
-
-    internal.desugaredLibrarySpecification = desugaredLibrarySpecification;
+    internal.setDesugaredLibrarySpecification(desugaredLibrarySpecification, getInputApp());
     internal.synthesizedClassPrefix = synthesizedClassPrefix;
     internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
 
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
index 01e1163..4c49b47 100644
--- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -377,7 +377,7 @@
 
   private void run() throws Exception {
     // Run over all the API levels that the desugared library can be compiled with.
-    for (int apiLevel = AndroidApiLevel.LATEST.getLevel();
+    for (int apiLevel = AndroidApiLevel.Sv2.getLevel();
         apiLevel >= desugaredLibrarySpecification.getRequiredCompilationApiLevel().getLevel();
         apiLevel--) {
       System.out.println("Generating lint files for compile API " + apiLevel);
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index 9ba6c7f..7c0a805 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -13,7 +13,7 @@
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.LazyLoadedDexApplication;
 import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
+import com.android.tools.r8.ir.desugar.TypeRewriter;
 import com.android.tools.r8.jar.CfApplicationWriter;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.naming.PrefixRewritingNamingLens;
@@ -166,10 +166,10 @@
     LazyLoadedDexApplication lazyApp =
         new ApplicationReader(inputApp, options, timing).read(executor);
 
-    PrefixRewritingMapper rewritePrefix = options.getPrefixRewritingMapper();
+    TypeRewriter typeRewriter = options.getTypeRewriter();
 
-    DexApplication app = new L8TreePruner(options).prune(lazyApp, rewritePrefix);
-    return AppView.createForL8(AppInfo.createInitialAppInfo(app), rewritePrefix);
+    DexApplication app = new L8TreePruner(options).prune(lazyApp, typeRewriter);
+    return AppView.createForL8(AppInfo.createInitialAppInfo(app), typeRewriter);
   }
 
   private static void run(String[] args) throws CompilationFailedException {
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 26c99ce..6da92e2 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -194,7 +194,7 @@
     internal.enableInheritanceClassInDexDistributor = false;
 
     assert desugaredLibrarySpecification != null;
-    internal.desugaredLibrarySpecification = desugaredLibrarySpecification;
+    internal.setDesugaredLibrarySpecification(desugaredLibrarySpecification, getInputApp());
     internal.synthesizedClassPrefix =
         desugaredLibrarySpecification.getSynthesizedLibraryClassesPackagePrefix();
 
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 194c531..8b93d23 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -49,7 +49,6 @@
 import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterLibraryTypeSynthesizer;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
 import com.android.tools.r8.ir.desugar.records.RecordDesugaring;
 import com.android.tools.r8.ir.desugar.records.RecordFieldValuesRewriter;
@@ -311,10 +310,6 @@
       if (!options.mainDexKeepRules.isEmpty()) {
         MainDexListBuilder.checkForAssumedLibraryTypes(appView.appInfo());
       }
-      if (!options.desugaredLibrarySpecification.getRetargetCoreLibMember().isEmpty()) {
-        DesugaredLibraryRetargeterLibraryTypeSynthesizer.checkForAssumedLibraryTypes(appView);
-        DesugaredLibraryRetargeterLibraryTypeSynthesizer.amendLibraryWithRetargetedMembers(appView);
-      }
       InterfaceMethodRewriter.checkForAssumedLibraryTypes(appView.appInfo(), options);
       BackportedMethodRewriter.registerAssumedLibraryTypes(options);
       if (options.enableEnumUnboxing) {
@@ -329,7 +324,7 @@
       // Upfront desugaring generation: Generates new program classes to be added in the app.
       CfClassSynthesizerDesugaringEventConsumer classSynthesizerEventConsumer =
           new CfClassSynthesizerDesugaringEventConsumer();
-      CfClassSynthesizerDesugaringCollection.create(appView, null)
+      CfClassSynthesizerDesugaringCollection.create(appView)
           .synthesizeClasses(executorService, classSynthesizerEventConsumer);
       if (appView.getSyntheticItems().hasPendingSyntheticClasses()) {
         appView.setAppInfo(
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index bf04680..5bc9ddd 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -943,7 +943,7 @@
 
     internal.enableInheritanceClassInDexDistributor = isOptimizeMultidexForLinearAlloc();
 
-    internal.desugaredLibrarySpecification = desugaredLibrarySpecification;
+    internal.setDesugaredLibrarySpecification(desugaredLibrarySpecification, getInputApp());
     internal.synthesizedClassPrefix = synthesizedClassPrefix;
     internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
 
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
index 2e2aa7a..cce0852 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.ConsumerUtils;
 import com.google.common.collect.ImmutableList;
@@ -18,7 +17,6 @@
 
 public class AndroidApiReferenceLevelCache {
 
-  private final LegacyDesugaredLibrarySpecification desugaredLibrarySpecification;
   private final AndroidApiLevelCompute apiLevelCompute;
   private final AndroidApiLevelDatabase androidApiLevelDatabase;
   private final AppView<?> appView;
@@ -33,7 +31,6 @@
     factory = appView.dexItemFactory();
     androidApiLevelDatabase =
         new AndroidApiLevelHashingDatabaseImpl(predefinedApiTypeLookupForHashing);
-    desugaredLibrarySpecification = appView.options().desugaredLibrarySpecification;
   }
 
   public static AndroidApiReferenceLevelCache create(
@@ -75,7 +72,7 @@
     if (reference.getContextType() == factory.objectType) {
       return appView.computedMinApiLevel();
     }
-    if (desugaredLibrarySpecification.isSupported(reference, appView)) {
+    if (appView.options().machineDesugaredLibrarySpecification.isSupported(reference)) {
       // If we end up desugaring the reference, the library classes is bridged by j$ which is part
       // of the program.
       return appView.computedMinApiLevel();
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 f9b13a4..ef86f73 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
@@ -19,7 +19,6 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ThrowExceptionCode;
 import com.android.tools.r8.graph.UseRegistry;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.synthesis.CommittedItems;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
@@ -112,12 +111,10 @@
       new ConcurrentHashMap<>();
   private final Set<DexType> seenTypes = Sets.newConcurrentHashSet();
   private final AndroidApiLevelCompute apiLevelCompute;
-  private final LegacyDesugaredLibrarySpecification desugaredLibraryConfiguration;
 
   public ApiReferenceStubber(AppView<? extends AppInfoWithClassHierarchy> appView) {
     this.appView = appView;
     apiLevelCompute = appView.apiLevelCompute();
-    desugaredLibraryConfiguration = appView.options().desugaredLibrarySpecification;
   }
 
   public void run(ExecutorService executorService) throws ExecutionException {
@@ -220,7 +217,10 @@
         || libraryClass.getType().toDescriptorString().startsWith("Ljava/")) {
       return;
     }
-    if (desugaredLibraryConfiguration.isSupported(libraryClass.getType(), appView)) {
+    if (appView
+        .options()
+        .machineDesugaredLibrarySpecification
+        .isSupported(libraryClass.getType())) {
       return;
     }
     appView
diff --git a/src/main/java/com/android/tools/r8/cf/CfVersion.java b/src/main/java/com/android/tools/r8/cf/CfVersion.java
index b23d44d..9ce8c64 100644
--- a/src/main/java/com/android/tools/r8/cf/CfVersion.java
+++ b/src/main/java/com/android/tools/r8/cf/CfVersion.java
@@ -38,6 +38,8 @@
   public static final CfVersion V16_PREVIEW = new CfVersion(Opcodes.V16 | Opcodes.V_PREVIEW);
   public static final CfVersion V17 = new CfVersion(Opcodes.V17);
   public static final CfVersion V17_PREVIEW = new CfVersion(Opcodes.V17 | Opcodes.V_PREVIEW);
+  public static final CfVersion V18 = new CfVersion(Opcodes.V18);
+  public static final CfVersion V18_PREVIEW = new CfVersion(Opcodes.V18 | Opcodes.V_PREVIEW);
 
   private final int version;
 
@@ -58,7 +60,8 @@
     CfVersion.V14,
     CfVersion.V15,
     CfVersion.V16,
-    CfVersion.V17
+    CfVersion.V17,
+    CfVersion.V18
   };
 
   // Private constructor in case we want to canonicalize versions.
diff --git a/src/main/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryKeepRuleGenerator.java b/src/main/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryKeepRuleGenerator.java
index a720021..eaec5a0 100644
--- a/src/main/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryKeepRuleGenerator.java
+++ b/src/main/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryKeepRuleGenerator.java
@@ -13,7 +13,7 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.references.ArrayReference;
 import com.android.tools.r8.references.ClassReference;
@@ -32,13 +32,11 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.NopDiagnosticsHandler;
-import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.TypeReferenceUtils;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Predicate;
 
@@ -71,7 +69,7 @@
       return false;
     }
     return namingLens.hasPrefixRewritingLogic()
-        || options.desugaredLibrarySpecification.hasEmulatedLibraryInterfaces();
+        || options.machineDesugaredLibrarySpecification.hasEmulatedInterfaces();
   }
 
   private void run() {
@@ -80,18 +78,15 @@
   }
 
   private Predicate<DexType> createTargetPredicate() {
-    LegacyDesugaredLibrarySpecification desugaredLibrarySpecification =
-        options.desugaredLibrarySpecification;
-    Set<DexType> potentialTypesToKeep =
-        SetUtils.newIdentityHashSet(
-            desugaredLibrarySpecification.getCustomConversions().values(),
-            desugaredLibrarySpecification.getEmulateLibraryInterface().values());
+    MachineDesugaredLibrarySpecification desugaredLibrarySpecification =
+        options.machineDesugaredLibrarySpecification;
     byte[] synthesizedLibraryClassesPackageDescriptorPrefix =
         DexString.encodeToMutf8(
             "L" + desugaredLibrarySpecification.getSynthesizedLibraryClassesPackagePrefix());
     return type ->
         namingLens.prefixRewrittenType(type) != null
-            || potentialTypesToKeep.contains(type)
+            || desugaredLibrarySpecification.isEmulatedInterfaceRewrittenType(type)
+            || desugaredLibrarySpecification.isCustomConversionRewrittenType(type)
             || type.getDescriptor().startsWith(synthesizedLibraryClassesPackageDescriptorPrefix);
   }
 
diff --git a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
index cc05c31..3e33ca8 100644
--- a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
+++ b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
@@ -25,7 +25,7 @@
 
   static CodeToKeep createCodeToKeep(InternalOptions options, NamingLens namingLens) {
     if ((!namingLens.hasPrefixRewritingLogic()
-            && !options.desugaredLibrarySpecification.hasEmulatedLibraryInterfaces())
+            && !options.machineDesugaredLibrarySpecification.hasEmulatedInterfaces())
         || options.isDesugaredLibraryCompilation()
         || options.testing.enableExperimentalDesugaredLibraryKeepRuleGenerator) {
       return new NopCodeToKeep();
@@ -57,27 +57,23 @@
     }
 
     private final NamingLens namingLens;
-    private final Set<DexType> potentialTypesToKeep = Sets.newIdentityHashSet();
     private final Map<DexType, KeepStruct> toKeep = new ConcurrentHashMap<>();
     private final InternalOptions options;
 
     public DesugaredLibraryCodeToKeep(NamingLens namingLens, InternalOptions options) {
       this.namingLens = namingLens;
       this.options = options;
-      potentialTypesToKeep.addAll(
-          options.desugaredLibrarySpecification.getEmulateLibraryInterface().values());
-      potentialTypesToKeep.addAll(
-          options.desugaredLibrarySpecification.getCustomConversions().values());
     }
 
     private boolean shouldKeep(DexType type) {
       return namingLens.prefixRewrittenType(type) != null
-          || potentialTypesToKeep.contains(type)
+          || options.machineDesugaredLibrarySpecification.isCustomConversionRewrittenType(type)
+          || options.machineDesugaredLibrarySpecification.isEmulatedInterfaceRewrittenType(type)
           // TODO(b/158632510): This should prefix match on DexString.
           || type.toDescriptorString()
               .startsWith(
                   "L"
-                      + options.desugaredLibrarySpecification
+                      + options.machineDesugaredLibrarySpecification
                           .getSynthesizedLibraryClassesPackagePrefix());
     }
 
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index 1cf6ae7..ef10e8d 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -140,15 +139,25 @@
   }
 
   @Override
+  public ClassResolutionResult contextIndependentDefinitionForWithResolutionResult(DexType type) {
+    assert checkIfObsolete();
+    return syntheticItems.definitionFor(
+        type, app::contextIndependentDefinitionForWithResolutionResult);
+  }
+
+  @Override
   public DexClass definitionFor(DexType type) {
     return definitionForWithoutExistenceAssert(type);
   }
 
   public final DexClass definitionForWithoutExistenceAssert(DexType type) {
     assert checkIfObsolete();
-    return syntheticItems.definitionFor(type, app::definitionFor);
+    return syntheticItems
+        .definitionFor(type, app::contextIndependentDefinitionForWithResolutionResult)
+        .toSingleClassWithProgramOverLibrary();
   }
 
+
   public DexClass definitionForDesugarDependency(DexClass dependent, DexType type) {
     if (dependent.type == type) {
       return dependent;
@@ -240,7 +249,7 @@
     DexProgramClass clazz = context.getHolder();
     DexEncodedField definition = clazz.lookupField(field);
     return definition != null
-        ? new SuccessfulFieldResolutionResult(clazz, clazz, definition)
+        ? FieldResolutionResult.createSingleFieldResolutionResult(clazz, clazz, definition)
         : FieldResolutionResult.unknown();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 24c47b4..cbf79f2 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -24,7 +24,7 @@
 import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteShrinker;
 import com.android.tools.r8.ir.analysis.proto.ProtoShrinker;
 import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
-import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
+import com.android.tools.r8.ir.desugar.TypeRewriter;
 import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
 import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoFactory;
 import com.android.tools.r8.ir.optimize.library.LibraryMemberOptimizer;
@@ -88,7 +88,7 @@
       new SimpleInliningConstraintFactory();
 
   // Desugaring.
-  public final PrefixRewritingMapper rewritePrefix;
+  public final TypeRewriter typeRewriter;
 
   // Modeling.
   private final LibraryMethodSideEffectModelCollection libraryMethodSideEffectModelCollection;
@@ -123,16 +123,14 @@
   private final ComputedApiLevel computedMinApiLevel;
 
   private AppView(
-      T appInfo,
-      WholeProgramOptimizations wholeProgramOptimizations,
-      PrefixRewritingMapper mapper) {
+      T appInfo, WholeProgramOptimizations wholeProgramOptimizations, TypeRewriter mapper) {
     assert appInfo != null;
     this.context = CompilationContext.createInitialContext(appInfo.options());
     this.appInfo = appInfo;
     this.dontWarnConfiguration = DontWarnConfiguration.create(options().getProguardConfiguration());
     this.wholeProgramOptimizations = wholeProgramOptimizations;
     this.initClassLens = InitClassLens.getThrowingInstance();
-    this.rewritePrefix = mapper;
+    this.typeRewriter = mapper;
 
     if (enableWholeProgramOptimizations() && options().callSiteOptimizationOptions().isEnabled()) {
       this.argumentPropagator = new ArgumentPropagator(withLiveness());
@@ -158,18 +156,16 @@
     return libraryMemberOptimizer.isModeled(type);
   }
 
-  private static <T extends AppInfo> PrefixRewritingMapper defaultPrefixRewritingMapper(T appInfo) {
+  private static <T extends AppInfo> TypeRewriter defaultTypeRewriter(T appInfo) {
     InternalOptions options = appInfo.options();
-    return options.getPrefixRewritingMapper();
+    return options.getTypeRewriter();
   }
 
   public static <T extends AppInfo> AppView<T> createForD8(T appInfo) {
-    return new AppView<>(
-        appInfo, WholeProgramOptimizations.OFF, defaultPrefixRewritingMapper(appInfo));
+    return new AppView<>(appInfo, WholeProgramOptimizations.OFF, defaultTypeRewriter(appInfo));
   }
 
-  public static <T extends AppInfo> AppView<T> createForD8(
-      T appInfo, PrefixRewritingMapper mapper) {
+  public static <T extends AppInfo> AppView<T> createForD8(T appInfo, TypeRewriter mapper) {
     return new AppView<>(appInfo, WholeProgramOptimizations.OFF, mapper);
   }
 
@@ -184,24 +180,20 @@
     AppInfoWithClassHierarchy appInfo =
         AppInfoWithClassHierarchy.createInitialAppInfoWithClassHierarchy(
             application, classToFeatureSplitMap, mainDexInfo);
-    return new AppView<>(
-        appInfo, WholeProgramOptimizations.ON, defaultPrefixRewritingMapper(appInfo));
+    return new AppView<>(appInfo, WholeProgramOptimizations.ON, defaultTypeRewriter(appInfo));
   }
 
-  public static <T extends AppInfo> AppView<T> createForL8(
-      T appInfo, PrefixRewritingMapper mapper) {
+  public static <T extends AppInfo> AppView<T> createForL8(T appInfo, TypeRewriter mapper) {
     return new AppView<>(appInfo, WholeProgramOptimizations.OFF, mapper);
   }
 
   public static <T extends AppInfo> AppView<T> createForRelocator(T appInfo) {
-    return new AppView<>(
-        appInfo, WholeProgramOptimizations.OFF, defaultPrefixRewritingMapper(appInfo));
+    return new AppView<>(appInfo, WholeProgramOptimizations.OFF, defaultTypeRewriter(appInfo));
   }
 
   public static AppView<AppInfoWithClassHierarchy> createForTracer(
       AppInfoWithClassHierarchy appInfo) {
-    return new AppView<>(
-        appInfo, WholeProgramOptimizations.ON, defaultPrefixRewritingMapper(appInfo));
+    return new AppView<>(appInfo, WholeProgramOptimizations.ON, defaultTypeRewriter(appInfo));
   }
 
   public AbstractValueFactory abstractValueFactory() {
@@ -305,6 +297,11 @@
   }
 
   @Override
+  public ClassResolutionResult contextIndependentDefinitionForWithResolutionResult(DexType type) {
+    return appInfo().contextIndependentDefinitionForWithResolutionResult(type);
+  }
+
+  @Override
   public final DexClass definitionFor(DexType type) {
     return appInfo().definitionFor(type);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java
new file mode 100644
index 0000000..4e34aa3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java
@@ -0,0 +1,138 @@
+// 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.graph;
+
+import static com.android.tools.r8.graph.ClassResolutionResult.NoResolutionResult.noResult;
+
+import java.util.function.Consumer;
+
+public interface ClassResolutionResult {
+
+  boolean hasClassResolutionResult();
+
+  DexClass toSingleClassWithProgramOverLibrary();
+
+  void forEachClassResolutionResult(Consumer<DexClass> consumer);
+
+  static Builder builder() {
+    return new Builder();
+  }
+
+  class Builder {
+
+    private ProgramOrClasspathClass programOrClasspathClass;
+    private DexLibraryClass libraryClass;
+
+    public Builder add(DexProgramClass programClass) {
+      assert this.programOrClasspathClass == null;
+      this.programOrClasspathClass = programClass;
+      return this;
+    }
+
+    public Builder add(DexClasspathClass classpathClass) {
+      assert this.programOrClasspathClass == null;
+      this.programOrClasspathClass = classpathClass;
+      return this;
+    }
+
+    public Builder add(DexLibraryClass libraryClass) {
+      assert this.libraryClass == null;
+      this.libraryClass = libraryClass;
+      return this;
+    }
+
+    public ClassResolutionResult build() {
+      if (programOrClasspathClass == null && libraryClass == null) {
+        return noResult();
+      } else if (programOrClasspathClass == null) {
+        return libraryClass;
+      } else if (libraryClass == null) {
+        return programOrClasspathClass;
+      } else if (programOrClasspathClass.isProgramClass()) {
+        return new ProgramAndLibraryClassResolutionResult(
+            programOrClasspathClass.asProgramClass(), libraryClass);
+      } else {
+        assert programOrClasspathClass.isClasspathClass();
+        return new ClasspathAndLibraryClassResolutionResult(
+            programOrClasspathClass.asClasspathClass(), libraryClass);
+      }
+    }
+  }
+
+  class NoResolutionResult implements ClassResolutionResult {
+
+    private static final NoResolutionResult NO_RESULT = new NoResolutionResult();
+
+    static ClassResolutionResult noResult() {
+      return NO_RESULT;
+    }
+
+    @Override
+    public boolean hasClassResolutionResult() {
+      return false;
+    }
+
+    @Override
+    public DexClass toSingleClassWithProgramOverLibrary() {
+      return null;
+    }
+
+    @Override
+    public void forEachClassResolutionResult(Consumer<DexClass> consumer) {
+      // Intentionally empty
+    }
+  }
+
+  abstract class MultipleClassResolutionResult<T extends DexClass>
+      implements ClassResolutionResult {
+
+    protected final T programOrClasspathClass;
+    protected final DexLibraryClass libraryClass;
+
+    public MultipleClassResolutionResult(T programOrClasspathClass, DexLibraryClass libraryClass) {
+      this.programOrClasspathClass = programOrClasspathClass;
+      this.libraryClass = libraryClass;
+    }
+
+    @Override
+    public boolean hasClassResolutionResult() {
+      return true;
+    }
+
+    @Override
+    public void forEachClassResolutionResult(Consumer<DexClass> consumer) {
+      consumer.accept(programOrClasspathClass);
+      consumer.accept(libraryClass);
+    }
+  }
+
+  class ProgramAndLibraryClassResolutionResult
+      extends MultipleClassResolutionResult<DexProgramClass> {
+
+    public ProgramAndLibraryClassResolutionResult(
+        DexProgramClass programClass, DexLibraryClass libraryClass) {
+      super(programClass, libraryClass);
+    }
+
+    @Override
+    public DexClass toSingleClassWithProgramOverLibrary() {
+      return programOrClasspathClass;
+    }
+  }
+
+  class ClasspathAndLibraryClassResolutionResult
+      extends MultipleClassResolutionResult<DexClasspathClass> {
+
+    public ClasspathAndLibraryClassResolutionResult(
+        DexClasspathClass classpathClass, DexLibraryClass libraryClass) {
+      super(classpathClass, libraryClass);
+    }
+
+    @Override
+    public DexClass toSingleClassWithProgramOverLibrary() {
+      return libraryClass;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/ClasspathClass.java b/src/main/java/com/android/tools/r8/graph/ClasspathClass.java
index 3801ef8..0b9367d 100644
--- a/src/main/java/com/android/tools/r8/graph/ClasspathClass.java
+++ b/src/main/java/com/android/tools/r8/graph/ClasspathClass.java
@@ -4,7 +4,8 @@
 
 package com.android.tools.r8.graph;
 
-public interface ClasspathClass extends ClasspathDefinition, ClasspathOrLibraryClass {
+public interface ClasspathClass
+    extends ClasspathDefinition, ClasspathOrLibraryClass, ProgramOrClasspathClass {
 
   @Override
   default DexClass asDexClass() {
diff --git a/src/main/java/com/android/tools/r8/graph/ClasspathDefinition.java b/src/main/java/com/android/tools/r8/graph/ClasspathDefinition.java
index 11cd7ad..43a4e70 100644
--- a/src/main/java/com/android/tools/r8/graph/ClasspathDefinition.java
+++ b/src/main/java/com/android/tools/r8/graph/ClasspathDefinition.java
@@ -6,7 +6,8 @@
 
 import java.util.function.Function;
 
-public interface ClasspathDefinition extends ClasspathOrLibraryDefinition {
+public interface ClasspathDefinition
+    extends ClasspathOrLibraryDefinition, ProgramOrClasspathDefinition {
 
   @Override
   default <T> T apply(
diff --git a/src/main/java/com/android/tools/r8/graph/Definition.java b/src/main/java/com/android/tools/r8/graph/Definition.java
index 00bca6f..05ad2d1 100644
--- a/src/main/java/com/android/tools/r8/graph/Definition.java
+++ b/src/main/java/com/android/tools/r8/graph/Definition.java
@@ -22,6 +22,14 @@
     return null;
   }
 
+  default ProgramOrClasspathClass asProgramOrClasspathClass() {
+    return null;
+  }
+
+  default ProgramOrClasspathDefinition asProgramOrClasspathDefinition() {
+    return null;
+  }
+
   ProgramDerivedContext asProgramDerivedContext(ProgramDerivedContext witness);
 
   AccessFlags<?> getAccessFlags();
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index 67f8be4..e08f2ed 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -66,8 +66,17 @@
     DexApplication self = this;
     return new DexDefinitionSupplier() {
       @Override
+      public ClassResolutionResult contextIndependentDefinitionForWithResolutionResult(
+          DexType type) {
+        return syntheticDefinitionsProvider.definitionFor(
+            type, self::contextIndependentDefinitionForWithResolutionResult);
+      }
+
+      @Override
       public DexClass definitionFor(DexType type) {
-        return syntheticDefinitionsProvider.definitionFor(type, self::definitionFor);
+        return syntheticDefinitionsProvider
+            .definitionFor(type, self::contextIndependentDefinitionForWithResolutionResult)
+            .toSingleClassWithProgramOverLibrary();
       }
 
       @Override
@@ -117,13 +126,14 @@
     return classesWithDeterministicOrder(new ArrayList<>(programClasses()));
   }
 
-  public static <T extends DexClass> List<T> classesWithDeterministicOrder(Collection<T> classes) {
+  public static <T extends ClassDefinition> List<T> classesWithDeterministicOrder(
+      Collection<T> classes) {
     return classesWithDeterministicOrder(new ArrayList<>(classes));
   }
 
-  public static <T extends DexClass> List<T> classesWithDeterministicOrder(List<T> classes) {
+  public static <T extends ClassDefinition> List<T> classesWithDeterministicOrder(List<T> classes) {
     // To keep the order deterministic, we sort the classes by their type, which is a unique key.
-    classes.sort(Comparator.comparing(DexClass::getType));
+    classes.sort(Comparator.comparing(ClassDefinition::getType));
     return classes;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 8c6bbc8..31e7030 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -40,7 +40,8 @@
 import java.util.function.Function;
 import java.util.function.Predicate;
 
-public abstract class DexClass extends DexDefinition implements ClassDefinition {
+public abstract class DexClass extends DexDefinition
+    implements ClassDefinition, ClassResolutionResult {
 
   public interface FieldSetter {
     void setField(int index, DexEncodedField field);
@@ -134,6 +135,21 @@
     }
   }
 
+  @Override
+  public boolean hasClassResolutionResult() {
+    return true;
+  }
+
+  @Override
+  public void forEachClassResolutionResult(Consumer<DexClass> consumer) {
+    consumer.accept(this);
+  }
+
+  @Override
+  public DexClass toSingleClassWithProgramOverLibrary() {
+    return this;
+  }
+
   public abstract void accept(
       Consumer<DexProgramClass> programClassConsumer,
       Consumer<DexClasspathClass> classpathClassConsumer,
diff --git a/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java b/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
index 57212d4..6968591 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
@@ -19,6 +19,17 @@
   }
 
   /**
+   * Lookup for the definition(s) of a type independent of context.
+   *
+   * <p>This will return multiple results if found in the precedence of library, program and
+   * classpath.
+   *
+   * @param type Type to look up the definition for.
+   * @return A {@link ClassResolutionResult} describing the result.
+   */
+  ClassResolutionResult contextIndependentDefinitionForWithResolutionResult(DexType type);
+
+  /**
    * Lookup for the definition of a type from a given context.
    *
    * <p>This ensures that a context overrides the usual lookup precedence if looking up itself.
@@ -81,6 +92,14 @@
     return holder != null ? holder.lookupClassMethod(method) : null;
   }
 
+  default ClassResolutionResult definitionForWithResolutionResult(
+      DexType type, DexProgramClass context) {
+    assert context.type != type || ClassResolutionResult.builder().add(context).build() == context;
+    return context.type == type
+        ? context
+        : contextIndependentDefinitionForWithResolutionResult(type);
+  }
+
   // Use programDefinitionFor with a context.
   @Deprecated
   default DexProgramClass definitionForProgramType(DexType type) {
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 686f124..3640575 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -1431,6 +1431,54 @@
           || method.match(waitLong)
           || method.match(waitLongInt);
     }
+
+    public DexMethod matchingPublicObjectMember(DexMethod method) {
+      switch (method.getName().byteAt(0)) {
+        case 't':
+          if (method.match(toString)) {
+            return toString;
+          }
+          break;
+        case 'h':
+          if (method.match(hashCode)) {
+            return hashCode;
+          }
+          break;
+        case 'e':
+          if (method.match(equals)) {
+            return equals;
+          }
+          break;
+        case 'g':
+          if (method.match(getClass)) {
+            return getClass;
+          }
+          break;
+        case 'n':
+          if (method.match(notify)) {
+            return notify;
+          }
+          if (method.match(notifyAll)) {
+            return notifyAll;
+          }
+          break;
+        case 'w':
+          if (method.match(wait)) {
+            return wait;
+          }
+          if (method.match(waitLong)) {
+            return waitLong;
+          }
+          if (method.match(waitLongInt)) {
+            return waitLongInt;
+          }
+          break;
+        default:
+          // Methods finalize and clone are not public.
+          return null;
+      }
+      return null;
+    }
   }
 
   public class BufferMembers {
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodSignature.java b/src/main/java/com/android/tools/r8/graph/DexMethodSignature.java
index e756d78..adee16b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodSignature.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodSignature.java
@@ -54,6 +54,10 @@
     return create(name, getProto());
   }
 
+  public DexMethodSignature withParameters(DexTypeList parameters, DexItemFactory dexItemFactory) {
+    return create(getName(), dexItemFactory.createProto(getReturnType(), parameters));
+  }
+
   public DexMethodSignature withProto(DexProto proto) {
     return create(getName(), proto);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 2e09eec..3077717 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -38,7 +38,7 @@
 import java.util.function.Supplier;
 
 public class DexProgramClass extends DexClass
-    implements ProgramDefinition, Supplier<DexProgramClass>, StructuralItem<DexProgramClass> {
+    implements ProgramClass, Supplier<DexProgramClass>, StructuralItem<DexProgramClass> {
 
   @FunctionalInterface
   public interface ChecksumSupplier {
diff --git a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
index 9589d1e..8e96fba 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -6,6 +6,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import static com.android.tools.r8.graph.ClassResolutionResult.NoResolutionResult.noResult;
+
 import com.android.tools.r8.DataResourceProvider;
 import com.android.tools.r8.graph.LazyLoadedDexApplication.AllClasses;
 import com.android.tools.r8.graph.classmerging.MergedClasses;
@@ -69,6 +71,12 @@
   }
 
   @Override
+  public ClassResolutionResult contextIndependentDefinitionForWithResolutionResult(DexType type) {
+    DexClass foundClass = definitionFor(type);
+    return foundClass == null ? noResult() : foundClass;
+  }
+
+  @Override
   public DexClass definitionFor(DexType type) {
     assert type.isClassType() : "Cannot lookup definition for type: " + type;
     return allClasses.get(type);
diff --git a/src/main/java/com/android/tools/r8/graph/FieldResolution.java b/src/main/java/com/android/tools/r8/graph/FieldResolution.java
index 120bee0..3dcd872 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldResolution.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldResolution.java
@@ -4,7 +4,8 @@
 
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
+import static com.android.tools.r8.graph.FieldResolutionResult.createSingleFieldResolutionResult;
+
 import com.android.tools.r8.utils.SetUtils;
 import java.util.Set;
 
@@ -23,8 +24,12 @@
   }
 
   public FieldResolutionResult resolveFieldOn(DexType type, DexField field) {
-    DexClass holder = definitionFor.contextIndependentDefinitionFor(type);
-    return holder != null ? resolveFieldOn(holder, field) : FieldResolutionResult.failure();
+    FieldResolutionResult.Builder builder = FieldResolutionResult.builder();
+    definitionFor
+        .contextIndependentDefinitionForWithResolutionResult(type)
+        .forEachClassResolutionResult(
+            clazz -> builder.addResolutionResult(resolveFieldOn(clazz, field)));
+    return builder.buildOrIfEmpty(FieldResolutionResult.failure());
   }
 
   public FieldResolutionResult resolveFieldOn(DexClass holder, DexField field) {
@@ -41,49 +46,76 @@
     // Step 1: Class declares the field.
     DexEncodedField definition = holder.lookupField(field);
     if (definition != null) {
-      return new SuccessfulFieldResolutionResult(initialResolutionHolder, holder, definition);
+      return createSingleFieldResolutionResult(initialResolutionHolder, holder, definition);
     }
     // Step 2: Apply recursively to direct superinterfaces. First match succeeds.
-    DexClassAndField result = resolveFieldOnDirectInterfaces(holder, field, visitedInterfaces);
+    FieldResolutionResult result =
+        resolveFieldOnDirectInterfaces(initialResolutionHolder, holder, field, visitedInterfaces);
     if (result != null) {
-      return new SuccessfulFieldResolutionResult(
-          initialResolutionHolder, result.getHolder(), result.getDefinition());
+      return result;
     }
     // Step 3: Apply recursively to superclass.
     if (holder.superType != null) {
-      DexClass superClass = definitionFor.contextIndependentDefinitionFor(holder.superType);
-      if (superClass != null) {
-        return resolveFieldOn(superClass, field, initialResolutionHolder, visitedInterfaces);
-      }
+      FieldResolutionResult.Builder builder = FieldResolutionResult.builder();
+      definitionFor
+          .contextIndependentDefinitionForWithResolutionResult(holder.superType)
+          .forEachClassResolutionResult(
+              superClass -> {
+                // Check if the subtype is a library type and if it is child of a non-library type.
+                // If that is the case, do not return any results.
+                if (holder.isLibraryClass() && !superClass.isLibraryClass()) {
+                  return;
+                }
+                builder.addResolutionResult(
+                    resolveFieldOn(superClass, field, initialResolutionHolder, visitedInterfaces));
+              });
+      return builder.buildOrIfEmpty(null);
     }
     return FieldResolutionResult.failure();
   }
 
-  private DexClassAndField resolveFieldOnDirectInterfaces(
-      DexClass clazz, DexField field, Set<DexType> visitedInterfaces) {
+  private FieldResolutionResult resolveFieldOnDirectInterfaces(
+      DexClass initialResolutionHolder,
+      DexClass clazz,
+      DexField field,
+      Set<DexType> visitedInterfaces) {
     for (DexType interfaceType : clazz.interfaces.values) {
       if (visitedInterfaces.add(interfaceType)) {
-        DexClass interfaceClass = definitionFor.contextIndependentDefinitionFor(interfaceType);
-        if (interfaceClass != null) {
-          DexClassAndField result =
-              resolveFieldOnInterface(interfaceClass, field, visitedInterfaces);
-          if (result != null) {
-            return result;
-          }
+        FieldResolutionResult.Builder builder = FieldResolutionResult.builder();
+        definitionFor
+            .contextIndependentDefinitionForWithResolutionResult(interfaceType)
+            .forEachClassResolutionResult(
+                ifaceClass -> {
+                  // Check if the subtype is a library type and if it is child of a non-library
+                  // type. If that is the case, do not return any results.
+                  if (clazz.isLibraryClass() && !ifaceClass.isLibraryClass()) {
+                    return;
+                  }
+                  builder.addResolutionResult(
+                      resolveFieldOnInterface(
+                          initialResolutionHolder, ifaceClass, field, visitedInterfaces));
+                });
+        FieldResolutionResult fieldResolutionResult = builder.buildOrIfEmpty(null);
+        if (fieldResolutionResult != null) {
+          return fieldResolutionResult;
         }
       }
     }
     return null;
   }
 
-  private DexClassAndField resolveFieldOnInterface(
-      DexClass interfaceClass, DexField field, Set<DexType> visitedInterfaces) {
+  private FieldResolutionResult resolveFieldOnInterface(
+      DexClass initialResolutionHolder,
+      DexClass interfaceClass,
+      DexField field,
+      Set<DexType> visitedInterfaces) {
     // Step 1: Class declares the field.
     DexEncodedField definition = interfaceClass.lookupField(field);
     if (definition != null) {
-      return DexClassAndField.create(interfaceClass, definition);
+      return createSingleFieldResolutionResult(initialResolutionHolder, interfaceClass, definition);
     }
     // Step 2: Apply recursively to direct superinterfaces. First match succeeds.
-    return resolveFieldOnDirectInterfaces(interfaceClass, field, visitedInterfaces);
+    return resolveFieldOnDirectInterfaces(
+        initialResolutionHolder, interfaceClass, field, visitedInterfaces);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
index 702d1fd..c5d89b3 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
@@ -4,7 +4,14 @@
 
 package com.android.tools.r8.graph;
 
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.OptionalBool;
+import com.google.common.collect.Iterables;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
 
 public abstract class FieldResolutionResult
     extends MemberResolutionResult<DexEncodedField, DexField> {
@@ -40,15 +47,31 @@
     return null;
   }
 
+  public ProgramField getSingleProgramField() {
+    return null;
+  }
+
   public ProgramField getProgramField() {
     return null;
   }
 
-  public boolean isSuccessfulResolution() {
+  public boolean isSingleFieldResolutionResult() {
     return false;
   }
 
-  public SuccessfulFieldResolutionResult asSuccessfulResolution() {
+  public boolean isSingleProgramFieldResolutionResult() {
+    return false;
+  }
+
+  public SingleFieldResolutionResult<?> asSingleFieldResolutionResult() {
+    return null;
+  }
+
+  public SingleProgramFieldResolutionResult asSingleProgramFieldResolutionResult() {
+    return null;
+  }
+
+  public SingleClasspathFieldResolutionResult asSingleClasspathFieldResolutionResult() {
     return null;
   }
 
@@ -58,27 +81,80 @@
   }
 
   @Override
-  public SuccessfulFieldResolutionResult asSuccessfulMemberResolutionResult() {
+  public SingleFieldResolutionResult<?> asSuccessfulMemberResolutionResult() {
     return null;
   }
 
-  public boolean isFailedOrUnknownResolution() {
+  public boolean isPossiblyFailedOrUnknownResolution() {
     return false;
   }
 
+  public boolean hasProgramOrClasspathResult() {
+    return false;
+  }
+
+  public boolean hasProgramResult() {
+    return false;
+  }
+
+  public boolean hasClasspathResult() {
+    return false;
+  }
+
+  public boolean isMultiFieldResolutionResult() {
+    return false;
+  }
+
+  public final void forEachFieldResolutionResult(Consumer<FieldResolutionResult> resultConsumer) {
+    visitFieldResolutionResults(resultConsumer, resultConsumer, resultConsumer);
+  }
+
+  public final void forEachSuccessfulFieldResolutionResult(
+      Consumer<SingleFieldResolutionResult<?>> resultConsumer) {
+    visitFieldResolutionResults(resultConsumer, failedResult -> {});
+  }
+
+  public final void visitFieldResolutionResults(
+      Consumer<SingleFieldResolutionResult<?>> singleResultConsumer,
+      Consumer<FailedOrUnknownFieldResolutionResult> failedResolutionConsumer) {
+    visitFieldResolutionResults(
+        singleResultConsumer, singleResultConsumer, failedResolutionConsumer);
+  }
+
+  public abstract void visitFieldResolutionResults(
+      Consumer<? super SingleFieldResolutionResult<?>> programOrClasspathConsumer,
+      Consumer<? super SingleLibraryFieldResolutionResult> libraryResultConsumer,
+      Consumer<? super FailedOrUnknownFieldResolutionResult> failedResolutionConsumer);
+
   public DexClass getInitialResolutionHolder() {
     return null;
   }
 
-  public static class SuccessfulFieldResolutionResult extends FieldResolutionResult
+  public static SingleFieldResolutionResult<?> createSingleFieldResolutionResult(
+      DexClass initialResolutionHolder, DexClass holder, DexEncodedField definition) {
+    if (holder.isLibraryClass()) {
+      return new SingleLibraryFieldResolutionResult(
+          initialResolutionHolder, holder.asLibraryClass(), definition);
+    } else if (holder.isClasspathClass()) {
+      return new SingleClasspathFieldResolutionResult(
+          initialResolutionHolder, holder.asClasspathClass(), definition);
+    } else {
+      assert holder.isProgramClass();
+      return new SingleProgramFieldResolutionResult(
+          initialResolutionHolder, holder.asProgramClass(), definition);
+    }
+  }
+
+  public abstract static class SingleFieldResolutionResult<T extends DexClass>
+      extends FieldResolutionResult
       implements SuccessfulMemberResolutionResult<DexEncodedField, DexField> {
 
     private final DexClass initialResolutionHolder;
-    private final DexClass resolvedHolder;
+    private final T resolvedHolder;
     private final DexEncodedField resolvedField;
 
-    SuccessfulFieldResolutionResult(
-        DexClass initialResolutionHolder, DexClass resolvedHolder, DexEncodedField resolvedField) {
+    SingleFieldResolutionResult(
+        DexClass initialResolutionHolder, T resolvedHolder, DexEncodedField resolvedField) {
       assert resolvedHolder.type == resolvedField.getHolderType();
       this.initialResolutionHolder = initialResolutionHolder;
       this.resolvedHolder = resolvedHolder;
@@ -91,7 +167,7 @@
     }
 
     @Override
-    public DexClass getResolvedHolder() {
+    public T getResolvedHolder() {
       return resolvedHolder;
     }
 
@@ -116,25 +192,18 @@
     }
 
     @Override
-    public ProgramField getProgramField() {
-      return resolvedHolder.isProgramClass()
-          ? new ProgramField(resolvedHolder.asProgramClass(), resolvedField)
-          : null;
-    }
-
-    @Override
     public OptionalBool isAccessibleFrom(
         ProgramDefinition context, AppInfoWithClassHierarchy appInfo) {
       return AccessControl.isMemberAccessible(this, context, appInfo);
     }
 
     @Override
-    public boolean isSuccessfulResolution() {
+    public boolean isSingleFieldResolutionResult() {
       return true;
     }
 
     @Override
-    public SuccessfulFieldResolutionResult asSuccessfulResolution() {
+    public SingleFieldResolutionResult<T> asSingleFieldResolutionResult() {
       return this;
     }
 
@@ -144,14 +213,232 @@
     }
 
     @Override
-    public SuccessfulFieldResolutionResult asSuccessfulMemberResolutionResult() {
+    public SingleFieldResolutionResult<T> asSuccessfulMemberResolutionResult() {
       return this;
     }
   }
 
-  public static class FailedFieldResolutionResult extends FieldResolutionResult {
+  public static class SingleProgramFieldResolutionResult
+      extends SingleFieldResolutionResult<DexProgramClass> {
 
-    private static final FailedFieldResolutionResult INSTANCE = new FailedFieldResolutionResult();
+    SingleProgramFieldResolutionResult(
+        DexClass initialResolutionHolder,
+        DexProgramClass resolvedHolder,
+        DexEncodedField resolvedField) {
+      super(initialResolutionHolder, resolvedHolder, resolvedField);
+    }
+
+    @Override
+    public ProgramField getProgramField() {
+      return getSingleProgramField();
+    }
+
+    @Override
+    public ProgramField getSingleProgramField() {
+      return new ProgramField(getResolvedHolder(), getResolvedField());
+    }
+
+    @Override
+    public boolean isSingleProgramFieldResolutionResult() {
+      return true;
+    }
+
+    @Override
+    public SingleProgramFieldResolutionResult asSingleProgramFieldResolutionResult() {
+      return this;
+    }
+
+    @Override
+    public boolean hasProgramOrClasspathResult() {
+      return true;
+    }
+
+    @Override
+    public boolean hasProgramResult() {
+      return true;
+    }
+
+    @Override
+    public void visitFieldResolutionResults(
+        Consumer<? super SingleFieldResolutionResult<?>> programOrClasspathConsumer,
+        Consumer<? super SingleLibraryFieldResolutionResult> libraryResultConsumer,
+        Consumer<? super FailedOrUnknownFieldResolutionResult> failedResolutionConsumer) {
+      programOrClasspathConsumer.accept(this);
+    }
+  }
+
+  public static class SingleClasspathFieldResolutionResult
+      extends SingleFieldResolutionResult<DexClasspathClass> {
+
+    SingleClasspathFieldResolutionResult(
+        DexClass initialResolutionHolder,
+        DexClasspathClass resolvedHolder,
+        DexEncodedField resolvedField) {
+      super(initialResolutionHolder, resolvedHolder, resolvedField);
+    }
+
+    @Override
+    public SingleClasspathFieldResolutionResult asSingleClasspathFieldResolutionResult() {
+      return this;
+    }
+
+    @Override
+    public boolean hasProgramOrClasspathResult() {
+      return true;
+    }
+
+    @Override
+    public boolean hasClasspathResult() {
+      return true;
+    }
+
+    @Override
+    public void visitFieldResolutionResults(
+        Consumer<? super SingleFieldResolutionResult<?>> programOrClasspathConsumer,
+        Consumer<? super SingleLibraryFieldResolutionResult> libraryResultConsumer,
+        Consumer<? super FailedOrUnknownFieldResolutionResult> failedResolutionConsumer) {
+      programOrClasspathConsumer.accept(this);
+    }
+  }
+
+  public static class SingleLibraryFieldResolutionResult
+      extends SingleFieldResolutionResult<DexLibraryClass> {
+
+    SingleLibraryFieldResolutionResult(
+        DexClass initialResolutionHolder,
+        DexLibraryClass resolvedHolder,
+        DexEncodedField resolvedField) {
+      super(initialResolutionHolder, resolvedHolder, resolvedField);
+    }
+
+    @Override
+    public void visitFieldResolutionResults(
+        Consumer<? super SingleFieldResolutionResult<?>> programOrClasspathConsumer,
+        Consumer<? super SingleLibraryFieldResolutionResult> libraryResultConsumer,
+        Consumer<? super FailedOrUnknownFieldResolutionResult> failedResolutionConsumer) {
+      libraryResultConsumer.accept(this);
+    }
+  }
+
+  public abstract static class MultipleFieldResolutionResult<
+          C extends DexClass & ProgramOrClasspathClass, T extends SingleFieldResolutionResult<C>>
+      extends FieldResolutionResult {
+
+    protected final T programOrClasspathResult;
+    protected final List<SingleLibraryFieldResolutionResult> libraryResolutionResults;
+    protected final List<FailedOrUnknownFieldResolutionResult> failedOrUnknownResolutionResults;
+
+    public MultipleFieldResolutionResult(
+        T programOrClasspathResult,
+        List<SingleLibraryFieldResolutionResult> libraryResolutionResults,
+        List<FailedOrUnknownFieldResolutionResult> failedOrUnknownResolutionResults) {
+      assert programOrClasspathResult == null
+          || !programOrClasspathResult.getResolvedHolder().isLibraryClass();
+      assert failedOrUnknownResolutionResults.stream()
+          .allMatch(FieldResolutionResult::isPossiblyFailedOrUnknownResolution);
+      assert BooleanUtils.intValue(programOrClasspathResult != null)
+                  + libraryResolutionResults.size()
+                  + failedOrUnknownResolutionResults.size()
+              > 1
+          : "Should have been a single or failed result";
+      this.programOrClasspathResult = programOrClasspathResult;
+      this.libraryResolutionResults = libraryResolutionResults;
+      this.failedOrUnknownResolutionResults = failedOrUnknownResolutionResults;
+    }
+
+    @Override
+    public boolean isMultiFieldResolutionResult() {
+      return true;
+    }
+
+    @Override
+    public DexClass getInitialResolutionHolder() {
+      throw new Unimplemented("Should not be called on MultipleFieldResolutionResult");
+    }
+
+    @Override
+    public boolean hasProgramOrClasspathResult() {
+      return programOrClasspathResult != null;
+    }
+
+    @Override
+    public boolean hasProgramResult() {
+      return programOrClasspathResult != null && programOrClasspathResult.hasProgramResult();
+    }
+
+    @Override
+    public boolean hasClasspathResult() {
+      return programOrClasspathResult != null && programOrClasspathResult.hasClasspathResult();
+    }
+
+    @Override
+    public OptionalBool isAccessibleFrom(
+        ProgramDefinition context, AppInfoWithClassHierarchy appInfo) {
+      throw new Unimplemented("Should not be called on MultipleFieldResolutionResult");
+    }
+
+    @Override
+    public boolean isPossiblyFailedOrUnknownResolution() {
+      return !failedOrUnknownResolutionResults.isEmpty();
+    }
+
+    @Override
+    public boolean isSuccessfulMemberResolutionResult() {
+      return failedOrUnknownResolutionResults.isEmpty();
+    }
+
+    @Override
+    public void visitFieldResolutionResults(
+        Consumer<? super SingleFieldResolutionResult<?>> programOrClasspathConsumer,
+        Consumer<? super SingleLibraryFieldResolutionResult> libraryResultConsumer,
+        Consumer<? super FailedOrUnknownFieldResolutionResult> failedResolutionConsumer) {
+      if (programOrClasspathResult != null) {
+        programOrClasspathConsumer.accept(programOrClasspathResult);
+      }
+      libraryResolutionResults.forEach(libraryResultConsumer);
+      failedOrUnknownResolutionResults.forEach(failedResolutionConsumer);
+    }
+  }
+
+  public static class MultipleProgramWithLibraryFieldResolutionResult
+      extends MultipleFieldResolutionResult<DexProgramClass, SingleProgramFieldResolutionResult> {
+
+    public MultipleProgramWithLibraryFieldResolutionResult(
+        SingleProgramFieldResolutionResult programOrClasspathResult,
+        List<SingleLibraryFieldResolutionResult> libraryResolutionResults,
+        List<FailedOrUnknownFieldResolutionResult> failedOrUnknownResolutionResults) {
+      super(programOrClasspathResult, libraryResolutionResults, failedOrUnknownResolutionResults);
+    }
+
+    @Override
+    public ProgramField getProgramField() {
+      return programOrClasspathResult == null ? null : programOrClasspathResult.getProgramField();
+    }
+  }
+
+  public static class MultipleClasspathWithLibraryFieldResolutionResult
+      extends MultipleFieldResolutionResult<
+          DexClasspathClass, SingleClasspathFieldResolutionResult> {
+
+    public MultipleClasspathWithLibraryFieldResolutionResult(
+        SingleClasspathFieldResolutionResult programOrClasspathResult,
+        List<SingleLibraryFieldResolutionResult> libraryResolutionResults,
+        List<FailedOrUnknownFieldResolutionResult> failedOrUnknownResolutionResults) {
+      super(programOrClasspathResult, libraryResolutionResults, failedOrUnknownResolutionResults);
+    }
+  }
+
+  public static class MultipleLibraryFieldResolutionResult
+      extends MultipleFieldResolutionResult<DexProgramClass, SingleProgramFieldResolutionResult> {
+
+    public MultipleLibraryFieldResolutionResult(
+        List<SingleLibraryFieldResolutionResult> libraryResolutionResults,
+        List<FailedOrUnknownFieldResolutionResult> failedOrUnknownResolutionResults) {
+      super(null, libraryResolutionResults, failedOrUnknownResolutionResults);
+    }
+  }
+
+  public abstract static class FailedOrUnknownFieldResolutionResult extends FieldResolutionResult {
 
     @Override
     public OptionalBool isAccessibleFrom(
@@ -160,9 +447,22 @@
     }
 
     @Override
-    public boolean isFailedOrUnknownResolution() {
+    public void visitFieldResolutionResults(
+        Consumer<? super SingleFieldResolutionResult<?>> programOrClasspathConsumer,
+        Consumer<? super SingleLibraryFieldResolutionResult> libraryResultConsumer,
+        Consumer<? super FailedOrUnknownFieldResolutionResult> failedResolutionConsumer) {
+      failedResolutionConsumer.accept(this);
+    }
+
+    @Override
+    public boolean isPossiblyFailedOrUnknownResolution() {
       return true;
     }
+  }
+
+  public static class FailedFieldResolutionResult extends FailedOrUnknownFieldResolutionResult {
+
+    private static final FailedFieldResolutionResult INSTANCE = new FailedFieldResolutionResult();
 
     @Override
     public boolean isFailedResolution() {
@@ -174,19 +474,84 @@
    * Used in D8 when trying to resolve a field that is not declared on the enclosing class of the
    * current method.
    */
-  public static class UnknownFieldResolutionResult extends FieldResolutionResult {
+  public static class UnknownFieldResolutionResult extends FailedOrUnknownFieldResolutionResult {
 
     private static final UnknownFieldResolutionResult INSTANCE = new UnknownFieldResolutionResult();
+  }
 
-    @Override
-    public OptionalBool isAccessibleFrom(
-        ProgramDefinition context, AppInfoWithClassHierarchy appInfo) {
-      return OptionalBool.FALSE;
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static class Builder {
+
+    private FieldResolutionResult currentResult = null;
+
+    private Builder() {}
+
+    public void addResolutionResult(FieldResolutionResult otherResult) {
+      if (currentResult == null) {
+        currentResult = otherResult;
+        return;
+      }
+      Box<SingleFieldResolutionResult<?>> singleResult = new Box<>();
+      List<SingleLibraryFieldResolutionResult> libraryResults = new ArrayList<>();
+      List<FailedOrUnknownFieldResolutionResult> failedResults = new ArrayList<>();
+      currentResult.visitFieldResolutionResults(
+          singleResult::set, libraryResults::add, failedResults::add);
+      otherResult.visitFieldResolutionResults(
+          otherProgramOrClasspathResult -> {
+            if (singleResult.isSet()) {
+              assert false : "Unexpected multiple results between program and classpath";
+              if (singleResult.get().hasProgramResult()) {
+                return;
+              }
+            }
+            singleResult.set(otherProgramOrClasspathResult);
+          },
+          newLibraryResult -> {
+            if (!Iterables.any(
+                libraryResults,
+                existing -> existing.getResolvedHolder() == newLibraryResult.getResolvedHolder())) {
+              libraryResults.add(newLibraryResult);
+            }
+          },
+          newFailedResult -> {
+            if (!Iterables.any(
+                failedResults,
+                existing ->
+                    existing.isFailedResolution() == newFailedResult.isFailedResolution())) {
+              failedResults.add(newFailedResult);
+            }
+          });
+      if (!singleResult.isSet()) {
+        if (libraryResults.size() == 1 && failedResults.isEmpty()) {
+          currentResult = libraryResults.get(0);
+        } else if (libraryResults.isEmpty() && failedResults.size() == 1) {
+          currentResult = failedResults.get(0);
+        } else {
+          currentResult = new MultipleLibraryFieldResolutionResult(libraryResults, failedResults);
+        }
+      } else if (libraryResults.isEmpty() && failedResults.isEmpty()) {
+        currentResult = singleResult.get();
+      } else if (singleResult.get().hasProgramResult()) {
+        currentResult =
+            new MultipleProgramWithLibraryFieldResolutionResult(
+                singleResult.get().asSingleProgramFieldResolutionResult(),
+                libraryResults,
+                failedResults);
+      } else {
+        SingleClasspathFieldResolutionResult classpathResult =
+            singleResult.get().asSingleClasspathFieldResolutionResult();
+        assert classpathResult != null;
+        currentResult =
+            new MultipleClasspathWithLibraryFieldResolutionResult(
+                classpathResult, libraryResults, failedResults);
+      }
     }
 
-    @Override
-    public boolean isFailedOrUnknownResolution() {
-      return true;
+    public FieldResolutionResult buildOrIfEmpty(FieldResolutionResult emptyResult) {
+      return currentResult == null ? emptyResult : currentResult;
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
index cce1760..772227c 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
@@ -21,6 +21,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
 public class LazyLoadedDexApplication extends DexApplication {
@@ -53,6 +54,32 @@
   }
 
   @Override
+  public ClassResolutionResult contextIndependentDefinitionForWithResolutionResult(DexType type) {
+    ClassResolutionResult.Builder builder = ClassResolutionResult.builder();
+    if (libraryClasses != null) {
+      addClassToBuilderIfNotNull(libraryClasses.get(type), builder::add);
+    }
+    if (programClasses == null
+        || !addClassToBuilderIfNotNull(programClasses.get(type), builder::add)) {
+      // When looking up a type that exists both on program path and classpath, we assume the
+      // program class is taken and only if not present will look at classpath.
+      if (classpathClasses != null) {
+        addClassToBuilderIfNotNull(classpathClasses.get(type), builder::add);
+      }
+    }
+    return builder.build();
+  }
+
+  private <T extends DexClass> boolean addClassToBuilderIfNotNull(T clazz, Consumer<T> adder) {
+    if (clazz != null) {
+      adder.accept(clazz);
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  @Override
   public DexClass definitionFor(DexType type) {
     assert type.isClassType() : "Cannot lookup definition for type: " + type;
     DexClass clazz = null;
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramClass.java b/src/main/java/com/android/tools/r8/graph/ProgramClass.java
new file mode 100644
index 0000000..c6fc81f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ProgramClass.java
@@ -0,0 +1,13 @@
+// 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.graph;
+
+public interface ProgramClass extends ProgramDefinition, ProgramOrClasspathClass {
+
+  @Override
+  default DexClass asDexClass() {
+    return asProgramClass();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java b/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
index ab0ee34..208c8ad 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
@@ -8,7 +8,8 @@
 import java.util.function.BiFunction;
 import java.util.function.Function;
 
-public interface ProgramDefinition extends Definition, ProgramDerivedContext {
+public interface ProgramDefinition
+    extends Definition, ProgramDerivedContext, ProgramOrClasspathDefinition {
 
   @Override
   default <T> T apply(
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramOrClasspathClass.java b/src/main/java/com/android/tools/r8/graph/ProgramOrClasspathClass.java
new file mode 100644
index 0000000..7c15abf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ProgramOrClasspathClass.java
@@ -0,0 +1,10 @@
+// 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.graph;
+
+public interface ProgramOrClasspathClass
+    extends ClassDefinition, ProgramOrClasspathDefinition, ClassResolutionResult {
+  DexClass asDexClass();
+}
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramOrClasspathDefinition.java b/src/main/java/com/android/tools/r8/graph/ProgramOrClasspathDefinition.java
new file mode 100644
index 0000000..b90c3f1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ProgramOrClasspathDefinition.java
@@ -0,0 +1,13 @@
+// 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.graph;
+
+public interface ProgramOrClasspathDefinition extends Definition {
+
+  @Override
+  default ProgramOrClasspathDefinition asProgramOrClasspathDefinition() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
index 3ebd37a..d871616 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.FieldResolutionResult;
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
 import com.android.tools.r8.ir.code.FieldInstruction;
@@ -82,27 +81,23 @@
     for (Instruction instruction : code.instructions()) {
       if (instruction.isFieldInstruction()) {
         FieldInstruction fieldInstruction = instruction.asFieldInstruction();
-        FieldResolutionResult resolutionResult =
-            appView.appInfo().resolveField(fieldInstruction.getField());
-        if (resolutionResult.isSuccessfulResolution()) {
-          ProgramField field =
-              resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
-          if (field != null) {
-            if (fieldAssignmentTracker != null) {
-              fieldAssignmentTracker.recordFieldAccess(fieldInstruction, field, code.context());
-            }
-            if (fieldBitAccessAnalysis != null) {
-              fieldBitAccessAnalysis.recordFieldAccess(
-                  fieldInstruction, field.getDefinition(), feedback);
-            }
-            if (fieldReadForInvokeReceiverAnalysis != null) {
-              fieldReadForInvokeReceiverAnalysis.recordFieldAccess(
-                  fieldInstruction, field, bytecodeMetadataProviderBuilder, code.context());
-            }
-            if (fieldReadForWriteAnalysis != null) {
-              fieldReadForWriteAnalysis.recordFieldAccess(
-                  fieldInstruction, field, bytecodeMetadataProviderBuilder);
-            }
+        ProgramField field =
+            appView.appInfo().resolveField(fieldInstruction.getField()).getProgramField();
+        if (field != null) {
+          if (fieldAssignmentTracker != null) {
+            fieldAssignmentTracker.recordFieldAccess(fieldInstruction, field, code.context());
+          }
+          if (fieldBitAccessAnalysis != null) {
+            fieldBitAccessAnalysis.recordFieldAccess(
+                fieldInstruction, field.getDefinition(), feedback);
+          }
+          if (fieldReadForInvokeReceiverAnalysis != null) {
+            fieldReadForInvokeReceiverAnalysis.recordFieldAccess(
+                fieldInstruction, field, bytecodeMetadataProviderBuilder, code.context());
+          }
+          if (fieldReadForWriteAnalysis != null) {
+            fieldReadForWriteAnalysis.recordFieldAccess(
+                fieldInstruction, field, bytecodeMetadataProviderBuilder);
           }
         }
       } else if (instruction.isNewInstance()) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
index 714535c..bbee604 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -458,7 +458,8 @@
           appView.appInfo().getFieldAccessInfoCollection();
       fieldAccessInfoCollection.forEach(
           info -> {
-            ProgramField field = appView.appInfo().resolveField(info.getField()).getProgramField();
+            ProgramField field =
+                appView.appInfo().resolveField(info.getField()).getSingleProgramField();
             if (field == null) {
               return;
             }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldReadForWriteAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldReadForWriteAnalysis.java
index 3b4f8dc..6a5c420 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldReadForWriteAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldReadForWriteAnalysis.java
@@ -58,7 +58,7 @@
         if (writtenFieldReference.match(field.getReference())
             && fieldPut.isStaticPut() == field.getAccessFlags().isStatic()) {
           ProgramField writtenField =
-              appView.appInfo().resolveField(writtenFieldReference).getProgramField();
+              appView.appInfo().resolveField(writtenFieldReference).getSingleProgramField();
           if (writtenField != null && writtenField.isStructurallyEqualTo(field)) {
             // OK.
             foundWrite = true;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
index ef8d041..5d34c84 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
@@ -22,7 +22,8 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.FieldAccessInfo;
 import com.android.tools.r8.graph.FieldAccessInfoCollection;
-import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
+import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata;
@@ -316,18 +317,19 @@
         boolean isStatic,
         boolean isWrite,
         BytecodeInstructionMetadata metadata) {
-      SuccessfulFieldResolutionResult resolutionResult =
-          appView.appInfo().resolveField(reference).asSuccessfulResolution();
-      if (resolutionResult == null) {
+      FieldResolutionResult resolutionResult = appView.appInfo().resolveField(reference);
+      if (!resolutionResult.hasProgramResult()) {
+        // We don't care about field accesses that may not resolve to a program field.
         return;
       }
 
-      DexClassAndField field = resolutionResult.getResolutionPair();
+      ProgramField field = resolutionResult.getProgramField();
       DexEncodedField definition = field.getDefinition();
 
       if (definition.isStatic() != isStatic
           || appView.isCfByteCodePassThrough(getContext().getDefinition())
-          || resolutionResult.isAccessibleFrom(getContext(), appView).isPossiblyFalse()) {
+          || resolutionResult.isAccessibleFrom(getContext(), appView).isPossiblyFalse()
+          || !resolutionResult.isSingleProgramFieldResolutionResult()) {
         recordAccessThatCannotBeOptimized(field, definition);
         return;
       }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
index ee985c4..9c09ef2 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexValue;
-import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
+import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.DominatorTree;
@@ -125,15 +125,13 @@
         if (instruction.isFieldPut()) {
           FieldInstruction fieldPut = instruction.asFieldInstruction();
           DexField field = fieldPut.getField();
-          SuccessfulFieldResolutionResult fieldResolutionResult =
-              appInfo.resolveField(field).asSuccessfulResolution();
-          if (fieldResolutionResult != null) {
-            DexEncodedField encodedField = fieldResolutionResult.getResolvedField();
-            assert encodedField != null;
+          ProgramField programField = appInfo.resolveField(field).getProgramField();
+          if (programField != null) {
+            DexEncodedField encodedField = programField.getDefinition();
             if (isSubjectToOptimization(encodedField)) {
               recordFieldPut(encodedField, fieldPut);
             } else if (isStaticFieldValueAnalysis()
-                && fieldResolutionResult.getResolvedHolder().isEnum()
+                && programField.getHolder().isEnum()
                 && isSubjectToOptimizationIgnoringPinning(encodedField)) {
               recordFieldPut(encodedField, fieldPut);
             }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
index 42a035f..88d1c47 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
@@ -14,7 +14,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.FieldAccessInfo;
 import com.android.tools.r8.graph.FieldAccessInfoCollection;
-import com.android.tools.r8.graph.FieldResolutionResult;
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.IRCode;
@@ -232,15 +231,10 @@
 
   public boolean isDeadProtoExtensionField(DexField fieldReference) {
     AppInfoWithLiveness appInfo = appView.appInfo();
-    FieldResolutionResult resolutionResult = appInfo.resolveField(fieldReference);
-    if (resolutionResult.isSuccessfulResolution()) {
-      ProgramField field =
-          resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
-      return field != null
-          && isDeadProtoExtensionField(
-              field, appInfo.getFieldAccessInfoCollection(), appInfo.getKeepInfo());
-    }
-    return false;
+    ProgramField field = appInfo.resolveField(fieldReference).getSingleProgramField();
+    return field != null
+        && isDeadProtoExtensionField(
+            field, appInfo.getFieldAccessInfoCollection(), appInfo.getKeepInfo());
   }
 
   public boolean isDeadProtoExtensionField(
@@ -262,7 +256,7 @@
 
     // Multiple GeneratedExtensionRegistries exist in Chrome; 1 per feature split.
     return fieldAccessInfo.isReadOnlyInMethodSatisfying(
-        method -> references.isFindLiteExtensionByNumberMethod(method));
+        references::isFindLiteExtensionByNumberMethod);
   }
 
   private void forEachDeadProtoExtensionField(Consumer<DexField> consumer) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
index b0ddb4a..62b8e28 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
@@ -530,16 +530,12 @@
     DexField oneOfCaseFieldReference = oneOfCaseObject.asLiveProtoFieldObject().getField();
     FieldResolutionResult oneOfCaseFieldResolutionResult =
         appView.appInfo().resolveField(oneOfCaseFieldReference);
-    if (oneOfCaseFieldResolutionResult.isFailedOrUnknownResolution()) {
+    if (!oneOfCaseFieldResolutionResult.isSingleProgramFieldResolutionResult()) {
       assert false;
       return;
     }
 
-    ProgramField oneOfCaseField =
-        oneOfCaseFieldResolutionResult
-            .asSuccessfulResolution()
-            .getResolutionPair()
-            .asProgramField();
+    ProgramField oneOfCaseField = oneOfCaseFieldResolutionResult.getProgramField();
     if (oneOfCaseField == null) {
       assert false;
       return;
@@ -565,13 +561,12 @@
     DexField oneOfFieldReference = oneOfObject.asLiveProtoFieldObject().getField();
     FieldResolutionResult oneOfFieldResolutionResult =
         appView.appInfo().resolveField(oneOfFieldReference);
-    if (oneOfFieldResolutionResult.isFailedOrUnknownResolution()) {
+    if (!oneOfFieldResolutionResult.isSingleProgramFieldResolutionResult()) {
       assert false;
       return;
     }
 
-    ProgramField oneOfField =
-        oneOfFieldResolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
+    ProgramField oneOfField = oneOfFieldResolutionResult.getProgramField();
     if (oneOfField == null || oneOfField.getHolder() != oneOfCaseField.getHolder()) {
       assert false;
       return;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldInfo.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldInfo.java
index 1515932..7c35106 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldInfo.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.FieldResolutionResult;
 import com.android.tools.r8.graph.ProgramField;
 import java.util.List;
 import java.util.OptionalInt;
@@ -131,12 +130,10 @@
 
     ProtoObject object = protoMessageInfo.getHasBitsObjects().get(hasBitsIndex);
     assert object.isLiveProtoFieldObject();
-    FieldResolutionResult resolutionResult =
-        appView.appInfo().resolveField(object.asLiveProtoFieldObject().getField());
-    if (resolutionResult.isSuccessfulResolution()) {
-      return resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
-    }
-    return null;
+    return appView
+        .appInfo()
+        .resolveField(object.asLiveProtoFieldObject().getField())
+        .getSingleProgramField();
   }
 
   public int getHazzerBitFieldIndex(ProtoMessageInfo protoMessageInfo) {
@@ -175,12 +172,10 @@
     assert type.isOneOf();
     ProtoObject object = protoMessageInfo.getOneOfObjects().get(getAuxData()).getOneOfCaseObject();
     assert object.isLiveProtoFieldObject();
-    FieldResolutionResult resolutionResult =
-        appView.appInfo().resolveField(object.asLiveProtoFieldObject().getField());
-    if (resolutionResult.isSuccessfulResolution()) {
-      return resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
-    }
-    return null;
+    return appView
+        .appInfo()
+        .resolveField(object.asLiveProtoFieldObject().getField())
+        .getSingleProgramField();
   }
 
   /**
@@ -196,12 +191,10 @@
             ? protoMessageInfo.getOneOfObjects().get(getAuxData()).getOneOfObject()
             : objects.get(0);
     assert object.isLiveProtoFieldObject();
-    FieldResolutionResult resolutionResult =
-        appView.appInfo().resolveField(object.asLiveProtoFieldObject().getField());
-    if (resolutionResult.isSuccessfulResolution()) {
-      return resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
-    }
-    return null;
+    return appView
+        .appInfo()
+        .resolveField(object.asLiveProtoFieldObject().getField())
+        .getProgramField();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
index ebbfffe..19af1c7 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.ir.analysis.value;
 
 import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
+import static com.android.tools.r8.utils.ForEachUtils.allMatch;
 
 import com.android.tools.r8.features.ClassToFeatureSplitMap;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -14,7 +15,6 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
@@ -96,9 +96,14 @@
   @Override
   public boolean isMaterializableInContext(
       AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
-    SuccessfulFieldResolutionResult resolutionResult =
-        appView.appInfo().resolveField(field).asSuccessfulResolution();
-    return resolutionResult != null && resolutionResult.isAccessibleFrom(context, appView).isTrue();
+    return allMatch(
+        appView.appInfo().resolveField(field)::forEachFieldResolutionResult,
+        resolutionResult -> {
+          if (resolutionResult.isPossiblyFailedOrUnknownResolution()) {
+            return false;
+          }
+          return resolutionResult.isAccessibleFrom(context, appView).isTrue();
+        });
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
index e1ed280..358369b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -283,25 +283,13 @@
       Value value,
       Position position) {
     InternalOptions options = appView.options();
-
-    InvokeMethod invoke;
-    if (appView.options().canUseJavaUtilObjectsRequireNonNull()) {
-      DexMethod requireNonNullMethod = appView.dexItemFactory().objectsMethods.requireNonNull;
-      invoke =
-          InvokeStatic.builder()
-              .setMethod(requireNonNullMethod)
-              .setSingleArgument(value)
-              .setPosition(position)
-              .build();
-    } else {
-      DexMethod getClassMethod = appView.dexItemFactory().objectMembers.getClass;
-      invoke =
-          InvokeVirtual.builder()
-              .setMethod(getClassMethod)
-              .setSingleArgument(value)
-              .setPosition(position)
-              .build();
-    }
+    DexMethod getClassMethod = appView.dexItemFactory().objectMembers.getClass;
+    InvokeMethod invoke =
+        InvokeVirtual.builder()
+            .setMethod(getClassMethod)
+            .setSingleArgument(value)
+            .setPosition(position)
+            .build();
     add(invoke);
     if (block.hasCatchHandlers()) {
       splitCopyCatchHandlers(code, blockIterator, options);
@@ -338,15 +326,8 @@
       removeOrReplaceByDebugLocalRead();
       return true;
     }
-    InvokeMethod replacement;
-    if (appView.options().canUseJavaUtilObjectsRequireNonNull()) {
-      DexMethod requireNonNullMethod = appView.dexItemFactory().objectsMethods.requireNonNull;
-      replacement = new InvokeStatic(requireNonNullMethod, null, ImmutableList.of(receiver));
-    } else {
-      DexMethod getClassMethod = appView.dexItemFactory().objectMembers.getClass;
-      replacement = new InvokeVirtual(getClassMethod, null, ImmutableList.of(receiver));
-    }
-    replaceCurrentInstruction(replacement);
+    DexMethod getClassMethod = appView.dexItemFactory().objectMembers.getClass;
+    replaceCurrentInstruction(new InvokeVirtual(getClassMethod, null, ImmutableList.of(receiver)));
     return true;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
index 6c773a6..c65bccc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -9,7 +9,8 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
+import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.graph.FieldResolutionResult.SingleFieldResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.ConcreteMutableFieldSet;
@@ -65,28 +66,28 @@
   public boolean instructionInstanceCanThrow(
       AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
     return internalInstructionInstanceCanThrow(
-        appView,
-        context,
-        assumption,
-        appView.appInfo().resolveField(field, context).asSuccessfulResolution());
+        appView, context, assumption, appView.appInfo().resolveField(field, context));
   }
 
   boolean internalInstructionInstanceCanThrow(
       AppView<?> appView,
       ProgramMethod context,
       SideEffectAssumption assumption,
-      SuccessfulFieldResolutionResult resolutionResult) {
-    if (resolutionResult == null) {
+      FieldResolutionResult resolutionResult) {
+    if (!resolutionResult.isSingleFieldResolutionResult()) {
+      // Conservatively treat instruction as being throwing.
       return true;
     }
-    DexEncodedField resolvedField = resolutionResult.getResolvedField();
+    SingleFieldResolutionResult<?> singleFieldResolutionResult =
+        resolutionResult.asSingleFieldResolutionResult();
+    DexEncodedField resolvedField = singleFieldResolutionResult.getResolvedField();
     // Check if the instruction may fail with an IncompatibleClassChangeError.
     if (resolvedField.isStatic() != isStaticFieldInstruction()) {
       return true;
     }
     // Check if the resolution target is accessible.
-    if (resolutionResult.getResolvedHolder() != context.getHolder()) {
-      if (resolutionResult
+    if (singleFieldResolutionResult.getResolvedHolder() != context.getHolder()) {
+      if (singleFieldResolutionResult
           .isAccessibleFrom(context, appView.appInfo().withClassHierarchy())
           .isPossiblyFalse()) {
         return true;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index 13cba81..e0ca6cd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -19,7 +19,7 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
+import com.android.tools.r8.graph.FieldResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
@@ -132,8 +132,7 @@
       AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
       AppInfoWithLiveness appInfoWithLiveness = appViewWithLiveness.appInfo();
 
-      SuccessfulFieldResolutionResult resolutionResult =
-          appInfoWithLiveness.resolveField(getField()).asSuccessfulResolution();
+      FieldResolutionResult resolutionResult = appInfoWithLiveness.resolveField(getField());
       if (internalInstructionInstanceCanThrow(appView, context, assumption, resolutionResult)) {
         return true;
       }
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index 2230cc7..b32a651 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -18,7 +18,7 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
+import com.android.tools.r8.graph.FieldResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
@@ -110,8 +110,7 @@
       AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
       AppInfoWithLiveness appInfoWithLiveness = appViewWithLiveness.appInfo();
 
-      SuccessfulFieldResolutionResult resolutionResult =
-          appInfoWithLiveness.resolveField(getField()).asSuccessfulResolution();
+      FieldResolutionResult resolutionResult = appInfoWithLiveness.resolveField(getField());
       if (internalInstructionInstanceCanThrow(appView, context, assumption, resolutionResult)) {
         return true;
       }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 145ffec..664da13 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -415,8 +415,7 @@
       ExecutorService executorService,
       CfClassSynthesizerDesugaringEventConsumer classSynthesizerEventConsumer)
       throws ExecutionException {
-    CfClassSynthesizerDesugaringCollection.create(
-            appView, instructionDesugaring.getRetargetingInfo())
+    CfClassSynthesizerDesugaringCollection.create(appView)
         .synthesizeClasses(executorService, classSynthesizerEventConsumer);
   }
 
@@ -431,8 +430,7 @@
     InterfaceMethodProcessorFacade interfaceDesugaring =
         instructionDesugaring.getInterfaceMethodPostProcessingDesugaringD8(
             ExcludeDexResources, interfaceProcessor);
-    CfPostProcessingDesugaringCollection.create(
-            appView, interfaceDesugaring, instructionDesugaring.getRetargetingInfo())
+    CfPostProcessingDesugaringCollection.create(appView, interfaceDesugaring)
         .postProcessingDesugaring(
             appView.appInfo().classes(), m -> true, eventConsumer, executorService);
     methodProcessor.awaitMethodProcessing();
@@ -590,7 +588,24 @@
               .append("'")
               .append(i < neverMergePrefixes.size() - 1 ? ", " : "");
         }
-        message.append(" with classes with any other prefixes is not allowed.");
+        message.append(" with classes with any other prefixes is not allowed: ");
+        boolean first = true;
+        int limit = 11;
+        for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
+          if (!clazz.type.descriptor.startsWith(neverMergePrefix)) {
+            if (limit-- < 0) {
+              message.append("..");
+              break;
+            }
+            if (first) {
+              first = false;
+            } else {
+              message.append(", ");
+            }
+            message.append(clazz.type);
+          }
+        }
+        message.append(".");
         throw new CompilationError(message.toString());
       }
     }
@@ -1092,6 +1107,12 @@
       timing.end();
     }
 
+    if (options.canHaveInvokeInterfaceToObjectMethodBug()) {
+      timing.begin("JDK-8272564 fix rewrite");
+      CodeRewriter.rewriteJdk8272564Fix(code, appView);
+      timing.end();
+    }
+
     boolean isDebugMode = options.debug || method.getOptimizationInfo().isReachabilitySensitive();
 
     if (isDebugMode) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
index 0da54ff..8ad701f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
@@ -54,8 +54,6 @@
 
   void markAsPropagated(DexEncodedMethod method);
 
-  void markTriggerClassInitBeforeAnySideEffect(DexEncodedMethod method, boolean mark);
-
   void setBridgeInfo(DexEncodedMethod method, BridgeInfo bridgeInfo);
 
   void setClassInlinerMethodConstraint(
@@ -119,8 +117,6 @@
 
   void unsetSimpleInliningConstraint(ProgramMethod method);
 
-  void unsetTriggerClassInitBeforeAnySideEffect(ProgramMethod method);
-
   void unsetUnusedArguments(ProgramMethod method);
 
   default void unsetOptimizationInfoForAbstractMethod(ProgramMethod method) {
@@ -143,7 +139,6 @@
       unsetReturnedArgument(method);
       unsetReturnValueOnlyDependsOnArguments(method);
       unsetSimpleInliningConstraint(method);
-      unsetTriggerClassInitBeforeAnySideEffect(method);
       unsetUnusedArguments(method);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/IRProcessingCallGraphUseRegistry.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/IRProcessingCallGraphUseRegistry.java
index b88c9d0..e20123e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/IRProcessingCallGraphUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/IRProcessingCallGraphUseRegistry.java
@@ -65,7 +65,7 @@
       return;
     }
 
-    ProgramField field = appView.appInfo().resolveField(rewrittenReference).getProgramField();
+    ProgramField field = appView.appInfo().resolveField(rewrittenReference).getSingleProgramField();
     if (field == null || appView.appInfo().isPinned(field)) {
       return;
     }
@@ -89,7 +89,7 @@
       return;
     }
 
-    ProgramField field = appView.appInfo().resolveField(rewrittenReference).getProgramField();
+    ProgramField field = appView.appInfo().resolveField(rewrittenReference).getSingleProgramField();
     if (field == null || appView.appInfo().isPinned(field)) {
       return;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index f99e380..dd2bb76 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -100,14 +100,14 @@
   public static List<DexMethod> generateListOfBackportedMethods(
       AndroidApp androidApp, InternalOptions options, ExecutorService executor) throws IOException {
     List<DexMethod> methods = new ArrayList<>();
-    PrefixRewritingMapper rewritePrefix = options.getPrefixRewritingMapper();
+    TypeRewriter typeRewriter = options.getTypeRewriter();
     AppInfo appInfo = null;
     if (androidApp != null) {
       DexApplication app =
           new ApplicationReader(androidApp, options, Timing.empty()).read(executor);
       appInfo = AppInfo.createInitialAppInfo(app);
     }
-    AppView<?> appView = AppView.createForD8(appInfo, rewritePrefix);
+    AppView<?> appView = AppView.createForD8(appInfo, typeRewriter);
     BackportedMethodRewriter.RewritableMethods rewritableMethods =
         new BackportedMethodRewriter.RewritableMethods(options, appView);
     rewritableMethods.visit(methods::add);
@@ -128,24 +128,16 @@
     DexMethod original = appView.graphLens().getOriginalMethodSignature(method);
     assert original != null;
     MethodProvider provider = rewritableMethods.getProvider(original);
-    // TODO(b/150693139): Since the DesugarLibraryRetargeter is run during IR processing while the
-    // backported method rewriter is run in cf to cf, we need here to compute if the method is
-    // actually going to be retargeted through desugared library backports, and compute the
-    // corresponding backported method if so. This can be removed once the DesugarLibraryRetargeter
-    // has been moved as a cf to cf transformation.
+    // Old versions of desugared library have in the jar file pre-desugared code. This is used
+    // to undesugar pre-desugared code, then the code is re-desugared with D8/R8. This is
+    // maintained for legacy only, recent desugared library should not be shipped with
+    // pre-desugared code.
+    Map<DexType, DexType> legacyBackport =
+        appView.options().machineDesugaredLibrarySpecification.getLegacyBackport();
     if (provider == null
         && appView.options().isDesugaredLibraryCompilation()
-        && appView
-            .options()
-            .desugaredLibrarySpecification
-            .getBackportCoreLibraryMember()
-            .containsKey(method.holder)) {
-      DexType newHolder =
-          appView
-              .options()
-              .desugaredLibrarySpecification
-              .getBackportCoreLibraryMember()
-              .get(method.holder);
+        && legacyBackport.containsKey(method.holder)) {
+      DexType newHolder = legacyBackport.get(method.holder);
       DexMethod backportedMethod =
           appView.dexItemFactory().createMethod(newHolder, method.proto, method.name);
       provider = rewritableMethods.getProvider(backportedMethod);
@@ -188,22 +180,25 @@
         initializeAndroidSv2MethodProviders(factory);
       }
 
-      // The following providers are currently not implemented at any API level in Android.
-      // They however require the Optional/Stream class to be present, either through desugared
-      // libraries or natively. If Optional/Stream class is not present, we do not desugar to
-      // avoid confusion in error messages.
-      if (appView.rewritePrefix.hasRewrittenType(factory.optionalType, appView)
-          || options.getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)) {
-        initializeJava9OptionalMethodProviders(factory);
-        initializeJava10OptionalMethodProviders(factory);
-        initializeJava11OptionalMethodProviders(factory);
+      // The following providers are implemented at API level T. For backporting they require
+      // the java.util.Optional class to be present, either through library desugaring or natively.
+      // If the java.util.Optional class is not present, we do not backport to avoid confusion in
+      // error messages.
+      if (appView.typeRewriter.hasRewrittenType(factory.optionalType, appView)
+          || options.getMinApiLevel().betweenBothIncluded(AndroidApiLevel.N, AndroidApiLevel.Sv2)) {
+        initializeAndroidOptionalTMethodProviders(factory);
       }
-      if (appView.rewritePrefix.hasRewrittenType(factory.streamType, appView)
+
+      // The following providers are currently not implemented at any API level in Android. For
+      // backporting they require the java.util.stream.Stream class to be present, either through
+      // library desugaring or natively. If the java.util.stream.Stream class is not present, we do
+      // not desugar to avoid confusion in error messages.
+      if (appView.typeRewriter.hasRewrittenType(factory.streamType, appView)
           || options.getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)) {
         initializeStreamMethodProviders(factory);
       }
 
-      if (appView.rewritePrefix.hasRewrittenType(factory.supplierType, appView)) {
+      if (appView.typeRewriter.hasRewrittenType(factory.supplierType, appView)) {
         // TODO(b/191188594): Consider adding the Objects method from R here, or instead
         //  rely on desugared library to support them.
         initializeObjectsMethodProviders(factory);
@@ -1163,6 +1158,122 @@
       }
     }
 
+    private void initializeAndroidOptionalTMethodProviders(DexItemFactory factory) {
+      DexType optionalType = factory.optionalType;
+      DexType[] optionalTypes =
+          new DexType[] {
+            factory.optionalType,
+            factory.optionalDoubleType,
+            factory.optionalLongType,
+            factory.optionalIntType,
+          };
+
+      // or (added in Java 9).
+      {
+        DexString name = factory.createString("or");
+        DexProto proto = factory.createProto(optionalType, factory.supplierType);
+        DexMethod method = factory.createMethod(optionalType, proto, name);
+        addProvider(
+            new StatifyingMethodGenerator(
+                method, BackportedMethods::OptionalMethods_or, "or", optionalType));
+      }
+
+      // stream (added in Java 9).
+      {
+        DexType[] streamReturnTypes =
+            new DexType[] {
+              factory.streamType,
+              factory.createType(factory.createString("Ljava/util/stream/DoubleStream;")),
+              factory.createType(factory.createString("Ljava/util/stream/LongStream;")),
+              factory.createType(factory.createString("Ljava/util/stream/IntStream;")),
+            };
+        TemplateMethodFactory[] streamMethodFactories =
+            new TemplateMethodFactory[] {
+              BackportedMethods::OptionalMethods_stream,
+              BackportedMethods::OptionalMethods_streamDouble,
+              BackportedMethods::OptionalMethods_streamLong,
+              BackportedMethods::OptionalMethods_streamInt,
+            };
+        DexString name = factory.createString("stream");
+        for (int i = 0; i < optionalTypes.length; i++) {
+          DexType optional = optionalTypes[i];
+          DexType streamReturnType = streamReturnTypes[i];
+          DexProto proto = factory.createProto(streamReturnType);
+          DexMethod method = factory.createMethod(optional, proto, name);
+          addProvider(
+              new StatifyingMethodGenerator(method, streamMethodFactories[i], "stream", optional));
+        }
+      }
+
+      // ifPresentOrElse (added in Java 9).
+      {
+        DexType[] consumerTypes =
+            new DexType[] {
+              factory.consumerType,
+              factory.doubleConsumer,
+              factory.longConsumer,
+              factory.intConsumer
+            };
+        TemplateMethodFactory[] methodFactories =
+            new TemplateMethodFactory[] {
+              BackportedMethods::OptionalMethods_ifPresentOrElse,
+              BackportedMethods::OptionalMethods_ifPresentOrElseDouble,
+              BackportedMethods::OptionalMethods_ifPresentOrElseLong,
+              BackportedMethods::OptionalMethods_ifPresentOrElseInt
+            };
+        for (int i = 0; i < optionalTypes.length; i++) {
+          DexType optional = optionalTypes[i];
+          DexType consumer = consumerTypes[i];
+          DexString name = factory.createString("ifPresentOrElse");
+          DexProto proto = factory.createProto(factory.voidType, consumer, factory.runnableType);
+          DexMethod method = factory.createMethod(optional, proto, name);
+          addProvider(
+              new StatifyingMethodGenerator(
+                  method, methodFactories[i], "ifPresentOrElse", optional));
+        }
+      }
+
+      // orElseThrow (added in Java 10).
+      {
+        DexType[] returnTypes =
+            new DexType[] {
+              factory.objectType, factory.doubleType, factory.longType, factory.intType,
+            };
+        MethodInvokeRewriter[] rewriters =
+            new MethodInvokeRewriter[] {
+              OptionalMethodRewrites.rewriteOrElseGet(),
+              OptionalMethodRewrites.rewriteDoubleOrElseGet(),
+              OptionalMethodRewrites.rewriteLongOrElseGet(),
+              OptionalMethodRewrites.rewriteIntOrElseGet(),
+            };
+        DexString name = factory.createString("orElseThrow");
+        for (int i = 0; i < optionalTypes.length; i++) {
+          DexProto proto = factory.createProto(returnTypes[i]);
+          DexMethod method = factory.createMethod(optionalTypes[i], proto, name);
+          addProvider(new InvokeRewriter(method, rewriters[i]));
+        }
+      }
+
+      // isEmpty (added in Java 11).
+      {
+        TemplateMethodFactory[] methodFactories =
+            new TemplateMethodFactory[] {
+              BackportedMethods::OptionalMethods_isEmpty,
+              BackportedMethods::OptionalMethods_isEmptyDouble,
+              BackportedMethods::OptionalMethods_isEmptyLong,
+              BackportedMethods::OptionalMethods_isEmptyInt
+            };
+        DexString name = factory.createString("isEmpty");
+        for (int i = 0; i < optionalTypes.length; i++) {
+          DexProto proto = factory.createProto(factory.booleanType);
+          DexMethod method = factory.createMethod(optionalTypes[i], proto, name);
+          addProvider(
+              new StatifyingMethodGenerator(
+                  method, methodFactories[i], "isEmpty", optionalTypes[i]));
+        }
+      }
+    }
+
     private void initializeJava9MethodProviders(DexItemFactory factory) {
       // Integer
       DexType type = factory.boxedIntType;
@@ -1298,127 +1409,6 @@
               method, BackportedMethods::StringMethods_stripTrailing, "stripTrailing", type));
     }
 
-    private void initializeJava9OptionalMethodProviders(DexItemFactory factory) {
-      // Optional
-      DexType optionalType = factory.optionalType;
-
-      // Optional.or(supplier)
-      DexString name = factory.createString("or");
-      DexProto proto = factory.createProto(optionalType, factory.supplierType);
-      DexMethod method = factory.createMethod(optionalType, proto, name);
-      addProvider(
-          new StatifyingMethodGenerator(
-              method, BackportedMethods::OptionalMethods_or, "or", optionalType));
-
-      // Optional{void,Int,Long,Double}.stream()
-      DexType[] optionalTypes =
-          new DexType[] {
-            optionalType,
-            factory.optionalDoubleType,
-            factory.optionalLongType,
-            factory.optionalIntType,
-          };
-      DexType[] streamReturnTypes =
-          new DexType[] {
-            factory.streamType,
-            factory.createType(factory.createString("Ljava/util/stream/DoubleStream;")),
-            factory.createType(factory.createString("Ljava/util/stream/LongStream;")),
-            factory.createType(factory.createString("Ljava/util/stream/IntStream;")),
-          };
-      TemplateMethodFactory[] streamMethodFactories =
-          new TemplateMethodFactory[] {
-            BackportedMethods::OptionalMethods_stream,
-            BackportedMethods::OptionalMethods_streamDouble,
-            BackportedMethods::OptionalMethods_streamLong,
-            BackportedMethods::OptionalMethods_streamInt,
-          };
-      name = factory.createString("stream");
-      for (int i = 0; i < optionalTypes.length; i++) {
-        DexType optional = optionalTypes[i];
-        DexType streamReturnType = streamReturnTypes[i];
-        proto = factory.createProto(streamReturnType);
-        method = factory.createMethod(optional, proto, name);
-        addProvider(
-            new StatifyingMethodGenerator(method, streamMethodFactories[i], "stream", optional));
-      }
-
-      // Optional{void,Int,Long,Double}.ifPresentOrElse(consumer,runnable)
-      DexType[] consumerTypes =
-          new DexType[] {
-            factory.consumerType, factory.doubleConsumer, factory.longConsumer, factory.intConsumer
-          };
-      TemplateMethodFactory[] methodFactories =
-          new TemplateMethodFactory[] {
-            BackportedMethods::OptionalMethods_ifPresentOrElse,
-            BackportedMethods::OptionalMethods_ifPresentOrElseDouble,
-            BackportedMethods::OptionalMethods_ifPresentOrElseLong,
-            BackportedMethods::OptionalMethods_ifPresentOrElseInt
-          };
-      for (int i = 0; i < optionalTypes.length; i++) {
-        DexType optional = optionalTypes[i];
-        DexType consumer = consumerTypes[i];
-        name = factory.createString("ifPresentOrElse");
-        proto = factory.createProto(factory.voidType, consumer, factory.runnableType);
-        method = factory.createMethod(optional, proto, name);
-        addProvider(
-            new StatifyingMethodGenerator(method, methodFactories[i], "ifPresentOrElse", optional));
-      }
-    }
-
-    private void initializeJava10OptionalMethodProviders(DexItemFactory factory) {
-      // Optional{void,Int,Long,Double}.orElseThrow()
-      DexType[] optionalTypes =
-          new DexType[] {
-            factory.optionalType,
-            factory.optionalDoubleType,
-            factory.optionalLongType,
-            factory.optionalIntType,
-          };
-      DexType[] returnTypes =
-          new DexType[] {
-            factory.objectType, factory.doubleType, factory.longType, factory.intType,
-          };
-      MethodInvokeRewriter[] rewriters =
-          new MethodInvokeRewriter[] {
-            OptionalMethodRewrites.rewriteOrElseGet(),
-            OptionalMethodRewrites.rewriteDoubleOrElseGet(),
-            OptionalMethodRewrites.rewriteLongOrElseGet(),
-            OptionalMethodRewrites.rewriteIntOrElseGet(),
-          };
-      DexString name = factory.createString("orElseThrow");
-      for (int i = 0; i < optionalTypes.length; i++) {
-        DexProto proto = factory.createProto(returnTypes[i]);
-        DexMethod method = factory.createMethod(optionalTypes[i], proto, name);
-        addProvider(new InvokeRewriter(method, rewriters[i]));
-      }
-    }
-
-    private void initializeJava11OptionalMethodProviders(DexItemFactory factory) {
-      // Optional{void,Int,Long,Double}.isEmpty()
-      DexType[] optionalTypes =
-          new DexType[] {
-            factory.optionalType,
-            factory.optionalDoubleType,
-            factory.optionalLongType,
-            factory.optionalIntType,
-          };
-      TemplateMethodFactory[] methodFactories =
-          new TemplateMethodFactory[] {
-            BackportedMethods::OptionalMethods_isEmpty,
-            BackportedMethods::OptionalMethods_isEmptyDouble,
-            BackportedMethods::OptionalMethods_isEmptyLong,
-            BackportedMethods::OptionalMethods_isEmptyInt
-          };
-      DexString name = factory.createString("isEmpty");
-      for (int i = 0; i < optionalTypes.length; i++) {
-        DexType optionalType = optionalTypes[i];
-        DexProto proto = factory.createProto(factory.booleanType);
-        DexMethod method = factory.createMethod(optionalType, proto, name);
-        addProvider(
-            new StatifyingMethodGenerator(method, methodFactories[i], "isEmpty", optionalType));
-      }
-    }
-
     private void initializeStreamMethodProviders(DexItemFactory factory) {
       // Stream
       DexType streamType = factory.streamType;
@@ -1445,7 +1435,7 @@
     }
 
     private void addProvider(MethodProvider generator) {
-      if (appView.options().desugaredLibrarySpecification.isSupported(generator.method, appView)) {
+      if (appView.options().machineDesugaredLibrarySpecification.isSupported(generator.method)) {
         // TODO(b/174453232): Remove this after the configuration file format has bee updated
         // with the "rewrite_method" section.
         if (generator.method.getHolderType() == appView.dexItemFactory().objectsType) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java
index c54f7ec..02c87a6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java
@@ -6,7 +6,6 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterL8Synthesizer;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.RetargetingInfo;
 import com.android.tools.r8.ir.desugar.itf.ProgramEmulatedInterfaceSynthesizer;
 import com.android.tools.r8.ir.desugar.records.RecordDesugaring;
 import com.android.tools.r8.utils.ThreadUtils;
@@ -17,8 +16,7 @@
 
 public abstract class CfClassSynthesizerDesugaringCollection {
 
-  public static CfClassSynthesizerDesugaringCollection create(
-      AppView<?> appView, RetargetingInfo retargetingInfo) {
+  public static CfClassSynthesizerDesugaringCollection create(AppView<?> appView) {
     Collection<CfClassSynthesizerDesugaring> synthesizers = new ArrayList<>();
     if (appView.options().isDesugaredLibraryCompilation()) {
       ProgramEmulatedInterfaceSynthesizer emulatedInterfaceSynthesizer =
@@ -27,7 +25,7 @@
         synthesizers.add(emulatedInterfaceSynthesizer);
       }
       DesugaredLibraryRetargeterL8Synthesizer retargeterL8Synthesizer =
-          DesugaredLibraryRetargeterL8Synthesizer.create(appView, retargetingInfo);
+          DesugaredLibraryRetargeterL8Synthesizer.create(appView);
       if (retargeterL8Synthesizer != null) {
         synthesizers.add(retargeterL8Synthesizer);
       }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
index de491c7..c457ace 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.RetargetingInfo;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor;
 import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor;
@@ -81,8 +80,6 @@
   public abstract InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaringR8(
       Flavor flavor, Predicate<ProgramMethod> isLiveMethod, InterfaceProcessor processor);
 
-  public abstract RetargetingInfo getRetargetingInfo();
-
   public abstract void withDesugaredLibraryAPIConverter(
       Consumer<DesugaredLibraryAPIConverter> consumer);
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
index 1fd0ad0..09b456b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPICallbackSynthesizer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterPostProcessor;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.RetargetingInfo;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
 import com.android.tools.r8.ir.desugar.records.RecordDesugaring;
 import java.util.ArrayList;
@@ -21,12 +20,10 @@
 public abstract class CfPostProcessingDesugaringCollection {
 
   public static CfPostProcessingDesugaringCollection create(
-      AppView<?> appView,
-      InterfaceMethodProcessorFacade interfaceMethodProcessorFacade,
-      RetargetingInfo retargetingInfo) {
+      AppView<?> appView, InterfaceMethodProcessorFacade interfaceMethodProcessorFacade) {
     if (appView.options().desugarState.isOn()) {
       return NonEmptyCfPostProcessingDesugaringCollection.create(
-          appView, interfaceMethodProcessorFacade, retargetingInfo);
+          appView, interfaceMethodProcessorFacade);
     }
     return empty();
   }
@@ -53,19 +50,17 @@
     }
 
     public static CfPostProcessingDesugaringCollection create(
-        AppView<?> appView,
-        InterfaceMethodProcessorFacade interfaceMethodProcessorFacade,
-        RetargetingInfo retargetingInfo) {
+        AppView<?> appView, InterfaceMethodProcessorFacade interfaceMethodProcessorFacade) {
       ArrayList<CfPostProcessingDesugaring> desugarings = new ArrayList<>();
-      if (!appView.options().desugaredLibrarySpecification.getRetargetCoreLibMember().isEmpty()
+      if (appView.options().machineDesugaredLibrarySpecification.hasRetargeting()
           && !appView.options().isDesugaredLibraryCompilation()) {
-        desugarings.add(new DesugaredLibraryRetargeterPostProcessor(appView, retargetingInfo));
+        desugarings.add(new DesugaredLibraryRetargeterPostProcessor(appView));
       }
       if (interfaceMethodProcessorFacade != null) {
         desugarings.add(interfaceMethodProcessorFacade);
       }
       DesugaredLibraryAPICallbackSynthesizer apiCallbackSynthesizor =
-          appView.rewritePrefix.isRewriting()
+          appView.typeRewriter.isRewriting()
               ? new DesugaredLibraryAPICallbackSynthesizer(appView)
               : null;
       // At this point the desugaredLibraryAPIConverter is required to be last to generate
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
index 1e4d6f8..e7a1587 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.RetargetingInfo;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor;
 import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor;
@@ -89,11 +88,6 @@
   }
 
   @Override
-  public RetargetingInfo getRetargetingInfo() {
-    return null;
-  }
-
-  @Override
   public void withDesugaredLibraryAPIConverter(Consumer<DesugaredLibraryAPIConverter> consumer) {
     // Intentionally empty.
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index 33c69ac..4c51b9f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -18,7 +18,6 @@
 import com.android.tools.r8.ir.desugar.constantdynamic.ConstantDynamicInstructionDesugaring;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeter;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.RetargetingInfo;
 import com.android.tools.r8.ir.desugar.icce.AlwaysThrowingInstructionDesugaring;
 import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialToSelfDesugaring;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
@@ -82,9 +81,9 @@
     this.nestBasedAccessDesugaring = NestBasedAccessDesugaring.create(appView);
     BackportedMethodRewriter backportedMethodRewriter = null;
     desugaredLibraryRetargeter =
-        appView.options().desugaredLibrarySpecification.getRetargetCoreLibMember().isEmpty()
-            ? null
-            : new DesugaredLibraryRetargeter(appView);
+        appView.options().machineDesugaredLibrarySpecification.hasRetargeting()
+            ? new DesugaredLibraryRetargeter(appView)
+            : null;
     if (desugaredLibraryRetargeter != null) {
       desugarings.add(desugaredLibraryRetargeter);
     }
@@ -112,7 +111,7 @@
       interfaceMethodRewriter = null;
     }
     desugaredLibraryAPIConverter =
-        appView.rewritePrefix.isRewriting()
+        appView.typeRewriter.isRewriting()
             ? new DesugaredLibraryAPIConverter(
                 appView,
                 SetUtils.newImmutableSetExcludingNullItems(
@@ -405,14 +404,6 @@
   }
 
   @Override
-  public RetargetingInfo getRetargetingInfo() {
-    if (desugaredLibraryRetargeter != null) {
-      return desugaredLibraryRetargeter.getRetargetingInfo();
-    }
-    return null;
-  }
-
-  @Override
   public void withDesugaredLibraryAPIConverter(Consumer<DesugaredLibraryAPIConverter> consumer) {
     if (desugaredLibraryAPIConverter != null) {
       consumer.accept(desugaredLibraryAPIConverter);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java b/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
deleted file mode 100644
index bf96b67..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
+++ /dev/null
@@ -1,296 +0,0 @@
-// Copyright (c) 2019, 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.ir.desugar;
-
-import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
-import com.android.tools.r8.utils.DescriptorUtils;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Sets;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Consumer;
-
-public abstract class PrefixRewritingMapper {
-
-  public static PrefixRewritingMapper empty() {
-    return new EmptyPrefixRewritingMapper();
-  }
-
-  public abstract void rewriteType(DexType type, DexType rewrittenType);
-
-  public abstract DexType rewrittenType(DexType type, AppView<?> appView);
-
-  public abstract DexType rewrittenContextType(DexType type, AppView<?> appView);
-
-  public boolean hasRewrittenType(DexType type, AppView<?> appView) {
-    return rewrittenType(type, appView) != null;
-  }
-
-  public boolean hasRewrittenTypeInSignature(DexProto proto, AppView<?> appView) {
-    if (hasRewrittenType(proto.returnType, appView)) {
-      return true;
-    }
-    for (DexType paramType : proto.parameters.values) {
-      if (hasRewrittenType(paramType, appView)) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  public abstract boolean isRewriting();
-
-  public abstract void forAllRewrittenTypes(Consumer<DexType> consumer);
-
-  public static class DesugarPrefixRewritingMapper extends PrefixRewritingMapper {
-
-    private final Set<DexType> notRewritten = Sets.newConcurrentHashSet();
-    private final Map<DexType, DexType> rewritten = new ConcurrentHashMap<>();
-    private final Map<DexString, DexString> initialPrefixes;
-    private final DexItemFactory factory;
-    private final boolean l8Compilation;
-
-    public DesugarPrefixRewritingMapper(
-        Map<String, String> prefixes, DexItemFactory itemFactory, boolean libraryCompilation) {
-      assert itemFactory != null || prefixes.isEmpty();
-      this.factory = itemFactory;
-      this.l8Compilation = libraryCompilation;
-      ImmutableMap.Builder<DexString, DexString> builder = ImmutableMap.builder();
-      for (String key : prefixes.keySet()) {
-        builder.put(toDescriptorPrefix(key), toDescriptorPrefix(prefixes.get(key)));
-      }
-      this.initialPrefixes = builder.build();
-      validatePrefixes(prefixes);
-    }
-
-    private DexString toDescriptorPrefix(String prefix) {
-      return factory.createString("L" + DescriptorUtils.getBinaryNameFromJavaType(prefix));
-    }
-
-    @Override
-    public void forAllRewrittenTypes(Consumer<DexType> consumer) {
-      rewritten.keySet().forEach(consumer);
-    }
-
-    private void validatePrefixes(Map<String, String> initialPrefixes) {
-      String[] prefixes = initialPrefixes.keySet().toArray(new String[0]);
-      for (int i = 0; i < prefixes.length; i++) {
-        for (int j = i + 1; j < prefixes.length; j++) {
-          String small, large;
-          if (prefixes[i].length() < prefixes[j].length()) {
-            small = prefixes[i];
-            large = prefixes[j];
-          } else {
-            small = prefixes[j];
-            large = prefixes[i];
-          }
-          if (large.startsWith(small)) {
-            throw new CompilationError(
-                "Inconsistent prefix in desugared library:"
-                    + " Should a class starting with "
-                    + small
-                    + " be rewritten using "
-                    + small
-                    + " -> "
-                    + initialPrefixes.get(small)
-                    + " or using "
-                    + large
-                    + " - > "
-                    + initialPrefixes.get(large)
-                    + " ?");
-          }
-        }
-      }
-    }
-
-    @Override
-    public DexType rewrittenType(DexType type, AppView<?> appView) {
-      assert appView != null || l8Compilation;
-      if (notRewritten.contains(type)) {
-        return null;
-      }
-      if (rewritten.containsKey(type)) {
-        return rewritten.get(type);
-      }
-      return computePrefix(type, appView);
-    }
-
-    @Override
-    public DexType rewrittenContextType(DexType type, AppView<?> appView) {
-      DexType rewritten = rewrittenType(type, appView);
-      if (rewritten != null) {
-        return rewritten;
-      }
-      LegacyDesugaredLibrarySpecification desugaredLibrarySpecification =
-          appView.options().desugaredLibrarySpecification;
-      if (desugaredLibrarySpecification.getEmulateLibraryInterface().containsKey(type)) {
-        return desugaredLibrarySpecification.getEmulateLibraryInterface().get(type);
-      }
-      for (Map<DexType, DexType> value :
-          desugaredLibrarySpecification.getRetargetCoreLibMember().values()) {
-        if (value.containsKey(type)) {
-          // Hack until machine specification are ready.
-          String prefix =
-              DescriptorUtils.getJavaTypeFromBinaryName(
-                  desugaredLibrarySpecification.getSynthesizedLibraryClassesPackagePrefix());
-          String interfaceType = type.toString();
-          int firstPackage = interfaceType.indexOf('.');
-          return appView
-              .dexItemFactory()
-              .createType(
-                  DescriptorUtils.javaTypeToDescriptor(
-                      prefix + interfaceType.substring(firstPackage + 1)));
-        }
-      }
-      return null;
-    }
-
-    // Besides L8 compilation, program types should not be rewritten.
-    private void failIfRewritingProgramType(DexType type, AppView<?> appView) {
-      if (l8Compilation) {
-        return;
-      }
-
-      DexType dexType = type.isArrayType() ? type.toBaseType(appView.dexItemFactory()) : type;
-      DexClass dexClass = appView.definitionFor(dexType);
-      if (dexClass != null && dexClass.isProgramClass()) {
-        appView
-            .options()
-            .reporter
-            .error(
-                "Cannot compile program class "
-                    + dexType
-                    + " since it conflicts with a desugared library rewriting rule.");
-      }
-    }
-
-    @Override
-    public void rewriteType(DexType type, DexType rewrittenType) {
-      assert !notRewritten.contains(type)
-          : "New rewriting rule for "
-              + type
-              + " but the compiler has already made decisions based on the fact that this type was"
-              + " not rewritten";
-      assert !rewritten.containsKey(type) || rewritten.get(type) == rewrittenType
-          : "New rewriting rule for "
-              + type
-              + " but the compiler has already made decisions based on a different rewriting rule"
-              + " for this type";
-      rewritten.put(type, rewrittenType);
-    }
-
-    private DexType computePrefix(DexType type, AppView<?> appView) {
-      DexString prefixToMatch = type.descriptor.withoutArray(factory);
-      DexType result = lookup(type, prefixToMatch, initialPrefixes);
-      if (result != null) {
-        failIfRewritingProgramType(type, appView);
-        return result;
-      }
-      notRewritten.add(type);
-      return null;
-    }
-
-    private DexType lookup(DexType type, DexString prefixToMatch, Map<DexString, DexString> map) {
-      // TODO(b/154800164): We could use tries instead of looking-up everywhere.
-      for (DexString prefix : map.keySet()) {
-        if (prefixToMatch.startsWith(prefix)) {
-          DexString rewrittenTypeDescriptor =
-              type.descriptor.withNewPrefix(prefix, map.get(prefix), factory);
-          DexType rewrittenType = factory.createType(rewrittenTypeDescriptor);
-          rewriteType(type, rewrittenType);
-          return rewrittenType;
-        }
-      }
-      return null;
-    }
-
-    @Override
-    public boolean isRewriting() {
-      return true;
-    }
-  }
-
-  public static class MachineDesugarPrefixRewritingMapper extends PrefixRewritingMapper {
-
-    private final PrefixRewritingMapper mapper;
-    private final Map<DexType, DexType> rewriteType;
-    private final Map<DexType, DexType> rewriteDerivedTypeOnly;
-
-    public MachineDesugarPrefixRewritingMapper(
-        PrefixRewritingMapper mapper, MachineRewritingFlags flags) {
-      this.mapper = mapper;
-      this.rewriteType = new ConcurrentHashMap<>(flags.getRewriteType());
-      rewriteDerivedTypeOnly = flags.getRewriteDerivedTypeOnly();
-    }
-
-    @Override
-    public DexType rewrittenType(DexType type, AppView<?> appView) {
-      assert mapper.rewrittenType(type, appView) == rewriteType.get(type);
-      return rewriteType.get(type);
-    }
-
-    @Override
-    public DexType rewrittenContextType(DexType context, AppView<?> appView) {
-      if (rewriteType.containsKey(context)) {
-        return rewriteType.get(context);
-      }
-      return rewriteDerivedTypeOnly.get(context);
-    }
-
-    @Override
-    public void rewriteType(DexType type, DexType rewrittenType) {
-      mapper.rewriteType(type, rewrittenType);
-      rewriteType.compute(
-          type,
-          (t, val) -> {
-            assert val == null || val == rewrittenType;
-            return rewrittenType;
-          });
-    }
-
-    @Override
-    public boolean isRewriting() {
-      return true;
-    }
-
-    @Override
-    public void forAllRewrittenTypes(Consumer<DexType> consumer) {
-      rewriteType.keySet().forEach(consumer);
-    }
-  }
-
-  public static class EmptyPrefixRewritingMapper extends PrefixRewritingMapper {
-
-    @Override
-    public DexType rewrittenType(DexType type, AppView<?> appView) {
-      return null;
-    }
-
-    @Override
-    public DexType rewrittenContextType(DexType type, AppView<?> appView) {
-      return null;
-    }
-
-    @Override
-    public void rewriteType(DexType type, DexType rewrittenType) {}
-
-    @Override
-    public boolean isRewriting() {
-      return false;
-    }
-
-    @Override
-    public void forAllRewrittenTypes(Consumer<DexType> consumer) {}
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/TypeRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/TypeRewriter.java
new file mode 100644
index 0000000..c8c5159
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/TypeRewriter.java
@@ -0,0 +1,125 @@
+// 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.ir.desugar;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
+
+public abstract class TypeRewriter {
+
+  public static TypeRewriter empty() {
+    return new EmptyPrefixRewritingMapper();
+  }
+
+  public abstract void rewriteType(DexType type, DexType rewrittenType);
+
+  public abstract DexType rewrittenType(DexType type, AppView<?> appView);
+
+  public abstract DexType rewrittenContextType(DexType type);
+
+  public boolean hasRewrittenType(DexType type, AppView<?> appView) {
+    return rewrittenType(type, appView) != null;
+  }
+
+  public boolean hasRewrittenTypeInSignature(DexProto proto, AppView<?> appView) {
+    if (hasRewrittenType(proto.returnType, appView)) {
+      return true;
+    }
+    for (DexType paramType : proto.parameters.values) {
+      if (hasRewrittenType(paramType, appView)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public abstract boolean isRewriting();
+
+  public abstract void forAllRewrittenTypes(Consumer<DexType> consumer);
+
+  public static class MachineDesugarPrefixRewritingMapper extends TypeRewriter {
+
+    private final Map<DexType, DexType> rewriteType;
+    private final Map<DexType, DexType> rewriteDerivedTypeOnly;
+
+    public MachineDesugarPrefixRewritingMapper(MachineDesugaredLibrarySpecification specification) {
+      this.rewriteType = new ConcurrentHashMap<>(specification.getRewriteType());
+      rewriteDerivedTypeOnly = specification.getRewriteDerivedTypeOnly();
+    }
+
+    @Override
+    public DexType rewrittenType(DexType type, AppView<?> appView) {
+      if (type.isArrayType()) {
+        DexType rewrittenBaseType =
+            rewrittenType(type.toBaseType(appView.dexItemFactory()), appView);
+        if (rewrittenBaseType == null) {
+          return null;
+        }
+        return appView
+            .dexItemFactory()
+            .createArrayType(type.getNumberOfLeadingSquareBrackets(), rewrittenBaseType);
+      }
+      return rewriteType.get(type);
+    }
+
+    @Override
+    public DexType rewrittenContextType(DexType context) {
+      assert !context.isArrayType();
+      if (rewriteType.containsKey(context)) {
+        return rewriteType.get(context);
+      }
+      return rewriteDerivedTypeOnly.get(context);
+    }
+
+    @Override
+    public void rewriteType(DexType type, DexType rewrittenType) {
+      rewriteType.compute(
+          type,
+          (t, val) -> {
+            assert val == null || val == rewrittenType;
+            return rewrittenType;
+          });
+    }
+
+    @Override
+    public boolean isRewriting() {
+      return true;
+    }
+
+    @Override
+    public void forAllRewrittenTypes(Consumer<DexType> consumer) {
+      rewriteType.keySet().forEach(consumer);
+    }
+  }
+
+  public static class EmptyPrefixRewritingMapper extends TypeRewriter {
+
+    @Override
+    public DexType rewrittenType(DexType type, AppView<?> appView) {
+      return null;
+    }
+
+    @Override
+    public DexType rewrittenContextType(DexType type) {
+      return null;
+    }
+
+    @Override
+    public void rewriteType(DexType type, DexType rewrittenType) {}
+
+    @Override
+    public boolean isRewriting() {
+      return false;
+    }
+
+    @Override
+    public void forAllRewrittenTypes(Consumer<DexType> consumer) {}
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
index 150e6c4..abbdb4f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
@@ -20,13 +20,13 @@
 import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaring;
 import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryAPICallbackSynthesizorEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APICallbackWrapperCfCodeProvider;
 import com.android.tools.r8.utils.OptionalBool;
 import com.android.tools.r8.utils.WorkList;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 
@@ -106,11 +106,11 @@
         || definition.isLibraryMethodOverride().isFalse()) {
       return false;
     }
-    if (!appView.rewritePrefix.hasRewrittenTypeInSignature(definition.getProto(), appView)
+    if (!appView.typeRewriter.hasRewrittenTypeInSignature(definition.getProto(), appView)
         || appView
             .options()
-            .desugaredLibrarySpecification
-            .getEmulateLibraryInterface()
+            .machineDesugaredLibrarySpecification
+            .getEmulatedInterfaces()
             .containsKey(method.getHolderType())) {
       return false;
     }
@@ -127,7 +127,7 @@
         return false;
       }
     }
-    if (!appView.options().desugaredLibrarySpecification.supportAllCallbacksFromLibrary()
+    if (!appView.options().machineDesugaredLibrarySpecification.supportAllCallbacksFromLibrary()
         && appView.options().isDesugaredLibraryCompilation()) {
       return false;
     }
@@ -163,7 +163,7 @@
       DexEncodedMethod dexEncodedMethod = dexClass.lookupVirtualMethod(method.getReference());
       if (dexEncodedMethod != null) {
         // In this case, the object will be wrapped.
-        if (appView.rewritePrefix.hasRewrittenType(dexClass.type, appView)) {
+        if (appView.typeRewriter.hasRewrittenType(dexClass.type, appView)) {
           return false;
         }
         if (dexEncodedMethod.isFinal()) {
@@ -178,13 +178,13 @@
   }
 
   private boolean shouldGenerateCallbacksForEmulateInterfaceAPIs(DexClass dexClass) {
-    if (appView.options().desugaredLibrarySpecification.supportAllCallbacksFromLibrary()) {
+    if (appView.options().machineDesugaredLibrarySpecification.supportAllCallbacksFromLibrary()) {
       return true;
     }
-    Map<DexType, DexType> emulateLibraryInterfaces =
-        appView.options().desugaredLibrarySpecification.getEmulateLibraryInterface();
-    return !(emulateLibraryInterfaces.containsKey(dexClass.type)
-        || emulateLibraryInterfaces.containsValue(dexClass.type));
+    MachineDesugaredLibrarySpecification specification =
+        appView.options().machineDesugaredLibrarySpecification;
+    return !(specification.getEmulatedInterfaces().containsKey(dexClass.type)
+        || specification.isEmulatedInterfaceRewrittenType(dexClass.type));
   }
 
   private ProgramMethod generateCallbackMethod(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
index 987dc8d..d8b8430 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
@@ -152,7 +152,7 @@
       return false;
     }
     DexType holderType = invokedMethod.getHolderType();
-    if (appView.rewritePrefix.hasRewrittenType(holderType, appView) || holderType.isArrayType()) {
+    if (appView.typeRewriter.hasRewrittenType(holderType, appView) || holderType.isArrayType()) {
       return false;
     }
     DexClass dexClass = appView.definitionFor(holderType);
@@ -165,7 +165,7 @@
     if (isAlreadyDesugared(invoke, context)) {
       return false;
     }
-    return appView.rewritePrefix.hasRewrittenTypeInSignature(invokedMethod.getProto(), appView);
+    return appView.typeRewriter.hasRewrittenTypeInSignature(invokedMethod.getProto(), appView);
   }
 
   // The problem is that a method can resolve into a library method which is not present at runtime,
@@ -181,8 +181,8 @@
     return interfaceResult != null
         && appView
             .options()
-            .desugaredLibrarySpecification
-            .getEmulateLibraryInterface()
+            .machineDesugaredLibrarySpecification
+            .getEmulatedInterfaces()
             .containsKey(interfaceResult.getHolderType());
   }
 
@@ -196,14 +196,14 @@
     DexType[] newParameters = originalMethod.proto.parameters.values.clone();
     int index = 0;
     for (DexType param : originalMethod.proto.parameters.values) {
-      if (appView.rewritePrefix.hasRewrittenType(param, appView)) {
+      if (appView.typeRewriter.hasRewrittenType(param, appView)) {
         newParameters[index] = vivifiedTypeFor(param, appView);
       }
       index++;
     }
     DexType returnType = originalMethod.proto.returnType;
     DexType newReturnType =
-        appView.rewritePrefix.hasRewrittenType(returnType, appView)
+        appView.typeRewriter.hasRewrittenType(returnType, appView)
             ? vivifiedTypeFor(returnType, appView)
             : returnType;
     DexProto newProto = appView.dexItemFactory().createProto(newReturnType, newParameters);
@@ -236,7 +236,7 @@
             .createSynthesizedType(
                 DescriptorUtils.javaTypeToDescriptor(VIVIFIED_PREFIX + type.toString()));
     // We would need to ensure a classpath class for each type to remove this rewriteType call.
-    appView.rewritePrefix.rewriteType(vivifiedType, type);
+    appView.typeRewriter.rewriteType(vivifiedType, type);
     return vivifiedType;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
index 4a8cabe..1581f83 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
@@ -27,7 +27,8 @@
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryClasspathWrapperSynthesizeEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.CustomConversionDescriptor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterConstructorCfCodeProvider;
 import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterVivifiedWrapperCfCodeProvider;
 import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterWrapperCfCodeProvider;
@@ -39,13 +40,12 @@
 import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
 import com.android.tools.r8.utils.StringDiagnostic;
-import com.google.common.collect.Iterables;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.LinkedList;
+import java.util.IdentityHashMap;
 import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.Map;
 import java.util.function.Function;
 
 // I am responsible for the generation of wrappers used to call library APIs when desugaring
@@ -95,8 +95,6 @@
 
   private final AppView<?> appView;
   private final DexItemFactory factory;
-  private final ConcurrentHashMap<DexType, List<DexEncodedMethod>> allImplementedMethodsCache =
-      new ConcurrentHashMap<>();
   private final DesugaredLibraryEnumConversionSynthesizer enumConverter;
 
   public DesugaredLibraryWrapperSynthesizer(AppView<?> appView) {
@@ -118,7 +116,7 @@
     if (type.isArrayType()) {
       return shouldConvert(type.toBaseType(appView.dexItemFactory()), method, context);
     }
-    if (!appView.rewritePrefix.hasRewrittenType(type, appView)) {
+    if (!appView.typeRewriter.hasRewrittenType(type, appView)) {
       return false;
     }
     if (canConvert(type)) {
@@ -174,22 +172,32 @@
   private DexMethod getCustomConversion(DexType type, DexType srcType, DexType destType) {
     // ConversionType holds the methods "rewrittenType convert(type)" and the other way around.
     // But everything is going to be rewritten, so we need to use vivifiedType and type".
-    DexType conversionHolder =
-        appView.options().desugaredLibrarySpecification.getCustomConversions().get(type);
-    if (conversionHolder != null) {
-      return factory.createMethod(
-          conversionHolder, factory.createProto(destType, srcType), factory.convertMethodName);
+    CustomConversionDescriptor descriptor =
+        appView.options().machineDesugaredLibrarySpecification.getCustomConversions().get(type);
+    if (descriptor == null) {
+      return null;
     }
-    return null;
+    // Because the conversion have rewritten types instead of vivified type we cannot use the
+    // specification content directly until the rewriting is done upfront in the compilation.
+    DexMethod conversion = type == srcType ? descriptor.getTo() : descriptor.getFrom();
+    assert type == srcType
+        ? type == conversion.getReturnType()
+        : type == conversion.getArgumentType(0, true);
+    return factory.createMethod(
+        conversion.getHolderType(), factory.createProto(destType, srcType), conversion.getName());
   }
 
   private boolean canConvert(DexType type) {
-    return appView.options().desugaredLibrarySpecification.getCustomConversions().containsKey(type)
+    return appView
+            .options()
+            .machineDesugaredLibrarySpecification
+            .getCustomConversions()
+            .containsKey(type)
         || canGenerateWrapper(type);
   }
 
   private void reportInvalidInvoke(DexType type, DexMethod invokedMethod, ProgramMethod context) {
-    DexType desugaredType = appView.rewritePrefix.rewrittenType(type, appView);
+    DexType desugaredType = appView.typeRewriter.rewrittenType(type, appView);
     Origin origin = context != null ? context.getOrigin() : Origin.unknown();
     Position position =
         context != null ? new MethodPosition(context.getMethodReference()) : Position.UNKNOWN;
@@ -212,7 +220,7 @@
   }
 
   private boolean canGenerateWrapper(DexType type) {
-    return appView.options().desugaredLibrarySpecification.getWrapperConversions().contains(type);
+    return appView.options().machineDesugaredLibrarySpecification.getWrappers().containsKey(type);
   }
 
   private DexClass getValidClassToWrap(DexType type) {
@@ -257,6 +265,9 @@
       return getExistingProgramWrapperConversions(context);
     }
     assert context.isNotProgramClass();
+    Iterable<DexMethod> methods =
+        appView.options().machineDesugaredLibrarySpecification.getWrappers().get(context.type);
+    assert methods != null;
     ClasspathOrLibraryClass classpathOrLibraryContext = context.asClasspathOrLibraryClass();
     DexType type = context.type;
     DexType vivifiedType = vivifiedTypeFor(type);
@@ -267,7 +278,7 @@
             type,
             classpathOrLibraryContext,
             eventConsumer,
-            wrapperField -> synthesizeVirtualMethodsForTypeWrapper(context, wrapperField));
+            wrapperField -> synthesizeVirtualMethodsForTypeWrapper(context, methods, wrapperField));
     DexClass vivifiedWrapper =
         ensureClasspathWrapper(
             SyntheticKind.VIVIFIED_WRAPPER,
@@ -275,7 +286,8 @@
             vivifiedType,
             classpathOrLibraryContext,
             eventConsumer,
-            wrapperField -> synthesizeVirtualMethodsForVivifiedTypeWrapper(context, wrapperField));
+            wrapperField ->
+                synthesizeVirtualMethodsForVivifiedTypeWrapper(context, methods, wrapperField));
     return new WrapperConversions(
         getConversion(wrapper, vivifiedType, type),
         getConversion(vivifiedWrapper, type, vivifiedType));
@@ -440,8 +452,7 @@
   }
 
   private Collection<DexEncodedMethod> synthesizeVirtualMethodsForVivifiedTypeWrapper(
-      DexClass dexClass, DexEncodedField wrapperField) {
-    Iterable<DexMethod> allImplementedMethods = allImplementedMethods(dexClass);
+      DexClass dexClass, Iterable<DexMethod> allImplementedMethods, DexEncodedField wrapperField) {
     List<DexEncodedMethod> generatedMethods = new ArrayList<>();
     // Each method should use only types in their signature, but each method the wrapper forwards
     // to should used only vivified types.
@@ -458,9 +469,8 @@
       if (holderClass == null) {
         assert appView
             .options()
-            .desugaredLibrarySpecification
-            .getEmulateLibraryInterface()
-            .containsValue(method.getHolderType());
+            .machineDesugaredLibrarySpecification
+            .isEmulatedInterfaceRewrittenType(method.getHolderType());
         isInterface = true;
       } else {
         isInterface = holderClass.isInterface();
@@ -483,8 +493,7 @@
   }
 
   private Collection<DexEncodedMethod> synthesizeVirtualMethodsForTypeWrapper(
-      DexClass dexClass, DexEncodedField wrapperField) {
-    Iterable<DexMethod> dexMethods = allImplementedMethods(dexClass);
+      DexClass dexClass, Iterable<DexMethod> dexMethods, DexEncodedField wrapperField) {
     List<DexEncodedMethod> generatedMethods = new ArrayList<>();
     // Each method should use only vivified types in their signature, but each method the wrapper
     // forwards
@@ -540,62 +549,6 @@
         .build();
   }
 
-  private Iterable<DexMethod> allImplementedMethods(DexClass clazz) {
-    if (appView.options().testing.machineDesugaredLibrarySpecification != null) {
-      return appView
-          .options()
-          .testing
-          .machineDesugaredLibrarySpecification
-          .getRewritingFlags()
-          .getWrappers()
-          .get(clazz.type);
-    }
-    List<DexEncodedMethod> dexEncodedMethods =
-        allImplementedMethodsCache.computeIfAbsent(
-            clazz.type, type -> internalAllImplementedMethods(clazz));
-    return Iterables.transform(dexEncodedMethods, m -> m.getReference());
-  }
-
-  private List<DexEncodedMethod> internalAllImplementedMethods(DexClass libraryClass) {
-    LinkedList<DexClass> workList = new LinkedList<>();
-    List<DexEncodedMethod> implementedMethods = new ArrayList<>();
-    workList.add(libraryClass);
-    while (!workList.isEmpty()) {
-      DexClass dexClass = workList.removeFirst();
-      for (DexEncodedMethod virtualMethod : dexClass.virtualMethods()) {
-        if (!virtualMethod.isPrivateMethod()) {
-          boolean alreadyAdded = false;
-          // This looks quadratic but given the size of the collections met in practice for
-          // desugared libraries (Max ~15) it does not matter.
-          for (DexEncodedMethod alreadyImplementedMethod : implementedMethods) {
-            if (alreadyImplementedMethod.getReference().match(virtualMethod.getReference())) {
-              alreadyAdded = true;
-              break;
-            }
-          }
-          if (!alreadyAdded) {
-            implementedMethods.add(virtualMethod);
-          }
-        }
-      }
-      for (DexType itf : dexClass.interfaces.values) {
-        DexClass itfClass = appView.definitionFor(itf);
-        // Cannot be null in program since we started from a LibraryClass.
-        assert itfClass != null || appView.options().isDesugaredLibraryCompilation();
-        if (itfClass != null) {
-          workList.add(itfClass);
-        }
-      }
-      if (dexClass.superType != factory.objectType) {
-        DexClass superClass = appView.definitionFor(dexClass.superType);
-        assert superClass != null; // Cannot be null since we started from a LibraryClass.
-        workList.add(superClass);
-      }
-    }
-    assert !Iterables.any(implementedMethods, DexEncodedMethod::isFinal);
-    return implementedMethods;
-  }
-
   private DexField wrappedValueField(DexType holder, DexType fieldType) {
     return factory.createField(holder, fieldType, factory.wrapperFieldName);
   }
@@ -619,25 +572,28 @@
   // conversion methods are present.
   @Override
   public void synthesizeClasses(CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
-    LegacyDesugaredLibrarySpecification spec = appView.options().desugaredLibrarySpecification;
-    List<DexProgramClass> validClassesToWrap = new ArrayList<>();
-    for (DexType type : spec.getWrapperConversions()) {
-      assert !spec.getCustomConversions().containsKey(type);
-      DexClass validClassToWrap = getValidClassToWrap(type);
-      // In broken set-ups we can end up having a json files containing wrappers of non desugared
-      // classes. Such wrappers are not required since the class won't be rewritten.
-      if (validClassToWrap.isProgramClass()) {
-        if (validClassToWrap.isEnum()) {
-          enumConverter.ensureProgramEnumConversionClass(validClassToWrap, eventConsumer);
-        } else {
-          validClassesToWrap.add(validClassToWrap.asProgramClass());
-          ensureProgramWrappersWithoutVirtualMethods(validClassToWrap, eventConsumer);
-        }
-      }
-    }
-    for (DexProgramClass validClassToWrap : validClassesToWrap) {
-      ensureProgramWrappersVirtualMethods(validClassToWrap);
-    }
+    MachineDesugaredLibrarySpecification librarySpecification =
+        appView.options().machineDesugaredLibrarySpecification;
+    Map<DexProgramClass, Iterable<DexMethod>> validClassesToWrap = new IdentityHashMap<>();
+    librarySpecification
+        .getWrappers()
+        .forEach(
+            (type, methods) -> {
+              assert !librarySpecification.getCustomConversions().containsKey(type);
+              DexClass validClassToWrap = getValidClassToWrap(type);
+              // In broken set-ups we can end up having a json files containing wrappers of non
+              // desugared classes. Such wrappers are not required since the class won't be
+              // rewritten.
+              if (validClassToWrap.isProgramClass()) {
+                if (validClassToWrap.isEnum()) {
+                  enumConverter.ensureProgramEnumConversionClass(validClassToWrap, eventConsumer);
+                } else {
+                  validClassesToWrap.put(validClassToWrap.asProgramClass(), methods);
+                  ensureProgramWrappersWithoutVirtualMethods(validClassToWrap, eventConsumer);
+                }
+              }
+            });
+    validClassesToWrap.forEach(this::ensureProgramWrappersVirtualMethods);
   }
 
   // We generate first the two wrappers with the constructor method and the fields, then we
@@ -665,15 +621,16 @@
         SyntheticKind.VIVIFIED_WRAPPER, programContext, vivifiedWrapper, wrapper);
   }
 
-  private void ensureProgramWrappersVirtualMethods(DexClass context) {
+  private void ensureProgramWrappersVirtualMethods(DexClass context, Iterable<DexMethod> methods) {
     assert context.isProgramClass();
     DexProgramClass wrapper = getExistingProgramWrapper(context, SyntheticKind.WRAPPER);
     wrapper.addVirtualMethods(
-        synthesizeVirtualMethodsForTypeWrapper(context, getWrapperUniqueEncodedField(wrapper)));
+        synthesizeVirtualMethodsForTypeWrapper(
+            context, methods, getWrapperUniqueEncodedField(wrapper)));
     DexProgramClass vivifiedWrapper =
         getExistingProgramWrapper(context, SyntheticKind.VIVIFIED_WRAPPER);
     vivifiedWrapper.addVirtualMethods(
         synthesizeVirtualMethodsForVivifiedTypeWrapper(
-            context, getWrapperUniqueEncodedField(vivifiedWrapper)));
+            context, methods, getWrapperUniqueEncodedField(vivifiedWrapper)));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
index 6a6a2d4..1e871b3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
@@ -6,14 +6,9 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
-import com.android.tools.r8.ir.desugar.PrefixRewritingMapper.DesugarPrefixRewritingMapper;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.InternalOptions;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -23,56 +18,20 @@
   private final boolean libraryCompilation;
   private final HumanTopLevelFlags topLevelFlags;
   private final HumanRewritingFlags rewritingFlags;
-  private final PrefixRewritingMapper prefixRewritingMapper;
-
-  public static HumanDesugaredLibrarySpecification withOnlyRewritePrefixForTesting(
-      Map<String, String> prefix, InternalOptions options) {
-    return new HumanDesugaredLibrarySpecification(
-        HumanTopLevelFlags.empty(),
-        HumanRewritingFlags.withOnlyRewritePrefixForTesting(prefix, options),
-        true,
-        options.itemFactory);
-  }
-
-  public static HumanDesugaredLibrarySpecification empty() {
-    return new HumanDesugaredLibrarySpecification(
-        HumanTopLevelFlags.empty(), HumanRewritingFlags.empty(), false, null) {
-
-      @Override
-      public boolean isSupported(DexReference reference, AppView<?> appView) {
-        return false;
-      }
-
-      @Override
-      public boolean isEmptyConfiguration() {
-        return true;
-      }
-    };
-  }
 
   public HumanDesugaredLibrarySpecification(
       HumanTopLevelFlags topLevelFlags,
       HumanRewritingFlags rewritingFlags,
-      boolean libraryCompilation,
-      DexItemFactory factory) {
+      boolean libraryCompilation) {
     this.libraryCompilation = libraryCompilation;
     this.topLevelFlags = topLevelFlags;
     this.rewritingFlags = rewritingFlags;
-    this.prefixRewritingMapper =
-        rewritingFlags.getRewritePrefix().isEmpty()
-            ? PrefixRewritingMapper.empty()
-            : new DesugarPrefixRewritingMapper(
-                rewritingFlags.getRewritePrefix(), factory, libraryCompilation);
   }
 
   public boolean supportAllCallbacksFromLibrary() {
     return topLevelFlags.supportAllCallbacksFromLibrary();
   }
 
-  public PrefixRewritingMapper getPrefixRewritingMapper() {
-    return prefixRewritingMapper;
-  }
-
   public AndroidApiLevel getRequiredCompilationApiLevel() {
     return topLevelFlags.getRequiredCompilationAPILevel();
   }
@@ -109,10 +68,6 @@
     return rewritingFlags.getEmulateLibraryInterface();
   }
 
-  public boolean isSupported(DexReference reference, AppView<?> appView) {
-    return prefixRewritingMapper.hasRewrittenType(reference.getContextType(), appView);
-  }
-
   // If the method is retargeted, answers the retargeted method, else null.
   public DexMethod retargetMethod(DexEncodedMethod method, AppView<?> appView) {
     Map<DexMethod, DexType> retargetCoreLibMember = rewritingFlags.getRetargetCoreLibMember();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
index a612177..e01def6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
@@ -103,7 +103,7 @@
 
     HumanDesugaredLibrarySpecification config =
         new HumanDesugaredLibrarySpecification(
-            topLevelFlags, legacyRewritingFlags, libraryCompilation, dexItemFactory);
+            topLevelFlags, legacyRewritingFlags, libraryCompilation);
     origin = null;
     return config;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
index 3da0b97..9ab05b9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
@@ -6,13 +6,9 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
-import com.android.tools.r8.ir.desugar.PrefixRewritingMapper.DesugarPrefixRewritingMapper;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
@@ -26,25 +22,18 @@
   private final boolean libraryCompilation;
   private final LegacyTopLevelFlags topLevelFlags;
   private final LegacyRewritingFlags rewritingFlags;
-  private final PrefixRewritingMapper prefixRewritingMapper;
 
   public static LegacyDesugaredLibrarySpecification withOnlyRewritePrefixForTesting(
       Map<String, String> prefix, InternalOptions options) {
     return new LegacyDesugaredLibrarySpecification(
         LegacyTopLevelFlags.empty(),
         LegacyRewritingFlags.withOnlyRewritePrefixForTesting(prefix, options),
-        true,
-        options.itemFactory);
+        true);
   }
 
   public static LegacyDesugaredLibrarySpecification empty() {
     return new LegacyDesugaredLibrarySpecification(
-        LegacyTopLevelFlags.empty(), LegacyRewritingFlags.empty(), false, null) {
-
-      @Override
-      public boolean isSupported(DexReference reference, AppView<?> appView) {
-        return false;
-      }
+        LegacyTopLevelFlags.empty(), LegacyRewritingFlags.empty(), false) {
 
       @Override
       public boolean isEmptyConfiguration() {
@@ -56,16 +45,10 @@
   public LegacyDesugaredLibrarySpecification(
       LegacyTopLevelFlags topLevelFlags,
       LegacyRewritingFlags rewritingFlags,
-      boolean libraryCompilation,
-      DexItemFactory factory) {
+      boolean libraryCompilation) {
     this.libraryCompilation = libraryCompilation;
     this.topLevelFlags = topLevelFlags;
     this.rewritingFlags = rewritingFlags;
-    this.prefixRewritingMapper =
-        rewritingFlags.getRewritePrefix().isEmpty()
-            ? PrefixRewritingMapper.empty()
-            : new DesugarPrefixRewritingMapper(
-                rewritingFlags.getRewritePrefix(), factory, libraryCompilation);
   }
 
   public LegacyTopLevelFlags getTopLevelFlags() {
@@ -80,10 +63,6 @@
     return topLevelFlags.supportAllCallbacksFromLibrary();
   }
 
-  public PrefixRewritingMapper getPrefixRewritingMapper() {
-    return prefixRewritingMapper;
-  }
-
   public AndroidApiLevel getRequiredCompilationApiLevel() {
     return topLevelFlags.getRequiredCompilationAPILevel();
   }
@@ -123,10 +102,6 @@
     return rewritingFlags.getEmulateLibraryInterface();
   }
 
-  public boolean isSupported(DexReference reference, AppView<?> appView) {
-    return prefixRewritingMapper.hasRewrittenType(reference.getContextType(), appView);
-  }
-
   // If the method is retargeted, answers the retargeted method, else null.
   public DexMethod retargetMethod(DexEncodedMethod method, AppView<?> appView) {
     Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecificationParser.java
index 7cece17..a3afcd9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecificationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecificationParser.java
@@ -110,7 +110,7 @@
 
     LegacyDesugaredLibrarySpecification config =
         new LegacyDesugaredLibrarySpecification(
-            topLevelFlags, legacyRewritingFlags, libraryCompilation, dexItemFactory);
+            topLevelFlags, legacyRewritingFlags, libraryCompilation);
     origin = null;
     return config;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyTopLevelFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyTopLevelFlags.java
index 465d125..8e0293b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyTopLevelFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyTopLevelFlags.java
@@ -54,7 +54,7 @@
 
   public static LegacyTopLevelFlags testing() {
     return new LegacyTopLevelFlags(
-        AndroidApiLevel.B,
+        AndroidApiLevel.O,
         FALL_BACK_SYNTHESIZED_CLASSES_PACKAGE_PREFIX,
         "testing",
         null,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/CustomConversionDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/CustomConversionDescriptor.java
new file mode 100644
index 0000000..e8c3ce3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/CustomConversionDescriptor.java
@@ -0,0 +1,27 @@
+// 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.ir.desugar.desugaredlibrary.machinespecification;
+
+import com.android.tools.r8.graph.DexMethod;
+
+public class CustomConversionDescriptor {
+  private final DexMethod to;
+  private final DexMethod from;
+
+  public CustomConversionDescriptor(DexMethod to, DexMethod from) {
+    this.to = to;
+    this.from = from;
+    assert to.getReturnType() == from.getArgumentType(0, true);
+    assert from.getReturnType() == to.getArgumentType(0, true);
+  }
+
+  public DexMethod getTo() {
+    return to;
+  }
+
+  public DexMethod getFrom() {
+    return from;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
index 501482e..adc507f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
@@ -4,12 +4,39 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification;
 
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+
 public class MachineDesugaredLibrarySpecification {
 
   private final boolean libraryCompilation;
   private final MachineTopLevelFlags topLevelFlags;
   private final MachineRewritingFlags rewritingFlags;
 
+  public static MachineDesugaredLibrarySpecification empty() {
+    return new MachineDesugaredLibrarySpecification(
+        false, MachineTopLevelFlags.empty(), MachineRewritingFlags.builder().build()) {
+      @Override
+      public boolean isSupported(DexReference reference) {
+        return false;
+      }
+    };
+  }
+
+  public static MachineDesugaredLibrarySpecification withOnlyRewriteTypeForTesting(
+      Map<DexType, DexType> rewriteTypeForTesting) {
+    MachineRewritingFlags.Builder builder = MachineRewritingFlags.builder();
+    rewriteTypeForTesting.forEach(builder::rewriteType);
+    return new MachineDesugaredLibrarySpecification(
+        true, MachineTopLevelFlags.empty(), builder.build());
+  }
+
   public MachineDesugaredLibrarySpecification(
       boolean libraryCompilation,
       MachineTopLevelFlags topLevelFlags,
@@ -23,11 +50,120 @@
     return libraryCompilation;
   }
 
-  public MachineTopLevelFlags getTopLevelFlags() {
-    return topLevelFlags;
+  public AndroidApiLevel getRequiredCompilationAPILevel() {
+    return topLevelFlags.getRequiredCompilationAPILevel();
   }
 
-  public MachineRewritingFlags getRewritingFlags() {
-    return rewritingFlags;
+  public String getSynthesizedLibraryClassesPackagePrefix() {
+    return topLevelFlags.getSynthesizedLibraryClassesPackagePrefix();
+  }
+
+  public String getIdentifier() {
+    return topLevelFlags.getIdentifier();
+  }
+
+  public String getJsonSource() {
+    return topLevelFlags.getJsonSource();
+  }
+
+  public boolean supportAllCallbacksFromLibrary() {
+    return topLevelFlags.supportAllCallbacksFromLibrary();
+  }
+
+  public List<String> getExtraKeepRules() {
+    return topLevelFlags.getExtraKeepRules();
+  }
+
+  public Map<DexType, DexType> getRewriteType() {
+    return rewritingFlags.getRewriteType();
+  }
+
+  public Map<DexType, DexType> getRewriteDerivedTypeOnly() {
+    return rewritingFlags.getRewriteDerivedTypeOnly();
+  }
+
+  public Map<DexMethod, DexMethod> getStaticRetarget() {
+    return rewritingFlags.getStaticRetarget();
+  }
+
+  public Map<DexMethod, DexMethod> getNonEmulatedVirtualRetarget() {
+    return rewritingFlags.getNonEmulatedVirtualRetarget();
+  }
+
+  public Map<DexMethod, EmulatedDispatchMethodDescriptor> getEmulatedVirtualRetarget() {
+    return rewritingFlags.getEmulatedVirtualRetarget();
+  }
+
+  public Map<DexMethod, DexMethod> getEmulatedVirtualRetargetThroughEmulatedInterface() {
+    return rewritingFlags.getEmulatedVirtualRetargetThroughEmulatedInterface();
+  }
+
+  public void forEachRetargetHolder(Consumer<DexType> consumer) {
+    rewritingFlags.forEachRetargetHolder(consumer);
+  }
+
+  public Map<DexType, EmulatedInterfaceDescriptor> getEmulatedInterfaces() {
+    return rewritingFlags.getEmulatedInterfaces();
+  }
+
+  public EmulatedDispatchMethodDescriptor getEmulatedInterfaceEmulatedDispatchMethodDescriptor(
+      DexMethod method) {
+    return rewritingFlags.getEmulatedInterfaceEmulatedDispatchMethodDescriptor(method);
+  }
+
+  public boolean isCustomConversionRewrittenType(DexType type) {
+    return rewritingFlags.isCustomConversionRewrittenType(type);
+  }
+
+  public boolean isEmulatedInterfaceRewrittenType(DexType type) {
+    return rewritingFlags.isEmulatedInterfaceRewrittenType(type);
+  }
+
+  public Map<DexType, List<DexMethod>> getWrappers() {
+    return rewritingFlags.getWrappers();
+  }
+
+  public Map<DexType, DexType> getLegacyBackport() {
+    return rewritingFlags.getLegacyBackport();
+  }
+
+  public Set<DexType> getDontRetarget() {
+    return rewritingFlags.getDontRetarget();
+  }
+
+  public Map<DexType, CustomConversionDescriptor> getCustomConversions() {
+    return rewritingFlags.getCustomConversions();
+  }
+
+  public boolean hasRetargeting() {
+    return rewritingFlags.hasRetargeting();
+  }
+
+  public boolean hasEmulatedInterfaces() {
+    return rewritingFlags.hasEmulatedInterfaces();
+  }
+
+  public boolean isSupported(DexReference reference) {
+    // Support through type rewriting.
+    if (rewritingFlags.getRewriteType().containsKey(reference.getContextType())) {
+      return true;
+    }
+    if (!reference.isDexMethod()) {
+      return false;
+    }
+    // Support through retargeting.
+    DexMethod dexMethod = reference.asDexMethod();
+    if (getStaticRetarget().containsKey(dexMethod)
+        || getNonEmulatedVirtualRetarget().containsKey(dexMethod)
+        || getEmulatedVirtualRetarget().containsKey(dexMethod)) {
+      return true;
+    }
+    // Support through emulated interface.
+    for (EmulatedInterfaceDescriptor descriptor : getEmulatedInterfaces().values()) {
+      if (descriptor.getEmulatedMethods().containsKey(dexMethod)) {
+        return true;
+      }
+    }
+    return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
index a6c487e..f34634d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
@@ -5,16 +5,16 @@
 package com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification;
 
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.utils.Pair;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Consumer;
 
 public class MachineRewritingFlags {
 
@@ -28,16 +28,19 @@
       Map<DexMethod, DexMethod> staticRetarget,
       Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget,
       Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget,
+      Map<DexMethod, DexMethod> emulatedVirtualRetargetThroughEmulatedInterface,
       Map<DexType, EmulatedInterfaceDescriptor> emulatedInterfaces,
       Map<DexType, List<DexMethod>> wrappers,
       Map<DexType, DexType> legacyBackport,
       Set<DexType> dontRetarget,
-      Map<DexType, Pair<DexType, DexString>> customConversions) {
+      Map<DexType, CustomConversionDescriptor> customConversions) {
     this.rewriteType = rewriteType;
     this.rewriteDerivedTypeOnly = rewriteDerivedTypeOnly;
     this.staticRetarget = staticRetarget;
     this.nonEmulatedVirtualRetarget = nonEmulatedVirtualRetarget;
     this.emulatedVirtualRetarget = emulatedVirtualRetarget;
+    this.emulatedVirtualRetargetThroughEmulatedInterface =
+        emulatedVirtualRetargetThroughEmulatedInterface;
     this.emulatedInterfaces = emulatedInterfaces;
     this.wrappers = wrappers;
     this.legacyBackport = legacyBackport;
@@ -63,6 +66,9 @@
 
   // Virtual methods to retarget through emulated dispatch.
   private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget;
+  // Virtual methods to retarget through emulated dispatch but handled through emulated interface
+  // dispatch. The method has to override an emulated interface method.
+  private final Map<DexMethod, DexMethod> emulatedVirtualRetargetThroughEmulatedInterface;
 
   // Emulated interface descriptors.
   private final Map<DexType, EmulatedInterfaceDescriptor> emulatedInterfaces;
@@ -72,7 +78,7 @@
 
   private final Map<DexType, DexType> legacyBackport;
   private final Set<DexType> dontRetarget;
-  private final Map<DexType, Pair<DexType, DexString>> customConversions;
+  private final Map<DexType, CustomConversionDescriptor> customConversions;
 
   public Map<DexType, DexType> getRewriteType() {
     return rewriteType;
@@ -94,6 +100,16 @@
     return emulatedVirtualRetarget;
   }
 
+  public Map<DexMethod, DexMethod> getEmulatedVirtualRetargetThroughEmulatedInterface() {
+    return emulatedVirtualRetargetThroughEmulatedInterface;
+  }
+
+  public void forEachRetargetHolder(Consumer<DexType> consumer) {
+    staticRetarget.keySet().forEach(m -> consumer.accept(m.getHolderType()));
+    nonEmulatedVirtualRetarget.keySet().forEach(m -> consumer.accept(m.getHolderType()));
+    emulatedVirtualRetarget.keySet().forEach(m -> consumer.accept(m.getHolderType()));
+  }
+
   public Map<DexType, EmulatedInterfaceDescriptor> getEmulatedInterfaces() {
     return emulatedInterfaces;
   }
@@ -110,10 +126,41 @@
     return dontRetarget;
   }
 
-  public Map<DexType, Pair<DexType, DexString>> getCustomConversions() {
+  public boolean isCustomConversionRewrittenType(DexType type) {
+    return Iterables.any(
+        customConversions.values(),
+        descriptor ->
+            descriptor.getFrom().getHolderType() == type
+                || descriptor.getTo().getHolderType() == type);
+  }
+
+  public Map<DexType, CustomConversionDescriptor> getCustomConversions() {
     return customConversions;
   }
 
+  public boolean hasRetargeting() {
+    return !staticRetarget.isEmpty()
+        || !nonEmulatedVirtualRetarget.isEmpty()
+        || !emulatedVirtualRetarget.isEmpty();
+  }
+
+  public boolean isEmulatedInterfaceRewrittenType(DexType type) {
+    return Iterables.any(
+        emulatedInterfaces.values(), descriptor -> descriptor.getRewrittenType() == type);
+  }
+
+  public boolean hasEmulatedInterfaces() {
+    return !emulatedInterfaces.isEmpty();
+  }
+
+  EmulatedDispatchMethodDescriptor getEmulatedInterfaceEmulatedDispatchMethodDescriptor(
+      DexMethod method) {
+    if (!emulatedInterfaces.containsKey(method.getHolderType())) {
+      return null;
+    }
+    return emulatedInterfaces.get(method.getHolderType()).getEmulatedMethods().get(method);
+  }
+
   public static class Builder {
 
     Builder() {}
@@ -126,17 +173,21 @@
         ImmutableMap.builder();
     private final ImmutableMap.Builder<DexMethod, EmulatedDispatchMethodDescriptor>
         emulatedVirtualRetarget = ImmutableMap.builder();
+    private final ImmutableMap.Builder<DexMethod, DexMethod>
+        emulatedVirtualRetargetThroughEmulatedInterface = ImmutableMap.builder();
     private final ImmutableMap.Builder<DexType, EmulatedInterfaceDescriptor> emulatedInterfaces =
         ImmutableMap.builder();
     private final ImmutableMap.Builder<DexType, List<DexMethod>> wrappers = ImmutableMap.builder();
     private final ImmutableMap.Builder<DexType, DexType> legacyBackport = ImmutableMap.builder();
     private final ImmutableSet.Builder<DexType> dontRetarget = ImmutableSet.builder();
-    private final ImmutableMap.Builder<DexType, Pair<DexType, DexString>> customConversions =
+    private final ImmutableMap.Builder<DexType, CustomConversionDescriptor> customConversions =
         ImmutableMap.builder();
 
     public void rewriteType(DexType src, DexType target) {
       assert src != null;
       assert target != null;
+      assert src != target;
+      assert !rewriteType.containsKey(src) || rewriteType.get(src) == target;
       rewriteType.put(src, target);
     }
 
@@ -160,6 +211,10 @@
       emulatedVirtualRetarget.put(src, dest);
     }
 
+    public void putEmulatedVirtualRetargetThroughEmulatedInterface(DexMethod src, DexMethod dest) {
+      emulatedVirtualRetargetThroughEmulatedInterface.put(src, dest);
+    }
+
     public void addWrapper(DexType wrapperConversion, List<DexMethod> methods) {
       wrappers.put(wrapperConversion, ImmutableList.copyOf(methods));
     }
@@ -172,8 +227,12 @@
       dontRetarget.add(type);
     }
 
-    public void putCustomConversion(DexType src, DexType conversionType, DexString conversionName) {
-      customConversions.put(src, new Pair<>(conversionType, conversionName));
+    public void putCustomConversion(DexType src, CustomConversionDescriptor descriptor) {
+      customConversions.put(src, descriptor);
+    }
+
+    public DexType getRewrittenType(DexType type) {
+      return rewriteType.get(type);
     }
 
     public MachineRewritingFlags build() {
@@ -183,6 +242,7 @@
           staticRetarget.build(),
           nonEmulatedVirtualRetarget.build(),
           emulatedVirtualRetarget.build(),
+          emulatedVirtualRetargetThroughEmulatedInterface.build(),
           emulatedInterfaces.build(),
           wrappers.build(),
           legacyBackport.build(),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java
index f426219..0c6a88a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification;
 
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
 import java.util.List;
 
 public class MachineTopLevelFlags {
@@ -23,6 +24,11 @@
   private final boolean supportAllCallbacksFromLibrary;
   private final List<String> extraKeepRules;
 
+  public static MachineTopLevelFlags empty() {
+    return new MachineTopLevelFlags(
+        AndroidApiLevel.B, "unused", null, null, false, ImmutableList.of());
+  }
+
   public MachineTopLevelFlags(
       AndroidApiLevel requiredCompilationAPILevel,
       String synthesizedLibraryClassesPackagePrefix,
@@ -54,7 +60,7 @@
     return jsonSource;
   }
 
-  public boolean isSupportAllCallbacksFromLibrary() {
+  public boolean supportAllCallbacksFromLibrary() {
     return supportAllCallbacksFromLibrary;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
index a54cd8d..73dd908 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.MethodResolutionResult;
@@ -23,6 +22,7 @@
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterInstructionEventConsumer;
 import java.util.Collection;
 import java.util.Collections;
@@ -36,7 +36,6 @@
   private final AppView<?> appView;
   private final DesugaredLibraryRetargeterSyntheticHelper syntheticHelper;
 
-  private final RetargetingInfo retargetingInfo;
   private final Map<DexMethod, DexMethod> staticRetarget;
   private final Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget;
   private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget;
@@ -44,10 +43,11 @@
   public DesugaredLibraryRetargeter(AppView<?> appView) {
     this.appView = appView;
     this.syntheticHelper = new DesugaredLibraryRetargeterSyntheticHelper(appView);
-    retargetingInfo = RetargetingInfo.get(appView);
-    staticRetarget = retargetingInfo.getStaticRetarget();
-    nonEmulatedVirtualRetarget = retargetingInfo.getNonEmulatedVirtualRetarget();
-    emulatedVirtualRetarget = retargetingInfo.getEmulatedVirtualRetarget();
+    MachineDesugaredLibrarySpecification specification =
+        appView.options().machineDesugaredLibrarySpecification;
+    staticRetarget = specification.getStaticRetarget();
+    nonEmulatedVirtualRetarget = specification.getNonEmulatedVirtualRetarget();
+    emulatedVirtualRetarget = specification.getEmulatedVirtualRetarget();
   }
 
   // Used by the ListOfBackportedMethods utility.
@@ -57,10 +57,6 @@
     emulatedVirtualRetarget.keySet().forEach(consumer);
   }
 
-  public RetargetingInfo getRetargetingInfo() {
-    return retargetingInfo;
-  }
-
   @Override
   public Collection<CfInstruction> desugarInstruction(
       CfInstruction instruction,
@@ -87,6 +83,18 @@
     return computeNewInvokeTarget(instruction, context).hasNewInvokeTarget();
   }
 
+  InvokeRetargetingResult ensureInvokeRetargetingResult(DexMethod retarget) {
+    if (retarget == null) {
+      return NO_REWRITING;
+    }
+    return new InvokeRetargetingResult(
+        true,
+        eventConsumer -> {
+          syntheticHelper.ensureRetargetMethod(retarget, eventConsumer);
+          return retarget;
+        });
+  }
+
   static class InvokeRetargetingResult {
 
     static InvokeRetargetingResult NO_REWRITING =
@@ -96,13 +104,6 @@
     private final Function<DesugaredLibraryRetargeterInstructionEventConsumer, DexMethod>
         newInvokeTargetSupplier;
 
-    static InvokeRetargetingResult createInvokeRetargetingResult(DexMethod retarget) {
-      if (retarget == null) {
-        return NO_REWRITING;
-      }
-      return new InvokeRetargetingResult(true, ignored -> retarget);
-    }
-
     private InvokeRetargetingResult(
         boolean hasNewInvokeTarget,
         Function<DesugaredLibraryRetargeterInstructionEventConsumer, DexMethod>
@@ -129,8 +130,8 @@
     }
     if (appView
         .options()
-        .desugaredLibrarySpecification
-        .getDontRetargetLibMember()
+        .machineDesugaredLibrarySpecification
+        .getDontRetarget()
         .contains(context.getContextType())) {
       return NO_REWRITING;
     }
@@ -148,44 +149,33 @@
     assert singleTarget != null;
     if (cfInvoke.isInvokeStatic()) {
       DexMethod retarget = staticRetarget.get(singleTarget);
-      return retarget == null
-          ? NO_REWRITING
-          : InvokeRetargetingResult.createInvokeRetargetingResult(retarget);
+      return retarget == null ? NO_REWRITING : ensureInvokeRetargetingResult(retarget);
     }
-    InvokeRetargetingResult retarget = computeNonStaticRetarget(singleTarget);
+    InvokeRetargetingResult retarget = computeNonStaticRetarget(singleTarget, false);
     if (!retarget.hasNewInvokeTarget()) {
       return NO_REWRITING;
     }
     if (cfInvoke.isInvokeSuper(context.getHolderType())) {
       DexClassAndMethod superTarget = appInfo.lookupSuperTarget(invokedMethod, context);
       if (superTarget != null) {
-        return computeSuperRetarget(superTarget.getDefinition());
+        assert !superTarget.getDefinition().isStatic();
+        return computeNonStaticRetarget(superTarget.getReference(), true);
       }
     }
     return retarget;
   }
 
-  private InvokeRetargetingResult computeNonStaticRetarget(DexMethod singleTarget) {
+  private InvokeRetargetingResult computeNonStaticRetarget(
+      DexMethod singleTarget, boolean superInvoke) {
     EmulatedDispatchMethodDescriptor descriptor = emulatedVirtualRetarget.get(singleTarget);
     if (descriptor != null) {
       return new InvokeRetargetingResult(
           true,
           eventConsumer ->
-              syntheticHelper.ensureEmulatedHolderDispatchMethod(descriptor, eventConsumer));
+              superInvoke
+                  ? syntheticHelper.ensureForwardingMethod(descriptor, eventConsumer)
+                  : syntheticHelper.ensureEmulatedHolderDispatchMethod(descriptor, eventConsumer));
     }
-    return InvokeRetargetingResult.createInvokeRetargetingResult(
-        nonEmulatedVirtualRetarget.get(singleTarget));
-  }
-
-  private InvokeRetargetingResult computeSuperRetarget(DexEncodedMethod singleTarget) {
-    assert !singleTarget.isStatic();
-    DexMethod reference = singleTarget.getReference();
-    EmulatedDispatchMethodDescriptor descriptor = emulatedVirtualRetarget.get(reference);
-    if (descriptor != null) {
-      return InvokeRetargetingResult.createInvokeRetargetingResult(
-          syntheticHelper.ensureForwardingMethod(descriptor));
-    }
-    return InvokeRetargetingResult.createInvokeRetargetingResult(
-        nonEmulatedVirtualRetarget.get(reference));
+    return ensureInvokeRetargetingResult(nonEmulatedVirtualRetarget.get(singleTarget));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java
index 11fcfb7..60086f8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java
@@ -14,30 +14,30 @@
 
   private final AppView<?> appView;
   private final DesugaredLibraryRetargeterSyntheticHelper syntheticHelper;
-  private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedDispatchMethods;
 
-  public static DesugaredLibraryRetargeterL8Synthesizer create(
-      AppView<?> appView, RetargetingInfo retargetingInfo) {
+  public static DesugaredLibraryRetargeterL8Synthesizer create(AppView<?> appView) {
     assert appView.options().isDesugaredLibraryCompilation();
-    if (retargetingInfo == null || retargetingInfo.getEmulatedVirtualRetarget().isEmpty()) {
-      assert appView.options().desugaredLibrarySpecification.getRetargetCoreLibMember().isEmpty();
+    if (appView
+        .options()
+        .machineDesugaredLibrarySpecification
+        .getEmulatedVirtualRetarget()
+        .isEmpty()) {
       return null;
     }
-    return new DesugaredLibraryRetargeterL8Synthesizer(appView, retargetingInfo);
+    return new DesugaredLibraryRetargeterL8Synthesizer(appView);
   }
 
-  public DesugaredLibraryRetargeterL8Synthesizer(
-      AppView<?> appView, RetargetingInfo retargetingInfo) {
+  public DesugaredLibraryRetargeterL8Synthesizer(AppView<?> appView) {
     this.appView = appView;
     this.syntheticHelper = new DesugaredLibraryRetargeterSyntheticHelper(appView);
-    emulatedDispatchMethods = retargetingInfo.getEmulatedVirtualRetarget();
   }
 
   @Override
   public void synthesizeClasses(CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
-    assert !emulatedDispatchMethods.isEmpty();
+    Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget =
+        appView.options().machineDesugaredLibrarySpecification.getEmulatedVirtualRetarget();
     for (EmulatedDispatchMethodDescriptor emulatedDispatchMethod :
-        emulatedDispatchMethods.values()) {
+        emulatedVirtualRetarget.values()) {
       syntheticHelper.ensureProgramEmulatedHolderDispatchMethod(
           emulatedDispatchMethod, eventConsumer);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterLibraryTypeSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterLibraryTypeSynthesizer.java
deleted file mode 100644
index bbaa4cf..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterLibraryTypeSynthesizer.java
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright (c) 2021, 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.ir.desugar.desugaredlibrary.retargeter;
-
-import com.android.tools.r8.ProgramResource.Kind;
-import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.ClassAccessFlags;
-import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexLibraryClass;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.graph.DirectMappedDexApplication;
-import com.android.tools.r8.graph.EnclosingMethodAttribute;
-import com.android.tools.r8.graph.GenericSignature.ClassSignature;
-import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.NestHostClassAttribute;
-import com.android.tools.r8.graph.NestMemberClassAttribute;
-import com.android.tools.r8.origin.SynthesizedOrigin;
-import com.android.tools.r8.utils.StringDiagnostic;
-import java.util.Comparator;
-import java.util.IdentityHashMap;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.TreeSet;
-
-public class DesugaredLibraryRetargeterLibraryTypeSynthesizer {
-
-  public static void checkForAssumedLibraryTypes(AppView<?> appView) {
-    Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
-        appView.options().desugaredLibrarySpecification.getRetargetCoreLibMember();
-    for (DexString methodName : retargetCoreLibMember.keySet()) {
-      for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
-        DexClass typeClass = appView.definitionFor(inType);
-        if (typeClass == null) {
-          warnMissingRetargetCoreLibraryMember(inType, appView);
-        }
-      }
-    }
-  }
-
-  public static void amendLibraryWithRetargetedMembers(AppView<AppInfoWithClassHierarchy> appView) {
-    Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
-        appView.options().desugaredLibrarySpecification.getRetargetCoreLibMember();
-    Map<DexType, DexLibraryClass> synthesizedLibraryClasses =
-        synthesizeLibraryClassesForRetargetedMembers(appView, retargetCoreLibMember);
-    Map<DexLibraryClass, Set<DexEncodedMethod>> synthesizedLibraryMethods =
-        synthesizedMembersForRetargetClasses(
-            appView, retargetCoreLibMember, synthesizedLibraryClasses);
-    synthesizedLibraryMethods.forEach(DexLibraryClass::addDirectMethods);
-    DirectMappedDexApplication newApplication =
-        appView
-            .appInfo()
-            .app()
-            .asDirect()
-            .builder()
-            .addLibraryClasses(synthesizedLibraryClasses.values())
-            .build();
-    appView.setAppInfo(appView.appInfo().rebuildWithClassHierarchy(app -> newApplication));
-  }
-
-  private static Map<DexType, DexLibraryClass> synthesizeLibraryClassesForRetargetedMembers(
-      AppView<AppInfoWithClassHierarchy> appView,
-      Map<DexString, Map<DexType, DexType>> retargetCoreLibMember) {
-    DexItemFactory dexItemFactory = appView.dexItemFactory();
-    Map<DexType, DexLibraryClass> synthesizedLibraryClasses = new LinkedHashMap<>();
-    for (Map<DexType, DexType> oldToNewTypeMap : retargetCoreLibMember.values()) {
-      for (DexType newType : oldToNewTypeMap.values()) {
-        if (appView.definitionFor(newType) == null) {
-          synthesizedLibraryClasses.computeIfAbsent(
-              newType,
-              type ->
-                  // Synthesize a library class with the given name. Note that this is assuming that
-                  // the library class inherits directly from java.lang.Object, does not implement
-                  // any interfaces, etc.
-                  new DexLibraryClass(
-                      type,
-                      Kind.CF,
-                      new SynthesizedOrigin(
-                          "Desugared library retargeter", DesugaredLibraryRetargeter.class),
-                      ClassAccessFlags.fromCfAccessFlags(Constants.ACC_PUBLIC),
-                      dexItemFactory.objectType,
-                      DexTypeList.empty(),
-                      dexItemFactory.createString("DesugaredLibraryRetargeter"),
-                      NestHostClassAttribute.none(),
-                      NestMemberClassAttribute.emptyList(),
-                      EnclosingMethodAttribute.none(),
-                      InnerClassAttribute.emptyList(),
-                      ClassSignature.noSignature(),
-                      DexAnnotationSet.empty(),
-                      DexEncodedField.EMPTY_ARRAY,
-                      DexEncodedField.EMPTY_ARRAY,
-                      DexEncodedMethod.EMPTY_ARRAY,
-                      DexEncodedMethod.EMPTY_ARRAY,
-                      dexItemFactory.getSkipNameValidationForTesting()));
-        }
-      }
-    }
-    return synthesizedLibraryClasses;
-  }
-
-  private static Map<DexLibraryClass, Set<DexEncodedMethod>> synthesizedMembersForRetargetClasses(
-      AppView<AppInfoWithClassHierarchy> appView,
-      Map<DexString, Map<DexType, DexType>> retargetCoreLibMember,
-      Map<DexType, DexLibraryClass> synthesizedLibraryClasses) {
-    DexItemFactory dexItemFactory = appView.dexItemFactory();
-    Map<DexLibraryClass, Set<DexEncodedMethod>> synthesizedMembers = new IdentityHashMap<>();
-    for (Entry<DexString, Map<DexType, DexType>> entry : retargetCoreLibMember.entrySet()) {
-      DexString methodName = entry.getKey();
-      Map<DexType, DexType> types = entry.getValue();
-      types.forEach(
-          (oldType, newType) -> {
-            DexClass oldClass = appView.definitionFor(oldType);
-            DexLibraryClass newClass = synthesizedLibraryClasses.get(newType);
-            if (oldClass == null || newClass == null) {
-              return;
-            }
-            for (DexEncodedMethod method :
-                oldClass.methods(method -> method.getName() == methodName)) {
-              DexMethod retargetMethod = method.getReference().withHolder(newType, dexItemFactory);
-              if (!method.isStatic()) {
-                retargetMethod = retargetMethod.withExtraArgumentPrepended(oldType, dexItemFactory);
-              }
-              synthesizedMembers
-                  .computeIfAbsent(
-                      newClass,
-                      ignore -> new TreeSet<>(Comparator.comparing(DexEncodedMethod::getReference)))
-                  .add(
-                      DexEncodedMethod.syntheticBuilder()
-                          .setMethod(retargetMethod)
-                          .setAccessFlags(
-                              MethodAccessFlags.fromCfAccessFlags(
-                                  Constants.ACC_PUBLIC | Constants.ACC_STATIC, false))
-                          .setCode(null)
-                          .setApiLevelForDefinition(method.getApiLevelForDefinition())
-                          .build());
-            }
-          });
-    }
-    return synthesizedMembers;
-  }
-
-  private static void warnMissingRetargetCoreLibraryMember(DexType type, AppView<?> appView) {
-    StringDiagnostic warning =
-        new StringDiagnostic(
-            "Cannot retarget core library member "
-                + type.getName()
-                + " because the class is missing.");
-    appView.options().reporter.warning(warning);
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
index 414c32d..e1447fb 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
@@ -36,11 +36,11 @@
   private final DesugaredLibraryRetargeterSyntheticHelper syntheticHelper;
   private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedDispatchMethods;
 
-  public DesugaredLibraryRetargeterPostProcessor(
-      AppView<?> appView, RetargetingInfo retargetingInfo) {
+  public DesugaredLibraryRetargeterPostProcessor(AppView<?> appView) {
     this.appView = appView;
     this.syntheticHelper = new DesugaredLibraryRetargeterSyntheticHelper(appView);
-    emulatedDispatchMethods = retargetingInfo.getEmulatedVirtualRetarget();
+    emulatedDispatchMethods =
+        appView.options().machineDesugaredLibrarySpecification.getEmulatedVirtualRetarget();
   }
 
   @Override
@@ -127,8 +127,8 @@
       }
       if (appView
           .options()
-          .desugaredLibrarySpecification
-          .getDontRetargetLibMember()
+          .machineDesugaredLibrarySpecification
+          .getDontRetarget()
           .contains(clazz.getType())) {
         continue;
       }
@@ -136,7 +136,7 @@
           Collections.singletonList(new ClassTypeSignature(newInterface.type)));
       eventConsumer.acceptInterfaceInjection(clazz, newInterface);
       DexMethod itfMethod =
-          syntheticHelper.getEmulatedInterfaceDispatchMethod(newInterface, descriptor);
+          syntheticHelper.emulatedInterfaceDispatchMethod(newInterface, descriptor);
       if (clazz.lookupVirtualMethod(method) == null) {
         DexEncodedMethod newMethod = createForwardingMethod(itfMethod, descriptor, clazz);
         clazz.addVirtualMethod(newMethod);
@@ -150,7 +150,7 @@
     // NOTE: Never add a forwarding method to methods of classes unknown or coming from android.jar
     // even if this results in invalid code, these classes are never desugared.
     // In desugared library, emulated interface methods can be overridden by retarget lib members.
-    DexMethod forwardMethod = syntheticHelper.ensureForwardingMethod(descriptor);
+    DexMethod forwardMethod = syntheticHelper.forwardingMethod(descriptor);
     assert forwardMethod != null && forwardMethod != target;
     DexEncodedMethod resolvedMethod =
         appView.appInfoForDesugaring().resolveMethod(target, true).getResolvedMethod();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java
index 5519d95..6102e1a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.ClasspathMethod;
 import com.android.tools.r8.graph.ClasspathOrLibraryClass;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexMethod;
@@ -29,12 +30,46 @@
     this.appView = appView;
   }
 
-  public DexMethod ensureForwardingMethod(EmulatedDispatchMethodDescriptor descriptor) {
-    // TODO(b/184026720): We may synthesize a stub on the classpath if absent.
+  public DexMethod ensureRetargetMethod(
+      DexMethod retarget, DesugaredLibraryRetargeterInstructionEventConsumer eventConsumer) {
+    DexClass holderClass = appView.definitionFor(retarget.getHolderType());
+    if (holderClass != null && !holderClass.isClasspathClass()) {
+      // The holder class is a library class in orthodox set-ups where the L8 compilation
+      // is done in multiple steps, this is only partially supported (most notably for tests).
+      assert holderClass.lookupMethod(retarget) != null;
+      return retarget;
+    }
+    assert eventConsumer != null;
+    ClasspathMethod ensuredMethod =
+        appView
+            .getSyntheticItems()
+            .ensureFixedClasspathMethodFromType(
+                retarget.getName(),
+                retarget.getProto(),
+                SyntheticKind.RETARGET_STUB,
+                retarget.getHolderType(),
+                appView,
+                ignored -> {},
+                eventConsumer::acceptDesugaredLibraryRetargeterDispatchClasspathClass,
+                methodBuilder ->
+                    methodBuilder
+                        .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+                        .setCode(null));
+    assert ensuredMethod.getReference() == retarget;
+    return retarget;
+  }
+
+  DexMethod forwardingMethod(EmulatedDispatchMethodDescriptor descriptor) {
     assert descriptor.getForwardingMethod().getHolderKind() == null;
     return descriptor.getForwardingMethod().getMethod();
   }
 
+  public DexMethod ensureForwardingMethod(
+      EmulatedDispatchMethodDescriptor descriptor,
+      DesugaredLibraryRetargeterInstructionEventConsumer eventConsumer) {
+    return ensureRetargetMethod(forwardingMethod(descriptor), eventConsumer);
+  }
+
   private DexMethod emulatedHolderDispatchMethod(DexType holder, DerivedMethod method) {
     assert method.getHolderKind() == SyntheticKind.RETARGET_CLASS;
     DexProto newProto = appView.dexItemFactory().prependHolderToProto(method.getMethod());
@@ -46,7 +81,7 @@
     return appView.dexItemFactory().createMethod(holder, method.getProto(), method.getName());
   }
 
-  public DexMethod getEmulatedInterfaceDispatchMethod(
+  public DexMethod emulatedInterfaceDispatchMethod(
       DexClass newInterface, EmulatedDispatchMethodDescriptor descriptor) {
     DexMethod method =
         emulatedInterfaceDispatchMethod(newInterface.type, descriptor.getInterfaceMethod());
@@ -83,7 +118,8 @@
                   SyntheticKind.RETARGET_CLASS,
                   context,
                   appView,
-                  classBuilder -> buildHolderDispatchMethod(classBuilder, itfClass, descriptor),
+                  classBuilder ->
+                      buildHolderDispatchMethod(classBuilder, itfClass, descriptor, eventConsumer),
                   eventConsumer::acceptDesugaredLibraryRetargeterDispatchClasspathClass);
     }
     DexMethod dispatchMethod =
@@ -107,7 +143,7 @@
             emulatedDispatchMethod.getHolderKind(),
             holderContext,
             appView,
-            classBuilder -> buildHolderDispatchMethod(classBuilder, itfClass, descriptor),
+            classBuilder -> buildHolderDispatchMethod(classBuilder, itfClass, descriptor, null),
             eventConsumer::acceptDesugaredLibraryRetargeterDispatchProgramClass);
   }
 
@@ -174,7 +210,10 @@
   }
 
   private <SCB extends SyntheticClassBuilder<?, ?>> void buildHolderDispatchMethod(
-      SCB classBuilder, DexClass itfClass, EmulatedDispatchMethodDescriptor descriptor) {
+      SCB classBuilder,
+      DexClass itfClass,
+      EmulatedDispatchMethodDescriptor descriptor,
+      DesugaredLibraryRetargeterInstructionEventConsumer eventConsumer) {
     classBuilder.addMethod(
         methodBuilder -> {
           DexMethod dispatchMethod =
@@ -189,15 +228,19 @@
               .setCode(
                   methodSig ->
                       appView.options().isDesugaredLibraryCompilation()
-                          ? generateEmulatedDispatchCfCode(descriptor, itfClass, methodSig)
+                          ? generateEmulatedDispatchCfCode(
+                              descriptor, itfClass, methodSig, eventConsumer)
                           : null);
         });
   }
 
   private CfCode generateEmulatedDispatchCfCode(
-      EmulatedDispatchMethodDescriptor descriptor, DexClass itfClass, DexMethod methodSig) {
-    DexMethod forwardingMethod = ensureForwardingMethod(descriptor);
-    DexMethod itfMethod = getEmulatedInterfaceDispatchMethod(itfClass, descriptor);
+      EmulatedDispatchMethodDescriptor descriptor,
+      DexClass itfClass,
+      DexMethod methodSig,
+      DesugaredLibraryRetargeterInstructionEventConsumer eventConsumer) {
+    DexMethod forwardingMethod = ensureForwardingMethod(descriptor, eventConsumer);
+    DexMethod itfMethod = emulatedInterfaceDispatchMethod(itfClass, descriptor);
     assert descriptor.getDispatchCases().isEmpty();
     return new EmulateDispatchSyntheticCfCodeProvider(
             methodSig.getHolderType(), forwardingMethod, itfMethod, new LinkedHashMap<>(), appView)
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java
deleted file mode 100644
index 9e1f5e3..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java
+++ /dev/null
@@ -1,213 +0,0 @@
-// Copyright (c) 2021, 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.ir.desugar.desugaredlibrary.retargeter;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
-import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-import com.android.tools.r8.utils.WorkList;
-import com.google.common.collect.ImmutableMap;
-import java.util.ArrayList;
-import java.util.IdentityHashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-public class RetargetingInfo {
-
-  private final Map<DexMethod, DexMethod> staticRetarget;
-  private final Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget;
-  private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget;
-
-  RetargetingInfo(
-      Map<DexMethod, DexMethod> staticRetarget,
-      Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget,
-      Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget) {
-    this.staticRetarget = staticRetarget;
-    this.nonEmulatedVirtualRetarget = nonEmulatedVirtualRetarget;
-    this.emulatedVirtualRetarget = emulatedVirtualRetarget;
-  }
-
-  public static RetargetingInfo get(AppView<?> appView) {
-    if (appView.options().testing.machineDesugaredLibrarySpecification != null) {
-      MachineRewritingFlags rewritingFlags =
-          appView.options().testing.machineDesugaredLibrarySpecification.getRewritingFlags();
-      return new RetargetingInfo(
-          rewritingFlags.getStaticRetarget(),
-          rewritingFlags.getNonEmulatedVirtualRetarget(),
-          rewritingFlags.getEmulatedVirtualRetarget());
-    }
-    return new RetargetingInfoBuilder(appView).computeRetargetingInfo();
-  }
-
-  public Map<DexMethod, DexMethod> getStaticRetarget() {
-    return staticRetarget;
-  }
-
-  public Map<DexMethod, DexMethod> getNonEmulatedVirtualRetarget() {
-    return nonEmulatedVirtualRetarget;
-  }
-
-  public Map<DexMethod, EmulatedDispatchMethodDescriptor> getEmulatedVirtualRetarget() {
-    return emulatedVirtualRetarget;
-  }
-
-  private static class RetargetingInfoBuilder {
-
-    private final AppView<?> appView;
-    private final Map<DexMethod, DexMethod> staticRetarget = new IdentityHashMap<>();
-    private final Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget = new IdentityHashMap<>();
-    private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget =
-        new IdentityHashMap<>();
-
-    public RetargetingInfoBuilder(AppView<?> appView) {
-      this.appView = appView;
-    }
-
-    private RetargetingInfo computeRetargetingInfo() {
-      LegacyDesugaredLibrarySpecification desugaredLibrarySpecification =
-          appView.options().desugaredLibrarySpecification;
-      Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
-          desugaredLibrarySpecification.getRetargetCoreLibMember();
-      if (retargetCoreLibMember.isEmpty()) {
-        return new RetargetingInfo(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of());
-      }
-      for (DexString methodName : retargetCoreLibMember.keySet()) {
-        for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
-          DexClass typeClass = appView.definitionFor(inType);
-          if (typeClass != null) {
-            DexType newHolder = retargetCoreLibMember.get(methodName).get(inType);
-            List<DexClassAndMethod> found = findMethodsWithName(methodName, typeClass);
-            for (DexClassAndMethod method : found) {
-              DexMethod methodReference = method.getReference();
-              if (method.getAccessFlags().isStatic()) {
-                staticRetarget.put(
-                    methodReference,
-                    computeRetargetMethod(
-                        methodReference, method.getAccessFlags().isStatic(), newHolder));
-                continue;
-              }
-              if (isEmulatedInterfaceDispatch(method)) {
-                continue;
-              }
-              if (typeClass.isFinal() || method.getAccessFlags().isFinal()) {
-                nonEmulatedVirtualRetarget.put(
-                    methodReference,
-                    computeRetargetMethod(
-                        methodReference, method.getAccessFlags().isStatic(), newHolder));
-              } else {
-                // Virtual rewrites require emulated dispatch for inheritance.
-                // The call is rewritten to the dispatch holder class instead.
-                DexProto newProto = appView.dexItemFactory().prependHolderToProto(methodReference);
-                DexMethod forwardingDexMethod =
-                    appView.dexItemFactory().createMethod(newHolder, newProto, methodName);
-                DerivedMethod forwardingMethod = new DerivedMethod(forwardingDexMethod);
-                DerivedMethod interfaceMethod =
-                    new DerivedMethod(methodReference, SyntheticKind.RETARGET_INTERFACE);
-                DerivedMethod dispatchMethod =
-                    new DerivedMethod(methodReference, SyntheticKind.RETARGET_CLASS);
-                emulatedVirtualRetarget.put(
-                    methodReference,
-                    new EmulatedDispatchMethodDescriptor(
-                        interfaceMethod, dispatchMethod, forwardingMethod, new LinkedHashMap<>()));
-              }
-            }
-          }
-        }
-      }
-      if (desugaredLibrarySpecification.isLibraryCompilation()) {
-        // TODO(b/177977763): This is only a workaround rewriting invokes of j.u.Arrays.deepEquals0
-        // to j.u.DesugarArrays.deepEquals0.
-        DexItemFactory itemFactory = appView.options().dexItemFactory();
-        DexString name = itemFactory.createString("deepEquals0");
-        DexProto proto =
-            itemFactory.createProto(
-                itemFactory.booleanType, itemFactory.objectType, itemFactory.objectType);
-        DexMethod source =
-            itemFactory.createMethod(
-                itemFactory.createType(itemFactory.arraysDescriptor), proto, name);
-        DexMethod target =
-            computeRetargetMethod(
-                source, true, itemFactory.createType("Ljava/util/DesugarArrays;"));
-        staticRetarget.put(source, target);
-        // TODO(b/181629049): This is only a workaround rewriting invokes of
-        //  j.u.TimeZone.getTimeZone taking a java.time.ZoneId.
-        name = itemFactory.createString("getTimeZone");
-        proto =
-            itemFactory.createProto(
-                itemFactory.createType("Ljava/util/TimeZone;"),
-                itemFactory.createType("Ljava/time/ZoneId;"));
-        source =
-            itemFactory.createMethod(itemFactory.createType("Ljava/util/TimeZone;"), proto, name);
-        target =
-            computeRetargetMethod(
-                source, true, itemFactory.createType("Ljava/util/DesugarTimeZone;"));
-        staticRetarget.put(source, target);
-      }
-      return new RetargetingInfo(
-          ImmutableMap.copyOf(staticRetarget),
-          ImmutableMap.copyOf(nonEmulatedVirtualRetarget),
-          emulatedVirtualRetarget);
-    }
-
-    DexMethod computeRetargetMethod(DexMethod method, boolean isStatic, DexType newHolder) {
-      DexItemFactory factory = appView.dexItemFactory();
-      DexProto newProto = isStatic ? method.getProto() : factory.prependHolderToProto(method);
-      return factory.createMethod(newHolder, newProto, method.getName());
-    }
-
-    private boolean isEmulatedInterfaceDispatch(DexClassAndMethod method) {
-      // Answers true if this method is already managed through emulated interface dispatch.
-      Map<DexType, DexType> emulateLibraryInterface =
-          appView.options().desugaredLibrarySpecification.getEmulateLibraryInterface();
-      if (emulateLibraryInterface.isEmpty()) {
-        return false;
-      }
-      DexMethod methodToFind = method.getReference();
-      // Look-up all superclass and interfaces, if an emulated interface is found, and it implements
-      // the method, answers true.
-      WorkList<DexClass> worklist = WorkList.newIdentityWorkList(method.getHolder());
-      while (worklist.hasNext()) {
-        DexClass clazz = worklist.next();
-        if (clazz.isInterface()
-            && emulateLibraryInterface.containsKey(clazz.getType())
-            && clazz.lookupMethod(methodToFind) != null) {
-          return true;
-        }
-        // All super types are library class, or we are doing L8 compilation.
-        clazz.forEachImmediateSupertype(
-            superType -> {
-              DexClass superClass = appView.definitionFor(superType);
-              if (superClass != null) {
-                worklist.addIfNotSeen(superClass);
-              }
-            });
-      }
-      return false;
-    }
-
-    private List<DexClassAndMethod> findMethodsWithName(DexString methodName, DexClass clazz) {
-      List<DexClassAndMethod> found = new ArrayList<>();
-      clazz.forEachClassMethodMatching(
-          definition -> definition.getName() == methodName, found::add);
-      assert !found.isEmpty()
-          : "Should have found a method (library specifications) for "
-              + clazz.toSourceString()
-              + "."
-              + methodName
-              + ". Maybe the library used for the compilation should be newer.";
-      return found;
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java
index 2411ba0..4be8ef0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java
@@ -8,8 +8,8 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
@@ -26,11 +26,13 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.BiConsumer;
 
 public class HumanToMachineEmulatedInterfaceConverter {
 
   private final AppInfoWithClassHierarchy appInfo;
-  private Map<DexType, List<DexType>> emulatedInterfaceHierarchy;
+  private final Map<DexType, List<DexType>> emulatedInterfaceHierarchy = new IdentityHashMap<>();
+  private final Set<DexType> missingEmulatedInterface = Sets.newIdentityHashSet();
 
   public HumanToMachineEmulatedInterfaceConverter(AppInfoWithClassHierarchy appInfo) {
     this.appInfo = appInfo;
@@ -39,15 +41,19 @@
   public void convertEmulatedInterfaces(
       HumanRewritingFlags rewritingFlags,
       AppInfoWithClassHierarchy appInfo,
-      MachineRewritingFlags.Builder builder) {
+      MachineRewritingFlags.Builder builder,
+      BiConsumer<String, Set<? extends DexReference>> warnConsumer) {
     Map<DexType, DexType> emulateInterfaces = rewritingFlags.getEmulateLibraryInterface();
     Set<DexMethod> dontRewriteInvocation = rewritingFlags.getDontRewriteInvocation();
-    emulatedInterfaceHierarchy = processEmulatedInterfaceHierarchy(appInfo, emulateInterfaces);
+    processEmulatedInterfaceHierarchy(appInfo, emulateInterfaces);
     for (DexType itf : emulateInterfaces.keySet()) {
-      DexProgramClass itfClass = appInfo.contextIndependentDefinitionFor(itf).asProgramClass();
-      assert itfClass != null;
+      DexClass itfClass = appInfo.contextIndependentDefinitionFor(itf);
+      if (itfClass == null) {
+        missingEmulatedInterface.add(itf);
+        continue;
+      }
       Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedMethods = new IdentityHashMap<>();
-      itfClass.forEachProgramVirtualMethodMatching(
+      itfClass.forEachClassMethodMatching(
           m -> m.isDefaultMethod() && !dontRewriteInvocation.contains(m.getReference()),
           method ->
               emulatedMethods.put(
@@ -57,6 +63,7 @@
       builder.putEmulatedInterface(
           itf, new EmulatedInterfaceDescriptor(emulateInterfaces.get(itf), emulatedMethods));
     }
+    warnConsumer.accept("Missing emulated interfaces: ", missingEmulatedInterface);
   }
 
   private EmulatedDispatchMethodDescriptor computeEmulatedDispatchDescriptor(
@@ -104,7 +111,6 @@
       for (int i = subInterfaces.size() - 1; i >= 0; i--) {
         DexClass subInterfaceClass = appInfo.definitionFor(subInterfaces.get(i));
         assert subInterfaceClass != null;
-        assert subInterfaceClass.isProgramClass();
         // Else computation of subInterface would have failed.
         // if the method is implemented, extra dispatch is required.
         DexEncodedMethod result = subInterfaceClass.lookupVirtualMethod(method);
@@ -137,29 +143,25 @@
     return false;
   }
 
-  private Map<DexType, List<DexType>> processEmulatedInterfaceHierarchy(
+  private void processEmulatedInterfaceHierarchy(
       AppInfoWithClassHierarchy appInfo, Map<DexType, DexType> emulateInterfaces) {
-    Map<DexType, List<DexType>> emulatedInterfacesHierarchy = new IdentityHashMap<>();
     Set<DexType> processed = Sets.newIdentityHashSet();
     ArrayList<DexType> emulatedInterfacesSorted = new ArrayList<>(emulateInterfaces.keySet());
     emulatedInterfacesSorted.sort(DexType::compareTo);
     for (DexType interfaceType : emulatedInterfacesSorted) {
-      processEmulatedInterfaceHierarchy(
-          appInfo, emulateInterfaces, interfaceType, processed, emulatedInterfacesHierarchy);
+      processEmulatedInterfaceHierarchy(appInfo, emulateInterfaces, interfaceType, processed);
     }
-    return emulatedInterfacesHierarchy;
   }
 
   private void processEmulatedInterfaceHierarchy(
       AppInfoWithClassHierarchy appInfo,
       Map<DexType, DexType> emulateInterfaces,
       DexType interfaceType,
-      Set<DexType> processed,
-      Map<DexType, List<DexType>> emulatedInterfacesHierarchy) {
+      Set<DexType> processed) {
     if (processed.contains(interfaceType)) {
       return;
     }
-    emulatedInterfacesHierarchy.put(interfaceType, new ArrayList<>());
+    emulatedInterfaceHierarchy.put(interfaceType, new ArrayList<>());
     processed.add(interfaceType);
     DexClass theInterface = appInfo.definitionFor(interfaceType);
     if (theInterface == null) {
@@ -170,9 +172,8 @@
     while (!workList.isEmpty()) {
       DexType next = workList.next();
       if (emulateInterfaces.containsKey(next)) {
-        processEmulatedInterfaceHierarchy(
-            appInfo, emulateInterfaces, next, processed, emulatedInterfacesHierarchy);
-        emulatedInterfacesHierarchy.get(next).add(interfaceType);
+        processEmulatedInterfaceHierarchy(appInfo, emulateInterfaces, next, processed);
+        emulatedInterfaceHierarchy.get(next).add(interfaceType);
         DexClass nextClass = appInfo.definitionFor(next);
         if (nextClass != null) {
           workList.addIfNotSeen(nextClass.interfaces.values);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
index 6ff7428..5704f3d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
@@ -5,44 +5,71 @@
 package com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion;
 
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+import java.util.IdentityHashMap;
 import java.util.Map;
+import java.util.Set;
+import java.util.function.BiConsumer;
 
 public class HumanToMachinePrefixConverter {
 
   private final AppInfoWithClassHierarchy appInfo;
+  private final MachineRewritingFlags.Builder builder;
+  private final String synthesizedPrefix;
+  private final Map<DexString, DexString> descriptorPrefix;
+  private final Map<DexType, DexType> reverse = new IdentityHashMap<>();
+  private final Set<DexString> usedPrefix = Sets.newIdentityHashSet();
 
-  public HumanToMachinePrefixConverter(AppInfoWithClassHierarchy appInfo) {
+  public HumanToMachinePrefixConverter(
+      AppInfoWithClassHierarchy appInfo,
+      MachineRewritingFlags.Builder builder,
+      String synthesizedPrefix,
+      Map<String, String> descriptorPrefix) {
     this.appInfo = appInfo;
-  }
-
-  private DexString toDescriptorPrefix(String prefix) {
-    return appInfo
-        .dexItemFactory()
-        .createString("L" + DescriptorUtils.getBinaryNameFromJavaType(prefix));
+    this.builder = builder;
+    this.synthesizedPrefix = synthesizedPrefix;
+    this.descriptorPrefix = convertRewritePrefix(descriptorPrefix);
   }
 
   public void convertPrefixFlags(
-      HumanRewritingFlags rewritingFlags,
-      MachineRewritingFlags.Builder builder,
-      String synthesizedPrefix) {
-    Map<DexString, DexString> descriptorPrefix = convertRewritePrefix(rewritingFlags);
-    rewriteClasses(descriptorPrefix, builder);
-    rewriteValues(descriptorPrefix, builder, rewritingFlags.getRetargetCoreLibMember());
-    rewriteValues(descriptorPrefix, builder, rewritingFlags.getCustomConversions());
-    rewriteEmulatedInterface(builder, rewritingFlags.getEmulateLibraryInterface());
-    rewriteRetargetKeys(builder, rewritingFlags.getRetargetCoreLibMember(), synthesizedPrefix);
+      HumanRewritingFlags rewritingFlags, BiConsumer<String, Set<DexString>> warnConsumer) {
+    rewriteClasses();
+    rewriteValues(rewritingFlags.getRetargetCoreLibMember());
+    rewriteValues(rewritingFlags.getCustomConversions());
+    rewriteEmulatedInterface(rewritingFlags.getEmulateLibraryInterface());
+    rewriteRetargetKeys(rewritingFlags.getRetargetCoreLibMember());
+    rewriteReverse();
+    warnIfUnusedPrefix(warnConsumer);
   }
 
-  public DexType convertJavaNameToDesugaredLibrary(DexType type, String prefix) {
-    String convertedPrefix = DescriptorUtils.getJavaTypeFromBinaryName(prefix);
+  private void warnIfUnusedPrefix(BiConsumer<String, Set<DexString>> warnConsumer) {
+    Set<DexString> prefixes = Sets.newIdentityHashSet();
+    prefixes.addAll(descriptorPrefix.keySet());
+    prefixes.removeAll(usedPrefix);
+    warnConsumer.accept("The following prefixes do not match any type: ", prefixes);
+  }
+
+  // For custom conversions, this is responsible in rewriting backward.
+  private void rewriteReverse() {
+    reverse.forEach(
+        (rewrittenType, type) -> {
+          DexType chainType = rewrittenType(rewrittenType);
+          if (chainType != null) {
+            builder.rewriteType(rewrittenType, chainType);
+          }
+        });
+  }
+
+  public DexType convertJavaNameToDesugaredLibrary(DexType type) {
+    String convertedPrefix = DescriptorUtils.getJavaTypeFromBinaryName(synthesizedPrefix);
     String interfaceType = type.toString();
     int firstPackage = interfaceType.indexOf('.');
     return appInfo
@@ -52,49 +79,65 @@
                 convertedPrefix + interfaceType.substring(firstPackage + 1)));
   }
 
-  private void rewriteRetargetKeys(
-      MachineRewritingFlags.Builder builder, Map<DexMethod, DexType> retarget, String prefix) {
+  private void rewriteRetargetKeys(Map<DexMethod, DexType> retarget) {
     for (DexMethod dexMethod : retarget.keySet()) {
-      DexType type = convertJavaNameToDesugaredLibrary(dexMethod.holder, prefix);
+      DexType type = convertJavaNameToDesugaredLibrary(dexMethod.holder);
       builder.rewriteDerivedTypeOnly(dexMethod.holder, type);
     }
   }
 
-  private void rewriteEmulatedInterface(
-      MachineRewritingFlags.Builder builder, Map<DexType, DexType> emulateLibraryInterface) {
+  private void rewriteEmulatedInterface(Map<DexType, DexType> emulateLibraryInterface) {
     emulateLibraryInterface.forEach(builder::rewriteDerivedTypeOnly);
   }
 
+  private void rewriteType(DexType type, DexType rewrittenType) {
+    builder.rewriteType(type, rewrittenType);
+    reverse.put(rewrittenType, type);
+  }
+
   private void rewriteValues(
-      Map<DexString, DexString> descriptorPrefix,
-      MachineRewritingFlags.Builder builder,
       Map<?, DexType> flags) {
     for (DexType type : flags.values()) {
-      DexType rewrittenType = rewrittenType(descriptorPrefix, type);
-      if (rewrittenType != null) {
-        builder.rewriteType(type, rewrittenType);
-      }
+      registerType(type);
     }
   }
 
-  private void rewriteClasses(
-      Map<DexString, DexString> descriptorPrefix, MachineRewritingFlags.Builder builder) {
-    for (DexProgramClass clazz : appInfo.classes()) {
-      DexType type = clazz.type;
-      DexType rewrittenType = rewrittenType(descriptorPrefix, type);
-      if (rewrittenType != null) {
-        builder.rewriteType(type, rewrittenType);
-      }
+  private void rewriteClasses() {
+    for (DexClass clazz : appInfo.app().asDirect().libraryClasses()) {
+      rewriteClass(clazz);
+    }
+    for (DexClass clazz : appInfo.classes()) {
+      rewriteClass(clazz);
     }
   }
 
-  private DexType rewrittenType(Map<DexString, DexString> descriptorPrefix, DexType type) {
+  private void rewriteClass(DexClass clazz) {
+    registerType(clazz.type);
+    // We allow missing referenced types for the work-in-progress desugaring.
+    if (clazz.superType != null) {
+      registerType(clazz.superType);
+    }
+    clazz.interfaces.forEach(this::registerType);
+    if (clazz.getInnerClasses() != null) {
+      clazz.getInnerClasses().forEach(attr -> attr.forEachType(this::registerType));
+    }
+  }
+
+  private void registerType(DexType type) {
+    DexType rewrittenType = rewrittenType(type);
+    if (rewrittenType != null) {
+      rewriteType(type, rewrittenType);
+    }
+  }
+
+  private DexType rewrittenType(DexType type) {
     DexString prefixToMatch = type.descriptor.withoutArray(appInfo.dexItemFactory());
     for (DexString prefix : descriptorPrefix.keySet()) {
       if (prefixToMatch.startsWith(prefix)) {
         DexString rewrittenTypeDescriptor =
             type.descriptor.withNewPrefix(
                 prefix, descriptorPrefix.get(prefix), appInfo.dexItemFactory());
+        usedPrefix.add(prefix);
         return appInfo.dexItemFactory().createType(rewrittenTypeDescriptor);
       }
     }
@@ -102,12 +145,17 @@
   }
 
   private ImmutableMap<DexString, DexString> convertRewritePrefix(
-      HumanRewritingFlags rewritingFlags) {
-    Map<String, String> rewritePrefix = rewritingFlags.getRewritePrefix();
+      Map<String, String> rewritePrefix) {
     ImmutableMap.Builder<DexString, DexString> mapBuilder = ImmutableMap.builder();
     for (String key : rewritePrefix.keySet()) {
       mapBuilder.put(toDescriptorPrefix(key), toDescriptorPrefix(rewritePrefix.get(key)));
     }
     return mapBuilder.build();
   }
+
+  private DexString toDescriptorPrefix(String prefix) {
+    return appInfo
+        .dexItemFactory()
+        .createString("L" + DescriptorUtils.getBinaryNameFromJavaType(prefix));
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
index 6dce945..e153c70 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.MethodResolutionResult;
 import com.android.tools.r8.graph.SubtypingInfo;
@@ -18,27 +19,33 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
 import com.android.tools.r8.utils.TraversalContinuation;
+import com.google.common.collect.Sets;
 import java.util.LinkedHashMap;
 import java.util.Map;
+import java.util.Set;
 import java.util.function.BiConsumer;
 
 public class HumanToMachineRetargetConverter {
 
   private final AppInfoWithClassHierarchy appInfo;
-  private SubtypingInfo subtypingInfo;
+  private final SubtypingInfo subtypingInfo;
+  private final Set<DexMethod> missingMethods = Sets.newIdentityHashSet();
 
   public HumanToMachineRetargetConverter(AppInfoWithClassHierarchy appInfo) {
     this.appInfo = appInfo;
+    subtypingInfo = SubtypingInfo.create(appInfo);
   }
 
   public void convertRetargetFlags(
-      HumanRewritingFlags rewritingFlags, MachineRewritingFlags.Builder builder) {
-    subtypingInfo = SubtypingInfo.create(appInfo);
+      HumanRewritingFlags rewritingFlags,
+      MachineRewritingFlags.Builder builder,
+      BiConsumer<String, Set<? extends DexReference>> warnConsumer) {
     rewritingFlags
         .getRetargetCoreLibMember()
         .forEach(
             (method, type) ->
                 convertRetargetCoreLibMemberFlag(builder, rewritingFlags, method, type));
+    warnConsumer.accept("Cannot retarget missing methods: ", missingMethods);
   }
 
   private void convertRetargetCoreLibMemberFlag(
@@ -48,7 +55,16 @@
       DexType type) {
     DexClass holder = appInfo.definitionFor(method.holder);
     DexEncodedMethod foundMethod = holder.lookupMethod(method);
-    assert foundMethod != null;
+    if (foundMethod == null && method.getName().toString().equals("deepEquals0")) {
+      // TODO(b/184026720): Temporary work-around (the method is missing).
+      DexMethod dest = method.withHolder(type, appInfo.dexItemFactory());
+      builder.putStaticRetarget(method, dest);
+      return;
+    }
+    if (foundMethod == null) {
+      missingMethods.add(foundMethod.getReference());
+      return;
+    }
     if (foundMethod.isStatic()) {
       convertStaticRetarget(builder, foundMethod, type);
       return;
@@ -65,14 +81,16 @@
       HumanRewritingFlags rewritingFlags,
       DexEncodedMethod src,
       DexType type) {
-    if (isEmulatedInterfaceDispatch(src, appInfo, rewritingFlags)) {
-      // Handled by emulated interface dispatch.
-      return;
-    }
-    // TODO(b/184026720): Implement library boundaries.
     DexProto newProto = appInfo.dexItemFactory().prependHolderToProto(src.getReference());
     DexMethod forwardingDexMethod =
         appInfo.dexItemFactory().createMethod(type, newProto, src.getName());
+    if (isEmulatedInterfaceDispatch(src, appInfo, rewritingFlags)) {
+      // Handled by emulated interface dispatch.
+      builder.putEmulatedVirtualRetargetThroughEmulatedInterface(
+          src.getReference(), forwardingDexMethod);
+      return;
+    }
+    // TODO(b/184026720): Implement library boundaries.
     DerivedMethod forwardingMethod = new DerivedMethod(forwardingDexMethod);
     DerivedMethod interfaceMethod =
         new DerivedMethod(src.getReference(), SyntheticKind.RETARGET_INTERFACE);
@@ -92,10 +110,14 @@
       DexClass subclass = appInfo.definitionFor(subtype);
       MethodResolutionResult resolutionResult =
           appInfo.resolveMethodOn(subclass, src.getReference());
-      if (resolutionResult.isSuccessfulMemberResolutionResult()
-          && resolutionResult.getResolvedMethod().getReference() != src.getReference()) {
-        assert false; // Unsupported.
-      }
+      // The resolution is not successful when compiling to dex if the method rewritten is missing
+      // in Android.jar.
+      assert !resolutionResult.isSuccessfulMemberResolutionResult()
+          || resolutionResult.getResolvedMethod().getReference() == src.getReference()
+          // There is a difference in the sql library between Android.jar and the JDK which leads
+          // to this resolution when compiling Cf to Cf while the methods do not exist in Android.
+          || (resolutionResult.getResolvedMethod().getHolderType().toString().contains("java.sql")
+              && resolutionResult.getResolvedMethod().getName().toString().equals("toInstant"));
     }
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
index bc3a663..c668858 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
@@ -4,15 +4,22 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion;
 
+import com.android.tools.r8.ClassFileResourceProvider;
+import com.android.tools.r8.ProgramResourceProvider;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanTopLevelFlags;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.CustomConversionDescriptor;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineTopLevelFlags;
@@ -22,20 +29,58 @@
 import com.android.tools.r8.utils.Timing;
 import java.io.IOException;
 import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
 import java.util.concurrent.ExecutorService;
 
 public class HumanToMachineSpecificationConverter {
 
+  private AppView<?> appView;
+
   public MachineDesugaredLibrarySpecification convert(
-      HumanDesugaredLibrarySpecification humanSpec, Path androidLib, InternalOptions options)
+      HumanDesugaredLibrarySpecification humanSpec,
+      List<ProgramResourceProvider> desugaredJDKLib,
+      List<ClassFileResourceProvider> library,
+      InternalOptions options)
       throws IOException {
-    DexApplication app = readApp(androidLib, options);
-    AppView<?> appView = AppView.createForD8(AppInfo.createInitialAppInfo(app));
+    assert !humanSpec.isLibraryCompilation() || desugaredJDKLib != null;
+    AndroidApp.Builder builder = AndroidApp.builder();
+    for (ClassFileResourceProvider classFileResourceProvider : library) {
+      builder.addLibraryResourceProvider(classFileResourceProvider);
+    }
+    if (humanSpec.isLibraryCompilation()) {
+      for (ProgramResourceProvider programResourceProvider : desugaredJDKLib) {
+        builder.addProgramResourceProvider(programResourceProvider);
+      }
+    }
+    return internalConvert(humanSpec, builder.build(), options);
+  }
+
+  public MachineDesugaredLibrarySpecification convert(
+      HumanDesugaredLibrarySpecification humanSpec,
+      Path desugaredJDKLib,
+      Path androidLib,
+      InternalOptions options)
+      throws IOException {
+    assert !humanSpec.isLibraryCompilation() || desugaredJDKLib != null;
+    AndroidApp.Builder builder = AndroidApp.builder();
+    if (humanSpec.isLibraryCompilation()) {
+      builder.addProgramFile(desugaredJDKLib);
+    }
+    AndroidApp inputApp = builder.addLibraryFile(androidLib).build();
+    return internalConvert(humanSpec, inputApp, options);
+  }
+
+  private MachineDesugaredLibrarySpecification internalConvert(
+      HumanDesugaredLibrarySpecification humanSpec, AndroidApp inputApp, InternalOptions options)
+      throws IOException {
+    DexApplication app = readApp(inputApp, options);
+    appView = AppView.createForD8(AppInfo.createInitialAppInfo(app));
+    LibraryValidator.validate(app, humanSpec.getTopLevelFlags().getRequiredCompilationAPILevel());
     MachineRewritingFlags machineRewritingFlags =
         convertRewritingFlags(
-            humanSpec.getSynthesizedLibraryClassesPackagePrefix(),
-            humanSpec.getRewritingFlags(),
-            appView.appInfoForDesugaring());
+            humanSpec.getSynthesizedLibraryClassesPackagePrefix(), humanSpec.getRewritingFlags());
     MachineTopLevelFlags topLevelFlags = convertTopLevelFlags(humanSpec.getTopLevelFlags());
     return new MachineDesugaredLibrarySpecification(
         humanSpec.isLibraryCompilation(), topLevelFlags, machineRewritingFlags);
@@ -52,34 +97,69 @@
   }
 
   private MachineRewritingFlags convertRewritingFlags(
-      String synthesizedPrefix,
-      HumanRewritingFlags rewritingFlags,
-      AppInfoWithClassHierarchy appInfo) {
+      String synthesizedPrefix, HumanRewritingFlags rewritingFlags) {
+    AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
     MachineRewritingFlags.Builder builder = MachineRewritingFlags.builder();
-    new HumanToMachineRetargetConverter(appInfo).convertRetargetFlags(rewritingFlags, builder);
+    new HumanToMachineRetargetConverter(appInfo)
+        .convertRetargetFlags(rewritingFlags, builder, this::warnMissingReferences);
     new HumanToMachineEmulatedInterfaceConverter(appInfo)
-        .convertEmulatedInterfaces(rewritingFlags, appInfo, builder);
-    new HumanToMachinePrefixConverter(appInfo)
-        .convertPrefixFlags(rewritingFlags, builder, synthesizedPrefix);
-    new HumanToMachineWrapperConverter(appInfo).convertWrappers(rewritingFlags, builder);
+        .convertEmulatedInterfaces(rewritingFlags, appInfo, builder, this::warnMissingReferences);
+    new HumanToMachinePrefixConverter(
+            appInfo, builder, synthesizedPrefix, rewritingFlags.getRewritePrefix())
+        .convertPrefixFlags(rewritingFlags, this::warnMissingDexString);
+    new HumanToMachineWrapperConverter(appInfo)
+        .convertWrappers(rewritingFlags, builder, this::warnMissingReferences);
     rewritingFlags
         .getCustomConversions()
         .forEach(
             (type, conversionType) ->
-                builder.putCustomConversion(
-                    type, conversionType, appInfo.dexItemFactory().convertMethodName));
-    for (DexType type : rewritingFlags.getDontRetargetLibMember()) {
-      builder.addDontRetarget(type);
-    }
+                convertCustomConversion(appInfo, builder, type, conversionType));
+    rewritingFlags.getDontRetargetLibMember().forEach(builder::addDontRetarget);
     rewritingFlags.getBackportCoreLibraryMember().forEach(builder::putLegacyBackport);
     return builder.build();
   }
 
-  private DexApplication readApp(Path androidLib, InternalOptions options) throws IOException {
-    AndroidApp androidApp = AndroidApp.builder().addProgramFile(androidLib).build();
-    ApplicationReader applicationReader =
-        new ApplicationReader(androidApp, options, Timing.empty());
+  private void convertCustomConversion(
+      AppInfoWithClassHierarchy appInfo,
+      MachineRewritingFlags.Builder builder,
+      DexType type,
+      DexType conversionType) {
+    DexType rewrittenType = builder.getRewrittenType(type);
+    DexProto fromProto = appInfo.dexItemFactory().createProto(rewrittenType, type);
+    DexMethod fromMethod =
+        appInfo
+            .dexItemFactory()
+            .createMethod(conversionType, fromProto, appInfo.dexItemFactory().convertMethodName);
+    DexProto toProto = appInfo.dexItemFactory().createProto(type, rewrittenType);
+    DexMethod toMethod =
+        appInfo
+            .dexItemFactory()
+            .createMethod(conversionType, toProto, appInfo.dexItemFactory().convertMethodName);
+    builder.putCustomConversion(type, new CustomConversionDescriptor(toMethod, fromMethod));
+  }
+
+  private DexApplication readApp(AndroidApp inputApp, InternalOptions options) throws IOException {
+    ApplicationReader applicationReader = new ApplicationReader(inputApp, options, Timing.empty());
     ExecutorService executorService = ThreadUtils.getExecutorService(options);
     return applicationReader.read(executorService).toDirect();
   }
+
+  void warnMissingReferences(String message, Set<? extends DexReference> missingReferences) {
+    List<DexReference> memberList = new ArrayList<>(missingReferences);
+    memberList.sort(DexReference::compareTo);
+    warn(message, memberList);
+  }
+
+  void warnMissingDexString(String message, Set<DexString> missingDexString) {
+    List<DexString> memberList = new ArrayList<>(missingDexString);
+    memberList.sort(DexString::compareTo);
+    warn(message, memberList);
+  }
+
+  private void warn(String message, List<?> memberList) {
+    if (memberList.isEmpty()) {
+      return;
+    }
+    appView.options().reporter.warning("Specification conversion: " + message + memberList);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java
index db87890..815cb45 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java
@@ -8,30 +8,47 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
+import java.util.function.BiConsumer;
 
 public class HumanToMachineWrapperConverter {
 
   private final AppInfoWithClassHierarchy appInfo;
+  private final Set<DexType> missingClasses = Sets.newIdentityHashSet();
 
   public HumanToMachineWrapperConverter(AppInfoWithClassHierarchy appInfo) {
     this.appInfo = appInfo;
   }
 
   public void convertWrappers(
-      HumanRewritingFlags rewritingFlags, MachineRewritingFlags.Builder builder) {
-    for (DexType wrapperConversion : rewritingFlags.getWrapperConversions()) {
-      DexClass wrapperClass = appInfo.definitionFor(wrapperConversion);
-      assert wrapperClass != null;
-      List<DexMethod> methods = allImplementedMethods(wrapperClass);
-      methods.sort(DexMethod::compareTo);
-      builder.addWrapper(wrapperConversion, methods);
+      HumanRewritingFlags rewritingFlags,
+      MachineRewritingFlags.Builder builder,
+      BiConsumer<String, Set<? extends DexReference>> warnConsumer) {
+    for (DexType wrapperType : rewritingFlags.getWrapperConversions()) {
+      DexClass wrapperClass = appInfo.definitionFor(wrapperType);
+      if (wrapperClass == null) {
+        missingClasses.add(wrapperType);
+        continue;
+      }
+      List<DexMethod> methods;
+      if (wrapperClass.isEnum()) {
+        methods = ImmutableList.of();
+      } else {
+        methods = allImplementedMethods(wrapperClass);
+        methods.sort(DexMethod::compareTo);
+      }
+      builder.addWrapper(wrapperType, methods);
     }
+    warnConsumer.accept("The following types to wrap are missing: ", missingClasses);
   }
 
   private List<DexMethod> allImplementedMethods(DexClass wrapperClass) {
@@ -53,7 +70,8 @@
             }
           }
           if (!alreadyAdded) {
-            assert !virtualMethod.isFinal() : "Cannot wrap final method " + virtualMethod;
+            assert !virtualMethod.isFinal()
+                : "Cannot wrap final method " + virtualMethod + " while wrapping " + wrapperClass;
             implementedMethods.add(virtualMethod.getReference());
           }
         }
@@ -66,7 +84,8 @@
       }
       if (dexClass.superType != appInfo.dexItemFactory().objectType) {
         DexClass superClass = appInfo.definitionFor(dexClass.superType);
-        assert superClass != null; // Cannot be null since we started from a LibraryClass.
+        assert superClass != null
+            : "Missing supertype " + dexClass.superType + " while wrapping " + wrapperClass;
         workList.add(superClass);
       }
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
index 34bf5fa..c4884a5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion;
 
+import com.android.tools.r8.ClassFileResourceProvider;
 import com.android.tools.r8.StringConsumer;
 import com.android.tools.r8.StringResource;
 import com.android.tools.r8.dex.ApplicationReader;
@@ -44,6 +45,8 @@
 
 public class LegacyToHumanSpecificationConverter {
 
+  private AndroidApiLevel legacyHackLevel = AndroidApiLevel.N_MR1;
+
   public void convertAllAPILevels(
       StringResource inputSpecification, Path androidLib, StringConsumer output)
       throws IOException {
@@ -65,7 +68,9 @@
       InternalOptions options)
       throws IOException {
     Origin origin = legacySpec.getOrigin();
-    DexApplication app = readApp(androidLib, options);
+    AndroidApp androidApp = AndroidApp.builder().addLibraryFile(androidLib).build();
+    DexApplication app = readApp(androidApp, options);
+    LibraryValidator.validate(app, legacySpec.getTopLevelFlags().getRequiredCompilationAPILevel());
     HumanTopLevelFlags humanTopLevelFlags = convertTopLevelFlags(legacySpec.getTopLevelFlags());
     Int2ObjectArrayMap<HumanRewritingFlags> commonFlags =
         convertRewritingFlagMap(legacySpec.getCommonFlags(), app, origin);
@@ -81,33 +86,60 @@
   }
 
   public HumanDesugaredLibrarySpecification convert(
+      LegacyDesugaredLibrarySpecification legacySpec,
+      List<ClassFileResourceProvider> library,
+      InternalOptions options)
+      throws IOException {
+    AndroidApp.Builder builder = AndroidApp.builder();
+    for (ClassFileResourceProvider classFileResourceProvider : library) {
+      builder.addLibraryResourceProvider(classFileResourceProvider);
+    }
+    return internalConvert(legacySpec, builder.build(), options);
+  }
+
+  public HumanDesugaredLibrarySpecification convert(
       LegacyDesugaredLibrarySpecification legacySpec, Path androidLib, InternalOptions options)
       throws IOException {
-    DexApplication app = readApp(androidLib, options);
-    HumanTopLevelFlags humanTopLevelFlags = convertTopLevelFlags(legacySpec.getTopLevelFlags());
+    AndroidApp androidApp = AndroidApp.builder().addLibraryFile(androidLib).build();
+    return internalConvert(legacySpec, androidApp, options);
+  }
 
+  public HumanDesugaredLibrarySpecification internalConvert(
+      LegacyDesugaredLibrarySpecification legacySpec, AndroidApp inputApp, InternalOptions options)
+      throws IOException {
+    DexApplication app = readApp(inputApp, options);
+    LibraryValidator.validate(app, legacySpec.getTopLevelFlags().getRequiredCompilationAPILevel());
+    HumanTopLevelFlags humanTopLevelFlags = convertTopLevelFlags(legacySpec.getTopLevelFlags());
     // The origin is not maintained in non multi-level specifications.
     // It should not matter since the origin is used to report invalid specifications, and
     // converting non multi-level specifications should be performed only with *valid*
     // specifications in practical cases.
     Origin origin = Origin.unknown();
-
     HumanRewritingFlags humanRewritingFlags =
         convertRewritingFlags(legacySpec.getRewritingFlags(), app, origin);
+    if (options.getMinApiLevel().isLessThanOrEqualTo(legacyHackLevel)
+        && legacySpec.isLibraryCompilation()) {
+      HumanRewritingFlags.Builder builder =
+          humanRewritingFlags.newBuilder(app.dexItemFactory(), app.options.reporter, origin);
+      legacyLibraryFlagHacks(app.dexItemFactory(), builder);
+      humanRewritingFlags = builder.build();
+    }
     return new HumanDesugaredLibrarySpecification(
-        humanTopLevelFlags,
-        humanRewritingFlags,
-        legacySpec.isLibraryCompilation(),
-        app.dexItemFactory());
+        humanTopLevelFlags, humanRewritingFlags, legacySpec.isLibraryCompilation());
   }
 
   private void legacyLibraryFlagHacks(
       Int2ObjectArrayMap<HumanRewritingFlags> libraryFlags, DexApplication app, Origin origin) {
-    int level = AndroidApiLevel.N_MR1.getLevel();
+    int level = legacyHackLevel.getLevel();
     HumanRewritingFlags humanRewritingFlags = libraryFlags.get(level);
     HumanRewritingFlags.Builder builder =
         humanRewritingFlags.newBuilder(app.dexItemFactory(), app.options.reporter, origin);
-    DexItemFactory itemFactory = app.dexItemFactory();
+    legacyLibraryFlagHacks(app.dexItemFactory(), builder);
+    libraryFlags.put(level, builder.build());
+  }
+
+  private void legacyLibraryFlagHacks(
+      DexItemFactory itemFactory, HumanRewritingFlags.Builder builder) {
 
     // TODO(b/177977763): This is only a workaround rewriting invokes of j.u.Arrays.deepEquals0
     // to j.u.DesugarArrays.deepEquals0.
@@ -130,14 +162,10 @@
     source = itemFactory.createMethod(itemFactory.createType("Ljava/util/TimeZone;"), proto, name);
     target = itemFactory.createType("Ljava/util/DesugarTimeZone;");
     builder.putRetargetCoreLibMember(source, target);
-
-    libraryFlags.put(level, builder.build());
   }
 
-  private DexApplication readApp(Path androidLib, InternalOptions options) throws IOException {
-    AndroidApp androidApp = AndroidApp.builder().addLibraryFile(androidLib).build();
-    ApplicationReader applicationReader =
-        new ApplicationReader(androidApp, options, Timing.empty());
+  private DexApplication readApp(AndroidApp inputApp, InternalOptions options) throws IOException {
+    ApplicationReader applicationReader = new ApplicationReader(inputApp, options, Timing.empty());
     ExecutorService executorService = ThreadUtils.getExecutorService(options);
     return applicationReader.read(executorService).toDirect();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java
new file mode 100644
index 0000000..b415c354
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java
@@ -0,0 +1,36 @@
+// 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.ir.desugar.desugaredlibrary.specificationconversion;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.AndroidApiLevel;
+
+public class LibraryValidator {
+
+  // Estimates if the library passed is at the expected minimum level, if it is not, raise
+  // a warning.
+  public static void validate(DexApplication app, AndroidApiLevel requiredCompilationAPILevel) {
+    DexType levelType;
+    if (requiredCompilationAPILevel.isEqualTo(AndroidApiLevel.O)) {
+      levelType = app.dexItemFactory.createType("Ljava/time/LocalTime;");
+    } else if (requiredCompilationAPILevel.isEqualTo(AndroidApiLevel.R)) {
+      levelType = app.dexItemFactory.createType("Ljava/util/concurrent/Flow;");
+    } else {
+      app.options.reporter.warning(
+          "Unsupported requiredCompilationAPILevel: " + requiredCompilationAPILevel);
+      return;
+    }
+    DexClass dexClass = app.definitionFor(levelType);
+    if (dexClass == null) {
+      app.options.reporter.warning(
+          "Desugared library requires to be compiled with a library file of API greater or equal to"
+              + " "
+              + requiredCompilationAPILevel
+              + ", but it seems the library file passed is of a lower API.");
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index 758812e..33f7037 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -24,10 +24,10 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GenericSignature;
 import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
-import com.android.tools.r8.graph.LibraryMethod;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.MethodResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
 import com.android.tools.r8.position.MethodPosition;
 import com.android.tools.r8.utils.BooleanBox;
 import com.android.tools.r8.utils.BooleanUtils;
@@ -49,7 +49,6 @@
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.BiConsumer;
-import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import org.objectweb.asm.Opcodes;
@@ -376,12 +375,7 @@
     this.dexItemFactory = appView.dexItemFactory();
     this.helper = new InterfaceDesugaringSyntheticHelper(appView);
     needsLibraryInfo =
-        !appView.options().desugaredLibrarySpecification.getEmulateLibraryInterface().isEmpty()
-            || !appView
-                .options()
-                .desugaredLibrarySpecification
-                .getRetargetCoreLibMember()
-                .isEmpty();
+        appView.options().machineDesugaredLibrarySpecification.hasEmulatedInterfaces();
     this.isLiveMethod = isLiveMethod;
   }
 
@@ -510,7 +504,7 @@
       DexClass iface = appView.definitionFor(emulatedInterface);
       if (iface != null) {
         assert iface.isLibraryClass()
-            || appView.options().desugaredLibrarySpecification.isLibraryCompilation();
+            || appView.options().machineDesugaredLibrarySpecification.isLibraryCompilation();
         workList.addIfNotSeen(iface.getInterfaces());
       }
     }
@@ -670,10 +664,10 @@
       resolveForwardForSignature(
           clazz,
           wrapper.get(),
-          target -> {
+          (target, forward) -> {
             if (isLiveMethod(target) && !superInfo.isTargetedByForwards(target)) {
               additionalForwards.add(target);
-              addForwardingMethod(target, clazz);
+              addForwardingMethod(target, forward, clazz);
             }
           });
     }
@@ -682,7 +676,7 @@
   // Looks up a method signature from the point of 'clazz', if it can dispatch to a default method
   // the 'addForward' call-back is called with the target of the forward.
   private void resolveForwardForSignature(
-      DexClass clazz, DexMethod method, Consumer<DexClassAndMethod> addForward) {
+      DexClass clazz, DexMethod method, BiConsumer<DexClassAndMethod, DexMethod> addForward) {
     AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOn(clazz, method);
     if (resolutionResult.isFailedResolution()
@@ -722,50 +716,22 @@
         resolutionResult.lookupVirtualDispatchTarget(clazz, appInfo);
     assert virtualDispatchTarget != null;
 
-    // Don't forward if the target is explicitly marked as 'dont-rewrite'
-    if (dontRewrite(virtualDispatchTarget)) {
-      return;
-    }
-
     // If resolution targets a default interface method, forward it.
     if (virtualDispatchTarget.isDefaultMethod()) {
-      addForward.accept(virtualDispatchTarget);
+      addForward.accept(
+          virtualDispatchTarget,
+          helper.ensureDefaultAsMethodOfCompanionClassStub(virtualDispatchTarget).getReference());
       return;
     }
 
-    // Remaining edge cases only pertain to desugaring of library methods.
-    if (!virtualDispatchTarget.isLibraryMethod() || ignoreLibraryInfo()) {
-      return;
+    DerivedMethod forwardingMethod =
+        helper.computeEmulatedInterfaceForwardingMethod(
+            virtualDispatchTarget.getHolder(), virtualDispatchTarget);
+    if (forwardingMethod != null) {
+      DexMethod concreteForwardingMethod =
+          helper.ensureEmulatedInterfaceForwardingMethod(forwardingMethod);
+      addForward.accept(virtualDispatchTarget, concreteForwardingMethod);
     }
-
-    LibraryMethod libraryMethod = virtualDispatchTarget.asLibraryMethod();
-    if (isRetargetMethod(libraryMethod)) {
-      addForward.accept(virtualDispatchTarget);
-      return;
-    }
-
-    // If target is a non-interface library class it may be an emulated interface,
-    // except on a rewritten type, where L8 has already dealt with the desugaring.
-    if (!libraryMethod.getHolder().isInterface()
-        && !appView.rewritePrefix.hasRewrittenType(libraryMethod.getHolderType(), appView)) {
-      // Here we use step-3 of resolution to find a maximally specific default interface method.
-      DexClassAndMethod result =
-          appInfo.lookupMaximallySpecificMethod(libraryMethod.getHolder(), method);
-      if (result != null && helper.isEmulatedInterface(result.getHolderType())) {
-        addForward.accept(result);
-      }
-    }
-  }
-
-  private boolean isRetargetMethod(LibraryMethod method) {
-    assert needsLibraryInfo();
-    assert method.getDefinition().isNonPrivateVirtualMethod();
-    return !method.getAccessFlags().isFinal()
-        && appView.options().desugaredLibrarySpecification.retargetMethod(method, appView) != null;
-  }
-
-  private boolean dontRewrite(DexClassAndMethod method) {
-    return needsLibraryInfo() && method.getHolder().isLibraryClass() && helper.dontRewrite(method);
   }
 
   // Construction of actual forwarding methods.
@@ -830,7 +796,8 @@
 
   // Note: The parameter 'target' may be a public method on a class in case of desugared
   // library retargeting (See below target.isInterface check).
-  private void addForwardingMethod(DexClassAndMethod target, DexClass clazz) {
+  private void addForwardingMethod(
+      DexClassAndMethod target, DexMethod forwardMethod, DexClass clazz) {
     if (!clazz.isProgramClass()) {
       return;
     }
@@ -847,10 +814,6 @@
     // NOTE: Never add a forwarding method to methods of classes unknown or coming from android.jar
     // even if this results in invalid code, these classes are never desugared.
     // In desugared library, emulated interface methods can be overridden by retarget lib members.
-    DexMethod forwardMethod =
-        target.getHolder().isInterface()
-            ? helper.ensureDefaultAsMethodOfCompanionClassStub(target).getReference()
-            : appView.options().desugaredLibrarySpecification.retargetMethod(target, appView);
     DexEncodedMethod desugaringForwardingMethod =
         DexEncodedMethod.createDesugaringForwardingMethod(
             target.getDefinition(), clazz, forwardMethod, dexItemFactory);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceApplicationRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceApplicationRewriter.java
index 74d94e3..d06fee7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceApplicationRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceApplicationRewriter.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.utils.IterableUtils;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -25,8 +26,15 @@
 
   public EmulatedInterfaceApplicationRewriter(AppView<?> appView) {
     this.appView = appView;
-    this.emulatedInterfaces =
-        appView.options().desugaredLibrarySpecification.getEmulateLibraryInterface();
+    emulatedInterfaces = new IdentityHashMap<>();
+    appView
+        .options()
+        .machineDesugaredLibrarySpecification
+        .getEmulatedInterfaces()
+        .forEach(
+            (ei, descriptor) -> {
+              emulatedInterfaces.put(ei, descriptor.getRewrittenType());
+            });
   }
 
   public void rewriteApplication(DexApplication.Builder<?> builder) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringForTesting.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringForTesting.java
index a3e0164..6636e1e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringForTesting.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringForTesting.java
@@ -4,10 +4,12 @@
 
 package com.android.tools.r8.ir.desugar.itf;
 
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+
 public class InterfaceDesugaringForTesting {
 
   public static String getEmulateLibraryClassNameSuffix() {
-    return InterfaceDesugaringSyntheticHelper.EMULATE_LIBRARY_CLASS_NAME_SUFFIX;
+    return SyntheticKind.EMULATED_INTERFACE_CLASS.descriptor;
   }
 
   public static String getCompanionClassNameSuffix() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
index 77d99e5..5d00719 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexValue.DexValueInt;
@@ -31,19 +32,20 @@
 import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.InvalidCode;
 import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.MethodResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ThrowNullCode;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedInterfaceDescriptor;
 import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceSynthesizerEventConsumer.ClasspathEmulatedInterfaceSynthesizerEventConsumer;
 import com.android.tools.r8.synthesis.SyntheticClassBuilder;
 import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.structural.Ordered;
 import com.google.common.collect.ImmutableList;
-import java.util.Map;
-import java.util.Set;
 import java.util.function.Predicate;
 
 public class InterfaceDesugaringSyntheticHelper {
@@ -57,37 +59,37 @@
   }
 
   // Use InterfaceDesugaringForTesting for public accesses in tests.
-  static final String EMULATE_LIBRARY_CLASS_NAME_SUFFIX = "$-EL";
   static final String COMPANION_CLASS_NAME_SUFFIX = "$-CC";
   static final String DEFAULT_METHOD_PREFIX = "$default$";
   static final String PRIVATE_METHOD_PREFIX = "$private$";
 
   private final AppView<?> appView;
-  private final Map<DexType, DexType> emulatedInterfaces;
   private final Predicate<DexType> shouldIgnoreFromReportsPredicate;
 
   public InterfaceDesugaringSyntheticHelper(AppView<?> appView) {
     this.appView = appView;
-    emulatedInterfaces =
-        appView.options().desugaredLibrarySpecification.getEmulateLibraryInterface();
-
     this.shouldIgnoreFromReportsPredicate = getShouldIgnoreFromReportsPredicate(appView);
   }
 
   boolean isEmulatedInterface(DexType itf) {
-    return emulatedInterfaces.containsKey(itf);
+    return appView
+        .options()
+        .machineDesugaredLibrarySpecification
+        .getEmulatedInterfaces()
+        .containsKey(itf);
   }
 
   boolean isRewrittenEmulatedInterface(DexType itf) {
-    return emulatedInterfaces.containsValue(itf);
-  }
-
-  Set<DexType> getEmulatedInterfaces() {
-    return emulatedInterfaces.keySet();
+    return appView
+        .options()
+        .machineDesugaredLibrarySpecification
+        .isEmulatedInterfaceRewrittenType(itf);
   }
 
   DexType getEmulatedInterface(DexType type) {
-    return emulatedInterfaces.get(type);
+    EmulatedInterfaceDescriptor interfaceDescriptor =
+        appView.options().machineDesugaredLibrarySpecification.getEmulatedInterfaces().get(type);
+    return interfaceDescriptor == null ? null : interfaceDescriptor.getRewrittenType();
   }
 
   boolean isInDesugaredLibrary(DexClass clazz) {
@@ -95,18 +97,7 @@
     if (isEmulatedInterface(clazz.type)) {
       return true;
     }
-    return appView.rewritePrefix.hasRewrittenType(clazz.type, appView);
-  }
-
-  boolean dontRewrite(DexClassAndMethod method) {
-    for (Pair<DexType, DexString> dontRewrite :
-        appView.options().desugaredLibrarySpecification.getDontRewriteInvocation()) {
-      if (method.getHolderType() == dontRewrite.getFirst()
-          && method.getName() == dontRewrite.getSecond()) {
-        return true;
-      }
-    }
-    return false;
+    return appView.typeRewriter.hasRewrittenType(clazz.type, appView);
   }
 
   final boolean isCompatibleDefaultMethod(DexEncodedMethod method) {
@@ -129,25 +120,15 @@
     return true;
   }
 
-  public DexMethod emulateInterfaceLibraryMethod(DexClassAndMethod method) {
-    DexItemFactory factory = appView.dexItemFactory();
-    return factory.createMethod(
-        getEmulateLibraryInterfaceClassType(method.getHolderType(), factory),
-        factory.prependTypeToProto(method.getHolderType(), method.getProto()),
-        method.getName());
+  DexMethod emulatedInterfaceDispatchMethod(DerivedMethod method, DexType holder) {
+    assert method.getHolderKind() == SyntheticKind.EMULATED_INTERFACE_CLASS;
+    DexProto newProto = appView.dexItemFactory().prependHolderToProto(method.getMethod());
+    return appView.dexItemFactory().createMethod(holder, newProto, method.getName());
   }
 
-  private static String getEmulateLibraryInterfaceClassDescriptor(String descriptor) {
-    return descriptor.substring(0, descriptor.length() - 1)
-        + EMULATE_LIBRARY_CLASS_NAME_SUFFIX
-        + ";";
-  }
-
-  public static DexType getEmulateLibraryInterfaceClassType(DexType type, DexItemFactory factory) {
-    assert type.isClassType();
-    String descriptor = type.descriptor.toString();
-    String elTypeDescriptor = getEmulateLibraryInterfaceClassDescriptor(descriptor);
-    return factory.createSynthesizedType(elTypeDescriptor);
+  DexMethod emulatedInterfaceInterfaceMethod(DerivedMethod method) {
+    assert method.getHolderKind() == null;
+    return method.getMethod();
   }
 
   public static String getCompanionClassDescriptor(String descriptor) {
@@ -167,10 +148,6 @@
     return type.descriptor.toString().endsWith(COMPANION_CLASS_NAME_SUFFIX + ";");
   }
 
-  public static boolean isEmulatedLibraryClassType(DexType type) {
-    return type.descriptor.toString().endsWith(EMULATE_LIBRARY_CLASS_NAME_SUFFIX + ";");
-  }
-
   // Gets the interface class for a companion class `type`.
   DexType getInterfaceClassType(DexType type) {
     return getInterfaceClassType(type, appView.dexItemFactory());
@@ -194,12 +171,98 @@
             SyntheticKind.EMULATED_INTERFACE_MARKER_CLASS,
             type,
             appView,
-            SyntheticClassBuilder::setInterface);
+            SyntheticClassBuilder::setInterface,
+            ignored -> {});
   }
 
-  DexClassAndMethod ensureEmulatedInterfaceMethod(
-      DexClassAndMethod method, ClasspathEmulatedInterfaceSynthesizerEventConsumer eventConsumer) {
-    DexMethod emulatedInterfaceMethod = emulateInterfaceLibraryMethod(method);
+  DexClassAndMethod lookupMaximallySpecificIncludingSelf(
+      DexClass initialResolutionHolder, DexClassAndMethod method) {
+    assert method.getHolderType().isClassType();
+    if (method.getHolder().isInterface()) {
+      return method;
+    }
+    return appView
+        .appInfoForDesugaring()
+        .lookupMaximallySpecificMethod(initialResolutionHolder, method.getReference());
+  }
+
+  EmulatedDispatchMethodDescriptor getEmulatedDispatchDescriptor(
+      DexClass initialResolutionHolder, DexClassAndMethod method) {
+    if (method == null) {
+      return null;
+    }
+    assert initialResolutionHolder != null;
+    if (!requiresEmulatedDispatch(method)) {
+      return null;
+    }
+    DexClassAndMethod maximallySpecificMethod =
+        lookupMaximallySpecificIncludingSelf(initialResolutionHolder, method);
+    if (maximallySpecificMethod == null) {
+      return null;
+    }
+    return appView
+        .options()
+        .machineDesugaredLibrarySpecification
+        .getEmulatedInterfaceEmulatedDispatchMethodDescriptor(
+            maximallySpecificMethod.getReference());
+  }
+
+  private boolean requiresEmulatedDispatch(DexClassAndMethod method) {
+    return method.isLibraryMethod()
+        || isEmulatedInterface(method.getHolderType())
+        || appView
+            .options()
+            .machineDesugaredLibrarySpecification
+            .getEmulatedVirtualRetargetThroughEmulatedInterface()
+            .containsKey(method.getReference());
+  }
+
+  DerivedMethod computeEmulatedInterfaceDispatchMethod(MethodResolutionResult resolutionResult) {
+    EmulatedDispatchMethodDescriptor descriptor =
+        getEmulatedDispatchDescriptor(
+            resolutionResult.getInitialResolutionHolder(), resolutionResult.getResolutionPair());
+    return descriptor == null ? null : descriptor.getEmulatedDispatchMethod();
+  }
+
+  DerivedMethod computeEmulatedInterfaceForwardingMethod(
+      DexClass initialResolutionHolder, DexClassAndMethod method) {
+    if (method == null) {
+      return null;
+    }
+    DexMethod retarget =
+        appView
+            .options()
+            .machineDesugaredLibrarySpecification
+            .getEmulatedVirtualRetargetThroughEmulatedInterface()
+            .get(method.getReference());
+    if (retarget != null) {
+      return new DerivedMethod(retarget);
+    }
+    EmulatedDispatchMethodDescriptor descriptor =
+        getEmulatedDispatchDescriptor(initialResolutionHolder, method);
+    return descriptor == null ? null : descriptor.getForwardingMethod();
+  }
+
+  DexMethod ensureEmulatedInterfaceForwardingMethod(DerivedMethod method) {
+    if (method.getHolderKind() == null) {
+      return method.getMethod();
+    }
+    assert method.getHolderKind() == SyntheticKind.COMPANION_CLASS;
+    DexClassAndMethod resolvedMethod =
+        appView.appInfoForDesugaring().resolveMethod(method.getMethod(), true).getResolutionPair();
+    return ensureDefaultAsMethodOfCompanionClassStub(resolvedMethod).getReference();
+  }
+
+  DexClassAndMethod ensureEmulatedInterfaceDispatchMethod(
+      DerivedMethod emulatedDispatchMethod,
+      ClasspathEmulatedInterfaceSynthesizerEventConsumer eventConsumer) {
+    assert emulatedDispatchMethod.getHolderKind() == SyntheticKind.EMULATED_INTERFACE_CLASS;
+    DexClassAndMethod method =
+        appView
+            .appInfoForDesugaring()
+            .resolveMethod(emulatedDispatchMethod.getMethod(), true)
+            .getResolutionPair();
+    assert emulatedDispatchMethod.getHolderKind() == SyntheticKind.EMULATED_INTERFACE_CLASS;
     if (method.isProgramMethod()) {
       assert appView.options().isDesugaredLibraryCompilation();
       DexProgramClass emulatedInterface =
@@ -209,8 +272,15 @@
                   SyntheticKind.EMULATED_INTERFACE_CLASS,
                   method.asProgramMethod().getHolder(),
                   appView);
+      DexMethod emulatedInterfaceMethod =
+          emulatedInterfaceDispatchMethod(emulatedDispatchMethod, emulatedInterface.type);
+      assert emulatedInterface.lookupProgramMethod(emulatedInterfaceMethod) != null;
       return emulatedInterface.lookupProgramMethod(emulatedInterfaceMethod);
     }
+    // The holder is not used.
+    DexMethod emulatedInterfaceMethod =
+        emulatedInterfaceDispatchMethod(
+            emulatedDispatchMethod, appView.dexItemFactory().objectType);
     return appView
         .getSyntheticItems()
         .ensureFixedClasspathClassMethod(
@@ -508,10 +578,10 @@
 
     return type -> {
       DexString descriptor = type.getDescriptor();
-      return appView.rewritePrefix.hasRewrittenType(type, appView)
+      return appView.typeRewriter.hasRewrittenType(type, appView)
           || descriptor.endsWith(companionClassNameDescriptorSuffix)
           || isRewrittenEmulatedInterface(type)
-          || options.desugaredLibrarySpecification.getCustomConversions().containsValue(type)
+          || options.machineDesugaredLibrarySpecification.isCustomConversionRewrittenType(type)
           || appView.getDontWarnConfiguration().matches(type);
     };
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 6740441..85efd2b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -35,7 +35,8 @@
 import com.android.tools.r8.ir.desugar.DesugarDescription;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.icce.AlwaysThrowingInstructionDesugaring;
 import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring;
 import com.android.tools.r8.ir.desugar.stringconcat.StringConcatInstructionDesugaring;
@@ -53,7 +54,6 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.BiConsumer;
 import java.util.function.Predicate;
 
 //
@@ -119,18 +119,23 @@
   }
 
   public static void checkForAssumedLibraryTypes(AppInfo appInfo, InternalOptions options) {
-    LegacyDesugaredLibrarySpecification spec = options.desugaredLibrarySpecification;
-    BiConsumer<DexType, DexType> registerEntry = registerMapEntry(appInfo);
-    spec.getEmulateLibraryInterface().forEach(registerEntry);
-    spec.getCustomConversions().forEach(registerEntry);
-    spec.getRetargetCoreLibMember().forEach((method, types) -> types.forEach(registerEntry));
-  }
-
-  private static BiConsumer<DexType, DexType> registerMapEntry(AppInfo appInfo) {
-    return (key, value) -> {
-      registerType(appInfo, key);
-      registerType(appInfo, value);
-    };
+    MachineDesugaredLibrarySpecification machineDesugaredLibrarySpecification =
+        options.machineDesugaredLibrarySpecification;
+    machineDesugaredLibrarySpecification
+        .getEmulatedInterfaces()
+        .forEach(
+            (ei, descriptor) -> {
+              registerType(appInfo, ei);
+              registerType(appInfo, descriptor.getRewrittenType());
+            });
+    machineDesugaredLibrarySpecification
+        .getCustomConversions()
+        .forEach(
+            (type, descriptor) -> {
+              registerType(appInfo, type);
+              registerType(appInfo, descriptor.getTo().getHolderType());
+              registerType(appInfo, descriptor.getFrom().getHolderType());
+            });
   }
 
   private static void registerType(AppInfo appInfo, DexType type) {
@@ -156,9 +161,9 @@
   }
 
   private void initializeEmulatedInterfaceVariables() {
-    Map<DexType, DexType> emulateLibraryInterface =
-        options.desugaredLibrarySpecification.getEmulateLibraryInterface();
-    for (DexType interfaceType : emulateLibraryInterface.keySet()) {
+    Set<DexType> emulateLibraryInterface =
+        options.machineDesugaredLibrarySpecification.getEmulatedInterfaces().keySet();
+    for (DexType interfaceType : emulateLibraryInterface) {
       DexClass emulatedInterfaceClass = appView.definitionFor(interfaceType);
       if (emulatedInterfaceClass != null) {
         for (DexEncodedMethod encodedMethod :
@@ -449,24 +454,27 @@
   }
 
   private DesugarDescription computeEmulatedInterfaceVirtualDispatchOrNull(CfInvoke invoke) {
-    DexClassAndMethod defaultMethod =
-        defaultMethodForEmulatedDispatchOrNull(invoke.getMethod(), invoke.isInterface());
-    if (defaultMethod != null) {
-      return DesugarDescription.builder()
-          .setDesugarRewrite(
-              (freshLocalProvider,
-                  localStackAllocator,
-                  eventConsumer,
-                  context1,
-                  methodProcessingContext,
-                  dexItemFactory) ->
-                  getInvokeStaticInstructions(
-                      helper
-                          .ensureEmulatedInterfaceMethod(defaultMethod, eventConsumer)
-                          .getReference()))
-          .build();
+    MethodResolutionResult resolutionResult =
+        appView.appInfoForDesugaring().resolveMethod(invoke.getMethod(), invoke.isInterface());
+    DerivedMethod emulatedDispatchMethod =
+        helper.computeEmulatedInterfaceDispatchMethod(resolutionResult);
+    if (emulatedDispatchMethod == null) {
+      return null;
     }
-    return null;
+    return DesugarDescription.builder()
+        .setDesugarRewrite(
+            (freshLocalProvider,
+                localStackAllocator,
+                eventConsumer,
+                context1,
+                methodProcessingContext,
+                dexItemFactory) ->
+                getInvokeStaticInstructions(
+                    helper
+                        .ensureEmulatedInterfaceDispatchMethod(
+                            emulatedDispatchMethod, eventConsumer)
+                        .getReference()))
+        .build();
   }
 
   private DesugarDescription computeInvokeDirect(
@@ -704,103 +712,49 @@
 
   private DesugarDescription computeEmulatedInterfaceInvokeSpecial(
       DexClass clazz, DexMethod invokedMethod, ProgramMethod context) {
-    DexType emulatedItf = maximallySpecificEmulatedInterfaceOrNull(invokedMethod);
-    if (emulatedItf == null) {
-      if (clazz.isInterface() && appView.rewritePrefix.hasRewrittenType(clazz.type, appView)) {
-        DexClassAndMethod target =
-            appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
-        if (target != null && target.getDefinition().isDefaultMethod()) {
-          DexClass holder = target.getHolder();
-          if (holder.isLibraryClass() && holder.isInterface()) {
-            return DesugarDescription.builder()
-                .setDesugarRewrite(
-                    (freshLocalProvider,
-                        localStackAllocator,
-                        eventConsumer,
-                        context13,
-                        methodProcessingContext,
-                        dexItemFactory) -> {
-                      DexClassAndMethod companionTarget =
-                          helper.ensureDefaultAsMethodOfCompanionClassStub(target);
-                      acceptCompanionMethod(target, companionTarget, eventConsumer);
-                      return getInvokeStaticInstructions(companionTarget.getReference());
-                    })
-                .build();
-          }
+    DexClassAndMethod superTarget =
+        appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
+    if (clazz.isInterface() && appView.typeRewriter.hasRewrittenType(clazz.type, appView)) {
+      if (superTarget != null && superTarget.getDefinition().isDefaultMethod()) {
+        DexClass holder = superTarget.getHolder();
+        if (holder.isLibraryClass() && holder.isInterface()) {
+          return DesugarDescription.builder()
+              .setDesugarRewrite(
+                  (freshLocalProvider,
+                      localStackAllocator,
+                      eventConsumer,
+                      context13,
+                      methodProcessingContext,
+                      dexItemFactory) -> {
+                    DexClassAndMethod companionTarget =
+                        helper.ensureDefaultAsMethodOfCompanionClassStub(superTarget);
+                    acceptCompanionMethod(superTarget, companionTarget, eventConsumer);
+                    return getInvokeStaticInstructions(companionTarget.getReference());
+                  })
+              .build();
         }
       }
-      return DesugarDescription.nothing();
     }
     // That invoke super may not resolve since the super method may not be present
     // since it's in the emulated interface. We need to force resolution. If it resolves
     // to a library method, then it needs to be rewritten.
     // If it resolves to a program overrides, the invoke-super can remain.
-    DexClassAndMethod superTarget =
-        appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
-    if (superTarget != null && superTarget.isLibraryMethod()) {
-      // Rewriting is required because the super invoke resolves into a missing
-      // method (method is on desugared library). Find out if it needs to be
-      // retargeted or if it just calls a companion class method and rewrite.
-      DexMethod retargetMethod =
-          options.desugaredLibrarySpecification.retargetMethod(superTarget, appView);
-      if (retargetMethod != null) {
-        return DesugarDescription.builder()
-            .setDesugarRewrite(
-                (freshLocalProvider,
-                    localStackAllocator,
-                    eventConsumer,
-                    context14,
-                    methodProcessingContext,
-                    dexItemFactory) -> getInvokeStaticInstructions(retargetMethod))
-            .build();
-      }
-      DexClassAndMethod emulatedMethod =
-          superTarget.getReference().lookupMemberOnClass(appView.definitionFor(emulatedItf));
-      if (emulatedMethod == null) {
-        assert false;
-        return DesugarDescription.nothing();
-      }
-      return DesugarDescription.builder()
-          .setDesugarRewrite(
-              (freshLocalProvider,
-                  localStackAllocator,
-                  eventConsumer,
-                  context15,
-                  methodProcessingContext,
-                  dexItemFactory) -> {
-                DexClassAndMethod companionMethod =
-                    helper.ensureDefaultAsMethodOfCompanionClassStub(emulatedMethod);
-                return getInvokeStaticInstructions(companionMethod.getReference());
-              })
-          .build();
+    DerivedMethod forwardingMethod =
+        helper.computeEmulatedInterfaceForwardingMethod(clazz, superTarget);
+    if (forwardingMethod == null) {
+      return DesugarDescription.nothing();
     }
-    return DesugarDescription.nothing();
-  }
-
-  private DexClassAndMethod defaultMethodForEmulatedDispatchOrNull(
-      DexMethod invokedMethod, boolean interfaceBit) {
-    DexType emulatedItf = maximallySpecificEmulatedInterfaceOrNull(invokedMethod);
-    if (emulatedItf == null) {
-      return null;
-    }
-    // The call potentially ends up in a library class, in which case we need to rewrite, since the
-    // code may be in the desugared library.
-    SingleResolutionResult resolution =
-        appView
-            .appInfoForDesugaring()
-            .resolveMethod(invokedMethod, interfaceBit)
-            .asSingleResolution();
-    if (resolution != null
-        && (resolution.getResolvedHolder().isLibraryClass()
-            || helper.isEmulatedInterface(resolution.getResolvedHolder().type))) {
-      DexClassAndMethod defaultMethod =
-          appView.definitionFor(emulatedItf).lookupClassMethod(invokedMethod);
-      if (defaultMethod != null && !helper.dontRewrite(defaultMethod)) {
-        assert !defaultMethod.getAccessFlags().isAbstract();
-        return defaultMethod;
-      }
-    }
-    return null;
+    return DesugarDescription.builder()
+        .setDesugarRewrite(
+            (freshLocalProvider,
+                localStackAllocator,
+                eventConsumer,
+                context14,
+                methodProcessingContext,
+                dexItemFactory) ->
+                getInvokeStaticInstructions(
+                    helper.ensureEmulatedInterfaceForwardingMethod(forwardingMethod)))
+        .build();
   }
 
   private boolean shouldRewriteToInvokeToThrow(
@@ -809,40 +763,6 @@
         || resolutionResult.getResolvedMethod().isStatic() != isInvokeStatic;
   }
 
-  private DexType maximallySpecificEmulatedInterfaceOrNull(DexMethod invokedMethod) {
-    // Here we try to avoid doing the expensive look-up on all invokes.
-    if (!emulatedMethods.contains(invokedMethod.name)) {
-      return null;
-    }
-    DexClass dexClass = appView.definitionFor(invokedMethod.holder);
-    // We cannot rewrite the invoke we do not know what the class is.
-    if (dexClass == null) {
-      return null;
-    }
-    DexEncodedMethod singleTarget = null;
-    if (dexClass.isInterface()) {
-      // Look for exact method on the interface.
-      singleTarget = dexClass.lookupMethod(invokedMethod);
-    }
-    if (singleTarget == null) {
-      DexClassAndMethod result =
-          appView.appInfoForDesugaring().lookupMaximallySpecificMethod(dexClass, invokedMethod);
-      if (result != null) {
-        singleTarget = result.getDefinition();
-      }
-    }
-    if (singleTarget == null) {
-      // At this point we are in a library class. Failures can happen with NoSuchMethod if a
-      // library class implement a method with same signature but not related to emulated
-      // interfaces.
-      return null;
-    }
-    if (!singleTarget.isAbstract() && helper.isEmulatedInterface(singleTarget.getHolderType())) {
-      return singleTarget.getHolderType();
-    }
-    return null;
-  }
-
   private boolean isNonDesugaredLibraryClass(DexClass clazz) {
     return clazz.isLibraryClass() && !helper.isInDesugaredLibrary(clazz);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
index f712cb4..7e8a2cc 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
@@ -5,11 +5,9 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -25,27 +23,16 @@
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
 import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
 import com.android.tools.r8.utils.StringDiagnostic;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.IdentityHashMap;
 import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
 
 public final class ProgramEmulatedInterfaceSynthesizer implements CfClassSynthesizerDesugaring {
 
   private final AppView<?> appView;
   private final InterfaceDesugaringSyntheticHelper helper;
-  private final Map<DexType, List<DexType>> emulatedInterfacesHierarchy;
 
   public static ProgramEmulatedInterfaceSynthesizer create(AppView<?> appView) {
     if (!appView.options().isDesugaredLibraryCompilation()
-        || appView.options().desugaredLibrarySpecification.getEmulateLibraryInterface().isEmpty()) {
+        || !appView.options().machineDesugaredLibrarySpecification.hasEmulatedInterfaces()) {
       return null;
     }
     return new ProgramEmulatedInterfaceSynthesizer(appView);
@@ -54,53 +41,11 @@
   public ProgramEmulatedInterfaceSynthesizer(AppView<?> appView) {
     this.appView = appView;
     helper = new InterfaceDesugaringSyntheticHelper(appView);
-    // Avoid the computation outside L8 since it is not needed.
-    emulatedInterfacesHierarchy =
-        appView.options().isDesugaredLibraryCompilation()
-            ? processEmulatedInterfaceHierarchy()
-            : Collections.emptyMap();
-  }
-
-  private Map<DexType, List<DexType>> processEmulatedInterfaceHierarchy() {
-    Map<DexType, List<DexType>> emulatedInterfacesHierarchy = new IdentityHashMap<>();
-    Set<DexType> processed = Sets.newIdentityHashSet();
-    ArrayList<DexType> emulatedInterfacesSorted = new ArrayList<>(helper.getEmulatedInterfaces());
-    emulatedInterfacesSorted.sort(DexType::compareTo);
-    for (DexType interfaceType : emulatedInterfacesSorted) {
-      processEmulatedInterfaceHierarchy(interfaceType, processed, emulatedInterfacesHierarchy);
-    }
-    return emulatedInterfacesHierarchy;
-  }
-
-  private void processEmulatedInterfaceHierarchy(
-      DexType interfaceType,
-      Set<DexType> processed,
-      Map<DexType, List<DexType>> emulatedInterfacesHierarchy) {
-    if (processed.contains(interfaceType)) {
-      return;
-    }
-    emulatedInterfacesHierarchy.put(interfaceType, new ArrayList<>());
-    processed.add(interfaceType);
-    DexClass theInterface = appView.definitionFor(interfaceType);
-    if (theInterface == null) {
-      return;
-    }
-    LinkedList<DexType> workList = new LinkedList<>(Arrays.asList(theInterface.interfaces.values));
-    while (!workList.isEmpty()) {
-      DexType next = workList.removeLast();
-      if (helper.isEmulatedInterface(next)) {
-        processEmulatedInterfaceHierarchy(next, processed, emulatedInterfacesHierarchy);
-        emulatedInterfacesHierarchy.get(next).add(interfaceType);
-        DexClass nextClass = appView.definitionFor(next);
-        if (nextClass != null) {
-          workList.addAll(Arrays.asList(nextClass.interfaces.values));
-        }
-      }
-    }
   }
 
   DexProgramClass synthesizeProgramEmulatedInterface(
       DexProgramClass emulatedInterface,
+      EmulatedInterfaceDescriptor emulatedInterfaceDescriptor,
       L8ProgramEmulatedInterfaceSynthesizerEventConsumer eventConsumer) {
     return appView
         .getSyntheticItems()
@@ -108,20 +53,26 @@
             SyntheticNaming.SyntheticKind.EMULATED_INTERFACE_CLASS,
             emulatedInterface,
             appView,
-            builder -> synthesizeEmulateInterfaceMethods(emulatedInterface, builder),
+            builder ->
+                synthesizeEmulateInterfaceMethods(
+                    emulatedInterface, emulatedInterfaceDescriptor, builder),
             eventConsumer::acceptProgramEmulatedInterface);
   }
 
   private void synthesizeEmulateInterfaceMethods(
-      DexProgramClass emulatedInterface, SyntheticProgramClassBuilder builder) {
-    assert helper.isEmulatedInterface(emulatedInterface.type);
+      DexProgramClass emulatedInterface,
+      EmulatedInterfaceDescriptor emulatedInterfaceDescriptor,
+      SyntheticProgramClassBuilder builder) {
     emulatedInterface.forEachProgramVirtualMethodMatching(
-        DexEncodedMethod::isDefaultMethod,
+        m -> emulatedInterfaceDescriptor.getEmulatedMethods().containsKey(m.getReference()),
         method ->
             builder.addMethod(
                 methodBuilder ->
                     synthesizeEmulatedInterfaceMethod(
-                        method, emulatedInterface, builder.getType(), methodBuilder)));
+                        method,
+                        emulatedInterfaceDescriptor.getEmulatedMethods().get(method.getReference()),
+                        builder.getType(),
+                        methodBuilder)));
   }
 
   private DexMethod emulatedMethod(DerivedMethod method, DexType holder) {
@@ -137,24 +88,17 @@
 
   private void synthesizeEmulatedInterfaceMethod(
       ProgramMethod method,
-      DexProgramClass theInterface,
+      EmulatedDispatchMethodDescriptor descriptor,
       DexType dispatchType,
       SyntheticMethodBuilder methodBuilder) {
     assert !method.getDefinition().isStatic();
-    if (appView.options().testing.machineDesugaredLibrarySpecification != null) {
-      synthesizeEmulatedInterfaceMethodFromMachineSpecification(
-          method, theInterface, dispatchType, methodBuilder);
-      return;
-    }
-    DexMethod emulatedMethod = helper.emulateInterfaceLibraryMethod(method);
-    DexMethod itfMethod =
-        method
-            .getReference()
-            .withHolder(helper.getEmulatedInterface(theInterface.type), appView.dexItemFactory());
+    DexMethod emulatedMethod =
+        helper.emulatedInterfaceDispatchMethod(
+            descriptor.getEmulatedDispatchMethod(), dispatchType);
+    DexMethod itfMethod = helper.emulatedInterfaceInterfaceMethod(descriptor.getInterfaceMethod());
     DexMethod companionMethod =
-        helper.ensureDefaultAsMethodOfProgramCompanionClassStub(method).getReference();
-    LinkedHashMap<DexType, DexMethod> extraDispatchCases =
-        getDispatchCases(method, theInterface, companionMethod);
+        helper.ensureEmulatedInterfaceForwardingMethod(descriptor.getForwardingMethod());
+    LinkedHashMap<DexType, DexMethod> extraDispatchCases = resolveDispatchCases(descriptor);
     methodBuilder
         .setName(emulatedMethod.getName())
         .setProto(emulatedMethod.getProto())
@@ -170,162 +114,40 @@
                     .generateCfCode());
   }
 
-  private void synthesizeEmulatedInterfaceMethodFromMachineSpecification(
-      ProgramMethod method,
-      DexProgramClass theInterface,
-      DexType dispatchType,
-      SyntheticMethodBuilder methodBuilder) {
-    EmulatedInterfaceDescriptor emulatedInterfaceDescriptor =
-        appView
-            .options()
-            .testing
-            .machineDesugaredLibrarySpecification
-            .getRewritingFlags()
-            .getEmulatedInterfaces()
-            .get(theInterface.type);
-    EmulatedDispatchMethodDescriptor descriptor =
-        emulatedInterfaceDescriptor.getEmulatedMethods().get(method.getReference());
-    DexMethod emulatedMethod = emulatedMethod(descriptor.getEmulatedDispatchMethod(), dispatchType);
-    DexMethod itfMethod = interfaceMethod(descriptor.getInterfaceMethod());
-    // TODO(b/184026720): Adapt to use the forwarding method.
-    DerivedMethod forwardingMethod = descriptor.getForwardingMethod();
-    assert forwardingMethod.getHolderKind() == SyntheticKind.COMPANION_CLASS;
-    assert forwardingMethod.getMethod() == method.getReference();
-    DexMethod companionMethod =
-        helper.ensureDefaultAsMethodOfProgramCompanionClassStub(method).getReference();
-    LinkedHashMap<DexType, DexMethod> extraDispatchCases = resolveDispatchCases(descriptor);
-    methodBuilder
-        .setName(descriptor.getEmulatedDispatchMethod().getName())
-        .setProto(descriptor.getEmulatedDispatchMethod().getProto())
-        .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
-        .setCode(
-            emulatedInterfaceMethod ->
-                new EmulateDispatchSyntheticCfCodeProvider(
-                        emulatedMethod.getHolderType(),
-                        companionMethod,
-                        itfMethod,
-                        extraDispatchCases,
-                        appView)
-                    .generateCfCode());
-  }
-
   private LinkedHashMap<DexType, DexMethod> resolveDispatchCases(
       EmulatedDispatchMethodDescriptor descriptor) {
     LinkedHashMap<DexType, DexMethod> extraDispatchCases = new LinkedHashMap<>();
     descriptor
         .getDispatchCases()
         .forEach(
-            (type, derivedMethod) -> {
-              DexMethod caseMethod;
-              if (derivedMethod.getHolderKind() == null) {
-                caseMethod = derivedMethod.getMethod();
-              } else {
-                assert derivedMethod.getHolderKind() == SyntheticKind.COMPANION_CLASS;
-                ProgramMethod resolvedProgramMethod =
-                    appView
-                        .appInfoForDesugaring()
-                        .resolveMethod(derivedMethod.getMethod(), true)
-                        .getResolvedProgramMethod();
-                caseMethod =
-                    helper
-                        .ensureDefaultAsMethodOfProgramCompanionClassStub(resolvedProgramMethod)
-                        .getReference();
-              }
-              extraDispatchCases.put(type, caseMethod);
-            });
+            (type, derivedMethod) ->
+                extraDispatchCases.put(
+                    type, helper.ensureEmulatedInterfaceForwardingMethod(derivedMethod)));
     return extraDispatchCases;
   }
 
-  private LinkedHashMap<DexType, DexMethod> getDispatchCases(
-      ProgramMethod method, DexProgramClass theInterface, DexMethod companionMethod) {
-    // To properly emulate the library interface call, we need to compute the interfaces
-    // inheriting from the interface and manually implement the dispatch with instance of.
-    // The list guarantees that an interface is always after interfaces it extends,
-    // hence reverse iteration.
-    List<DexType> subInterfaces = emulatedInterfacesHierarchy.get(theInterface.type);
-    LinkedHashMap<DexType, DexMethod> extraDispatchCases = new LinkedHashMap<>();
-    // In practice, there is usually a single case (except for tests),
-    // so we do not bother to make the following loop more clever.
-    Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
-        appView.options().desugaredLibrarySpecification.getRetargetCoreLibMember();
-    for (DexString methodName : retargetCoreLibMember.keySet()) {
-      if (method.getName() == methodName) {
-        for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
-          DexClass inClass = appView.definitionFor(inType);
-          if (inClass != null && implementsInterface(inClass, theInterface.type)) {
-            extraDispatchCases.put(
-                inType,
-                appView
-                    .dexItemFactory()
-                    .createMethod(
-                        retargetCoreLibMember.get(methodName).get(inType),
-                        appView
-                            .dexItemFactory()
-                            .protoWithDifferentFirstParameter(companionMethod.proto, inType),
-                        method.getName()));
-          }
-        }
-      }
-    }
-    if (subInterfaces != null) {
-      for (int i = subInterfaces.size() - 1; i >= 0; i--) {
-        DexClass subInterfaceClass = appView.definitionFor(subInterfaces.get(i));
-        assert subInterfaceClass != null;
-        assert subInterfaceClass.isProgramClass();
-        // Else computation of subInterface would have failed.
-        // if the method is implemented, extra dispatch is required.
-        DexEncodedMethod result = subInterfaceClass.lookupVirtualMethod(method.getReference());
-        if (result != null && !result.isAbstract()) {
-          assert result.isDefaultMethod();
-          DexMethod forward =
-              helper
-                  .ensureDefaultAsMethodOfProgramCompanionClassStub(
-                      new ProgramMethod(subInterfaceClass.asProgramClass(), result))
-                  .getReference();
-          extraDispatchCases.put(subInterfaceClass.type, forward);
-        }
-      }
-    } else {
-      assert extraDispatchCases.size() <= 1;
-    }
-    return extraDispatchCases;
-  }
-
-  private boolean implementsInterface(DexClass clazz, DexType interfaceType) {
-    LinkedList<DexType> workList = new LinkedList<>(Arrays.asList(clazz.interfaces.values));
-    while (!workList.isEmpty()) {
-      DexType next = workList.removeLast();
-      if (interfaceType == next) {
-        return true;
-      }
-      DexClass nextClass = appView.definitionFor(next);
-      if (nextClass != null) {
-        workList.addAll(Arrays.asList(nextClass.interfaces.values));
-      }
-    }
-    return false;
-  }
-
   @Override
   public void synthesizeClasses(CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
     assert appView.options().isDesugaredLibraryCompilation();
-    for (DexType emulatedInterfaceType : helper.getEmulatedInterfaces()) {
-      DexClass emulatedInterfaceClazz = appView.definitionFor(emulatedInterfaceType);
-      if (emulatedInterfaceClazz == null || !emulatedInterfaceClazz.isProgramClass()) {
-        warnMissingEmulatedInterface(emulatedInterfaceType);
-        continue;
-      }
-      DexProgramClass emulatedInterface = emulatedInterfaceClazz.asProgramClass();
-      assert emulatedInterface != null;
-      if (!appView.isAlreadyLibraryDesugared(emulatedInterface)
-          && needsEmulateInterfaceLibrary(emulatedInterface)) {
-        synthesizeProgramEmulatedInterface(emulatedInterface, eventConsumer);
-      }
-    }
-  }
-
-  private boolean needsEmulateInterfaceLibrary(DexClass emulatedInterface) {
-    return Iterables.any(emulatedInterface.methods(), DexEncodedMethod::isDefaultMethod);
+    appView
+        .options()
+        .machineDesugaredLibrarySpecification
+        .getEmulatedInterfaces()
+        .forEach(
+            (emulatedInterfaceType, emulatedInterfaceDescriptor) -> {
+              DexClass emulatedInterfaceClazz = appView.definitionFor(emulatedInterfaceType);
+              if (emulatedInterfaceClazz == null || !emulatedInterfaceClazz.isProgramClass()) {
+                warnMissingEmulatedInterface(emulatedInterfaceType);
+                return;
+              }
+              DexProgramClass emulatedInterface = emulatedInterfaceClazz.asProgramClass();
+              assert emulatedInterface != null;
+              if (!appView.isAlreadyLibraryDesugared(emulatedInterface)
+                  && !emulatedInterfaceDescriptor.getEmulatedMethods().isEmpty()) {
+                synthesizeProgramEmulatedInterface(
+                    emulatedInterface, emulatedInterfaceDescriptor, eventConsumer);
+              }
+            });
   }
 
   private void warnMissingEmulatedInterface(DexType interfaceType) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java
index 4689b6d..d8a62a0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java
@@ -165,7 +165,7 @@
           appView
               .appInfo()
               .resolveField(appView.graphLens().getRenamedFieldSignature(fields[index]), context);
-      if (resolution.isSuccessfulResolution()) {
+      if (resolution.isSingleFieldResolutionResult()) {
         newInValues.add(inValues.get(index));
       }
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
index 30ea9f8..97c8d47 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
@@ -10,7 +10,7 @@
 import com.android.tools.r8.graph.DexClassAndField;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
+import com.android.tools.r8.graph.FieldResolutionResult.SingleFieldResolutionResult;
 import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
@@ -285,8 +285,8 @@
       return false;
     }
 
-    SuccessfulFieldResolutionResult resolutionResult =
-        appView.appInfo().resolveField(fieldGet.getField()).asSuccessfulResolution();
+    SingleFieldResolutionResult resolutionResult =
+        appView.appInfo().resolveField(fieldGet.getField()).asSingleFieldResolutionResult();
     if (resolutionResult == null) {
       return false;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 7d12496..cd28d57 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -26,7 +26,7 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
+import com.android.tools.r8.graph.FieldResolutionResult.SingleProgramFieldResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerUtils;
 import com.android.tools.r8.ir.analysis.equivalence.BasicBlockBehavioralSubsumption;
@@ -70,6 +70,7 @@
 import com.android.tools.r8.ir.code.IntSwitch;
 import com.android.tools.r8.ir.code.Invoke;
 import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeInterface;
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
 import com.android.tools.r8.ir.code.InvokeNewArray;
@@ -286,10 +287,8 @@
 
           Throw throwInstruction = valueIsNullTarget.exit().asThrow();
           Value exceptionValue = throwInstruction.exception().getAliasedValue();
-          Value message;
-          if (exceptionValue.isConstZero()) {
-            message = null;
-          } else if (exceptionValue.isDefinedByInstructionSatisfying(Instruction::isNewInstance)) {
+          if (!exceptionValue.isConstZero()
+              && exceptionValue.isDefinedByInstructionSatisfying(Instruction::isNewInstance)) {
             NewInstance newInstance = exceptionValue.definition.asNewInstance();
             if (newInstance.clazz != dexItemFactory.npeType) {
               continue;
@@ -301,18 +300,10 @@
             if (constructorCall == null) {
               continue;
             }
-            DexMethod invokedMethod = constructorCall.getInvokedMethod();
-            if (invokedMethod == dexItemFactory.npeMethods.init) {
-              message = null;
-            } else if (invokedMethod == dexItemFactory.npeMethods.initWithMessage) {
-              if (!appView.options().canUseJavaUtilObjectsRequireNonNull()) {
-                continue;
-              }
-              message = constructorCall.getArgument(1);
-            } else {
+            if (constructorCall.getInvokedMethod() != dexItemFactory.npeMethods.init) {
               continue;
             }
-          } else {
+          } else if (!exceptionValue.isConstZero()) {
             continue;
           }
 
@@ -327,26 +318,12 @@
             continue;
           }
 
-          if (message != null) {
-            Instruction definition = message.definition;
-            if (message.definition.getBlock() == valueIsNullTarget) {
-              it.previous();
-              Instruction entry;
-              do {
-                entry = valueIsNullTarget.getInstructions().removeFirst();
-                it.add(entry);
-              } while (entry != definition);
-              it.next();
-            }
-          }
-
-          rewriteIfToRequireNonNull(
+          insertNotNullCheck(
               block,
               it,
               ifInstruction,
               ifInstruction.targetFromCondition(1),
               valueIsNullTarget,
-              message,
               throwInstruction.getPosition());
           shouldRemoveUnreachableBlocks = true;
         }
@@ -1207,8 +1184,8 @@
       return false;
     }
     InstanceGet instanceGet = switchValue.getDefinition().asInstanceGet();
-    SuccessfulFieldResolutionResult resolutionResult =
-        appInfo.resolveField(instanceGet.getField()).asSuccessfulResolution();
+    SingleProgramFieldResolutionResult resolutionResult =
+        appInfo.resolveField(instanceGet.getField()).asSingleProgramFieldResolutionResult();
     if (resolutionResult == null) {
       return false;
     }
@@ -3363,33 +3340,19 @@
     assert block.exit().asGoto().getTarget() == target;
   }
 
-  private void rewriteIfToRequireNonNull(
+  private void insertNotNullCheck(
       BasicBlock block,
       InstructionListIterator iterator,
       If theIf,
       BasicBlock target,
       BasicBlock deadTarget,
-      Value message,
       Position position) {
     deadTarget.unlinkSinglePredecessorSiblingsAllowed();
     assert theIf == block.exit();
     iterator.previous();
     Instruction instruction;
-    if (appView.options().canUseJavaUtilObjectsRequireNonNull()) {
-      if (message != null) {
-        DexMethod requireNonNullMethod =
-            appView.dexItemFactory().objectsMethods.requireNonNullWithMessage;
-        instruction =
-            new InvokeStatic(requireNonNullMethod, null, ImmutableList.of(theIf.lhs(), message));
-      } else {
-        DexMethod requireNonNullMethod = appView.dexItemFactory().objectsMethods.requireNonNull;
-        instruction = new InvokeStatic(requireNonNullMethod, null, ImmutableList.of(theIf.lhs()));
-      }
-    } else {
-      assert message == null;
-      DexMethod getClassMethod = appView.dexItemFactory().objectMembers.getClass;
-      instruction = new InvokeVirtual(getClassMethod, null, ImmutableList.of(theIf.lhs()));
-    }
+    DexMethod getClassMethod = appView.dexItemFactory().objectMembers.getClass;
+    instruction = new InvokeVirtual(getClassMethod, null, ImmutableList.of(theIf.lhs()));
     instruction.setPosition(position);
     iterator.add(instruction);
     iterator.next();
@@ -3811,6 +3774,26 @@
     }
   }
 
+  // The javac fix for JDK-8272564 has to be rewritten back to invoke-virtual on Object if the
+  // method with an Object signature is not defined on the interface. See
+  // https://bugs.openjdk.java.net/browse/JDK-8272564
+  public static void rewriteJdk8272564Fix(IRCode code, AppView<?> appView) {
+    DexItemFactory dexItemFactory = appView.dexItemFactory();
+    InstructionListIterator it = code.instructionListIterator();
+    while (it.hasNext()) {
+      Instruction instruction = it.next();
+      if (instruction.isInvokeInterface()) {
+        InvokeInterface invoke = instruction.asInvokeInterface();
+        DexMethod method = invoke.getInvokedMethod();
+        DexMethod objectMember = dexItemFactory.objectMembers.matchingPublicObjectMember(method);
+        if (objectMember != null && appView.definitionFor(method) == null) {
+          it.replaceCurrentInstruction(
+              new InvokeVirtual(objectMember, invoke.outValue(), invoke.arguments()));
+        }
+      }
+    }
+  }
+
   private static NewInstance findNewInstance(Phi phi) {
     Set<Phi> seen = Sets.newIdentityHashSet();
     Set<Value> values = Sets.newIdentityHashSet();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 8182229..4ad92d7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -395,7 +395,7 @@
     }
     if (appView.canUseInitClass()
         && inlinerOptions.enableInliningOfInvokesWithClassInitializationSideEffects) {
-      action.setShouldSynthesizeInitClass();
+      action.setShouldEnsureStaticInitialization();
       return action;
     }
     whyAreYouNotInliningReporter.reportMustTriggerClassInitialization();
@@ -409,17 +409,12 @@
       ClassInitializationAnalysis classInitializationAnalysis) {
     // Only proceed with inlining a static invoke if:
     // - the holder for the target is a subtype of the holder for the method,
-    // - the target method always triggers class initialization of its holder before any other side
-    //   effect (hence preserving class initialization semantics),
     // - the current method has already triggered the holder for the target method to be
     //   initialized, or
     // - there is no non-trivial class initializer.
     if (appView.appInfo().isSubtype(context.getHolderType(), target.getHolderType())) {
       return true;
     }
-    if (target.getDefinition().getOptimizationInfo().triggersClassInitBeforeAnySideEffect()) {
-      return true;
-    }
     if (!context.getDefinition().isStatic()) {
       boolean targetIsGuaranteedToBeInitialized =
           appView.withInitializedClassesInInstanceMethods(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index fba3991..c458574 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -39,7 +39,6 @@
 import com.android.tools.r8.ir.code.InstructionListIterator;
 import com.android.tools.r8.ir.code.Invoke;
 import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.InvokeVirtual;
 import com.android.tools.r8.ir.code.Monitor;
 import com.android.tools.r8.ir.code.MoveException;
@@ -515,7 +514,7 @@
     public final Invoke invoke;
     final Reason reason;
 
-    private boolean shouldSynthesizeInitClass;
+    private boolean shouldEnsureStaticInitialization;
 
     private DexProgramClass downcastClass;
 
@@ -538,13 +537,14 @@
       this.downcastClass = downcastClass;
     }
 
-    void setShouldSynthesizeInitClass() {
-      shouldSynthesizeInitClass = true;
+    void setShouldEnsureStaticInitialization() {
+      shouldEnsureStaticInitialization = true;
     }
 
     InlineeWithReason buildInliningIR(
         AppView<AppInfoWithLiveness> appView,
         InvokeMethod invoke,
+        ProgramMethod context,
         InliningIRProvider inliningIRProvider,
         LensCodeRewriter lensCodeRewriter) {
       DexItemFactory dexItemFactory = appView.dexItemFactory();
@@ -555,8 +555,13 @@
 
       // Insert a init class instruction if this is needed to preserve class initialization
       // semantics.
-      if (shouldSynthesizeInitClass) {
-        synthesizeInitClass(code);
+      if (shouldEnsureStaticInitialization) {
+        handleSimpleEffectAnalysisResult(
+            SimpleDominatingEffectAnalysis.triggersClassInitializationBeforeAnyStaticRead(
+                appView, code, context),
+            code.entryBlock(),
+            ConsumerUtils.emptyConsumer(),
+            failingBlock -> synthesizeInitClass(code, failingBlock));
       }
 
       // Insert a null check if this is needed to preserve the implicit null check for the receiver.
@@ -573,19 +578,11 @@
       if (invoke.isInvokeMethodWithReceiver()
           && invoke.asInvokeMethodWithReceiver().getReceiver().isMaybeNull()
           && !isSynthesizingNullCheckForReceiverUsingMonitorEnter) {
-        SimpleEffectAnalysisResult checksReceiverBeingNull =
-            canInlineWithoutSynthesizingNullCheckForReceiver(appView, code);
-        if (checksReceiverBeingNull.isNotSatisfied()
-            || (checksReceiverBeingNull.isPartial()
-                && checksReceiverBeingNull.topMostNotSatisfiedBlockSize() > 1)) {
-          synthesizeNullCheckForReceiver(appView, code, invoke, code.entryBlock());
-        } else {
-          checksReceiverBeingNull.forEachSatisfyingInstruction(
-              this::setRemoveInnerFramePositionForReceiverUse);
-          // Also add a null check on failing paths
-          checksReceiverBeingNull.forEachTopMostNotSatisfiedBlock(
-              block -> synthesizeNullCheckForReceiver(appView, code, invoke, block));
-        }
+        handleSimpleEffectAnalysisResult(
+            canInlineWithoutSynthesizingNullCheckForReceiver(appView, code),
+            code.entryBlock(),
+            this::setRemoveInnerFramePositionForReceiverUse,
+            failingBlock -> synthesizeNullCheckForReceiver(appView, code, invoke, failingBlock));
       }
       // Insert monitor-enter and monitor-exit instructions if the method is synchronized.
       if (shouldSynthesizeMonitorEnterExit) {
@@ -711,12 +708,32 @@
       return new InlineeWithReason(code, reason);
     }
 
-    private void synthesizeInitClass(IRCode code) {
-      List<Value> arguments = code.collectArguments();
-      BasicBlock block = code.entryBlock();
+    private void handleSimpleEffectAnalysisResult(
+        SimpleEffectAnalysisResult result,
+        BasicBlock entryBlock,
+        Consumer<Instruction> satisfyingInstructionConsumer,
+        Consumer<BasicBlock> failingPathConsumer) {
+      List<BasicBlock> topmostNotSatisfiedBlocks = result.getTopmostNotSatisfiedBlocks();
+      if (result.isNotSatisfied()
+          // We should only handle partial results if the number of failing paths are small (1) and
+          // if the failing blocks that root the failing paths do not have catch handlers.
+          || (result.isPartial() && topmostNotSatisfiedBlocks.size() > 1)
+          || (result.isPartial() && topmostNotSatisfiedBlocks.get(0).hasCatchHandlers())) {
+        failingPathConsumer.accept(entryBlock);
+      } else {
+        result.forEachSatisfyingInstruction(satisfyingInstructionConsumer);
+        topmostNotSatisfiedBlocks.forEach(failingPathConsumer);
+      }
+    }
+
+    private void synthesizeInitClass(IRCode code, BasicBlock block) {
       // Insert a new block between the last argument instruction and the first actual instruction
-      // of the method.
-      BasicBlock initClassBlock = block.listIterator(code, arguments.size()).split(code, 0, null);
+      // of the method, or the first instruction if not entry block.
+      assert !block.hasCatchHandlers();
+      BasicBlock initClassBlock =
+          block
+              .listIterator(code, block.isEntry() ? code.collectArguments().size() : 0)
+              .split(code, 0, null);
       assert !initClassBlock.hasCatchHandlers();
 
       InstructionListIterator iterator = initClassBlock.listIterator(code);
@@ -738,13 +755,8 @@
 
         InstructionListIterator iterator = throwBlock.listIterator(code);
         iterator.setInsertionPosition(invoke.getPosition());
-        if (appView.options().canUseJavaUtilObjectsRequireNonNull()) {
-          DexMethod requireNonNullMethod = appView.dexItemFactory().objectsMethods.requireNonNull;
-          iterator.add(new InvokeStatic(requireNonNullMethod, null, ImmutableList.of(receiver)));
-        } else {
-          DexMethod getClassMethod = appView.dexItemFactory().objectMembers.getClass;
-          iterator.add(new InvokeVirtual(getClassMethod, null, ImmutableList.of(receiver)));
-        }
+        DexMethod getClassMethod = appView.dexItemFactory().objectMembers.getClass;
+        iterator.add(new InvokeVirtual(getClassMethod, null, ImmutableList.of(receiver)));
       } else {
         assert false : "Unable to synthesize a null check for the receiver";
       }
@@ -1014,7 +1026,8 @@
           }
 
           InlineeWithReason inlinee =
-              action.buildInliningIR(appView, invoke, inliningIRProvider, lensCodeRewriter);
+              action.buildInliningIR(
+                  appView, invoke, context, inliningIRProvider, lensCodeRewriter);
           if (strategy.willExceedBudget(
               code, invoke, inlinee, block, whyAreYouNotInliningReporter)) {
             assert whyAreYouNotInliningReporter.unsetReasonHasBeenReportedFlag();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 4807cda..b4584c4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -14,7 +14,7 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
+import com.android.tools.r8.graph.FieldResolutionResult.SingleFieldResolutionResult;
 import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
@@ -364,8 +364,8 @@
     DexField field = current.getField();
 
     // TODO(b/123857022): Should be able to use definitionFor().
-    SuccessfulFieldResolutionResult resolutionResult =
-        appView.appInfo().resolveField(field).asSuccessfulResolution();
+    SingleFieldResolutionResult resolutionResult =
+        appView.appInfo().resolveField(field).asSingleFieldResolutionResult();
     if (resolutionResult == null) {
       boolean replaceCurrentInstructionWithConstNull =
           appView.withGeneratedExtensionRegistryShrinker(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
index bcef49c..715dfdd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
@@ -13,7 +13,7 @@
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
+import com.android.tools.r8.graph.FieldResolutionResult.SingleFieldResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
 import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
@@ -40,7 +40,6 @@
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
-import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.Sets;
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
@@ -92,9 +91,7 @@
   }
 
   public static boolean shouldRun(AppView<?> appView, IRCode code) {
-    InternalOptions options = appView.options();
-    return options.enableRedundantFieldLoadElimination
-        && !options.debug
+    return appView.options().enableRedundantFieldLoadElimination
         && (code.metadata().mayHaveArrayGet()
             || code.metadata().mayHaveFieldInstruction()
             || code.metadata().mayHaveInitClass());
@@ -287,8 +284,8 @@
 
   private DexClassAndField resolveField(DexField field) {
     if (appView.enableWholeProgramOptimizations()) {
-      SuccessfulFieldResolutionResult resolutionResult =
-          appView.appInfo().withLiveness().resolveField(field).asSuccessfulResolution();
+      SingleFieldResolutionResult resolutionResult =
+          appView.appInfo().withLiveness().resolveField(field).asSingleFieldResolutionResult();
       return resolutionResult != null ? resolutionResult.getResolutionPair() : null;
     }
     if (field.getHolderType() == method.getHolderType()) {
@@ -556,6 +553,17 @@
   }
 
   private void handleArrayGet(InstructionListIterator it, ArrayGet arrayGet) {
+    if (arrayGet.array().hasLocalInfo()) {
+      // The array may be modified through the debugger. Therefore subsequent reads of the same
+      // array slot may not read this local.
+      return;
+    }
+    if (arrayGet.outValue().hasLocalInfo()) {
+      // This local may be modified through the debugger. Therefore subsequent reads of the same
+      // array slot may not read this local.
+      return;
+    }
+
     Value array = arrayGet.array().getAliasedValue();
     Value index = arrayGet.index().getAliasedValue();
     ArraySlot arraySlot = ArraySlot.create(array, index, arrayGet.getMemberType());
@@ -593,6 +601,11 @@
       InstanceGet instanceGet,
       DexClassAndField field,
       AssumeRemover assumeRemover) {
+    if (instanceGet.outValue().hasLocalInfo()) {
+      clearMostRecentInstanceFieldWrite(instanceGet, field);
+      return;
+    }
+
     Value object = instanceGet.object().getAliasedValue();
     FieldAndObject fieldAndObject = new FieldAndObject(field.getReference(), object);
     FieldValue replacement = activeState.getInstanceFieldValue(fieldAndObject);
@@ -686,6 +699,12 @@
       AssumeRemover assumeRemover) {
     markClassAsInitialized(field.getHolderType());
 
+    if (staticGet.outValue().hasLocalInfo()) {
+      killNonFinalActiveFields(staticGet);
+      clearMostRecentStaticFieldWrite(staticGet, field);
+      return;
+    }
+
     FieldValue replacement = activeState.getStaticFieldValue(field.getReference());
     if (replacement != null) {
       markAssumeDynamicTypeUsersForRemoval(staticGet, replacement, assumeRemover);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java
index 0f68783..ddbe46b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java
@@ -188,12 +188,8 @@
       satisfyingInstructions.forEach(instructionConsumer);
     }
 
-    public void forEachTopMostNotSatisfiedBlock(Consumer<BasicBlock> blockConsumer) {
-      topmostNotSatisfiedBlocks.forEach(blockConsumer);
-    }
-
-    public int topMostNotSatisfiedBlockSize() {
-      return topmostNotSatisfiedBlocks.size();
+    public List<BasicBlock> getTopmostNotSatisfiedBlocks() {
+      return topmostNotSatisfiedBlocks;
     }
 
     public static SimpleEffectAnalysisResultBuilder builder() {
@@ -337,14 +333,13 @@
   }
 
   public static SimpleEffectAnalysisResult triggersClassInitializationBeforeAnyStaticRead(
-      AppView<AppInfoWithLiveness> appView, IRCode code) {
+      AppView<AppInfoWithLiveness> appView, IRCode code, ProgramMethod context) {
     assert code.context().getDefinition().isStatic();
-    ProgramMethod context = code.context();
     return run(
         code,
         instruction -> {
           if (instruction.definitelyTriggersClassInitialization(
-              context.getHolderType(),
+              code.context().getHolderType(),
               context,
               appView,
               DIRECTLY,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 0a0bed9..e6494f3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -19,7 +19,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
+import com.android.tools.r8.graph.FieldResolutionResult.SingleProgramFieldResolutionResult;
 import com.android.tools.r8.graph.LibraryMethod;
 import com.android.tools.r8.graph.MethodResolutionResult;
 import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
@@ -167,8 +167,8 @@
     assert root.isStaticGet();
 
     StaticGet staticGet = root.asStaticGet();
-    SuccessfulFieldResolutionResult fieldResolutionResult =
-        appView.appInfo().resolveField(staticGet.getField()).asSuccessfulResolution();
+    SingleProgramFieldResolutionResult fieldResolutionResult =
+        appView.appInfo().resolveField(staticGet.getField()).asSingleProgramFieldResolutionResult();
     if (fieldResolutionResult == null) {
       return EligibilityStatus.NOT_ELIGIBLE;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
index 17d5658..152c212 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
@@ -190,7 +190,7 @@
     // Instance field reads are OK, as long as the field resolves, since the class inliner will
     // just replace the field read by the value of the field.
     FieldResolutionResult resolutionResult = appView.appInfo().resolveField(instanceGet.getField());
-    if (resolutionResult.isSuccessfulResolution()) {
+    if (resolutionResult.isSingleFieldResolutionResult()) {
       // Record that the field is read from the parameter. For class inlining of singletons, this
       // parameter is only eligible for class inlining if the singleton's field value is known.
       return state.rebuildParameter(
@@ -217,7 +217,7 @@
     }
 
     FieldResolutionResult resolutionResult = appView.appInfo().resolveField(instancePut.getField());
-    if (resolutionResult.isSuccessfulResolution()) {
+    if (resolutionResult.isSingleFieldResolutionResult()) {
       return state.rebuildParameter(objectRoot, (context, usage) -> usage.setParameterMutated());
     } else {
       return state.abandonClassInliningInCurrentContexts(objectRoot);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
index c8c21eb..40ccc32 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -376,7 +376,7 @@
     DexProgramClass enumClass = getEnumUnboxingCandidateOrNull(field.holder);
     if (enumClass != null) {
       FieldResolutionResult resolutionResult = appView.appInfo().resolveField(field, context);
-      if (resolutionResult.isSuccessfulResolution()) {
+      if (resolutionResult.isSingleFieldResolutionResult()) {
         eligibleEnums.add(enumClass.getType());
       } else {
         markEnumAsUnboxable(Reason.UNRESOLVABLE_FIELD, enumClass);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index 0214979..c054329 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -18,7 +18,6 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexValue;
-import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
 import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ProgramField;
@@ -398,11 +397,9 @@
             continue;
           }
 
-          SuccessfulFieldResolutionResult resolutionResult =
-              appView.appInfo().resolveField(rewrittenField).asSuccessfulResolution();
-          if (resolutionResult != null
-              && resolutionResult.getResolvedHolder().isProgramClass()
-              && isPrunedAfterEnumUnboxing(resolutionResult.getProgramField(), enumData)) {
+          ProgramField programField =
+              appView.appInfo().resolveField(rewrittenField).getSingleProgramField();
+          if (programField != null && isPrunedAfterEnumUnboxing(programField, enumData)) {
             instructionIterator.removeOrReplaceByDebugLocalRead();
           }
         }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index f6aa6ad..9faaebc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -172,11 +172,6 @@
   }
 
   @Override
-  public boolean triggersClassInitBeforeAnySideEffect() {
-    return UNKNOWN_TRIGGERS_CLASS_INIT_BEFORE_ANY_SIDE_EFFECT;
-  }
-
-  @Override
   public boolean mayHaveSideEffects() {
     return UNKNOWN_MAY_HAVE_SIDE_EFFECTS;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index 473f718..ffcad46 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -91,8 +91,6 @@
 
   public abstract boolean forceInline();
 
-  public abstract boolean triggersClassInitBeforeAnySideEffect();
-
   public abstract boolean mayHaveSideEffects();
 
   /** Context sensitive version of {@link #mayHaveSideEffects()}. */
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index 594328f..29a843b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -87,8 +87,6 @@
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.conversion.MethodProcessor;
 import com.android.tools.r8.ir.optimize.DynamicTypeOptimization;
-import com.android.tools.r8.ir.optimize.SimpleDominatingEffectAnalysis;
-import com.android.tools.r8.ir.optimize.SimpleDominatingEffectAnalysis.SimpleEffectAnalysisResult;
 import com.android.tools.r8.ir.optimize.classinliner.analysis.ClassInlinerMethodConstraintAnalysis;
 import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
 import com.android.tools.r8.ir.optimize.enums.classification.EnumUnboxerMethodClassification;
@@ -142,9 +140,6 @@
     DexEncodedMethod definition = method.getDefinition();
     identifyBridgeInfo(definition, code, feedback, timing);
     analyzeReturns(code, feedback, methodProcessor, timing);
-    if (options.inlinerOptions().enableInlining) {
-      identifyInvokeSemanticsForInlining(definition, code, feedback, timing);
-    }
     if (options.enableClassInlining) {
       computeClassInlinerMethodConstraint(method, code, feedback, timing);
     }
@@ -529,25 +524,6 @@
     return false;
   }
 
-  private void identifyInvokeSemanticsForInlining(
-      DexEncodedMethod method, IRCode code, OptimizationFeedback feedback, Timing timing) {
-    timing.begin("Identify invoke semantics for inlining");
-    identifyInvokeSemanticsForInlining(method, code, feedback);
-    timing.end();
-  }
-
-  private void identifyInvokeSemanticsForInlining(
-      DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
-    if (method.isStatic()) {
-      // Identifies if the method preserves class initialization after inlining.
-      SimpleEffectAnalysisResult simpleEffectAnalysisResult =
-          SimpleDominatingEffectAnalysis.triggersClassInitializationBeforeAnyStaticRead(
-              appView, code);
-      feedback.markTriggerClassInitBeforeAnySideEffect(
-          method, simpleEffectAnalysisResult.isSatisfied());
-    }
-  }
-
   /**
    * Returns true if the given code unconditionally triggers an expected effect before anything
    * else, false otherwise.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
index 2cffcc0..b810885 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
@@ -92,7 +92,7 @@
   private static final int NEVER_RETURNS_NORMALLY_FLAG = 0x40;
   private static final int UNUSED_FLAG_2 = 0x80;
   private static final int UNUSED_FLAG_3 = 0x100;
-  private static final int TRIGGERS_CLASS_INIT_BEFORE_ANY_SIDE_EFFECT_FLAG = 0x200;
+  private static final int UNUSED_FLAG_4 = 0x200;
   private static final int INITIALIZER_ENABLING_JAVA_ASSERTIONS_FLAG = 0x400;
   private static final int REACHABILITY_SENSITIVE_FLAG = 0x800;
   private static final int RETURN_VALUE_HAS_BEEN_PROPAGATED_FLAG = 0x1000;
@@ -119,9 +119,7 @@
         BooleanUtils.intValue(defaultOptInfo.neverReturnsNormally()) * NEVER_RETURNS_NORMALLY_FLAG;
     defaultFlags |= 0 * UNUSED_FLAG_2;
     defaultFlags |= 0 * UNUSED_FLAG_3;
-    defaultFlags |=
-        BooleanUtils.intValue(defaultOptInfo.triggersClassInitBeforeAnySideEffect())
-            * TRIGGERS_CLASS_INIT_BEFORE_ANY_SIDE_EFFECT_FLAG;
+    defaultFlags |= 0 * UNUSED_FLAG_4;
     defaultFlags |=
         BooleanUtils.intValue(defaultOptInfo.isInitializerEnablingJavaVmAssertions())
             * INITIALIZER_ENABLING_JAVA_ASSERTIONS_FLAG;
@@ -497,11 +495,6 @@
   }
 
   @Override
-  public boolean triggersClassInitBeforeAnySideEffect() {
-    return isFlagSet(TRIGGERS_CLASS_INIT_BEFORE_ANY_SIDE_EFFECT_FLAG);
-  }
-
-  @Override
   public boolean mayHaveSideEffects() {
     return isFlagSet(MAY_HAVE_SIDE_EFFECT_FLAG);
   }
@@ -675,14 +668,6 @@
     }
   }
 
-  void markTriggerClassInitBeforeAnySideEffect(boolean mark) {
-    setFlag(TRIGGERS_CLASS_INIT_BEFORE_ANY_SIDE_EFFECT_FLAG, mark);
-  }
-
-  void unsetTriggerClassInitBeforeAnySideEffect() {
-    clearFlag(TRIGGERS_CLASS_INIT_BEFORE_ANY_SIDE_EFFECT_FLAG);
-  }
-
   // TODO(b/140214568): Should be package-private.
   public void markAsPropagated() {
     setFlag(RETURN_VALUE_HAS_BEEN_PROPAGATED_FLAG);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
index 4b6241a..65b9aaa 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -229,12 +229,6 @@
   }
 
   @Override
-  public synchronized void markTriggerClassInitBeforeAnySideEffect(
-      DexEncodedMethod method, boolean mark) {
-    getMethodOptimizationInfoForUpdating(method).markTriggerClassInitBeforeAnySideEffect(mark);
-  }
-
-  @Override
   public synchronized void setBridgeInfo(DexEncodedMethod method, BridgeInfo bridgeInfo) {
     getMethodOptimizationInfoForUpdating(method).setBridgeInfo(bridgeInfo);
   }
@@ -390,11 +384,6 @@
   }
 
   @Override
-  public synchronized void unsetTriggerClassInitBeforeAnySideEffect(ProgramMethod method) {
-    getMethodOptimizationInfoForUpdating(method).unsetTriggerClassInitBeforeAnySideEffect();
-  }
-
-  @Override
   public synchronized void unsetUnusedArguments(ProgramMethod method) {
     getMethodOptimizationInfoForUpdating(method).unsetUnusedArguments();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
index ac66916..d15bb00 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
@@ -94,9 +94,6 @@
   public void markProcessed(DexEncodedMethod method, ConstraintWithTarget state) {}
 
   @Override
-  public void markTriggerClassInitBeforeAnySideEffect(DexEncodedMethod method, boolean mark) {}
-
-  @Override
   public void setBridgeInfo(DexEncodedMethod method, BridgeInfo bridgeInfo) {}
 
   @Override
@@ -191,8 +188,5 @@
   public void unsetSimpleInliningConstraint(ProgramMethod method) {}
 
   @Override
-  public void unsetTriggerClassInitBeforeAnySideEffect(ProgramMethod method) {}
-
-  @Override
   public void unsetUnusedArguments(ProgramMethod method) {}
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index 1b54d1f..a9e3cba 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -137,11 +137,6 @@
     method.markProcessed(state);
   }
 
-  @Override
-  public void markTriggerClassInitBeforeAnySideEffect(DexEncodedMethod method, boolean mark) {
-    // Ignored.
-  }
-
   public void setArgumentInfos(ProgramMethod method, CallSiteOptimizationInfo argumentInfos) {
     method.getDefinition().getMutableOptimizationInfo().setArgumentInfos(argumentInfos);
   }
@@ -333,12 +328,6 @@
   }
 
   @Override
-  public void unsetTriggerClassInitBeforeAnySideEffect(ProgramMethod method) {
-    withMutableMethodOptimizationInfo(
-        method, MutableMethodOptimizationInfo::unsetTriggerClassInitBeforeAnySideEffect);
-  }
-
-  @Override
   public void unsetUnusedArguments(ProgramMethod method) {
     withMutableMethodOptimizationInfo(method, MutableMethodOptimizationInfo::unsetUnusedArguments);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
index dfe017a..6a20d01 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
@@ -120,7 +120,7 @@
 
       DexType returnType = forwardMethod.proto.returnType;
       DexType forwardMethodReturnType =
-          appView.rewritePrefix.hasRewrittenType(returnType, appView)
+          appView.typeRewriter.hasRewrittenType(returnType, appView)
               ? vivifiedTypeFor(returnType)
               : returnType;
 
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
index e95253c..2a3698b 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
@@ -7,7 +7,9 @@
 import static com.android.tools.r8.kotlin.KotlinClassMetadataReader.hasKotlinClassMetadataAnnotation;
 import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getNoKotlinInfo;
 
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClassResolutionResult;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedMember;
@@ -152,6 +154,11 @@
     }
 
     @Override
+    public ClassResolutionResult contextIndependentDefinitionForWithResolutionResult(DexType type) {
+      throw new Unreachable("Not yet used");
+    }
+
+    @Override
     public DexClass definitionFor(DexType type) {
       // TODO(b/157700128) Metadata cannot at this point keep anything alive. Therefore, if a type
       //  has been pruned it may still be referenced, so we do an early check here to ensure it will
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index ca54e1b..9a801b9 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -11,12 +11,13 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndField;
+import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.graph.ProgramOrClasspathClass;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
@@ -35,7 +36,7 @@
 
   private final AppView<AppInfoWithLiveness> appView;
   private final ClassNamingStrategy classNamingStrategy;
-  private final Iterable<? extends DexClass> classes;
+  private final Iterable<? extends ProgramOrClasspathClass> classes;
   private final Set<String> usedTypeNames = Sets.newHashSet();
   private final Map<DexType, DexString> renaming = Maps.newIdentityHashMap();
   private final Map<String, Namespace> states = new HashMap<>();
@@ -48,7 +49,7 @@
   ClassNameMinifier(
       AppView<AppInfoWithLiveness> appView,
       ClassNamingStrategy classNamingStrategy,
-      Iterable<? extends DexClass> classes) {
+      Iterable<? extends ProgramOrClasspathClass> classes) {
     this.appView = appView;
     this.classNamingStrategy = classNamingStrategy;
     this.classes = classes;
@@ -86,11 +87,11 @@
   ClassRenaming computeRenaming(Timing timing) {
     // Collect names we have to keep.
     timing.begin("reserve");
-    for (DexClass clazz : classes) {
-      DexString descriptor = classNamingStrategy.reservedDescriptor(clazz.type);
+    for (ProgramOrClasspathClass clazz : classes) {
+      DexString descriptor = classNamingStrategy.reservedDescriptor(clazz.getType());
       if (descriptor != null) {
-        assert !renaming.containsKey(clazz.type);
-        registerClassAsUsed(clazz.type, descriptor);
+        assert !renaming.containsKey(clazz.getType());
+        registerClassAsUsed(clazz.getType(), descriptor);
       }
     }
     appView
@@ -100,24 +101,17 @@
     timing.end();
 
     timing.begin("rename-classes");
-    for (DexClass clazz : classes) {
-      if (!renaming.containsKey(clazz.type)) {
-        DexString renamed = computeName(clazz.type);
-        renaming.put(clazz.type, renamed);
-        // If the class is a member class and it has used $ separator, its renamed name should have
-        // the same separator (as long as inner-class attribute is honored).
-        assert !keepInnerClassStructure
-            || !clazz.isMemberClass()
-            || !clazz.type.getInternalName().contains(String.valueOf(INNER_CLASS_SEPARATOR))
-            || renamed.toString().contains(String.valueOf(INNER_CLASS_SEPARATOR))
-            || classNamingStrategy.isRenamedByApplyMapping(clazz.type)
-                : clazz.toSourceString() + " -> " + renamed;
+    for (ProgramOrClasspathClass clazz : classes) {
+      if (!renaming.containsKey(clazz.getType())) {
+        DexString renamed = computeName(clazz.getType());
+        renaming.put(clazz.getType(), renamed);
+        assert verifyMemberRenamingOfInnerClasses(clazz.asDexClass(), renamed);
       }
     }
     timing.end();
 
     timing.begin("rename-dangling-types");
-    for (DexClass clazz : classes) {
+    for (ProgramOrClasspathClass clazz : classes) {
       renameDanglingTypes(clazz);
     }
     timing.end();
@@ -125,6 +119,18 @@
     return new ClassRenaming(Collections.unmodifiableMap(renaming), getPackageRenaming());
   }
 
+  private boolean verifyMemberRenamingOfInnerClasses(DexClass clazz, DexString renamed) {
+    // If the class is a member class and it has used $ separator, its renamed name should have
+    // the same separator (as long as inner-class attribute is honored).
+    assert !keepInnerClassStructure
+            || !clazz.isMemberClass()
+            || !clazz.getType().getInternalName().contains(String.valueOf(INNER_CLASS_SEPARATOR))
+            || renamed.toString().contains(String.valueOf(INNER_CLASS_SEPARATOR))
+            || classNamingStrategy.isRenamedByApplyMapping(clazz.getType())
+        : clazz + " -> " + renamed;
+    return true;
+  }
+
   private Map<String, String> getPackageRenaming() {
     ImmutableMap.Builder<String, String> packageRenaming = ImmutableMap.builder();
     for (Entry<String, Namespace> entry : states.entrySet()) {
@@ -137,16 +143,16 @@
     return packageRenaming.build();
   }
 
-  private void renameDanglingTypes(DexClass clazz) {
-    clazz.forEachMethod(this::renameDanglingTypesInMethod);
-    clazz.forEachField(this::renameDanglingTypesInField);
+  private void renameDanglingTypes(ProgramOrClasspathClass clazz) {
+    clazz.forEachClassMethod(this::renameDanglingTypesInMethod);
+    clazz.forEachClassField(this::renameDanglingTypesInField);
   }
 
-  private void renameDanglingTypesInField(DexEncodedField field) {
+  private void renameDanglingTypesInField(DexClassAndField field) {
     renameDanglingType(field.getReference().type);
   }
 
-  private void renameDanglingTypesInMethod(DexEncodedMethod method) {
+  private void renameDanglingTypesInMethod(DexClassAndMethod method) {
     DexProto proto = method.getReference().proto;
     renameDanglingType(proto.returnType);
     for (DexType type : proto.parameters.values) {
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index 26c8d70..3bc7392 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -246,7 +246,7 @@
       super(appView.options().getProguardConfiguration().getObfuscationDictionary(), false);
       this.appView = appView;
       this.factory = appView.dexItemFactory();
-      this.desugaredLibraryRenaming = appView.rewritePrefix.isRewriting();
+      this.desugaredLibraryRenaming = appView.typeRewriter.isRewriting();
     }
 
     @Override
@@ -289,7 +289,7 @@
       }
       if (desugaredLibraryRenaming
           && method.isLibraryMethodOverride().isTrue()
-          && appView.rewritePrefix.hasRewrittenTypeInSignature(
+          && appView.typeRewriter.hasRewrittenTypeInSignature(
               method.getReference().proto, appView)) {
         // With desugared library, call-backs names are reserved here.
         return method.getReference().name;
diff --git a/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java b/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
index 5ab9772..2add666 100644
--- a/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
@@ -26,7 +26,7 @@
 
   public static NamingLens createPrefixRewritingNamingLens(
       AppView<?> appView, NamingLens namingLens) {
-    if (!appView.rewritePrefix.isRewriting()) {
+    if (!appView.typeRewriter.isRewriting()) {
       return namingLens;
     }
     return new PrefixRewritingNamingLens(namingLens, appView);
@@ -45,8 +45,8 @@
 
   private DexString getRenaming(DexType type) {
     DexString descriptor = null;
-    if (appView.rewritePrefix.hasRewrittenType(type, appView)) {
-      descriptor = appView.rewritePrefix.rewrittenType(type, appView).descriptor;
+    if (appView.typeRewriter.hasRewrittenType(type, appView)) {
+      descriptor = appView.typeRewriter.rewrittenType(type, appView).descriptor;
     }
     return descriptor;
   }
@@ -106,7 +106,7 @@
   }
 
   private boolean verifyNotPrefixRewrittenPackage(String packageName) {
-    appView.rewritePrefix.forAllRewrittenTypes(
+    appView.typeRewriter.forAllRewrittenTypes(
         dexType -> {
           assert !dexType.getPackageDescriptor().equals(packageName);
         });
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index 43cf17d..174614b 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramOrClasspathClass;
 import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.naming.ClassNameMinifier.ClassRenaming;
 import com.android.tools.r8.naming.FieldNameMinifier.FieldRenaming;
@@ -78,7 +79,7 @@
   private final SeedMapper seedMapper;
   private final BiMap<DexType, DexString> mappedNames = HashBiMap.create();
   // To keep the order deterministic, we sort the classes by their type, which is a unique key.
-  private final Set<DexClass> mappedClasses = Sets.newIdentityHashSet();
+  private final Set<ProgramOrClasspathClass> mappedClasses = Sets.newIdentityHashSet();
   private final Map<DexReference, MemberNaming> memberNames = Maps.newIdentityHashMap();
   private final Map<DexMethod, DexString> defaultInterfaceMethodImplementationNames =
       Maps.newIdentityHashMap();
@@ -193,14 +194,17 @@
     DexClass clazz = appView.definitionFor(type);
 
     // Keep track of classes that needs to get renamed.
-    if (clazz != null && (classNaming != null || clazz.isProgramClass())) {
-      mappedClasses.add(clazz);
+    if (clazz != null) {
+      if (clazz.isClasspathClass() && classNaming != null) {
+        mappedClasses.add(clazz.asClasspathClass());
+      } else if (clazz.isProgramClass()) {
+        mappedClasses.add(clazz.asProgramClass());
+      }
     }
 
     Map<DexReference, MemberNaming> nonPrivateMembers = new IdentityHashMap<>();
 
-    if (classNaming != null) {
-      // TODO(b/133091438) assert that !dexClass.isLibraryClass();
+    if (classNaming != null && (clazz == null || !clazz.isLibraryClass())) {
       DexString mappedName = factory.createString(classNaming.renamedName);
       checkAndAddMappedNames(type, mappedName, classNaming.position);
       classNaming.forAllMemberNaming(
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index 89ac4ce..b933b4d 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -17,7 +17,6 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.FieldAccessInfoCollection;
-import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
 import com.android.tools.r8.graph.LibraryMethod;
 import com.android.tools.r8.graph.MethodAccessInfoCollection;
 import com.android.tools.r8.graph.MethodResolutionResult;
@@ -541,12 +540,15 @@
                   }
 
                   private void registerFieldReference(DexField field) {
-                    SuccessfulFieldResolutionResult resolutionResult =
-                        appView.appInfo().resolveField(field).asSuccessfulResolution();
-                    if (resolutionResult != null
-                        && resolutionResult.getResolvedField().getReference() != field) {
-                      nonReboundFieldReferences.add(field);
-                    }
+                    appView
+                        .appInfo()
+                        .resolveField(field)
+                        .forEachSuccessfulFieldResolutionResult(
+                            resolutionResult -> {
+                              if (resolutionResult.getResolvedField().getReference() != field) {
+                                nonReboundFieldReferences.add(field);
+                              }
+                            });
                   }
 
                   @Override
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
index 8995b69..1363a68 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
@@ -14,7 +14,6 @@
 import com.android.tools.r8.graph.FieldAccessInfoCollection;
 import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl;
 import com.android.tools.r8.graph.FieldAccessInfoImpl;
-import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
 import com.android.tools.r8.graph.MethodAccessInfoCollection;
 import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -148,31 +147,36 @@
       if (!seenFieldReferences.add(field)) {
         return;
       }
-      SuccessfulFieldResolutionResult resolutionResult =
-          appInfo.resolveField(field).asSuccessfulResolution();
-      if (resolutionResult == null) {
-        return;
-      }
-      DexField reboundReference = resolutionResult.getResolvedField().getReference();
-      if (field == reboundReference) {
-        // For the purpose of member rebinding, we don't care about already rebound references.
-        return;
-      }
-      FieldAccessInfoImpl fieldAccessInfo =
-          fieldAccessInfoCollection.computeIfAbsent(reboundReference, FieldAccessInfoImpl::new);
-      synchronized (fieldAccessInfo) {
-        // Record the fact that there is a non-rebound access to the given field. We don't
-        // distinguish between non-rebound reads and writes, so we just record it as a read.
-        if (fieldAccessInfo.getReadsWithContexts().isBottom()) {
-          fieldAccessInfo.setReadsWithContexts(new ConcreteAccessContexts());
-        } else {
-          assert fieldAccessInfo.getReadsWithContexts().isConcrete();
-        }
-        // For the purpose of member rebinding, we don't care about the access contexts, so we
-        // simply use the empty set.
-        ConcreteAccessContexts accessContexts = fieldAccessInfo.getReadsWithContexts().asConcrete();
-        accessContexts.getAccessesWithContexts().put(field, ProgramMethodSet.empty());
-      }
+      appInfo
+          .resolveField(field)
+          .forEachSuccessfulFieldResolutionResult(
+              resolutionResult -> {
+                DexField reboundReference = resolutionResult.getResolvedField().getReference();
+                if (field == reboundReference) {
+                  // For the purpose of member rebinding, we don't care about already rebound
+                  // references.
+                  return;
+                }
+                FieldAccessInfoImpl fieldAccessInfo =
+                    fieldAccessInfoCollection.computeIfAbsent(
+                        reboundReference, FieldAccessInfoImpl::new);
+                synchronized (fieldAccessInfo) {
+                  // Record the fact that there is a non-rebound access to the given field. We don't
+                  // distinguish between non-rebound reads and writes, so we just record it as a
+                  // read.
+                  if (fieldAccessInfo.getReadsWithContexts().isBottom()) {
+                    fieldAccessInfo.setReadsWithContexts(new ConcreteAccessContexts());
+                  } else {
+                    assert fieldAccessInfo.getReadsWithContexts().isConcrete();
+                  }
+                  // For the purpose of member rebinding, we don't care about the access contexts,
+                  // so we
+                  // simply use the empty set.
+                  ConcreteAccessContexts accessContexts =
+                      fieldAccessInfo.getReadsWithContexts().asConcrete();
+                  accessContexts.getAccessesWithContexts().put(field, ProgramMethodSet.empty());
+                }
+              });
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
index 42537e8..6e6e88c 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
@@ -224,11 +224,11 @@
 
     private void registerFieldAccess(DexField field) {
       FieldResolutionResult resolutionResult = appView.appInfo().resolveField(field);
-      if (resolutionResult.getProgramField() == null) {
+      if (resolutionResult.getSingleProgramField() == null) {
         return;
       }
 
-      ProgramField resolvedField = resolutionResult.getProgramField();
+      ProgramField resolvedField = resolutionResult.getSingleProgramField();
       DexField rewrittenFieldReference =
           graphLens.internalGetNextFieldSignature(resolvedField.getReference());
       if (rewrittenFieldReference != resolvedField.getReference()) {
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java
index 2ffbf8d..e0341ae 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java
@@ -199,7 +199,7 @@
         // This needs to map the field all the way to the final graph lens.
         DexField rewrittenField = appView.graphLens().lookupField(field, graphLens);
         FieldResolutionResult resolutionResult = appView.appInfo().resolveField(rewrittenField);
-        return resolutionResult.isSuccessfulResolution()
+        return resolutionResult.isSingleFieldResolutionResult()
             && !appView.appInfo().isFieldRead(resolutionResult.getResolvedField());
       }
       return false;
diff --git a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
index 2b49179..29ed273 100644
--- a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
@@ -4,21 +4,32 @@
 
 package com.android.tools.r8.optimize.proto;
 
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexMethodSignature;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
-import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
 import com.android.tools.r8.utils.collections.DexMethodSignatureSet;
 import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
-import java.util.ArrayList;
-import java.util.List;
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 
 public class ProtoNormalizer {
@@ -33,35 +44,36 @@
     this.options = appView.options();
   }
 
-  public void run(ExecutorService executorService, Timing timing) {
+  public void run(ExecutorService executorService, Timing timing) throws ExecutionException {
     if (options.testing.enableExperimentalProtoNormalization) {
       timing.time("Proto normalization", () -> run(executorService));
     }
   }
 
-  // TODO(b/195112263): Parallelize using executor service.
-  private void run(ExecutorService executorService) {
-    // Compute mapping from method signatures to new method signatures.
-    BidirectionalOneToOneMap<DexMethodSignature, DexMethodSignature> normalization =
-        computeNormalization();
+  private void run(ExecutorService executorService) throws ExecutionException {
+    GlobalReservationState globalReservationState = computeGlobalReservationState(executorService);
 
+    // TODO(b/173398086): This uses a single LocalReservationState for the entire program. This
+    //  should process the strongly connected program components in parallel, each with their own
+    //  LocalReservationState.
+    LocalReservationState localReservationState = new LocalReservationState();
     ProtoNormalizerGraphLens.Builder lensBuilder = ProtoNormalizerGraphLens.builder(appView);
-    for (DexProgramClass clazz : appView.appInfo().classes()) {
+    for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
       clazz
           .getMethodCollection()
           .replaceMethods(
               method -> {
                 DexMethodSignature methodSignature = method.getSignature();
-                assert normalization.containsKey(methodSignature);
-                DexMethodSignature normalizedMethodSignature = normalization.get(methodSignature);
-                if (methodSignature.equals(normalizedMethodSignature)) {
+                DexMethodSignature newMethodSignature =
+                    localReservationState.getNewMethodSignature(
+                        methodSignature, dexItemFactory, globalReservationState);
+                if (methodSignature.equals(newMethodSignature)) {
                   return method;
                 }
-                DexMethod normalizedMethodReference =
-                    normalizedMethodSignature.withHolder(clazz, dexItemFactory);
-                lensBuilder.recordNewMethodSignature(method, normalizedMethodReference);
+                DexMethod newMethodReference = newMethodSignature.withHolder(clazz, dexItemFactory);
+                lensBuilder.recordNewMethodSignature(method, newMethodReference);
                 // TODO(b/195112263): Fixup any optimization info and parameter annotations.
-                return method.toTypeSubstitutedMethod(normalizedMethodReference);
+                return method.toTypeSubstitutedMethod(newMethodReference);
               });
     }
 
@@ -70,57 +82,180 @@
     }
   }
 
-  // TODO(b/195112263): This naively maps each method signatures to their normalized method
-  //  signature if it is not already reserved by another method. This means that we will rewrite
-  //  foo(A,B), bar(B,A), and baz(B,A) into foo(A,B), bar(A,B), and baz(A,B), such that all of the
-  //  method signatures share the same parameter type list. However, if there is a method foo(A,B)
-  //  and foo(B,A) then this does not rewrite foo(B,A) into foo(A,B). If foo(B,A) is not in the same
-  //  hierarchy as foo(A,B), this would be possible, however.
-  // TODO(b/195112263): Do not optimize foo(B, A) into foo(A, B) if this won't lead to any parameter
-  //  type lists being shared (e.g., if foo(B, A) is the only method where sorted(parameters) is
-  //  [A, B]).
-  private BidirectionalOneToOneMap<DexMethodSignature, DexMethodSignature> computeNormalization() {
-    // Reserve the signatures of unoptimizable methods to avoid collisions.
-    MutableBidirectionalOneToOneMap<DexMethodSignature, DexMethodSignature> normalization =
-        new BidirectionalOneToOneHashMap<>();
-    DexMethodSignatureSet optimizableMethodSignatures = DexMethodSignatureSet.create();
-    for (DexProgramClass clazz : appView.appInfo().classes()) {
-      clazz.forEachProgramMethod(
-          method -> {
-            DexMethodSignature methodSignature = method.getMethodSignature();
-            if (isUnoptimizable(method)) {
-              normalization.put(methodSignature, methodSignature);
-            } else if (!normalization.containsKey(methodSignature)) {
-              optimizableMethodSignatures.add(methodSignature);
-            }
-          });
+  private GlobalReservationState computeGlobalReservationState(ExecutorService executorService)
+      throws ExecutionException {
+    // Tracks how many different parameter lists can be optimized into the same parameter list.
+    // If only (B, A) can be rewritten into (A, B), then there is no need to rewrite parameter lists
+    // on the form (B, A) into (A, B), as that won't lead to any sharing of parameter lists.
+    Map<DexTypeList, Set<DexTypeList>> optimizableParameterLists = new ConcurrentHashMap<>();
+
+    // Used to track if a given parameter list should be mapped to a specific permutation instead of
+    // just sorting the parameter list. This is used to ensure that we will rewrite parameter lists
+    // such as (A, B) into (B, A) if there is an unoptimizable method with parameter list (B, A).
+    Map<DexTypeList, Set<DexTypeList>> reservedParameterLists = new ConcurrentHashMap<>();
+
+    // Tracks the set of unoptimizable method signatures. These must remain as-is.
+    DexMethodSignatureSet unoptimizableSignatures = DexMethodSignatureSet.createConcurrent();
+
+    ThreadUtils.processMethods(
+        appView,
+        method ->
+            computeReservationsFromMethod(
+                method, optimizableParameterLists, reservedParameterLists, unoptimizableSignatures),
+        executorService);
+
+    // Reserve parameter lists that won't lead to any sharing after normalization. Any method with
+    // such a parameter list must remain as-is.
+    Set<DexTypeList> unoptimizableParameterLists = new HashSet<>();
+    optimizableParameterLists.forEach(
+        (sortedParameters, parameterListsBeforeNormalization) -> {
+          int size = parameterListsBeforeNormalization.size();
+          if (size != 1) {
+            // There are multiple optimizable methods with different parameter lists that can be
+            // rewritten into having the same parameter list.
+            assert size > 1;
+            return;
+          }
+          DexTypeList parameters = parameterListsBeforeNormalization.iterator().next();
+          Set<DexTypeList> reservedParameters =
+              reservedParameterLists.getOrDefault(sortedParameters, Collections.emptySet());
+          if (!reservedParameters.isEmpty() && !reservedParameters.contains(parameters)) {
+            // There is at least one optimizable method that can be rewritten into having the same
+            // parameter list as an unoptimizable method.
+            return;
+          }
+          unoptimizableParameterLists.add(parameters);
+        });
+
+    ThreadUtils.processMethods(
+        appView,
+        method ->
+            computeExtraReservationsFromMethod(
+                method, unoptimizableParameterLists, unoptimizableSignatures),
+        executorService);
+
+    return new GlobalReservationState(reservedParameterLists, unoptimizableSignatures);
+  }
+
+  private void computeReservationsFromMethod(
+      ProgramMethod method,
+      Map<DexTypeList, Set<DexTypeList>> optimizableParameterLists,
+      Map<DexTypeList, Set<DexTypeList>> reservedParameterLists,
+      DexMethodSignatureSet unoptimizableSignatures) {
+    if (isUnoptimizable(method)) {
+      // Record that other optimizable methods with the same set of parameter types should be
+      // rewritten to have the same parameter list as this method.
+      reservedParameterLists
+          .computeIfAbsent(
+              method.getParameters().getSorted(), ignoreKey(Sets::newConcurrentHashSet))
+          .add(method.getParameters());
+
+      // Mark signature as unoptimizable.
+      unoptimizableSignatures.add(method);
+    } else {
+      // Record that the method's parameter list can be rewritten into any permutation.
+      optimizableParameterLists
+          .computeIfAbsent(
+              method.getParameters().getSorted(), ignoreKey(Sets::newConcurrentHashSet))
+          .add(method.getParameters());
     }
-    optimizableMethodSignatures.removeAll(normalization.keySet());
+  }
 
-    // Normalize each signature that is subject to optimization.
-    List<DexMethodSignature> sortedOptimizableMethodSignatures =
-        new ArrayList<>(optimizableMethodSignatures);
-    sortedOptimizableMethodSignatures.sort(DexMethodSignature::compareTo);
-
-    for (DexMethodSignature signature : sortedOptimizableMethodSignatures) {
-      assert !normalization.containsKey(signature);
-      assert !normalization.containsValue(signature);
-      DexMethodSignature normalizedSignature =
-          signature.withProto(
-              dexItemFactory.createProto(
-                  signature.getReturnType(), signature.getParameters().getSorted()));
-      if (normalization.containsValue(normalizedSignature)) {
-        normalization.put(signature, signature);
-      } else {
-        normalization.put(signature, normalizedSignature);
-      }
+  private void computeExtraReservationsFromMethod(
+      ProgramMethod method,
+      Set<DexTypeList> unoptimizableParameterLists,
+      DexMethodSignatureSet unoptimizableSignatures) {
+    if (unoptimizableParameterLists.contains(method.getParameters())) {
+      unoptimizableSignatures.add(method.getMethodSignature());
     }
-
-    return normalization;
   }
 
   private boolean isUnoptimizable(ProgramMethod method) {
     // TODO(b/195112263): This is incomplete.
-    return appView.getKeepInfo(method).isPinned(options);
+    return appView.getKeepInfo(method).isPinned(options)
+        || method.getDefinition().isLibraryMethodOverride().isPossiblyTrue();
+  }
+
+  static class GlobalReservationState {
+
+    // Used to track if a given parameter list should be mapped to a specific permutation instead of
+    // just sorting the parameter list. This is used to ensure that we will rewrite parameter lists
+    // such as (A, B) into (B, A) if there is an unoptimizable method with parameter list (B, A).
+    Map<DexTypeList, DexTypeList> reservedParameters;
+
+    // Tracks the set of unoptimizable method signatures. These must remain as-is.
+    DexMethodSignatureSet unoptimizableSignatures;
+
+    GlobalReservationState(
+        Map<DexTypeList, Set<DexTypeList>> reservedParameterLists,
+        DexMethodSignatureSet unoptimizableSignatures) {
+      this.reservedParameters = selectDeterministicTarget(reservedParameterLists);
+      this.unoptimizableSignatures = unoptimizableSignatures;
+    }
+
+    private static Map<DexTypeList, DexTypeList> selectDeterministicTarget(
+        Map<DexTypeList, Set<DexTypeList>> reservedParameterLists) {
+      Map<DexTypeList, DexTypeList> result = new HashMap<>();
+      reservedParameterLists.forEach(
+          (sortedParameters, candidates) -> {
+            Iterator<DexTypeList> iterator = candidates.iterator();
+            DexTypeList smallestCandidate = iterator.next();
+            while (iterator.hasNext()) {
+              DexTypeList candidate = iterator.next();
+              if (candidate.compareTo(smallestCandidate) < 0) {
+                smallestCandidate = candidate;
+              }
+            }
+            result.put(sortedParameters, smallestCandidate);
+          });
+      return result;
+    }
+
+    DexTypeList getReservedParameters(DexMethodSignature methodSignature) {
+      DexTypeList sortedParameters = methodSignature.getParameters().getSorted();
+      return reservedParameters.getOrDefault(sortedParameters, sortedParameters);
+    }
+
+    boolean isUnoptimizable(DexMethodSignature methodSignature) {
+      return unoptimizableSignatures.contains(methodSignature);
+    }
+  }
+
+  static class LocalReservationState {
+
+    MutableBidirectionalOneToOneMap<DexMethodSignature, DexMethodSignature> newMethodSignatures =
+        new BidirectionalOneToOneHashMap<>();
+
+    // TODO: avoid sorting multiple times.
+    DexMethodSignature getNewMethodSignature(
+        DexMethodSignature methodSignature,
+        DexItemFactory dexItemFactory,
+        GlobalReservationState globalReservationState) {
+      if (globalReservationState.isUnoptimizable(methodSignature)) {
+        assert !newMethodSignatures.containsKey(methodSignature);
+        return methodSignature;
+      }
+      DexMethodSignature reservedSignature = newMethodSignatures.get(methodSignature);
+      if (reservedSignature != null) {
+        assert reservedSignature
+            .getParameters()
+            .equals(globalReservationState.getReservedParameters(methodSignature));
+        return reservedSignature;
+      }
+      DexTypeList reservedParameters =
+          globalReservationState.getReservedParameters(methodSignature);
+      DexMethodSignature newMethodSignature =
+          methodSignature.withParameters(reservedParameters, dexItemFactory);
+      if (newMethodSignatures.containsValue(newMethodSignature)) {
+        int index = 1;
+        String newMethodBaseName = methodSignature.getName().toString();
+        do {
+          DexString newMethodName = dexItemFactory.createString(newMethodBaseName + "$" + index);
+          newMethodSignature = newMethodSignature.withName(newMethodName);
+        } while (newMethodSignatures.containsValue(newMethodSignature));
+      }
+      newMethodSignatures.put(methodSignature, newMethodSignature);
+      return newMethodSignature;
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index e156407..8216777 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -665,7 +665,6 @@
             || getMissingClasses().contains(type)
             // TODO(b/150693139): Remove these exceptions once fixed.
             || InterfaceDesugaringSyntheticHelper.isCompanionClassType(type)
-            || InterfaceDesugaringSyntheticHelper.isEmulatedLibraryClassType(type)
             // TODO(b/150736225): Not sure how to remove these.
             || DesugaredLibraryAPIConverter.isVivifiedType(type)
         : "Failed lookup of non-missing type: " + type;
@@ -1215,7 +1214,7 @@
     assert lens.assertDefinitionsNotModified(
         switchMaps.keySet().stream()
             .map(this::resolveField)
-            .filter(FieldResolutionResult::isSuccessfulResolution)
+            .filter(FieldResolutionResult::isSingleFieldResolutionResult)
             .map(FieldResolutionResult::getResolvedField)
             .collect(Collectors.toList()));
 
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 1622376..8681f33 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -62,6 +62,7 @@
 import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl;
 import com.android.tools.r8.graph.FieldAccessInfoImpl;
 import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.graph.FieldResolutionResult.FailedOrUnknownFieldResolutionResult;
 import com.android.tools.r8.graph.GenericSignatureEnqueuerAnalysis;
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.graph.InvalidCode;
@@ -317,6 +318,8 @@
    */
   private final Set<ClasspathOrLibraryClass> liveNonProgramTypes = Sets.newIdentityHashSet();
 
+  private final Set<ClasspathOrLibraryClass> referencedNonProgramTypes = Sets.newIdentityHashSet();
+
   /** Set of reachable proto types that will be dead code eliminated. */
   private final Set<DexProgramClass> deadProtoTypeCandidates = Sets.newIdentityHashSet();
 
@@ -602,7 +605,7 @@
       ignoreMissingClass(type);
     } else if (clazz.isNotProgramClass()) {
       addLiveNonProgramType(
-          clazz.asClasspathOrLibraryClass(), this::ignoreMissingClasspathOrLibraryClass);
+          clazz.asClasspathOrLibraryClass(), true, this::ignoreMissingClasspathOrLibraryClass);
     }
   }
 
@@ -697,51 +700,62 @@
 
   private void addLiveNonProgramType(
       ClasspathOrLibraryClass clazz,
+      // TODO(b/216576191): Remove when tracking live library members.
+      boolean visitMembers,
       BiConsumer<DexType, ClasspathOrLibraryDefinition> missingClassConsumer) {
     WorkList<ClasspathOrLibraryClass> worklist =
         WorkList.newIdentityWorkList(clazz, liveNonProgramTypes);
     while (worklist.hasNext()) {
       ClasspathOrLibraryClass definition = worklist.next();
-      processNewLiveNonProgramType(definition, worklist, missingClassConsumer);
+      processNewLiveNonProgramType(definition, worklist, missingClassConsumer, visitMembers);
     }
   }
 
   private void processNewLiveNonProgramType(
       ClasspathOrLibraryClass clazz,
       WorkList<ClasspathOrLibraryClass> worklist,
-      BiConsumer<DexType, ClasspathOrLibraryDefinition> missingClassConsumer) {
+      BiConsumer<DexType, ClasspathOrLibraryDefinition> missingClassConsumer,
+      boolean visitMembers) {
+    ensureMethodsContinueToWidenAccess(clazz);
     if (clazz.isLibraryClass()) {
-      // TODO(b/149201735): This likely needs to apply to classpath too.
-      ensureMethodsContinueToWidenAccess(clazz);
       // Only libraries must not derive program. Classpath classes can, assuming correct keep rules.
       warnIfLibraryTypeInheritsFromProgramType(clazz.asLibraryClass());
     }
-    clazz.forEachClassField(
-        field ->
+    if (visitMembers) {
+      clazz.forEachClassField(
+          field ->
+              addNonProgramClassToWorklist(
+                  field.getType(),
+                  field.asClasspathOrLibraryDefinition(),
+                  referencedNonProgramTypes::add,
+                  missingClassConsumer));
+      clazz.forEachClassMethod(
+          method -> {
+            ClasspathOrLibraryDefinition derivedContext = method.asClasspathOrLibraryDefinition();
             addNonProgramClassToWorklist(
-                field.getType(),
-                field.asClasspathOrLibraryDefinition(),
-                worklist,
-                missingClassConsumer));
-    clazz.forEachClassMethod(
-        method -> {
-          ClasspathOrLibraryDefinition derivedContext = method.asClasspathOrLibraryDefinition();
-          addNonProgramClassToWorklist(
-              method.getReturnType(), derivedContext, worklist, missingClassConsumer);
-          for (DexType parameter : method.getParameters()) {
-            addNonProgramClassToWorklist(parameter, derivedContext, worklist, missingClassConsumer);
-          }
-        });
+                method.getReturnType(),
+                derivedContext,
+                referencedNonProgramTypes::add,
+                missingClassConsumer);
+            for (DexType parameter : method.getParameters()) {
+              addNonProgramClassToWorklist(
+                  parameter, derivedContext, referencedNonProgramTypes::add, missingClassConsumer);
+            }
+          });
+    }
     for (DexType supertype : clazz.allImmediateSupertypes()) {
       addNonProgramClassToWorklist(
-          supertype, clazz.asClasspathOrLibraryDefinition(), worklist, missingClassConsumer);
+          supertype,
+          clazz.asClasspathOrLibraryDefinition(),
+          worklist::addIfNotSeen,
+          missingClassConsumer);
     }
   }
 
   private void addNonProgramClassToWorklist(
       DexType type,
       ClasspathOrLibraryDefinition context,
-      WorkList<ClasspathOrLibraryClass> worklist,
+      Consumer<ClasspathOrLibraryClass> classAdder,
       BiConsumer<DexType, ClasspathOrLibraryDefinition> missingClassConsumer) {
     if (type.isArrayType()) {
       type = type.toBaseType(appView.dexItemFactory());
@@ -753,7 +767,7 @@
     if (clazz == null) {
       missingClassConsumer.accept(type, context);
     } else if (!clazz.isProgramClass()) {
-      worklist.addIfNotSeen(clazz.asClasspathOrLibraryClass());
+      classAdder.accept(clazz.asClasspathOrLibraryClass());
     }
   }
 
@@ -769,7 +783,8 @@
 
   private DexClass getClassOrNullFromReflectiveAccess(DexType type, ProgramDefinition context) {
     // To avoid that we report reflectively accessed types as missing.
-    return definitionFor(type, context, this::recordNonProgramClass, this::ignoreMissingClass);
+    return definitionFor(
+        type, context, this::recordNonProgramClassWithNoMissingReporting, this::ignoreMissingClass);
   }
 
   private DexProgramClass getProgramClassOrNullFromReflectiveAccess(
@@ -1460,49 +1475,52 @@
       return;
     }
 
-    FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
-    fieldAccessAnalyses.forEach(
-        analysis ->
-            analysis.traceInstanceFieldRead(
-                fieldReference, resolutionResult, currentMethod, workList));
+    resolveField(fieldReference, currentMethod)
+        .visitFieldResolutionResults(
+            resolutionResult -> {
+              fieldAccessAnalyses.forEach(
+                  analysis ->
+                      analysis.traceInstanceFieldRead(
+                          fieldReference, resolutionResult, currentMethod, workList));
 
-    if (resolutionResult.isFailedOrUnknownResolution()) {
-      // Must trace the types from the field reference even if it does not exist.
-      traceFieldReference(fieldReference, resolutionResult, currentMethod);
-      noClassMerging.add(fieldReference.getHolderType());
-      return;
-    }
+              ProgramField field = resolutionResult.getProgramField();
+              if (field == null) {
+                // No need to trace into the non-program code.
+                return;
+              }
 
-    ProgramField field =
-        resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
-    if (field == null) {
-      // No need to trace into the non-program code.
-      return;
-    }
+              assert !mode.isFinalTreeShaking()
+                      || !field.getDefinition().getOptimizationInfo().isDead()
+                  : "Unexpected reference in `"
+                      + currentMethod.toSourceString()
+                      + "` to field marked dead: "
+                      + field.getReference().toSourceString();
 
-    assert !mode.isFinalTreeShaking() || !field.getDefinition().getOptimizationInfo().isDead()
-        : "Unexpected reference in `"
-            + currentMethod.toSourceString()
-            + "` to field marked dead: "
-            + field.getReference().toSourceString();
+              if (readType == FieldReadType.READ_FROM_METHOD_HANDLE) {
+                fieldAccessInfoCollection.get(field.getReference()).setReadFromMethodHandle();
+              } else if (readType == FieldReadType.READ_FROM_RECORD_METHOD_HANDLE) {
+                fieldAccessInfoCollection
+                    .get(field.getReference())
+                    .setReadFromRecordInvokeDynamic();
+              }
 
-    if (readType == FieldReadType.READ_FROM_METHOD_HANDLE) {
-      fieldAccessInfoCollection.get(field.getReference()).setReadFromMethodHandle();
-    } else if (readType == FieldReadType.READ_FROM_RECORD_METHOD_HANDLE) {
-      fieldAccessInfoCollection.get(field.getReference()).setReadFromRecordInvokeDynamic();
-    }
+              if (Log.ENABLED) {
+                Log.verbose(getClass(), "Register Iget `%s`.", fieldReference);
+              }
 
-    if (Log.ENABLED) {
-      Log.verbose(getClass(), "Register Iget `%s`.", fieldReference);
-    }
+              if (field.getReference() != fieldReference) {
+                // Mark the initial resolution holder as live.
+                markTypeAsLive(resolutionResult.getInitialResolutionHolder(), currentMethod);
+              }
 
-    if (field.getReference() != fieldReference) {
-      // Mark the initial resolution holder as live.
-      markTypeAsLive(resolutionResult.getInitialResolutionHolder(), currentMethod);
-    }
-
-    workList.enqueueMarkFieldAsReachableAction(
-        field, currentMethod, KeepReason.fieldReferencedIn(currentMethod));
+              workList.enqueueMarkFieldAsReachableAction(
+                  field, currentMethod, KeepReason.fieldReferencedIn(currentMethod));
+            },
+            failedResolution -> {
+              // Must trace the types from the field reference even if it does not exist.
+              traceFieldReference(fieldReference, failedResolution, currentMethod);
+              noClassMerging.add(fieldReference.getHolderType());
+            });
   }
 
   void traceInstanceFieldWrite(DexField field, ProgramMethod currentMethod) {
@@ -1519,47 +1537,48 @@
       return;
     }
 
-    FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
-    fieldAccessAnalyses.forEach(
-        analysis ->
-            analysis.traceInstanceFieldWrite(
-                fieldReference, resolutionResult, currentMethod, workList));
+    resolveField(fieldReference, currentMethod)
+        .visitFieldResolutionResults(
+            resolutionResult -> {
+              fieldAccessAnalyses.forEach(
+                  analysis ->
+                      analysis.traceInstanceFieldWrite(
+                          fieldReference, resolutionResult, currentMethod, workList));
 
-    if (resolutionResult.isFailedOrUnknownResolution()) {
-      // Must trace the types from the field reference even if it does not exist.
-      traceFieldReference(fieldReference, resolutionResult, currentMethod);
-      noClassMerging.add(fieldReference.getHolderType());
-      return;
-    }
+              ProgramField field = resolutionResult.getProgramField();
+              if (field == null) {
+                // No need to trace into the non-program code.
+                return;
+              }
 
-    ProgramField field =
-        resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
-    if (field == null) {
-      // No need to trace into the non-program code.
-      return;
-    }
+              assert !mode.isFinalTreeShaking()
+                      || !field.getDefinition().getOptimizationInfo().isDead()
+                  : "Unexpected reference in `"
+                      + currentMethod.toSourceString()
+                      + "` to field marked dead: "
+                      + field.getReference().toSourceString();
 
-    assert !mode.isFinalTreeShaking() || !field.getDefinition().getOptimizationInfo().isDead()
-        : "Unexpected reference in `"
-            + currentMethod.toSourceString()
-            + "` to field marked dead: "
-            + field.getReference().toSourceString();
+              if (fromMethodHandle) {
+                fieldAccessInfoCollection.get(field.getReference()).setWrittenFromMethodHandle();
+              }
 
-    if (fromMethodHandle) {
-      fieldAccessInfoCollection.get(field.getReference()).setWrittenFromMethodHandle();
-    }
+              if (Log.ENABLED) {
+                Log.verbose(getClass(), "Register Iput `%s`.", fieldReference);
+              }
 
-    if (Log.ENABLED) {
-      Log.verbose(getClass(), "Register Iput `%s`.", fieldReference);
-    }
+              if (field.getReference() != fieldReference) {
+                // Mark the initial resolution holder as live.
+                markTypeAsLive(resolutionResult.getInitialResolutionHolder(), currentMethod);
+              }
 
-    if (field.getReference() != fieldReference) {
-      // Mark the initial resolution holder as live.
-      markTypeAsLive(resolutionResult.getInitialResolutionHolder(), currentMethod);
-    }
-
-    KeepReason reason = KeepReason.fieldReferencedIn(currentMethod);
-    workList.enqueueMarkFieldAsReachableAction(field, currentMethod, reason);
+              KeepReason reason = KeepReason.fieldReferencedIn(currentMethod);
+              workList.enqueueMarkFieldAsReachableAction(field, currentMethod, reason);
+            },
+            failedResolution -> {
+              // Must trace the types from the field reference even if it does not exist.
+              traceFieldReference(fieldReference, failedResolution, currentMethod);
+              noClassMerging.add(fieldReference.getHolderType());
+            });
   }
 
   void traceStaticFieldRead(DexField field, ProgramMethod currentMethod) {
@@ -1576,63 +1595,66 @@
       return;
     }
 
-    FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
-    fieldAccessAnalyses.forEach(
-        analysis ->
-            analysis.traceStaticFieldRead(
-                fieldReference, resolutionResult, currentMethod, workList));
+    resolveField(fieldReference, currentMethod)
+        .visitFieldResolutionResults(
+            resolutionResult -> {
+              fieldAccessAnalyses.forEach(
+                  analysis ->
+                      analysis.traceStaticFieldRead(
+                          fieldReference, resolutionResult, currentMethod, workList));
 
-    if (resolutionResult.isFailedOrUnknownResolution()) {
-      // Must trace the types from the field reference even if it does not exist.
-      traceFieldReference(fieldReference, resolutionResult, currentMethod);
-      noClassMerging.add(fieldReference.getHolderType());
+              ProgramField field = resolutionResult.getProgramField();
+              if (field == null) {
+                // No need to trace into the non-program code.
+                return;
+              }
 
-      // Record field reference for generated extension registry shrinking.
-      appView.withGeneratedExtensionRegistryShrinker(
-          shrinker ->
-              shrinker.handleFailedOrUnknownFieldResolution(fieldReference, currentMethod, mode));
-      return;
-    }
+              assert !mode.isFinalTreeShaking()
+                      || !field.getDefinition().getOptimizationInfo().isDead()
+                  : "Unexpected reference in `"
+                      + currentMethod.toSourceString()
+                      + "` to field marked dead: "
+                      + field.getReference().toSourceString();
 
-    ProgramField field =
-        resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
-    if (field == null) {
-      // No need to trace into the non-program code.
-      return;
-    }
+              if (fromMethodHandle) {
+                fieldAccessInfoCollection.get(field.getReference()).setReadFromMethodHandle();
+              }
 
-    assert !mode.isFinalTreeShaking() || !field.getDefinition().getOptimizationInfo().isDead()
-        : "Unexpected reference in `"
-            + currentMethod.toSourceString()
-            + "` to field marked dead: "
-            + field.getReference().toSourceString();
+              if (Log.ENABLED) {
+                Log.verbose(getClass(), "Register Sget `%s`.", fieldReference);
+              }
 
-    if (fromMethodHandle) {
-      fieldAccessInfoCollection.get(field.getReference()).setReadFromMethodHandle();
-    }
+              // If it is a dead proto extension field, don't trace onwards.
+              boolean skipTracing =
+                  appView.withGeneratedExtensionRegistryShrinker(
+                      shrinker ->
+                          shrinker.isDeadProtoExtensionField(
+                              field, fieldAccessInfoCollection, keepInfo),
+                      false);
+              if (skipTracing) {
+                addDeadProtoTypeCandidate(field.getHolder());
+                return;
+              }
 
-    if (Log.ENABLED) {
-      Log.verbose(getClass(), "Register Sget `%s`.", fieldReference);
-    }
+              if (field.getReference() != fieldReference) {
+                // Mark the initial resolution holder as live. Note that this should only be done if
+                // the field
+                // is not a dead proto field (in which case we bail-out above).
+                markTypeAsLive(resolutionResult.getInitialResolutionHolder(), currentMethod);
+              }
 
-    // If it is a dead proto extension field, don't trace onwards.
-    boolean skipTracing =
-        appView.withGeneratedExtensionRegistryShrinker(
-            shrinker ->
-                shrinker.isDeadProtoExtensionField(field, fieldAccessInfoCollection, keepInfo),
-            false);
-    if (skipTracing) {
-      addDeadProtoTypeCandidate(field.getHolder());
-      return;
-    }
-
-    if (field.getReference() != fieldReference) {
-      // Mark the initial resolution holder as live. Note that this should only be done if the field
-      // is not a dead proto field (in which case we bail-out above).
-      markTypeAsLive(resolutionResult.getInitialResolutionHolder(), currentMethod);
-    }
-
-    markFieldAsLive(field, currentMethod);
+              markFieldAsLive(field, currentMethod);
+            },
+            failedResolution -> {
+              // Must trace the types from the field reference even if it does not exist.
+              traceFieldReference(fieldReference, failedResolution, currentMethod);
+              noClassMerging.add(fieldReference.getHolderType());
+              // Record field reference for generated extension registry shrinking.
+              appView.withGeneratedExtensionRegistryShrinker(
+                  shrinker ->
+                      shrinker.handleFailedOrUnknownFieldResolution(
+                          fieldReference, currentMethod, mode));
+            });
   }
 
   void traceStaticFieldWrite(DexField field, ProgramMethod currentMethod) {
@@ -1649,60 +1671,63 @@
       return;
     }
 
-    FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
-    fieldAccessAnalyses.forEach(
-        analysis ->
-            analysis.traceStaticFieldWrite(
-                fieldReference, resolutionResult, currentMethod, workList));
+    resolveField(fieldReference, currentMethod)
+        .visitFieldResolutionResults(
+            resolutionResult -> {
+              fieldAccessAnalyses.forEach(
+                  analysis ->
+                      analysis.traceStaticFieldWrite(
+                          fieldReference, resolutionResult, currentMethod, workList));
 
-    if (resolutionResult.isFailedOrUnknownResolution()) {
-      // Must trace the types from the field reference even if it does not exist.
-      traceFieldReference(fieldReference, resolutionResult, currentMethod);
-      noClassMerging.add(fieldReference.getHolderType());
-      return;
-    }
+              ProgramField field = resolutionResult.getProgramField();
+              if (field == null) {
+                // No need to trace into the non-program code.
+                return;
+              }
 
-    ProgramField field =
-        resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
-    if (field == null) {
-      // No need to trace into the non-program code.
-      return;
-    }
+              assert !mode.isFinalTreeShaking()
+                      || !field.getDefinition().getOptimizationInfo().isDead()
+                  : "Unexpected reference in `"
+                      + currentMethod.toSourceString()
+                      + "` to field marked dead: "
+                      + field.getReference().toSourceString();
 
-    assert !mode.isFinalTreeShaking() || !field.getDefinition().getOptimizationInfo().isDead()
-        : "Unexpected reference in `"
-            + currentMethod.toSourceString()
-            + "` to field marked dead: "
-            + field.getReference().toSourceString();
+              if (fromMethodHandle) {
+                fieldAccessInfoCollection.get(field.getReference()).setWrittenFromMethodHandle();
+              }
 
-    if (fromMethodHandle) {
-      fieldAccessInfoCollection.get(field.getReference()).setWrittenFromMethodHandle();
-    }
+              if (Log.ENABLED) {
+                Log.verbose(getClass(), "Register Sput `%s`.", fieldReference);
+              }
 
-    if (Log.ENABLED) {
-      Log.verbose(getClass(), "Register Sput `%s`.", fieldReference);
-    }
+              if (appView.options().protoShrinking().enableGeneratedExtensionRegistryShrinking) {
+                // If it is a dead proto extension field, don't trace onwards.
+                boolean skipTracing =
+                    appView.withGeneratedExtensionRegistryShrinker(
+                        shrinker ->
+                            shrinker.isDeadProtoExtensionField(
+                                field, fieldAccessInfoCollection, keepInfo),
+                        false);
+                if (skipTracing) {
+                  addDeadProtoTypeCandidate(field.getHolder());
+                  return;
+                }
+              }
 
-    if (appView.options().protoShrinking().enableGeneratedExtensionRegistryShrinking) {
-      // If it is a dead proto extension field, don't trace onwards.
-      boolean skipTracing =
-          appView.withGeneratedExtensionRegistryShrinker(
-              shrinker ->
-                  shrinker.isDeadProtoExtensionField(field, fieldAccessInfoCollection, keepInfo),
-              false);
-      if (skipTracing) {
-        addDeadProtoTypeCandidate(field.getHolder());
-        return;
-      }
-    }
+              if (field.getReference() != fieldReference) {
+                // Mark the initial resolution holder as live. Note that this should only be done if
+                // the field
+                // is not a dead proto field (in which case we bail-out above).
+                markTypeAsLive(resolutionResult.getInitialResolutionHolder(), currentMethod);
+              }
 
-    if (field.getReference() != fieldReference) {
-      // Mark the initial resolution holder as live. Note that this should only be done if the field
-      // is not a dead proto field (in which case we bail-out above).
-      markTypeAsLive(resolutionResult.getInitialResolutionHolder(), currentMethod);
-    }
-
-    markFieldAsLive(field, currentMethod);
+              markFieldAsLive(field, currentMethod);
+            },
+            failedResolution -> {
+              // Must trace the types from the field reference even if it does not exist.
+              traceFieldReference(fieldReference, failedResolution, currentMethod);
+              noClassMerging.add(fieldReference.getHolderType());
+            });
   }
 
   private DexMethod getInvokeSuperTarget(DexMethod method, ProgramMethod currentMethod) {
@@ -2069,16 +2094,17 @@
 
   private FieldResolutionResult resolveField(DexField field, ProgramDefinition context) {
     // Record the references in case they are not program types.
-    FieldResolutionResult resolutionResult = appInfo.resolveField(field);
-    if (resolutionResult.isSuccessfulResolution()) {
-      recordFieldReference(
-          field, resolutionResult.getResolutionPair().asProgramDerivedContext(context));
-    } else {
-      assert resolutionResult.isFailedOrUnknownResolution();
-      failedFieldResolutionTargets.add(field);
-      recordFieldReference(field, context);
-    }
-    return resolutionResult;
+    FieldResolutionResult fieldResolutionResult = appInfo.resolveField(field);
+    fieldResolutionResult.visitFieldResolutionResults(
+        resolutionResult -> {
+          recordFieldReference(
+              field, resolutionResult.getResolutionPair().asProgramDerivedContext(context));
+        },
+        failedResolution -> {
+          failedFieldResolutionTargets.add(field);
+          recordFieldReference(field, context);
+        });
+    return fieldResolutionResult;
   }
 
   private SingleResolutionResult resolveMethod(
@@ -2355,11 +2381,20 @@
     if (!clazz.isProgramClass()) {
       addLiveNonProgramType(
           clazz.asClasspathOrLibraryClass(),
+          true,
           (missingType, derivedContext) ->
               reportMissingClass(missingType, derivedContext.asProgramDerivedContext(context)));
     }
   }
 
+  private void recordNonProgramClassWithNoMissingReporting(
+      DexClass clazz, ProgramDerivedContext context) {
+    if (!clazz.isProgramClass()) {
+      addLiveNonProgramType(
+          clazz.asClasspathOrLibraryClass(), true, this::ignoreMissingClasspathOrLibraryClass);
+    }
+  }
+
   private void ignoreMissingClass(DexType clazz) {
     missingClassesBuilder.ignoreNewMissingClass(clazz);
   }
@@ -2633,7 +2668,7 @@
       // maintains the library override. In the second enqueuer phase, the signature has been
       // desugared, and the second resolution maintains the the library override.
       if (instantiation.isClass()
-          && appView.rewritePrefix.hasRewrittenTypeInSignature(
+          && appView.typeRewriter.hasRewrittenTypeInSignature(
               method.getReference().proto, appView)) {
         DexMethod methodToResolve =
             DesugaredLibraryAPIConverter.methodWithVivifiedTypeInSignature(
@@ -2838,8 +2873,9 @@
   }
 
   private void traceFieldReference(
-      DexField field, FieldResolutionResult resolutionResult, ProgramMethod context) {
-    assert resolutionResult.isFailedOrUnknownResolution();
+      DexField field,
+      FailedOrUnknownFieldResolutionResult resolutionResult,
+      ProgramMethod context) {
     markTypeAsLive(field.getHolderType(), context);
     markTypeAsLive(field.getType(), context);
   }
@@ -3750,6 +3786,9 @@
     // Rebuild a new app only containing referenced types.
     Set<DexLibraryClass> libraryClasses = Sets.newIdentityHashSet();
     Set<DexClasspathClass> classpathClasses = Sets.newIdentityHashSet();
+    // Ensure all referenced non program types have their hierarchy built as live.
+    referencedNonProgramTypes.forEach(
+        clazz -> addLiveNonProgramType(clazz, false, this::ignoreMissingClasspathOrLibraryClass));
     for (ClasspathOrLibraryClass clazz : liveNonProgramTypes) {
       if (clazz.isLibraryClass()) {
         libraryClasses.add(clazz.asLibraryClass());
@@ -3908,14 +3947,16 @@
     }
     assert clazz.isProgramClass() || liveNonProgramTypes.contains(clazz)
         : "Expected type to be in live non-program types: " + clazz;
-    for (DexEncodedField field : clazz.fields()) {
-      if (clazz.isNotProgramClass() || isFieldReferenced(field)) {
-        assert verifyReferencedType(field.getReference().type, worklist, app);
+    if (clazz.isProgramClass()) {
+      for (DexEncodedField field : clazz.fields()) {
+        if (isFieldReferenced(field)) {
+          assert verifyReferencedType(field.getReference().type, worklist, app);
+        }
       }
-    }
-    for (DexEncodedMethod method : clazz.methods()) {
-      if (clazz.isNotProgramClass() || isMethodTargeted(method)) {
-        assert verifyReferencedMethod(method, worklist, app);
+      for (DexEncodedMethod method : clazz.methods()) {
+        if (isMethodTargeted(method)) {
+          assert verifyReferencedMethod(method, worklist, app);
+        }
       }
     }
     return true;
@@ -4075,8 +4116,7 @@
     InterfaceMethodProcessorFacade interfaceDesugaring =
         desugaring.getInterfaceMethodPostProcessingDesugaringR8(
             ExcludeDexResources, liveMethods::contains, interfaceProcessor);
-    CfPostProcessingDesugaringCollection.create(
-            appView, interfaceDesugaring, desugaring.getRetargetingInfo())
+    CfPostProcessingDesugaringCollection.create(appView, interfaceDesugaring)
         .postProcessingDesugaring(
             liveTypes.items, liveMethods::contains, eventConsumer, executorService);
 
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
index c82c666..cd46a71 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
@@ -28,6 +28,7 @@
   private final boolean allowConstantArgumentOptimization;
   private final boolean allowInlining;
   private final boolean allowMethodStaticizing;
+  private final boolean allowParameterReordering;
   private final boolean allowParameterTypeStrengthening;
   private final boolean allowReturnTypeStrengthening;
   private final boolean allowUnusedArgumentOptimization;
@@ -39,6 +40,7 @@
     this.allowConstantArgumentOptimization = builder.isConstantArgumentOptimizationAllowed();
     this.allowInlining = builder.isInliningAllowed();
     this.allowMethodStaticizing = builder.isMethodStaticizingAllowed();
+    this.allowParameterReordering = builder.isParameterReorderingAllowed();
     this.allowParameterTypeStrengthening = builder.isParameterTypeStrengtheningAllowed();
     this.allowReturnTypeStrengthening = builder.isReturnTypeStrengtheningAllowed();
     this.allowUnusedArgumentOptimization = builder.isUnusedArgumentOptimizationAllowed();
@@ -97,6 +99,16 @@
     return allowMethodStaticizing;
   }
 
+  public boolean isParameterReorderingAllowed(GlobalKeepInfoConfiguration configuration) {
+    return isOptimizationAllowed(configuration)
+        && isShrinkingAllowed(configuration)
+        && internalIsParameterReorderingAllowed();
+  }
+
+  boolean internalIsParameterReorderingAllowed() {
+    return allowParameterReordering;
+  }
+
   public boolean isParameterTypeStrengtheningAllowed(GlobalKeepInfoConfiguration configuration) {
     return isOptimizationAllowed(configuration)
         && isShrinkingAllowed(configuration)
@@ -158,6 +170,7 @@
     private boolean allowConstantArgumentOptimization;
     private boolean allowInlining;
     private boolean allowMethodStaticizing;
+    private boolean allowParameterReordering;
     private boolean allowParameterTypeStrengthening;
     private boolean allowReturnTypeStrengthening;
     private boolean allowUnusedArgumentOptimization;
@@ -173,6 +186,7 @@
       allowConstantArgumentOptimization = original.internalIsConstantArgumentOptimizationAllowed();
       allowInlining = original.internalIsInliningAllowed();
       allowMethodStaticizing = original.internalIsMethodStaticizingAllowed();
+      allowParameterReordering = original.internalIsParameterReorderingAllowed();
       allowParameterTypeStrengthening = original.internalIsParameterTypeStrengtheningAllowed();
       allowReturnTypeStrengthening = original.internalIsReturnTypeStrengtheningAllowed();
       allowUnusedArgumentOptimization = original.internalIsUnusedArgumentOptimizationAllowed();
@@ -256,6 +270,25 @@
       return setAllowMethodStaticizing(false);
     }
 
+    // Parameter reordering.
+
+    public boolean isParameterReorderingAllowed() {
+      return allowParameterReordering;
+    }
+
+    public Builder setAllowParameterReordering(boolean allowParameterReordering) {
+      this.allowParameterReordering = allowParameterReordering;
+      return self();
+    }
+
+    public Builder allowParameterReordering() {
+      return setAllowParameterReordering(true);
+    }
+
+    public Builder disallowParameterReordering() {
+      return setAllowParameterReordering(false);
+    }
+
     // Parameter type strengthening.
 
     public boolean isParameterTypeStrengtheningAllowed() {
@@ -361,6 +394,7 @@
               == other.internalIsConstantArgumentOptimizationAllowed()
           && isInliningAllowed() == other.internalIsInliningAllowed()
           && isMethodStaticizingAllowed() == other.internalIsMethodStaticizingAllowed()
+          && isParameterReorderingAllowed() == other.internalIsParameterReorderingAllowed()
           && isParameterTypeStrengtheningAllowed()
               == other.internalIsParameterTypeStrengtheningAllowed()
           && isReturnTypeStrengtheningAllowed() == other.internalIsReturnTypeStrengtheningAllowed()
@@ -382,6 +416,7 @@
           .disallowConstantArgumentOptimization()
           .disallowInlining()
           .disallowMethodStaticizing()
+          .disallowParameterReordering()
           .disallowParameterTypeStrengthening()
           .disallowReturnTypeStrengthening()
           .disallowUnusedArgumentOptimization()
@@ -395,6 +430,7 @@
           .allowConstantArgumentOptimization()
           .allowInlining()
           .allowMethodStaticizing()
+          .allowParameterReordering()
           .allowParameterTypeStrengthening()
           .allowReturnTypeStrengthening()
           .allowUnusedArgumentOptimization()
@@ -428,6 +464,11 @@
       return self();
     }
 
+    public Joiner disallowParameterReordering() {
+      builder.disallowParameterReordering();
+      return self();
+    }
+
     public Joiner disallowParameterTypeStrengthening() {
       builder.disallowParameterTypeStrengthening();
       return self();
@@ -464,6 +505,8 @@
           .applyIf(!joiner.builder.isInliningAllowed(), Joiner::disallowInlining)
           .applyIf(!joiner.builder.isMethodStaticizingAllowed(), Joiner::disallowMethodStaticizing)
           .applyIf(
+              !joiner.builder.isParameterReorderingAllowed(), Joiner::disallowParameterReordering)
+          .applyIf(
               !joiner.builder.isParameterTypeStrengtheningAllowed(),
               Joiner::disallowParameterTypeStrengthening)
           .applyIf(
diff --git a/src/main/java/com/android/tools/r8/shaking/L8TreePruner.java b/src/main/java/com/android/tools/r8/shaking/L8TreePruner.java
index 960ccd5..46e77e8 100644
--- a/src/main/java/com/android/tools/r8/shaking/L8TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/L8TreePruner.java
@@ -7,7 +7,7 @@
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
+import com.android.tools.r8.ir.desugar.TypeRewriter;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
@@ -28,18 +28,18 @@
 
   public L8TreePruner(InternalOptions options) {
     this.options = options;
-    backports.addAll(options.desugaredLibrarySpecification.getBackportCoreLibraryMember().keySet());
+    backports.addAll(options.machineDesugaredLibrarySpecification.getLegacyBackport().keySet());
     emulatedInterfaces.addAll(
-        options.desugaredLibrarySpecification.getEmulateLibraryInterface().keySet());
+        options.machineDesugaredLibrarySpecification.getEmulatedInterfaces().keySet());
   }
 
-  public DexApplication prune(DexApplication app, PrefixRewritingMapper rewritePrefix) {
+  public DexApplication prune(DexApplication app, TypeRewriter typeRewriter) {
     Map<DexType, DexProgramClass> typeMap = new IdentityHashMap<>();
     List<DexProgramClass> toKeep = new ArrayList<>();
     boolean pruneNestMember = false;
     for (DexProgramClass aClass : app.classes()) {
       typeMap.put(aClass.type, aClass);
-      if (rewritePrefix.hasRewrittenType(aClass.type, null)
+      if (typeRewriter.hasRewrittenType(aClass.type, null)
           || emulatedInterfaces.contains(aClass.type)) {
         toKeep.add(aClass);
       } else {
diff --git a/src/main/java/com/android/tools/r8/shaking/NoParameterReorderingRule.java b/src/main/java/com/android/tools/r8/shaking/NoParameterReorderingRule.java
new file mode 100644
index 0000000..c8d0e6c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/NoParameterReorderingRule.java
@@ -0,0 +1,84 @@
+// 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;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.util.List;
+
+public class NoParameterReorderingRule extends NoOptimizationBaseRule<NoParameterReorderingRule> {
+
+  public static final String RULE_NAME = "noparameterreordering";
+
+  public static class Builder
+      extends NoOptimizationBaseRule.Builder<NoParameterReorderingRule, Builder> {
+
+    Builder() {
+      super();
+    }
+
+    @Override
+    public NoParameterReorderingRule.Builder self() {
+      return this;
+    }
+
+    @Override
+    public NoParameterReorderingRule build() {
+      return new NoParameterReorderingRule(
+          origin,
+          getPosition(),
+          source,
+          buildClassAnnotations(),
+          classAccessFlags,
+          negatedClassAccessFlags,
+          classTypeNegated,
+          classType,
+          classNames,
+          buildInheritanceAnnotations(),
+          inheritanceClassName,
+          inheritanceIsExtends,
+          memberRules);
+    }
+  }
+
+  NoParameterReorderingRule(
+      Origin origin,
+      Position position,
+      String source,
+      List<ProguardTypeMatcher> classAnnotations,
+      ProguardAccessFlags classAccessFlags,
+      ProguardAccessFlags negatedClassAccessFlags,
+      boolean classTypeNegated,
+      ProguardClassType classType,
+      ProguardClassNameList classNames,
+      List<ProguardTypeMatcher> inheritanceAnnotations,
+      ProguardTypeMatcher inheritanceClassName,
+      boolean inheritanceIsExtends,
+      List<ProguardMemberRule> memberRules) {
+    super(
+        origin,
+        position,
+        source,
+        classAnnotations,
+        classAccessFlags,
+        negatedClassAccessFlags,
+        classTypeNegated,
+        classType,
+        classNames,
+        inheritanceAnnotations,
+        inheritanceClassName,
+        inheritanceIsExtends,
+        memberRules);
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  @Override
+  String typeString() {
+    return RULE_NAME;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 0f2ad2c..4ed94e5 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -514,6 +514,12 @@
           configurationBuilder.addRule(rule);
           return true;
         }
+        if (acceptString(NoParameterReorderingRule.RULE_NAME)) {
+          ProguardConfigurationRule rule =
+              parseNoOptimizationRule(optionStart, NoParameterReorderingRule.builder());
+          configurationBuilder.addRule(rule);
+          return true;
+        }
         if (acceptString(NoParameterTypeStrengtheningRule.RULE_NAME)) {
           ProguardConfigurationRule rule =
               parseNoOptimizationRule(optionStart, NoParameterTypeStrengtheningRule.builder());
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
index 41181b1..ea30b42 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
@@ -3,8 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClassResolutionResult;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -128,6 +130,12 @@
           specificTypes,
           new DexDefinitionSupplier() {
             @Override
+            public ClassResolutionResult contextIndependentDefinitionForWithResolutionResult(
+                DexType type) {
+              throw new Unreachable("Add support for multiple definitions with rule evaluation");
+            }
+
+            @Override
             public DexClass definitionFor(DexType type) {
               if (canReferenceDeadTypes) {
                 return appView.appInfo().definitionForWithoutExistenceAssert(type);
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 89b55dd..95ec8c3 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -267,6 +267,7 @@
           || rule instanceof KeepConstantArgumentRule
           || rule instanceof KeepUnusedReturnValueRule
           || rule instanceof NoMethodStaticizingRule
+          || rule instanceof NoParameterReorderingRule
           || rule instanceof NoParameterTypeStrengtheningRule
           || rule instanceof NoReturnTypeStrengtheningRule
           || rule instanceof KeepUnusedArgumentRule
@@ -1255,6 +1256,13 @@
             .asMethodJoiner()
             .disallowMethodStaticizing();
         context.markAsUsed();
+      } else if (context instanceof NoParameterReorderingRule) {
+        assert item.isProgramMethod();
+        dependentMinimumKeepInfo
+            .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+            .asMethodJoiner()
+            .disallowParameterReordering();
+        context.markAsUsed();
       } else if (context instanceof NoParameterTypeStrengtheningRule) {
         assert item.isProgramMethod();
         dependentMinimumKeepInfo
diff --git a/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java b/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java
index 1e4071f..dddb984 100644
--- a/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java
@@ -3,8 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.synthesis;
 
+import com.android.tools.r8.graph.ClassResolutionResult;
 import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexType;
 import com.google.common.collect.ImmutableList;
 import java.util.Collection;
@@ -55,7 +55,8 @@
   }
 
   @Override
-  public DexClass definitionFor(DexType type, Function<DexType, DexClass> baseDefinitionFor) {
+  public ClassResolutionResult definitionFor(
+      DexType type, Function<DexType, ClassResolutionResult> baseDefinitionFor) {
     // All synthetic types are committed to the application so lookup is just the base lookup.
     return baseDefinitionFor.apply(type);
   }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
index bbf4258..3b3aeea 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
@@ -129,12 +129,7 @@
       return;
     }
     assert hygienicType.toSourceString().startsWith(synthesizingContextType.toSourceString());
-    DexType rewrittenContext =
-        appView
-            .options()
-            .desugaredLibrarySpecification
-            .getEmulateLibraryInterface()
-            .get(synthesizingContextType);
+    DexType rewrittenContext = appView.typeRewriter.rewrittenContextType(synthesizingContextType);
     if (rewrittenContext == null) {
       return;
     }
@@ -148,7 +143,7 @@
         appView
             .dexItemFactory()
             .createType(getDescriptorFromClassBinaryName(rewrittenPrefix + suffix));
-    appView.rewritePrefix.rewriteType(hygienicType, rewrittenType);
+    appView.typeRewriter.rewriteType(hygienicType, rewrittenType);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinitionsProvider.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinitionsProvider.java
index e66401e..a428331 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinitionsProvider.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinitionsProvider.java
@@ -3,10 +3,11 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.synthesis;
 
-import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.ClassResolutionResult;
 import com.android.tools.r8.graph.DexType;
 import java.util.function.Function;
 
 public interface SyntheticDefinitionsProvider {
-  DexClass definitionFor(DexType type, Function<DexType, DexClass> baseDefinitionFor);
+  ClassResolutionResult definitionFor(
+      DexType type, Function<DexType, ClassResolutionResult> baseDefinitionFor);
 }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 0cb0137..1e9ffb4 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -10,6 +10,8 @@
 import com.android.tools.r8.features.ClassToFeatureSplitMap;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClassResolutionResult;
+import com.android.tools.r8.graph.ClasspathMethod;
 import com.android.tools.r8.graph.ClasspathOrLibraryClass;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
@@ -175,7 +177,8 @@
   // Predicates and accessors.
 
   @Override
-  public DexClass definitionFor(DexType type, Function<DexType, DexClass> baseDefinitionFor) {
+  public ClassResolutionResult definitionFor(
+      DexType type, Function<DexType, ClassResolutionResult> baseDefinitionFor) {
     DexClass clazz = null;
     SyntheticKind kind = null;
     SyntheticDefinition<?, ?, ?> item = pending.nonLegacyDefinitions.get(type);
@@ -187,7 +190,8 @@
     }
     if (clazz != null) {
       assert kind != null;
-      assert baseDefinitionFor.apply(type) == null || kind.mayOverridesNonProgramType
+      assert !baseDefinitionFor.apply(type).hasClassResolutionResult()
+              || kind.mayOverridesNonProgramType
           : "Pending synthetic definition also present in the active program: " + type;
       return clazz;
     }
@@ -483,14 +487,13 @@
       AppView<?> appView,
       DexType type) {
     DexType rewrittenContextType =
-        appView.rewritePrefix.rewrittenContextType(
-            outerContext.getSynthesizingContextType(), appView);
+        appView.typeRewriter.rewrittenContextType(outerContext.getSynthesizingContextType());
     if (rewrittenContextType == null) {
       return;
     }
     SynthesizingContext synthesizingContext = SynthesizingContext.fromType(rewrittenContextType);
     DexType rewrittenType = contextToType.apply(synthesizingContext);
-    appView.rewritePrefix.rewriteType(type, rewrittenType);
+    appView.typeRewriter.rewriteType(type, rewrittenType);
   }
 
   public DexProgramClass createClass(
@@ -650,10 +653,11 @@
       SyntheticKind kind,
       DexType contextType,
       AppView<?> appView,
-      Consumer<SyntheticClasspathClassBuilder> classConsumer) {
+      Consumer<SyntheticClasspathClassBuilder> classConsumer,
+      Consumer<DexClasspathClass> onCreationConsumer) {
     SynthesizingContext outerContext = SynthesizingContext.fromType(contextType);
     return internalEnsureFixedClasspathClass(
-        kind, classConsumer, ignored -> {}, outerContext, appView);
+        kind, classConsumer, onCreationConsumer, outerContext, appView);
   }
 
   public DexClasspathClass ensureFixedClasspathClass(
@@ -669,7 +673,23 @@
         kind, classConsumer, onCreationConsumer, outerContext, appView);
   }
 
-  public DexClassAndMethod ensureFixedClasspathClassMethod(
+  public ClasspathMethod ensureFixedClasspathMethodFromType(
+      DexString methodName,
+      DexProto methodProto,
+      SyntheticKind kind,
+      DexType contextType,
+      AppView<?> appView,
+      Consumer<SyntheticClasspathClassBuilder> classConsumer,
+      Consumer<DexClasspathClass> onCreationConsumer,
+      Consumer<SyntheticMethodBuilder> buildMethodCallback) {
+    DexClasspathClass clazz =
+        ensureFixedClasspathClassFromType(
+            kind, contextType, appView, classConsumer, onCreationConsumer);
+    return internalEnsureFixedClasspathMethod(
+        methodName, methodProto, kind, appView, buildMethodCallback, clazz);
+  }
+
+  public ClasspathMethod ensureFixedClasspathClassMethod(
       DexString methodName,
       DexProto methodProto,
       SyntheticKind kind,
@@ -681,6 +701,17 @@
     DexClasspathClass clazz =
         ensureFixedClasspathClass(
             kind, context, appView, buildClassCallback, onClassCreationCallback);
+    return internalEnsureFixedClasspathMethod(
+        methodName, methodProto, kind, appView, buildMethodCallback, clazz);
+  }
+
+  private ClasspathMethod internalEnsureFixedClasspathMethod(
+      DexString methodName,
+      DexProto methodProto,
+      SyntheticKind kind,
+      AppView<?> appView,
+      Consumer<SyntheticMethodBuilder> buildMethodCallback,
+      DexClasspathClass clazz) {
     DexMethod methodReference =
         appView.dexItemFactory().createMethod(clazz.getType(), methodProto, methodName);
     DexEncodedMethod methodDefinition =
@@ -695,7 +726,7 @@
               buildMethodCallback.accept(methodBuilder.disableAndroidApiLevelCheck());
             },
             emptyConsumer());
-    return DexClassAndMethod.create(clazz, methodDefinition);
+    return new ClasspathMethod(clazz, methodDefinition);
   }
 
   @SuppressWarnings("unchecked")
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
index b7861e3..ec8a010 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
@@ -180,6 +180,10 @@
   }
 
   private Code getCodeObject(DexMethod methodSignature) {
+    if (codeGenerator == null) {
+      // If the method is on the classpath then no code is needed.
+      return null;
+    }
     return codeGenerator.generate(methodSignature);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index 62c7c6f..4d8b529 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -31,6 +31,7 @@
     COMPANION_CLASS("$-CC", 2, false, true),
     EMULATED_INTERFACE_CLASS("$-EL", 3, false, true),
     RETARGET_CLASS("RetargetClass", 20, false, true),
+    RETARGET_STUB("", 36, false, true),
     RETARGET_INTERFACE("RetargetInterface", 21, false, true),
     WRAPPER("$Wrapper", 22, false, true),
     VIVIFIED_WRAPPER("$VivifiedWrapper", 23, false, true),
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
index 38934f2..6bec653 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
@@ -42,12 +42,13 @@
   R(30),
   S(31),
   Sv2(32),
+  T(33),
   ANDROID_PLATFORM(10000);
 
   // When updating LATEST and a new version goes stable, add a new api-versions.xml to third_party
   // and update the version and generated jar in AndroidApiDatabaseBuilderGeneratorTest.
-  // TODO(b/204738868): Update API database for Sv2.
-  public static final AndroidApiLevel LATEST = Sv2;
+  // TODO(b/204738868): Update API database for Sv2 / T when they are ready.
+  public static final AndroidApiLevel LATEST = T;
 
   private final int level;
 
@@ -96,6 +97,7 @@
 
   public static AndroidApiLevel getAndroidApiLevel(int apiLevel) {
     assert apiLevel > 0;
+    assert T == LATEST; // This has to be updated when we add new api levels.
     assert ANDROID_PLATFORM.isGreaterThan(LATEST);
     switch (apiLevel) {
       case 1:
@@ -162,11 +164,11 @@
         return S;
       case 32:
         return Sv2;
+      case 33:
+        return T;
       case 10000:
         return ANDROID_PLATFORM;
       default:
-        // This has to be updated when we add new api levels.
-        assert Sv2 == LATEST;
         return LATEST;
     }
   }
diff --git a/src/main/java/com/android/tools/r8/utils/DexVersion.java b/src/main/java/com/android/tools/r8/utils/DexVersion.java
index 624e70e..ac962fa 100644
--- a/src/main/java/com/android/tools/r8/utils/DexVersion.java
+++ b/src/main/java/com/android/tools/r8/utils/DexVersion.java
@@ -41,6 +41,7 @@
         // ANDROID_PLATFORM is an unknown higher api version we therefore choose the highest known
         // version.
       case ANDROID_PLATFORM:
+      case T:
       case Sv2:
       case S:
       case R:
diff --git a/src/main/java/com/android/tools/r8/utils/ForEachUtils.java b/src/main/java/com/android/tools/r8/utils/ForEachUtils.java
new file mode 100644
index 0000000..532acf7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ForEachUtils.java
@@ -0,0 +1,20 @@
+// 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.utils;
+
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+public class ForEachUtils {
+
+  public static <T> boolean allMatch(Consumer<Consumer<T>> forEach, Predicate<T> predicate) {
+    BooleanBox acc = new BooleanBox(true);
+    forEach.accept(
+        value -> {
+          acc.and(predicate.test(value));
+        });
+    return acc.isTrue();
+  }
+}
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 ec5dca6..066ca9f 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -52,10 +52,13 @@
 import com.android.tools.r8.horizontalclassmerging.Policy;
 import com.android.tools.r8.inspector.internal.InspectorImpl;
 import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
-import com.android.tools.r8.ir.desugar.PrefixRewritingMapper.MachineDesugarPrefixRewritingMapper;
+import com.android.tools.r8.ir.desugar.TypeRewriter;
+import com.android.tools.r8.ir.desugar.TypeRewriter.MachineDesugarPrefixRewritingMapper;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.HumanToMachineSpecificationConverter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.LegacyToHumanSpecificationConverter;
 import com.android.tools.r8.ir.desugar.nest.Nest;
 import com.android.tools.r8.ir.optimize.Inliner;
 import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
@@ -85,7 +88,9 @@
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import java.io.IOException;
 import java.io.PrintStream;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -390,8 +395,8 @@
     if (isGeneratingDex() || desugarState == DesugarState.ON) {
       marker.setMinApi(getMinApiLevel().getLevel());
     }
-    if (desugaredLibrarySpecification.getIdentifier() != null) {
-      marker.setDesugaredLibraryIdentifiers(desugaredLibrarySpecification.getIdentifier());
+    if (machineDesugaredLibrarySpecification.getIdentifier() != null) {
+      marker.setDesugaredLibraryIdentifiers(machineDesugaredLibrarySpecification.getIdentifier());
     }
     if (Version.isDevelopmentVersion()) {
       marker.setSha1(VersionProperties.INSTANCE.getSha());
@@ -438,7 +443,7 @@
   }
 
   public boolean isDesugaredLibraryCompilation() {
-    return desugaredLibrarySpecification.isLibraryCompilation();
+    return machineDesugaredLibrarySpecification.isLibraryCompilation();
   }
 
   public boolean isRelocatorCompilation() {
@@ -883,18 +888,49 @@
   // If non-null, configuration must be passed to the consumer.
   public StringConsumer configurationConsumer = null;
 
-  // If null, no desugaring of library is performed.
-  // If non null it contains flags describing library desugaring.
-  public LegacyDesugaredLibrarySpecification desugaredLibrarySpecification =
-      LegacyDesugaredLibrarySpecification.empty();
-
-  public PrefixRewritingMapper getPrefixRewritingMapper() {
-    if (testing.machineDesugaredLibrarySpecification != null) {
-      return new MachineDesugarPrefixRewritingMapper(
-          desugaredLibrarySpecification.getPrefixRewritingMapper(),
-          testing.machineDesugaredLibrarySpecification.getRewritingFlags());
+  public void setDesugaredLibrarySpecification(
+      LegacyDesugaredLibrarySpecification specification, AndroidApp app) {
+    if (specification.isEmptyConfiguration()) {
+      return;
     }
-    return desugaredLibrarySpecification.getPrefixRewritingMapper();
+    try {
+      HumanDesugaredLibrarySpecification human =
+          new LegacyToHumanSpecificationConverter()
+              .convert(specification, app.getLibraryResourceProviders(), this);
+      machineDesugaredLibrarySpecification =
+          new HumanToMachineSpecificationConverter()
+              .convert(
+                  human,
+                  specification.isLibraryCompilation() ? app.getProgramResourceProviders() : null,
+                  app.getLibraryResourceProviders(),
+                  this);
+    } catch (IOException e) {
+      reporter.error(new ExceptionDiagnostic(e, Origin.unknown()));
+    }
+  }
+
+  public void setDesugaredLibrarySpecificationForTesting(
+      LegacyDesugaredLibrarySpecification specification, Path desugaredJDKLib, Path library)
+      throws IOException {
+    HumanDesugaredLibrarySpecification human =
+        new LegacyToHumanSpecificationConverter().convert(specification, library, this);
+    machineDesugaredLibrarySpecification =
+        new HumanToMachineSpecificationConverter()
+            .convert(
+                human,
+                specification.isLibraryCompilation() ? desugaredJDKLib : null,
+                library,
+                this);
+  }
+
+  // Contains flags describing library desugaring.
+  public MachineDesugaredLibrarySpecification machineDesugaredLibrarySpecification =
+      MachineDesugaredLibrarySpecification.empty();
+
+  public TypeRewriter getTypeRewriter() {
+    return machineDesugaredLibrarySpecification.getRewriteType().isEmpty()
+        ? TypeRewriter.empty()
+        : new MachineDesugarPrefixRewritingMapper(machineDesugaredLibrarySpecification);
   }
 
   public boolean relocatorCompilation = false;
@@ -1599,9 +1635,6 @@
 
     public Consumer<Deque<ProgramMethodSet>> waveModifier = waves -> {};
 
-    // Meant to replace desugaredLibrarySpecification, set only from tests at the moment.
-    public MachineDesugaredLibrarySpecification machineDesugaredLibrarySpecification = null;
-
     /**
      * If this flag is enabled, we will also compute the set of possible targets for invoke-
      * interface and invoke-virtual instructions that target a library method, and add the
@@ -1903,10 +1936,6 @@
     return isGeneratingDex() && hasMinApi(AndroidApiLevel.N);
   }
 
-  public boolean canUseJavaUtilObjectsRequireNonNull() {
-    return isGeneratingDex() && hasMinApi(AndroidApiLevel.K);
-  }
-
   public boolean canUseSuppressedExceptions() {
     // TODO(b/214239152): Suppressed exceptions are @hide from at least 4.0.1 / Android I / API 14.
     return !isDesugaring() || hasMinApi(AndroidApiLevel.K);
@@ -2309,4 +2338,16 @@
   public boolean canHaveSuperInvokeBug() {
     return getMinApiLevel().isLessThan(AndroidApiLevel.N);
   }
+
+  // Some Dalvik and Art MVs does not support interface invokes to Object
+  // members not explicitly defined on the symbolic reference of the
+  // interface invoke. In these cases rewrite to a virtual invoke with
+  // the symbolic reference java.lang.Object.
+  //
+  // javac started generating code like this with the fix for JDK-8272564.
+  //
+  // See b/218298666.
+  public boolean canHaveInvokeInterfaceToObjectMethodBug() {
+    return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.O);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
index 3ae44fb..b0a6710 100644
--- a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils;
 
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ProgramMethod;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
@@ -122,6 +124,17 @@
         items.entrySet(), arg -> consumer.apply(arg.getKey(), arg.getValue()), executorService);
   }
 
+  public static <E extends Exception> void processMethods(
+      AppView<?> appView,
+      ThrowingConsumer<ProgramMethod, E> consumer,
+      ExecutorService executorService)
+      throws ExecutionException {
+    processItems(
+        appView.appInfo().classes(),
+        clazz -> clazz.forEachProgramMethod(consumer::acceptWithRuntimeException),
+        executorService);
+  }
+
   public static void awaitFutures(Iterable<? extends Future<?>> futures)
       throws ExecutionException {
     Iterator<? extends Future<?>> futureIterator = futures.iterator();
diff --git a/src/main/java/com/android/tools/r8/utils/ThrowingConsumer.java b/src/main/java/com/android/tools/r8/utils/ThrowingConsumer.java
index 61557e5..2b6eb66 100644
--- a/src/main/java/com/android/tools/r8/utils/ThrowingConsumer.java
+++ b/src/main/java/com/android/tools/r8/utils/ThrowingConsumer.java
@@ -14,4 +14,16 @@
 @FunctionalInterface
 public interface ThrowingConsumer<T, E extends Throwable> {
   void accept(T t) throws E;
+
+  default void acceptWithRuntimeException(T t) {
+    try {
+      accept(t);
+    } catch (Throwable throwable) {
+      RuntimeException runtimeException =
+          throwable instanceof RuntimeException
+              ? (RuntimeException) throwable
+              : new RuntimeException(throwable);
+      throw runtimeException;
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureSet.java b/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureSet.java
index 8ef1277..901ef75 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureSet.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureSet.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexMethodSignature;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
@@ -36,6 +37,10 @@
     return new DexMethodSignatureSet(new HashSet<>(collection.backing));
   }
 
+  public static DexMethodSignatureSet createConcurrent() {
+    return new DexMethodSignatureSet(Sets.newConcurrentHashSet());
+  }
+
   public static DexMethodSignatureSet createLinked() {
     return new DexMethodSignatureSet(new LinkedHashSet<>());
   }
diff --git a/src/main/java/com/android/tools/r8/utils/structural/Ordered.java b/src/main/java/com/android/tools/r8/utils/structural/Ordered.java
index 9c9a588..61508a0 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/Ordered.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/Ordered.java
@@ -60,4 +60,8 @@
   default boolean isGreaterThanOrEqualTo(T other) {
     return compareTo(other) >= 0;
   }
+
+  default boolean betweenBothIncluded(T lower, T upper) {
+    return isGreaterThanOrEqualTo(lower) && isLessThanOrEqualTo(upper);
+  }
 }
diff --git a/src/test/examplesJava18/jdk8272564/A.java b/src/test/examplesJava18/jdk8272564/A.java
new file mode 100644
index 0000000..f548fc0
--- /dev/null
+++ b/src/test/examplesJava18/jdk8272564/A.java
@@ -0,0 +1,7 @@
+// 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 jdk8272564;
+
+public class A implements I {}
diff --git a/src/test/examplesJava18/jdk8272564/B.java b/src/test/examplesJava18/jdk8272564/B.java
new file mode 100644
index 0000000..678d2d5
--- /dev/null
+++ b/src/test/examplesJava18/jdk8272564/B.java
@@ -0,0 +1,7 @@
+// 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 jdk8272564;
+
+public class B implements J {}
diff --git a/src/test/examplesJava18/jdk8272564/C.java b/src/test/examplesJava18/jdk8272564/C.java
new file mode 100644
index 0000000..3308d8e
--- /dev/null
+++ b/src/test/examplesJava18/jdk8272564/C.java
@@ -0,0 +1,7 @@
+// 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 jdk8272564;
+
+public class C implements K {}
diff --git a/src/test/examplesJava18/jdk8272564/I.java b/src/test/examplesJava18/jdk8272564/I.java
new file mode 100644
index 0000000..1694eea
--- /dev/null
+++ b/src/test/examplesJava18/jdk8272564/I.java
@@ -0,0 +1,13 @@
+// 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 jdk8272564;
+
+interface I {
+  public String toString();
+
+  public int hashCode();
+
+  public boolean equals(Object other);
+}
diff --git a/src/test/examplesJava18/jdk8272564/J.java b/src/test/examplesJava18/jdk8272564/J.java
new file mode 100644
index 0000000..38b2551
--- /dev/null
+++ b/src/test/examplesJava18/jdk8272564/J.java
@@ -0,0 +1,7 @@
+// 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 jdk8272564;
+
+interface J extends I {}
diff --git a/src/test/examplesJava18/jdk8272564/K.java b/src/test/examplesJava18/jdk8272564/K.java
new file mode 100644
index 0000000..726f781
--- /dev/null
+++ b/src/test/examplesJava18/jdk8272564/K.java
@@ -0,0 +1,7 @@
+// 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 jdk8272564;
+
+interface K {}
diff --git a/src/test/examplesJava18/jdk8272564/Main.java b/src/test/examplesJava18/jdk8272564/Main.java
new file mode 100644
index 0000000..c5963fd
--- /dev/null
+++ b/src/test/examplesJava18/jdk8272564/Main.java
@@ -0,0 +1,48 @@
+// 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 jdk8272564;
+
+public class Main {
+  // From javac in JDK-18 all of the following three invokes of toString are compiled to
+  // invokeinterface. Prior to JDK 18 the last two where compiled to invokevirtual.
+  // See https://bugs.openjdk.java.net/browse/JDK-8272564.
+  static void f(I i, J j, K k) {
+    i.toString();
+    j.toString();
+    k.toString();
+  }
+
+  // Remaining public methods on Object.
+  static void g(I i, J j, K k) throws InterruptedException {
+    i.hashCode();
+    j.hashCode();
+    k.hashCode();
+    i.equals(new Object());
+    j.equals(new Object());
+    k.equals(new Object());
+    i.getClass();
+    j.getClass();
+    k.getClass();
+    i.notify();
+    j.notify();
+    k.notify();
+    i.notifyAll();
+    j.notifyAll();
+    k.notifyAll();
+    i.wait();
+    j.wait();
+    k.wait();
+    i.wait(1L);
+    j.wait(1L);
+    k.wait(1L);
+    i.wait(1L, 1);
+    j.wait(1L, 1);
+    k.wait(1L, 1);
+  }
+
+  public static void main(String[] args) {
+    f(new A(), new B(), new C());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/BackportedMethodListTest.java b/src/test/java/com/android/tools/r8/BackportedMethodListTest.java
index 706126c..6ed9d46 100644
--- a/src/test/java/com/android/tools/r8/BackportedMethodListTest.java
+++ b/src/test/java/com/android/tools/r8/BackportedMethodListTest.java
@@ -155,8 +155,8 @@
               .setConsumer(new ListStringConsumer())
               .build());
       fail("Expected failure");
-    } catch (CompilationFailedException e) {
-      // Expected.
+    } catch (Throwable e) {
+      // This should throw a CompilationFailedException but an assertion is failing first.
     }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java b/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java
index accc8af..318b3c2 100644
--- a/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java
+++ b/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java
@@ -9,6 +9,7 @@
 
 import com.android.tools.r8.TestRuntime.CfRuntime;
 import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.diagnostic.MissingDefinitionsDiagnostic;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.util.List;
@@ -109,23 +110,28 @@
         // java.util.concurrent.Flow$Subscriber is not present on Android.
         testBuilder
             .run(parameters.getRuntime(), "MySubscriber")
-            .assertFailureWithErrorThatMatches(
-                anyOf(
-                    // Dalvik 4.0.4
-                    containsString("java.lang.NoClassDefFoundError: MySubscriber"),
-                    // Other Dalviks.
-                    containsString(
-                        "java.lang.ClassNotFoundException: Didn't find class \"MySubscriber\""),
-                    // Art.
-                    containsString(
-                        "java.lang.NoClassDefFoundError: "
-                            + "Failed resolution of: Ljava/util/concurrent/Flow$Subscriber;"),
-                    // Art 10.
-                    containsString("java.lang.ClassNotFoundException: MySubscriber"),
-                    // Art 11+.
-                    containsString(
-                        "java.lang.ClassNotFoundException: "
-                            + "java.util.concurrent.SubmissionPublisher")));
+            .applyIf(
+                parameters.asDexRuntime().getVersion().isOlderThan(DexVm.Version.V13_MASTER),
+                b ->
+                    b.assertFailureWithErrorThatMatches(
+                        anyOf(
+                            // Dalvik 4.0.4
+                            containsString("java.lang.NoClassDefFoundError: MySubscriber"),
+                            // Other Dalviks.
+                            containsString(
+                                "java.lang.ClassNotFoundException: Didn't find class"
+                                    + " \"MySubscriber\""),
+                            // Art.
+                            containsString(
+                                "java.lang.NoClassDefFoundError: Failed resolution of:"
+                                    + " Ljava/util/concurrent/Flow$Subscriber;"),
+                            // Art 10.
+                            containsString("java.lang.ClassNotFoundException: MySubscriber"),
+                            // Art 11+.
+                            containsString(
+                                "java.lang.ClassNotFoundException: "
+                                    + "java.util.concurrent.SubmissionPublisher"))),
+                b -> b.assertSuccessWithOutputLines("Got : 1", "Got : 2", "Got : 3", "Done"));
       } else {
         if (parameters.getRuntime().asCf().getVm() == CfVm.JDK8) {
           // java.util.concurrent.Flow$Subscriber not present in JDK8.
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index 9740064..7a8c4e4 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.origin.EmbeddedOrigin;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.FileUtils;
@@ -36,6 +37,7 @@
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
+import java.util.function.Function;
 import java.util.zip.ZipFile;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -586,6 +588,13 @@
     assertEquals(AssertionTransformationScope.ALL, entries.get(0).getScope());
   }
 
+  private void checkSingleForceAllAssertion(
+      List<AssertionsConfiguration> entries, Function<AssertionsConfiguration, Boolean> check) {
+    assertEquals(1, entries.size());
+    assertTrue(check.apply(entries.get(0)));
+    assertEquals(AssertionTransformationScope.ALL, entries.get(0).getScope());
+  }
+
   private void checkSingleForceClassAndPackageAssertion(
       List<AssertionsConfiguration> entries, AssertionTransformation transformation) {
     assertEquals(2, entries.size());
@@ -597,6 +606,19 @@
     assertEquals("PackageName", entries.get(1).getValue());
   }
 
+  private void checkSingleForceClassAndPackageAssertion(
+      List<AssertionsConfiguration> entries,
+      Function<AssertionsConfiguration, Boolean> checkClass,
+      Function<AssertionsConfiguration, Boolean> checkPackage) {
+    assertEquals(2, entries.size());
+    assertTrue(checkClass.apply(entries.get(0)));
+    assertEquals(AssertionTransformationScope.CLASS, entries.get(0).getScope());
+    assertEquals("ClassName", entries.get(0).getValue());
+    assertTrue(checkPackage.apply(entries.get(1)));
+    assertEquals(AssertionTransformationScope.PACKAGE, entries.get(1).getScope());
+    assertEquals("PackageName", entries.get(1).getValue());
+  }
+
   @Test
   public void forceAssertionOption() throws Exception {
     checkSingleForceAllAssertion(
@@ -622,6 +644,47 @@
                 "--force-passthrough-assertions:PackageName...")
             .getAssertionsConfiguration(),
         AssertionTransformation.PASSTHROUGH);
+    checkSingleForceAllAssertion(
+        parse("--force-assertions-handler:com.example.MyHandler.handler")
+            .getAssertionsConfiguration(),
+        configuration ->
+            configuration.isAssertionHandler()
+                && configuration
+                    .getAssertionHandler()
+                    .getHolderClass()
+                    .equals(Reference.classFromDescriptor("Lcom/example/MyHandler;"))
+                && configuration.getAssertionHandler().getMethodName().equals("handler")
+                && configuration
+                    .getAssertionHandler()
+                    .getMethodDescriptor()
+                    .equals("(Ljava/lang/Throwable;)V"));
+    checkSingleForceClassAndPackageAssertion(
+        parse(
+                "--force-assertions-handler:com.example.MyHandler.handler1:ClassName",
+                "--force-assertions-handler:com.example.MyHandler.handler2:PackageName...")
+            .getAssertionsConfiguration(),
+        configuration ->
+            configuration.isAssertionHandler()
+                && configuration
+                    .getAssertionHandler()
+                    .getHolderClass()
+                    .equals(Reference.classFromDescriptor("Lcom/example/MyHandler;"))
+                && configuration.getAssertionHandler().getMethodName().equals("handler1")
+                && configuration
+                    .getAssertionHandler()
+                    .getMethodDescriptor()
+                    .equals("(Ljava/lang/Throwable;)V"),
+        configuration ->
+            configuration.isAssertionHandler()
+                && configuration
+                    .getAssertionHandler()
+                    .getHolderClass()
+                    .equals(Reference.classFromDescriptor("Lcom/example/MyHandler;"))
+                && configuration.getAssertionHandler().getMethodName().equals("handler2")
+                && configuration
+                    .getAssertionHandler()
+                    .getMethodDescriptor()
+                    .equals("(Ljava/lang/Throwable;)V"));
   }
 
   @Test(expected = CompilationFailedException.class)
@@ -632,9 +695,18 @@
 
   @Test
   public void desugaredLibrary() throws CompilationFailedException {
-    D8Command d8Command = parse("--desugared-lib", "src/library_desugar/desugar_jdk_libs.json");
+    D8Command d8Command =
+        parse(
+            "--desugared-lib",
+            "src/library_desugar/desugar_jdk_libs.json",
+            "--lib",
+            ToolHelper.getAndroidJar(AndroidApiLevel.P).toString());
     assertFalse(
-        d8Command.getInternalOptions().desugaredLibrarySpecification.getRewritePrefix().isEmpty());
+        d8Command
+            .getInternalOptions()
+            .machineDesugaredLibrarySpecification
+            .getRewriteType()
+            .isEmpty());
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/L8CommandTest.java b/src/test/java/com/android/tools/r8/L8CommandTest.java
index e1902f4..2e54294 100644
--- a/src/test/java/com/android/tools/r8/L8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/L8CommandTest.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.origin.EmbeddedOrigin;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
@@ -30,6 +31,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.function.Function;
 import org.hamcrest.Matcher;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -355,9 +357,17 @@
   @Test
   public void desugaredLibrary() throws CompilationFailedException {
     L8Command l8Command =
-        parse("--desugared-lib", ToolHelper.getDesugarLibJsonForTesting().toString());
+        parse(
+            "--desugared-lib",
+            ToolHelper.getDesugarLibJsonForTesting().toString(),
+            "--lib",
+            ToolHelper.getAndroidJar(AndroidApiLevel.P).toString());
     assertFalse(
-        l8Command.getInternalOptions().desugaredLibrarySpecification.getRewritePrefix().isEmpty());
+        l8Command
+            .getInternalOptions()
+            .machineDesugaredLibrarySpecification
+            .getRewriteType()
+            .isEmpty());
   }
 
   private void checkSingleForceAllAssertion(
@@ -367,6 +377,13 @@
     assertEquals(AssertionTransformationScope.ALL, entries.get(0).getScope());
   }
 
+  private void checkSingleForceAllAssertion(
+      List<AssertionsConfiguration> entries, Function<AssertionsConfiguration, Boolean> check) {
+    assertEquals(1, entries.size());
+    assertTrue(check.apply(entries.get(0)));
+    assertEquals(AssertionTransformationScope.ALL, entries.get(0).getScope());
+  }
+
   private void checkSingleForceClassAndPackageAssertion(
       List<AssertionsConfiguration> entries, AssertionTransformation transformation) {
     assertEquals(2, entries.size());
@@ -378,6 +395,19 @@
     assertEquals("PackageName", entries.get(1).getValue());
   }
 
+  private void checkSingleForceClassAndPackageAssertion(
+      List<AssertionsConfiguration> entries,
+      Function<AssertionsConfiguration, Boolean> checkClass,
+      Function<AssertionsConfiguration, Boolean> checkPackage) {
+    assertEquals(2, entries.size());
+    assertTrue(checkClass.apply(entries.get(0)));
+    assertEquals(AssertionTransformationScope.CLASS, entries.get(0).getScope());
+    assertEquals("ClassName", entries.get(0).getValue());
+    assertTrue(checkPackage.apply(entries.get(1)));
+    assertEquals(AssertionTransformationScope.PACKAGE, entries.get(1).getScope());
+    assertEquals("PackageName", entries.get(1).getValue());
+  }
+
   @Test
   public void forceAssertionOption() throws Exception {
     checkSingleForceAllAssertion(
@@ -425,6 +455,52 @@
                 ToolHelper.getDesugarLibJsonForTesting().toString())
             .getAssertionsConfiguration(),
         AssertionTransformation.PASSTHROUGH);
+    checkSingleForceAllAssertion(
+        parse(
+                "--force-assertions-handler:com.example.MyHandler.handler",
+                "--desugared-lib",
+                ToolHelper.getDesugarLibJsonForTesting().toString())
+            .getAssertionsConfiguration(),
+        configuration ->
+            configuration.isAssertionHandler()
+                && configuration
+                    .getAssertionHandler()
+                    .getHolderClass()
+                    .equals(Reference.classFromDescriptor("Lcom/example/MyHandler;"))
+                && configuration.getAssertionHandler().getMethodName().equals("handler")
+                && configuration
+                    .getAssertionHandler()
+                    .getMethodDescriptor()
+                    .equals("(Ljava/lang/Throwable;)V"));
+    checkSingleForceClassAndPackageAssertion(
+        parse(
+                "--force-assertions-handler:com.example.MyHandler.handler1:ClassName",
+                "--force-assertions-handler:com.example.MyHandler.handler2:PackageName...",
+                "--desugared-lib",
+                ToolHelper.getDesugarLibJsonForTesting().toString())
+            .getAssertionsConfiguration(),
+        configuration ->
+            configuration.isAssertionHandler()
+                && configuration
+                    .getAssertionHandler()
+                    .getHolderClass()
+                    .equals(Reference.classFromDescriptor("Lcom/example/MyHandler;"))
+                && configuration.getAssertionHandler().getMethodName().equals("handler1")
+                && configuration
+                    .getAssertionHandler()
+                    .getMethodDescriptor()
+                    .equals("(Ljava/lang/Throwable;)V"),
+        configuration ->
+            configuration.isAssertionHandler()
+                && configuration
+                    .getAssertionHandler()
+                    .getHolderClass()
+                    .equals(Reference.classFromDescriptor("Lcom/example/MyHandler;"))
+                && configuration.getAssertionHandler().getMethodName().equals("handler2")
+                && configuration
+                    .getAssertionHandler()
+                    .getMethodDescriptor()
+                    .equals("(Ljava/lang/Throwable;)V"));
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/L8TestBuilder.java b/src/test/java/com/android/tools/r8/L8TestBuilder.java
index 44202c6..7a42ab1 100644
--- a/src/test/java/com/android/tools/r8/L8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/L8TestBuilder.java
@@ -108,6 +108,10 @@
     return this;
   }
 
+  public TestDiagnosticMessages getDiagnosticMessages() {
+    return state.getDiagnosticsMessages();
+  }
+
   public L8TestBuilder setDebug() {
     this.mode = CompilationMode.DEBUG;
     return this;
diff --git a/src/test/java/com/android/tools/r8/NoParameterReordering.java b/src/test/java/com/android/tools/r8/NoParameterReordering.java
new file mode 100644
index 0000000..0a82d0f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/NoParameterReordering.java
@@ -0,0 +1,11 @@
+// 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;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD})
+public @interface NoParameterReordering {}
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index 0b48fc2..536f5bb 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.origin.EmbeddedOrigin;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.ThreadUtils;
@@ -36,6 +37,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.function.Function;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 import java.util.zip.ZipOutputStream;
@@ -717,6 +719,13 @@
     assertEquals(AssertionTransformationScope.ALL, entries.get(0).getScope());
   }
 
+  private void checkSingleForceAllAssertion(
+      List<AssertionsConfiguration> entries, Function<AssertionsConfiguration, Boolean> check) {
+    assertEquals(1, entries.size());
+    assertTrue(check.apply(entries.get(0)));
+    assertEquals(AssertionTransformationScope.ALL, entries.get(0).getScope());
+  }
+
   private void checkSingleForceClassAndPackageAssertion(
       List<AssertionsConfiguration> entries, AssertionTransformation transformation) {
     assertEquals(2, entries.size());
@@ -728,6 +737,19 @@
     assertEquals("PackageName", entries.get(1).getValue());
   }
 
+  private void checkSingleForceClassAndPackageAssertion(
+      List<AssertionsConfiguration> entries,
+      Function<AssertionsConfiguration, Boolean> checkClass,
+      Function<AssertionsConfiguration, Boolean> checkPackage) {
+    assertEquals(2, entries.size());
+    assertTrue(checkClass.apply(entries.get(0)));
+    assertEquals(AssertionTransformationScope.CLASS, entries.get(0).getScope());
+    assertEquals("ClassName", entries.get(0).getValue());
+    assertTrue(checkPackage.apply(entries.get(1)));
+    assertEquals(AssertionTransformationScope.PACKAGE, entries.get(1).getScope());
+    assertEquals("PackageName", entries.get(1).getValue());
+  }
+
   @Test
   public void forceAssertionOption() throws Exception {
     checkSingleForceAllAssertion(
@@ -753,6 +775,47 @@
                 "--force-passthrough-assertions:PackageName...")
             .getAssertionsConfiguration(),
         AssertionTransformation.PASSTHROUGH);
+    checkSingleForceAllAssertion(
+        parse("--force-assertions-handler:com.example.MyHandler.handler")
+            .getAssertionsConfiguration(),
+        configuration ->
+            configuration.isAssertionHandler()
+                && configuration
+                    .getAssertionHandler()
+                    .getHolderClass()
+                    .equals(Reference.classFromDescriptor("Lcom/example/MyHandler;"))
+                && configuration.getAssertionHandler().getMethodName().equals("handler")
+                && configuration
+                    .getAssertionHandler()
+                    .getMethodDescriptor()
+                    .equals("(Ljava/lang/Throwable;)V"));
+    checkSingleForceClassAndPackageAssertion(
+        parse(
+                "--force-assertions-handler:com.example.MyHandler.handler1:ClassName",
+                "--force-assertions-handler:com.example.MyHandler.handler2:PackageName...")
+            .getAssertionsConfiguration(),
+        configuration ->
+            configuration.isAssertionHandler()
+                && configuration
+                    .getAssertionHandler()
+                    .getHolderClass()
+                    .equals(Reference.classFromDescriptor("Lcom/example/MyHandler;"))
+                && configuration.getAssertionHandler().getMethodName().equals("handler1")
+                && configuration
+                    .getAssertionHandler()
+                    .getMethodDescriptor()
+                    .equals("(Ljava/lang/Throwable;)V"),
+        configuration ->
+            configuration.isAssertionHandler()
+                && configuration
+                    .getAssertionHandler()
+                    .getHolderClass()
+                    .equals(Reference.classFromDescriptor("Lcom/example/MyHandler;"))
+                && configuration.getAssertionHandler().getMethodName().equals("handler2")
+                && configuration
+                    .getAssertionHandler()
+                    .getMethodDescriptor()
+                    .equals("(Ljava/lang/Throwable;)V"));
   }
 
   @Test(expected = CompilationFailedException.class)
@@ -763,9 +826,18 @@
 
   @Test
   public void desugaredLibrary() throws CompilationFailedException {
-    R8Command r8Command = parse("--desugared-lib", "src/library_desugar/desugar_jdk_libs.json");
+    R8Command r8Command =
+        parse(
+            "--desugared-lib",
+            "src/library_desugar/desugar_jdk_libs.json",
+            "--lib",
+            ToolHelper.getAndroidJar(AndroidApiLevel.P).toString());
     assertFalse(
-        r8Command.getInternalOptions().desugaredLibrarySpecification.getRewritePrefix().isEmpty());
+        r8Command
+            .getInternalOptions()
+            .machineDesugaredLibrarySpecification
+            .getRewriteType()
+            .isEmpty());
   }
 
   @Test
@@ -775,10 +847,16 @@
         parse(
             "--desugared-lib",
             "src/library_desugar/desugar_jdk_libs.json",
+            "--lib",
+            ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(),
             "--desugared-lib-pg-conf-output",
             pgout.toString());
     assertFalse(
-        r8Command.getInternalOptions().desugaredLibrarySpecification.getRewritePrefix().isEmpty());
+        r8Command
+            .getInternalOptions()
+            .machineDesugaredLibrarySpecification
+            .getRewriteType()
+            .isEmpty());
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index ccdd0ca..5b735b1 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -107,7 +107,8 @@
           DexVm.Version.V8_1_0,
           DexVm.Version.V9_0_0,
           DexVm.Version.V10_0_0,
-          DexVm.Version.V12_0_0);
+          DexVm.Version.V12_0_0,
+          DexVm.Version.V13_MASTER);
 
   // Input jar for jctf tests.
   private static final String JCTF_COMMON_JAR = "build/libs/jctfCommon.jar";
@@ -195,12 +196,16 @@
               TestCondition.match(TestCondition.runtimes(DexVm.Version.V12_0_0)))
           // TODO(b/197078746): Triage - fails with "java.lang.NoSuchMethodException:
           //  org.apache.harmony.dalvik.ddmc.DdmVmInternal.enableRecentAllocations [boolean]"
-          .put("098-ddmc", TestCondition.match(TestCondition.runtimes(DexVm.Version.V12_0_0)))
+          .put(
+              "098-ddmc",
+              TestCondition.match(
+                  TestCondition.runtimes(DexVm.Version.V12_0_0, DexVm.Version.V13_MASTER)))
           // TODO(b/197079442): Triage - fails with "java.lang.NoSuchMethodException:
           //  org.apache.harmony.dalvik.ddmc.DdmVmInternal.enableRecentAllocations [boolean]"
           .put(
               "145-alloc-tracking-stress",
-              TestCondition.match(TestCondition.runtimes(DexVm.Version.V12_0_0)))
+              TestCondition.match(
+                  TestCondition.runtimes(DexVm.Version.V12_0_0, DexVm.Version.V13_MASTER)))
           .build();
 
   // Tests that are flaky with the Art version we currently use.
@@ -507,6 +512,9 @@
     ImmutableMap.Builder<DexVm.Version, List<String>> builder = ImmutableMap.builder();
     builder
         .put(
+            DexVm.Version.V13_MASTER,
+            ImmutableList.of("454-get-vreg", "457-regs", "543-env-long-ref", "518-null-array-get"))
+        .put(
             DexVm.Version.V12_0_0,
             ImmutableList.of("454-get-vreg", "457-regs", "543-env-long-ref", "518-null-array-get"))
         .put(
@@ -852,7 +860,8 @@
                           DexVm.Version.V4_4_4,
                           DexVm.Version.V5_1_1,
                           DexVm.Version.V6_0_1,
-                          DexVm.Version.V7_0_0)),
+                          DexVm.Version.V7_0_0,
+                          DexVm.Version.V13_MASTER)),
                   TestCondition.match(
                       TestCondition.compilers(
                           CompilerUnderTest.R8,
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index e1be80b..ab32b4f 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.shaking.NoFieldTypeStrengtheningRule;
 import com.android.tools.r8.shaking.NoHorizontalClassMergingRule;
 import com.android.tools.r8.shaking.NoMethodStaticizingRule;
+import com.android.tools.r8.shaking.NoParameterReorderingRule;
 import com.android.tools.r8.shaking.NoParameterTypeStrengtheningRule;
 import com.android.tools.r8.shaking.NoReturnTypeStrengtheningRule;
 import com.android.tools.r8.shaking.NoUnusedInterfaceRemovalRule;
@@ -517,6 +518,12 @@
             NoMethodStaticizingRule.RULE_NAME, NoMethodStaticizing.class);
   }
 
+  public T enableNoParameterReorderingAnnotations() {
+    return addNoParameterReorderingAnnotation()
+        .addInternalMatchAnnotationOnMethodRule(
+            NoParameterReorderingRule.RULE_NAME, NoParameterReordering.class);
+  }
+
   public T enableNoParameterTypeStrengtheningAnnotations() {
     return addNoParameterTypeStrengtheningAnnotation()
         .addInternalMatchAnnotationOnMethodRule(
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index bad14ef..545f842 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -562,6 +562,12 @@
     return buildClasses(programClasses, libraryClasses).build();
   }
 
+  protected static AndroidApp readClasses(
+      List<Class<?>> programClasses, List<Class<?>> classpathClasses, List<Class<?>> libraryClasses)
+      throws IOException {
+    return buildClasses(programClasses, classpathClasses, libraryClasses).build();
+  }
+
   protected static AndroidApp.Builder buildClasses(Class<?>... programClasses) throws IOException {
     return buildClasses(Arrays.asList(programClasses));
   }
@@ -587,22 +593,38 @@
 
   protected static AndroidApp.Builder buildClasses(
       Collection<Class<?>> programClasses, Collection<Class<?>> libraryClasses) throws IOException {
+    return buildClasses(programClasses, Collections.emptyList(), libraryClasses);
+  }
+
+  protected static AndroidApp.Builder buildClasses(
+      Collection<Class<?>> programClasses,
+      Collection<Class<?>> classpathClasses,
+      Collection<Class<?>> libraryClasses)
+      throws IOException {
     AndroidApp.Builder builder = AndroidApp.builder();
     for (Class<?> clazz : programClasses) {
       builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz));
     }
+    if (!classpathClasses.isEmpty()) {
+      builder.addClasspathResourceProvider(getClassFileProvider(classpathClasses));
+    }
     if (!libraryClasses.isEmpty()) {
-      PreloadedClassFileProvider.Builder libraryBuilder = PreloadedClassFileProvider.builder();
-      for (Class<?> clazz : libraryClasses) {
-        Path file = ToolHelper.getClassFileForTestClass(clazz);
-        libraryBuilder.addResource(DescriptorUtils.javaTypeToDescriptor(clazz.getTypeName()),
-            Files.readAllBytes(file));
-      }
-      builder.addLibraryResourceProvider(libraryBuilder.build());
+      builder.addLibraryResourceProvider(getClassFileProvider(libraryClasses));
     }
     return builder;
   }
 
+  private static PreloadedClassFileProvider getClassFileProvider(Collection<Class<?>> classes)
+      throws IOException {
+    PreloadedClassFileProvider.Builder providerBuilder = PreloadedClassFileProvider.builder();
+    for (Class<?> clazz : classes) {
+      Path file = ToolHelper.getClassFileForTestClass(clazz);
+      providerBuilder.addResource(
+          DescriptorUtils.javaTypeToDescriptor(clazz.getTypeName()), Files.readAllBytes(file));
+    }
+    return providerBuilder.build();
+  }
+
   protected static AndroidApp.Builder buildInnerClasses(Class<?> clazz) throws IOException {
     AndroidApp.Builder builder = AndroidApp.builder();
     builder.addProgramFiles(ToolHelper.getClassFilesForInnerClasses(clazz));
@@ -1807,11 +1829,6 @@
         && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N);
   }
 
-  public static boolean canUseJavaUtilObjectsRequireNonNull(TestParameters parameters) {
-    return parameters.isDexRuntime()
-        && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.K);
-  }
-
   public Path compileToZip(
       TestParameters parameters, Collection<Class<?>> classPath, Class<?>... compilationUnit)
       throws Exception {
diff --git a/src/test/java/com/android/tools/r8/TestCondition.java b/src/test/java/com/android/tools/r8/TestCondition.java
index 07fff0e..cd0a1e7 100644
--- a/src/test/java/com/android/tools/r8/TestCondition.java
+++ b/src/test/java/com/android/tools/r8/TestCondition.java
@@ -26,6 +26,7 @@
     ART_V9_0_0,
     ART_V10_0_0,
     ART_V12_0_0,
+    ART_V13_0_0,
     ART_DEFAULT,
     JAVA;
 
@@ -52,6 +53,8 @@
           return ART_V10_0_0;
         case V12_0_0:
           return ART_V12_0_0;
+        case V13_MASTER:
+          return ART_V13_0_0;
         case DEFAULT:
           return ART_DEFAULT;
         default:
diff --git a/src/test/java/com/android/tools/r8/TestRuntime.java b/src/test/java/com/android/tools/r8/TestRuntime.java
index a4b8697..dd19c1e 100644
--- a/src/test/java/com/android/tools/r8/TestRuntime.java
+++ b/src/test/java/com/android/tools/r8/TestRuntime.java
@@ -30,6 +30,7 @@
     JDK10("jdk10", 54),
     JDK11("jdk11", 55),
     JDK17("jdk17", 61),
+    JDK18("jdk18", 62),
     ;
 
     /** This should generally be the latest checked in CF runtime we fully support. */
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index e88f882..5445d03 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -474,6 +474,10 @@
     return addTestingAnnotation(NoMethodStaticizing.class);
   }
 
+  public final T addNoParameterReorderingAnnotation() {
+    return addTestingAnnotation(NoParameterReordering.class);
+  }
+
   public final T addNoParameterTypeStrengtheningAnnotation() {
     return addTestingAnnotation(NoParameterTypeStrengthening.class);
   }
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 2d8ebe3..fb17345 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -268,7 +268,9 @@
     ART_10_0_0_TARGET(Version.V10_0_0, Kind.TARGET),
     ART_10_0_0_HOST(Version.V10_0_0, Kind.HOST),
     ART_12_0_0_TARGET(Version.V12_0_0, Kind.TARGET),
-    ART_12_0_0_HOST(Version.V12_0_0, Kind.HOST);
+    ART_12_0_0_HOST(Version.V12_0_0, Kind.HOST),
+    ART_13_0_0_TARGET(Version.V13_MASTER, Kind.TARGET),
+    ART_13_0_0_HOST(Version.V13_MASTER, Kind.HOST);
 
     private static final ImmutableMap<String, DexVm> SHORT_NAME_MAP =
         Arrays.stream(DexVm.values()).collect(ImmutableMap.toImmutableMap(
@@ -285,7 +287,8 @@
       DEFAULT("default"),
       V9_0_0("9.0.0"),
       V10_0_0("10.0.0"),
-      V12_0_0("12.0.0");
+      V12_0_0("12.0.0"),
+      V13_MASTER("13.0.0");
 
       /** This should generally be the latest DEX VM fully supported. */
       // TODO(b/204855476): Rename to DEFAULT alias once the checked in VM is removed.
@@ -343,7 +346,7 @@
       }
 
       public static Version last() {
-        return V12_0_0;
+        return V13_MASTER;
       }
 
       static {
@@ -614,6 +617,7 @@
   private static final Map<DexVm, String> ART_DIRS =
       ImmutableMap.<DexVm, String>builder()
           .put(DexVm.ART_DEFAULT, "art")
+          .put(DexVm.ART_13_0_0_HOST, "host/art-13-master")
           .put(DexVm.ART_12_0_0_HOST, "host/art-12.0.0-beta4")
           .put(DexVm.ART_10_0_0_HOST, "art-10.0.0")
           .put(DexVm.ART_9_0_0_HOST, "art-9.0.0")
@@ -627,6 +631,7 @@
   private static final Map<DexVm, String> ART_BINARY_VERSIONS =
       ImmutableMap.<DexVm, String>builder()
           .put(DexVm.ART_DEFAULT, "bin/art")
+          .put(DexVm.ART_13_0_0_HOST, "bin/art")
           .put(DexVm.ART_12_0_0_HOST, "bin/art")
           .put(DexVm.ART_10_0_0_HOST, "bin/art")
           .put(DexVm.ART_9_0_0_HOST, "bin/art")
@@ -641,6 +646,7 @@
   private static final Map<DexVm, String> ART_BINARY_VERSIONS_X64 =
       ImmutableMap.<DexVm, String>builder()
           .put(DexVm.ART_DEFAULT, "bin/art")
+          .put(DexVm.ART_13_0_0_HOST, "bin/art")
           .put(DexVm.ART_12_0_0_HOST, "bin/art")
           .put(DexVm.ART_10_0_0_HOST, "bin/art")
           .put(DexVm.ART_9_0_0_HOST, "bin/art")
@@ -667,6 +673,7 @@
     ImmutableMap.Builder<DexVm, List<String>> builder = ImmutableMap.builder();
     builder
         .put(DexVm.ART_DEFAULT, ART_BOOT_LIBS)
+        .put(DexVm.ART_13_0_0_HOST, ART_BOOT_LIBS)
         .put(DexVm.ART_12_0_0_HOST, ART_BOOT_LIBS)
         .put(DexVm.ART_10_0_0_HOST, ART_BOOT_LIBS)
         .put(DexVm.ART_9_0_0_HOST, ART_BOOT_LIBS)
@@ -685,6 +692,7 @@
     ImmutableMap.Builder<DexVm, String> builder = ImmutableMap.builder();
     builder
         .put(DexVm.ART_DEFAULT, "angler")
+        .put(DexVm.ART_13_0_0_HOST, "redfin")
         .put(DexVm.ART_12_0_0_HOST, "redfin")
         .put(DexVm.ART_10_0_0_HOST, "coral")
         .put(DexVm.ART_9_0_0_HOST, "marlin")
@@ -1013,6 +1021,8 @@
 
   public static AndroidApiLevel getMinApiLevelForDexVm(DexVm dexVm) {
     switch (dexVm.version) {
+      case V13_MASTER:
+        return AndroidApiLevel.T;
       case V12_0_0:
         return AndroidApiLevel.S;
       case V10_0_0:
@@ -1973,7 +1983,10 @@
     // TODO(jmhenaff): find a way to run this on windows (push dex and run on device/emulator?)
     Assume.assumeTrue(ToolHelper.isDex2OatSupported());
     Assume.assumeFalse(
-        "b/144975341", vm.version == DexVm.Version.V10_0_0 || vm.version == DexVm.Version.V12_0_0);
+        "b/144975341",
+        vm.version == DexVm.Version.V10_0_0
+            || vm.version == DexVm.Version.V12_0_0
+            || vm.version == DexVm.Version.V13_MASTER);
     if (vm.isOlderThanOrEqual(DexVm.ART_4_4_4_HOST)) {
       // Run default dex2oat for tests on dalvik runtimes.
       vm = DexVm.ART_DEFAULT;
diff --git a/src/test/java/com/android/tools/r8/androidapi/AndroidJarTTest.java b/src/test/java/com/android/tools/r8/androidapi/AndroidJarTTest.java
new file mode 100644
index 0000000..a812587
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/androidapi/AndroidJarTTest.java
@@ -0,0 +1,58 @@
+// 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.androidapi;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+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;
+
+@RunWith(Parameterized.class)
+public class AndroidJarTTest extends TestBase {
+
+  @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 testD8() throws Exception {
+    testForD8(parameters.getBackend())
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
+        .addInnerClasses(getClass())
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      System.out.println("Hello, world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelDesugaredLibraryReferenceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelDesugaredLibraryReferenceTest.java
index 0d451cb..dc66150 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelDesugaredLibraryReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelDesugaredLibraryReferenceTest.java
@@ -7,10 +7,8 @@
 import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
 import java.lang.reflect.Method;
 import java.time.Clock;
@@ -45,7 +43,7 @@
     Method main = Executor.class.getDeclaredMethod("main", String[].class);
     testForR8(parameters.getBackend())
         .addProgramClasses(Executor.class, DesugaredLibUser.class)
-        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+        .addLibraryFiles(getLibraryFile())
         .addKeepMainRule(Executor.class)
         .setMinApi(parameters.getApiLevel())
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java
index 7c60172..a868cf1 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java
@@ -37,9 +37,10 @@
 
   @Test
   public void testR8() throws Exception {
-    // TODO(b/197078995): Make this work on 12.
+    // TODO(b/197078995): Make this work on 12+.
     assumeFalse(
-        parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+        parameters.isDexRuntime()
+            && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
     boolean isMockApiLevel =
         parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(mockLevel);
     testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java
index ad0961d..916badd 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java
@@ -33,9 +33,10 @@
 
   @Test
   public void testR8() throws Exception {
-    // TODO(b/197078995): Make this work on 12.
+    // TODO(b/197078995): Make this work on 12+.
     assumeFalse(
-        parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+        parameters.isDexRuntime()
+            && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
     boolean isLibraryOnBootClassPath =
         parameters.isDexRuntime()
             && parameters.getRuntime().maxSupportedApiLevel().isGreaterThanOrEqualTo(mockLevel);
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
index dddfcbc..51d34a9 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
@@ -36,9 +36,10 @@
 
   @Test
   public void testR8() throws Exception {
-    // TODO(b/197078995): Make this work on 12.
+    // TODO(b/197078995): Make this work on 12+.
     assumeFalse(
-        parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+        parameters.isDexRuntime()
+            && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
     boolean isMockApiLevel =
         parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(mockLevel);
     testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
index 474874c..4f1c13b 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
@@ -35,9 +35,10 @@
 
   @Test
   public void testR8() throws Exception {
-    // TODO(b/197078995): Make this work on 12.
+    // TODO(b/197078995): Make this work on 12+.
     assumeFalse(
-        parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+        parameters.isDexRuntime()
+            && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
     boolean isMockApiLevel =
         parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(mockLevel);
     testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
index 6c8b93d..94eb314 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
@@ -36,9 +36,10 @@
 
   @Test
   public void testR8() throws Exception {
-    // TODO(b/197078995): Make this work on 12.
+    // TODO(b/197078995): Make this work on 12+.
     assumeFalse(
-        parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+        parameters.isDexRuntime()
+            && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
     boolean isMockApiLevel =
         parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(mockApiLevel);
     testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoDesugaredLibraryReferenceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoDesugaredLibraryReferenceTest.java
index 6e8d3b0..0457c25 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoDesugaredLibraryReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoDesugaredLibraryReferenceTest.java
@@ -8,7 +8,6 @@
 import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.references.Reference;
@@ -42,7 +41,7 @@
     Method main = Executor.class.getDeclaredMethod("main", String[].class);
     testForR8(parameters.getBackend())
         .addProgramClasses(Executor.class, LibUser.class)
-        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+        .addLibraryFiles(getLibraryFile())
         .addKeepMainRule(Executor.class)
         .setMinApi(parameters.getApiLevel())
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java
index 76e8b57..db72c72 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java
@@ -41,8 +41,10 @@
 
   @Test
   public void testR8() throws Exception {
+    // TODO(b/197078995): Make this work on 12+.
     assumeFalse(
-        parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+        parameters.isDexRuntime()
+            && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
     boolean isLibraryApiLevel =
         parameters.isDexRuntime()
             && parameters.getApiLevel().isGreaterThanOrEqualTo(libraryApiLevel);
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
index c4dcd42..67fad04 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
@@ -47,8 +47,10 @@
 
   @Test
   public void testR8() throws Exception {
+    // TODO(b/197078995): Make this work on 12+.
     assumeFalse(
-        parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+        parameters.isDexRuntime()
+            && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
     boolean isMethodApiLevel =
         parameters.isDexRuntime()
             && parameters.getApiLevel().isGreaterThanOrEqualTo(methodApiLevel);
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
index ce21ee4..97bced2 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
@@ -45,8 +45,10 @@
 
   @Test
   public void testR8() throws Exception {
+    // TODO(b/197078995): Make this work on 12+.
     assumeFalse(
-        parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+        parameters.isDexRuntime()
+            && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
     boolean beforeFirstApiMethodLevel =
         parameters.isCfRuntime() || parameters.getApiLevel().isLessThan(firstMethodApiLevel);
     boolean afterSecondApiMethodLevel =
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
index a3e2d46..d71eec4 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
@@ -39,9 +39,10 @@
 
   @Test
   public void testR8() throws Exception {
-    // TODO(b/197078995): Make this work on 12.
+    // TODO(b/197078995): Make this work on 12+.
     assumeFalse(
-        parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+        parameters.isDexRuntime()
+            && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
     boolean libraryClassNotStubbed =
         parameters.isDexRuntime()
             && parameters.getApiLevel().isGreaterThanOrEqualTo(libraryClassLevel);
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
index 97ee26b..b537125 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
@@ -47,9 +47,10 @@
 
   @Test
   public void testR8() throws Exception {
-    // TODO(b/197078995): Make this work on 12.
+    // TODO(b/197078995): Make this work on 12+.
     assumeFalse(
-        parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+        parameters.isDexRuntime()
+            && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
     boolean preMockApis =
         parameters.isCfRuntime() || parameters.getApiLevel().isLessThan(initialLibraryMockLevel);
     boolean postMockApis =
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodProtectedTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodProtectedTest.java
index 42ee1c9..33a73cf 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodProtectedTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodProtectedTest.java
@@ -43,9 +43,10 @@
 
   @Test
   public void testR8() throws Exception {
-    // TODO(b/197078995): Make this work on 12.
+    // TODO(b/197078995): Make this work on 12+.
     assumeFalse(
-        parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+        parameters.isDexRuntime()
+            && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
     Method[] apiMethods =
         new Method[] {
           LibraryClass.class.getDeclaredMethod("addedOn27"),
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodUnknownTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodUnknownTest.java
index f8b3bf7..0e9a5b3 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodUnknownTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodUnknownTest.java
@@ -35,9 +35,10 @@
 
   @Test
   public void testR8() throws Exception {
-    // TODO(b/197078995): Make this work on 12.
+    // TODO(b/197078995): Make this work on 12+.
     assumeFalse(
-        parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+        parameters.isDexRuntime()
+            && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
     boolean willInvokeLibraryMethods =
         parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel);
     testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlinePackagePrivateTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlinePackagePrivateTest.java
index 40b302d..f4fe8d7 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlinePackagePrivateTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlinePackagePrivateTest.java
@@ -74,9 +74,10 @@
 
   @Test
   public void testR8() throws Exception {
-    // TODO(b/197078995): Make this work on 12.
+    // TODO(b/197078995): Make this work on 12+.
     assumeFalse(
-        parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+        parameters.isDexRuntime()
+            && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
     testForR8(parameters.getBackend())
         .addLibraryClasses(LibraryClass.class)
         .addDefaultRuntimeLibrary(parameters)
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/ForceInlineConstructorWithRetargetedLibMemberTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/ForceInlineConstructorWithRetargetedLibMemberTest.java
index f7921ff..33bd1a2 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/ForceInlineConstructorWithRetargetedLibMemberTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/ForceInlineConstructorWithRetargetedLibMemberTest.java
@@ -13,8 +13,7 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import java.util.Arrays;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -22,7 +21,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class ForceInlineConstructorWithRetargetedLibMemberTest extends TestBase {
+public class ForceInlineConstructorWithRetargetedLibMemberTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
 
@@ -39,7 +38,7 @@
   public void test() throws Exception {
     // Regression test for b/170677722.
     testForR8(parameters.getBackend())
-        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+        .addLibraryFiles(getLibraryFile())
         .addInnerClasses(getClass())
         .addKeepMainRule(TestClass.class)
         .addVerticallyMergedClassesInspector(
diff --git a/src/test/java/com/android/tools/r8/desugar/InvokeSuperToRewrittenDefaultMethodTest.java b/src/test/java/com/android/tools/r8/desugar/InvokeSuperToRewrittenDefaultMethodTest.java
index 72cdc59..342f9a4 100644
--- a/src/test/java/com/android/tools/r8/desugar/InvokeSuperToRewrittenDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/InvokeSuperToRewrittenDefaultMethodTest.java
@@ -3,23 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.desugar;
 
-import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
-import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
-import static org.hamcrest.CoreMatchers.allOf;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.LibraryDesugaringTestConfiguration;
+import com.android.tools.r8.TestDiagnosticMessages;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.StringUtils;
 import java.util.List;
 import java.util.function.Consumer;
@@ -33,19 +23,15 @@
 
   private static final String EXPECTED = StringUtils.lines("Y", "89");
 
-  @Parameterized.Parameters(name = "{0}, old-rt:{1}")
+  @Parameterized.Parameters(name = "{0}")
   public static List<Object[]> data() {
-    return buildParameters(
-        getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+    return buildParameters(getTestParameters().withAllRuntimesAndApiLevels().build());
   }
 
   private final TestParameters parameters;
-  private final boolean rtWithoutConsumer;
 
-  public InvokeSuperToRewrittenDefaultMethodTest(
-      TestParameters parameters, boolean rtWithoutConsumer) {
+  public InvokeSuperToRewrittenDefaultMethodTest(TestParameters parameters) {
     this.parameters = parameters;
-    this.rtWithoutConsumer = rtWithoutConsumer;
   }
 
   private boolean needsDefaultInterfaceMethodDesugaring() {
@@ -65,35 +51,15 @@
   @Test
   public void testDesugaring() throws Exception {
     assumeTrue(needsDefaultInterfaceMethodDesugaring());
-    try {
-      testForD8()
-          .addInnerClasses(InvokeSuperToRewrittenDefaultMethodTest.class)
-          .setMinApi(parameters.getApiLevel())
-          .addLibraryFiles(
-              rtWithoutConsumer
-                  ? ToolHelper.getAndroidJar(AndroidApiLevel.B)
-                  : ToolHelper.getAndroidJar(AndroidApiLevel.P))
-          .enableCoreLibraryDesugaring(
-              LibraryDesugaringTestConfiguration.forApiLevel(parameters.getApiLevel()))
-          .compileWithExpectedDiagnostics(
-              diagnostics -> {
-                if (rtWithoutConsumer) {
-                  diagnostics.assertOnlyErrors();
-                  // TODO(b/158543011): Should fail with a nice user error for invalid library.
-                  diagnostics.assertErrorsMatch(
-                      allOf(
-                          diagnosticType(ExceptionDiagnostic.class),
-                          diagnosticMessage(containsString("AssertionError"))));
-                } else {
-                  diagnostics.assertNoMessages();
-                }
-              })
-          .run(parameters.getRuntime(), TestClass.class)
-          .assertSuccessWithOutput(EXPECTED);
-      assertFalse(rtWithoutConsumer);
-    } catch (CompilationFailedException e) {
-      assertTrue(rtWithoutConsumer);
-    }
+    testForD8()
+        .addInnerClasses(InvokeSuperToRewrittenDefaultMethodTest.class)
+        .setMinApi(parameters.getApiLevel())
+        .addLibraryFiles(getLibraryFile())
+        .enableCoreLibraryDesugaring(
+            LibraryDesugaringTestConfiguration.forApiLevel(parameters.getApiLevel()))
+        .compileWithExpectedDiagnostics(TestDiagnosticMessages::assertNoMessages)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
   }
 
   public interface CharConsumer extends Consumer<Character>, IntConsumer {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.java b/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.java
index ea0f614..2cb5556 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.java
@@ -88,10 +88,14 @@
         .addProgramClassFileData(transformTestMathMultiplyExactLongInt())
         .setMinApi(AndroidApiLevel.ANDROID_PLATFORM)
         .run(parameters.getRuntime(), TestMathMultiplyExactLongInt.class)
-        .assertFailureWithErrorThatMatches(
-            containsString(
-                "java.lang.NoSuchMethodError: No static method"
-                    + " parseInt(Ljava/lang/CharSequence;III)I"));
+        .applyIf(
+            parameters.getDexRuntimeVersion().isOlderThan(Version.V13_MASTER),
+            b ->
+                b.assertFailureWithErrorThatMatches(
+                    containsString(
+                        "java.lang.NoSuchMethodError: No static method"
+                            + " parseInt(Ljava/lang/CharSequence;III)I")),
+            b -> b.assertSuccessWithOutputLines("4"));
   }
 
   // Test class for using: List List.of()
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceArrayTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceArrayTest.java
index 2c2af2b..3032a12 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceArrayTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceArrayTest.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.util.concurrent.atomic.AtomicReferenceArray;
-import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
@@ -25,7 +24,9 @@
   }
 
   public AtomicReferenceArrayTest(TestParameters parameters) {
-    super(parameters, AtomicReferenceFieldUpdater.class, Main.class);
+    super(parameters, AtomicReferenceArray.class, Main.class);
+
+    ignoreInvokes("<init>");
 
     // java.util.concurrent.atomic.AtomicReferenceArray issue is on API 31, see b/211646483.
     registerTarget(AndroidApiLevel.Sv2, 3);
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceTest.java
index 1f5d11e..6dc9faf 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceTest.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.util.concurrent.atomic.AtomicReference;
-import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
@@ -25,7 +24,9 @@
   }
 
   public AtomicReferenceTest(TestParameters parameters) {
-    super(parameters, AtomicReferenceFieldUpdater.class, Main.class);
+    super(parameters, AtomicReference.class, Main.class);
+
+    ignoreInvokes("<init>");
 
     // java.util.concurrent.atomic.AtomicReference issue is on API 31, see b/211646483.
     registerTarget(AndroidApiLevel.Sv2, 3);
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava10Test.java
index 25d4081..c5e6adc 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava10Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava10Test.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.desugar.backports;
 
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
@@ -16,8 +18,6 @@
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-
 @RunWith(Parameterized.class)
 public final class OptionalBackportJava10Test extends AbstractBackportTest {
   @Parameters(name = "{0}")
@@ -34,12 +34,14 @@
 
   public OptionalBackportJava10Test(TestParameters parameters) {
     super(parameters, Optional.class, TEST_JAR, "backport.OptionalBackportJava10Main");
-    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
-    // an actual API level, migrate these tests to OptionalBackportTest.
+    // Note: The methods in this test exist in android.jar from Android T. When R8 builds targeting
+    // Java 11 move these tests to OptionalBackportTest (out of examplesJava10).
 
-    // Available since N as part of library desugaring.
+    // Available since N.
     ignoreInvokes("empty");
     ignoreInvokes("get");
     ignoreInvokes("of");
+
+    registerTarget(AndroidApiLevel.T, 2);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava11Test.java
index dee14c1..8b57c0f 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava11Test.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.desugar.backports;
 
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
@@ -16,8 +18,6 @@
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-
 @RunWith(Parameterized.class)
 public final class OptionalBackportJava11Test extends AbstractBackportTest {
   @Parameters(name = "{0}")
@@ -34,11 +34,13 @@
 
   public OptionalBackportJava11Test(TestParameters parameters) {
     super(parameters, Optional.class, TEST_JAR, "backport.OptionalBackportJava11Main");
-    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
-    // an actual API level, migrate these tests to OptionalBackportTest.
+    // Note: The methods in this test exist in android.jar from Android T. When R8 builds targeting
+    // Java 11 move these tests to OptionalBackportTest (out of examplesJava11).
 
-    // Available since N as part of library desugaring.
+    // Available since N.
     ignoreInvokes("empty");
     ignoreInvokes("of");
+
+    registerTarget(AndroidApiLevel.T, 2);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java
index 31bc82f..a726dc5 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java
@@ -34,11 +34,13 @@
 
   public OptionalBackportJava9Test(TestParameters parameters) {
     super(parameters, Optional.class, TEST_JAR, "backport.OptionalBackportJava9Main");
-    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
-    // an actual API level, migrate these tests to OptionalBackportTest.
+    // Note: The methods in this test exist in android.jar from Android T. When R8 builds targeting
+    // Java 11 move these tests to OptionalBackportTest (out of examplesJava9).
 
-    // Available since N as part of library desugaring.
+    // Available since N.
     ignoreInvokes("empty");
     ignoreInvokes("of");
+
+    registerTarget(AndroidApiLevel.T, 10);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava10Test.java
index 97d70df..a181a64 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava10Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava10Test.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.desugar.backports;
 
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
@@ -11,14 +13,11 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.util.Optional;
 import java.util.OptionalDouble;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-
 @RunWith(Parameterized.class)
 public final class OptionalDoubleBackportJava10Test extends AbstractBackportTest {
   @Parameters(name = "{0}")
@@ -35,12 +34,14 @@
 
   public OptionalDoubleBackportJava10Test(TestParameters parameters) {
     super(parameters, OptionalDouble.class, TEST_JAR, "backport.OptionalDoubleBackportJava10Main");
-    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
-    // an actual API level, migrate these tests to OptionalBackportTest.
+    // Note: The methods in this test exist in android.jar from Android T. When R8 builds targeting
+    // Java 11 move these tests to OptionalBackportTest (out of examplesJava10).
 
-    // Available since N as part of library desugaring.
+    // Available since N.
     ignoreInvokes("empty");
     ignoreInvokes("getAsDouble");
     ignoreInvokes("of");
+
+    registerTarget(AndroidApiLevel.T, 2);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava11Test.java
index 981e1e2..b5e9c02 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava11Test.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.desugar.backports;
 
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
@@ -16,8 +18,6 @@
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-
 @RunWith(Parameterized.class)
 public final class OptionalDoubleBackportJava11Test extends AbstractBackportTest {
   @Parameters(name = "{0}")
@@ -34,11 +34,13 @@
 
   public OptionalDoubleBackportJava11Test(TestParameters parameters) {
     super(parameters, OptionalDouble.class, TEST_JAR, "backport.OptionalDoubleBackportJava11Main");
-    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
-    // an actual API level, migrate these tests to OptionalBackportTest.
+    // Note: The methods in this test exist in android.jar from Android T. When R8 builds targeting
+    // Java 11 move these tests to OptionalBackportTest (out of examplesJava11).
 
-    // Available since N as part of library desugaring.
+    // Available since N.
     ignoreInvokes("empty");
     ignoreInvokes("of");
+
+    registerTarget(AndroidApiLevel.T, 2);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava9Test.java
index 5aa0e1c..bb9bb65 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava9Test.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.desugar.backports;
 
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
@@ -11,14 +13,11 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.util.Optional;
 import java.util.OptionalDouble;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-
 @RunWith(Parameterized.class)
 public final class OptionalDoubleBackportJava9Test extends AbstractBackportTest {
   @Parameters(name = "{0}")
@@ -35,11 +34,13 @@
 
   public OptionalDoubleBackportJava9Test(TestParameters parameters) {
     super(parameters, OptionalDouble.class, TEST_JAR, "backport.OptionalDoubleBackportJava9Main");
-    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
-    // an actual API level, migrate these tests to OptionalDoubleBackportTest.
+    // Note: The methods in this test exist in android.jar from Android T. When R8 builds targeting
+    // Java 11 move these tests to OptionalBackportTest (out of examplesJava9).
 
-    // Available since N as part of library desugaring.
+    // Available since N.
     ignoreInvokes("empty");
     ignoreInvokes("of");
+
+    registerTarget(AndroidApiLevel.T, 4);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava10Test.java
index b44f1cf..00f5b4b 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava10Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava10Test.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.desugar.backports;
 
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
@@ -12,13 +14,10 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.OptionalInt;
-import java.util.OptionalLong;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-
 @RunWith(Parameterized.class)
 public final class OptionalIntBackportJava10Test extends AbstractBackportTest {
   @Parameters(name = "{0}")
@@ -35,12 +34,14 @@
 
   public OptionalIntBackportJava10Test(TestParameters parameters) {
     super(parameters, OptionalInt.class, TEST_JAR, "backport.OptionalIntBackportJava10Main");
-    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
-    // an actual API level, migrate these tests to OptionalBackportTest.
+    // Note: The methods in this test exist in android.jar from Android T. When R8 builds targeting
+    // Java 11 move these tests to OptionalBackportTest (out of examplesJava10).
 
-    // Available since N as part of library desugaring.
+    // Available since N.
     ignoreInvokes("empty");
     ignoreInvokes("getAsInt");
     ignoreInvokes("of");
+
+    registerTarget(AndroidApiLevel.T, 2);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava11Test.java
index e594db3..62d66cd 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava11Test.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.desugar.backports;
 
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
@@ -16,8 +18,6 @@
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-
 @RunWith(Parameterized.class)
 public final class OptionalIntBackportJava11Test extends AbstractBackportTest {
   @Parameters(name = "{0}")
@@ -34,11 +34,13 @@
 
   public OptionalIntBackportJava11Test(TestParameters parameters) {
     super(parameters, OptionalInt.class, TEST_JAR, "backport.OptionalIntBackportJava11Main");
-    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
-    // an actual API level, migrate these tests to OptionalBackportTest.
+    // Note: The methods in this test exist in android.jar from Android T. When R8 builds targeting
+    // Java 11 move these tests to OptionalBackportTest (out of examplesJava11).
 
-    // Available since N as part of library desugaring.
+    // Available since N.
     ignoreInvokes("empty");
     ignoreInvokes("of");
+
+    registerTarget(AndroidApiLevel.T, 2);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava9Test.java
index 71a8dd9..54f08ae 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava9Test.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.desugar.backports;
 
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
@@ -16,8 +18,6 @@
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-
 @RunWith(Parameterized.class)
 public final class OptionalIntBackportJava9Test extends AbstractBackportTest {
   @Parameters(name = "{0}")
@@ -34,11 +34,13 @@
 
   public OptionalIntBackportJava9Test(TestParameters parameters) {
     super(parameters, OptionalInt.class, TEST_JAR, "backport.OptionalIntBackportJava9Main");
-    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
-    // an actual API level, migrate these tests to OptionalIntBackportTest.
+    // Note: The methods in this test exist in android.jar from Android T. When R8 builds targeting
+    // Java 11 move these tests to OptionalBackportTest (out of examplesJava9).
 
-    // Available since N as part of library desugaring.
+    // Available since N.
     ignoreInvokes("empty");
     ignoreInvokes("of");
+
+    registerTarget(AndroidApiLevel.T, 4);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava10Test.java
index a4bef8c..c353e92 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava10Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava10Test.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.desugar.backports;
 
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
@@ -16,8 +18,6 @@
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-
 @RunWith(Parameterized.class)
 public final class OptionalLongBackportJava10Test extends AbstractBackportTest {
   @Parameters(name = "{0}")
@@ -34,12 +34,14 @@
 
   public OptionalLongBackportJava10Test(TestParameters parameters) {
     super(parameters, OptionalLong.class, TEST_JAR, "backport.OptionalLongBackportJava10Main");
-    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
-    // an actual API level, migrate these tests to OptionalBackportTest.
+    // Note: The methods in this test exist in android.jar from Android T. When R8 builds targeting
+    // Java 11 move these tests to OptionalBackportTest (out of examplesJava10).
 
-    // Available since N as part of library desugaring.
+    // Available since N.
     ignoreInvokes("empty");
     ignoreInvokes("getAsLong");
     ignoreInvokes("of");
+
+    registerTarget(AndroidApiLevel.T, 2);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava11Test.java
index bc1f4aa..fd8fc6f 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava11Test.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.desugar.backports;
 
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
@@ -16,8 +18,6 @@
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-
 @RunWith(Parameterized.class)
 public final class OptionalLongBackportJava11Test extends AbstractBackportTest {
   @Parameters(name = "{0}")
@@ -34,11 +34,13 @@
 
   public OptionalLongBackportJava11Test(TestParameters parameters) {
     super(parameters, OptionalLong.class, TEST_JAR, "backport.OptionalLongBackportJava11Main");
-    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
-    // an actual API level, migrate these tests to OptionalBackportTest.
+    // Note: The methods in this test exist in android.jar from Android T. When R8 builds targeting
+    // Java 11 move these tests to OptionalBackportTest (out of examplesJava11).
 
-    // Available since N as part of library desugaring.
+    // Available since N.
     ignoreInvokes("empty");
     ignoreInvokes("of");
+
+    registerTarget(AndroidApiLevel.T, 2);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava9Test.java
index b0dfd62..efb516a 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava9Test.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.desugar.backports;
 
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
@@ -16,8 +18,6 @@
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-
 @RunWith(Parameterized.class)
 public final class OptionalLongBackportJava9Test extends AbstractBackportTest {
   @Parameters(name = "{0}")
@@ -34,13 +34,15 @@
 
   public OptionalLongBackportJava9Test(TestParameters parameters) {
     super(parameters, OptionalLong.class, TEST_JAR, "backport.OptionalLongBackportJava9Main");
-    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
-    // an actual API level, migrate these tests to OptionalLongBackportTest.
+    // Note: The methods in this test exist in android.jar from Android T. When R8 builds targeting
+    // Java 11 move these tests to OptionalBackportTest (out of examplesJava9).
 
-    // Available since N as part of library desugaring.
+    // Available since N.
     ignoreInvokes("empty");
     ignoreInvokes("getAsLong");
     ignoreInvokes("isPresent");
     ignoreInvokes("of");
+
+    registerTarget(AndroidApiLevel.T, 4);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java b/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
index 237b0e1..195dfd2 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
@@ -79,6 +79,9 @@
   @Test
   public void testBackportedMethodsPerAPILevel() throws Exception {
     for (AndroidApiLevel apiLevel : AndroidApiLevel.values()) {
+      if (apiLevel == AndroidApiLevel.T) {
+        continue;
+      }
       if (!ToolHelper.hasAndroidJar(apiLevel)) {
         // Only check for the android jar versions present in third_party.
         System.out.println("Skipping check for " + apiLevel);
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
index 6b54606..b5e5c03 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
@@ -73,11 +73,13 @@
   }
 
   private void configurationForProgramCompilation(InternalOptions options) {
-    options.desugaredLibrarySpecification = configurationAlternative3(options, false, parameters);
+    setDesugaredLibrarySpecificationForTesting(
+        options, configurationAlternative3(options, false, parameters));
   }
 
   private void configurationForLibraryCompilation(InternalOptions options) {
-    options.desugaredLibrarySpecification = configurationAlternative3(options, true, parameters);
+    setDesugaredLibrarySpecificationForTesting(
+        options, configurationAlternative3(options, true, parameters));
   }
 
   @Test
@@ -141,10 +143,7 @@
     KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
     testForD8()
         .addLibraryFiles(getLibraryFile())
-        .addOptionsModification(
-            options ->
-                options.desugaredLibrarySpecification =
-                    configurationAlternative3(options, false, parameters))
+        .addOptionsModification(this::configurationForProgramCompilation)
         .addInnerClasses(BufferedReaderTest.class)
         .setMinApi(parameters.getApiLevel())
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
@@ -170,10 +169,7 @@
     KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
     testForR8(parameters.getBackend())
         .addLibraryFiles(getLibraryFile())
-        .addOptionsModification(
-            options ->
-                options.desugaredLibrarySpecification =
-                    configurationAlternative3(options, false, parameters))
+        .addOptionsModification(this::configurationForProgramCompilation)
         .addInnerClasses(BufferedReaderTest.class)
         .addKeepMainRule(TestClass.class)
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
index d1e9746..473153c 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
@@ -10,17 +10,11 @@
 import com.android.tools.r8.D8TestRunResult;
 import com.android.tools.r8.R8TestRunResult;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.HumanToMachineSpecificationConverter;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.LegacyToHumanSpecificationConverter;
 import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import java.io.IOException;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -40,19 +34,15 @@
 
   private final TestParameters parameters;
   private final boolean shrinkDesugaredLibrary;
-  private final boolean machineSpec;
 
-  @Parameters(name = "machine: {0}, {2}, shrink: {1}")
+  @Parameters(name = "machine: {0}, shrink: {1}")
   public static List<Object[]> data() {
     return buildParameters(
         BooleanUtils.values(),
-        BooleanUtils.values(),
         getTestParameters().withDexRuntimes().withAllApiLevels().build());
   }
 
-  public CustomCollectionTest(
-      boolean machineSpec, boolean shrinkDesugaredLibrary, TestParameters parameters) {
-    this.machineSpec = machineSpec;
+  public CustomCollectionTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
     this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
     this.parameters = parameters;
   }
@@ -60,22 +50,6 @@
   private final String EXECUTOR =
       "com.android.tools.r8.desugar.desugaredlibrary.CustomCollectionTest$Executor";
 
-  private void setMachineSpec(InternalOptions opt) {
-    if (!machineSpec) {
-      return;
-    }
-    try {
-      HumanDesugaredLibrarySpecification human =
-          new LegacyToHumanSpecificationConverter()
-              .convert(opt.desugaredLibrarySpecification, getLibraryFile(), opt);
-      MachineDesugaredLibrarySpecification machine =
-          new HumanToMachineSpecificationConverter().convert(human, getLibraryFile(), opt);
-      opt.testing.machineDesugaredLibrarySpecification = machine;
-    } catch (IOException e) {
-      throw new RuntimeException(e);
-    }
-  }
-
   @Test
   public void testCustomCollectionD8() throws Exception {
     KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
@@ -83,7 +57,6 @@
         testForD8()
             .addLibraryFiles(getLibraryFile())
             .addInnerClasses(CustomCollectionTest.class)
-            .addOptionsModification(this::setMachineSpec)
             .setMinApi(parameters.getApiLevel())
             .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
             .compile()
@@ -106,7 +79,6 @@
     Path jar =
         testForD8(Backend.CF)
             .addInnerClasses(CustomCollectionTest.class)
-            .addOptionsModification(this::setMachineSpec)
             .setMinApi(parameters.getApiLevel())
             .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
             .compile()
@@ -162,7 +134,6 @@
         testForR8(Backend.DEX)
             .addLibraryFiles(getLibraryFile())
             .addInnerClasses(CustomCollectionTest.class)
-            .addOptionsModification(this::setMachineSpec)
             .setMinApi(parameters.getApiLevel())
             .addKeepClassAndMembersRules(Executor.class)
             .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryCHMOnlyContentTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryCHMOnlyContentTest.java
index 3e7d97d..e27dda9 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryCHMOnlyContentTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryCHMOnlyContentTest.java
@@ -43,10 +43,9 @@
             "",
             false,
             Collections.emptyList(),
-            options -> {
-              options.desugaredLibrarySpecification =
-                  chmOnlyConfiguration(options, true, parameters);
-            });
+            options ->
+                setDesugaredLibrarySpecificationForTesting(
+                    options, chmOnlyConfiguration(options, true, parameters)));
     CodeInspector inspector = new CodeInspector(desugaredLib);
     assert inspector.clazz("j$.util.concurrent.ConcurrentHashMap").isPresent();
   }
@@ -61,10 +60,9 @@
             "-keep class * { *; }",
             true,
             Collections.emptyList(),
-            options -> {
-              options.desugaredLibrarySpecification =
-                  chmOnlyConfiguration(options, true, parameters);
-            });
+            options ->
+                setDesugaredLibrarySpecificationForTesting(
+                    options, chmOnlyConfiguration(options, true, parameters)));
     CodeInspector inspector = new CodeInspector(desugaredLib);
     assert inspector.clazz("j$.util.concurrent.ConcurrentHashMap").isPresent();
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
index e353126..f4786f7 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
@@ -5,15 +5,19 @@
 package com.android.tools.r8.desugar.desugaredlibrary;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertTrue;
 import static org.hamcrest.CoreMatchers.startsWith;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.fail;
 
 import com.android.tools.r8.L8Command;
+import com.android.tools.r8.L8TestBuilder;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestDiagnosticMessages;
 import com.android.tools.r8.TestDiagnosticMessagesImpl;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
@@ -25,6 +29,7 @@
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.Collections;
 import org.junit.Assume;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -45,6 +50,33 @@
   }
 
   @Test
+  public void testInvalidLibrary() throws Exception {
+    Assume.assumeTrue(requiresAnyCoreLibDesugaring(parameters));
+    L8TestBuilder l8TestBuilder =
+        testForL8(parameters.getApiLevel())
+            .addProgramFiles(Collections.singleton(ToolHelper.getDesugarJDKLibs()))
+            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.L))
+            .setDesugarJDKLibsConfiguration(ToolHelper.DESUGAR_LIB_CONVERSIONS);
+    try {
+      l8TestBuilder.compile();
+      fail();
+    } catch (AssertionError ae) {
+      // Expected since the library is invalid.
+    }
+    TestDiagnosticMessages diagnosticMessages = l8TestBuilder.getDiagnosticMessages();
+    diagnosticMessages.assertOnlyWarnings();
+    assertEquals(diagnosticMessages.getWarnings().size(), 1);
+    assertTrue(
+        diagnosticMessages
+            .getWarnings()
+            .get(0)
+            .getDiagnosticMessage()
+            .contains(
+                "Desugared library requires to be compiled with a library file of API greater or"
+                    + " equal to"));
+  }
+
+  @Test
   public void testDesugaredLibraryContentD8() throws Exception {
     Assume.assumeTrue(requiresAnyCoreLibDesugaring(parameters));
     CodeInspector inspector = new CodeInspector(buildDesugaredLibrary(parameters.getApiLevel()));
@@ -89,11 +121,20 @@
     ToolHelper.runL8(l8Builder.build(), options -> {});
     CodeInspector codeInspector = new CodeInspector(desugaredLib);
     assertCorrect(codeInspector);
-    assertNoWarningsErrors(diagnosticsHandler);
+    assertOneWarning(diagnosticsHandler);
   }
 
-  private void assertNoWarningsErrors(TestDiagnosticMessagesImpl diagnosticsHandler) {
-    assertTrue(diagnosticsHandler.getWarnings().isEmpty());
+  private void assertOneWarning(TestDiagnosticMessagesImpl diagnosticsHandler) {
+    assertEquals(
+        (isJDK11DesugaredLibrary() && parameters.getApiLevel().isLessThan(AndroidApiLevel.O))
+            ? 2
+            : 1,
+        diagnosticsHandler.getWarnings().size());
+    String msg = diagnosticsHandler.getWarnings().get(0).getDiagnosticMessage();
+    assertTrue(
+        msg.contains(
+            "The following library types, prefixed by java., are present both as library and non"
+                + " library classes"));
     assertTrue(diagnosticsHandler.getErrors().isEmpty());
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryDumpInputsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryDumpInputsTest.java
index 03f861b..92e689b 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryDumpInputsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryDumpInputsTest.java
@@ -9,7 +9,6 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.ZipUtils;
 import com.google.common.collect.ImmutableList;
@@ -48,7 +47,7 @@
     Path dumpDir = temp.newFolder().toPath();
     testForD8(parameters.getBackend())
         .addProgramClasses(TestClass.class)
-        .addLibraryFiles(ToolHelper.getJava8RuntimeJar())
+        .addLibraryFiles(getLibraryFile())
         .setMinApi(parameters.getApiLevel())
         .addOptionsModification(options -> options.dumpInputToDirectory = dumpDir.toString())
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
@@ -76,7 +75,7 @@
     Path dumpDir = temp.newFolder().toPath();
     testForR8(parameters.getBackend())
         .addProgramClasses(TestClass.class)
-        .addLibraryFiles(ToolHelper.getJava8RuntimeJar())
+        .addLibraryFiles(getLibraryFile())
         .setMinApi(parameters.getApiLevel())
         .addKeepMainRule(TestClass.class)
         .addOptionsModification(options -> options.dumpInputToDirectory = dumpDir.toString())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
index 34faa58..eb25adf 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
@@ -67,6 +67,18 @@
     return property.contains("jdk11");
   }
 
+  public void setDesugaredLibrarySpecificationForTesting(
+      InternalOptions options, LegacyDesugaredLibrarySpecification specification) {
+    try {
+      options.setDesugaredLibrarySpecificationForTesting(
+          specification,
+          ToolHelper.getDesugarJDKLibs(),
+          ToolHelper.getAndroidJar(AndroidApiLevel.R));
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
   // For conversions tests, we need DexRuntimes where classes to convert are present (DexRuntimes
   // above N and O depending if Stream or Time APIs are used), but we need to compile the program
   // with a minAPI below to force the use of conversions.
@@ -87,10 +99,12 @@
     throw new Error("Unsupported conversion parameters");
   }
 
+  protected AndroidApiLevel getRequiredCompilationAPILevel() {
+    return isJDK11DesugaredLibrary() ? AndroidApiLevel.R : AndroidApiLevel.P;
+  }
+
   protected Path getLibraryFile() {
-    return isJDK11DesugaredLibrary()
-        ? ToolHelper.getAndroidJar(AndroidApiLevel.S)
-        : ToolHelper.getAndroidJar(AndroidApiLevel.P);
+    return ToolHelper.getAndroidJar(getRequiredCompilationAPILevel());
   }
 
   protected boolean requiresEmulatedInterfaceCoreLibDesugaring(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java
index 0c61a06..47b01f1 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java
@@ -4,6 +4,9 @@
 
 package com.android.tools.r8.desugar.desugaredlibrary;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.L8Command;
 import com.android.tools.r8.OutputMode;
@@ -12,6 +15,7 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
 import java.nio.file.Path;
 import java.util.Arrays;
@@ -65,6 +69,19 @@
           Arrays.asList(FUNCTION_KEEP.split(System.lineSeparator())), Origin.unknown());
     }
     ToolHelper.runL8(l8Builder.build(), options -> {});
-    diagnosticsHandler.assertNoMessages();
+    assertEquals(
+        (isJDK11DesugaredLibrary() && parameters.getApiLevel().isLessThan(AndroidApiLevel.O))
+            ? 2
+            : 1,
+        diagnosticsHandler.getWarnings().size());
+    diagnosticsHandler.assertNoErrors();
+    assertTrue(
+        diagnosticsHandler
+            .getWarnings()
+            .get(0)
+            .getDiagnosticMessage()
+            .contains(
+                "The following library types, prefixed by java., are present both as library and"
+                    + " non library classes:"));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InconsistentPrefixTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InconsistentPrefixTest.java
deleted file mode 100644
index b1f57e1..0000000
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InconsistentPrefixTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) 2019, 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.desugaredlibrary;
-
-import static org.hamcrest.CoreMatchers.containsString;
-
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
-import com.android.tools.r8.jasmin.JasminBuilder;
-import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.Map;
-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 InconsistentPrefixTest extends TestBase {
-
-  @Parameters(name = "{0}")
-  public static TestParametersCollection data() {
-    return getTestParameters().withNoneRuntime().build();
-  }
-
-  public InconsistentPrefixTest(TestParameters parameters) {
-    parameters.assertNoneRuntime();
-  }
-
-  @Test(expected = CompilationFailedException.class)
-  public void testNoInconsistentPrefixes() throws Exception {
-    Map<String, String> x = new HashMap<>();
-    x.put("pkg.sub", "p$.bus");
-    x.put("pkg", "p$");
-
-    JasminBuilder jasminBuilder = new JasminBuilder();
-    jasminBuilder.addClass("pkg/notsub/A");
-    jasminBuilder.addClass("pkg/sub/A");
-    Path inputJar = temp.getRoot().toPath().resolve("input.jar");
-    jasminBuilder.writeJar(inputJar);
-
-    testForD8()
-        .addProgramFiles(inputJar)
-        .addOptionsModification(
-            options ->
-                options.desugaredLibrarySpecification =
-                    LegacyDesugaredLibrarySpecification.withOnlyRewritePrefixForTesting(x, options))
-        .compileWithExpectedDiagnostics(
-            diagnostics -> {
-              diagnostics.assertErrorMessageThatMatches(
-                  containsString("Inconsistent prefix in desugared library"));
-            });
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/J$ExtensionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/J$ExtensionTest.java
index 88f3ca9..371443d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/J$ExtensionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/J$ExtensionTest.java
@@ -5,15 +5,12 @@
 package com.android.tools.r8.desugar.desugaredlibrary;
 
 import static junit.framework.TestCase.assertTrue;
-import static junit.framework.TestCase.fail;
 
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.utils.BooleanUtils;
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileWriter;
@@ -31,7 +28,6 @@
 public class J$ExtensionTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
-  private final boolean shrinkDesugaredLibrary;
 
   private static final String MAIN_CLASS_NAME = "Main";
   private static final String MAIN_CLASS =
@@ -55,14 +51,12 @@
           + "}";
   private static Path[] compiledClasses = new Path[2];
 
-  @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+  @Parameters(name = "{0}")
   public static List<Object[]> data() {
-    return buildParameters(
-        BooleanUtils.values(), getTestParameters().withAllRuntimes().withAllApiLevels().build());
+    return buildParameters(getTestParameters().withAllRuntimes().withAllApiLevels().build());
   }
 
-  public J$ExtensionTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
-    this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+  public J$ExtensionTest(TestParameters parameters) {
     this.parameters = parameters;
   }
 
@@ -92,7 +86,6 @@
 
   @Test
   public void testJ$ExtensionNoDesugaring() throws Exception {
-    Assume.assumeFalse(shrinkDesugaredLibrary);
     String stderr;
     if (parameters.isCfRuntime()) {
       stderr =
@@ -110,24 +103,31 @@
               .assertFailure()
               .getStdErr();
     }
-    assertError(stderr);
+    assertError(stderr, false);
   }
 
-  private void assertError(String stderr) {
-    if (parameters.isCfRuntime() && parameters.getRuntime().asCf().getVm() == CfVm.JDK8) {
-      assertTrue(
-          stderr.contains("java.lang.SecurityException: Prohibited package name: java.time"));
-    } else if (parameters.isCfRuntime()) {
-      assertTrue(stderr.contains("java.lang.ClassNotFoundException: java.time.LocalTimeAccess"));
-    } else if (parameters
-        .getRuntime()
-        .asDex()
-        .getVm()
-        .getVersion()
-        .isOlderThanOrEqual(Version.V6_0_1)) {
-      assertTrue(stderr.contains("java.lang.NoClassDefFoundError"));
-    } else if (parameters.getRuntime().asDex().getVm().getVersion() == Version.V7_0_0) {
-      assertTrue(stderr.contains("java.lang.ClassNotFoundException"));
+  private void assertError(String stderr, boolean desugaring) {
+    if (parameters.isCfRuntime()) {
+      if (parameters.getRuntime().asCf().getVm() == CfVm.JDK8) {
+        assertTrue(
+            stderr.contains("java.lang.SecurityException: Prohibited package name: java.time"));
+      } else {
+        assertTrue(stderr.contains("java.lang.ClassNotFoundException: java.time.LocalTimeAccess"));
+      }
+      return;
+    }
+    assert !parameters.isCfRuntime();
+    if (!desugaring) {
+      if (parameters.getDexRuntimeVersion().isOlderThanOrEqual(Version.V6_0_1)) {
+        assertTrue(stderr.contains("java.lang.NoClassDefFoundError"));
+      } else if (parameters.getDexRuntimeVersion() == Version.V7_0_0) {
+        assertTrue(stderr.contains("java.lang.ClassNotFoundException"));
+      }
+      return;
+    }
+    if (parameters.getDexRuntimeVersion() == Version.V8_1_0) {
+      // On Android 8 the library package private method is accessible.
+      assertTrue(stderr.contains("java.lang.NullPointerException"));
     } else {
       assertTrue(stderr.contains("java.lang.IllegalAccessError"));
     }
@@ -140,19 +140,18 @@
     Assume.assumeTrue(requiresTimeDesugaring(parameters));
     KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
 
-    try {
-      testForD8()
-          .addLibraryFiles(getLibraryFile())
-          .addProgramFiles(compiledClasses)
-          .setMinApi(parameters.getApiLevel())
-          .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
-          .compile();
-      fail();
-    } catch (CompilationFailedException e) {
-      assertTrue(
-          e.getCause()
-              .getMessage()
-              .contains("Cannot compile program class java.time.LocalTimeAccess"));
-    }
+    String stdErr =
+        testForD8()
+            .addLibraryFiles(getLibraryFile())
+            .addProgramFiles(compiledClasses)
+            .setMinApi(parameters.getApiLevel())
+            .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+            .compile()
+            .addDesugaredCoreLibraryRunClassPath(
+                this::buildDesugaredLibrary, parameters.getApiLevel())
+            .run(parameters.getRuntime(), MAIN_CLASS_NAME)
+            .assertFailure()
+            .getStdErr();
+    assertError(stdErr, true);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
index 6598d6b..d78df31 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
@@ -144,16 +144,22 @@
             .map(TypeSubject::toString)
             .collect(Collectors.toSet());
     assertEquals(expectedCatchGuards, foundCatchGuards);
-
-    if (isR8) {
-      assertThat(
-          inspector.clazz(TemporalAccessorImpl.class).uniqueMethodWithFinalName("query"),
-          not(isPresent()));
-    } else {
+    if (!(parameters
+            .getApiLevel()
+            .isGreaterThanOrEqualTo(TestBase.apiLevelWithDefaultInterfaceMethodsSupport())
+        && isR8)) {
       assertThat(
           inspector.clazz(TemporalAccessorImplSub.class).uniqueMethodWithFinalName("query"),
           CodeMatchers.invokesMethod(
-              null, TemporalAccessorImpl.class.getTypeName(), "query", null));
+              null, inspector.clazz(TemporalAccessorImpl.class).getFinalName(), "query", null));
+    } else {
+      String holder =
+          requiresTimeDesugaring(parameters)
+              ? "j$.time.temporal.TemporalAccessor"
+              : "java.time.temporal.TemporalAccessor";
+      assertThat(
+          inspector.clazz(TemporalAccessorImplSub.class).uniqueMethodWithFinalName("query"),
+          CodeMatchers.invokesMethod(null, holder, "query", null));
     }
     if (parameters
         .getApiLevel()
@@ -163,9 +169,7 @@
           not(isPresent()));
     } else {
       assertThat(
-          inspector
-              .clazz(isR8 ? TemporalAccessorImplSub.class : TemporalAccessorImpl.class)
-              .uniqueMethodWithFinalName("query"),
+          inspector.clazz(TemporalAccessorImpl.class).uniqueMethodWithFinalName("query"),
           CodeMatchers.invokesMethod(
               null, "j$.time.temporal.TemporalAccessor$-CC", "$default$query", null));
     }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LinkedHashSetTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LinkedHashSetTest.java
index 981edea..de87214 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LinkedHashSetTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LinkedHashSetTest.java
@@ -11,6 +11,7 @@
 import java.util.Set;
 import java.util.Spliterator;
 import java.util.Spliterators;
+import java.util.function.Predicate;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -165,6 +166,14 @@
       System.out.println("true");
       System.out.println(spliterator.hasCharacteristics(Spliterator.IMMUTABLE));
       System.out.println("false");
+
+      spliterator = ((Set<String>) new CustomLinkedHashSetForceForwarding<String>()).spliterator();
+      System.out.println(spliterator.hasCharacteristics(Spliterator.DISTINCT));
+      System.out.println("true");
+      System.out.println(spliterator.hasCharacteristics(Spliterator.ORDERED));
+      System.out.println("true");
+      System.out.println(spliterator.hasCharacteristics(Spliterator.IMMUTABLE));
+      System.out.println("false");
     }
   }
 
@@ -198,4 +207,13 @@
       return super.spliterator();
     }
   }
+
+  // The over method force the forwarding methods to be installed.
+  static class CustomLinkedHashSetForceForwarding<E> extends LinkedHashSet<E> {
+
+    @Override
+    public boolean removeIf(Predicate<? super E> filter) {
+      return super.removeIf(filter);
+    }
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
index 18af69f..d296ec9 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
@@ -131,7 +131,7 @@
             .parse(StringResource.fromFile(ToolHelper.getDesugarLibJsonForTesting()));
 
     for (AndroidApiLevel apiLevel : AndroidApiLevel.values()) {
-      if (apiLevel.isGreaterThan(AndroidApiLevel.LATEST)) {
+      if (apiLevel.isGreaterThan(AndroidApiLevel.Sv2)) {
         continue;
       }
       Path compileApiLevelDirectory = directory.resolve("compile_api_level_" + apiLevel.getLevel());
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java
index d5300ad..1208414 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java
@@ -118,8 +118,8 @@
     L8.run(
         L8Command.builder()
             .addLibraryFiles(getLibraryFile())
+            .addLibraryFiles(ToolHelper.getDesugarJDKLibs())
             .addProgramFiles(JDK_11_JAVA_BASE_EXTENSION_COMPILED_FILES)
-            .addClasspathFiles(ToolHelper.getDesugarJDKLibs())
             .addDesugaredLibraryConfiguration(
                 StringResource.fromFile(ToolHelper.getDesugarLibJsonForTesting()))
             .setMinApiLevel(AndroidApiLevel.B.getLevel())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NeverMergeCoreLibDesugarClasses.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NeverMergeCoreLibDesugarClasses.java
index fbb422a..8a86def 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NeverMergeCoreLibDesugarClasses.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NeverMergeCoreLibDesugarClasses.java
@@ -45,15 +45,16 @@
           .addInnerClasses(NeverMergeCoreLibDesugarClasses.class)
           .addProgramDexFileData(builder.compile())
           .setMinApi(parameters.getRuntime())
-          .compileWithExpectedDiagnostics(diagnostics -> {
-            diagnostics.assertErrorsCount(1);
-            String message = diagnostics.getErrors().get(0).getDiagnosticMessage();
-            assertThat(
-                message,
-                containsString(
-                    "Merging dex file containing classes with prefix 'j$.' "
-                        + "with classes with any other prefixes is not allowed."));
-          });
+          .compileWithExpectedDiagnostics(
+              diagnostics -> {
+                diagnostics.assertErrorsCount(1);
+                String message = diagnostics.getErrors().get(0).getDiagnosticMessage();
+                assertThat(
+                    message,
+                    containsString(
+                        "Merging dex file containing classes with prefix 'j$.' "
+                            + "with classes with any other prefixes is not allowed"));
+              });
     } catch (CompilationFailedException e) {
       // Expected compilation failed.
       return;
@@ -78,7 +79,7 @@
                     message,
                     containsString(
                         "Merging dex file containing classes with prefix 'j$.' "
-                            + "with classes with any other prefixes is not allowed."));
+                            + "with classes with any other prefixes is not allowed"));
               });
     } catch (CompilationFailedException e) {
       // Expected compilation failed.
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
index abdf5c5..ed772cb 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
@@ -79,9 +79,9 @@
   public ObjectsTest(TestParameters parameters, boolean libraryDesugarJavaUtilObjects) {
     this.parameters = parameters;
     this.libraryDesugarJavaUtilObjects = libraryDesugarJavaUtilObjects;
-    // Using desugared library require a compile SDK of 26 or higher.
     this.androidJar =
-        ToolHelper.getAndroidJar(Ordered.max(parameters.getApiLevel(), AndroidApiLevel.O));
+        ToolHelper.getAndroidJar(
+            Ordered.max(parameters.getApiLevel(), getRequiredCompilationAPILevel()));
   }
 
   LegacyDesugaredLibrarySpecification desugaredLibrarySpecification(
@@ -99,13 +99,13 @@
   }
 
   private void configurationForProgramCompilation(InternalOptions options) {
-    options.desugaredLibrarySpecification =
-        desugaredLibrarySpecification(options, false, parameters);
+    setDesugaredLibrarySpecificationForTesting(
+        options, desugaredLibrarySpecification(options, false, parameters));
   }
 
   private void configurationForLibraryCompilation(InternalOptions options) {
-    options.desugaredLibrarySpecification =
-        desugaredLibrarySpecification(options, true, parameters);
+    setDesugaredLibrarySpecificationForTesting(
+        options, desugaredLibrarySpecification(options, true, parameters));
   }
 
   private Matcher<MethodSubject> invokesObjectsCompare(String holder) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetAndBackportTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetAndBackportTest.java
index 359a18c..27762ea 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetAndBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetAndBackportTest.java
@@ -7,6 +7,7 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyRewritingFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyTopLevelFlags;
@@ -14,6 +15,7 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import java.io.IOException;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -44,7 +46,6 @@
    *  {
    *    "rewrite_prefix":{"java.time.": "j$.time."},
    *    "backport": {"java.lang.DesugarMath": "java.lang.Math"},
-   *    "retarget_lib_member": {"java.util.Date#toInstant": "java.util.DesugarDate"}
    *  }
    * ],
    */
@@ -53,11 +54,16 @@
         LegacyRewritingFlags.builder(options.itemFactory, options.reporter, Origin.unknown())
             .putRewritePrefix("java.time.", "j$.time.")
             .putBackportCoreLibraryMember("java.lang.DesugarMath", "java.lang.Math")
-            .putRetargetCoreLibMember("java.util.Date#toInstant", "java.util.DesugarDate")
             .build();
-    options.desugaredLibrarySpecification =
-        new LegacyDesugaredLibrarySpecification(
-            LegacyTopLevelFlags.testing(), rewritingFlags, true, options.itemFactory);
+    try {
+      options.setDesugaredLibrarySpecificationForTesting(
+          new LegacyDesugaredLibrarySpecification(
+              LegacyTopLevelFlags.testing(), rewritingFlags, true),
+          ToolHelper.getDesugarJDKLibs(),
+          ToolHelper.getAndroidJar(AndroidApiLevel.R));
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
index 6bf49a0..e629c47 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
@@ -5,13 +5,7 @@
 package com.android.tools.r8.desugar.desugaredlibrary;
 
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.HumanToMachineSpecificationConverter;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.LegacyToHumanSpecificationConverter;
 import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.InternalOptions;
-import java.io.IOException;
 import java.nio.file.Path;
 import java.time.Instant;
 import java.time.ZonedDateTime;
@@ -31,19 +25,15 @@
 
   private final TestParameters parameters;
   private final boolean shrinkDesugaredLibrary;
-  private final boolean machineSpec;
 
   @Parameters(name = "machine: {0}, {2}, shrink: {1}")
   public static List<Object[]> data() {
     return buildParameters(
         BooleanUtils.values(),
-        BooleanUtils.values(),
         getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
   }
 
-  public RetargetOverrideTest(
-      boolean machineSpec, boolean shrinkDesugaredLibrary, TestParameters parameters) {
-    this.machineSpec = machineSpec;
+  public RetargetOverrideTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
     this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
     this.parameters = parameters;
   }
@@ -54,7 +44,6 @@
     Path desugaredTwice =
         testForD8(Backend.CF)
             .addLibraryFiles(getLibraryFile())
-            .addOptionsModification(this::setMachineSpec)
             .addProgramFiles(
                 testForD8(Backend.CF)
                     .addLibraryFiles(getLibraryFile())
@@ -76,7 +65,6 @@
       stdout =
           testForD8(Backend.DEX)
               .addProgramFiles(desugaredTwice)
-              .addOptionsModification(this::setMachineSpec)
               .setMinApi(parameters.getApiLevel())
               .disableDesugaring()
               .compile()
@@ -107,22 +95,6 @@
     assertLines2By2Correct(stdout);
   }
 
-  private void setMachineSpec(InternalOptions opt) {
-    if (!machineSpec) {
-      return;
-    }
-    try {
-      HumanDesugaredLibrarySpecification human =
-          new LegacyToHumanSpecificationConverter()
-              .convert(opt.desugaredLibrarySpecification, getLibraryFile(), opt);
-      MachineDesugaredLibrarySpecification machine =
-          new HumanToMachineSpecificationConverter().convert(human, getLibraryFile(), opt);
-      opt.testing.machineDesugaredLibrarySpecification = machine;
-    } catch (IOException e) {
-      throw new RuntimeException(e);
-    }
-  }
-
   @Test
   public void testRetargetOverrideD8() throws Exception {
     Assume.assumeTrue(parameters.getRuntime().isDex());
@@ -130,7 +102,6 @@
     String stdout =
         testForD8()
             .addLibraryFiles(getLibraryFile())
-            .addOptionsModification(this::setMachineSpec)
             .addInnerClasses(RetargetOverrideTest.class)
             .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
             .setMinApi(parameters.getApiLevel())
@@ -156,7 +127,6 @@
     String stdout =
         testForR8(Backend.DEX)
             .addLibraryFiles(getLibraryFile())
-            .addOptionsModification(this::setMachineSpec)
             .addKeepMainRule(Executor.class)
             .addInnerClasses(RetargetOverrideTest.class)
             .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java
index f9d1828..7abed46 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java
@@ -58,6 +58,7 @@
         .setMinApi(AndroidApiLevel.B)
         .addProgramClasses(Executor.class)
         .addLibraryClasses(CustomLibClass.class)
+        .addLibraryFiles(getLibraryFile())
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .compile()
         .addDesugaredCoreLibraryRunClassPath(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AccessModeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AccessModeConversionTest.java
index 6691cb1..a57140b 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AccessModeConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AccessModeConversionTest.java
@@ -66,8 +66,8 @@
             .build();
     LegacyDesugaredLibrarySpecification specification =
         new LegacyDesugaredLibrarySpecification(
-            LegacyTopLevelFlags.testing(), rewritingFlags, l8Compilation, options.itemFactory);
-    options.desugaredLibrarySpecification = specification;
+            LegacyTopLevelFlags.testing(), rewritingFlags, l8Compilation);
+    setDesugaredLibrarySpecificationForTesting(options, specification);
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
index aa97e55..957515a 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
@@ -87,9 +87,10 @@
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .addOptionsModification(
             opt ->
-                opt.desugaredLibrarySpecification =
+                setDesugaredLibrarySpecificationForTesting(
+                    opt,
                     configurationWithSupportAllCallbacksFromLibrary(
-                        opt, false, parameters, supportAllCallbacksFromLibrary))
+                        opt, false, parameters, supportAllCallbacksFromLibrary)))
         .compile()
         .inspect(this::assertDoubleForEach)
         .inspect(this::assertWrapperMethodsPresent)
@@ -151,9 +152,10 @@
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .addOptionsModification(
             opt ->
-                opt.desugaredLibrarySpecification =
+                setDesugaredLibrarySpecificationForTesting(
+                    opt,
                     configurationWithSupportAllCallbacksFromLibrary(
-                        opt, false, parameters, supportAllCallbacksFromLibrary))
+                        opt, false, parameters, supportAllCallbacksFromLibrary)))
         .compile()
         .inspect(this::assertDoubleForEach)
         .inspect(this::assertWrapperMethodsPresent)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionsPresentTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionsPresentTest.java
index 01f9b80..6972449 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionsPresentTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionsPresentTest.java
@@ -46,6 +46,7 @@
         L8Command.builder(diagnosticsHandler)
             .addLibraryFiles(getLibraryFile())
             .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
+            .addProgramFiles(ToolHelper.getDesugarJDKLibs())
             .addDesugaredLibraryConfiguration(
                 StringResource.fromFile(ToolHelper.getDesugarLibJsonForTesting()))
             .setMinApiLevel(parameters.getApiLevel().getLevel())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIDesugaredLibTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIDesugaredLibTest.java
index 6b7d86e..b966b00 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIDesugaredLibTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIDesugaredLibTest.java
@@ -71,9 +71,10 @@
                         this.buildDesugaredLibrary(
                             api,
                             opt ->
-                                opt.desugaredLibrarySpecification =
+                                setDesugaredLibrarySpecificationForTesting(
+                                    opt,
                                     configurationWithSupportAllCallbacksFromLibrary(
-                                        opt, true, parameters, supportAllCallbacksFromLibrary)));
+                                        opt, true, parameters, supportAllCallbacksFromLibrary))));
                     return desugaredLibBox.get();
                   },
                   AndroidApiLevel.B)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/TryCatchTimeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/TryCatchTimeConversionTest.java
index a669527..aaab5cb 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/TryCatchTimeConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/TryCatchTimeConversionTest.java
@@ -58,6 +58,7 @@
         .setMinApi(parameters.getApiLevel())
         .addProgramClasses(BaselineExecutor.class)
         .addLibraryClasses(CustomLibClass.class)
+        .addLibraryFiles(getLibraryFile())
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .compile()
         .addDesugaredCoreLibraryRunClassPath(
@@ -98,6 +99,7 @@
         .setMinApi(parameters.getApiLevel())
         .addProgramClasses(TryCatchExecutor.class)
         .addLibraryClasses(CustomLibClass.class)
+        .addLibraryFiles(getLibraryFile())
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .compile()
         .addDesugaredCoreLibraryRunClassPath(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
index 2bd50b2..3b6ae0a 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
@@ -270,6 +270,7 @@
               .addProgramFiles(testNGSupportProgramFiles())
               .addOptionsModification(opt -> opt.testing.trackDesugaredAPIConversions = true)
               .addLibraryFiles(getLibraryFile())
+              .addLibraryFiles(JDK_11_JAVA_BASE_EXTENSION_CLASSES_DIR)
               .setMinApi(parameters.getApiLevel())
               .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
               .allowStdoutMessages()
@@ -303,6 +304,7 @@
           .addProgramFiles(testNGSupportProgramFiles())
           .addOptionsModification(opt -> opt.testing.trackDesugaredAPIConversions = true)
           .addLibraryFiles(getLibraryFile())
+          .addLibraryFiles(JDK_11_JAVA_BASE_EXTENSION_CLASSES_DIR)
           .setMinApi(parameters.getApiLevel())
           .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
           .compile()
diff --git a/src/test/java/com/android/tools/r8/desugar/jdk8272564/Jdk8272564Test.java b/src/test/java/com/android/tools/r8/desugar/jdk8272564/Jdk8272564Test.java
new file mode 100644
index 0000000..191cc22
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/jdk8272564/Jdk8272564Test.java
@@ -0,0 +1,164 @@
+// 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.jdk8272564;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+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.TestRuntime;
+import com.android.tools.r8.examples.jdk18.jdk8272564.Jdk8272564;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions.TestingOptions;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+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 Jdk8272564Test extends TestBase {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    // TODO(b/218293990): Right now the JDK 18 tests are built with -target 17, as our Gradle
+    //  version does not know of -target 18.
+    // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk17).
+    return getTestParameters()
+        .withCustomRuntime(TestRuntime.getCheckedInJdk17())
+        .withDexRuntimes()
+        .withAllApiLevelsAlsoForCf()
+        .build();
+  }
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  // With the fix for JDK-8272564 there are no invokevirtual instructions.
+  private void assertJdk8272564FixedCode(CodeInspector inspector) {
+    assertTrue(
+        inspector
+            .clazz(Jdk8272564.Main.typeName())
+            .uniqueMethodWithName("f")
+            .streamInstructions()
+            .noneMatch(InstructionSubject::isInvokeVirtual));
+    assertTrue(
+        inspector
+            .clazz(Jdk8272564.Main.typeName())
+            .uniqueMethodWithName("g")
+            .streamInstructions()
+            .noneMatch(InstructionSubject::isInvokeVirtual));
+  }
+
+  // Without the fix for JDK-8272564 there is one invokeinterface and 2 invokevirtual instructions.
+  private void assertJdk8272564NotFixedCode(
+      CodeInspector inspector, int invokeVirtualCount, int getClassCount) {
+    assertEquals(
+        1,
+        inspector
+            .clazz(Jdk8272564.Main.typeName())
+            .uniqueMethodWithName("f")
+            .streamInstructions()
+            .filter(InstructionSubject::isInvokeInterface)
+            .count());
+    assertEquals(
+        2,
+        inspector
+            .clazz(Jdk8272564.Main.typeName())
+            .uniqueMethodWithName("f")
+            .streamInstructions()
+            .filter(InstructionSubject::isInvokeVirtual)
+            .count());
+    assertEquals(
+        2,
+        inspector
+            .clazz(Jdk8272564.Main.typeName())
+            .uniqueMethodWithName("g")
+            .streamInstructions()
+            .filter(InstructionSubject::isInvokeInterface)
+            .count());
+    assertEquals(
+        2,
+        inspector
+            .clazz(Jdk8272564.Main.typeName())
+            .uniqueMethodWithName("g")
+            .streamInstructions()
+            .filter(InstructionSubject::isInvokeInterface)
+            .count());
+    assertEquals(
+        invokeVirtualCount,
+        inspector
+            .clazz(Jdk8272564.Main.typeName())
+            .uniqueMethodWithName("g")
+            .streamInstructions()
+            .filter(InstructionSubject::isInvokeVirtual)
+            .count());
+    assertEquals(
+        getClassCount,
+        inspector
+            .clazz(Jdk8272564.Main.typeName())
+            .uniqueMethodWithName("g")
+            .streamInstructions()
+            .filter(InstructionSubject::isInvoke)
+            .filter(instruction -> instruction.getMethod().getName().toString().equals("getClass"))
+            .count());
+  }
+
+  private void assertJdk8272564NotFixedCode(CodeInspector inspector) {
+    assertJdk8272564NotFixedCode(inspector, 22, 3);
+  }
+
+  private void assertJdk8272564NotFixedCodeR8(CodeInspector inspector) {
+    assertJdk8272564NotFixedCode(inspector, 19, 0);
+  }
+
+  @Test
+  // See https://bugs.openjdk.java.net/browse/JDK-8272564.
+  public void testJdk8272564Compiler() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    // Ensure that the test is running with CF input from fixing JDK-8272564.
+    assertJdk8272564FixedCode(new CodeInspector(Jdk8272564.jar()));
+  }
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForJvm()
+        .addRunClasspathFiles(Jdk8272564.jar())
+        .run(TestRuntime.getCheckedInJdk17(), Jdk8272564.Main.typeName())
+        .assertSuccess();
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    testForDesugaring(parameters)
+        .addProgramFiles(Jdk8272564.jar())
+        .run(parameters.getRuntime(), Jdk8272564.Main.typeName())
+        .applyIf(
+            parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
+            b -> b.inspect(this::assertJdk8272564NotFixedCode),
+            b -> b.inspect(this::assertJdk8272564FixedCode))
+        .assertSuccess();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    // The R8 lens code rewriter rewrites to the code prior to fixing JDK-8272564.
+    testForR8(parameters.getBackend())
+        .addProgramFiles(Jdk8272564.jar())
+        .setMinApi(parameters.getApiLevel())
+        .noTreeShaking()
+        .addKeepClassAndMembersRules(Jdk8272564.Main.typeName())
+        .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
+        .run(parameters.getRuntime(), Jdk8272564.Main.typeName())
+        .inspect(this::assertJdk8272564NotFixedCodeR8)
+        .assertSuccess();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/examples/jdk18/jdk8272564/Jdk8272564.java b/src/test/java/com/android/tools/r8/examples/jdk18/jdk8272564/Jdk8272564.java
new file mode 100644
index 0000000..5e891aa
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/jdk18/jdk8272564/Jdk8272564.java
@@ -0,0 +1,32 @@
+// 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.examples.jdk18.jdk8272564;
+
+import com.android.tools.r8.examples.JavaExampleClassProxy;
+import java.nio.file.Path;
+
+public class Jdk8272564 {
+
+  private static final String EXAMPLE_FILE = "examplesJava18/jdk8272564";
+
+  public static final JavaExampleClassProxy A =
+      new JavaExampleClassProxy(EXAMPLE_FILE, "jdk8272564/A");
+  public static final JavaExampleClassProxy B =
+      new JavaExampleClassProxy(EXAMPLE_FILE, "jdk8272564/B");
+  public static final JavaExampleClassProxy C =
+      new JavaExampleClassProxy(EXAMPLE_FILE, "jdk8272564/C");
+  public static final JavaExampleClassProxy I =
+      new JavaExampleClassProxy(EXAMPLE_FILE, "jdk8272564/I");
+  public static final JavaExampleClassProxy J =
+      new JavaExampleClassProxy(EXAMPLE_FILE, "jdk8272564/J");
+  public static final JavaExampleClassProxy K =
+      new JavaExampleClassProxy(EXAMPLE_FILE, "jdk8272564/K");
+  public static final JavaExampleClassProxy Main =
+      new JavaExampleClassProxy(EXAMPLE_FILE, "jdk8272564/Main");
+
+  public static Path jar() {
+    return JavaExampleClassProxy.examplesJar(EXAMPLE_FILE);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/NarrowingWithoutSubtypingTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/NarrowingWithoutSubtypingTest.java
index b78279e..403372c 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/NarrowingWithoutSubtypingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/NarrowingWithoutSubtypingTest.java
@@ -4,6 +4,12 @@
 
 package com.android.tools.r8.ir.analysis.type;
 
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThrows;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.D8TestBuilder;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.utils.BooleanUtils;
@@ -32,17 +38,32 @@
 
   @Test
   public void test() throws Exception {
-    testForD8()
-        .addInnerClasses(NarrowingWithoutSubtypingTest.class)
-        .addOptionsModification(
-            options -> {
-              options.testing.readInputStackMaps = readStackMap;
-              options.testing.enableNarrowAndWideningingChecksInD8 = true;
-              options.testing.noLocalsTableOnInput = true;
-            })
-        .setMinApi(parameters.getApiLevel())
-        .run(parameters.getRuntime(), TestClass.class)
-        .assertSuccessWithOutputLines("Hello world!");
+    D8TestBuilder d8TestBuilder =
+        testForD8()
+            .addInnerClasses(NarrowingWithoutSubtypingTest.class)
+            .addOptionsModification(
+                options -> {
+                  options.testing.readInputStackMaps = readStackMap;
+                  options.testing.enableNarrowAndWideningingChecksInD8 = true;
+                  options.testing.noLocalsTableOnInput = true;
+                })
+            .setMinApi(parameters.getApiLevel());
+    if (readStackMap) {
+      d8TestBuilder
+          .run(parameters.getRuntime(), TestClass.class)
+          .assertSuccessWithOutputLines("Hello world!");
+    } else {
+      // TODO(b/169120386): We should not be narrowing in D8.
+      assertThrows(
+          CompilationFailedException.class,
+          () -> {
+            d8TestBuilder.compileWithExpectedDiagnostics(
+                diagnostics ->
+                    diagnostics.assertAllErrorsMatch(
+                        diagnosticMessage(
+                            containsString("java.lang.AssertionError: During NARROWING"))));
+          });
+    }
   }
 
   static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ifs/IfThrowNullPointerExceptionTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ifs/IfThrowNullPointerExceptionTest.java
index e679e80..d0b977b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/ifs/IfThrowNullPointerExceptionTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ifs/IfThrowNullPointerExceptionTest.java
@@ -17,7 +17,6 @@
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -80,7 +79,7 @@
     ClassSubject classSubject = inspector.clazz(TestClass.class);
     assertThat(classSubject, isPresent());
     inspectMethod(inspector, classSubject, "testThrowNPE", false, true);
-    inspectMethod(inspector, classSubject, "testThrowNPEWithMessage", true, canUseRequireNonNull());
+    inspectMethod(inspector, classSubject, "testThrowNPEWithMessage", true, false);
     inspectMethod(inspector, classSubject, "testThrowNull", false, true);
   }
 
@@ -105,31 +104,18 @@
 
       Instruction nullCheckInstruction =
           entryBlock.getInstructions().get(1 + BooleanUtils.intValue(isNPEWithMessage));
-      if (canUseRequireNonNull()) {
-        assertTrue(nullCheckInstruction.isInvokeStatic());
-        if (isNPEWithMessage) {
-          assertEquals(
-              inspector.getFactory().objectsMethods.requireNonNullWithMessage,
-              nullCheckInstruction.asInvokeStatic().getInvokedMethod());
-        } else {
-          assertEquals(
-              inspector.getFactory().objectsMethods.requireNonNull,
-              nullCheckInstruction.asInvokeStatic().getInvokedMethod());
-        }
-      } else {
-        assertFalse(isNPEWithMessage);
-        assertTrue(nullCheckInstruction.isInvokeVirtual());
-        assertEquals(
-            inspector.getFactory().objectMembers.getClass,
-            nullCheckInstruction.asInvokeVirtual().getInvokedMethod());
-      }
+      assertFalse(isNPEWithMessage);
+      assertTrue(nullCheckInstruction.isInvokeVirtual());
+      assertEquals(
+          inspector.getFactory().objectMembers.getClass,
+          nullCheckInstruction.asInvokeVirtual().getInvokedMethod());
     } else {
       assertEquals(3, code.blocks.size());
     }
   }
 
   private String getExpectedStdout() {
-    if (parameters.isCfRuntime() || canUseRequireNonNull() || isDalvik()) {
+    if (parameters.isCfRuntime() || isDalvik()) {
       return StringUtils.lines("Caught NPE: null", "Caught NPE: x was null", "Caught NPE: null");
     }
     return StringUtils.lines(
@@ -140,11 +126,6 @@
             + " on a null object reference");
   }
 
-  private boolean canUseRequireNonNull() {
-    return parameters.isDexRuntime()
-        && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.K);
-  }
-
   private boolean isDalvik() {
     return parameters.isDexRuntime()
         && parameters.getRuntime().asDex().getVm().getVersion().isDalvik();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineInvokeWithNullableReceiverTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineInvokeWithNullableReceiverTest.java
index dce7b2d..527d320 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineInvokeWithNullableReceiverTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineInvokeWithNullableReceiverTest.java
@@ -68,20 +68,16 @@
     assertThat(methodSubject, isPresent());
 
     // A `throw` instruction should have been synthesized into main().
-    if (canUseRequireNonNull()) {
-      assertTrue(methodSubject.streamInstructions().anyMatch(InstructionSubject::isInvokeStatic));
-    } else {
-      assertTrue(
-          methodSubject
-              .streamInstructions()
-              .filter(InstructionSubject::isInvokeVirtual)
-              .anyMatch(
-                  method ->
-                      method
-                          .getMethod()
-                          .toSourceString()
-                          .equals("java.lang.Class java.lang.Object.getClass()")));
-    }
+    assertTrue(
+        methodSubject
+            .streamInstructions()
+            .filter(InstructionSubject::isInvokeVirtual)
+            .anyMatch(
+                method ->
+                    method
+                        .getMethod()
+                        .toSourceString()
+                        .equals("java.lang.Class java.lang.Object.getClass()")));
 
     // Class A is still present because the instance flows into a phi that has a null-check.
     ClassSubject otherClassSubject = inspector.clazz(A.class);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineMethodWithRetargetedLibMemberTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineMethodWithRetargetedLibMemberTest.java
index 03b1280..71b7da8 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineMethodWithRetargetedLibMemberTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineMethodWithRetargetedLibMemberTest.java
@@ -12,8 +12,7 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import java.util.Arrays;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -21,7 +20,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class InlineMethodWithRetargetedLibMemberTest extends TestBase {
+public class InlineMethodWithRetargetedLibMemberTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
 
@@ -37,7 +36,7 @@
   @Test
   public void test() throws Exception {
     testForR8(parameters.getBackend())
-        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+        .addLibraryFiles(getLibraryFile())
         .addProgramClasses(TestClass.class)
         .addKeepMainRule(TestClass.class)
         .enableCoreLibraryDesugaring(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SyntheticInlineNullCheckPositionTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SyntheticInlineNullCheckPositionTest.java
index 7cdd9d0..a10e302 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SyntheticInlineNullCheckPositionTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SyntheticInlineNullCheckPositionTest.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.ir.optimize.inliner;
 
 import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
-import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForSpecificLineNumber;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.TestBase;
@@ -70,16 +69,7 @@
         .run(parameters.getRuntime(), Main.class)
         .assertFailureWithErrorThatThrows(NullPointerException.class)
         .inspectStackTrace(
-            stackTrace -> {
-              if (canUseJavaUtilObjectsRequireNonNull(parameters)) {
-                assertThat(
-                    stackTrace,
-                    isSameExceptForSpecificLineNumber(
-                        expectedStackTraceWithRequireNonNull, REQUIRE_NON_NULL_LINE));
-              } else {
-                assertThat(stackTrace, isSame(expectedStackTraceWithGetClass));
-              }
-            });
+            stackTrace -> assertThat(stackTrace, isSame(expectedStackTraceWithGetClass)));
   }
 
   static class Main {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/AssumeInstanceFieldValueTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/AssumeInstanceFieldValueTest.java
index 4e275f4..edd2219 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/AssumeInstanceFieldValueTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/AssumeInstanceFieldValueTest.java
@@ -70,11 +70,7 @@
 
     MethodSubject mainMethodSubject = testClassSubject.mainMethod();
     assertThat(mainMethodSubject, isPresent());
-    if (canUseJavaUtilObjectsRequireNonNull(parameters)) {
-      assertThat(mainMethodSubject, invokesMethodWithName("requireNonNull"));
-    } else {
-      assertThat(mainMethodSubject, invokesMethodWithName("getClass"));
-    }
+    assertThat(mainMethodSubject, invokesMethodWithName("getClass"));
   }
 
   static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/EffectivelyUnusedNullableArgumentTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/EffectivelyUnusedNullableArgumentTest.java
index 54d2071..f01e7b7 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/EffectivelyUnusedNullableArgumentTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/EffectivelyUnusedNullableArgumentTest.java
@@ -55,12 +55,7 @@
               assertEquals(
                   aClassSubject.getDexProgramClass().getType(),
                   fooMethodSubject.getProgramMethod().getParameter(0));
-              assertThat(
-                  fooMethodSubject,
-                  invokesMethodWithName(
-                      canUseJavaUtilObjectsRequireNonNull(parameters)
-                          ? "requireNonNull"
-                          : "getClass"));
+              assertThat(fooMethodSubject, invokesMethodWithName("getClass"));
 
               MethodSubject barMethodSubject = aClassSubject.uniqueMethodWithName("bar");
               assertThat(barMethodSubject, isStatic());
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java b/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
index f3103f1..2d1b98c 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
@@ -12,10 +12,12 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.memberrebinding.b135627418.library.Drawable;
 import com.android.tools.r8.memberrebinding.b135627418.library.DrawableWrapper;
 import com.android.tools.r8.memberrebinding.b135627418.library.InsetDrawable;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -75,11 +77,23 @@
                 com.android.tools.r8.memberrebinding.b135627418.runtime.InsetDrawable.class)
             .setMinApi(parameters.getRuntime())
             .addOptionsModification(
-                options ->
-                    options.desugaredLibrarySpecification =
-                        LegacyDesugaredLibrarySpecification.withOnlyRewritePrefixForTesting(
-                            ImmutableMap.of(packageName + ".runtime", packageName + ".library"),
-                            options))
+                options -> {
+                  DexType type =
+                      options
+                          .dexItemFactory()
+                          .createType(
+                              DescriptorUtils.javaTypeToDescriptor(
+                                  packageName + ".runtime.InsetDrawable"));
+                  DexType rewrittenType =
+                      options
+                          .dexItemFactory()
+                          .createType(
+                              DescriptorUtils.javaTypeToDescriptor(
+                                  packageName + ".library.InsetDrawable"));
+                  options.machineDesugaredLibrarySpecification =
+                      MachineDesugaredLibrarySpecification.withOnlyRewriteTypeForTesting(
+                          ImmutableMap.of(type, rewrittenType));
+                })
             .compile();
 
     testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingOnLibraryPathTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingOnLibraryPathTest.java
new file mode 100644
index 0000000..df6fe4d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingOnLibraryPathTest.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.naming.applymapping;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+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 ApplyMappingOnLibraryPathTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addLibraryClasses(LibraryClass.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .addProgramClasses(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .addApplyMapping(typeName(LibraryClass.class) + " -> a.a:")
+        .addKeepMainRule(Main.class)
+        .compile()
+        .inspect(
+            inspector -> {
+              ClassSubject clazz = inspector.clazz(Main.class);
+              assertThat(clazz, isPresentAndNotRenamed());
+              FoundClassSubject foundClassSubject = clazz.asFoundClassSubject();
+              assertEquals(
+                  typeName(LibraryClass.class),
+                  foundClassSubject.getSuperClass().getOriginalName());
+            })
+        .addRunClasspathClasses(LibraryClass.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("LibraryClass::foo");
+  }
+
+  public static class LibraryClass {
+
+    public void foo() {
+      System.out.println("LibraryClass::foo");
+    }
+  }
+
+  public static class Main extends LibraryClass {
+
+    public static void main(String[] args) {
+      new Main().foo();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineBranchTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineBranchTest.java
index 92b6019..b07255b 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineBranchTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineBranchTest.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.naming.retrace;
 
 import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
-import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForLineNumbers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
@@ -16,11 +15,9 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import java.util.List;
-import java.util.Objects;
 import java.util.Optional;
 import java.util.stream.Collectors;
 import org.junit.Before;
@@ -64,50 +61,30 @@
         .run(parameters.getRuntime(), Main.class)
         .inspectStackTrace(
             (stackTrace, inspector) -> {
-              if (canUseJavaUtilObjectsRequireNonNull(parameters)) {
-                StackTrace requireNonNullFrame =
-                    StackTrace.builder().add(stackTrace.get(0)).build();
-                assertThat(
-                    requireNonNullFrame,
-                    isSameExceptForLineNumbers(
-                        StackTrace.builder()
-                            .add(
-                                StackTraceLine.builder()
-                                    .setClassName(Objects.class.getTypeName())
-                                    .setMethodName("requireNonNull")
-                                    .setFileName("Objects.java")
-                                    .build())
-                            .build()));
-
-                StackTrace stackTraceWithoutRequireNonNull =
-                    StackTrace.builder().add(stackTrace).remove(0).build();
-                assertThat(stackTraceWithoutRequireNonNull, isSame(expectedStackTrace));
-              } else {
-                // To check that we inserted the check in the branch correctly we use a rudimentary
-                // line check by comparing that the getClass method comes later than the first if.
-                MethodSubject methodSubject =
-                    inspector.clazz(Main.class).uniqueMethodWithName("call");
-                assertThat(methodSubject, isPresent());
-                List<InstructionSubject> getClassCalls =
-                    methodSubject
-                        .streamInstructions()
-                        .filter(
-                            instructionSubject ->
-                                instructionSubject.isInvoke()
-                                    && instructionSubject
-                                        .getMethod()
-                                        .qualifiedName()
-                                        .contains("getClass"))
-                        .collect(Collectors.toList());
-                assertEquals(1, getClassCalls.size());
-                Optional<InstructionSubject> firstIf =
-                    methodSubject.streamInstructions().filter(InstructionSubject::isIf).findFirst();
-                assertTrue(firstIf.isPresent());
+              MethodSubject methodSubject =
+                  inspector.clazz(Main.class).uniqueMethodWithName("call");
+              assertThat(methodSubject, isPresent());
+              List<InstructionSubject> getClassCalls =
+                  methodSubject
+                      .streamInstructions()
+                      .filter(
+                          instructionSubject ->
+                              instructionSubject.isInvoke()
+                                  && instructionSubject
+                                      .getMethod()
+                                      .qualifiedName()
+                                      .contains("getClass"))
+                      .collect(Collectors.toList());
+              assertEquals(1, getClassCalls.size());
+              Optional<InstructionSubject> firstIf =
+                  methodSubject.streamInstructions().filter(InstructionSubject::isIf).findFirst();
+              assertTrue(firstIf.isPresent());
+              if (methodSubject.hasLineNumberTable()) {
                 assertTrue(
                     methodSubject.getLineNumberForInstruction(getClassCalls.get(0))
                         > methodSubject.getLineNumberForInstruction(firstIf.get()));
-                assertThat(stackTrace, isSame(expectedStackTrace));
               }
+              assertThat(stackTrace, isSame(expectedStackTrace));
             });
   }
 
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineConditionTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineConditionTest.java
index 36921ce..b005549 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineConditionTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineConditionTest.java
@@ -4,15 +4,20 @@
 
 package com.android.tools.r8.naming.retrace;
 
+import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 
+import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.SingleTestRunResult;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import org.hamcrest.CoreMatchers;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -48,16 +53,26 @@
 
   @Test
   public void testR8() throws Throwable {
-    testForR8(parameters.getBackend())
-        .addInnerClasses(getClass())
-        .setMinApi(parameters.getApiLevel())
-        .addKeepMainRule(Main.class)
-        .compile()
-        .inspectProguardMap(
-            map -> {
-              assertThat(
-                  map, not(CoreMatchers.containsString("com.android.tools.r8.rewriteFrame")));
-            });
+    R8TestCompileResult compileResult =
+        testForR8(parameters.getBackend())
+            .addInnerClasses(getClass())
+            .setMinApi(parameters.getApiLevel())
+            .addKeepMainRule(Main.class)
+            .addKeepAttributeLineNumberTable()
+            .compile()
+            .inspectProguardMap(map -> not(containsString("com.android.tools.r8.rewriteFrame")))
+            .inspect(
+                inspector -> {
+                  ClassSubject fooClass = inspector.clazz(Foo.class);
+                  assertThat(fooClass, isPresent());
+                  MethodSubject inlineable = fooClass.uniqueMethodWithName("inlineable");
+                  assertThat(inlineable, not(isPresent()));
+                });
+    compileResult
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailureWithErrorThatThrows(NullPointerException.class)
+        .inspectStackTrace(
+            (stackTrace, codeInspector) -> assertThat(stackTrace, isSame(expectedStackTrace)));
   }
 
   static class Foo {
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckSequenceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckSequenceTest.java
index be348ec..48379c5 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckSequenceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckSequenceTest.java
@@ -4,15 +4,12 @@
 package com.android.tools.r8.naming.retrace;
 
 import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
-import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForLineNumbers;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
 import java.util.List;
-import java.util.Objects;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -58,27 +55,7 @@
         .assertFailureWithErrorThatThrows(NullPointerException.class)
         .inspectStackTrace(
             (stackTrace, codeInspector) -> {
-              if (canUseJavaUtilObjectsRequireNonNull(parameters)) {
-                StackTrace requireNonNullFrame =
-                    StackTrace.builder().add(stackTrace.get(0)).build();
-                assertThat(
-                    requireNonNullFrame,
-                    isSameExceptForLineNumbers(
-                        StackTrace.builder()
-                            .add(
-                                StackTraceLine.builder()
-                                    .setClassName(Objects.class.getTypeName())
-                                    .setMethodName("requireNonNull")
-                                    .setFileName("Objects.java")
-                                    .build())
-                            .build()));
-
-                StackTrace stackTraceWithoutRequireNonNull =
-                    StackTrace.builder().add(stackTrace).remove(0).build();
-                assertThat(stackTraceWithoutRequireNonNull, isSame(expectedStackTrace));
-              } else {
-                assertThat(stackTrace, isSame(expectedStackTrace));
-              }
+              assertThat(stackTrace, isSame(expectedStackTrace));
             });
   }
 
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckTest.java
index 70c2be4..0855291 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckTest.java
@@ -4,16 +4,13 @@
 package com.android.tools.r8.naming.retrace;
 
 import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
-import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForLineNumbers;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
 import com.android.tools.r8.utils.BooleanUtils;
 import java.util.List;
-import java.util.Objects;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -66,31 +63,7 @@
         .run(parameters.getRuntime(), Caller.class, getArgs())
         .assertFailureWithErrorThatThrows(NullPointerException.class)
         .inspectStackTrace(
-            (stackTrace, codeInspector) -> {
-              // TODO(b/214377135): The stack traces should be the same (when 206427323) is
-              //  resolved.
-              if (throwReceiverNpe && canUseJavaUtilObjectsRequireNonNull(parameters)) {
-                StackTrace requireNonNullFrame =
-                    StackTrace.builder().add(stackTrace.get(0)).build();
-                assertThat(
-                    requireNonNullFrame,
-                    isSameExceptForLineNumbers(
-                        StackTrace.builder()
-                            .add(
-                                StackTraceLine.builder()
-                                    .setClassName(Objects.class.getTypeName())
-                                    .setMethodName("requireNonNull")
-                                    .setFileName("Objects.java")
-                                    .build())
-                            .build()));
-
-                StackTrace stackTraceWithoutRequireNonNull =
-                    StackTrace.builder().add(stackTrace).remove(0).build();
-                assertThat(stackTraceWithoutRequireNonNull, isSame(expectedStackTrace));
-              } else {
-                assertThat(stackTrace, isSame(expectedStackTrace));
-              }
-            });
+            (stackTrace, codeInspector) -> assertThat(stackTrace, isSame(expectedStackTrace)));
   }
 
   static class Foo {
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/NullableCompanionConstructorShakingTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/NullableCompanionConstructorShakingTest.java
index 594f86f..bca23cb 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/NullableCompanionConstructorShakingTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/NullableCompanionConstructorShakingTest.java
@@ -59,12 +59,7 @@
                       .streamInstructions()
                       .filter(InstructionSubject::isInvoke)
                       .count());
-              assertThat(
-                  mainMethodSubject,
-                  invokesMethodWithName(
-                      canUseJavaUtilObjectsRequireNonNull(parameters)
-                          ? "requireNonNull"
-                          : "getClass"));
+              assertThat(mainMethodSubject, invokesMethodWithName("getClass"));
               assertThat(mainMethodSubject, invokesMethod(companionMethodSubject));
             })
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/NormalizeTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptMethodTest.java
similarity index 65%
copy from src/test/java/com/android/tools/r8/optimize/proto/NormalizeTest.java
copy to src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptMethodTest.java
index c78ced4..70dbf9d 100644
--- a/src/test/java/com/android/tools/r8/optimize/proto/NormalizeTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptMethodTest.java
@@ -5,16 +5,16 @@
 package com.android.tools.r8.optimize.proto;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.MethodMatchers.hasParameters;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NoHorizontalClassMerging;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.TypeSubject;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -22,7 +22,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class NormalizeTest extends TestBase {
+public class ProtoNormalizationWithKeptMethodTest extends TestBase {
 
   @Parameter(0)
   public TestParameters parameters;
@@ -37,6 +37,7 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
+        .addKeepRules("-keep class " + Main.class.getTypeName() + " { void foo(...); }")
         .addOptionsModification(
             options -> options.testing.enableExperimentalProtoNormalization = true)
         .enableInliningAnnotations()
@@ -47,40 +48,48 @@
         .compile()
         .inspect(
             inspector -> {
-              ClassSubject aClassSubject = inspector.clazz(A.class);
-              assertThat(aClassSubject, isPresent());
-
-              ClassSubject bClassSubject = inspector.clazz(B.class);
-              assertThat(bClassSubject, isPresent());
+              TypeSubject aTypeSubject = inspector.clazz(A.class).asTypeSubject();
+              TypeSubject bTypeSubject = inspector.clazz(B.class).asTypeSubject();
 
               MethodSubject fooMethodSubject =
                   inspector.clazz(Main.class).uniqueMethodWithName("foo");
               assertThat(fooMethodSubject, isPresent());
+              assertThat(fooMethodSubject, hasParameters(bTypeSubject, aTypeSubject));
 
-              String expectedMethodSignature =
-                  "void "
-                      + fooMethodSubject.getFinalName()
-                      + "("
-                      + aClassSubject.getFinalName()
-                      + ", "
-                      + bClassSubject.getFinalName()
-                      + ")";
-              assertEquals(
-                  expectedMethodSignature,
-                  fooMethodSubject.getProgramMethod().getMethodSignature().toString());
+              for (String methodName : new String[] {"bar", "baz"}) {
+                MethodSubject methodSubject =
+                    inspector.clazz(Main.class).uniqueMethodWithName(methodName);
+                assertThat(methodSubject, isPresent());
+                assertThat(methodSubject, hasParameters(bTypeSubject, aTypeSubject));
+              }
             })
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines("A", "B");
+        .assertSuccessWithOutputLines("A", "B", "A", "B", "A", "B");
   }
 
   static class Main {
 
     public static void main(String[] args) {
       foo(new B(), new A());
+      bar(new B(), new A());
+      baz(new A(), new B());
+    }
+
+    // @Keep
+    @NeverInline
+    static void foo(B b, A a) {
+      System.out.println(a);
+      System.out.println(b);
     }
 
     @NeverInline
-    static void foo(B b, A a) {
+    static void bar(B b, A a) {
+      System.out.println(a);
+      System.out.println(b);
+    }
+
+    @NeverInline
+    static void baz(A a, B b) {
       System.out.println(a);
       System.out.println(b);
     }
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptVirtualMethodTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptVirtualMethodTest.java
new file mode 100644
index 0000000..e9655ba
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptVirtualMethodTest.java
@@ -0,0 +1,118 @@
+// 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.optimize.proto;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.MethodMatchers.hasParameters;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.TypeSubject;
+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 ProtoNormalizationWithKeptVirtualMethodTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepClassAndMembersRules(Main.class)
+        .addKeepRules("-keepclassmembers class " + A.class.getTypeName() + " { void foo(...); }")
+        .addOptionsModification(
+            options -> options.testing.enableExperimentalProtoNormalization = true)
+        .enableInliningAnnotations()
+        .enableNoVerticalClassMergingAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(
+            inspector -> {
+              ClassSubject aClassSubject = inspector.clazz(A.class);
+              assertThat(aClassSubject, isPresent());
+
+              ClassSubject bClassSubject = inspector.clazz(B.class);
+              assertThat(bClassSubject, isPresent());
+
+              TypeSubject aTypeSubject = aClassSubject.asTypeSubject();
+              TypeSubject bTypeSubject = bClassSubject.asTypeSubject();
+
+              // A.foo(B, A) is kept.
+              MethodSubject fooMethodSubject = aClassSubject.uniqueMethodWithName("foo");
+              assertThat(fooMethodSubject, isPresent());
+              assertThat(fooMethodSubject, hasParameters(bTypeSubject, aTypeSubject));
+
+              // B.foo(B, A) overrides kept method.
+              MethodSubject otherFooMethodSubject = bClassSubject.uniqueMethodWithName("foo");
+              assertThat(otherFooMethodSubject, isPresent());
+              assertThat(otherFooMethodSubject, hasParameters(bTypeSubject, aTypeSubject));
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("A", "B", "A", "B");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      A a = new A();
+      B b = new B();
+      a.foo(b, a);
+      b.foo(b, a);
+      extra(a, b);
+    }
+
+    // @Keep to ensure that the program has parameter lists (A, B) and (B, A), or we will not
+    // optimize in the first place.
+    static void extra(A a, B b) {}
+  }
+
+  @NoVerticalClassMerging
+  static class A {
+
+    // @Keep
+    @NeverInline
+    public void foo(B b, A a) {
+      System.out.println(a);
+      System.out.println(b);
+    }
+
+    @Override
+    public String toString() {
+      return "A";
+    }
+  }
+
+  static class B extends A {
+
+    @NeverInline
+    @Override
+    public void foo(B b, A a) {
+      System.out.println(a);
+      System.out.println(b);
+    }
+
+    @Override
+    public String toString() {
+      return "B";
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithLibraryOverrideTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithLibraryOverrideTest.java
new file mode 100644
index 0000000..52d4df1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithLibraryOverrideTest.java
@@ -0,0 +1,113 @@
+// 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.optimize.proto;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.MethodMatchers.hasParameters;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.TypeSubject;
+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 ProtoNormalizationWithLibraryOverrideTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(Main.class, Program.class)
+        .addLibraryClasses(A.class, B.class, Library.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            options -> options.testing.enableExperimentalProtoNormalization = true)
+        .enableInliningAnnotations()
+        // TODO(b/173398086): uniqueMethodWithName() does not work with proto changes.
+        .noMinification()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(
+            inspector -> {
+              TypeSubject aTypeSubject = inspector.getTypeSubject(A.class.getTypeName());
+              TypeSubject bTypeSubject = inspector.getTypeSubject(B.class.getTypeName());
+
+              MethodSubject fooMethodSubject =
+                  inspector.clazz(Main.class).uniqueMethodWithName("foo");
+              assertThat(fooMethodSubject, isPresent());
+              assertThat(fooMethodSubject, hasParameters(bTypeSubject, aTypeSubject));
+
+              MethodSubject libraryOverrideMethodSubject =
+                  inspector.clazz(Program.class).uniqueMethodWithName("m");
+              assertThat(libraryOverrideMethodSubject, isPresent());
+              assertThat(libraryOverrideMethodSubject, hasParameters(bTypeSubject, aTypeSubject));
+            })
+        .addRunClasspathClasses(A.class, B.class, Library.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Program", "A", "B");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      Library library = System.currentTimeMillis() > 0 ? new Program() : new Library();
+      library.m(new B(), new A());
+      foo(new A(), new B());
+    }
+
+    @NeverInline
+    static void foo(A a, B b) {
+      System.out.println(a);
+      System.out.println(b);
+    }
+  }
+
+  static class A {
+
+    @Override
+    public String toString() {
+      return "A";
+    }
+  }
+
+  static class B {
+
+    @Override
+    public String toString() {
+      return "B";
+    }
+  }
+
+  public static class Library {
+
+    public void m(B b, A a) {
+      System.out.println("Library");
+    }
+  }
+
+  static class Program extends Library {
+
+    @Override
+    public void m(B b, A a) {
+      System.out.println("Program");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/NormalizeTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithVirtualMethodCollisionTest.java
similarity index 63%
copy from src/test/java/com/android/tools/r8/optimize/proto/NormalizeTest.java
copy to src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithVirtualMethodCollisionTest.java
index c78ced4..babbf9f 100644
--- a/src/test/java/com/android/tools/r8/optimize/proto/NormalizeTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithVirtualMethodCollisionTest.java
@@ -5,16 +5,17 @@
 package com.android.tools.r8.optimize.proto;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.MethodMatchers.hasParameters;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoVerticalClassMerging;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.TypeSubject;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -22,7 +23,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class NormalizeTest extends TestBase {
+public class ProtoNormalizationWithVirtualMethodCollisionTest extends TestBase {
 
   @Parameter(0)
   public TestParameters parameters;
@@ -40,7 +41,7 @@
         .addOptionsModification(
             options -> options.testing.enableExperimentalProtoNormalization = true)
         .enableInliningAnnotations()
-        .enableNoHorizontalClassMergingAnnotations()
+        .enableNoVerticalClassMergingAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with proto changes.
         .noMinification()
         .setMinApi(parameters.getApiLevel())
@@ -53,50 +54,55 @@
               ClassSubject bClassSubject = inspector.clazz(B.class);
               assertThat(bClassSubject, isPresent());
 
-              MethodSubject fooMethodSubject =
-                  inspector.clazz(Main.class).uniqueMethodWithName("foo");
-              assertThat(fooMethodSubject, isPresent());
+              TypeSubject aTypeSubject = aClassSubject.asTypeSubject();
+              TypeSubject bTypeSubject = bClassSubject.asTypeSubject();
 
-              String expectedMethodSignature =
-                  "void "
-                      + fooMethodSubject.getFinalName()
-                      + "("
-                      + aClassSubject.getFinalName()
-                      + ", "
-                      + bClassSubject.getFinalName()
-                      + ")";
-              assertEquals(
-                  expectedMethodSignature,
-                  fooMethodSubject.getProgramMethod().getMethodSignature().toString());
+              MethodSubject fooMethodSubject = aClassSubject.uniqueMethodWithName("foo");
+              assertThat(fooMethodSubject, isPresent());
+              assertThat(fooMethodSubject, hasParameters(aTypeSubject, bTypeSubject));
+
+              // TODO(b/173398086): Consider rewriting B.foo(B, A) to B.foo(A, B, C) instead of
+              //  B.foo$1(A, B).
+              MethodSubject otherFooMethodSubject = bClassSubject.uniqueMethodWithName("foo$1");
+              assertThat(otherFooMethodSubject, isPresent());
+              assertThat(otherFooMethodSubject, hasParameters(aTypeSubject, bTypeSubject));
             })
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines("A", "B");
+        .assertSuccessWithOutputLines("A", "B", "A", "B");
   }
 
   static class Main {
 
     public static void main(String[] args) {
-      foo(new B(), new A());
-    }
-
-    @NeverInline
-    static void foo(B b, A a) {
-      System.out.println(a);
-      System.out.println(b);
+      A a = new A();
+      B b = new B();
+      a.foo(a, b);
+      b.foo(b, a);
     }
   }
 
-  @NoHorizontalClassMerging
+  @NoVerticalClassMerging
   static class A {
 
+    @NeverInline
+    public void foo(A a, B b) {
+      System.out.println(a);
+      System.out.println(b);
+    }
+
     @Override
     public String toString() {
       return "A";
     }
   }
 
-  @NoHorizontalClassMerging
-  static class B {
+  static class B extends A {
+
+    @NeverInline
+    public void foo(B b, A a) {
+      System.out.println(a);
+      System.out.println(b);
+    }
 
     @Override
     public String toString() {
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/NormalizeTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithoutSharingTest.java
similarity index 73%
rename from src/test/java/com/android/tools/r8/optimize/proto/NormalizeTest.java
rename to src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithoutSharingTest.java
index c78ced4..0088b3b 100644
--- a/src/test/java/com/android/tools/r8/optimize/proto/NormalizeTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithoutSharingTest.java
@@ -5,16 +5,16 @@
 package com.android.tools.r8.optimize.proto;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.MethodMatchers.hasParameters;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NoHorizontalClassMerging;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.TypeSubject;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -22,7 +22,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class NormalizeTest extends TestBase {
+public class ProtoNormalizationWithoutSharingTest extends TestBase {
 
   @Parameter(0)
   public TestParameters parameters;
@@ -47,27 +47,14 @@
         .compile()
         .inspect(
             inspector -> {
-              ClassSubject aClassSubject = inspector.clazz(A.class);
-              assertThat(aClassSubject, isPresent());
+              TypeSubject aTypeSubject = inspector.clazz(A.class).asTypeSubject();
+              TypeSubject bTypeSubject = inspector.clazz(B.class).asTypeSubject();
 
-              ClassSubject bClassSubject = inspector.clazz(B.class);
-              assertThat(bClassSubject, isPresent());
-
+              // Should not be normalized as there is no sharing of protos.
               MethodSubject fooMethodSubject =
                   inspector.clazz(Main.class).uniqueMethodWithName("foo");
               assertThat(fooMethodSubject, isPresent());
-
-              String expectedMethodSignature =
-                  "void "
-                      + fooMethodSubject.getFinalName()
-                      + "("
-                      + aClassSubject.getFinalName()
-                      + ", "
-                      + bClassSubject.getFinalName()
-                      + ")";
-              assertEquals(
-                  expectedMethodSignature,
-                  fooMethodSubject.getProgramMethod().getMethodSignature().toString());
+              assertThat(fooMethodSubject, hasParameters(bTypeSubject, aTypeSubject));
             })
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("A", "B");
diff --git a/src/test/java/com/android/tools/r8/resolution/access/indirectfield/IndirectFieldAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/indirectfield/IndirectFieldAccessTest.java
index ea51b57..fc7a254 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/indirectfield/IndirectFieldAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/indirectfield/IndirectFieldAccessTest.java
@@ -66,7 +66,7 @@
             Reference.field(Reference.classFromClass(B.class), "f", Reference.INT),
             appInfo.dexItemFactory());
     FieldResolutionResult resolutionResult = appInfo.resolveField(f);
-    assertTrue(resolutionResult.isSuccessfulResolution());
+    assertTrue(resolutionResult.isSingleFieldResolutionResult());
     assertEquals(OptionalBool.TRUE, resolutionResult.isAccessibleFrom(barMethod, appInfo));
   }
 
diff --git a/src/test/java/com/android/tools/r8/resolution/field/FieldResolutionWithMultipleResultsTest.java b/src/test/java/com/android/tools/r8/resolution/field/FieldResolutionWithMultipleResultsTest.java
new file mode 100644
index 0000000..7b46775
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/field/FieldResolutionWithMultipleResultsTest.java
@@ -0,0 +1,115 @@
+// 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.resolution.field;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+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.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.ClassResolutionResult;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.AndroidApp;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+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 FieldResolutionWithMultipleResultsTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  private final List<Class<?>> libraryClasses = ImmutableList.of(A.class, B.class);
+
+  @Test
+  public void testMultipleResultsWithProgramAndLibrary() throws Exception {
+    List<Class<?>> programClasses = new ArrayList<>(libraryClasses);
+    programClasses.add(C.class);
+    testMultipleResolutionsMatchesFoundClasses(
+        readClasses(programClasses, libraryClasses),
+        fieldResult -> assertTrue(fieldResult.hasProgramResult()));
+  }
+
+  @Test
+  public void testMultipleResultsWithClasspathAndLibrary() throws Exception {
+    List<Class<?>> classPath = new ArrayList<>(libraryClasses);
+    classPath.add(C.class);
+    testMultipleResolutionsMatchesFoundClasses(
+        readClasses(Collections.emptyList(), classPath, libraryClasses),
+        fieldResult -> assertTrue(fieldResult.hasClasspathResult()));
+  }
+
+  @Test
+  public void testProgramOverClasspath() throws Exception {
+    List<Class<?>> programAndClasspathClasses = new ArrayList<>(libraryClasses);
+    programAndClasspathClasses.add(C.class);
+    testMultipleResolutionsMatchesFoundClasses(
+        readClasses(programAndClasspathClasses, programAndClasspathClasses, libraryClasses),
+        fieldResult -> assertTrue(fieldResult.hasProgramResult()));
+  }
+
+  private void testMultipleResolutionsMatchesFoundClasses(
+      AndroidApp androidApp, Consumer<FieldResolutionResult> resultConsumer) throws Exception {
+    AppInfoWithClassHierarchy appInfoWithClassHierarchy =
+        computeAppInfoWithClassHierarchy(androidApp);
+    DexItemFactory factory = appInfoWithClassHierarchy.dexItemFactory();
+    DexField field =
+        factory.createField(
+            Reference.field(
+                Reference.classFromClass(C.class), "foo", Reference.primitiveFromDescriptor("I")));
+    FieldResolutionResult fieldResolutionResult = appInfoWithClassHierarchy.resolveField(field);
+    assertTrue(fieldResolutionResult.isMultiFieldResolutionResult());
+    resultConsumer.accept(fieldResolutionResult);
+    assertTrue(fieldResolutionResult.hasProgramOrClasspathResult());
+    assertFalse(fieldResolutionResult.isPossiblyFailedOrUnknownResolution());
+    Set<DexClass> resolvedHolders = new HashSet<>();
+    fieldResolutionResult.forEachFieldResolutionResult(
+        resolutionResult -> {
+          assertTrue(resolutionResult.isSingleFieldResolutionResult());
+          boolean existing =
+              resolvedHolders.add(
+                  resolutionResult.asSingleFieldResolutionResult().getResolvedHolder());
+          assertTrue(existing);
+        });
+    assertEquals(2, resolvedHolders.size());
+    ClassResolutionResult classResolutionResult =
+        appInfoWithClassHierarchy.contextIndependentDefinitionForWithResolutionResult(
+            factory.createType(Reference.classFromClass(A.class)));
+    assertTrue(classResolutionResult.hasClassResolutionResult());
+    Set<DexClass> foundClasses = new HashSet<>();
+    classResolutionResult.forEachClassResolutionResult(foundClasses::add);
+    assertEquals(foundClasses, resolvedHolders);
+  }
+
+  public static class A {
+
+    public int foo;
+  }
+
+  public static class B extends A {}
+
+  public static class C extends B {}
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java b/src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java
index 6973f6b..820be96 100644
--- a/src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java
@@ -17,7 +17,6 @@
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.naming.retrace.StackTrace;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -111,25 +110,15 @@
         .addKeepMainRule(TestClassForInlineMethod.class)
         .enableAlwaysInliningAnnotations()
         .enableInliningAnnotations()
-        .addKeepAttributes(ProguardKeepAttributes.SOURCE_FILE)
+        .addKeepAttributeSourceFile()
+        .addKeepAttributeLineNumberTable()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::checkSomething)
         .run(parameters.getRuntime(), TestClassForInlineMethod.class)
         .assertFailure()
-        // TODO(b/143607166): The stack trace has one more frame on the top than expected.
         .inspectStackTrace(
-            stackTrace -> assertThat(expectedStackTraceForInlineMethod, not(isSame(stackTrace))))
-        .inspectStackTrace(
-            stackTrace ->
-                assertThat(
-                    stackTrace,
-                    isSameExceptForFileNameAndLineNumber(
-                        createStackTraceBuilder()
-                            .addWithoutFileNameAndLineNumber(
-                                A.class, "inlineMethodWhichAccessInstanceMethod")
-                            .addWithoutFileNameAndLineNumber(TestClassForInlineMethod.class, "main")
-                            .build())));
+            stackTrace -> assertThat(expectedStackTraceForInlineMethod, isSame(stackTrace)));
   }
 
   @Test
@@ -139,25 +128,15 @@
         .addKeepMainRule(TestClassForInlineField.class)
         .enableAlwaysInliningAnnotations()
         .enableInliningAnnotations()
-        .addKeepAttributes(ProguardKeepAttributes.SOURCE_FILE)
+        .addKeepAttributeSourceFile()
+        .addKeepAttributeLineNumberTable()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::checkSomething)
         .run(parameters.getRuntime(), TestClassForInlineField.class)
         .assertFailure()
-        // TODO(b/143607166): The stack trace has one more frame on the top than expected.
         .inspectStackTrace(
-            stackTrace -> assertThat(expectedStackTraceForInlineField, not(isSame(stackTrace))))
-        .inspectStackTrace(
-            stackTrace ->
-                assertThat(
-                    stackTrace,
-                    isSameExceptForFileNameAndLineNumber(
-                        createStackTraceBuilder()
-                            .addWithoutFileNameAndLineNumber(
-                                A.class, "inlineMethodWhichAccessInstanceField")
-                            .addWithoutFileNameAndLineNumber(TestClassForInlineField.class, "main")
-                            .build())));
+            stackTrace -> assertThat(expectedStackTraceForInlineField, isSame(stackTrace)));
   }
 
   @Test
@@ -168,27 +147,15 @@
         .addKeepMainRule(TestClassForInlineStaticField.class)
         .enableAlwaysInliningAnnotations()
         .enableInliningAnnotations()
-        .addKeepAttributes(ProguardKeepAttributes.SOURCE_FILE)
+        .addKeepAttributeSourceFile()
+        .addKeepAttributeLineNumberTable()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::checkSomething)
         .run(parameters.getRuntime(), TestClassForInlineStaticField.class)
         .assertFailure()
-        // TODO(b/143607166): The stack trace has one more frame on the top than expected.
         .inspectStackTrace(
-            stackTrace ->
-                assertThat(expectedStackTraceForInlineStaticField, not(isSame(stackTrace))))
-        .inspectStackTrace(
-            stackTrace ->
-                assertThat(
-                    stackTrace,
-                    isSameExceptForFileNameAndLineNumber(
-                        createStackTraceBuilder()
-                            .addWithoutFileNameAndLineNumber(
-                                A.class, "inlineMethodWhichAccessStaticField")
-                            .addWithoutFileNameAndLineNumber(
-                                TestClassForInlineStaticField.class, "main")
-                            .build())));
+            stackTrace -> assertThat(expectedStackTraceForInlineStaticField, isSame(stackTrace)));
   }
 
   private StackTrace.Builder createStackTraceBuilder() {
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerAssertionInClinitOnlyTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerAssertionInClinitOnlyTest.java
index 314d180..f2de8f4 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerAssertionInClinitOnlyTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerAssertionInClinitOnlyTest.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.rewrite.assertions;
 
+import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers;
@@ -35,4 +36,9 @@
   List<Class<?>> getTestClasses() {
     return ImmutableList.of(AssertionsInClinit.class);
   }
+
+  @Override
+  protected void configure(R8TestBuilder<?> builder) {
+    builder.allowUnusedProguardConfigurationRules();
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerMissingClassTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerMissingClassTest.java
new file mode 100644
index 0000000..72057c3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerMissingClassTest.java
@@ -0,0 +1,97 @@
+// 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.assertions;
+
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionsSimple;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
+import java.nio.file.Path;
+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;
+
+@RunWith(Parameterized.class)
+public class AssertionConfigurationAssertionHandlerMissingClassTest extends TestBase {
+
+  private static final String EXPECTED_OUTPUT =
+      StringUtils.lines(
+          "assertionHandler: simpleAssertion",
+          "assertionHandler: multipleAssertions",
+          "assertionHandler: multipleAssertions");
+  private static Class<?> MAIN_CLASS = AssertionsSimple.class;
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
+  }
+
+  private MethodReference getAssertionHandler() {
+    try {
+      return Reference.methodFromMethod(
+          AssertionHandlers.class.getMethod("assertionHandler", Throwable.class));
+    } catch (NoSuchMethodException e) {
+    }
+    fail();
+    return null;
+  }
+
+  private Path jarWithAssertionHandler() throws Exception {
+    Path jar = temp.newFolder().toPath().resolve("assertion_handler.jar");
+    if (parameters.isDexRuntime()) {
+      testForD8()
+          .addProgramClasses(AssertionHandlers.class)
+          .setMinApi(parameters.getApiLevel())
+          .compile()
+          .writeToZip(jar);
+      return jar;
+    } else {
+      return ZipBuilder.builder(jar)
+          .addFilesRelative(
+              ToolHelper.getClassPathForTests(),
+              ToolHelper.getClassFileForTestClass(AssertionHandlers.class))
+          .build();
+    }
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(MAIN_CLASS)
+        .addKeepMainRule(MAIN_CLASS)
+        .addKeepAnnotation()
+        .addKeepRules("-keepclassmembers class * { @com.android.tools.r8.Keep *; }")
+        .setMinApi(parameters.getApiLevel())
+        .addIgnoreWarnings()
+        .addAssertionsConfiguration(
+            builder -> builder.setAssertionHandler(getAssertionHandler()).setScopeAll().build())
+        .allowDiagnosticMessages()
+        .compileWithExpectedDiagnostics(
+            diagnostics ->
+                diagnostics
+                    .assertOnlyWarnings()
+                    .inspectWarnings(
+                        diagnostic ->
+                            diagnostic
+                                .assertIsMissingDefinitionsDiagnostic()
+                                .assertIsAllMissingClasses(AssertionHandlers.class)))
+        .addRunClasspathFiles(jarWithAssertionHandler())
+        .run(parameters.getRuntime(), MAIN_CLASS)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerNoAssertionsAfterOptimizingTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerNoAssertionsAfterOptimizingTest.java
new file mode 100644
index 0000000..36f01a8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerNoAssertionsAfterOptimizingTest.java
@@ -0,0 +1,49 @@
+// 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.assertions;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.NoAssertionsAfterOptimization;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class AssertionConfigurationAssertionHandlerNoAssertionsAfterOptimizingTest
+    extends AssertionConfigurationAssertionHandlerTestBase {
+
+  private static final String EXPECTED_OUTPUT = StringUtils.lines("Hello, world!");
+
+  @Override
+  String getExpectedOutput() {
+    return EXPECTED_OUTPUT;
+  }
+
+  @Override
+  MethodReference getAssertionHandler() throws Exception {
+    return Reference.methodFromMethod(
+        AssertionHandlers.class.getMethod("assertionHandler", Throwable.class));
+  }
+
+  @Override
+  protected void inspect(CodeInspector inspector) {
+    assert getAssertionHandlerClasses().size() == 1;
+    assertThat(inspector.clazz(getAssertionHandlerClasses().get(0)), not(isPresent()));
+  }
+
+  @Override
+  List<Class<?>> getTestClasses() {
+    return ImmutableList.of(NoAssertionsAfterOptimization.class);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerTestBase.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerTestBase.java
index fa4de84..0583b32 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerTestBase.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerTestBase.java
@@ -6,10 +6,13 @@
 
 import static org.junit.Assume.assumeTrue;
 
+import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -34,6 +37,14 @@
 
   abstract List<Class<?>> getTestClasses();
 
+  protected List<Class<?>> getAssertionHandlerClasses() {
+    return ImmutableList.of(AssertionHandlers.class);
+  }
+
+  protected void configure(R8TestBuilder<?> builder) {}
+
+  protected void inspect(CodeInspector inspector) {}
+
   private MethodReference getAssertionHandlerIgnoreException() {
     try {
       return getAssertionHandler();
@@ -46,7 +57,7 @@
   public void testD8() throws Exception {
     assumeTrue(parameters.isDexRuntime());
     testForD8(parameters.getBackend())
-        .addProgramClasses(AssertionHandlers.class)
+        .addProgramClasses(getAssertionHandlerClasses())
         .addProgramClasses(getTestClasses())
         .setMinApi(parameters.getApiLevel())
         .addOptionsModification(o -> o.testing.forceIRForCfToCfDesugar = true)
@@ -63,11 +74,11 @@
   @Test
   public void testR8() throws Exception {
     testForR8(parameters.getBackend())
-        .addProgramClasses(AssertionHandlers.class)
+        .addProgramClasses(getAssertionHandlerClasses())
         .addProgramClasses(getTestClasses())
         .addKeepMainRule(getTestClasses().get(0))
         .addKeepAnnotation()
-        .addKeepRules("-keep class * { @com.android.tools.r8.Keep *; }")
+        .addKeepRules("-keepclassmembers class * { @com.android.tools.r8.Keep *; }")
         .setMinApi(parameters.getApiLevel())
         .addAssertionsConfiguration(
             builder ->
@@ -75,7 +86,9 @@
                     .setAssertionHandler(getAssertionHandlerIgnoreException())
                     .setScopeAll()
                     .build())
+        .apply(this::configure)
         .run(parameters.getRuntime(), getTestClasses().get(0))
+        .inspect(this::inspect)
         .assertSuccessWithOutput(getExpectedOutput());
   }
 }
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/NoAssertionsAfterOptimization.java b/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/NoAssertionsAfterOptimization.java
new file mode 100644
index 0000000..994c83a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/NoAssertionsAfterOptimization.java
@@ -0,0 +1,27 @@
+// 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.assertions.assertionhandler;
+
+import com.android.tools.r8.Keep;
+
+public class NoAssertionsAfterOptimization {
+
+  private static boolean alwaysFalse() {
+    return false;
+  }
+
+  @Keep
+  private static void assertionUnderAlwaysFalseCondition() {
+    if (alwaysFalse()) {
+      assert false : "Fifth assertion";
+    }
+  }
+
+  public static void main(String[] args) {
+    System.out.print("Hello, ");
+    assertionUnderAlwaysFalseCondition();
+    System.out.println("world!");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
index e110f57..f22ae69 100644
--- a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
@@ -78,6 +78,7 @@
                 return StringUtils.joinLines("Hello!", "Goodbye!", "");
 
               case V7_0_0:
+              case V13_MASTER:
                 return StringUtils.joinLines(
                     "Hello!",
                     "Unexpected outcome of checkcast",
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitInlineForStaticGetterInSuperTypeTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitInlineForStaticGetterInSuperTypeTest.java
index 7a4c15e..ae433d9 100644
--- a/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitInlineForStaticGetterInSuperTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitInlineForStaticGetterInSuperTypeTest.java
@@ -18,7 +18,6 @@
 public class ClassInitInlineForStaticGetterInSuperTypeTest extends TestBase {
 
   private static final String EXPECTED = "Hello World";
-  private static final String R8_EXPECTED = "Goodbye World";
 
   @Parameter() public TestParameters parameters;
 
@@ -44,8 +43,7 @@
         .addKeepRules("-keep class " + typeName(B.class) + " { <fields>; }")
         .enableInliningAnnotations()
         .run(parameters.getRuntime(), Main.class)
-        // TODO(b/215477768): Should be Hello World
-        .assertSuccessWithOutputLines(R8_EXPECTED);
+        .assertSuccessWithOutputLines(EXPECTED);
   }
 
   public static class A {
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
index 8cc71c8..804c5f8 100644
--- a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -76,7 +76,8 @@
   public static boolean isExternalSynthetic(ClassReference reference) {
     for (SyntheticKind kind : SyntheticKind.values()) {
       if (kind == SyntheticKind.RECORD_TAG
-          || kind == SyntheticKind.EMULATED_INTERFACE_MARKER_CLASS) {
+          || kind == SyntheticKind.EMULATED_INTERFACE_MARKER_CLASS
+          || kind == SyntheticKind.RETARGET_STUB) {
         continue;
       }
       if (kind.isFixedSuffixSynthetic) {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
index 4ffcdb4..9be6468 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
@@ -77,6 +77,16 @@
   }
 
   @Override
+  public TypeSubject getParameter(int index) {
+    throw new Unreachable("Cannot get the parameter for an absent method");
+  }
+
+  @Override
+  public List<TypeSubject> getParameters() {
+    throw new Unreachable("Cannot get the parameters for an absent method");
+  }
+
+  @Override
   public ProgramMethod getProgramMethod() {
     return null;
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index 04f0d52..30b3fb0 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -171,6 +171,10 @@
     return null;
   }
 
+  public TypeSubject asTypeSubject() {
+    return null;
+  }
+
   @Override
   public abstract ClassAccessFlags getAccessFlags();
 
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index 0d62343..cdc420e 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -289,6 +289,11 @@
   }
 
   @Override
+  public TypeSubject asTypeSubject() {
+    return new TypeSubject(codeInspector, dexClass.getType());
+  }
+
+  @Override
   public boolean isAbstract() {
     return dexClass.accessFlags.isAbstract();
   }
@@ -504,10 +509,6 @@
     return dexClass.toSourceString();
   }
 
-  public TypeSubject asTypeSubject() {
-    return new TypeSubject(codeInspector, getDexProgramClass().type);
-  }
-
   @Override
   public KmClassSubject getKmClass() {
     AnnotationSubject annotationSubject = annotation(METADATA_TYPE);
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index a41ab17..b99b090 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -124,6 +124,18 @@
   }
 
   @Override
+  public TypeSubject getParameter(int index) {
+    return new TypeSubject(codeInspector, getMethod().getParameter(index));
+  }
+
+  @Override
+  public List<TypeSubject> getParameters() {
+    return getMethod().getParameters().stream()
+        .map(parameter -> new TypeSubject(codeInspector, parameter))
+        .collect(Collectors.toList());
+  }
+
+  @Override
   public ProgramMethod getProgramMethod() {
     return new ProgramMethod(clazz.getDexProgramClass(), getMethod());
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodMatchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodMatchers.java
new file mode 100644
index 0000000..8d7e78b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodMatchers.java
@@ -0,0 +1,54 @@
+// 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.utils.codeinspector;
+
+import static junit.framework.TestCase.assertEquals;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+public class MethodMatchers {
+
+  public static Matcher<MethodSubject> hasParameters(TypeSubject... expectedParameters) {
+    return hasParameters(Arrays.asList(expectedParameters));
+  }
+
+  public static Matcher<MethodSubject> hasParameters(List<TypeSubject> expectedParameters) {
+    return new TypeSafeMatcher<MethodSubject>() {
+      @Override
+      protected boolean matchesSafely(MethodSubject methodSubject) {
+        if (!methodSubject.isPresent()) {
+          return false;
+        }
+        if (methodSubject.getParameters().size() != expectedParameters.size()) {
+          return false;
+        }
+        for (int i = 0; i < expectedParameters.size(); i++) {
+          TypeSubject actualParameter = methodSubject.getParameter(i);
+          TypeSubject expectedParameter = expectedParameters.get(i);
+          assertEquals(expectedParameter, actualParameter);
+        }
+        return true;
+      }
+
+      @Override
+      public void describeTo(Description description) {
+        description.appendText(
+            "has parameters ("
+                + StringUtils.join(", ", expectedParameters, TypeSubject::getTypeName)
+                + ")");
+      }
+
+      @Override
+      public void describeMismatchSafely(final MethodSubject subject, Description description) {
+        description.appendText("method did not");
+      }
+    };
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
index 594b17a..3b7fe05 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.google.common.collect.Streams;
 import java.util.Iterator;
+import java.util.List;
 import java.util.function.Predicate;
 import java.util.stream.Stream;
 
@@ -51,6 +52,10 @@
 
   public abstract DexEncodedMethod getMethod();
 
+  public abstract TypeSubject getParameter(int index);
+
+  public abstract List<TypeSubject> getParameters();
+
   public abstract ProgramMethod getProgramMethod();
 
   public Iterator<InstructionSubject> iterateInstructions() {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/TypeSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/TypeSubject.java
index 357c777..5761b99 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/TypeSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/TypeSubject.java
@@ -1,4 +1,4 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// 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.
 
@@ -17,6 +17,10 @@
     this.dexType = dexType;
   }
 
+  public String getTypeName() {
+    return dexType.getTypeName();
+  }
+
   @Override
   public boolean isPresent() {
     return true;
diff --git a/third_party/android_jar/lib-v33.tar.gz.sha1 b/third_party/android_jar/lib-v33.tar.gz.sha1
new file mode 100644
index 0000000..2e88eb6
--- /dev/null
+++ b/third_party/android_jar/lib-v33.tar.gz.sha1
@@ -0,0 +1 @@
+ecd236f896f9a19eeb7d46eb983cbaf94fd31d76
\ No newline at end of file
diff --git a/third_party/openjdk/jdk-18/linux.tar.gz.sha1 b/third_party/openjdk/jdk-18/linux.tar.gz.sha1
new file mode 100644
index 0000000..3c8efd9
--- /dev/null
+++ b/third_party/openjdk/jdk-18/linux.tar.gz.sha1
@@ -0,0 +1 @@
+b545df5778c9025fe105dddcd8b3830d8731137b
\ No newline at end of file
diff --git a/third_party/openjdk/jdk-18/osx.tar.gz.sha1 b/third_party/openjdk/jdk-18/osx.tar.gz.sha1
new file mode 100644
index 0000000..9699f89
--- /dev/null
+++ b/third_party/openjdk/jdk-18/osx.tar.gz.sha1
@@ -0,0 +1 @@
+527883f93a157ae249f83f461b0368d87c8fe6a1
\ No newline at end of file
diff --git a/third_party/openjdk/jdk-18/windows.tar.gz.sha1 b/third_party/openjdk/jdk-18/windows.tar.gz.sha1
new file mode 100644
index 0000000..854167c
--- /dev/null
+++ b/third_party/openjdk/jdk-18/windows.tar.gz.sha1
@@ -0,0 +1 @@
+b1e3cb3be67dd78423a0a96c0214ac97b5edd112
\ No newline at end of file
diff --git a/tools/linux/README.art-versions b/tools/linux/README.art-versions
index ff57695..fee679c 100644
--- a/tools/linux/README.art-versions
+++ b/tools/linux/README.art-versions
@@ -42,6 +42,28 @@
   <continue with repo sync as above>
 
 
+art-13-master (Android T)
+-------------------------
+Build from master commit e208b04cc2efaf707390d7acbe8f978142701d72.
+
+repo sync -cq -j24
+source build/envsetup.sh
+lunch aosp_redfin-userdebug
+m -j48
+m -j48 build-art
+m -j48 test-art-host
+
+Collected into tools/linux/host/art-13-master. The "host" path element is checked
+by the script for running Art.
+
+  cd <r8 checkout>
+  scripts/update-host-art.sh \
+     --android-checkout <...>/android/master \
+     --art-dir host/art-13-master \
+     --android-product redfin
+
+(cd tools/linux/host; upload_to_google_storage.py -a --bucket r8-deps art-13-master)
+
 art-12.0.0 (Android S)
 ---------------------
 Build from branch sc-beta4-release.
diff --git a/tools/linux/host/art-13-master.tar.gz.sha1 b/tools/linux/host/art-13-master.tar.gz.sha1
new file mode 100644
index 0000000..8b16edd
--- /dev/null
+++ b/tools/linux/host/art-13-master.tar.gz.sha1
@@ -0,0 +1 @@
+a2cf2b34b8712adb5c25a18bc6135946cfb1a047
\ No newline at end of file
diff --git a/tools/test.py b/tools/test.py
index 9d5e99f..e822efe 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -24,6 +24,7 @@
 
 ALL_ART_VMS = [
     "default",
+    "13.0.0",
     "12.0.0",
     "10.0.0",
     "9.0.0",