Merge commit 'b6c3b24b' into dev-release

Change-Id: I6edc62a72fe3ade68162eb482185a22b5acd8044
diff --git a/BUILD.md b/BUILD.md
new file mode 100644
index 0000000..a2271c0
--- /dev/null
+++ b/BUILD.md
@@ -0,0 +1,214 @@
+# R8 Build System Architecture & Overview
+
+This document provides an overview of the build architecture.
+
+---
+
+## Build Design
+
+The build setup follows these core principles:
+
+- **Hermetic:** Builds only download against Google resources (located in
+  [`third_party/`](./third_party)).
+- **Layered Tooling:**
+  - **Python Drivers** ([`tools/gradle.py`](./tools/gradle.py),
+    [`tools/test.py`](./tools/test.py), etc.) ensure pre-builts (JDKs,
+    Gradle binaries, local Maven caches) from Google Cloud Storage (GCS)
+    before invoking Gradle.
+  - **Gradle Project** (located under [`d8_r8/`](./d8_r8)) coordinates the
+    actual build.
+- **Bootstrapping:** The official compiler jar is compiled with R8.
+- **Testing what you release:** Tests run against the official bootstrapped and
+  optimized [`r8lib.jar`](./build/libs/r8lib.jar) release artifact rather than a
+  debug build.
+
+---
+
+## The Build Process
+
+```
++---------------------------------------------------------------------------------------+
+|                                   R8 COMPILER SUITE                                   |
++---------------------------------------------------------------------------------------+
+
+  [main] ───────────────────────────> threading-module-blocking.jar ───────┐
+  [main] ───────────────────────────> threading-module-single-threaded.jar ┼──> deps.jar
+  [third_party], [resourceshrinker] ───────────────────────────────────────┘       │
+                                                                                   │
+  [keepradius], [libanalyzer] ────────────────────────────────────────> proto.jar  │
+                                                                           │       │
+  [main], [keepanno], [resourceshrinker],                                  │       │
+  [keepradius], [libanalyzer], [assistant] ──┬──> r8-src.jar               │       │
+                                             │                             │       │
+                                             └──> r8-full-exclude-deps.jar │       │
+                     r8lib-exclude-deps.jar <──────┘    │                  │       │
+                                      ▲                 ▼                  │       │
+                                      │               r8.jar <─────────────┴───────┘
+                                      │              /      \
+                                      │             ▼        ▼
+  [tests] ───────────────> test-references ─--> r8lib.jar  processkeepruleslib.jar
+                                                    │
+                                                    ▼
+                                                r8lib.zip
+
++---------------------------------------------------------------------------------------+
+|                                KEEP ANNOTATIONS TOOLS                                 |
++---------------------------------------------------------------------------------------+
+
+  [keepanno] ──┬──> keepanno-annotations.jar
+               ├──> keepanno-annotations-legacy.jar
+               ├──> keepanno-annotations-androidx.jar
+               └──> keepanno-tools.jar ──> keepanno-toolslib.jar
+
++---------------------------------------------------------------------------------------+
+|                                 DESUGAR CONFIGURATION                                 |
++---------------------------------------------------------------------------------------+
+
+  [library_desugar] ──> desugar_jdk_libs_configuration.zip
+```
+
+### R8 Compiler Suite
+
+- [`r8lib.zip`](./build/libs/r8lib.zip):
+  - Released: To GMaven for distribution to AGP and other build systems.
+  - What: The Maven repository archive for the compiler.
+  - How: Package [`r8lib.jar`](./build/libs/r8lib.jar) with POM and checksum
+    files in a Maven repository layout.
+- [`r8lib.jar`](./build/libs/r8lib.jar):
+  - Released: The primary production compiler JAR.
+  - What: The relocated, standalone, optimized compiler JAR.
+  - How: [`r8.jar`](./build/libs/r8.jar) is compiled and optimized with R8 with
+    respect to declared keep rules and dynamic keep rules from `test-references`.
+- [`r8.jar`](./build/libs/r8.jar):
+  - Released: The non-optimized production compiler JAR.
+  - What: The relocated, standalone compiler JAR.
+  - How: [`r8-full-exclude-deps.jar`](./build/libs/r8-full-exclude-deps.jar)
+    and [`proto.jar`](./build/libs/proto.jar) are bundled together, along with
+    dependencies in [`deps.jar`](./build/libs/deps.jar) relocated under
+    `com.android.tools.r8.*`.
+- [`r8lib-exclude-deps.jar`](./build/libs/r8lib-exclude-deps.jar):
+  - Released: Published for build systems (such as internal Google repo)
+    that manage and provide their own dependencies on the classpath.
+  - What: The optimized compiler JAR without dependencies (note the special
+    proto and threading modules (part of [`deps.jar`](./build/libs/deps.jar))
+    that are not generic dependencies).
+  - How: [`r8-full-exclude-deps.jar`](./build/libs/r8-full-exclude-deps.jar)
+    is compiled and optimized with R8 with respect to declared keep rules and
+    dynamic keep rules from `test-references`, using
+    [`deps.jar`](./build/libs/deps.jar) and
+    [`proto.jar`](./build/libs/proto.jar) as classpath.
+- `test-references`:
+  - What: Dynamically generated keep rules extracted from test references.
+  - How: Trace compiler references from the test suite to preserve compiler
+    internals accessed during testing.
+- [`r8-full-exclude-deps.jar`](./build/libs/r8-full-exclude-deps.jar):
+  - Released: Published as an unoptimized compiler JAR without third-party
+    dependencies for custom downstream packaging.
+  - What: The compiler JAR without dependencies.
+  - How: Compile classes from
+    [`main`](./d8_r8/main),
+    [`keepanno`](./d8_r8/keepanno),
+    [`resourceshrinker`](./d8_r8/resourceshrinker),
+    [`keepradius`](./d8_r8/keepradius),
+    [`libanalyzer`](./d8_r8/libanalyzer),
+    and [`assistant`](./d8_r8/assistant).
+- [`r8-src.jar`](./build/libs/r8-src.jar):
+  - Released: Published to provide source code for IDE navigation, debugging,
+    and open-source compliance.
+  - What: The compiler sources JAR.
+  - How: Package sources from
+    [`main`](./d8_r8/main),
+    [`keepanno`](./d8_r8/keepanno),
+    [`resourceshrinker`](./d8_r8/resourceshrinker),
+    [`keepradius`](./d8_r8/keepradius),
+    [`libanalyzer`](./d8_r8/libanalyzer),
+    and [`assistant`](./d8_r8/assistant).
+- [`deps.jar`](./build/libs/deps.jar):
+  - What: The JAR containing third-party dependencies, resource shrinker
+    dependencies, threading modules, and consolidated licensing.
+  - How: Runtime dependencies from [`third_party/`](./third_party),
+    resource shrinker dependencies from
+    [`resourceshrinker`](./d8_r8/resourceshrinker),
+    [`threading-module-blocking.jar`](./build/libs/threading-module-blocking.jar),
+    [`threading-module-single-threaded.jar`](./build/libs/threading-module-single-threaded.jar),
+    and the consolidated `LICENSE` file are merged together.
+- [`proto.jar`](./build/libs/proto.jar):
+  - What: The JAR containing compiled protobuf classes used for keep rule
+    radius and library analysis data models. These are only needed for optional
+    analysis features. Users without this dependency must compile the published
+    `.proto` schemas directly using their own toolchains.
+  - How: Protobuf classes from [`keepradius`](./d8_r8/keepradius) and
+    [`libanalyzer`](./d8_r8/libanalyzer) are bundled together.
+- [`threading-module-blocking.jar`](./build/libs/threading-module-blocking.jar):
+  - Released: Published as an optional runtime module providing multi-threaded
+    execution for modular classpath environments.
+  - What: The multi-threaded execution provider. It is packaged separately so
+    the concurrency model can be controlled via classpath. This is useful to
+    have a compiler without code even mentioning blocking code (for aggressive
+    code checkers).
+  - How: Package blocking provider classes from [`main`](./d8_r8/main).
+- [`threading-module-single-threaded.jar`](./build/libs/threading-module-single-threaded.jar):
+  - Released: Published as an optional runtime module providing single-threaded
+    execution for environments that prohibit blocking code.
+  - What: The single-threaded execution provider. (see
+    [`threading-module-blocking.jar`](./build/libs/threading-module-blocking.jar))
+  - How: Package single-threaded provider classes from [`main`](./d8_r8/main).
+- [`processkeepruleslib.jar`](./build/libs/processkeepruleslib.jar):
+  - Released: Published as a standalone optimized tool for parsing, evaluating,
+    and filtering ProGuard keep rules.
+  - What: The standalone, optimized tool JAR used to process and filter ProGuard
+    keep rules.
+  - How: [`r8.jar`](./build/libs/r8.jar) is compiled and optimized with R8 using
+    `src/main/keep_processkeeprules.txt`.
+
+### Keep Annotations Tools
+
+- [`keepanno-toolslib.jar`](./build/libs/keepanno-toolslib.jar):
+  - Released: Published as a standalone, optimized library for processing keep
+    annotations in build pipelines.
+  - What: The relocated, standalone, optimized keep-annotation tools JAR.
+  - How: [`keepanno-tools.jar`](./build/libs/keepanno-tools.jar) is compiled and
+    optimized with R8 with respect to keep rules, using unrelocated ASM on the
+    classpath.
+- [`keepanno-tools.jar`](./build/libs/keepanno-tools.jar):
+  - Released: Published as an unminified standalone JAR with relocated
+    dependencies for keep-annotation processing and debugging.
+  - What: The standalone JAR containing tools for processing keep annotations
+    with non-ASM dependencies relocated under
+    `com.android.tools.r8.keepanno.*`. ASM is kept external as it is used as the
+    public API interface.
+  - How: Compiled classes from [`keepanno`](./d8_r8/keepanno) and its non-ASM
+    dependencies are bundled together, with dependencies relocated under
+    `com.android.tools.r8.keepanno.*` (leaving ASM external for public API
+    integration).
+- [`keepanno-annotations.jar`](./build/libs/keepanno-annotations.jar):
+  - Released: Published as the compile-time annotations library for source
+    code declaring keep annotations in
+    `com.android.tools.r8.keepanno.annotations.*`.
+  - What: The annotation library containing keep-annotation definitions
+    (`com.android.tools.r8.keepanno.annotations.*`).
+  - How: Package compiled annotation classes from [`keepanno`](./d8_r8/keepanno).
+- [`keepanno-annotations-legacy.jar`](./build/libs/keepanno-annotations-legacy.jar):
+  - Released: Published for backward compatibility with projects using legacy
+    keep-annotation package namespaces.
+  - What: The legacy package variant of the keep-annotations library.
+  - How: Package compiled legacy annotation classes from
+    [`keepanno`](./d8_r8/keepanno).
+- [`keepanno-annotations-androidx.jar`](./build/libs/keepanno-annotations-androidx.jar):
+  - Released: Published for AndroidX projects using the
+    `androidx.annotation.keep.*` package namespace.
+  - What: The AndroidX package variant (`androidx.annotation.keep.*`) of the
+    keep-annotations library.
+  - How: Package compiled AndroidX keep-annotation classes from
+    [`keepanno`](./d8_r8/keepanno).
+
+### Desugar Configuration
+
+- [`desugar_jdk_libs_configuration.zip`](./build/libs/desugar_jdk_libs_configuration.zip):
+  - Released: Published to GMaven to configure Java standard library desugaring
+    in D8 and R8 (along with JDK 11, minimal, and NIO variants).
+  - What: The Maven repository archive containing the configuration JAR and POM.
+  - How: Package the desugar specification JSON, generated lint metadata, and
+    compiled conversion classes from
+    [`library_desugar`](./d8_r8/library_desugar) with POM and checksum files in
+    a Maven repository layout.
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/CreateArtTestsTask.kt b/d8_r8/commonBuildSrc/src/main/kotlin/CreateArtTestsTask.kt
new file mode 100644
index 0000000..e6a57ca
--- /dev/null
+++ b/d8_r8/commonBuildSrc/src/main/kotlin/CreateArtTestsTask.kt
@@ -0,0 +1,90 @@
+// Copyright (c) 2026, 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.
+
+import java.io.File
+import kotlin.io.path.listDirectoryEntries
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.tasks.CacheableTask
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.PathSensitive
+import org.gradle.api.tasks.PathSensitivity
+import org.gradle.api.tasks.TaskAction
+
+@CacheableTask
+public abstract class CreateArtTestsTask : DefaultTask() {
+  @get:[InputFiles PathSensitive(PathSensitivity.RELATIVE)]
+  public abstract val artTestCaseDirectory: DirectoryProperty
+
+  @get:OutputDirectory public abstract val outputDir: DirectoryProperty
+
+  @TaskAction
+  public fun createTests() {
+    val outputDirectory = outputDir.get().asFile
+    outputDirectory.deleteRecursively()
+    val artTestCaseSubdirectory = File(artTestCaseDirectory.get().asFile, "dx")
+    check(artTestCaseSubdirectory.exists())
+    val subDirectories = artTestCaseSubdirectory.toPath().listDirectoryEntries()
+    val testCases = subDirectories.map { it.fileName.toString() }
+    for ((tool, toolEnum) in
+      listOf("r8" to "R8", "d8" to "R8_AFTER_D8", "r8cf" to "D8_AFTER_R8CF")) {
+      val packageDirectory = File(outputDirectory, "com/android/tools/r8/art/none/$tool")
+      packageDirectory.mkdirs()
+      for (testCase in testCases) {
+        val className = "Art${testCase.replace("-", "_")}Test"
+        val testRuntimes =
+          if (testCase == "800-smali")
+            "withDexRuntimesEndingAtExcluding(\n          com.android.tools.r8.ToolHelper.DexVm.Version.V17_0_0)"
+          else "withDexRuntimes()"
+        File(packageDirectory, "$className.java")
+          .writeText(
+            """// Copyright (c) 2016, 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.art.none.$tool;
+
+import static com.android.tools.r8.R8RunArtTestsTest.DexTool.NONE;
+
+import com.android.tools.r8.R8RunArtTestsTest;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * Auto-generated test for the art $testCase test using the none toolchain.
+ *
+ * DO NOT EDIT THIS FILE. EDIT THE HERE DOCUMENT TEMPLATE IN d8_r8/commonBuildSrc/src/main/kotlin/CreateArtTestsTask.kt INSTEAD!
+ */
+@RunWith(Parameterized.class)
+public class $className extends R8RunArtTestsTest {
+
+    @Parameters(name = "{0}")
+    public static TestParametersCollection data() {
+      return TestBase.getTestParameters().$testRuntimes.build();
+    }
+
+    private final TestParameters parameters;
+
+    public $className(TestParameters parameters) {
+      super("$testCase", NONE);
+      this.parameters = parameters;
+    }
+
+    @Test
+    public void test() throws Throwable {
+      runArtTest(parameters.getRuntime().asDex().getVm(), CompilerUnderTest.$toolEnum);
+    }
+}
+"""
+          )
+      }
+    }
+  }
+}
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/CreateR8LibraryTask.kt b/d8_r8/commonBuildSrc/src/main/kotlin/CreateR8LibraryTask.kt
index e9c96d3..ebf0c68 100644
--- a/d8_r8/commonBuildSrc/src/main/kotlin/CreateR8LibraryTask.kt
+++ b/d8_r8/commonBuildSrc/src/main/kotlin/CreateR8LibraryTask.kt
@@ -135,14 +135,16 @@
       }
       args = myArgs
     }
-    if (!enableKeepAnnotations.get()) { // delete api database if we do not enable keep annotations
+    if (!enableKeepAnnotations.get()) { // delete api resources if we do not enable keep annotations
       // The URI must be prefixed with "jar:" to tell the FileSystem provider to treat it as a
       // ZIP/JAR
       val uri = create("jar:${outputJarFile.toPath().toUri()}")
       val env = mapOf("create" to "false")
       newFileSystem(uri, env).use { zipfs ->
-        val pathInZip = zipfs.getPath("resources/api_database.ser")
-        deleteIfExists(pathInZip)
+        deleteIfExists(zipfs.getPath("resources/api_database.ser"))
+        deleteIfExists(zipfs.getPath("resources/missing.api.txt"))
+        deleteIfExists(zipfs.getPath("resources/hidden.api.txt"))
+        deleteIfExists(zipfs.getPath("resources/hidden.jar.txt"))
       }
     }
     if (replaceInOutputJar.isPresent) {
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
index c6333f0..cb899e3 100644
--- a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
+++ b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
@@ -10,7 +10,6 @@
 import kotlin.reflect.full.declaredMemberProperties
 import org.gradle.api.Plugin
 import org.gradle.api.Project
-import org.gradle.api.Task
 import org.gradle.api.file.ConfigurableFileCollection
 import org.gradle.api.file.Directory
 import org.gradle.api.file.RegularFile
@@ -135,12 +134,6 @@
   return files(project.getRoot().resolve(thirdPartyDependency.path).resolveAll(*paths))
 }
 
-public fun Task.dependOnPythonScripts() {
-  // There is no easy way to track transitive python dependencies, so add all python files.
-  val toolsDir = project.fileTree(project.getRoot().resolve("tools")).exclude("linux/**")
-  inputs.files(toolsDir.include("**/*.py"))
-}
-
 public fun File.resolveAll(vararg xs: String): File {
   var that = this
   for (x in xs) {
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/R8ConventionPlugin.kt b/d8_r8/commonBuildSrc/src/main/kotlin/R8ConventionPlugin.kt
index 96d4131..41c150c 100644
--- a/d8_r8/commonBuildSrc/src/main/kotlin/R8ConventionPlugin.kt
+++ b/d8_r8/commonBuildSrc/src/main/kotlin/R8ConventionPlugin.kt
@@ -27,7 +27,7 @@
         is JavaPlugin -> {
           target.extensions.getByType<JavaPluginExtension>().apply {
             // Compile using newer compiler for performance and other improvements
-            toolchain { languageVersion.set(JavaLanguageVersion.of(21)) }
+            toolchain { languageVersion.set(JavaLanguageVersion.of(25)) }
             withSourcesJar()
           }
           target.tasks.withType<JavaCompile>().configureEach {
diff --git a/d8_r8/main/build.gradle.kts b/d8_r8/main/build.gradle.kts
index 4550de3..21ca265 100644
--- a/d8_r8/main/build.gradle.kts
+++ b/d8_r8/main/build.gradle.kts
@@ -174,7 +174,10 @@
     val srcDir = getRoot().resolveAll("src", "main", "java")
 
     main {
-      resources.srcDirs(getRoot().resolveAll("third_party", "api_database", "api_database"))
+      resources.srcDirs(
+        getRoot().resolveAll("third_party", "api_database", "api_database"),
+        getRoot().resolveAll("src", "main", "resources"),
+      )
       java {
         srcDir(srcDir)
         if (turboState != null && !turboState.paths.isEmpty()) {
diff --git a/d8_r8/test_modules/tests_java_8/build.gradle.kts b/d8_r8/test_modules/tests_java_8/build.gradle.kts
index 53f6fde..7239241 100644
--- a/d8_r8/test_modules/tests_java_8/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_8/build.gradle.kts
@@ -17,13 +17,7 @@
   configurations.resolvable("partialTestClassesConfig") { extendsFrom(partialTestClassesScope) }
 
 java {
-  sourceSets.test.configure {
-    java {
-      srcDir(root.resolveAll("src", "test", "java"))
-      // Generated art tests
-      srcDir(root.resolveAll("build", "generated", "test", "java"))
-    }
-  }
+  sourceSets.test.configure { java { srcDir(root.resolveAll("src", "test", "java")) } }
   // We are using a new JDK to compile to an older language version, as we don't have JDK-8 for
   // Windows in our repo.
   sourceCompatibility = JavaVersion.VERSION_1_8
@@ -102,34 +96,20 @@
 }
 
 tasks {
-  getByName<Delete>("clean") {
-    // TODO(b/327315907): Don't generating into the root build dir.
-    delete.add(
-      getRoot()
-        .resolveAll("build", "generated", "test", "java", "com", "android", "tools", "r8", "art")
-    )
-  }
-
   val createArtTests =
-    register<Exec>("createArtTests") {
-      dependsOn(sharedDepsConfig)
-      dependOnPythonScripts()
-      // TODO(b/327315907): Don't generating into the root build dir.
-      val outputDir =
-        getRoot()
-          .resolveAll("build", "generated", "test", "java", "com", "android", "tools", "r8", "art")
-      val createArtTestsScript = getRoot().resolveAll("tools", "create_art_tests.py")
-      inputs.dir(getRoot().resolveAll("tests", "2017-10-04"))
-      outputs.dir(outputDir)
-      workingDir(getRoot())
-      commandLine("python3", createArtTestsScript)
+    register<CreateArtTestsTask>("createArtTests") {
+      artTestCaseDirectory =
+        project(":third_party").tasks.named<DownloadDependency>("download_art-tests").flatMap {
+          it.outputDir
+        }
+      outputDir = layout.buildDirectory.dir("generated/test/java")
     }
+  java.sourceSets.test.configure { java.srcDir(createArtTests) }
   "compileTestJava" {
     dependsOn(sharedDepsConfig)
     dependsOn(":testbase:compileJava")
   }
   withType<JavaCompile> {
-    dependsOn(createArtTests)
     dependsOn(sharedDepsConfig)
     dependsOn(":testbase:compileJava")
   }
@@ -160,6 +140,13 @@
       include("$r8/desugaring/interfacemethods/methodparameters/I.java")
     }
   }
+
+  register<JavaExec>("generateApiAmendments") {
+    dependsOn(partialTestClassesConfig)
+    mainClass.set("com.android.tools.r8.androidapi.GenerateApiAmendments")
+    classpath = sourceSets.test.get().runtimeClasspath + partialTestClassesConfig
+    // The output is deliberately not Gradle tracked since its a circular dependency.
+  }
 }
 
 val testJar by configurations.consumable("testJar")
diff --git a/infra/config/global/generated/commit-queue.cfg b/infra/config/global/generated/commit-queue.cfg
index 19b5ed9..b03dadb 100644
--- a/infra/config/global/generated/commit-queue.cfg
+++ b/infra/config/global/generated/commit-queue.cfg
@@ -31,4 +31,31 @@
       }
     }
   }
+  post_actions {
+    name: "presubmit-verification-success"
+    conditions {
+      mode: "DRY_RUN"
+      statuses: SUCCEEDED
+    }
+    vote_gerrit_labels {
+      votes {
+        name: "Presubmit-Verified"
+        value: 1
+      }
+    }
+  }
+  post_actions {
+    name: "presubmit-verification-failure"
+    conditions {
+      mode: "DRY_RUN"
+      statuses: FAILED
+      statuses: CANCELLED
+    }
+    vote_gerrit_labels {
+      votes {
+        name: "Presubmit-Verified"
+        value: -1
+      }
+    }
+  }
 }
diff --git a/infra/config/global/generated/project.cfg b/infra/config/global/generated/project.cfg
index 70ba83c..63e9dd9 100644
--- a/infra/config/global/generated/project.cfg
+++ b/infra/config/global/generated/project.cfg
@@ -7,7 +7,7 @@
 name: "r8"
 access: "group:all"
 lucicfg {
-  version: "1.46.3"
+  version: "1.46.6"
   package_dir: ".."
   config_dir: "generated"
   entry_point: "main.star"
diff --git a/infra/config/global/main.star b/infra/config/global/main.star
index b69d7ec..5098efd 100755
--- a/infra/config/global/main.star
+++ b/infra/config/global/main.star
@@ -190,13 +190,40 @@
     name = "main-cq",
     watch = cq.refset(
         repo = "https://r8.googlesource.com/r8",
-        refs = ["refs/heads/.+"]
+        refs = ["refs/heads/.+"],
     ),
     retry_config = cq.RETRY_TRANSIENT_FAILURES,
     acls = [
         acl.entry(acl.CQ_COMMITTER, groups = ["project-r8-committers"]),
         acl.entry(acl.CQ_DRY_RUNNER, groups = ["googlers"]),
     ],
+    post_actions = [
+        cq.post_action_gerrit_label_votes(
+            name = "presubmit-verification-success",
+            conditions = [
+                cq.post_action_triggering_condition(
+                    mode = cq.MODE_DRY_RUN,
+                    statuses = [cq.STATUS_SUCCEEDED],
+                ),
+            ],
+            labels = {
+                "Presubmit-Verified": 1,
+            },
+        ),
+        cq.post_action_gerrit_label_votes(
+            name = "presubmit-verification-failure",
+            conditions = [
+                cq.post_action_triggering_condition(
+                    mode = cq.MODE_DRY_RUN,
+                    statuses = [cq.STATUS_FAILED, cq.STATUS_CANCELLED],
+                ),
+            ],
+            labels = {
+                # set it with -1 to indicate that it failed (or cancelled).
+                "Presubmit-Verified": -1,
+            },
+        ),
+    ],
 )
 
 luci.list_view(
@@ -252,7 +279,7 @@
     elif archive:
         dimensions["archive"] = "true"
     elif tester:
-         dimensions["tester"] = "true"
+        dimensions["tester"] = "true"
     else:
         dimensions["normal"] = "true"
     return dimensions
diff --git a/src/main/java/com/android/tools/r8/ApiDatabaseGenerator.java b/src/main/java/com/android/tools/r8/ApiDatabaseGenerator.java
index 7a9c6e3..13d5898 100644
--- a/src/main/java/com/android/tools/r8/ApiDatabaseGenerator.java
+++ b/src/main/java/com/android/tools/r8/ApiDatabaseGenerator.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
-import com.android.tools.r8.androidapi.ApiDatabaseEntry;
+import com.android.tools.r8.apimodel.AmendApiFromResources;
 import com.android.tools.r8.apimodel.AndroidApiHashingDatabaseBuilderGenerator;
 import com.android.tools.r8.apimodel.AndroidApiHashingDatabaseBuilderGenerator.GenerationException;
 import com.android.tools.r8.apimodel.AndroidApiVersionsXmlParser;
@@ -11,22 +11,24 @@
 import com.android.tools.r8.apimodel.ParsedApiClass;
 import com.android.tools.r8.apimodel.ParsedApiClassFlattening;
 import com.android.tools.r8.apimodel.ParsedApiClassMerging;
+import com.android.tools.r8.apimodel.ParsedApiClassSorting;
 import com.android.tools.r8.apimodel.ParsedApiClassTrimming;
 import com.android.tools.r8.apimodel.ParsedApiClassTrimming.JarTrimmer;
 import com.android.tools.r8.apimodel.ParsedApiClassTrimming.RemovedTrimmer;
+import com.android.tools.r8.apimodel.ParsedApiClassTrimming.Trimmer;
 import com.android.tools.r8.apimodel.ParsedApiClassVerifier;
 import com.android.tools.r8.apimodel.jar.ApiJarInfo;
+import com.android.tools.r8.apimodel.jar.ApiJarMerging;
 import com.android.tools.r8.apimodel.jar.ApiJarReader;
 import com.android.tools.r8.keepanno.annotations.KeepForApi;
 import com.android.tools.r8.origin.CommandLineOrigin;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.ExceptionUtils;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
-import java.util.Map;
+import java.util.function.Function;
 
 @KeepForApi
 public class ApiDatabaseGenerator {
@@ -49,22 +51,59 @@
   private static void runInternal(ApiDatabaseGeneratorCommand command)
       throws ApiDatabaseGeneratorException {
     try {
-      Collection<ParsedApiClass> classes = extractClasses(command);
-      ApiJarInfo jarInfo = ApiJarReader.read(command.getJarPaths());
-      classes = ParsedApiClassMerging.merge(classes, command.getDiagnosticsHandler());
-      classes = ParsedApiClassFlattening.flatten(classes);
-      classes = ParsedApiClassTrimming.trim(classes, new RemovedTrimmer());
-      classes = ParsedApiClassTrimming.trim(classes, new JarTrimmer(jarInfo));
-      ParsedApiClassVerifier.verify(classes);
-      Map<ApiDatabaseEntry, AndroidApiLevel> databaseEntries =
-          AndroidApiHashingDatabaseBuilderGenerator.generateEntries(classes);
-      AndroidApiHashingDatabaseBuilderGenerator.writeEntries(
-          databaseEntries, command.getOutputPath());
-    } catch (ParsingException | GenerationException | IOException e) {
+      var apiClasses = generateClasses(command, trimmer -> trimmer);
+      var entries = AndroidApiHashingDatabaseBuilderGenerator.generateEntries(apiClasses);
+      AndroidApiHashingDatabaseBuilderGenerator.writeEntries(entries, command.getOutputPath());
+    } catch (GenerationException e) {
       throw new ApiDatabaseGeneratorException("Failed to generate API database", e);
     }
   }
 
+  static <E extends Throwable> Collection<ParsedApiClass> generateClasses(
+      ApiDatabaseGeneratorCommand command, Function<JarTrimmer, Trimmer<E>> jarTrimmerWrapper)
+      throws ApiDatabaseGeneratorException, E {
+    try {
+      Collection<ParsedApiClass> classes = extractClasses(command);
+      classes = ParsedApiClassMerging.merge(classes, command.getDiagnosticsHandler());
+
+      List<ApiJarInfo> jarInfos = extractJars(command);
+      ApiJarInfo jarInfo = ApiJarMerging.mergeJarInfos(jarInfos);
+
+      if (command.shouldAmend()) {
+        amendApiData(classes, jarInfo);
+      }
+      classes = flattenHierarchy(classes);
+      classes = ParsedApiClassTrimming.trim(classes, new RemovedTrimmer());
+      Trimmer<E> jarTrimmer = jarTrimmerWrapper.apply(new JarTrimmer(jarInfo));
+      classes = ParsedApiClassTrimming.trim(classes, jarTrimmer);
+      classes = ParsedApiClassSorting.sorted(classes);
+      ParsedApiClassVerifier.verify(classes);
+      return classes;
+    } catch (ParsingException | IOException e) {
+      throw new ApiDatabaseGeneratorException("Failed to generate API classes", e);
+    }
+  }
+
+  private static Collection<ParsedApiClass> flattenHierarchy(Collection<ParsedApiClass> classes)
+      throws ApiDatabaseGeneratorException {
+    try {
+      return ParsedApiClassFlattening.flatten(classes);
+    } catch (ApiDatabaseGeneratorException e) {
+      throw new ApiDatabaseGeneratorException("Could not flatten class hierarchy", e);
+    }
+  }
+
+  private static void amendApiData(Collection<ParsedApiClass> classes, ApiJarInfo jarInfo)
+      throws ApiDatabaseGeneratorException, IOException {
+    try {
+      AmendApiFromResources.applyAmendments(classes, jarInfo);
+    } catch (ApiDatabaseGeneratorException e) {
+      throw new ApiDatabaseGeneratorException("Database amendment failed", e);
+    } catch (IOException e) {
+      throw new IOException("Database amendment failed", e);
+    }
+  }
+
   private static List<ParsedApiClass> extractClasses(ApiDatabaseGeneratorCommand command)
       throws ParsingException {
     List<ParsedApiClass> allParsed = new ArrayList<>();
@@ -75,6 +114,15 @@
     return allParsed;
   }
 
+  private static List<ApiJarInfo> extractJars(ApiDatabaseGeneratorCommand command)
+      throws ApiDatabaseGeneratorException, IOException {
+    List<ApiJarInfo> allRead = new ArrayList<>();
+    for (Path jarPath : command.getJarPaths()) {
+      allRead.add(ApiJarReader.read(jarPath));
+    }
+    return allRead;
+  }
+
   public static void main(String[] args) {
     try {
       run(ApiDatabaseGeneratorCommand.parse(args, CommandLineOrigin.INSTANCE).build());
diff --git a/src/main/java/com/android/tools/r8/ApiDatabaseGeneratorCommand.java b/src/main/java/com/android/tools/r8/ApiDatabaseGeneratorCommand.java
index f3d557e..6695eb8 100644
--- a/src/main/java/com/android/tools/r8/ApiDatabaseGeneratorCommand.java
+++ b/src/main/java/com/android/tools/r8/ApiDatabaseGeneratorCommand.java
@@ -21,15 +21,17 @@
   private final List<Path> jarPaths;
   private final List<Path> xmlPaths;
   private final Path outputPath;
+  private final boolean amend;
   private final Reporter reporter;
   private final boolean printHelp;
   private final boolean printVersion;
 
   private ApiDatabaseGeneratorCommand(
-      List<Path> jarPaths, List<Path> xmlPaths, Path outputPath, Reporter reporter) {
+      List<Path> jarPaths, List<Path> xmlPaths, Path outputPath, boolean amend, Reporter reporter) {
     this.jarPaths = jarPaths;
     this.xmlPaths = xmlPaths;
     this.outputPath = outputPath;
+    this.amend = amend;
     this.reporter = reporter;
     this.printHelp = false;
     this.printVersion = false;
@@ -39,6 +41,7 @@
     this.jarPaths = Collections.emptyList();
     this.xmlPaths = Collections.emptyList();
     this.outputPath = null;
+    this.amend = false;
     this.reporter = new Reporter();
     this.printHelp = printHelp;
     this.printVersion = printVersion;
@@ -60,6 +63,10 @@
     return xmlPaths;
   }
 
+  public boolean shouldAmend() {
+    return amend;
+  }
+
   public Path getOutputPath() {
     return outputPath;
   }
@@ -90,8 +97,10 @@
 
   @KeepForApi
   public static class Builder {
-    private final List<Path> inputPaths = new ArrayList<>();
+    private final List<Path> jarPaths = new ArrayList<>();
+    private final List<Path> xmlPaths = new ArrayList<>();
     private Path outputPath = null;
+    private boolean amend = true;
     private boolean printHelp = false;
     private boolean printVersion = false;
     private final Reporter reporter;
@@ -104,8 +113,19 @@
       this.reporter = new Reporter(diagnosticsHandler);
     }
 
-    public Builder addInputPath(Path inputPath) {
-      this.inputPaths.add(inputPath);
+    public Builder addJarPath(Path jarPath) {
+      this.jarPaths.add(jarPath);
+      return this;
+    }
+
+    public Builder addXmlPath(Path xmlPath) {
+      this.xmlPaths.add(xmlPath);
+      return this;
+    }
+
+    public Builder addSdkPath(Path sdkPath) {
+      addJarPath(sdkPath.resolve("android.jar"));
+      addXmlPath(sdkPath.resolve("data").resolve("api-versions.xml"));
       return this;
     }
 
@@ -114,6 +134,11 @@
       return this;
     }
 
+    public Builder setAmend(boolean amend) {
+      this.amend = amend;
+      return this;
+    }
+
     public Builder setPrintHelp(boolean printHelp) {
       this.printHelp = printHelp;
       return this;
@@ -132,22 +157,6 @@
       ExceptionUtils.withDiagnosticsHandler(
           reporter,
           () -> {
-            List<Path> jarPaths = new ArrayList<>();
-            List<Path> xmlPaths = new ArrayList<>();
-            for (Path path : inputPaths) {
-              String name = path.getFileName().toString().toLowerCase();
-              if (name.endsWith(".jar")) {
-                jarPaths.add(path);
-              } else if (name.endsWith(".xml")) {
-                xmlPaths.add(path);
-              } else {
-                error(
-                    new StringDiagnostic(
-                        "Unsupported input file extension: "
-                            + path
-                            + ". Must be either .jar or .xml"));
-              }
-            }
             if (jarPaths.isEmpty()) {
               error(new StringDiagnostic("At least one SDK JAR input path must be specified"));
             }
@@ -157,7 +166,8 @@
             if (outputPath == null) {
               outputPath = Paths.get(".", "api_database.ser");
             }
-            box.set(new ApiDatabaseGeneratorCommand(jarPaths, xmlPaths, outputPath, reporter));
+            box.set(
+                new ApiDatabaseGeneratorCommand(jarPaths, xmlPaths, outputPath, amend, reporter));
           },
           (message, cause, cancelled) -> new ApiDatabaseGeneratorException(message, cause));
       return box.get();
diff --git a/src/main/java/com/android/tools/r8/ApiDatabaseGeneratorCommandParser.java b/src/main/java/com/android/tools/r8/ApiDatabaseGeneratorCommandParser.java
index 415f4fa..09d00a0 100644
--- a/src/main/java/com/android/tools/r8/ApiDatabaseGeneratorCommandParser.java
+++ b/src/main/java/com/android/tools/r8/ApiDatabaseGeneratorCommandParser.java
@@ -26,22 +26,42 @@
   private static CliParser<ParserState> createParser() {
     String usageHeader =
         StringUtils.joinLines(
-            "Usage: apidatabasegenerator [options] <input-files>",
-            "where <input-files> are Android API XML files (e.g., api-versions.xml) to merge,",
-            "OR Android SDK Jar files (e.g., android.jar).",
-            "The XML lack sufficient information so the Jar files are used to add information to"
-                + " entries.",
+            "Usage: apidatabasegenerator [options]",
+            "Combines Android API information (--xml) with Android SDK information (--jar)",
+            "into an API database file required for compilation.",
+            "Multiple inputs of both are supported and any entries not present in both JAR and XML",
+            "form is trimmed away.",
             "The options are:");
     CliParser<ParserState> parser = new CliParser<>(usageHeader);
     return parser
         .option0("--help", "Print help.", state -> state.builder.setPrintHelp(true), "-h")
         .option0("--version", "Print version.", state -> state.builder.setPrintVersion(true))
         .option1(
+            "--jar",
+            "<jar-file>",
+            "Android SDK JAR file (e.g., android.jar).",
+            (state, arg) -> state.builder.addJarPath(Paths.get(arg)))
+        .option1(
+            "--xml",
+            "<xml-file>",
+            "Android API XML file (e.g., api-versions.xml).",
+            (state, arg) -> state.builder.addXmlPath(Paths.get(arg)))
+        .option1(
+            "--sdk",
+            "<sdk-dir>",
+            "Android SDK platform directory (e.g., $ANDROID_HOME/android-34).",
+            (state, arg) -> state.builder.addSdkPath(Paths.get(arg)))
+        .option1(
             "--output",
             "<database-file>",
             "Output result in <database-file> (must be a file, not a directory). Defaults to"
                 + " 'api_database.ser'.",
             (state, arg) -> state.builder.setOutputPath(Paths.get(arg)))
+        .option0(
+            "--dont-amend",
+            "By default, the API database is amended with known missing information."
+                + " This option disables that and processes inputs directly as they are.",
+            state -> state.builder.setAmend(false))
         .prefix2(
             "--map-diagnostics",
             "[:<type>]",
@@ -59,8 +79,7 @@
                     toLevel,
                     m -> state.builder.addDiagnosticsLevelMapping(m.from, m.diagnosticType, m.to),
                     state.builder::error,
-                    state.origin))
-        .positional((state, arg) -> state.builder.addInputPath(Paths.get(arg)));
+                    state.origin));
   }
 
   public static ApiDatabaseGeneratorCommand.Builder parse(String[] args, Origin origin) {
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 90ea229..604e564 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -563,9 +563,6 @@
         if (getMode() != CompilationMode.RELEASE) {
           reporter.error("Option --reoptimize-dex requires --release");
         }
-        if (proguardMapConsumer != null) {
-          reporter.error("Option --reoptimize-dex cannot be used with an output mapping file");
-        }
       }
       super.validate();
     }
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
index 839765e..4b9b8ed 100644
--- a/src/main/java/com/android/tools/r8/D8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -162,15 +162,6 @@
     }
   }
 
-  public static void main(String[] args) throws CompilationFailedException {
-    D8Command command = parse(args, Origin.root()).build();
-    if (command.isPrintHelp()) {
-      System.out.println(getUsageMessage());
-    } else {
-      D8.run(command);
-    }
-  }
-
   static String getUsageMessage() {
     StringBuilder builder = new StringBuilder();
     StringUtils.appendLines(
diff --git a/src/main/java/com/android/tools/r8/L8CommandParser.java b/src/main/java/com/android/tools/r8/L8CommandParser.java
index e82d6c0..f0e679a 100644
--- a/src/main/java/com/android/tools/r8/L8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/L8CommandParser.java
@@ -35,15 +35,6 @@
   // Note: this must be a subset of OPTIONS_WITH_ONE_PARAMETER.
   private static final Set<String> OPTIONS_WITH_TWO_PARAMETERS = ImmutableSet.of(ART_PROFILE_FLAG);
 
-  public static void main(String[] args) throws CompilationFailedException {
-    L8Command command = parse(args, Origin.root()).build();
-    if (command.isPrintHelp()) {
-      System.out.println(getUsageMessage());
-    } else {
-      L8.run(command);
-    }
-  }
-
   static String getUsageMessage() {
     StringBuilder builder = new StringBuilder();
     StringUtils.appendLines(
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiDataAccess.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiDataAccess.java
index 91326a8..7510ece 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiDataAccess.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiDataAccess.java
@@ -4,6 +4,9 @@
 
 package com.android.tools.r8.androidapi;
 
+import static com.android.tools.r8.apimodel.AndroidApiHashingDatabaseBuilderGenerator.DATABASE_FORMAT_VERSION;
+import static com.android.tools.r8.apimodel.AndroidApiHashingDatabaseBuilderGenerator.HEADER_SIZE;
+import static com.android.tools.r8.apimodel.AndroidApiHashingDatabaseBuilderGenerator.MAGIC_BYTES;
 import static com.android.tools.r8.lightir.ByteUtils.unsetBitAtIndex;
 
 import com.android.tools.r8.DiagnosticsHandler;
@@ -30,13 +33,46 @@
 import java.nio.file.StandardOpenOption;
 import java.util.Map;
 import java.util.function.BiPredicate;
+import java.util.function.Consumer;
 
 /**
- * Implements low-level access methods for seeking on top of the database file defined by {@code
+ * Implements low-level access methods for seeking on top of the database file defined by {@link
  * AndroidApiLevelHashingDatabaseImpl} where a description of the format can also be found.
  */
 public abstract class AndroidApiDataAccess {
 
+  private static Integer readHeader(CompatByteBuffer buffer) {
+    if (buffer == null || buffer.capacity() < HEADER_SIZE) {
+      return null;
+    }
+    for (int i = 0; i < MAGIC_BYTES.length; i++) {
+      if (buffer.get(i) != MAGIC_BYTES[i]) {
+        return null;
+      }
+    }
+    return buffer.getInt(MAGIC_BYTES.length);
+  }
+
+  private static Integer readHeader(byte[] data) {
+    return data == null ? null : readHeader(CompatByteBuffer.wrap(data));
+  }
+
+  private static boolean isHeaderValid(Integer headerVersion, Consumer<String> errorHandler) {
+    if (headerVersion == null) {
+      errorHandler.accept("Invalid header");
+      return false;
+    } else if (headerVersion != DATABASE_FORMAT_VERSION) {
+      errorHandler.accept(
+          "Incompatible database version (expected "
+              + DATABASE_FORMAT_VERSION
+              + " found "
+              + headerVersion
+              + ")");
+      return false;
+    }
+    return true;
+  }
+
   private static final String RESOURCE_NAME = "resources/api_database.ser";
   private static final int ENTRY_SIZE_IN_BITS_FOR_CONSTANT_POOL_MAP = 17;
   private static final int ENTRY_SIZE_IN_BITS_FOR_API_MAP = 18;
@@ -91,27 +127,9 @@
 
   public static AndroidApiDataAccess create(
       InternalOptions options, DiagnosticsHandler diagnosticsHandler) {
-    URL resource;
-    Path apiDatabasePath = options.apiModelingOptions().apiDatabasePath;
-    if (apiDatabasePath != null) {
-      if (!Files.exists(apiDatabasePath)) {
-        diagnosticsHandler.error(
-            new StringDiagnostic("API database file does not exist: " + apiDatabasePath));
-        return new AndroidApiDataAccessNoBacking();
-      }
-      try {
-        resource = apiDatabasePath.toUri().toURL();
-      } catch (IOException e) {
-        throw new Unreachable(e);
-      }
-    } else {
-      URL resourceUrl = AndroidApiDataAccess.class.getClassLoader().getResource(RESOURCE_NAME);
-      if (resourceUrl == null) {
-        diagnosticsHandler.warning(
-            new StringDiagnostic("Could not find the api database at " + RESOURCE_NAME));
-        return new AndroidApiDataAccessNoBacking();
-      }
-      resource = resourceUrl;
+    URL resource = getApiDatabaseUrl(options, diagnosticsHandler);
+    if (resource == null) {
+      return new AndroidApiDataAccessNoBacking();
     }
 
     if (options.apiModelingOptions().useMemoryMappedByteBuffer) {
@@ -120,7 +138,7 @@
         // protocol: file, path: <path-to-file>
         // protocol: jar, path: file:<path-to-jar>!/<resource-name-in-jar>
         if (resource.getProtocol().equals("file")) {
-          return memoryMappedFromFile(Paths.get(resource.toURI()));
+          return memoryMappedFromFile(Paths.get(resource.toURI()), diagnosticsHandler);
         } else if (resource.getProtocol().equals("jar") && resource.getPath().startsWith("file:")) {
           // The path is on form 'file:<path-to-jar>!/<resource-name-in-jar>
           JarURLConnection jarUrl = (JarURLConnection) resource.openConnection();
@@ -133,7 +151,8 @@
               return memoryMappedFromSectionOfFile(
                   jarFile.toPath(),
                   entry.getPayloadLocation().first,
-                  entry.getPayloadLocation().last - entry.getPayloadLocation().first + 1);
+                  entry.getPayloadLocation().last - entry.getPayloadLocation().first + 1,
+                  diagnosticsHandler);
             }
           }
         }
@@ -155,28 +174,68 @@
             new StringDiagnostic("Could not open the api database at " + RESOURCE_NAME));
         return new AndroidApiDataAccessNoBacking();
       }
-      return new AndroidApiDataAccessInMemory(ByteStreams.toByteArray(apiInputStream));
+      byte[] bytes = ByteStreams.toByteArray(apiInputStream);
+      boolean valid =
+          isHeaderValid(
+              readHeader(bytes),
+              err -> diagnosticsHandler.warning(new StringDiagnostic(err + " in " + resource)));
+      if (!valid) {
+        return new AndroidApiDataAccessNoBacking();
+      }
+      return new AndroidApiDataAccessInMemory(bytes);
     } catch (IOException e) {
       diagnosticsHandler.warning(new ExceptionDiagnostic(e));
       return new AndroidApiDataAccessNoBacking();
     }
   }
 
-  private static AndroidApiDataAccessByteMapped memoryMappedFromFile(Path path) throws IOException {
-    FileChannel fileChannel = (FileChannel) Files.newByteChannel(path, StandardOpenOption.READ);
-    MappedByteBuffer mappedByteBuffer =
-        fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());
-    // Ensure that we can run on JDK 8 by using the CompatByteBuffer.
-    return new AndroidApiDataAccessByteMapped(new CompatByteBuffer(mappedByteBuffer));
+  private static URL getApiDatabaseUrl(
+      InternalOptions options, DiagnosticsHandler diagnosticsHandler) {
+    Path apiDatabasePath = options.apiModelingOptions().apiDatabasePath;
+    if (apiDatabasePath != null) {
+      if (!Files.exists(apiDatabasePath)) {
+        diagnosticsHandler.error(
+            new StringDiagnostic("API database file does not exist: " + apiDatabasePath));
+        return null;
+      }
+      try {
+        return apiDatabasePath.toUri().toURL();
+      } catch (IOException e) {
+        throw new Unreachable(e);
+      }
+    } else {
+      URL resourceUrl = AndroidApiDataAccess.class.getClassLoader().getResource(RESOURCE_NAME);
+      if (resourceUrl == null) {
+        diagnosticsHandler.warning(
+            new StringDiagnostic("Could not find the api database at " + RESOURCE_NAME));
+        return null;
+      }
+      return resourceUrl;
+    }
   }
 
-  private static AndroidApiDataAccessByteMapped memoryMappedFromSectionOfFile(
-      Path path, long offset, long length) throws IOException {
+  private static AndroidApiDataAccess memoryMappedFromFile(
+      Path path, DiagnosticsHandler diagnosticsHandler) throws IOException {
+    return memoryMappedFromSectionOfFile(path, 0, -1, diagnosticsHandler);
+  }
+
+  private static AndroidApiDataAccess memoryMappedFromSectionOfFile(
+      Path path, long offset, long length, DiagnosticsHandler diagnosticsHandler)
+      throws IOException {
     FileChannel fileChannel = (FileChannel) Files.newByteChannel(path, StandardOpenOption.READ);
     MappedByteBuffer mappedByteBuffer =
-        fileChannel.map(FileChannel.MapMode.READ_ONLY, offset, length);
+        fileChannel.map(
+            FileChannel.MapMode.READ_ONLY, offset, length < 0 ? fileChannel.size() : length);
     // Ensure that we can run on JDK 8 by using the CompatByteBuffer.
-    return new AndroidApiDataAccessByteMapped(new CompatByteBuffer(mappedByteBuffer));
+    CompatByteBuffer bytes = new CompatByteBuffer(mappedByteBuffer);
+    boolean valid =
+        isHeaderValid(
+            readHeader(bytes),
+            err -> diagnosticsHandler.warning(new StringDiagnostic(err + " in " + path)));
+    if (!valid) {
+      return new AndroidApiDataAccessNoBacking();
+    }
+    return new AndroidApiDataAccessByteMapped(bytes);
   }
 
   public static int entrySizeInBitsForConstantPoolMap() {
@@ -211,9 +270,14 @@
     return PAYLOAD_OFFSET_WITH_LENGTH;
   }
 
+  /** The start of the constant pool size */
+  public static int constantPoolSizeOffset() {
+    return HEADER_SIZE;
+  }
+
   /** The start of the constant pool */
   public static int constantPoolOffset() {
-    return 4;
+    return HEADER_SIZE + 4;
   }
 
   /** The start of the constant pool hash map. */
@@ -357,7 +421,7 @@
 
     @Override
     int readConstantPoolSize() {
-      return mappedByteBuffer.getInt(0);
+      return mappedByteBuffer.getInt(constantPoolSizeOffset());
     }
 
     @Override
@@ -426,7 +490,7 @@
 
     @Override
     public int readConstantPoolSize() {
-      return readIntFromOffset(data, 0);
+      return readIntFromOffset(data, constantPoolSizeOffset());
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/apimodel/AmendApiFromResources.java b/src/main/java/com/android/tools/r8/apimodel/AmendApiFromResources.java
new file mode 100644
index 0000000..0aac870
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/apimodel/AmendApiFromResources.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2026, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import com.android.tools.r8.ApiDatabaseGeneratorException;
+import com.android.tools.r8.apimodel.jar.ApiJarInfo;
+import com.android.tools.r8.utils.internal.ThrowingFunction;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+
+public class AmendApiFromResources {
+
+  public static void applyAmendments(Collection<ParsedApiClass> classes, ApiJarInfo jarInfo)
+      throws ApiDatabaseGeneratorException, IOException {
+    ApiAmendments missingApi =
+        readResource("/resources/missing.api.txt", ApiAmendmentsParser::parseApiAmendments);
+    ApiAmendments hiddenApi =
+        readResource("/resources/hidden.api.txt", ApiAmendmentsParser::parseApiAmendments);
+    JarAmendments jarHidden =
+        readResource("/resources/hidden.jar.txt", ApiAmendmentsParser::parseJarAmendments);
+
+    ParsedApiClassAmending.amendApi(classes, missingApi);
+    ParsedApiClassAmending.amendApi(classes, hiddenApi);
+    ApiJarInfoAmending.amendJar(jarInfo, jarHidden);
+  }
+
+  private static <T, E extends Throwable> T readResource(
+      String resourceName, ThrowingFunction<InputStream, T, E> consumer)
+      throws IOException, ApiDatabaseGeneratorException, E {
+    try (InputStream stream = AmendApiFromResources.class.getResourceAsStream(resourceName)) {
+      if (stream == null) {
+        throw new ApiDatabaseGeneratorException("Resource " + resourceName + " not found");
+      }
+      return consumer.apply(stream);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java b/src/main/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
index 2e5ef8b..800cccb 100644
--- a/src/main/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
+++ b/src/main/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
@@ -26,6 +26,7 @@
 import java.io.DataOutputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -40,6 +41,17 @@
 
 public class AndroidApiHashingDatabaseBuilderGenerator {
 
+  public static final String MAGIC = "r8-apidatabase";
+  public static final byte[] MAGIC_BYTES = MAGIC.getBytes(StandardCharsets.US_ASCII);
+  public static final int HEADER_SIZE = MAGIC_BYTES.length + 4;
+
+  /**
+   * This version must be increased whenever the output of {@link
+   * com.android.tools.r8.ApiDatabaseGenerator} changes. This can be because of changes to
+   * amendments or changes to the underlying format.
+   */
+  public static final int DATABASE_FORMAT_VERSION = 1;
+
   public static class GenerationException extends Exception {
     public GenerationException(String message) {
       super(message);
@@ -108,6 +120,8 @@
    * number of unsigned bytes):
    *
    * <pre>
+   * magic:              r8-apidatabase
+   * format_version:     u4
    * constant_pool_size: u4
    * constant_pool:      [constant_pool_size * payload_entry]
    * constant_pool_map:  [0..max_hash(ConstantPoolEntry) * payload_entry]
@@ -187,6 +201,10 @@
       offsetMap.put(entry.getIntKey(), Pair.create(startingOffset, length));
     }
 
+    // Write header: magic string and database format version.
+    outputStream.write(MAGIC_BYTES);
+    outputStream.writeInt(DATABASE_FORMAT_VERSION);
+
     // Write constant pool size <u4:size>.
     outputStream.writeInt(constantPool.size());
 
diff --git a/src/main/java/com/android/tools/r8/apimodel/ApiAmendments.java b/src/main/java/com/android/tools/r8/apimodel/ApiAmendments.java
new file mode 100644
index 0000000..15c79e9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/apimodel/ApiAmendments.java
@@ -0,0 +1,96 @@
+// Copyright (c) 2026, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ApiAmendments {
+
+  public static class ClassAmendment {
+    private final ClassReference classReference;
+    private final AndroidApiLevel apiLevel;
+
+    public ClassAmendment(ClassReference classReference, AndroidApiLevel apiLevel) {
+      this.classReference = classReference;
+      this.apiLevel = apiLevel;
+    }
+
+    public ClassReference getClassReference() {
+      return classReference;
+    }
+
+    public AndroidApiLevel getApiLevel() {
+      return apiLevel;
+    }
+  }
+
+  public static class MethodAmendment {
+    private final MethodReference methodReference;
+    private final AndroidApiLevel apiLevel;
+
+    public MethodAmendment(MethodReference methodReference, AndroidApiLevel apiLevel) {
+      this.methodReference = methodReference;
+      this.apiLevel = apiLevel;
+    }
+
+    public MethodReference getMethodReference() {
+      return methodReference;
+    }
+
+    public AndroidApiLevel getApiLevel() {
+      return apiLevel;
+    }
+  }
+
+  public static class FieldAmendment {
+    private final FieldTypelessReference fieldReference;
+    private final AndroidApiLevel apiLevel;
+
+    public FieldAmendment(FieldTypelessReference fieldReference, AndroidApiLevel apiLevel) {
+      this.fieldReference = fieldReference;
+      this.apiLevel = apiLevel;
+    }
+
+    public FieldTypelessReference getFieldReference() {
+      return fieldReference;
+    }
+
+    public AndroidApiLevel getApiLevel() {
+      return apiLevel;
+    }
+  }
+
+  private final List<ClassAmendment> classes = new ArrayList<>();
+  private final List<MethodAmendment> methods = new ArrayList<>();
+  private final List<FieldAmendment> fields = new ArrayList<>();
+
+  public void addClass(ClassReference classReference, AndroidApiLevel apiLevel) {
+    classes.add(new ClassAmendment(classReference, apiLevel));
+  }
+
+  public void addMethod(MethodReference methodReference, AndroidApiLevel apiLevel) {
+    methods.add(new MethodAmendment(methodReference, apiLevel));
+  }
+
+  public void addField(FieldTypelessReference fieldReference, AndroidApiLevel apiLevel) {
+    fields.add(new FieldAmendment(fieldReference, apiLevel));
+  }
+
+  public List<ClassAmendment> getClasses() {
+    return classes;
+  }
+
+  public List<MethodAmendment> getMethods() {
+    return methods;
+  }
+
+  public List<FieldAmendment> getFields() {
+    return fields;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/apimodel/ApiAmendmentsParser.java b/src/main/java/com/android/tools/r8/apimodel/ApiAmendmentsParser.java
new file mode 100644
index 0000000..5434832
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/apimodel/ApiAmendmentsParser.java
@@ -0,0 +1,234 @@
+// Copyright (c) 2026, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Parser for API and Jar amendments files (*.api.txt and *.jar.txt). This format is only written
+ * and read internally so it is free to change.
+ *
+ * <p>Empty lines and lines starting with {@code //} or {@code #} are ignored.
+ *
+ * <p><b>API Amendments format (*.api.txt):</b>
+ *
+ * <ul>
+ *   <li>{@code class <class-descriptor> <api-level>}
+ *   <li>{@code method <holder-descriptor> <method-name> <method-descriptor> <api-level>}
+ *   <li>{@code field <holder-descriptor> <field-name> <api-level>}
+ * </ul>
+ *
+ * <p><b>Jar Amendments format (*.jar.txt):</b>
+ *
+ * <ul>
+ *   <li>{@code class <class-descriptor> extends <super-descriptor> implements <interfaces|none>}
+ *   <li>{@code interface <interface-descriptor> extends <interfaces|none>}
+ *   <li>{@code method <holder-descriptor> <method-name> <method-descriptor> <static|instance>}
+ *   <li>{@code field <holder-descriptor> <field-name>}
+ * </ul>
+ *
+ * <p>Multiple interfaces are comma-separated without spaces (e.g. {@code
+ * Ljava/lang/Cloneable;,Ljava/io/Serializable;}) or {@code none} if there are no interfaces.
+ */
+public class ApiAmendmentsParser {
+
+  public static ApiAmendments parseApiAmendments(InputStream inputStream) throws IOException {
+    ApiAmendments amendments = new ApiAmendments();
+    try (BufferedReader reader =
+        new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
+      String line;
+      int lineNumber = 0;
+      while ((line = reader.readLine()) != null) {
+        lineNumber++;
+        String[] parts = splitLine(line);
+        if (parts == null) {
+          continue;
+        }
+        parseApiLine(parts[0], parts, amendments, lineNumber);
+      }
+    }
+    return amendments;
+  }
+
+  private static void parseApiLine(
+      String type, String[] parts, ApiAmendments amendments, int lineNumber) {
+    switch (type) {
+      case "class":
+        parseClassAmendment(parts, amendments, lineNumber);
+        break;
+      case "method":
+        parseMethodAmendment(parts, amendments, lineNumber);
+        break;
+      case "field":
+        parseFieldAmendment(parts, amendments, lineNumber);
+        break;
+      default:
+        throw new RuntimeException("Unknown type " + type + " at line " + lineNumber);
+    }
+  }
+
+  private static void parseClassAmendment(
+      String[] parts, ApiAmendments amendments, int lineNumber) {
+    if (parts.length != 3) {
+      throw new RuntimeException("Invalid class amendment at line " + lineNumber);
+    }
+    ClassReference classReference = Reference.classFromDescriptor(parts[1]);
+    AndroidApiLevel apiLevel = AndroidApiLevel.parseAndroidApiLevel(parts[2]);
+    amendments.addClass(classReference, apiLevel);
+  }
+
+  private static void parseMethodAmendment(
+      String[] parts, ApiAmendments amendments, int lineNumber) {
+    if (parts.length != 5) {
+      throw new RuntimeException("Invalid method amendment at line " + lineNumber);
+    }
+    ClassReference holder = Reference.classFromDescriptor(parts[1]);
+    String name = parts[2];
+    String descriptor = parts[3];
+    AndroidApiLevel apiLevel = AndroidApiLevel.parseAndroidApiLevel(parts[4]);
+    amendments.addMethod(Reference.methodFromDescriptor(holder, name, descriptor), apiLevel);
+  }
+
+  private static void parseFieldAmendment(
+      String[] parts, ApiAmendments amendments, int lineNumber) {
+    if (parts.length != 4) {
+      throw new RuntimeException("Invalid field amendment at line " + lineNumber);
+    }
+    ClassReference holder = Reference.classFromDescriptor(parts[1]);
+    String name = parts[2];
+    AndroidApiLevel apiLevel = AndroidApiLevel.parseAndroidApiLevel(parts[3]);
+    amendments.addField(new FieldTypelessReference(holder, name), apiLevel);
+  }
+
+  public static JarAmendments parseJarAmendments(InputStream inputStream) throws IOException {
+    JarAmendments amendments = new JarAmendments();
+    try (BufferedReader reader =
+        new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
+      String line;
+      int lineNumber = 0;
+      while ((line = reader.readLine()) != null) {
+        lineNumber++;
+        String[] parts = splitLine(line);
+        if (parts == null) {
+          continue;
+        }
+        parseJarLine(parts[0], parts, amendments, lineNumber);
+      }
+    }
+    return amendments;
+  }
+
+  private static void parseJarLine(
+      String type, String[] parts, JarAmendments amendments, int lineNumber) {
+    switch (type) {
+      case "class":
+        parseJarClassAmendment(parts, amendments, lineNumber);
+        break;
+      case "interface":
+        parseJarInterfaceAmendment(parts, amendments, lineNumber);
+        break;
+      case "method":
+        parseJarMethodAmendment(parts, amendments, lineNumber);
+        break;
+      case "field":
+        parseJarFieldAmendment(parts, amendments, lineNumber);
+        break;
+      default:
+        throw new RuntimeException("Unknown type " + type + " at line " + lineNumber);
+    }
+  }
+
+  private static String[] splitLine(String line) {
+    line = line.trim();
+    if (line.isEmpty() || line.startsWith("//") || line.startsWith("#")) {
+      return null;
+    } else {
+      String[] split = line.split(" ");
+      assert split.length >= 1;
+      return split;
+    }
+  }
+
+  private static void parseJarClassAmendment(
+      String[] parts, JarAmendments amendments, int lineNumber) {
+    if (parts.length != 6) {
+      throw new RuntimeException("Invalid jar class amendment at line " + lineNumber);
+    }
+    ClassReference classReference = Reference.classFromDescriptor(parts[1]);
+    if (!parts[2].equals("extends")) {
+      throw new RuntimeException("Expected 'extends' as third word at line " + lineNumber);
+    }
+    ClassReference superClass = Reference.classFromDescriptor(parts[3]);
+    if (!parts[4].equals("implements")) {
+      throw new RuntimeException("Expected 'implements' as fifth word at line " + lineNumber);
+    }
+    List<ClassReference> interfaces = parseInterfaces(parts[5]);
+    amendments.addClass(classReference, superClass, interfaces);
+  }
+
+  private static void parseJarInterfaceAmendment(
+      String[] parts, JarAmendments amendments, int lineNumber) {
+    if (parts.length != 4) {
+      throw new RuntimeException("Invalid jar interface amendment at line " + lineNumber);
+    }
+    ClassReference interfaceReference = Reference.classFromDescriptor(parts[1]);
+    if (!parts[2].equals("extends")) {
+      throw new RuntimeException("Expected 'extends' as third word at line " + lineNumber);
+    }
+    List<ClassReference> interfaces = parseInterfaces(parts[3]);
+    amendments.addInterface(interfaceReference, interfaces);
+  }
+
+  private static List<ClassReference> parseInterfaces(String interfacesStr) {
+    if (interfacesStr.equals("none")) {
+      return ImmutableList.of();
+    }
+    return Arrays.stream(interfacesStr.split(","))
+        .map(Reference::classFromDescriptor)
+        .collect(Collectors.toList());
+  }
+
+  private static void parseJarMethodAmendment(
+      String[] parts, JarAmendments amendments, int lineNumber) {
+    if (parts.length != 5) {
+      throw new RuntimeException("Invalid jar method amendment at line " + lineNumber);
+    }
+    ClassReference holder = Reference.classFromDescriptor(parts[1]);
+    String name = parts[2];
+    String descriptor = parts[3];
+    String relation = parts[4];
+    boolean isStatic;
+    if (relation.equals("static")) {
+      isStatic = true;
+    } else if (relation.equals("instance")) {
+      isStatic = false;
+    } else {
+      throw new RuntimeException(
+          "Expected 'static' or 'instance' as fifth word at line " + lineNumber);
+    }
+    amendments.addMethod(holder, name, descriptor, isStatic);
+  }
+
+  private static void parseJarFieldAmendment(
+      String[] parts, JarAmendments amendments, int lineNumber) {
+    if (parts.length != 3) {
+      throw new RuntimeException("Invalid jar field amendment at line " + lineNumber);
+    }
+    ClassReference holder = Reference.classFromDescriptor(parts[1]);
+    String name = parts[2];
+    amendments.addField(holder, name);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/apimodel/ApiJarInfoAmending.java b/src/main/java/com/android/tools/r8/apimodel/ApiJarInfoAmending.java
new file mode 100644
index 0000000..38e8a25
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/apimodel/ApiJarInfoAmending.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2026, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import com.android.tools.r8.ApiDatabaseGeneratorException;
+import com.android.tools.r8.apimodel.JarAmendments.ClassAmendment;
+import com.android.tools.r8.apimodel.JarAmendments.FieldAmendment;
+import com.android.tools.r8.apimodel.JarAmendments.InterfaceAmendment;
+import com.android.tools.r8.apimodel.JarAmendments.MethodAmendment;
+import com.android.tools.r8.apimodel.jar.ApiClassInfo;
+import com.android.tools.r8.apimodel.jar.ApiJarInfo;
+import com.android.tools.r8.references.ClassReference;
+
+public class ApiJarInfoAmending {
+
+  public static void amendJar(ApiJarInfo jarInfo, JarAmendments jarAmendments)
+      throws ApiDatabaseGeneratorException {
+    for (ClassAmendment classAmendment : jarAmendments.getClasses()) {
+      amendJarClass(jarInfo, classAmendment);
+    }
+    for (JarAmendments.InterfaceAmendment interfaceAmendment : jarAmendments.getInterfaces()) {
+      amendJarInterface(jarInfo, interfaceAmendment);
+    }
+    for (MethodAmendment methodAmendment : jarAmendments.getMethods()) {
+      amendJarMethod(jarInfo, methodAmendment);
+    }
+    for (FieldAmendment fieldAmendment : jarAmendments.getFields()) {
+      amendJarField(jarInfo, fieldAmendment);
+    }
+  }
+
+  private static void amendJarClass(ApiJarInfo jarInfo, ClassAmendment classAmendment)
+      throws ApiDatabaseGeneratorException {
+    ClassReference classRef = classAmendment.getClassReference();
+    if (jarInfo.hasClass(classRef)) {
+      throw new ApiDatabaseGeneratorException(
+          "Hidden jar class " + classRef + " already exists in JAR info");
+    }
+
+    String superClassBin =
+        classAmendment.getSuperClass() != null
+            ? classAmendment.getSuperClass().getBinaryName()
+            : null;
+
+    ApiClassInfo classInfo = new ApiClassInfo(classRef.getBinaryName(), superClassBin, false);
+    classInfo.addInterfaces(classAmendment.getInterfaces(), ClassReference::getBinaryName);
+    jarInfo.addClass(classInfo);
+  }
+
+  private static void amendJarInterface(ApiJarInfo jarInfo, InterfaceAmendment interfaceAmendment)
+      throws ApiDatabaseGeneratorException {
+    ClassReference ifaceRef = interfaceAmendment.getInterfaceReference();
+    if (jarInfo.hasClass(ifaceRef)) {
+      throw new ApiDatabaseGeneratorException(
+          "Hidden jar interface " + ifaceRef + " already exists in JAR info");
+    }
+
+    ApiClassInfo classInfo = new ApiClassInfo(ifaceRef.getBinaryName(), null, true);
+    classInfo.addInterfaces(interfaceAmendment.getInterfaces(), ClassReference::getBinaryName);
+    jarInfo.addClass(classInfo);
+  }
+
+  private static void amendJarMethod(ApiJarInfo jarInfo, MethodAmendment methodAmendment)
+      throws ApiDatabaseGeneratorException {
+    ClassReference holderRef = methodAmendment.getHolder();
+    ApiClassInfo holder = jarInfo.getClassInfo(holderRef);
+    if (holder == null) {
+      throw new ApiDatabaseGeneratorException(
+          "Holder class "
+              + holderRef
+              + " for hidden jar method "
+              + methodAmendment.getName()
+              + " does not exist");
+    }
+    if (holder.hasMethod(methodAmendment.getName(), methodAmendment.getDescriptor())) {
+      throw new ApiDatabaseGeneratorException(
+          "Hidden jar method "
+              + methodAmendment.getName()
+              + methodAmendment.getDescriptor()
+              + " already exists in holder "
+              + holderRef);
+    }
+    holder.addMethod(
+        methodAmendment.getName(), methodAmendment.getDescriptor(), methodAmendment.isStatic());
+  }
+
+  private static void amendJarField(ApiJarInfo jarInfo, FieldAmendment fieldAmendment)
+      throws ApiDatabaseGeneratorException {
+    ClassReference holderRef = fieldAmendment.getHolder();
+    ApiClassInfo holder = jarInfo.getClassInfo(holderRef);
+    if (holder == null) {
+      throw new ApiDatabaseGeneratorException(
+          "Holder class "
+              + holderRef
+              + " for hidden jar field "
+              + fieldAmendment.getName()
+              + " does not exist");
+    }
+    if (holder.hasField(fieldAmendment.getName())) {
+      throw new ApiDatabaseGeneratorException(
+          "Hidden jar field "
+              + fieldAmendment.getName()
+              + " already exists in holder "
+              + holderRef);
+    }
+    holder.addField(fieldAmendment.getName());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/apimodel/JarAmendments.java b/src/main/java/com/android/tools/r8/apimodel/JarAmendments.java
new file mode 100644
index 0000000..5ff6208
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/apimodel/JarAmendments.java
@@ -0,0 +1,143 @@
+// Copyright (c) 2026, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import com.android.tools.r8.references.ClassReference;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JarAmendments {
+
+  public static class ClassAmendment {
+    private final ClassReference classReference;
+    private final ClassReference superClass;
+    private final List<ClassReference> interfaces;
+
+    public ClassAmendment(
+        ClassReference classReference, ClassReference superClass, List<ClassReference> interfaces) {
+      this.classReference = classReference;
+      this.superClass = superClass;
+      this.interfaces = interfaces;
+    }
+
+    public ClassReference getClassReference() {
+      return classReference;
+    }
+
+    public ClassReference getSuperClass() {
+      return superClass;
+    }
+
+    public List<ClassReference> getInterfaces() {
+      return interfaces;
+    }
+  }
+
+  public static class InterfaceAmendment {
+    private final ClassReference interfaceReference;
+    private final List<ClassReference> interfaces;
+
+    public InterfaceAmendment(ClassReference interfaceReference, List<ClassReference> interfaces) {
+      this.interfaceReference = interfaceReference;
+      this.interfaces = interfaces;
+    }
+
+    public ClassReference getInterfaceReference() {
+      return interfaceReference;
+    }
+
+    public List<ClassReference> getInterfaces() {
+      return interfaces;
+    }
+  }
+
+  public static class MethodAmendment {
+    private final ClassReference holder;
+    private final String name;
+    private final String descriptor;
+    private final boolean isStatic;
+
+    public MethodAmendment(
+        ClassReference holder, String name, String descriptor, boolean isStatic) {
+      this.holder = holder;
+      this.name = name;
+      this.descriptor = descriptor;
+      this.isStatic = isStatic;
+    }
+
+    public ClassReference getHolder() {
+      return holder;
+    }
+
+    public String getName() {
+      return name;
+    }
+
+    public String getDescriptor() {
+      return descriptor;
+    }
+
+    public boolean isStatic() {
+      return isStatic;
+    }
+  }
+
+  public static class FieldAmendment {
+    private final ClassReference holder;
+    private final String name;
+
+    public FieldAmendment(ClassReference holder, String name) {
+      this.holder = holder;
+      this.name = name;
+    }
+
+    public ClassReference getHolder() {
+      return holder;
+    }
+
+    public String getName() {
+      return name;
+    }
+  }
+
+  private final List<ClassAmendment> classes = new ArrayList<>();
+  private final List<InterfaceAmendment> interfaces = new ArrayList<>();
+  private final List<MethodAmendment> methods = new ArrayList<>();
+  private final List<FieldAmendment> fields = new ArrayList<>();
+
+  public void addClass(
+      ClassReference classReference, ClassReference superClass, List<ClassReference> interfaces) {
+    classes.add(new ClassAmendment(classReference, superClass, interfaces));
+  }
+
+  public void addInterface(
+      ClassReference interfaceReference, List<ClassReference> implementedInterfaces) {
+    interfaces.add(new InterfaceAmendment(interfaceReference, implementedInterfaces));
+  }
+
+  public void addMethod(ClassReference holder, String name, String descriptor, boolean isStatic) {
+    methods.add(new MethodAmendment(holder, name, descriptor, isStatic));
+  }
+
+  public void addField(ClassReference holder, String name) {
+    fields.add(new FieldAmendment(holder, name));
+  }
+
+  public List<ClassAmendment> getClasses() {
+    return classes;
+  }
+
+  public List<InterfaceAmendment> getInterfaces() {
+    return interfaces;
+  }
+
+  public List<MethodAmendment> getMethods() {
+    return methods;
+  }
+
+  public List<FieldAmendment> getFields() {
+    return fields;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/apimodel/ParsedApiClassAmending.java b/src/main/java/com/android/tools/r8/apimodel/ParsedApiClassAmending.java
new file mode 100644
index 0000000..b9e893d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/apimodel/ParsedApiClassAmending.java
@@ -0,0 +1,92 @@
+// Copyright (c) 2026, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import com.android.tools.r8.ApiDatabaseGeneratorException;
+import com.android.tools.r8.apimodel.ApiAmendments.ClassAmendment;
+import com.android.tools.r8.apimodel.ApiAmendments.FieldAmendment;
+import com.android.tools.r8.apimodel.ApiAmendments.MethodAmendment;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ParsedApiClassAmending {
+
+  public static void amendApi(Collection<ParsedApiClass> apiClasses, ApiAmendments amendments)
+      throws ApiDatabaseGeneratorException {
+    Map<ClassReference, ParsedApiClass> classMap = new HashMap<>();
+    for (ParsedApiClass apiClass : apiClasses) {
+      classMap.put(apiClass.getClassReference(), apiClass);
+    }
+
+    for (ApiAmendments.ClassAmendment classAmendment : amendments.getClasses()) {
+      amendApiClass(apiClasses, classAmendment, classMap);
+    }
+    for (ApiAmendments.MethodAmendment methodAmendment : amendments.getMethods()) {
+      amendApiMethod(methodAmendment, classMap);
+    }
+    for (ApiAmendments.FieldAmendment fieldAmendment : amendments.getFields()) {
+      amendApiField(fieldAmendment, classMap);
+    }
+  }
+
+  private static void amendApiField(
+      FieldAmendment fieldAmendment, Map<ClassReference, ParsedApiClass> classMap)
+      throws ApiDatabaseGeneratorException {
+    FieldTypelessReference fieldRef = fieldAmendment.getFieldReference();
+    ParsedApiClass holder = classMap.get(fieldRef.getHolderClass());
+    if (holder == null) {
+      throw new ApiDatabaseGeneratorException(
+          "Holder class "
+              + fieldRef.getHolderClass()
+              + " for field "
+              + fieldRef
+              + " does not exist");
+    }
+    if (holder.hasField(fieldRef)) {
+      throw new ApiDatabaseGeneratorException(
+          "Field " + fieldRef + " already exists in " + holder.getClassReference());
+    }
+    holder.registerField(fieldRef, new ApiRange(fieldAmendment.getApiLevel()));
+  }
+
+  private static void amendApiMethod(
+      MethodAmendment methodAmendment, Map<ClassReference, ParsedApiClass> classMap)
+      throws ApiDatabaseGeneratorException {
+    MethodReference methodRef = methodAmendment.getMethodReference();
+    ParsedApiClass holder = classMap.get(methodRef.getHolderClass());
+    if (holder == null) {
+      throw new ApiDatabaseGeneratorException(
+          "Holder class "
+              + methodRef.getHolderClass()
+              + " for method "
+              + methodRef
+              + " does not exist");
+    }
+    if (holder.hasMethod(methodRef)) {
+      throw new ApiDatabaseGeneratorException(
+          "Method " + methodRef + " already exists in " + holder.getClassReference());
+    }
+    holder.registerMethod(methodRef, new ApiRange(methodAmendment.getApiLevel()));
+  }
+
+  private static void amendApiClass(
+      Collection<ParsedApiClass> apiClasses,
+      ClassAmendment classAmendment,
+      Map<ClassReference, ParsedApiClass> classMap)
+      throws ApiDatabaseGeneratorException {
+    ClassReference classRef = classAmendment.getClassReference();
+    if (classMap.containsKey(classRef)) {
+      throw new ApiDatabaseGeneratorException("Class " + classRef + " already exists");
+    } else {
+      ParsedApiClass newClass =
+          new ParsedApiClass(classRef, new ApiRange(classAmendment.getApiLevel()));
+      apiClasses.add(newClass);
+      classMap.put(classRef, newClass);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/apimodel/ParsedApiClassFlattening.java b/src/main/java/com/android/tools/r8/apimodel/ParsedApiClassFlattening.java
index f509379..07543a0 100644
--- a/src/main/java/com/android/tools/r8/apimodel/ParsedApiClassFlattening.java
+++ b/src/main/java/com/android/tools/r8/apimodel/ParsedApiClassFlattening.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
@@ -45,7 +46,7 @@
     for (ClassReference ref : classMap.keySet()) {
       flattenClass(ref);
     }
-    return flattenedCache.values();
+    return new ArrayList<>(flattenedCache.values());
   }
 
   /** Returns a flattened class of {@code ref} with all inherited members except from Object. */
diff --git a/src/main/java/com/android/tools/r8/apimodel/ParsedApiClassMerging.java b/src/main/java/com/android/tools/r8/apimodel/ParsedApiClassMerging.java
index f1e2779..f7e961d 100644
--- a/src/main/java/com/android/tools/r8/apimodel/ParsedApiClassMerging.java
+++ b/src/main/java/com/android/tools/r8/apimodel/ParsedApiClassMerging.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.androidapi.DuplicateApiDatabaseEntryDiagnostic;
 import com.android.tools.r8.references.ClassReference;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.Map;
@@ -27,7 +28,7 @@
       throws ApiDatabaseGeneratorException {
     ParsedApiClassMerging merger = new ParsedApiClassMerging(diagnosticsHandler);
     merger.merge(parsedClasses);
-    return merger.merged.values();
+    return new ArrayList<>(merger.merged.values());
   }
 
   private void merge(Iterable<ParsedApiClass> classes) throws ApiDatabaseGeneratorException {
diff --git a/src/main/java/com/android/tools/r8/apimodel/ParsedApiClassSorting.java b/src/main/java/com/android/tools/r8/apimodel/ParsedApiClassSorting.java
new file mode 100644
index 0000000..f4fa0a4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/apimodel/ParsedApiClassSorting.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2026, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import com.android.tools.r8.ApiDatabaseGeneratorException;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.function.Function;
+
+public class ParsedApiClassSorting {
+
+  private static final Comparator<ClassReference> CLASS_REFERENCE_COMPARATOR =
+      Comparator.comparing(ClassReference::getDescriptor);
+
+  private static final Comparator<MethodReference> METHOD_REFERENCE_COMPARATOR =
+      Comparator.comparing(MethodReference::getMethodName)
+          .thenComparing(MethodReference::getMethodDescriptor)
+          .thenComparing(method -> method.getHolderClass().getDescriptor());
+
+  private static final Comparator<FieldTypelessReference> FIELD_REFERENCE_COMPARATOR =
+      Comparator.comparing(FieldTypelessReference::getFieldName)
+          .thenComparing(field -> field.getHolderClass().getDescriptor());
+
+  private static final Comparator<ParsedApiClass> PARSED_API_CLASS_COMPARATOR =
+      Comparator.comparing(ParsedApiClass::getClassReference, CLASS_REFERENCE_COMPARATOR);
+
+  public static Collection<ParsedApiClass> sorted(Collection<ParsedApiClass> apiClasses)
+      throws ApiDatabaseGeneratorException {
+    List<ParsedApiClass> sortedClasses = new ArrayList<>(apiClasses.size());
+    for (ParsedApiClass apiClass : apiClasses) {
+      sortedClasses.add(sortedClass(apiClass));
+    }
+    return uniquelySorted(
+        sortedClasses, PARSED_API_CLASS_COMPARATOR, ParsedApiClass::getClassReference);
+  }
+
+  private static ParsedApiClass sortedClass(ParsedApiClass apiClass)
+      throws ApiDatabaseGeneratorException {
+    ParsedApiClass sortedClass =
+        new ParsedApiClass(apiClass.getClassReference(), apiClass.getRange());
+
+    List<ClassReference> supertypes = new ArrayList<>();
+    apiClass.forEachSupertype((ref, range) -> supertypes.add(ref));
+    for (ClassReference supertype : uniquelySorted(supertypes, CLASS_REFERENCE_COMPARATOR)) {
+      sortedClass.registerSupertype(supertype, apiClass.getSupertypeRange(supertype));
+    }
+
+    List<ClassReference> interfaces = new ArrayList<>();
+    apiClass.forEachInterface((ref, range) -> interfaces.add(ref));
+    for (ClassReference iface : uniquelySorted(interfaces, CLASS_REFERENCE_COMPARATOR)) {
+      sortedClass.registerInterface(iface, apiClass.getInterfaceRange(iface));
+    }
+
+    List<MethodReference> methods = new ArrayList<>();
+    apiClass.forEachMethod((ref, range) -> methods.add(ref));
+    for (MethodReference method : uniquelySorted(methods, METHOD_REFERENCE_COMPARATOR)) {
+      sortedClass.registerMethod(method, apiClass.getMethodRange(method));
+    }
+
+    List<FieldTypelessReference> fields = new ArrayList<>();
+    apiClass.forEachField((ref, range) -> fields.add(ref));
+    for (FieldTypelessReference field : uniquelySorted(fields, FIELD_REFERENCE_COMPARATOR)) {
+      sortedClass.registerField(field, apiClass.getFieldRange(field));
+    }
+
+    return sortedClass;
+  }
+
+  private static <T> List<T> uniquelySorted(
+      Collection<T> items, Comparator<T> comparator, Function<T, ?> toMessage)
+      throws ApiDatabaseGeneratorException {
+    List<T> sorted = new ArrayList<>(items);
+    sorted.sort(comparator);
+    T previous = null;
+    for (T current : sorted) {
+      if (previous != null && comparator.compare(previous, current) == 0) {
+        throw new ApiDatabaseGeneratorException(
+            "Found duplicates for: " + toMessage.apply(current));
+      }
+      previous = current;
+    }
+    return sorted;
+  }
+
+  private static <T> List<T> uniquelySorted(Collection<T> items, Comparator<T> comparator)
+      throws ApiDatabaseGeneratorException {
+    return uniquelySorted(items, comparator, Object::toString);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/apimodel/ParsedApiClassTrimming.java b/src/main/java/com/android/tools/r8/apimodel/ParsedApiClassTrimming.java
index 7245ff4..ea71d6c 100644
--- a/src/main/java/com/android/tools/r8/apimodel/ParsedApiClassTrimming.java
+++ b/src/main/java/com/android/tools/r8/apimodel/ParsedApiClassTrimming.java
@@ -35,6 +35,8 @@
 
     SkipAnswer skipField(ClassReference clazz, FieldTypelessReference field, ApiRange range)
         throws E;
+
+    void done() throws E;
   }
 
   public static <E extends Throwable> Collection<ParsedApiClass> trim(
@@ -45,6 +47,7 @@
         result.add(trim(clazz, trimmer));
       }
     }
+    trimmer.done();
     return result;
   }
 
@@ -107,6 +110,11 @@
         ClassReference clazz, FieldTypelessReference field, ApiRange range) {
       return range.isRemoved() ? SkipAnswer.SKIP : SkipAnswer.KEEP;
     }
+
+    @Override
+    public void done() throws RuntimeException {
+      // Do nothing.
+    }
   }
 
   public static class JarTrimmer
@@ -117,6 +125,10 @@
       this.jarInfo = jarInfo;
     }
 
+    public ApiJarInfo getJarInfo() {
+      return jarInfo;
+    }
+
     @Override
     public SkipAnswer skipClass(ClassReference clazz, ApiRange range) {
       return SkipAnswer.skipIf(!jarInfo.hasClass(clazz));
@@ -160,6 +172,11 @@
         throws ApiDatabaseGeneratorException {
       return SkipAnswer.skipIf(!jarInfo.hasField(clazz.getBinaryName(), field.getFieldName()));
     }
+
+    @Override
+    public void done() throws ApiDatabaseGeneratorException {
+      // Do nothing.
+    }
   }
 
   public interface TrimmerListener {
@@ -176,6 +193,8 @@
 
     void skipField(
         ClassReference clazz, FieldTypelessReference field, ApiRange range, SkipAnswer answer);
+
+    void done();
   }
 
   public static class ListeningDecorator<E extends Throwable> implements Trimmer<E> {
@@ -226,5 +245,11 @@
       listener.skipField(clazz, field, range, result);
       return result;
     }
+
+    @Override
+    public void done() throws E {
+      trimmer.done();
+      listener.done();
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/apimodel/jar/ApiClassInfo.java b/src/main/java/com/android/tools/r8/apimodel/jar/ApiClassInfo.java
index 5bd8a9c..148d776 100644
--- a/src/main/java/com/android/tools/r8/apimodel/jar/ApiClassInfo.java
+++ b/src/main/java/com/android/tools/r8/apimodel/jar/ApiClassInfo.java
@@ -5,58 +5,76 @@
 package com.android.tools.r8.apimodel.jar;
 
 import com.android.tools.r8.utils.DescriptorUtils;
-import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.function.Function;
 
 public class ApiClassInfo {
-  private final String binaryName;
+  private final String internalName;
   private final String superClass;
-  private final Set<String> interfaces;
   private final boolean isInterface;
+  private final Set<String> interfaces;
   private final Set<ApiMethodInfo> methods;
   private final Set<String> fields;
 
-  public ApiClassInfo(
-      String binaryName,
+  public ApiClassInfo(String internalName, String superClass, boolean isInterface) {
+    this(internalName, superClass, isInterface, new HashSet<>(), new HashSet<>(), new HashSet<>());
+  }
+
+  private ApiClassInfo(
+      String internalName,
       String superClass,
-      Collection<String> interfaces,
       boolean isInterface,
-      Collection<ApiMethodInfo> methods,
-      Collection<String> fields) {
-    assert binaryName != null;
-    assert DescriptorUtils.isValidInternalName(binaryName);
+      Set<String> interfaces,
+      Set<ApiMethodInfo> methods,
+      Set<String> fields) {
+    assert internalName != null;
+    assert DescriptorUtils.isValidInternalName(internalName);
     assert superClass == null || DescriptorUtils.isValidInternalName(superClass);
     assert interfaces != null;
     assert interfaces.stream().allMatch(DescriptorUtils::isValidInternalName);
     assert methods != null;
     assert fields != null;
-    this.binaryName = binaryName;
+    this.internalName = internalName;
     this.superClass = superClass;
-    this.interfaces = new HashSet<>(interfaces);
+    this.interfaces = interfaces;
     this.isInterface = isInterface;
-    this.methods = new HashSet<>(methods);
-    this.fields = new HashSet<>(fields);
+    this.methods = methods;
+    this.fields = fields;
   }
 
   public String getBinaryName() {
-    return binaryName;
+    return internalName;
   }
 
   public String getSuperClass() {
     return superClass;
   }
 
-  public boolean implementsInterface(String binaryInterfaceName) {
-    return interfaces.contains(binaryInterfaceName);
+  public boolean implementsInterface(String internalInterfaceName) {
+    return interfaces.contains(internalInterfaceName);
   }
 
   public boolean isInterface() {
     return isInterface;
   }
 
-  public Iterable<String> getInterfaces() {
-    return interfaces;
+  /** Returns a read-only set. */
+  public Set<String> getInterfaces() {
+    return Collections.unmodifiableSet(interfaces);
+  }
+
+  public void addInterface(String internalInterfaceName) {
+    assert DescriptorUtils.isValidInternalName(internalInterfaceName)
+        : internalInterfaceName + " is not a valid name";
+    assert !interfaces.contains(internalInterfaceName)
+        : internalInterfaceName + " is already present";
+    interfaces.add(internalInterfaceName);
+  }
+
+  public <T> void addInterfaces(Iterable<T> interfaces, Function<T, String> getInternalName) {
+    interfaces.forEach(i -> addInterface(getInternalName.apply(i)));
   }
 
   public boolean hasMethod(String name, String descriptor) {
@@ -68,7 +86,7 @@
     ApiMethodInfo instanceMethod = new ApiMethodInfo(name, descriptor, false);
     if (methods.contains(staticMethod)) {
       assert !methods.contains(instanceMethod)
-          : this.binaryName
+          : this.internalName
               + "."
               + name
               + descriptor
@@ -82,25 +100,45 @@
   }
 
   public void addMethod(String name, String descriptor, boolean isStatic) {
-    assert !hasMethod(name, descriptor)
-        : "method already exists " + this.binaryName + "." + name + descriptor;
-    methods.add(new ApiMethodInfo(name, descriptor, isStatic));
+    addMethod(new ApiMethodInfo(name, descriptor, isStatic));
   }
 
-  public Iterable<ApiMethodInfo> getMethods() {
-    return methods;
+  public void addMethod(ApiMethodInfo methodInfo) {
+    assert !hasMethod(methodInfo.name, methodInfo.descriptor)
+        : "method already exists "
+            + this.internalName
+            + "."
+            + methodInfo.name
+            + methodInfo.descriptor;
+    methods.add(methodInfo);
+  }
+
+  /** Returns a read-only set. */
+  public Set<ApiMethodInfo> getMethods() {
+    return Collections.unmodifiableSet(methods);
   }
 
   public boolean hasField(String fieldName) {
     return fields.contains(fieldName);
   }
 
-  public Iterable<String> getFields() {
-    return fields;
+  /** Returns a read-only set. */
+  public Set<String> getFields() {
+    return Collections.unmodifiableSet(fields);
   }
 
   public void addField(String name) {
-    assert !hasField(name) : "field already exists: " + this.binaryName + "." + name;
+    assert !hasField(name) : "field already exists: " + this.internalName + "." + name;
     fields.add(name);
   }
+
+  public ApiClassInfo copy() {
+    return new ApiClassInfo(
+        internalName,
+        superClass,
+        isInterface,
+        new HashSet<>(interfaces),
+        new HashSet<>(methods),
+        new HashSet<>(fields));
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/apimodel/jar/ApiJarInfo.java b/src/main/java/com/android/tools/r8/apimodel/jar/ApiJarInfo.java
index e0296b0..ed5d5fa 100644
--- a/src/main/java/com/android/tools/r8/apimodel/jar/ApiJarInfo.java
+++ b/src/main/java/com/android/tools/r8/apimodel/jar/ApiJarInfo.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.ApiDatabaseGeneratorException;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.MethodReference;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
@@ -19,11 +20,21 @@
     this.classes = classes;
   }
 
+  public static ApiJarInfo empty() {
+    return new ApiJarInfo(new HashMap<>());
+  }
+
   public void addClass(ApiClassInfo info) {
     assert !classes.containsKey(info.getBinaryName()) : info.getBinaryName() + " already exists";
     classes.put(info.getBinaryName(), info);
   }
 
+  public void overwriteClass(ApiClassInfo info) {
+    assert classes.containsKey(info.getBinaryName())
+        : info.getBinaryName() + " does not exist, cannot overwrite it";
+    classes.put(info.getBinaryName(), info);
+  }
+
   public int size() {
     return classes.size();
   }
@@ -44,6 +55,10 @@
     return getClassInfo(classReference.getBinaryName());
   }
 
+  public Iterable<ApiClassInfo> getClasses() {
+    return classes.values();
+  }
+
   public boolean hasMethod(MethodReference methodReference) throws ApiDatabaseGeneratorException {
     return hasMethod(
         methodReference.getHolderClass().getBinaryName(),
diff --git a/src/main/java/com/android/tools/r8/apimodel/jar/ApiJarMerging.java b/src/main/java/com/android/tools/r8/apimodel/jar/ApiJarMerging.java
new file mode 100644
index 0000000..e33d8fd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/apimodel/jar/ApiJarMerging.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2026, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel.jar;
+
+import com.android.tools.r8.ApiDatabaseGeneratorException;
+import java.util.Objects;
+
+public class ApiJarMerging {
+
+  /** Merges {@code additions} into {@code jarInfo} and returns {@code jarInfo}. */
+  public static ApiJarInfo mergeJarInfo(ApiJarInfo jarInfo, ApiJarInfo additions)
+      throws ApiDatabaseGeneratorException {
+    for (ApiClassInfo additionClass : additions.getClasses()) {
+      mergeJarInfo(jarInfo, additionClass);
+    }
+    return jarInfo;
+  }
+
+  /** Returns a new info, which has all the merged info of {@param infos}. */
+  public static ApiJarInfo mergeJarInfos(Iterable<ApiJarInfo> infos)
+      throws ApiDatabaseGeneratorException {
+    ApiJarInfo info = ApiJarInfo.empty();
+    for (var i : infos) {
+      mergeJarInfo(info, i);
+    }
+    return info;
+  }
+
+  /** Merges {@code additionClass} into {@code jarInfo} and returns {@code jarInfo}. */
+  private static void mergeJarInfo(ApiJarInfo jarInfo, ApiClassInfo additionClass)
+      throws ApiDatabaseGeneratorException {
+    String binaryName = additionClass.getBinaryName();
+    ApiClassInfo originalClass = jarInfo.getClassInfo(binaryName);
+    if (originalClass == null) {
+      jarInfo.addClass(additionClass.copy());
+    } else {
+      jarInfo.overwriteClass(mergeClass(originalClass, additionClass));
+    }
+  }
+
+  /** Merged {@code addition} into {@code classInfo} and returns {@code classInfo}. */
+  private static ApiClassInfo mergeClass(ApiClassInfo classInfo, ApiClassInfo addition)
+      throws ApiDatabaseGeneratorException {
+    checkClassHeaderConsistency(classInfo, addition);
+    for (ApiMethodInfo method : addition.getMethods()) {
+      ApiMethodInfo existing = classInfo.getMethod(method.name, method.descriptor);
+      if (existing == null) {
+        classInfo.addMethod(method.name, method.descriptor, method.isStatic);
+      } else {
+        if (!existing.equals(method)) {
+          throw new ApiDatabaseGeneratorException(
+              "Overlapping inconsistent method "
+                  + method.name
+                  + method.descriptor
+                  + " in class "
+                  + classInfo.getBinaryName());
+        }
+      }
+    }
+    for (String field : addition.getFields()) {
+      if (!classInfo.hasField(field)) {
+        classInfo.addField(field);
+      }
+    }
+    return classInfo;
+  }
+
+  private static void checkClassHeaderConsistency(ApiClassInfo classInfo, ApiClassInfo addition)
+      throws ApiDatabaseGeneratorException {
+    if (classInfo.isInterface() != addition.isInterface()) {
+      throw new ApiDatabaseGeneratorException(
+          "Inconsistent isInterface for class " + classInfo.getBinaryName());
+    }
+    if (!Objects.equals(classInfo.getSuperClass(), addition.getSuperClass())) {
+      throw new ApiDatabaseGeneratorException(
+          "Inconsistent superClass for class "
+              + classInfo.getBinaryName()
+              + ": "
+              + classInfo.getSuperClass()
+              + " vs "
+              + addition.getSuperClass());
+    }
+    if (!classInfo.getInterfaces().equals(addition.getInterfaces())) {
+      throw new ApiDatabaseGeneratorException(
+          "Inconsistent interfaces for class " + classInfo.getBinaryName());
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/apimodel/jar/ApiJarReader.java b/src/main/java/com/android/tools/r8/apimodel/jar/ApiJarReader.java
index e7b9d4f..3c1e049 100644
--- a/src/main/java/com/android/tools/r8/apimodel/jar/ApiJarReader.java
+++ b/src/main/java/com/android/tools/r8/apimodel/jar/ApiJarReader.java
@@ -11,7 +11,6 @@
 import java.io.InputStream;
 import java.nio.file.Path;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -24,32 +23,29 @@
 
 public class ApiJarReader {
 
-  public static ApiJarInfo read(Collection<Path> jarPaths)
-      throws ApiDatabaseGeneratorException, IOException {
+  public static ApiJarInfo read(Path jarPath) throws ApiDatabaseGeneratorException, IOException {
     Map<String, ApiClassInfo> classes = new HashMap<>();
-    readClasses(
-        jarPaths,
+    readClass(
+        jarPath,
         (classInfo) -> {
           if (classes.containsKey(classInfo.getBinaryName())) {
             throw new ApiDatabaseGeneratorException(
-                "Duplicate classes found in JARs: " + classInfo.getBinaryName());
+                "Duplicate classes found in JAR: " + jarPath + ", " + classInfo.getBinaryName());
           }
           classes.put(classInfo.getBinaryName(), classInfo);
         });
     return new ApiJarInfo(classes);
   }
 
-  private static <E extends Throwable> void readClasses(
-      Collection<Path> jarPaths, ThrowingConsumer<ApiClassInfo, E> handler) throws E, IOException {
-    for (Path jarPath : jarPaths) {
-      ZipUtils.iterWithZipFileAndInputStream(
-          jarPath,
-          (zip, entry, input) -> {
-            if (ZipUtils.isClassFile(entry.getName())) {
-              handler.accept(readClass(input));
-            }
-          });
-    }
+  private static <E extends Throwable> void readClass(
+      Path jarPath, ThrowingConsumer<ApiClassInfo, E> handler) throws IOException, E {
+    ZipUtils.iterWithZipFileAndInputStream(
+        jarPath,
+        (zip, entry, input) -> {
+          if (ZipUtils.isClassFile(entry.getName())) {
+            handler.accept(readClass(input));
+          }
+        });
   }
 
   private static ApiClassInfo readClass(InputStream input) throws IOException {
@@ -58,13 +54,12 @@
     reader.accept(
         visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
 
-    return new ApiClassInfo(
-        visitor.className,
-        visitor.superName,
-        visitor.interfaces,
-        visitor.isInterface,
-        visitor.methods,
-        visitor.fields);
+    ApiClassInfo classInfo =
+        new ApiClassInfo(visitor.className, visitor.superName, visitor.isInterface);
+    visitor.interfaces.forEach(classInfo::addInterface);
+    visitor.methods.forEach(classInfo::addMethod);
+    visitor.fields.forEach(classInfo::addField);
+    return classInfo;
   }
 
   private static class JarClassVisitor extends ClassVisitor {
diff --git a/src/main/java/com/android/tools/r8/apimodel/jar/ApiMethodInfo.java b/src/main/java/com/android/tools/r8/apimodel/jar/ApiMethodInfo.java
index 399d101..24ad464 100644
--- a/src/main/java/com/android/tools/r8/apimodel/jar/ApiMethodInfo.java
+++ b/src/main/java/com/android/tools/r8/apimodel/jar/ApiMethodInfo.java
@@ -8,12 +8,12 @@
 
 public class ApiMethodInfo {
   public final String name;
-  public final String desc;
+  public final String descriptor;
   public final boolean isStatic;
 
-  public ApiMethodInfo(String name, String desc, boolean isStatic) {
+  public ApiMethodInfo(String name, String descriptor, boolean isStatic) {
     this.name = name;
-    this.desc = desc;
+    this.descriptor = descriptor;
     this.isStatic = isStatic;
   }
 
@@ -27,12 +27,12 @@
     }
     ApiMethodInfo methodSig = (ApiMethodInfo) o;
     return name.equals(methodSig.name)
-        && desc.equals(methodSig.desc)
+        && descriptor.equals(methodSig.descriptor)
         && isStatic == methodSig.isStatic;
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(name, desc, isStatic);
+    return Objects.hash(name, descriptor, isStatic);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index dd0e5ad..2532a28 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -127,20 +127,16 @@
 
     private void processClass(DexProgramClass clazz) {
       // Sort all annotations.
-      clazz.annotations().forEach(this::processAnnotation);
+      processAnnotationSet(clazz.annotations());
       for (DexEncodedMethod method : clazz.methods()) {
-        method.annotations().forEach(this::processAnnotation);
+        processAnnotationSet(method.annotations());
         method.getParameterAnnotations().forEach(this::processAnnotationSet);
       }
       for (DexEncodedField field : clazz.fields()) {
-        field.annotations().forEach(this::processAnnotation);
+        processAnnotationSet(field.annotations());
       }
     }
 
-    private void processAnnotation(DexAnnotation annotation) {
-      annotation.getAnnotation().sort();
-    }
-
     private void processAnnotationSet(DexAnnotationSet annotationSet) {
       annotationSet.sort(namingLens);
     }
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 6aacf26..0dd8e1f 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -243,9 +243,9 @@
   }
 
   public static int getMaxNumberOfTypes(InternalOptions options) {
-    return options.canHaveTypeIdOver32KIssue() && options.canUseMultidex()
+    return options.canHaveTypeIdOver32KIssue() && !options.alwaysAllow64KTypeIds
         ? MAX_ENTRIES_ONLY_32K
-        : MAX_ENTRIES;
+        : MAX_ENTRIES_ONLY_65535;
   }
 
   public int getMaxNumberOfTypes() {
diff --git a/src/main/java/com/android/tools/r8/graph/CachedHashValueDexItem.java b/src/main/java/com/android/tools/r8/graph/CachedHashValueDexItem.java
index eda8647..93864e4 100644
--- a/src/main/java/com/android/tools/r8/graph/CachedHashValueDexItem.java
+++ b/src/main/java/com/android/tools/r8/graph/CachedHashValueDexItem.java
@@ -40,9 +40,7 @@
     return true;
   }
 
-  @Override
   public void flushCachedValues() {
-    super.flushCachedValues();
     hash = NOT_COMPUTED_HASH_VALUE;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
index 19fbc65..e11409d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
@@ -33,7 +33,7 @@
   private static final DexAnnotationSet THE_EMPTY_ANNOTATIONS_SET = new DexAnnotationSet();
 
   public final DexAnnotation[] annotations;
-  private int sorted = UNSORTED;
+  private volatile int sorted = UNSORTED;
 
   private static void specify(StructuralSpecification<DexAnnotationSet, ?> spec) {
     spec.withItemArray(a -> a.annotations);
@@ -139,17 +139,21 @@
   }
 
   public void sort(NamingLens namingLens) {
-    if (sorted != UNSORTED) {
-      assert sorted == sortedHashCode();
-      return;
+    if (sorted == UNSORTED) {
+      synchronized (this) {
+        if (sorted == UNSORTED) {
+          Arrays.sort(
+              annotations,
+              (a, b) -> a.annotation.type.compareToWithNamingLens(b.annotation.type, namingLens));
+          for (DexAnnotation annotation : annotations) {
+            annotation.annotation.sort();
+          }
+          flushCachedValues();
+          sorted = sortedHashCode();
+        }
+      }
     }
-    Arrays.sort(
-        annotations,
-        (a, b) -> a.annotation.type.compareToWithNamingLens(b.annotation.type, namingLens));
-    for (DexAnnotation annotation : annotations) {
-      annotation.annotation.sort();
-    }
-    sorted = hashCode();
+    assert sorted == sortedHashCode();
   }
 
   public boolean hasAnnotation(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexItem.java b/src/main/java/com/android/tools/r8/graph/DexItem.java
index 928673f..ee1a6ce 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItem.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItem.java
@@ -35,10 +35,6 @@
 
   protected abstract void collectMixedSectionItems(MixedSectionCollection collection);
 
-  protected void flushCachedValues() {
-    // Overwritten in subclasses.
-  }
-
   public String toSmaliString() {
     return toString();
   }
@@ -46,5 +42,4 @@
   public String toSourceString() {
     return toString();
   }
-
 }
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
index fe16b49..ce5a8a6 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -58,7 +58,8 @@
     RewrittenPrototypeDescription protoChanges = RewrittenPrototypeDescription.none();
     if (methodProcessor.shouldApplyCodeRewritings(this)) {
       codeLens = getDefinition().getCode().getCodeLens(appView);
-      protoChanges = appView.graphLens().lookupPrototypeChangesForMethodDefinition(getReference());
+      protoChanges =
+          appView.graphLens().lookupPrototypeChangesForMethodDefinition(getReference(), codeLens);
     }
     return code.buildInliningIR(
         context,
diff --git a/src/main/java/com/android/tools/r8/graph/lens/DefaultNonIdentityGraphLens.java b/src/main/java/com/android/tools/r8/graph/lens/DefaultNonIdentityGraphLens.java
index 26376cd..5f37239 100644
--- a/src/main/java/com/android/tools/r8/graph/lens/DefaultNonIdentityGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/lens/DefaultNonIdentityGraphLens.java
@@ -89,6 +89,7 @@
       return RewrittenPrototypeDescription.none();
     }
     DexMethod previousMethodSignature = getPreviousMethodSignature(method);
-    return getPrevious().lookupPrototypeChangesForMethodDefinition(previousMethodSignature);
+    return getPrevious()
+        .lookupPrototypeChangesForMethodDefinition(previousMethodSignature, codeLens);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java b/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java
index 4c45082..608135b 100644
--- a/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java
@@ -338,6 +338,8 @@
     MethodLookupResult lookupMethod(MethodLookupResult previous);
   }
 
+  @Deprecated
+  @SuppressWarnings("InlineMeSuggester")
   public final RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
       DexMethod method) {
     return lookupPrototypeChangesForMethodDefinition(method, null);
@@ -359,6 +361,8 @@
     return lookupFieldResult(field, codeLens).getReference();
   }
 
+  @Deprecated
+  @SuppressWarnings("InlineMeSuggester")
   /** Lookup a rebound or non-rebound field reference using the current graph lens. */
   public final FieldLookupResult lookupFieldResult(DexField field) {
     // Lookup the field using the graph lens and return the lookup result.
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 2892bef..4dc3882 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
@@ -1729,6 +1729,205 @@
             new StatifyingMethodGenerator(
                 method, BackportedMethods::StringMethods_stripTrailing, "stripTrailing", type));
       }
+
+      // java.util.Arrays.
+      {
+        DexString name = factory.createString("compareUnsigned");
+
+        class ArrayCompareUnsignedMethodInfo {
+          private final DexType arrayType;
+          private final TemplateMethodFactory provider;
+          private final TemplateMethodFactory providerRange;
+
+          private ArrayCompareUnsignedMethodInfo(
+              DexType arrayType,
+              TemplateMethodFactory provider,
+              TemplateMethodFactory providerRange) {
+            this.arrayType = arrayType;
+            this.provider = provider;
+            this.providerRange = providerRange;
+          }
+        }
+
+        ImmutableList.Builder<ArrayCompareUnsignedMethodInfo> builder = ImmutableList.builder();
+        builder.add(
+            new ArrayCompareUnsignedMethodInfo(
+                factory.byteArrayType,
+                BackportedMethods::ArraysMethods_compareUnsignedByte,
+                BackportedMethods::ArraysMethods_compareUnsignedByteRange));
+        builder.add(
+            new ArrayCompareUnsignedMethodInfo(
+                factory.shortArrayType,
+                BackportedMethods::ArraysMethods_compareUnsignedShort,
+                BackportedMethods::ArraysMethods_compareUnsignedShortRange));
+        builder.add(
+            new ArrayCompareUnsignedMethodInfo(
+                factory.intArrayType,
+                BackportedMethods::ArraysMethods_compareUnsignedInt,
+                BackportedMethods::ArraysMethods_compareUnsignedIntRange));
+        builder.add(
+            new ArrayCompareUnsignedMethodInfo(
+                factory.longArrayType,
+                BackportedMethods::ArraysMethods_compareUnsignedLong,
+                BackportedMethods::ArraysMethods_compareUnsignedLongRange));
+        builder
+            .build()
+            .forEach(
+                info -> {
+                  DexProto proto =
+                      factory.createProto(factory.intType, info.arrayType, info.arrayType);
+                  DexMethod method = factory.createMethod(factory.arraysType, proto, name);
+                  addProvider(new MethodGenerator(method, info.provider));
+
+                  proto =
+                      factory.createProto(
+                          factory.intType,
+                          info.arrayType,
+                          factory.intType,
+                          factory.intType,
+                          info.arrayType,
+                          factory.intType,
+                          factory.intType);
+                  method = factory.createMethod(factory.arraysType, proto, name);
+                  addProvider(new MethodGenerator(method, info.providerRange));
+                });
+      }
+      {
+        DexString name = factory.createString("mismatch");
+
+        class ArrayMismatchMethodInfo {
+          private final DexType arrayType;
+          private final TemplateMethodFactory provider;
+          private final TemplateMethodFactory providerRange;
+
+          private ArrayMismatchMethodInfo(
+              DexType arrayType,
+              TemplateMethodFactory provider,
+              TemplateMethodFactory providerRange) {
+            this.arrayType = arrayType;
+            this.provider = provider;
+            this.providerRange = providerRange;
+          }
+        }
+
+        ImmutableList.Builder<ArrayMismatchMethodInfo> builder = ImmutableList.builder();
+        builder.add(
+            new ArrayMismatchMethodInfo(
+                factory.booleanArrayType,
+                BackportedMethods::ArraysMethods_mismatchBoolean,
+                BackportedMethods::ArraysMethods_mismatchBooleanRange));
+        builder.add(
+            new ArrayMismatchMethodInfo(
+                factory.byteArrayType,
+                BackportedMethods::ArraysMethods_mismatchByte,
+                BackportedMethods::ArraysMethods_mismatchByteRange));
+        builder.add(
+            new ArrayMismatchMethodInfo(
+                factory.charArrayType,
+                BackportedMethods::ArraysMethods_mismatchChar,
+                BackportedMethods::ArraysMethods_mismatchCharRange));
+        builder.add(
+            new ArrayMismatchMethodInfo(
+                factory.shortArrayType,
+                BackportedMethods::ArraysMethods_mismatchShort,
+                BackportedMethods::ArraysMethods_mismatchShortRange));
+        builder.add(
+            new ArrayMismatchMethodInfo(
+                factory.intArrayType,
+                BackportedMethods::ArraysMethods_mismatchInt,
+                BackportedMethods::ArraysMethods_mismatchIntRange));
+        builder.add(
+            new ArrayMismatchMethodInfo(
+                factory.longArrayType,
+                BackportedMethods::ArraysMethods_mismatchLong,
+                BackportedMethods::ArraysMethods_mismatchLongRange));
+        builder.add(
+            new ArrayMismatchMethodInfo(
+                factory.floatArrayType,
+                BackportedMethods::ArraysMethods_mismatchFloat,
+                BackportedMethods::ArraysMethods_mismatchFloatRange));
+        builder.add(
+            new ArrayMismatchMethodInfo(
+                factory.doubleArrayType,
+                BackportedMethods::ArraysMethods_mismatchDouble,
+                BackportedMethods::ArraysMethods_mismatchDoubleRange));
+        builder.add(
+            new ArrayMismatchMethodInfo(
+                factory.objectArrayType,
+                BackportedMethods::ArraysMethods_mismatchObject,
+                BackportedMethods::ArraysMethods_mismatchObjectRange));
+        builder
+            .build()
+            .forEach(
+                info -> {
+                  DexProto proto =
+                      factory.createProto(factory.intType, info.arrayType, info.arrayType);
+                  DexMethod method = factory.createMethod(factory.arraysType, proto, name);
+                  addProvider(new MethodGenerator(method, info.provider));
+
+                  proto =
+                      factory.createProto(
+                          factory.intType,
+                          info.arrayType,
+                          factory.intType,
+                          factory.intType,
+                          info.arrayType,
+                          factory.intType,
+                          factory.intType);
+                  method = factory.createMethod(factory.arraysType, proto, name);
+                  addProvider(
+                      new MethodWithHelperGenerator(
+                          method,
+                          info.providerRange,
+                          cfInvoke ->
+                              cfInvoke.getMethod().getName().toString().equals("checkValidRange"),
+                          appView
+                              .dexItemFactory()
+                              .createProto(
+                                  appView.dexItemFactory().voidType,
+                                  appView.dexItemFactory().intType,
+                                  appView.dexItemFactory().intType,
+                                  appView.dexItemFactory().intType),
+                          BackportedMethods::ArraysMethods_checkValidRange));
+                });
+
+        // mismatch(Object[], Object[], Comparator)
+        DexProto proto =
+            factory.createProto(
+                factory.intType,
+                factory.objectArrayType,
+                factory.objectArrayType,
+                factory.comparatorType);
+        DexMethod method = factory.createMethod(factory.arraysType, proto, name);
+        addProvider(
+            new MethodGenerator(method, BackportedMethods::ArraysMethods_mismatchComparator));
+
+        // mismatch(Object[], int, int, Object[], int, int, Comparator)
+        proto =
+            factory.createProto(
+                factory.intType,
+                factory.objectArrayType,
+                factory.intType,
+                factory.intType,
+                factory.objectArrayType,
+                factory.intType,
+                factory.intType,
+                factory.comparatorType);
+        method = factory.createMethod(factory.arraysType, proto, name);
+        addProvider(
+            new MethodWithHelperGenerator(
+                method,
+                BackportedMethods::ArraysMethods_mismatchComparatorRange,
+                cfInvoke -> cfInvoke.getMethod().getName().toString().equals("checkValidRange"),
+                appView
+                    .dexItemFactory()
+                    .createProto(
+                        appView.dexItemFactory().voidType,
+                        appView.dexItemFactory().intType,
+                        appView.dexItemFactory().intType,
+                        appView.dexItemFactory().intType),
+                BackportedMethods::ArraysMethods_checkValidRange));
+      }
     }
 
     private void initializeAndroidOptionalTMethodProviders(DexItemFactory factory) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
index fe90886..1c4cfad 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -71,6 +71,7 @@
     factory.createSynthesizedType("Ljava/lang/ArithmeticException;");
     factory.createSynthesizedType("Ljava/lang/ArrayIndexOutOfBoundsException;");
     factory.createSynthesizedType("Ljava/lang/AssertionError;");
+    factory.createSynthesizedType("Ljava/lang/Byte;");
     factory.createSynthesizedType("Ljava/lang/Double;");
     factory.createSynthesizedType("Ljava/lang/Exception;");
     factory.createSynthesizedType("Ljava/lang/ExceptionInInitializerError;");
@@ -91,6 +92,7 @@
     factory.createSynthesizedType("Ljava/lang/Runnable;");
     factory.createSynthesizedType("Ljava/lang/RuntimeException;");
     factory.createSynthesizedType("Ljava/lang/SecurityException;");
+    factory.createSynthesizedType("Ljava/lang/Short;");
     factory.createSynthesizedType("Ljava/lang/System;");
     factory.createSynthesizedType("Ljava/lang/Thread;");
     factory.createSynthesizedType("Ljava/lang/reflect/Constructor;");
@@ -406,6 +408,875 @@
         ImmutableList.of());
   }
 
+  public static CfCode ArraysMethods_compareUnsignedByte(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        3,
+        3,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfIfCmp(IfType.NE, ValueType.OBJECT, label2),
+            label1,
+            new CfConstNumber(0, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label2,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.initializedNonNullReference(factory.byteArrayType)
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfIf(IfType.NE, ValueType.OBJECT, label4),
+            label3,
+            new CfConstNumber(-1, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label4,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.initializedNonNullReference(factory.byteArrayType)
+                    })),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfIf(IfType.NE, ValueType.OBJECT, label6),
+            label5,
+            new CfConstNumber(1, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label6,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.initializedNonNullReference(factory.byteArrayType)
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/util/Arrays;"),
+                    factory.createProto(
+                        factory.intType, factory.byteArrayType, factory.byteArrayType),
+                    factory.createString("mismatch")),
+                false),
+            new CfStore(ValueType.INT, 2),
+            label7,
+            new CfLoad(ValueType.INT, 2),
+            new CfIf(IfType.LT, ValueType.INT, label9),
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfIfCmp(IfType.GE, ValueType.INT, label9),
+            label8,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 2),
+            new CfArrayLoad(MemberType.BOOLEAN_OR_BYTE),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfArrayLoad(MemberType.BOOLEAN_OR_BYTE),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Byte;"),
+                    factory.createProto(factory.intType, factory.byteType, factory.byteType),
+                    factory.createString("compareUnsigned")),
+                false),
+            new CfReturn(ValueType.INT),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfReturn(ValueType.INT),
+            label10),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_compareUnsignedByteRange(
+      DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        6,
+        7,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 5),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/util/Arrays;"),
+                    factory.createProto(
+                        factory.intType,
+                        factory.byteArrayType,
+                        factory.intType,
+                        factory.intType,
+                        factory.byteArrayType,
+                        factory.intType,
+                        factory.intType),
+                    factory.createString("mismatch")),
+                false),
+            new CfStore(ValueType.INT, 6),
+            label1,
+            new CfLoad(ValueType.INT, 6),
+            new CfIf(IfType.LT, ValueType.INT, label3),
+            new CfLoad(ValueType.INT, 6),
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.INT, 1),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfLoad(ValueType.INT, 5),
+            new CfLoad(ValueType.INT, 4),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfIfCmp(IfType.GE, ValueType.INT, label3),
+            label2,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 6),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.BOOLEAN_OR_BYTE),
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 6),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.BOOLEAN_OR_BYTE),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Byte;"),
+                    factory.createProto(factory.intType, factory.byteType, factory.byteType),
+                    factory.createString("compareUnsigned")),
+                false),
+            new CfReturn(ValueType.INT),
+            label3,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.INT, 1),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfLoad(ValueType.INT, 5),
+            new CfLoad(ValueType.INT, 4),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfReturn(ValueType.INT),
+            label4),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_compareUnsignedInt(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        3,
+        3,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfIfCmp(IfType.NE, ValueType.OBJECT, label2),
+            label1,
+            new CfConstNumber(0, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label2,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.initializedNonNullReference(factory.intArrayType)
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfIf(IfType.NE, ValueType.OBJECT, label4),
+            label3,
+            new CfConstNumber(-1, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label4,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.initializedNonNullReference(factory.intArrayType)
+                    })),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfIf(IfType.NE, ValueType.OBJECT, label6),
+            label5,
+            new CfConstNumber(1, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label6,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.initializedNonNullReference(factory.intArrayType)
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/util/Arrays;"),
+                    factory.createProto(
+                        factory.intType, factory.intArrayType, factory.intArrayType),
+                    factory.createString("mismatch")),
+                false),
+            new CfStore(ValueType.INT, 2),
+            label7,
+            new CfLoad(ValueType.INT, 2),
+            new CfIf(IfType.LT, ValueType.INT, label9),
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfIfCmp(IfType.GE, ValueType.INT, label9),
+            label8,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 2),
+            new CfArrayLoad(MemberType.INT),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfArrayLoad(MemberType.INT),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Integer;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("compareUnsigned")),
+                false),
+            new CfReturn(ValueType.INT),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfReturn(ValueType.INT),
+            label10),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_compareUnsignedIntRange(
+      DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        6,
+        7,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 5),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/util/Arrays;"),
+                    factory.createProto(
+                        factory.intType,
+                        factory.intArrayType,
+                        factory.intType,
+                        factory.intType,
+                        factory.intArrayType,
+                        factory.intType,
+                        factory.intType),
+                    factory.createString("mismatch")),
+                false),
+            new CfStore(ValueType.INT, 6),
+            label1,
+            new CfLoad(ValueType.INT, 6),
+            new CfIf(IfType.LT, ValueType.INT, label3),
+            new CfLoad(ValueType.INT, 6),
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.INT, 1),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfLoad(ValueType.INT, 5),
+            new CfLoad(ValueType.INT, 4),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfIfCmp(IfType.GE, ValueType.INT, label3),
+            label2,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 6),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.INT),
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 6),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.INT),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Integer;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("compareUnsigned")),
+                false),
+            new CfReturn(ValueType.INT),
+            label3,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.INT, 1),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfLoad(ValueType.INT, 5),
+            new CfLoad(ValueType.INT, 4),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfReturn(ValueType.INT),
+            label4),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_compareUnsignedLong(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        4,
+        3,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfIfCmp(IfType.NE, ValueType.OBJECT, label2),
+            label1,
+            new CfConstNumber(0, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label2,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.initializedNonNullReference(factory.longArrayType)
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfIf(IfType.NE, ValueType.OBJECT, label4),
+            label3,
+            new CfConstNumber(-1, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label4,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.initializedNonNullReference(factory.longArrayType)
+                    })),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfIf(IfType.NE, ValueType.OBJECT, label6),
+            label5,
+            new CfConstNumber(1, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label6,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.initializedNonNullReference(factory.longArrayType)
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/util/Arrays;"),
+                    factory.createProto(
+                        factory.intType, factory.longArrayType, factory.longArrayType),
+                    factory.createString("mismatch")),
+                false),
+            new CfStore(ValueType.INT, 2),
+            label7,
+            new CfLoad(ValueType.INT, 2),
+            new CfIf(IfType.LT, ValueType.INT, label9),
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfIfCmp(IfType.GE, ValueType.INT, label9),
+            label8,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 2),
+            new CfArrayLoad(MemberType.LONG),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfArrayLoad(MemberType.LONG),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Long;"),
+                    factory.createProto(factory.intType, factory.longType, factory.longType),
+                    factory.createString("compareUnsigned")),
+                false),
+            new CfReturn(ValueType.INT),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfReturn(ValueType.INT),
+            label10),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_compareUnsignedLongRange(
+      DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        6,
+        7,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 5),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/util/Arrays;"),
+                    factory.createProto(
+                        factory.intType,
+                        factory.longArrayType,
+                        factory.intType,
+                        factory.intType,
+                        factory.longArrayType,
+                        factory.intType,
+                        factory.intType),
+                    factory.createString("mismatch")),
+                false),
+            new CfStore(ValueType.INT, 6),
+            label1,
+            new CfLoad(ValueType.INT, 6),
+            new CfIf(IfType.LT, ValueType.INT, label3),
+            new CfLoad(ValueType.INT, 6),
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.INT, 1),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfLoad(ValueType.INT, 5),
+            new CfLoad(ValueType.INT, 4),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfIfCmp(IfType.GE, ValueType.INT, label3),
+            label2,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 6),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.LONG),
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 6),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.LONG),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Long;"),
+                    factory.createProto(factory.intType, factory.longType, factory.longType),
+                    factory.createString("compareUnsigned")),
+                false),
+            new CfReturn(ValueType.INT),
+            label3,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.INT, 1),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfLoad(ValueType.INT, 5),
+            new CfLoad(ValueType.INT, 4),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfReturn(ValueType.INT),
+            label4),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_compareUnsignedShort(
+      DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        3,
+        3,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfIfCmp(IfType.NE, ValueType.OBJECT, label2),
+            label1,
+            new CfConstNumber(0, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label2,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.initializedNonNullReference(factory.shortArrayType)
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfIf(IfType.NE, ValueType.OBJECT, label4),
+            label3,
+            new CfConstNumber(-1, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label4,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.initializedNonNullReference(factory.shortArrayType)
+                    })),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfIf(IfType.NE, ValueType.OBJECT, label6),
+            label5,
+            new CfConstNumber(1, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label6,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.initializedNonNullReference(factory.shortArrayType)
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/util/Arrays;"),
+                    factory.createProto(
+                        factory.intType, factory.shortArrayType, factory.shortArrayType),
+                    factory.createString("mismatch")),
+                false),
+            new CfStore(ValueType.INT, 2),
+            label7,
+            new CfLoad(ValueType.INT, 2),
+            new CfIf(IfType.LT, ValueType.INT, label9),
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfIfCmp(IfType.GE, ValueType.INT, label9),
+            label8,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 2),
+            new CfArrayLoad(MemberType.SHORT),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfArrayLoad(MemberType.SHORT),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Short;"),
+                    factory.createProto(factory.intType, factory.shortType, factory.shortType),
+                    factory.createString("compareUnsigned")),
+                false),
+            new CfReturn(ValueType.INT),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfReturn(ValueType.INT),
+            label10),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_compareUnsignedShortRange(
+      DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        6,
+        7,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 5),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/util/Arrays;"),
+                    factory.createProto(
+                        factory.intType,
+                        factory.shortArrayType,
+                        factory.intType,
+                        factory.intType,
+                        factory.shortArrayType,
+                        factory.intType,
+                        factory.intType),
+                    factory.createString("mismatch")),
+                false),
+            new CfStore(ValueType.INT, 6),
+            label1,
+            new CfLoad(ValueType.INT, 6),
+            new CfIf(IfType.LT, ValueType.INT, label3),
+            new CfLoad(ValueType.INT, 6),
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.INT, 1),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfLoad(ValueType.INT, 5),
+            new CfLoad(ValueType.INT, 4),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfIfCmp(IfType.GE, ValueType.INT, label3),
+            label2,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 6),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.SHORT),
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 6),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.SHORT),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Short;"),
+                    factory.createProto(factory.intType, factory.shortType, factory.shortType),
+                    factory.createString("compareUnsigned")),
+                false),
+            new CfReturn(ValueType.INT),
+            label3,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.INT, 1),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfLoad(ValueType.INT, 5),
+            new CfLoad(ValueType.INT, 4),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfReturn(ValueType.INT),
+            label4),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
   public static CfCode ArraysMethods_equalsBoolean(DexItemFactory factory, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
@@ -2634,6 +3505,3258 @@
         ImmutableList.of());
   }
 
+  public static CfCode ArraysMethods_mismatchBoolean(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        3,
+        4,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfStore(ValueType.INT, 2),
+            label1,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfIfCmp(IfType.NE, ValueType.OBJECT, label3),
+            label2,
+            new CfConstNumber(-1, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label3,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.booleanArrayType),
+                      FrameType.initializedNonNullReference(factory.booleanArrayType),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 3),
+            label4,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.booleanArrayType),
+                      FrameType.initializedNonNullReference(factory.booleanArrayType),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 3),
+            new CfLoad(ValueType.INT, 2),
+            new CfIfCmp(IfType.GE, ValueType.INT, label8),
+            label5,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 3),
+            new CfArrayLoad(MemberType.BOOLEAN_OR_BYTE),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.INT, 3),
+            new CfArrayLoad(MemberType.BOOLEAN_OR_BYTE),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label7),
+            label6,
+            new CfLoad(ValueType.INT, 3),
+            new CfReturn(ValueType.INT),
+            label7,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.booleanArrayType),
+                      FrameType.initializedNonNullReference(factory.booleanArrayType),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfIinc(3, 1),
+            new CfGoto(label4),
+            label8,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.booleanArrayType),
+                      FrameType.initializedNonNullReference(factory.booleanArrayType),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label9),
+            new CfLoad(ValueType.INT, 2),
+            new CfGoto(label10),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.booleanArrayType),
+                      FrameType.initializedNonNullReference(factory.booleanArrayType),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(-1, ValueType.INT),
+            label10,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.booleanArrayType),
+                      FrameType.initializedNonNullReference(factory.booleanArrayType),
+                      FrameType.intType()
+                    }),
+                new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
+            new CfReturn(ValueType.INT),
+            label11),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_mismatchBooleanRange(
+      DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    CfLabel label12 = new CfLabel();
+    CfLabel label13 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        4,
+        10,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/ir/desugar/backports/ArraysMethods;"),
+                    factory.createProto(
+                        factory.voidType, factory.intType, factory.intType, factory.intType),
+                    factory.createString("checkValidRange")),
+                false),
+            label1,
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfArrayLength(),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 5),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/ir/desugar/backports/ArraysMethods;"),
+                    factory.createProto(
+                        factory.voidType, factory.intType, factory.intType, factory.intType),
+                    factory.createString("checkValidRange")),
+                false),
+            label2,
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.INT, 1),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfStore(ValueType.INT, 6),
+            label3,
+            new CfLoad(ValueType.INT, 5),
+            new CfLoad(ValueType.INT, 4),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfStore(ValueType.INT, 7),
+            label4,
+            new CfLoad(ValueType.INT, 6),
+            new CfLoad(ValueType.INT, 7),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfStore(ValueType.INT, 8),
+            label5,
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 9),
+            label6,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.booleanArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.booleanArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 9),
+            new CfLoad(ValueType.INT, 8),
+            new CfIfCmp(IfType.GE, ValueType.INT, label10),
+            label7,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 9),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.BOOLEAN_OR_BYTE),
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 9),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.BOOLEAN_OR_BYTE),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label9),
+            label8,
+            new CfLoad(ValueType.INT, 9),
+            new CfReturn(ValueType.INT),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.booleanArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.booleanArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfIinc(9, 1),
+            new CfGoto(label6),
+            label10,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.booleanArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.booleanArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 6),
+            new CfLoad(ValueType.INT, 7),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label11),
+            new CfLoad(ValueType.INT, 8),
+            new CfGoto(label12),
+            label11,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.booleanArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.booleanArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(-1, ValueType.INT),
+            label12,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.booleanArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.booleanArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    }),
+                new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
+            new CfReturn(ValueType.INT),
+            label13),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_mismatchByte(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        3,
+        4,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfStore(ValueType.INT, 2),
+            label1,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfIfCmp(IfType.NE, ValueType.OBJECT, label3),
+            label2,
+            new CfConstNumber(-1, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label3,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 3),
+            label4,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 3),
+            new CfLoad(ValueType.INT, 2),
+            new CfIfCmp(IfType.GE, ValueType.INT, label8),
+            label5,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 3),
+            new CfArrayLoad(MemberType.BOOLEAN_OR_BYTE),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.INT, 3),
+            new CfArrayLoad(MemberType.BOOLEAN_OR_BYTE),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label7),
+            label6,
+            new CfLoad(ValueType.INT, 3),
+            new CfReturn(ValueType.INT),
+            label7,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfIinc(3, 1),
+            new CfGoto(label4),
+            label8,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label9),
+            new CfLoad(ValueType.INT, 2),
+            new CfGoto(label10),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(-1, ValueType.INT),
+            label10,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.intType()
+                    }),
+                new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
+            new CfReturn(ValueType.INT),
+            label11),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_mismatchByteRange(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    CfLabel label12 = new CfLabel();
+    CfLabel label13 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        4,
+        10,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/ir/desugar/backports/ArraysMethods;"),
+                    factory.createProto(
+                        factory.voidType, factory.intType, factory.intType, factory.intType),
+                    factory.createString("checkValidRange")),
+                false),
+            label1,
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfArrayLength(),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 5),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/ir/desugar/backports/ArraysMethods;"),
+                    factory.createProto(
+                        factory.voidType, factory.intType, factory.intType, factory.intType),
+                    factory.createString("checkValidRange")),
+                false),
+            label2,
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.INT, 1),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfStore(ValueType.INT, 6),
+            label3,
+            new CfLoad(ValueType.INT, 5),
+            new CfLoad(ValueType.INT, 4),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfStore(ValueType.INT, 7),
+            label4,
+            new CfLoad(ValueType.INT, 6),
+            new CfLoad(ValueType.INT, 7),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfStore(ValueType.INT, 8),
+            label5,
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 9),
+            label6,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 9),
+            new CfLoad(ValueType.INT, 8),
+            new CfIfCmp(IfType.GE, ValueType.INT, label10),
+            label7,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 9),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.BOOLEAN_OR_BYTE),
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 9),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.BOOLEAN_OR_BYTE),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label9),
+            label8,
+            new CfLoad(ValueType.INT, 9),
+            new CfReturn(ValueType.INT),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfIinc(9, 1),
+            new CfGoto(label6),
+            label10,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 6),
+            new CfLoad(ValueType.INT, 7),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label11),
+            new CfLoad(ValueType.INT, 8),
+            new CfGoto(label12),
+            label11,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(-1, ValueType.INT),
+            label12,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.byteArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    }),
+                new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
+            new CfReturn(ValueType.INT),
+            label13),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_mismatchChar(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        3,
+        4,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfStore(ValueType.INT, 2),
+            label1,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfIfCmp(IfType.NE, ValueType.OBJECT, label3),
+            label2,
+            new CfConstNumber(-1, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label3,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.charArrayType),
+                      FrameType.initializedNonNullReference(factory.charArrayType),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 3),
+            label4,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.charArrayType),
+                      FrameType.initializedNonNullReference(factory.charArrayType),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 3),
+            new CfLoad(ValueType.INT, 2),
+            new CfIfCmp(IfType.GE, ValueType.INT, label8),
+            label5,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 3),
+            new CfArrayLoad(MemberType.CHAR),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.INT, 3),
+            new CfArrayLoad(MemberType.CHAR),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label7),
+            label6,
+            new CfLoad(ValueType.INT, 3),
+            new CfReturn(ValueType.INT),
+            label7,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.charArrayType),
+                      FrameType.initializedNonNullReference(factory.charArrayType),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfIinc(3, 1),
+            new CfGoto(label4),
+            label8,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.charArrayType),
+                      FrameType.initializedNonNullReference(factory.charArrayType),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label9),
+            new CfLoad(ValueType.INT, 2),
+            new CfGoto(label10),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.charArrayType),
+                      FrameType.initializedNonNullReference(factory.charArrayType),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(-1, ValueType.INT),
+            label10,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.charArrayType),
+                      FrameType.initializedNonNullReference(factory.charArrayType),
+                      FrameType.intType()
+                    }),
+                new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
+            new CfReturn(ValueType.INT),
+            label11),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_mismatchCharRange(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    CfLabel label12 = new CfLabel();
+    CfLabel label13 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        4,
+        10,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/ir/desugar/backports/ArraysMethods;"),
+                    factory.createProto(
+                        factory.voidType, factory.intType, factory.intType, factory.intType),
+                    factory.createString("checkValidRange")),
+                false),
+            label1,
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfArrayLength(),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 5),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/ir/desugar/backports/ArraysMethods;"),
+                    factory.createProto(
+                        factory.voidType, factory.intType, factory.intType, factory.intType),
+                    factory.createString("checkValidRange")),
+                false),
+            label2,
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.INT, 1),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfStore(ValueType.INT, 6),
+            label3,
+            new CfLoad(ValueType.INT, 5),
+            new CfLoad(ValueType.INT, 4),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfStore(ValueType.INT, 7),
+            label4,
+            new CfLoad(ValueType.INT, 6),
+            new CfLoad(ValueType.INT, 7),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfStore(ValueType.INT, 8),
+            label5,
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 9),
+            label6,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.charArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.charArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 9),
+            new CfLoad(ValueType.INT, 8),
+            new CfIfCmp(IfType.GE, ValueType.INT, label10),
+            label7,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 9),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.CHAR),
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 9),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.CHAR),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label9),
+            label8,
+            new CfLoad(ValueType.INT, 9),
+            new CfReturn(ValueType.INT),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.charArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.charArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfIinc(9, 1),
+            new CfGoto(label6),
+            label10,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.charArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.charArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 6),
+            new CfLoad(ValueType.INT, 7),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label11),
+            new CfLoad(ValueType.INT, 8),
+            new CfGoto(label12),
+            label11,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.charArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.charArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(-1, ValueType.INT),
+            label12,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.charArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.charArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    }),
+                new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
+            new CfReturn(ValueType.INT),
+            label13),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_mismatchComparator(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    CfLabel label12 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        4,
+        5,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 2),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/util/Objects;"),
+                    factory.createProto(factory.objectType, factory.objectType),
+                    factory.createString("requireNonNull")),
+                false),
+            new CfStackInstruction(CfStackInstruction.Opcode.Pop),
+            label1,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfStore(ValueType.INT, 3),
+            label2,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfIfCmp(IfType.NE, ValueType.OBJECT, label4),
+            label3,
+            new CfConstNumber(-1, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label4,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("Ljava/util/Comparator;")),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 4),
+            label5,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("Ljava/util/Comparator;")),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 3),
+            new CfIfCmp(IfType.GE, ValueType.INT, label9),
+            label6,
+            new CfLoad(ValueType.OBJECT, 2),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 4),
+            new CfArrayLoad(MemberType.OBJECT),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.INT, 4),
+            new CfArrayLoad(MemberType.OBJECT),
+            new CfInvoke(
+                185,
+                factory.createMethod(
+                    factory.createType("Ljava/util/Comparator;"),
+                    factory.createProto(factory.intType, factory.objectType, factory.objectType),
+                    factory.createString("compare")),
+                true),
+            new CfIf(IfType.EQ, ValueType.INT, label8),
+            label7,
+            new CfLoad(ValueType.INT, 4),
+            new CfReturn(ValueType.INT),
+            label8,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("Ljava/util/Comparator;")),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfIinc(4, 1),
+            new CfGoto(label5),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("Ljava/util/Comparator;")),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label10),
+            new CfLoad(ValueType.INT, 3),
+            new CfGoto(label11),
+            label10,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("Ljava/util/Comparator;")),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(-1, ValueType.INT),
+            label11,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("Ljava/util/Comparator;")),
+                      FrameType.intType()
+                    }),
+                new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
+            new CfReturn(ValueType.INT),
+            label12),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_mismatchComparatorRange(
+      DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    CfLabel label12 = new CfLabel();
+    CfLabel label13 = new CfLabel();
+    CfLabel label14 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        5,
+        11,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 6),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/util/Objects;"),
+                    factory.createProto(factory.objectType, factory.objectType),
+                    factory.createString("requireNonNull")),
+                false),
+            new CfStackInstruction(CfStackInstruction.Opcode.Pop),
+            label1,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/ir/desugar/backports/ArraysMethods;"),
+                    factory.createProto(
+                        factory.voidType, factory.intType, factory.intType, factory.intType),
+                    factory.createString("checkValidRange")),
+                false),
+            label2,
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfArrayLength(),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 5),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/ir/desugar/backports/ArraysMethods;"),
+                    factory.createProto(
+                        factory.voidType, factory.intType, factory.intType, factory.intType),
+                    factory.createString("checkValidRange")),
+                false),
+            label3,
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.INT, 1),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfStore(ValueType.INT, 7),
+            label4,
+            new CfLoad(ValueType.INT, 5),
+            new CfLoad(ValueType.INT, 4),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfStore(ValueType.INT, 8),
+            label5,
+            new CfLoad(ValueType.INT, 7),
+            new CfLoad(ValueType.INT, 8),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfStore(ValueType.INT, 9),
+            label6,
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 10),
+            label7,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("Ljava/util/Comparator;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 10),
+            new CfLoad(ValueType.INT, 9),
+            new CfIfCmp(IfType.GE, ValueType.INT, label11),
+            label8,
+            new CfLoad(ValueType.OBJECT, 6),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 10),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.OBJECT),
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 10),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.OBJECT),
+            new CfInvoke(
+                185,
+                factory.createMethod(
+                    factory.createType("Ljava/util/Comparator;"),
+                    factory.createProto(factory.intType, factory.objectType, factory.objectType),
+                    factory.createString("compare")),
+                true),
+            new CfIf(IfType.EQ, ValueType.INT, label10),
+            label9,
+            new CfLoad(ValueType.INT, 10),
+            new CfReturn(ValueType.INT),
+            label10,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("Ljava/util/Comparator;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfIinc(10, 1),
+            new CfGoto(label7),
+            label11,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("Ljava/util/Comparator;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 7),
+            new CfLoad(ValueType.INT, 8),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label12),
+            new CfLoad(ValueType.INT, 9),
+            new CfGoto(label13),
+            label12,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("Ljava/util/Comparator;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(-1, ValueType.INT),
+            label13,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("Ljava/util/Comparator;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    }),
+                new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
+            new CfReturn(ValueType.INT),
+            label14),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_mismatchDouble(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        4,
+        4,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfStore(ValueType.INT, 2),
+            label1,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfIfCmp(IfType.NE, ValueType.OBJECT, label3),
+            label2,
+            new CfConstNumber(-1, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label3,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.doubleArrayType),
+                      FrameType.initializedNonNullReference(factory.doubleArrayType),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 3),
+            label4,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.doubleArrayType),
+                      FrameType.initializedNonNullReference(factory.doubleArrayType),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 3),
+            new CfLoad(ValueType.INT, 2),
+            new CfIfCmp(IfType.GE, ValueType.INT, label8),
+            label5,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 3),
+            new CfArrayLoad(MemberType.DOUBLE),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Double;"),
+                    factory.createProto(factory.longType, factory.doubleType),
+                    factory.createString("doubleToLongBits")),
+                false),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.INT, 3),
+            new CfArrayLoad(MemberType.DOUBLE),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Double;"),
+                    factory.createProto(factory.longType, factory.doubleType),
+                    factory.createString("doubleToLongBits")),
+                false),
+            new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
+            new CfIf(IfType.EQ, ValueType.INT, label7),
+            label6,
+            new CfLoad(ValueType.INT, 3),
+            new CfReturn(ValueType.INT),
+            label7,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.doubleArrayType),
+                      FrameType.initializedNonNullReference(factory.doubleArrayType),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfIinc(3, 1),
+            new CfGoto(label4),
+            label8,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.doubleArrayType),
+                      FrameType.initializedNonNullReference(factory.doubleArrayType),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label9),
+            new CfLoad(ValueType.INT, 2),
+            new CfGoto(label10),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.doubleArrayType),
+                      FrameType.initializedNonNullReference(factory.doubleArrayType),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(-1, ValueType.INT),
+            label10,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.doubleArrayType),
+                      FrameType.initializedNonNullReference(factory.doubleArrayType),
+                      FrameType.intType()
+                    }),
+                new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
+            new CfReturn(ValueType.INT),
+            label11),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_mismatchDoubleRange(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    CfLabel label12 = new CfLabel();
+    CfLabel label13 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        5,
+        10,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/ir/desugar/backports/ArraysMethods;"),
+                    factory.createProto(
+                        factory.voidType, factory.intType, factory.intType, factory.intType),
+                    factory.createString("checkValidRange")),
+                false),
+            label1,
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfArrayLength(),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 5),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/ir/desugar/backports/ArraysMethods;"),
+                    factory.createProto(
+                        factory.voidType, factory.intType, factory.intType, factory.intType),
+                    factory.createString("checkValidRange")),
+                false),
+            label2,
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.INT, 1),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfStore(ValueType.INT, 6),
+            label3,
+            new CfLoad(ValueType.INT, 5),
+            new CfLoad(ValueType.INT, 4),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfStore(ValueType.INT, 7),
+            label4,
+            new CfLoad(ValueType.INT, 6),
+            new CfLoad(ValueType.INT, 7),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfStore(ValueType.INT, 8),
+            label5,
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 9),
+            label6,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.doubleArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.doubleArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 9),
+            new CfLoad(ValueType.INT, 8),
+            new CfIfCmp(IfType.GE, ValueType.INT, label10),
+            label7,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 9),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.DOUBLE),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Double;"),
+                    factory.createProto(factory.longType, factory.doubleType),
+                    factory.createString("doubleToLongBits")),
+                false),
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 9),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.DOUBLE),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Double;"),
+                    factory.createProto(factory.longType, factory.doubleType),
+                    factory.createString("doubleToLongBits")),
+                false),
+            new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
+            new CfIf(IfType.EQ, ValueType.INT, label9),
+            label8,
+            new CfLoad(ValueType.INT, 9),
+            new CfReturn(ValueType.INT),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.doubleArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.doubleArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfIinc(9, 1),
+            new CfGoto(label6),
+            label10,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.doubleArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.doubleArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 6),
+            new CfLoad(ValueType.INT, 7),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label11),
+            new CfLoad(ValueType.INT, 8),
+            new CfGoto(label12),
+            label11,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.doubleArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.doubleArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(-1, ValueType.INT),
+            label12,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.doubleArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.doubleArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    }),
+                new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
+            new CfReturn(ValueType.INT),
+            label13),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_mismatchFloat(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        3,
+        4,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfStore(ValueType.INT, 2),
+            label1,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfIfCmp(IfType.NE, ValueType.OBJECT, label3),
+            label2,
+            new CfConstNumber(-1, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label3,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.floatArrayType),
+                      FrameType.initializedNonNullReference(factory.floatArrayType),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 3),
+            label4,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.floatArrayType),
+                      FrameType.initializedNonNullReference(factory.floatArrayType),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 3),
+            new CfLoad(ValueType.INT, 2),
+            new CfIfCmp(IfType.GE, ValueType.INT, label8),
+            label5,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 3),
+            new CfArrayLoad(MemberType.FLOAT),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Float;"),
+                    factory.createProto(factory.intType, factory.floatType),
+                    factory.createString("floatToIntBits")),
+                false),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.INT, 3),
+            new CfArrayLoad(MemberType.FLOAT),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Float;"),
+                    factory.createProto(factory.intType, factory.floatType),
+                    factory.createString("floatToIntBits")),
+                false),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label7),
+            label6,
+            new CfLoad(ValueType.INT, 3),
+            new CfReturn(ValueType.INT),
+            label7,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.floatArrayType),
+                      FrameType.initializedNonNullReference(factory.floatArrayType),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfIinc(3, 1),
+            new CfGoto(label4),
+            label8,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.floatArrayType),
+                      FrameType.initializedNonNullReference(factory.floatArrayType),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label9),
+            new CfLoad(ValueType.INT, 2),
+            new CfGoto(label10),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.floatArrayType),
+                      FrameType.initializedNonNullReference(factory.floatArrayType),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(-1, ValueType.INT),
+            label10,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.floatArrayType),
+                      FrameType.initializedNonNullReference(factory.floatArrayType),
+                      FrameType.intType()
+                    }),
+                new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
+            new CfReturn(ValueType.INT),
+            label11),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_mismatchFloatRange(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    CfLabel label12 = new CfLabel();
+    CfLabel label13 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        4,
+        10,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/ir/desugar/backports/ArraysMethods;"),
+                    factory.createProto(
+                        factory.voidType, factory.intType, factory.intType, factory.intType),
+                    factory.createString("checkValidRange")),
+                false),
+            label1,
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfArrayLength(),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 5),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/ir/desugar/backports/ArraysMethods;"),
+                    factory.createProto(
+                        factory.voidType, factory.intType, factory.intType, factory.intType),
+                    factory.createString("checkValidRange")),
+                false),
+            label2,
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.INT, 1),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfStore(ValueType.INT, 6),
+            label3,
+            new CfLoad(ValueType.INT, 5),
+            new CfLoad(ValueType.INT, 4),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfStore(ValueType.INT, 7),
+            label4,
+            new CfLoad(ValueType.INT, 6),
+            new CfLoad(ValueType.INT, 7),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfStore(ValueType.INT, 8),
+            label5,
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 9),
+            label6,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.floatArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.floatArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 9),
+            new CfLoad(ValueType.INT, 8),
+            new CfIfCmp(IfType.GE, ValueType.INT, label10),
+            label7,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 9),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.FLOAT),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Float;"),
+                    factory.createProto(factory.intType, factory.floatType),
+                    factory.createString("floatToIntBits")),
+                false),
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 9),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.FLOAT),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Float;"),
+                    factory.createProto(factory.intType, factory.floatType),
+                    factory.createString("floatToIntBits")),
+                false),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label9),
+            label8,
+            new CfLoad(ValueType.INT, 9),
+            new CfReturn(ValueType.INT),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.floatArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.floatArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfIinc(9, 1),
+            new CfGoto(label6),
+            label10,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.floatArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.floatArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 6),
+            new CfLoad(ValueType.INT, 7),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label11),
+            new CfLoad(ValueType.INT, 8),
+            new CfGoto(label12),
+            label11,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.floatArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.floatArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(-1, ValueType.INT),
+            label12,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.floatArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.floatArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    }),
+                new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
+            new CfReturn(ValueType.INT),
+            label13),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_mismatchInt(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        3,
+        4,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfStore(ValueType.INT, 2),
+            label1,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfIfCmp(IfType.NE, ValueType.OBJECT, label3),
+            label2,
+            new CfConstNumber(-1, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label3,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 3),
+            label4,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 3),
+            new CfLoad(ValueType.INT, 2),
+            new CfIfCmp(IfType.GE, ValueType.INT, label8),
+            label5,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 3),
+            new CfArrayLoad(MemberType.INT),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.INT, 3),
+            new CfArrayLoad(MemberType.INT),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label7),
+            label6,
+            new CfLoad(ValueType.INT, 3),
+            new CfReturn(ValueType.INT),
+            label7,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfIinc(3, 1),
+            new CfGoto(label4),
+            label8,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label9),
+            new CfLoad(ValueType.INT, 2),
+            new CfGoto(label10),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(-1, ValueType.INT),
+            label10,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.intType()
+                    }),
+                new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
+            new CfReturn(ValueType.INT),
+            label11),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_mismatchIntRange(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    CfLabel label12 = new CfLabel();
+    CfLabel label13 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        4,
+        10,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/ir/desugar/backports/ArraysMethods;"),
+                    factory.createProto(
+                        factory.voidType, factory.intType, factory.intType, factory.intType),
+                    factory.createString("checkValidRange")),
+                false),
+            label1,
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfArrayLength(),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 5),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/ir/desugar/backports/ArraysMethods;"),
+                    factory.createProto(
+                        factory.voidType, factory.intType, factory.intType, factory.intType),
+                    factory.createString("checkValidRange")),
+                false),
+            label2,
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.INT, 1),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfStore(ValueType.INT, 6),
+            label3,
+            new CfLoad(ValueType.INT, 5),
+            new CfLoad(ValueType.INT, 4),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfStore(ValueType.INT, 7),
+            label4,
+            new CfLoad(ValueType.INT, 6),
+            new CfLoad(ValueType.INT, 7),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfStore(ValueType.INT, 8),
+            label5,
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 9),
+            label6,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 9),
+            new CfLoad(ValueType.INT, 8),
+            new CfIfCmp(IfType.GE, ValueType.INT, label10),
+            label7,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 9),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.INT),
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 9),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.INT),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label9),
+            label8,
+            new CfLoad(ValueType.INT, 9),
+            new CfReturn(ValueType.INT),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfIinc(9, 1),
+            new CfGoto(label6),
+            label10,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 6),
+            new CfLoad(ValueType.INT, 7),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label11),
+            new CfLoad(ValueType.INT, 8),
+            new CfGoto(label12),
+            label11,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(-1, ValueType.INT),
+            label12,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.intArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    }),
+                new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
+            new CfReturn(ValueType.INT),
+            label13),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_mismatchLong(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        4,
+        4,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfStore(ValueType.INT, 2),
+            label1,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfIfCmp(IfType.NE, ValueType.OBJECT, label3),
+            label2,
+            new CfConstNumber(-1, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label3,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 3),
+            label4,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 3),
+            new CfLoad(ValueType.INT, 2),
+            new CfIfCmp(IfType.GE, ValueType.INT, label8),
+            label5,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 3),
+            new CfArrayLoad(MemberType.LONG),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.INT, 3),
+            new CfArrayLoad(MemberType.LONG),
+            new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
+            new CfIf(IfType.EQ, ValueType.INT, label7),
+            label6,
+            new CfLoad(ValueType.INT, 3),
+            new CfReturn(ValueType.INT),
+            label7,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfIinc(3, 1),
+            new CfGoto(label4),
+            label8,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label9),
+            new CfLoad(ValueType.INT, 2),
+            new CfGoto(label10),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(-1, ValueType.INT),
+            label10,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.intType()
+                    }),
+                new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
+            new CfReturn(ValueType.INT),
+            label11),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_mismatchLongRange(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    CfLabel label12 = new CfLabel();
+    CfLabel label13 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        5,
+        10,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/ir/desugar/backports/ArraysMethods;"),
+                    factory.createProto(
+                        factory.voidType, factory.intType, factory.intType, factory.intType),
+                    factory.createString("checkValidRange")),
+                false),
+            label1,
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfArrayLength(),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 5),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/ir/desugar/backports/ArraysMethods;"),
+                    factory.createProto(
+                        factory.voidType, factory.intType, factory.intType, factory.intType),
+                    factory.createString("checkValidRange")),
+                false),
+            label2,
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.INT, 1),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfStore(ValueType.INT, 6),
+            label3,
+            new CfLoad(ValueType.INT, 5),
+            new CfLoad(ValueType.INT, 4),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfStore(ValueType.INT, 7),
+            label4,
+            new CfLoad(ValueType.INT, 6),
+            new CfLoad(ValueType.INT, 7),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfStore(ValueType.INT, 8),
+            label5,
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 9),
+            label6,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 9),
+            new CfLoad(ValueType.INT, 8),
+            new CfIfCmp(IfType.GE, ValueType.INT, label10),
+            label7,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 9),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.LONG),
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 9),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.LONG),
+            new CfCmp(Cmp.Bias.NONE, NumericType.LONG),
+            new CfIf(IfType.EQ, ValueType.INT, label9),
+            label8,
+            new CfLoad(ValueType.INT, 9),
+            new CfReturn(ValueType.INT),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfIinc(9, 1),
+            new CfGoto(label6),
+            label10,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 6),
+            new CfLoad(ValueType.INT, 7),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label11),
+            new CfLoad(ValueType.INT, 8),
+            new CfGoto(label12),
+            label11,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(-1, ValueType.INT),
+            label12,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.longArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    }),
+                new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
+            new CfReturn(ValueType.INT),
+            label13),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_mismatchObject(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        3,
+        4,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfStore(ValueType.INT, 2),
+            label1,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfIfCmp(IfType.NE, ValueType.OBJECT, label3),
+            label2,
+            new CfConstNumber(-1, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label3,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 3),
+            label4,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 3),
+            new CfLoad(ValueType.INT, 2),
+            new CfIfCmp(IfType.GE, ValueType.INT, label8),
+            label5,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 3),
+            new CfArrayLoad(MemberType.OBJECT),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.INT, 3),
+            new CfArrayLoad(MemberType.OBJECT),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/util/Objects;"),
+                    factory.createProto(
+                        factory.booleanType, factory.objectType, factory.objectType),
+                    factory.createString("equals")),
+                false),
+            new CfIf(IfType.NE, ValueType.INT, label7),
+            label6,
+            new CfLoad(ValueType.INT, 3),
+            new CfReturn(ValueType.INT),
+            label7,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfIinc(3, 1),
+            new CfGoto(label4),
+            label8,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label9),
+            new CfLoad(ValueType.INT, 2),
+            new CfGoto(label10),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(-1, ValueType.INT),
+            label10,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType()
+                    }),
+                new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
+            new CfReturn(ValueType.INT),
+            label11),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_mismatchObjectRange(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    CfLabel label12 = new CfLabel();
+    CfLabel label13 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        4,
+        10,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/ir/desugar/backports/ArraysMethods;"),
+                    factory.createProto(
+                        factory.voidType, factory.intType, factory.intType, factory.intType),
+                    factory.createString("checkValidRange")),
+                false),
+            label1,
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfArrayLength(),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 5),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/ir/desugar/backports/ArraysMethods;"),
+                    factory.createProto(
+                        factory.voidType, factory.intType, factory.intType, factory.intType),
+                    factory.createString("checkValidRange")),
+                false),
+            label2,
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.INT, 1),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfStore(ValueType.INT, 6),
+            label3,
+            new CfLoad(ValueType.INT, 5),
+            new CfLoad(ValueType.INT, 4),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfStore(ValueType.INT, 7),
+            label4,
+            new CfLoad(ValueType.INT, 6),
+            new CfLoad(ValueType.INT, 7),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfStore(ValueType.INT, 8),
+            label5,
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 9),
+            label6,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 9),
+            new CfLoad(ValueType.INT, 8),
+            new CfIfCmp(IfType.GE, ValueType.INT, label10),
+            label7,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 9),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.OBJECT),
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 9),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.OBJECT),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/util/Objects;"),
+                    factory.createProto(
+                        factory.booleanType, factory.objectType, factory.objectType),
+                    factory.createString("equals")),
+                false),
+            new CfIf(IfType.NE, ValueType.INT, label9),
+            label8,
+            new CfLoad(ValueType.INT, 9),
+            new CfReturn(ValueType.INT),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfIinc(9, 1),
+            new CfGoto(label6),
+            label10,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 6),
+            new CfLoad(ValueType.INT, 7),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label11),
+            new CfLoad(ValueType.INT, 8),
+            new CfGoto(label12),
+            label11,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(-1, ValueType.INT),
+            label12,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("[Ljava/lang/Object;")),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    }),
+                new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
+            new CfReturn(ValueType.INT),
+            label13),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_mismatchShort(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        3,
+        4,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfStore(ValueType.INT, 2),
+            label1,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfIfCmp(IfType.NE, ValueType.OBJECT, label3),
+            label2,
+            new CfConstNumber(-1, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label3,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 3),
+            label4,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 3),
+            new CfLoad(ValueType.INT, 2),
+            new CfIfCmp(IfType.GE, ValueType.INT, label8),
+            label5,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 3),
+            new CfArrayLoad(MemberType.SHORT),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.INT, 3),
+            new CfArrayLoad(MemberType.SHORT),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label7),
+            label6,
+            new CfLoad(ValueType.INT, 3),
+            new CfReturn(ValueType.INT),
+            label7,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfIinc(3, 1),
+            new CfGoto(label4),
+            label8,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayLength(),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label9),
+            new CfLoad(ValueType.INT, 2),
+            new CfGoto(label10),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(-1, ValueType.INT),
+            label10,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.intType()
+                    }),
+                new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
+            new CfReturn(ValueType.INT),
+            label11),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode ArraysMethods_mismatchShortRange(DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    CfLabel label6 = new CfLabel();
+    CfLabel label7 = new CfLabel();
+    CfLabel label8 = new CfLabel();
+    CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    CfLabel label12 = new CfLabel();
+    CfLabel label13 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        4,
+        10,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfArrayLength(),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/ir/desugar/backports/ArraysMethods;"),
+                    factory.createProto(
+                        factory.voidType, factory.intType, factory.intType, factory.intType),
+                    factory.createString("checkValidRange")),
+                false),
+            label1,
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfArrayLength(),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 5),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/ir/desugar/backports/ArraysMethods;"),
+                    factory.createProto(
+                        factory.voidType, factory.intType, factory.intType, factory.intType),
+                    factory.createString("checkValidRange")),
+                false),
+            label2,
+            new CfLoad(ValueType.INT, 2),
+            new CfLoad(ValueType.INT, 1),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfStore(ValueType.INT, 6),
+            label3,
+            new CfLoad(ValueType.INT, 5),
+            new CfLoad(ValueType.INT, 4),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.INT),
+            new CfStore(ValueType.INT, 7),
+            label4,
+            new CfLoad(ValueType.INT, 6),
+            new CfLoad(ValueType.INT, 7),
+            new CfInvoke(
+                184,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/Math;"),
+                    factory.createProto(factory.intType, factory.intType, factory.intType),
+                    factory.createString("min")),
+                false),
+            new CfStore(ValueType.INT, 8),
+            label5,
+            new CfConstNumber(0, ValueType.INT),
+            new CfStore(ValueType.INT, 9),
+            label6,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 9),
+            new CfLoad(ValueType.INT, 8),
+            new CfIfCmp(IfType.GE, ValueType.INT, label10),
+            label7,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 9),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.SHORT),
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfLoad(ValueType.INT, 4),
+            new CfLoad(ValueType.INT, 9),
+            new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.INT),
+            new CfArrayLoad(MemberType.SHORT),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label9),
+            label8,
+            new CfLoad(ValueType.INT, 9),
+            new CfReturn(ValueType.INT),
+            label9,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfIinc(9, 1),
+            new CfGoto(label6),
+            label10,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfLoad(ValueType.INT, 6),
+            new CfLoad(ValueType.INT, 7),
+            new CfIfCmp(IfType.EQ, ValueType.INT, label11),
+            new CfLoad(ValueType.INT, 8),
+            new CfGoto(label12),
+            label11,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    })),
+            new CfConstNumber(-1, ValueType.INT),
+            label12,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.initializedNonNullReference(factory.shortArrayType),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType(),
+                      FrameType.intType()
+                    }),
+                new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
+            new CfReturn(ValueType.INT),
+            label13),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
   public static CfCode AssertionErrorMethods_createAssertionError(
       DexItemFactory factory, DexMethod method) {
     CfLabel label0 = new CfLabel();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/code/CheckNotZeroCode.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/code/CheckNotZeroCode.java
index 283d254..8c57a9b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/code/CheckNotZeroCode.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/code/CheckNotZeroCode.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.graph.lens.GraphLens;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.Argument;
 import com.android.tools.r8.ir.code.IRCode;
@@ -57,20 +58,21 @@
             .setIsD8R8Synthesized(checkNotZeroMethod.getDefinition().isD8R8Synthesized())
             .build();
     NumberGenerator valueNumberGenerator = new NumberGenerator();
+
+    Code checkNotNullMethodCode = checkNotNullMethod.getDefinition().getCode();
+    GraphLens codeLens = checkNotNullMethodCode.getCodeLens(appView);
     IRCode code =
-        checkNotNullMethod
-            .getDefinition()
-            .getCode()
-            .buildInliningIR(
-                checkNotZeroMethod,
-                checkNotNullMethod,
-                appView,
-                appView.graphLens(),
-                valueNumberGenerator,
-                callerPosition,
-                appView
-                    .graphLens()
-                    .lookupPrototypeChangesForMethodDefinition(checkNotNullMethod.getReference()));
+        checkNotNullMethodCode.buildInliningIR(
+            checkNotZeroMethod,
+            checkNotNullMethod,
+            appView,
+            appView.graphLens(),
+            valueNumberGenerator,
+            callerPosition,
+            appView
+                .graphLens()
+                .lookupPrototypeChangesForMethodDefinition(
+                    checkNotNullMethod.getReference(), codeLens));
     InstructionListIterator instructionIterator = code.instructionListIterator();
 
     // Start iterating at the argument instruction for the checked argument.
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
index 3baed60..ec48f6f 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
@@ -114,6 +114,10 @@
     return null;
   }
 
+  public MergedClassesInformation asMergedClassesInformation() {
+    return null;
+  }
+
   public boolean shouldCompose(MappingInformation existing) {
     return !allowOther(existing);
   }
@@ -192,7 +196,7 @@
         PartitionFileNameInformation.deserialize(object, onMappingInfo);
         return;
       case MergedClassesInformation.ID:
-        MergedClassesInformation.deserialize(version, onMappingInfo);
+        MergedClassesInformation.deserialize(version, object, onMappingInfo);
         return;
       default:
         diagnosticsHandler.info(MappingInformationDiagnostics.noHandlerFor(lineNumber, id));
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/MergedClassesInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/MergedClassesInformation.java
index edefd02..0a81a8f 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/MergedClassesInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MergedClassesInformation.java
@@ -15,7 +15,11 @@
   public static final String ID = "com.android.tools.r8.mergedClasses";
   public static final MapVersion SUPPORTED_VERSION = MapVersion.MAP_VERSION_2_3;
 
-  private MergedClassesInformation() {}
+  private final JsonObject rawObject;
+
+  private MergedClassesInformation(JsonObject rawObject) {
+    this.rawObject = rawObject;
+  }
 
   @Override
   public String getId() {
@@ -37,20 +41,35 @@
     return true;
   }
 
+  @Override
+  public MergedClassesInformation asMergedClassesInformation() {
+    return this;
+  }
+
+  public static MergedClassesInformation build(JsonObject rawObject) {
+    return new MergedClassesInformation(rawObject);
+  }
+
   public static MergedClassesInformation build() {
-    return new MergedClassesInformation();
+    JsonObject object = new JsonObject();
+    object.add(MAPPING_ID_KEY, new JsonPrimitive(ID));
+    return new MergedClassesInformation(object);
   }
 
   @Override
   public String serialize() {
+    if (rawObject != null) {
+      return rawObject.toString();
+    }
     JsonObject object = new JsonObject();
     object.add(MAPPING_ID_KEY, new JsonPrimitive(ID));
     return object.toString();
   }
 
-  public static void deserialize(MapVersion version, Consumer<MappingInformation> onMappingInfo) {
+  public static void deserialize(
+      MapVersion version, JsonObject object, Consumer<MappingInformation> onMappingInfo) {
     if (isSupported(version)) {
-      onMappingInfo.accept(new MergedClassesInformation());
+      onMappingInfo.accept(new MergedClassesInformation(object));
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java
index 23a782f..d33092b 100644
--- a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java
@@ -54,7 +54,7 @@
     }
     DexMethod previousMethodSignature = getPreviousMethodSignature(method);
     RewrittenPrototypeDescription previousPrototypeChanges =
-        getPrevious().lookupPrototypeChangesForMethodDefinition(previousMethodSignature);
+        getPrevious().lookupPrototypeChangesForMethodDefinition(previousMethodSignature, codeLens);
     if (previousMethodSignature == method) {
       return previousPrototypeChanges;
     }
diff --git a/src/main/java/com/android/tools/r8/utils/CliParserUtils.java b/src/main/java/com/android/tools/r8/utils/CliParserUtils.java
index ce3f94c..f6ddcac 100644
--- a/src/main/java/com/android/tools/r8/utils/CliParserUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/CliParserUtils.java
@@ -32,8 +32,8 @@
       List<String> helpLines = StringUtils.wrapToWidth(info.description, descriptionWidth);
       List<String> alternatives;
       if (info.shorthand != null) {
-        assert info.suffixLabel == null : "shorthands and prefixes cannot be combined.";
-        alternatives = ImmutableList.of(commandString(info.shorthand, null, info.paramLabels));
+        alternatives =
+            ImmutableList.of(commandString(info.shorthand, info.suffixLabel, info.paramLabels));
       } else {
         alternatives = ImmutableList.of();
       }
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 1c47571..cb5727b 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -613,6 +613,9 @@
   public boolean disableLambdaMethodAnnotations =
       System.getProperty("com.android.tools.r8.disableLambdaMethodAnnotations") != null;
 
+  // TODO(b/547484295): Remove this flag.
+  public boolean alwaysAllow64KTypeIds =
+      System.getProperty("com.android.tools.r8.alwaysAllow64KTypeIds") != null;
   // TODO(b/548528931): Remove this flag.
   public boolean alwaysAllow64KFieldIds =
       System.getProperty("com.android.tools.r8.alwaysAllow64KFieldIds") != null;
diff --git a/src/main/resources/resources/hidden.api.txt b/src/main/resources/resources/hidden.api.txt
new file mode 100644
index 0000000..1bc2961
--- /dev/null
+++ b/src/main/resources/resources/hidden.api.txt
@@ -0,0 +1,143 @@
+// Copyright (c) 2026, 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.
+//
+// This file is generated by GenerateApiAmendments.java.
+// Do not edit manually.
+class Lsun/misc/Unsafe; 1
+field Lsun/misc/Unsafe; INVALID_FIELD_OFFSET 24
+method Ljava/nio/ByteBuffer; clear ()Ljava/nio/ByteBuffer; 28
+method Ljava/nio/ByteBuffer; flip ()Ljava/nio/ByteBuffer; 28
+method Ljava/nio/ByteBuffer; limit (I)Ljava/nio/ByteBuffer; 28
+method Ljava/nio/ByteBuffer; mark ()Ljava/nio/ByteBuffer; 28
+method Ljava/nio/ByteBuffer; position (I)Ljava/nio/ByteBuffer; 28
+method Ljava/nio/ByteBuffer; reset ()Ljava/nio/ByteBuffer; 28
+method Ljava/nio/ByteBuffer; rewind ()Ljava/nio/ByteBuffer; 28
+method Ljava/nio/CharBuffer; clear ()Ljava/nio/CharBuffer; 28
+method Ljava/nio/CharBuffer; flip ()Ljava/nio/CharBuffer; 28
+method Ljava/nio/CharBuffer; limit (I)Ljava/nio/CharBuffer; 28
+method Ljava/nio/CharBuffer; mark ()Ljava/nio/CharBuffer; 28
+method Ljava/nio/CharBuffer; position (I)Ljava/nio/CharBuffer; 28
+method Ljava/nio/CharBuffer; reset ()Ljava/nio/CharBuffer; 28
+method Ljava/nio/CharBuffer; rewind ()Ljava/nio/CharBuffer; 28
+method Ljava/nio/DoubleBuffer; clear ()Ljava/nio/DoubleBuffer; 28
+method Ljava/nio/DoubleBuffer; flip ()Ljava/nio/DoubleBuffer; 28
+method Ljava/nio/DoubleBuffer; limit (I)Ljava/nio/DoubleBuffer; 28
+method Ljava/nio/DoubleBuffer; mark ()Ljava/nio/DoubleBuffer; 28
+method Ljava/nio/DoubleBuffer; position (I)Ljava/nio/DoubleBuffer; 28
+method Ljava/nio/DoubleBuffer; reset ()Ljava/nio/DoubleBuffer; 28
+method Ljava/nio/DoubleBuffer; rewind ()Ljava/nio/DoubleBuffer; 28
+method Ljava/nio/FloatBuffer; clear ()Ljava/nio/FloatBuffer; 28
+method Ljava/nio/FloatBuffer; flip ()Ljava/nio/FloatBuffer; 28
+method Ljava/nio/FloatBuffer; limit (I)Ljava/nio/FloatBuffer; 28
+method Ljava/nio/FloatBuffer; mark ()Ljava/nio/FloatBuffer; 28
+method Ljava/nio/FloatBuffer; position (I)Ljava/nio/FloatBuffer; 28
+method Ljava/nio/FloatBuffer; reset ()Ljava/nio/FloatBuffer; 28
+method Ljava/nio/FloatBuffer; rewind ()Ljava/nio/FloatBuffer; 28
+method Ljava/nio/IntBuffer; clear ()Ljava/nio/IntBuffer; 28
+method Ljava/nio/IntBuffer; flip ()Ljava/nio/IntBuffer; 28
+method Ljava/nio/IntBuffer; limit (I)Ljava/nio/IntBuffer; 28
+method Ljava/nio/IntBuffer; mark ()Ljava/nio/IntBuffer; 28
+method Ljava/nio/IntBuffer; position (I)Ljava/nio/IntBuffer; 28
+method Ljava/nio/IntBuffer; reset ()Ljava/nio/IntBuffer; 28
+method Ljava/nio/IntBuffer; rewind ()Ljava/nio/IntBuffer; 28
+method Ljava/nio/LongBuffer; clear ()Ljava/nio/LongBuffer; 28
+method Ljava/nio/LongBuffer; flip ()Ljava/nio/LongBuffer; 28
+method Ljava/nio/LongBuffer; limit (I)Ljava/nio/LongBuffer; 28
+method Ljava/nio/LongBuffer; mark ()Ljava/nio/LongBuffer; 28
+method Ljava/nio/LongBuffer; position (I)Ljava/nio/LongBuffer; 28
+method Ljava/nio/LongBuffer; reset ()Ljava/nio/LongBuffer; 28
+method Ljava/nio/LongBuffer; rewind ()Ljava/nio/LongBuffer; 28
+method Ljava/nio/MappedByteBuffer; clear ()Ljava/nio/ByteBuffer; 34
+method Ljava/nio/MappedByteBuffer; clear ()Ljava/nio/MappedByteBuffer; 34
+method Ljava/nio/MappedByteBuffer; compact ()Ljava/nio/MappedByteBuffer; 34
+method Ljava/nio/MappedByteBuffer; duplicate ()Ljava/nio/MappedByteBuffer; 34
+method Ljava/nio/MappedByteBuffer; flip ()Ljava/nio/ByteBuffer; 34
+method Ljava/nio/MappedByteBuffer; flip ()Ljava/nio/MappedByteBuffer; 34
+method Ljava/nio/MappedByteBuffer; limit (I)Ljava/nio/ByteBuffer; 34
+method Ljava/nio/MappedByteBuffer; limit (I)Ljava/nio/MappedByteBuffer; 34
+method Ljava/nio/MappedByteBuffer; mark ()Ljava/nio/ByteBuffer; 34
+method Ljava/nio/MappedByteBuffer; mark ()Ljava/nio/MappedByteBuffer; 34
+method Ljava/nio/MappedByteBuffer; position (I)Ljava/nio/ByteBuffer; 34
+method Ljava/nio/MappedByteBuffer; position (I)Ljava/nio/MappedByteBuffer; 34
+method Ljava/nio/MappedByteBuffer; reset ()Ljava/nio/ByteBuffer; 34
+method Ljava/nio/MappedByteBuffer; reset ()Ljava/nio/MappedByteBuffer; 34
+method Ljava/nio/MappedByteBuffer; rewind ()Ljava/nio/ByteBuffer; 34
+method Ljava/nio/MappedByteBuffer; rewind ()Ljava/nio/MappedByteBuffer; 34
+method Ljava/nio/MappedByteBuffer; slice ()Ljava/nio/MappedByteBuffer; 34
+method Ljava/nio/ShortBuffer; clear ()Ljava/nio/ShortBuffer; 28
+method Ljava/nio/ShortBuffer; flip ()Ljava/nio/ShortBuffer; 28
+method Ljava/nio/ShortBuffer; limit (I)Ljava/nio/ShortBuffer; 28
+method Ljava/nio/ShortBuffer; mark ()Ljava/nio/ShortBuffer; 28
+method Ljava/nio/ShortBuffer; position (I)Ljava/nio/ShortBuffer; 28
+method Ljava/nio/ShortBuffer; reset ()Ljava/nio/ShortBuffer; 28
+method Ljava/nio/ShortBuffer; rewind ()Ljava/nio/ShortBuffer; 28
+method Ljava/time/LocalDate; getEra ()Ljava/time/chrono/IsoEra; 33
+method Ljava/util/concurrent/ConcurrentHashMap; keySet ()Ljava/util/concurrent/ConcurrentHashMap$KeySetView; 28
+method Lsun/misc/Unsafe; addressSize ()I 24
+method Lsun/misc/Unsafe; allocateInstance (Ljava/lang/Class;)Ljava/lang/Object; 16
+method Lsun/misc/Unsafe; allocateMemory (J)J 24
+method Lsun/misc/Unsafe; arrayBaseOffset (Ljava/lang/Class;)I 1
+method Lsun/misc/Unsafe; arrayIndexScale (Ljava/lang/Class;)I 1
+method Lsun/misc/Unsafe; compareAndSwapInt (Ljava/lang/Object;JII)Z 1
+method Lsun/misc/Unsafe; compareAndSwapLong (Ljava/lang/Object;JJJ)Z 1
+method Lsun/misc/Unsafe; compareAndSwapObject (Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z 1
+method Lsun/misc/Unsafe; copyMemory (JJJ)V 24
+method Lsun/misc/Unsafe; copyMemoryFromPrimitiveArray (Ljava/lang/Object;JJJ)V 24
+method Lsun/misc/Unsafe; copyMemoryToPrimitiveArray (JLjava/lang/Object;JJ)V 24
+method Lsun/misc/Unsafe; freeMemory (J)V 24
+method Lsun/misc/Unsafe; fullFence ()V 24
+method Lsun/misc/Unsafe; getAndAddInt (Ljava/lang/Object;JI)I 24
+method Lsun/misc/Unsafe; getAndAddLong (Ljava/lang/Object;JJ)J 24
+method Lsun/misc/Unsafe; getAndSetInt (Ljava/lang/Object;JI)I 24
+method Lsun/misc/Unsafe; getAndSetLong (Ljava/lang/Object;JJ)J 24
+method Lsun/misc/Unsafe; getAndSetObject (Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object; 24
+method Lsun/misc/Unsafe; getBoolean (Ljava/lang/Object;J)Z 24
+method Lsun/misc/Unsafe; getByte (J)B 24
+method Lsun/misc/Unsafe; getByte (Ljava/lang/Object;J)B 24
+method Lsun/misc/Unsafe; getChar (J)C 24
+method Lsun/misc/Unsafe; getChar (Ljava/lang/Object;J)C 24
+method Lsun/misc/Unsafe; getDouble (J)D 24
+method Lsun/misc/Unsafe; getDouble (Ljava/lang/Object;J)D 24
+method Lsun/misc/Unsafe; getFloat (J)F 24
+method Lsun/misc/Unsafe; getFloat (Ljava/lang/Object;J)F 24
+method Lsun/misc/Unsafe; getInt (J)I 24
+method Lsun/misc/Unsafe; getInt (Ljava/lang/Object;J)I 1
+method Lsun/misc/Unsafe; getIntVolatile (Ljava/lang/Object;J)I 1
+method Lsun/misc/Unsafe; getLong (J)J 24
+method Lsun/misc/Unsafe; getLong (Ljava/lang/Object;J)J 1
+method Lsun/misc/Unsafe; getLongVolatile (Ljava/lang/Object;J)J 1
+method Lsun/misc/Unsafe; getObject (Ljava/lang/Object;J)Ljava/lang/Object; 1
+method Lsun/misc/Unsafe; getObjectVolatile (Ljava/lang/Object;J)Ljava/lang/Object; 1
+method Lsun/misc/Unsafe; getShort (J)S 24
+method Lsun/misc/Unsafe; getShort (Ljava/lang/Object;J)S 24
+method Lsun/misc/Unsafe; getUnsafe ()Lsun/misc/Unsafe; 1
+method Lsun/misc/Unsafe; loadFence ()V 24
+method Lsun/misc/Unsafe; objectFieldOffset (Ljava/lang/reflect/Field;)J 1
+method Lsun/misc/Unsafe; pageSize ()I 24
+method Lsun/misc/Unsafe; park (ZJ)V 1
+method Lsun/misc/Unsafe; putBoolean (Ljava/lang/Object;JZ)V 24
+method Lsun/misc/Unsafe; putByte (JB)V 24
+method Lsun/misc/Unsafe; putByte (Ljava/lang/Object;JB)V 24
+method Lsun/misc/Unsafe; putChar (JC)V 24
+method Lsun/misc/Unsafe; putChar (Ljava/lang/Object;JC)V 24
+method Lsun/misc/Unsafe; putDouble (JD)V 24
+method Lsun/misc/Unsafe; putDouble (Ljava/lang/Object;JD)V 24
+method Lsun/misc/Unsafe; putFloat (JF)V 24
+method Lsun/misc/Unsafe; putFloat (Ljava/lang/Object;JF)V 24
+method Lsun/misc/Unsafe; putInt (JI)V 24
+method Lsun/misc/Unsafe; putInt (Ljava/lang/Object;JI)V 1
+method Lsun/misc/Unsafe; putIntVolatile (Ljava/lang/Object;JI)V 1
+method Lsun/misc/Unsafe; putLong (JJ)V 24
+method Lsun/misc/Unsafe; putLong (Ljava/lang/Object;JJ)V 1
+method Lsun/misc/Unsafe; putLongVolatile (Ljava/lang/Object;JJ)V 1
+method Lsun/misc/Unsafe; putObject (Ljava/lang/Object;JLjava/lang/Object;)V 1
+method Lsun/misc/Unsafe; putObjectVolatile (Ljava/lang/Object;JLjava/lang/Object;)V 1
+method Lsun/misc/Unsafe; putOrderedInt (Ljava/lang/Object;JI)V 1
+method Lsun/misc/Unsafe; putOrderedLong (Ljava/lang/Object;JJ)V 1
+method Lsun/misc/Unsafe; putOrderedObject (Ljava/lang/Object;JLjava/lang/Object;)V 1
+method Lsun/misc/Unsafe; putShort (JS)V 24
+method Lsun/misc/Unsafe; putShort (Ljava/lang/Object;JS)V 24
+method Lsun/misc/Unsafe; setMemory (JJB)V 24
+method Lsun/misc/Unsafe; storeFence ()V 24
+method Lsun/misc/Unsafe; unpark (Ljava/lang/Object;)V 1
diff --git a/src/main/resources/resources/hidden.jar.txt b/src/main/resources/resources/hidden.jar.txt
new file mode 100644
index 0000000..e640fc5
--- /dev/null
+++ b/src/main/resources/resources/hidden.jar.txt
@@ -0,0 +1,143 @@
+// Copyright (c) 2026, 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.
+//
+// This file is generated by GenerateApiAmendments.java.
+// Do not edit manually.
+class Lsun/misc/Unsafe; extends Ljava/lang/Object; implements none
+field Lsun/misc/Unsafe; INVALID_FIELD_OFFSET
+method Ljava/nio/ByteBuffer; clear ()Ljava/nio/ByteBuffer; instance
+method Ljava/nio/ByteBuffer; flip ()Ljava/nio/ByteBuffer; instance
+method Ljava/nio/ByteBuffer; limit (I)Ljava/nio/ByteBuffer; instance
+method Ljava/nio/ByteBuffer; mark ()Ljava/nio/ByteBuffer; instance
+method Ljava/nio/ByteBuffer; position (I)Ljava/nio/ByteBuffer; instance
+method Ljava/nio/ByteBuffer; reset ()Ljava/nio/ByteBuffer; instance
+method Ljava/nio/ByteBuffer; rewind ()Ljava/nio/ByteBuffer; instance
+method Ljava/nio/CharBuffer; clear ()Ljava/nio/CharBuffer; instance
+method Ljava/nio/CharBuffer; flip ()Ljava/nio/CharBuffer; instance
+method Ljava/nio/CharBuffer; limit (I)Ljava/nio/CharBuffer; instance
+method Ljava/nio/CharBuffer; mark ()Ljava/nio/CharBuffer; instance
+method Ljava/nio/CharBuffer; position (I)Ljava/nio/CharBuffer; instance
+method Ljava/nio/CharBuffer; reset ()Ljava/nio/CharBuffer; instance
+method Ljava/nio/CharBuffer; rewind ()Ljava/nio/CharBuffer; instance
+method Ljava/nio/DoubleBuffer; clear ()Ljava/nio/DoubleBuffer; instance
+method Ljava/nio/DoubleBuffer; flip ()Ljava/nio/DoubleBuffer; instance
+method Ljava/nio/DoubleBuffer; limit (I)Ljava/nio/DoubleBuffer; instance
+method Ljava/nio/DoubleBuffer; mark ()Ljava/nio/DoubleBuffer; instance
+method Ljava/nio/DoubleBuffer; position (I)Ljava/nio/DoubleBuffer; instance
+method Ljava/nio/DoubleBuffer; reset ()Ljava/nio/DoubleBuffer; instance
+method Ljava/nio/DoubleBuffer; rewind ()Ljava/nio/DoubleBuffer; instance
+method Ljava/nio/FloatBuffer; clear ()Ljava/nio/FloatBuffer; instance
+method Ljava/nio/FloatBuffer; flip ()Ljava/nio/FloatBuffer; instance
+method Ljava/nio/FloatBuffer; limit (I)Ljava/nio/FloatBuffer; instance
+method Ljava/nio/FloatBuffer; mark ()Ljava/nio/FloatBuffer; instance
+method Ljava/nio/FloatBuffer; position (I)Ljava/nio/FloatBuffer; instance
+method Ljava/nio/FloatBuffer; reset ()Ljava/nio/FloatBuffer; instance
+method Ljava/nio/FloatBuffer; rewind ()Ljava/nio/FloatBuffer; instance
+method Ljava/nio/IntBuffer; clear ()Ljava/nio/IntBuffer; instance
+method Ljava/nio/IntBuffer; flip ()Ljava/nio/IntBuffer; instance
+method Ljava/nio/IntBuffer; limit (I)Ljava/nio/IntBuffer; instance
+method Ljava/nio/IntBuffer; mark ()Ljava/nio/IntBuffer; instance
+method Ljava/nio/IntBuffer; position (I)Ljava/nio/IntBuffer; instance
+method Ljava/nio/IntBuffer; reset ()Ljava/nio/IntBuffer; instance
+method Ljava/nio/IntBuffer; rewind ()Ljava/nio/IntBuffer; instance
+method Ljava/nio/LongBuffer; clear ()Ljava/nio/LongBuffer; instance
+method Ljava/nio/LongBuffer; flip ()Ljava/nio/LongBuffer; instance
+method Ljava/nio/LongBuffer; limit (I)Ljava/nio/LongBuffer; instance
+method Ljava/nio/LongBuffer; mark ()Ljava/nio/LongBuffer; instance
+method Ljava/nio/LongBuffer; position (I)Ljava/nio/LongBuffer; instance
+method Ljava/nio/LongBuffer; reset ()Ljava/nio/LongBuffer; instance
+method Ljava/nio/LongBuffer; rewind ()Ljava/nio/LongBuffer; instance
+method Ljava/nio/MappedByteBuffer; clear ()Ljava/nio/ByteBuffer; instance
+method Ljava/nio/MappedByteBuffer; clear ()Ljava/nio/MappedByteBuffer; instance
+method Ljava/nio/MappedByteBuffer; compact ()Ljava/nio/MappedByteBuffer; instance
+method Ljava/nio/MappedByteBuffer; duplicate ()Ljava/nio/MappedByteBuffer; instance
+method Ljava/nio/MappedByteBuffer; flip ()Ljava/nio/ByteBuffer; instance
+method Ljava/nio/MappedByteBuffer; flip ()Ljava/nio/MappedByteBuffer; instance
+method Ljava/nio/MappedByteBuffer; limit (I)Ljava/nio/ByteBuffer; instance
+method Ljava/nio/MappedByteBuffer; limit (I)Ljava/nio/MappedByteBuffer; instance
+method Ljava/nio/MappedByteBuffer; mark ()Ljava/nio/ByteBuffer; instance
+method Ljava/nio/MappedByteBuffer; mark ()Ljava/nio/MappedByteBuffer; instance
+method Ljava/nio/MappedByteBuffer; position (I)Ljava/nio/ByteBuffer; instance
+method Ljava/nio/MappedByteBuffer; position (I)Ljava/nio/MappedByteBuffer; instance
+method Ljava/nio/MappedByteBuffer; reset ()Ljava/nio/ByteBuffer; instance
+method Ljava/nio/MappedByteBuffer; reset ()Ljava/nio/MappedByteBuffer; instance
+method Ljava/nio/MappedByteBuffer; rewind ()Ljava/nio/ByteBuffer; instance
+method Ljava/nio/MappedByteBuffer; rewind ()Ljava/nio/MappedByteBuffer; instance
+method Ljava/nio/MappedByteBuffer; slice ()Ljava/nio/MappedByteBuffer; instance
+method Ljava/nio/ShortBuffer; clear ()Ljava/nio/ShortBuffer; instance
+method Ljava/nio/ShortBuffer; flip ()Ljava/nio/ShortBuffer; instance
+method Ljava/nio/ShortBuffer; limit (I)Ljava/nio/ShortBuffer; instance
+method Ljava/nio/ShortBuffer; mark ()Ljava/nio/ShortBuffer; instance
+method Ljava/nio/ShortBuffer; position (I)Ljava/nio/ShortBuffer; instance
+method Ljava/nio/ShortBuffer; reset ()Ljava/nio/ShortBuffer; instance
+method Ljava/nio/ShortBuffer; rewind ()Ljava/nio/ShortBuffer; instance
+method Ljava/time/LocalDate; getEra ()Ljava/time/chrono/IsoEra; instance
+method Ljava/util/concurrent/ConcurrentHashMap; keySet ()Ljava/util/concurrent/ConcurrentHashMap$KeySetView; instance
+method Lsun/misc/Unsafe; addressSize ()I instance
+method Lsun/misc/Unsafe; allocateInstance (Ljava/lang/Class;)Ljava/lang/Object; instance
+method Lsun/misc/Unsafe; allocateMemory (J)J instance
+method Lsun/misc/Unsafe; arrayBaseOffset (Ljava/lang/Class;)I instance
+method Lsun/misc/Unsafe; arrayIndexScale (Ljava/lang/Class;)I instance
+method Lsun/misc/Unsafe; compareAndSwapInt (Ljava/lang/Object;JII)Z instance
+method Lsun/misc/Unsafe; compareAndSwapLong (Ljava/lang/Object;JJJ)Z instance
+method Lsun/misc/Unsafe; compareAndSwapObject (Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z instance
+method Lsun/misc/Unsafe; copyMemory (JJJ)V instance
+method Lsun/misc/Unsafe; copyMemoryFromPrimitiveArray (Ljava/lang/Object;JJJ)V instance
+method Lsun/misc/Unsafe; copyMemoryToPrimitiveArray (JLjava/lang/Object;JJ)V instance
+method Lsun/misc/Unsafe; freeMemory (J)V instance
+method Lsun/misc/Unsafe; fullFence ()V instance
+method Lsun/misc/Unsafe; getAndAddInt (Ljava/lang/Object;JI)I instance
+method Lsun/misc/Unsafe; getAndAddLong (Ljava/lang/Object;JJ)J instance
+method Lsun/misc/Unsafe; getAndSetInt (Ljava/lang/Object;JI)I instance
+method Lsun/misc/Unsafe; getAndSetLong (Ljava/lang/Object;JJ)J instance
+method Lsun/misc/Unsafe; getAndSetObject (Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object; instance
+method Lsun/misc/Unsafe; getBoolean (Ljava/lang/Object;J)Z instance
+method Lsun/misc/Unsafe; getByte (J)B instance
+method Lsun/misc/Unsafe; getByte (Ljava/lang/Object;J)B instance
+method Lsun/misc/Unsafe; getChar (J)C instance
+method Lsun/misc/Unsafe; getChar (Ljava/lang/Object;J)C instance
+method Lsun/misc/Unsafe; getDouble (J)D instance
+method Lsun/misc/Unsafe; getDouble (Ljava/lang/Object;J)D instance
+method Lsun/misc/Unsafe; getFloat (J)F instance
+method Lsun/misc/Unsafe; getFloat (Ljava/lang/Object;J)F instance
+method Lsun/misc/Unsafe; getInt (J)I instance
+method Lsun/misc/Unsafe; getInt (Ljava/lang/Object;J)I instance
+method Lsun/misc/Unsafe; getIntVolatile (Ljava/lang/Object;J)I instance
+method Lsun/misc/Unsafe; getLong (J)J instance
+method Lsun/misc/Unsafe; getLong (Ljava/lang/Object;J)J instance
+method Lsun/misc/Unsafe; getLongVolatile (Ljava/lang/Object;J)J instance
+method Lsun/misc/Unsafe; getObject (Ljava/lang/Object;J)Ljava/lang/Object; instance
+method Lsun/misc/Unsafe; getObjectVolatile (Ljava/lang/Object;J)Ljava/lang/Object; instance
+method Lsun/misc/Unsafe; getShort (J)S instance
+method Lsun/misc/Unsafe; getShort (Ljava/lang/Object;J)S instance
+method Lsun/misc/Unsafe; getUnsafe ()Lsun/misc/Unsafe; instance
+method Lsun/misc/Unsafe; loadFence ()V instance
+method Lsun/misc/Unsafe; objectFieldOffset (Ljava/lang/reflect/Field;)J instance
+method Lsun/misc/Unsafe; pageSize ()I instance
+method Lsun/misc/Unsafe; park (ZJ)V instance
+method Lsun/misc/Unsafe; putBoolean (Ljava/lang/Object;JZ)V instance
+method Lsun/misc/Unsafe; putByte (JB)V instance
+method Lsun/misc/Unsafe; putByte (Ljava/lang/Object;JB)V instance
+method Lsun/misc/Unsafe; putChar (JC)V instance
+method Lsun/misc/Unsafe; putChar (Ljava/lang/Object;JC)V instance
+method Lsun/misc/Unsafe; putDouble (JD)V instance
+method Lsun/misc/Unsafe; putDouble (Ljava/lang/Object;JD)V instance
+method Lsun/misc/Unsafe; putFloat (JF)V instance
+method Lsun/misc/Unsafe; putFloat (Ljava/lang/Object;JF)V instance
+method Lsun/misc/Unsafe; putInt (JI)V instance
+method Lsun/misc/Unsafe; putInt (Ljava/lang/Object;JI)V instance
+method Lsun/misc/Unsafe; putIntVolatile (Ljava/lang/Object;JI)V instance
+method Lsun/misc/Unsafe; putLong (JJ)V instance
+method Lsun/misc/Unsafe; putLong (Ljava/lang/Object;JJ)V instance
+method Lsun/misc/Unsafe; putLongVolatile (Ljava/lang/Object;JJ)V instance
+method Lsun/misc/Unsafe; putObject (Ljava/lang/Object;JLjava/lang/Object;)V instance
+method Lsun/misc/Unsafe; putObjectVolatile (Ljava/lang/Object;JLjava/lang/Object;)V instance
+method Lsun/misc/Unsafe; putOrderedInt (Ljava/lang/Object;JI)V instance
+method Lsun/misc/Unsafe; putOrderedLong (Ljava/lang/Object;JJ)V instance
+method Lsun/misc/Unsafe; putOrderedObject (Ljava/lang/Object;JLjava/lang/Object;)V instance
+method Lsun/misc/Unsafe; putShort (JS)V instance
+method Lsun/misc/Unsafe; putShort (Ljava/lang/Object;JS)V instance
+method Lsun/misc/Unsafe; setMemory (JJB)V instance
+method Lsun/misc/Unsafe; storeFence ()V instance
+method Lsun/misc/Unsafe; unpark (Ljava/lang/Object;)V instance
diff --git a/src/main/resources/resources/missing.api.txt b/src/main/resources/resources/missing.api.txt
new file mode 100644
index 0000000..5b0ca9d
--- /dev/null
+++ b/src/main/resources/resources/missing.api.txt
@@ -0,0 +1,177 @@
+// Copyright (c) 2026, 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.
+//
+// This file is generated by GenerateApiAmendments.java.
+// Do not edit manually.
+method Landroid/nfc/tech/IsoDep; close ()V 19
+method Landroid/nfc/tech/IsoDep; connect ()V 19
+method Landroid/nfc/tech/IsoDep; getTag ()Landroid/nfc/Tag; 19
+method Landroid/nfc/tech/IsoDep; isConnected ()Z 19
+method Landroid/nfc/tech/MifareClassic; close ()V 19
+method Landroid/nfc/tech/MifareClassic; connect ()V 19
+method Landroid/nfc/tech/MifareClassic; getTag ()Landroid/nfc/Tag; 19
+method Landroid/nfc/tech/MifareClassic; isConnected ()Z 19
+method Landroid/nfc/tech/MifareUltralight; close ()V 19
+method Landroid/nfc/tech/MifareUltralight; connect ()V 19
+method Landroid/nfc/tech/MifareUltralight; getTag ()Landroid/nfc/Tag; 19
+method Landroid/nfc/tech/MifareUltralight; isConnected ()Z 19
+method Landroid/nfc/tech/Ndef; close ()V 19
+method Landroid/nfc/tech/Ndef; connect ()V 19
+method Landroid/nfc/tech/Ndef; getTag ()Landroid/nfc/Tag; 19
+method Landroid/nfc/tech/Ndef; isConnected ()Z 19
+method Landroid/nfc/tech/NdefFormatable; close ()V 19
+method Landroid/nfc/tech/NdefFormatable; connect ()V 19
+method Landroid/nfc/tech/NdefFormatable; getTag ()Landroid/nfc/Tag; 19
+method Landroid/nfc/tech/NdefFormatable; isConnected ()Z 19
+method Landroid/nfc/tech/NfcA; close ()V 19
+method Landroid/nfc/tech/NfcA; connect ()V 19
+method Landroid/nfc/tech/NfcA; getTag ()Landroid/nfc/Tag; 19
+method Landroid/nfc/tech/NfcA; isConnected ()Z 19
+method Landroid/nfc/tech/NfcB; close ()V 19
+method Landroid/nfc/tech/NfcB; connect ()V 19
+method Landroid/nfc/tech/NfcB; getTag ()Landroid/nfc/Tag; 19
+method Landroid/nfc/tech/NfcB; isConnected ()Z 19
+method Landroid/nfc/tech/NfcBarcode; close ()V 19
+method Landroid/nfc/tech/NfcBarcode; connect ()V 19
+method Landroid/nfc/tech/NfcBarcode; getTag ()Landroid/nfc/Tag; 19
+method Landroid/nfc/tech/NfcBarcode; isConnected ()Z 19
+method Landroid/nfc/tech/NfcF; close ()V 19
+method Landroid/nfc/tech/NfcF; connect ()V 19
+method Landroid/nfc/tech/NfcF; getTag ()Landroid/nfc/Tag; 19
+method Landroid/nfc/tech/NfcF; isConnected ()Z 19
+method Landroid/nfc/tech/NfcV; close ()V 19
+method Landroid/nfc/tech/NfcV; connect ()V 19
+method Landroid/nfc/tech/NfcV; getTag ()Landroid/nfc/Tag; 19
+method Landroid/nfc/tech/NfcV; isConnected ()Z 19
+method Landroid/webkit/CookieSyncManager; resetSync ()V 14
+method Landroid/webkit/CookieSyncManager; run ()V 14
+method Landroid/webkit/CookieSyncManager; startSync ()V 14
+method Landroid/webkit/CookieSyncManager; stopSync ()V 14
+method Landroid/webkit/CookieSyncManager; sync ()V 14
+method Ljava/lang/StringBuffer; capacity ()I 1
+method Ljava/lang/StringBuffer; codePointAt (I)I 1
+method Ljava/lang/StringBuffer; codePointBefore (I)I 1
+method Ljava/lang/StringBuffer; codePointCount (II)I 1
+method Ljava/lang/StringBuffer; ensureCapacity (I)V 1
+method Ljava/lang/StringBuffer; getChars (II[CI)V 1
+method Ljava/lang/StringBuffer; indexOf (Ljava/lang/String;)I 1
+method Ljava/lang/StringBuffer; indexOf (Ljava/lang/String;I)I 1
+method Ljava/lang/StringBuffer; lastIndexOf (Ljava/lang/String;)I 1
+method Ljava/lang/StringBuffer; lastIndexOf (Ljava/lang/String;I)I 1
+method Ljava/lang/StringBuffer; offsetByCodePoints (II)I 1
+method Ljava/lang/StringBuffer; setCharAt (IC)V 1
+method Ljava/lang/StringBuffer; setLength (I)V 1
+method Ljava/lang/StringBuffer; substring (I)Ljava/lang/String; 1
+method Ljava/lang/StringBuffer; substring (II)Ljava/lang/String; 1
+method Ljava/lang/StringBuffer; trimToSize ()V 1
+method Ljava/lang/StringBuilder; capacity ()I 1
+method Ljava/lang/StringBuilder; codePointAt (I)I 1
+method Ljava/lang/StringBuilder; codePointBefore (I)I 1
+method Ljava/lang/StringBuilder; codePointCount (II)I 1
+method Ljava/lang/StringBuilder; ensureCapacity (I)V 1
+method Ljava/lang/StringBuilder; getChars (II[CI)V 1
+method Ljava/lang/StringBuilder; indexOf (Ljava/lang/String;)I 1
+method Ljava/lang/StringBuilder; indexOf (Ljava/lang/String;I)I 1
+method Ljava/lang/StringBuilder; lastIndexOf (Ljava/lang/String;)I 1
+method Ljava/lang/StringBuilder; lastIndexOf (Ljava/lang/String;I)I 1
+method Ljava/lang/StringBuilder; offsetByCodePoints (II)I 1
+method Ljava/lang/StringBuilder; setCharAt (IC)V 1
+method Ljava/lang/StringBuilder; setLength (I)V 1
+method Ljava/lang/StringBuilder; substring (I)Ljava/lang/String; 1
+method Ljava/lang/StringBuilder; substring (II)Ljava/lang/String; 1
+method Ljava/lang/StringBuilder; trimToSize ()V 1
+method Ljava/time/chrono/HijrahDate; atTime (Ljava/time/LocalTime;)Ljava/time/chrono/ChronoLocalDateTime; 26
+method Ljava/time/chrono/HijrahDate; getChronology ()Ljava/time/chrono/Chronology; 26
+method Ljava/time/chrono/HijrahDate; getEra ()Ljava/time/chrono/Era; 26
+method Ljava/time/chrono/HijrahDate; getLong (Ljava/time/temporal/TemporalField;)J 26
+method Ljava/time/chrono/HijrahDate; isLeapYear ()Z 26
+method Ljava/time/chrono/HijrahDate; isSupported (Ljava/time/temporal/TemporalField;)Z 26
+method Ljava/time/chrono/HijrahDate; lengthOfMonth ()I 26
+method Ljava/time/chrono/HijrahDate; lengthOfYear ()I 26
+method Ljava/time/chrono/HijrahDate; minus (JLjava/time/temporal/TemporalUnit;)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/HijrahDate; minus (JLjava/time/temporal/TemporalUnit;)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/HijrahDate; minus (Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/HijrahDate; minus (Ljava/time/temporal/TemporalAmount;)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/HijrahDate; plus (JLjava/time/temporal/TemporalUnit;)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/HijrahDate; plus (JLjava/time/temporal/TemporalUnit;)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/HijrahDate; plus (Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/HijrahDate; plus (Ljava/time/temporal/TemporalAmount;)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/HijrahDate; range (Ljava/time/temporal/TemporalField;)Ljava/time/temporal/ValueRange; 26
+method Ljava/time/chrono/HijrahDate; toEpochDay ()J 26
+method Ljava/time/chrono/HijrahDate; until (Ljava/time/chrono/ChronoLocalDate;)Ljava/time/chrono/ChronoPeriod; 26
+method Ljava/time/chrono/HijrahDate; until (Ljava/time/temporal/Temporal;Ljava/time/temporal/TemporalUnit;)J 26
+method Ljava/time/chrono/HijrahDate; with (Ljava/time/temporal/TemporalAdjuster;)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/HijrahDate; with (Ljava/time/temporal/TemporalAdjuster;)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/HijrahDate; with (Ljava/time/temporal/TemporalField;J)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/HijrahDate; with (Ljava/time/temporal/TemporalField;J)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/JapaneseDate; atTime (Ljava/time/LocalTime;)Ljava/time/chrono/ChronoLocalDateTime; 26
+method Ljava/time/chrono/JapaneseDate; getChronology ()Ljava/time/chrono/Chronology; 26
+method Ljava/time/chrono/JapaneseDate; getEra ()Ljava/time/chrono/Era; 26
+method Ljava/time/chrono/JapaneseDate; getLong (Ljava/time/temporal/TemporalField;)J 26
+method Ljava/time/chrono/JapaneseDate; isSupported (Ljava/time/temporal/TemporalField;)Z 26
+method Ljava/time/chrono/JapaneseDate; lengthOfMonth ()I 26
+method Ljava/time/chrono/JapaneseDate; lengthOfYear ()I 26
+method Ljava/time/chrono/JapaneseDate; minus (JLjava/time/temporal/TemporalUnit;)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/JapaneseDate; minus (JLjava/time/temporal/TemporalUnit;)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/JapaneseDate; minus (Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/JapaneseDate; minus (Ljava/time/temporal/TemporalAmount;)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/JapaneseDate; plus (JLjava/time/temporal/TemporalUnit;)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/JapaneseDate; plus (JLjava/time/temporal/TemporalUnit;)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/JapaneseDate; plus (Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/JapaneseDate; plus (Ljava/time/temporal/TemporalAmount;)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/JapaneseDate; range (Ljava/time/temporal/TemporalField;)Ljava/time/temporal/ValueRange; 26
+method Ljava/time/chrono/JapaneseDate; toEpochDay ()J 26
+method Ljava/time/chrono/JapaneseDate; until (Ljava/time/chrono/ChronoLocalDate;)Ljava/time/chrono/ChronoPeriod; 26
+method Ljava/time/chrono/JapaneseDate; until (Ljava/time/temporal/Temporal;Ljava/time/temporal/TemporalUnit;)J 26
+method Ljava/time/chrono/JapaneseDate; with (Ljava/time/temporal/TemporalAdjuster;)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/JapaneseDate; with (Ljava/time/temporal/TemporalAdjuster;)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/JapaneseDate; with (Ljava/time/temporal/TemporalField;J)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/JapaneseDate; with (Ljava/time/temporal/TemporalField;J)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/MinguoDate; atTime (Ljava/time/LocalTime;)Ljava/time/chrono/ChronoLocalDateTime; 26
+method Ljava/time/chrono/MinguoDate; getChronology ()Ljava/time/chrono/Chronology; 26
+method Ljava/time/chrono/MinguoDate; getEra ()Ljava/time/chrono/Era; 26
+method Ljava/time/chrono/MinguoDate; getLong (Ljava/time/temporal/TemporalField;)J 26
+method Ljava/time/chrono/MinguoDate; isSupported (Ljava/time/temporal/TemporalField;)Z 26
+method Ljava/time/chrono/MinguoDate; lengthOfMonth ()I 26
+method Ljava/time/chrono/MinguoDate; lengthOfYear ()I 26
+method Ljava/time/chrono/MinguoDate; minus (JLjava/time/temporal/TemporalUnit;)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/MinguoDate; minus (JLjava/time/temporal/TemporalUnit;)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/MinguoDate; minus (Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/MinguoDate; minus (Ljava/time/temporal/TemporalAmount;)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/MinguoDate; plus (JLjava/time/temporal/TemporalUnit;)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/MinguoDate; plus (JLjava/time/temporal/TemporalUnit;)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/MinguoDate; plus (Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/MinguoDate; plus (Ljava/time/temporal/TemporalAmount;)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/MinguoDate; range (Ljava/time/temporal/TemporalField;)Ljava/time/temporal/ValueRange; 26
+method Ljava/time/chrono/MinguoDate; toEpochDay ()J 26
+method Ljava/time/chrono/MinguoDate; until (Ljava/time/chrono/ChronoLocalDate;)Ljava/time/chrono/ChronoPeriod; 26
+method Ljava/time/chrono/MinguoDate; until (Ljava/time/temporal/Temporal;Ljava/time/temporal/TemporalUnit;)J 26
+method Ljava/time/chrono/MinguoDate; with (Ljava/time/temporal/TemporalAdjuster;)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/MinguoDate; with (Ljava/time/temporal/TemporalAdjuster;)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/MinguoDate; with (Ljava/time/temporal/TemporalField;J)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/MinguoDate; with (Ljava/time/temporal/TemporalField;J)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/ThaiBuddhistDate; atTime (Ljava/time/LocalTime;)Ljava/time/chrono/ChronoLocalDateTime; 26
+method Ljava/time/chrono/ThaiBuddhistDate; getChronology ()Ljava/time/chrono/Chronology; 26
+method Ljava/time/chrono/ThaiBuddhistDate; getEra ()Ljava/time/chrono/Era; 26
+method Ljava/time/chrono/ThaiBuddhistDate; getLong (Ljava/time/temporal/TemporalField;)J 26
+method Ljava/time/chrono/ThaiBuddhistDate; isSupported (Ljava/time/temporal/TemporalField;)Z 26
+method Ljava/time/chrono/ThaiBuddhistDate; lengthOfMonth ()I 26
+method Ljava/time/chrono/ThaiBuddhistDate; lengthOfYear ()I 26
+method Ljava/time/chrono/ThaiBuddhistDate; minus (JLjava/time/temporal/TemporalUnit;)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/ThaiBuddhistDate; minus (JLjava/time/temporal/TemporalUnit;)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/ThaiBuddhistDate; minus (Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/ThaiBuddhistDate; minus (Ljava/time/temporal/TemporalAmount;)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/ThaiBuddhistDate; plus (JLjava/time/temporal/TemporalUnit;)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/ThaiBuddhistDate; plus (JLjava/time/temporal/TemporalUnit;)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/ThaiBuddhistDate; plus (Ljava/time/temporal/TemporalAmount;)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/ThaiBuddhistDate; plus (Ljava/time/temporal/TemporalAmount;)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/ThaiBuddhistDate; range (Ljava/time/temporal/TemporalField;)Ljava/time/temporal/ValueRange; 26
+method Ljava/time/chrono/ThaiBuddhistDate; toEpochDay ()J 26
+method Ljava/time/chrono/ThaiBuddhistDate; until (Ljava/time/chrono/ChronoLocalDate;)Ljava/time/chrono/ChronoPeriod; 26
+method Ljava/time/chrono/ThaiBuddhistDate; until (Ljava/time/temporal/Temporal;Ljava/time/temporal/TemporalUnit;)J 26
+method Ljava/time/chrono/ThaiBuddhistDate; with (Ljava/time/temporal/TemporalAdjuster;)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/ThaiBuddhistDate; with (Ljava/time/temporal/TemporalAdjuster;)Ljava/time/temporal/Temporal; 26
+method Ljava/time/chrono/ThaiBuddhistDate; with (Ljava/time/temporal/TemporalField;J)Ljava/time/chrono/ChronoLocalDate; 26
+method Ljava/time/chrono/ThaiBuddhistDate; with (Ljava/time/temporal/TemporalField;J)Ljava/time/temporal/Temporal; 26
+method Ljava/util/concurrent/ConcurrentHashMap$KeySetView; getMap ()Ljava/util/concurrent/ConcurrentHashMap; 24
diff --git a/src/test/bootstrap/com/android/tools/r8/bootstrap/SanityCheck.java b/src/test/bootstrap/com/android/tools/r8/bootstrap/SanityCheck.java
index 5a23fbb..418857b 100644
--- a/src/test/bootstrap/com/android/tools/r8/bootstrap/SanityCheck.java
+++ b/src/test/bootstrap/com/android/tools/r8/bootstrap/SanityCheck.java
@@ -66,7 +66,10 @@
       throws Exception {
     BooleanBox licenseSeen = new BooleanBox();
     BooleanBox manifestSeen = new BooleanBox();
-    Set<String> apiDatabaseFiles = Sets.newHashSet("resources/api_database.ser");
+    BooleanBox apiDatabaseSeen = new BooleanBox();
+    Set<String> apiResourceFiles =
+        Sets.newHashSet(
+            "resources/missing.api.txt", "resources/hidden.api.txt", "resources/hidden.jar.txt");
     Set<String> r8AssistantRuntime =
         ImmutableSet.of(
             "ReflectiveEventType.java",
@@ -128,18 +131,21 @@
               // Allow.
             } else if (entryTester.test(name)) {
               // Allow.
-            } else if (apiDatabaseFiles.contains(name)) {
-              // Allow all api database files.
-              apiDatabaseFiles.remove(name);
+            } else if (name.equals("resources/api_database.ser")) {
+              apiDatabaseSeen.set();
               // Check that the entry is stored uncompressed.
-              assertEquals(ZipEntry.STORED, entry.getMethod());
+              assertEquals(name + " has the wrong zip-method", ZipEntry.STORED, entry.getMethod());
+            } else if (apiResourceFiles.contains(name)) {
+              // Allow all api database resource files.
+              apiResourceFiles.remove(name);
             } else if (name.endsWith("/")) {
               assertTrue("Unexpected directory entry in" + jar, allowDirectories);
             } else {
               fail("Unexpected entry '" + name + "' in " + jar);
             }
           });
-      assertTrue(apiDatabaseFiles.isEmpty());
+      assertTrue(apiResourceFiles.isEmpty());
+      assertTrue("No resources/api_database.ser entry found in " + jar, apiDatabaseSeen.isTrue());
       assertTrue("No LICENSE entry found in " + jar, licenseSeen.isTrue());
       assertTrue("No META-INF/MANIFEST.MF entry found in " + jar, manifestSeen.isTrue());
     } catch (IOException e) {
diff --git a/src/test/java/com/android/tools/r8/ApiDatabaseGeneratorCommandParserTest.java b/src/test/java/com/android/tools/r8/ApiDatabaseGeneratorCommandParserTest.java
index a955b75..05482ef 100644
--- a/src/test/java/com/android/tools/r8/ApiDatabaseGeneratorCommandParserTest.java
+++ b/src/test/java/com/android/tools/r8/ApiDatabaseGeneratorCommandParserTest.java
@@ -5,7 +5,11 @@
 
 import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.origin.CommandLineOrigin;
 import com.android.tools.r8.utils.internal.StringUtils;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collections;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -27,19 +31,27 @@
   public void testHelpMessage() {
     assertEquals(
         StringUtils.lines(
-            "Usage: apidatabasegenerator [options] <input-files>",
-            "where <input-files> are Android API XML files (e.g., api-versions.xml) to merge,",
-            "OR Android SDK Jar files (e.g., android.jar).",
-            "The XML lack sufficient information so the Jar files are used to add information to"
-                + " entries.",
+            "Usage: apidatabasegenerator [options]",
+            "Combines Android API information (--xml) with Android SDK information (--jar)",
+            "into an API database file required for compilation.",
+            "Multiple inputs of both are supported and any entries not present in both JAR and XML",
+            "form is trimmed away.",
             "The options are:",
             "  --help",
             "  -h                      # Print help.",
             "  --version               # Print version.",
+            "  --jar <jar-file>        # Android SDK JAR file (e.g., android.jar).",
+            "  --xml <xml-file>        # Android API XML file (e.g., api-versions.xml).",
+            "  --sdk <sdk-dir>         # Android SDK platform directory (e.g.,"
+                + " $ANDROID_HOME/android-34).",
             "  --output <database-file>",
             "                          # Output result in <database-file> (must be a file, not a"
                 + " directory).",
             "                          # Defaults to 'api_database.ser'.",
+            "  --dont-amend            # By default, the API database is amended with known missing"
+                + " information.",
+            "                          # This option disables that and processes inputs directly as"
+                + " they are.",
             "  --map-diagnostics[:<type>] <from-level> <to-level>",
             "                          # Map diagnostics of <type> (default any) reported as"
                 + " <from-level> to",
@@ -54,4 +66,82 @@
             "                          # errors cannot be mapped."),
         ApiDatabaseGeneratorCommandParser.getUsageMessage());
   }
+
+  @Test
+  public void testParseJarAndXml() throws Exception {
+    Path jarFile = temp.newFile("android.jar").toPath();
+    Path xmlFile = temp.newFile("api-versions.xml").toPath();
+    String[] args = {"--jar", jarFile.toString(), "--xml", xmlFile.toString()};
+    ApiDatabaseGeneratorCommand command =
+        ApiDatabaseGeneratorCommand.parse(args, CommandLineOrigin.INSTANCE).build();
+    assertEquals(Collections.singletonList(jarFile), command.getJarPaths());
+    assertEquals(Collections.singletonList(xmlFile), command.getXmlPaths());
+  }
+
+  @Test
+  public void testParseMultipleJarsAndXmls() throws Exception {
+    Path jar1 = temp.newFile("android1.jar").toPath();
+    Path jar2 = temp.newFile("android2.jar").toPath();
+    Path xml1 = temp.newFile("api1.xml").toPath();
+    Path xml2 = temp.newFile("api2.xml").toPath();
+    String[] args = {
+      "--jar", jar1.toString(),
+      "--jar", jar2.toString(),
+      "--xml", xml1.toString(),
+      "--xml", xml2.toString()
+    };
+    ApiDatabaseGeneratorCommand command =
+        ApiDatabaseGeneratorCommand.parse(args, CommandLineOrigin.INSTANCE).build();
+    assertEquals(Arrays.asList(jar1, jar2), command.getJarPaths());
+    assertEquals(Arrays.asList(xml1, xml2), command.getXmlPaths());
+  }
+
+  @Test
+  public void testParseSdkDirectory() throws Exception {
+    Path sdkDir = temp.newFolder("sdk").toPath();
+    String[] args = {"--sdk", sdkDir.toString()};
+    ApiDatabaseGeneratorCommand command =
+        ApiDatabaseGeneratorCommand.parse(args, CommandLineOrigin.INSTANCE).build();
+    assertEquals(Collections.singletonList(sdkDir.resolve("android.jar")), command.getJarPaths());
+    assertEquals(
+        Collections.singletonList(sdkDir.resolve("data").resolve("api-versions.xml")),
+        command.getXmlPaths());
+  }
+
+  @Test
+  public void testParseMultipleSdks() throws Exception {
+    Path sdkDir1 = temp.newFolder("sdk1").toPath();
+    Path sdkDir2 = temp.newFolder("sdk2").toPath();
+
+    String[] args = {"--sdk", sdkDir1.toString(), "--sdk", sdkDir2.toString()};
+    ApiDatabaseGeneratorCommand command =
+        ApiDatabaseGeneratorCommand.parse(args, CommandLineOrigin.INSTANCE).build();
+    assertEquals(
+        Arrays.asList(sdkDir1.resolve("android.jar"), sdkDir2.resolve("android.jar")),
+        command.getJarPaths());
+    assertEquals(
+        Arrays.asList(
+            sdkDir1.resolve("data").resolve("api-versions.xml"),
+            sdkDir2.resolve("data").resolve("api-versions.xml")),
+        command.getXmlPaths());
+  }
+
+  @Test
+  public void testParseSdkCombinedWithJarAndXml() throws Exception {
+    Path sdkDir = temp.newFolder("sdk").toPath();
+    Path extraJar = temp.newFile("extra.jar").toPath();
+    Path extraXml = temp.newFile("extra.xml").toPath();
+
+    String[] args = {
+      "--sdk", sdkDir.toString(),
+      "--jar", extraJar.toString(),
+      "--xml", extraXml.toString()
+    };
+    ApiDatabaseGeneratorCommand command =
+        ApiDatabaseGeneratorCommand.parse(args, CommandLineOrigin.INSTANCE).build();
+    assertEquals(Arrays.asList(sdkDir.resolve("android.jar"), extraJar), command.getJarPaths());
+    assertEquals(
+        Arrays.asList(sdkDir.resolve("data").resolve("api-versions.xml"), extraXml),
+        command.getXmlPaths());
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/ApiDatabaseGeneratorTest.java b/src/test/java/com/android/tools/r8/ApiDatabaseGeneratorTest.java
index 71abf3b..23eb880 100644
--- a/src/test/java/com/android/tools/r8/ApiDatabaseGeneratorTest.java
+++ b/src/test/java/com/android/tools/r8/ApiDatabaseGeneratorTest.java
@@ -58,9 +58,10 @@
 
     ApiDatabaseGeneratorCommand command =
         ApiDatabaseGeneratorCommand.builder()
-            .addInputPath(apiVersionsXml)
-            .addInputPath(dummyJar)
+            .addXmlPath(apiVersionsXml)
+            .addJarPath(dummyJar)
             .setOutputPath(outputDb)
+            .setAmend(false)
             .build();
 
     ApiDatabaseGenerator.run(command);
@@ -99,10 +100,11 @@
     TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
     ApiDatabaseGeneratorCommand command =
         ApiDatabaseGeneratorCommand.builder(diagnosticsHandler)
-            .addInputPath(apiVersionsXml1)
-            .addInputPath(apiVersionsXml2)
-            .addInputPath(dummyJar)
+            .addXmlPath(apiVersionsXml1)
+            .addXmlPath(apiVersionsXml2)
+            .addJarPath(dummyJar)
             .setOutputPath(outputDb)
+            .setAmend(false)
             .build();
 
     try {
@@ -153,11 +155,12 @@
     TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
     ApiDatabaseGeneratorCommand command =
         ApiDatabaseGeneratorCommand.builder(diagnosticsHandler)
-            .addInputPath(apiVersionsXml1)
-            .addInputPath(apiVersionsXml2)
-            .addInputPath(apiVersionsXml3)
-            .addInputPath(dummyJar)
+            .addXmlPath(apiVersionsXml1)
+            .addXmlPath(apiVersionsXml2)
+            .addXmlPath(apiVersionsXml3)
+            .addJarPath(dummyJar)
             .setOutputPath(outputDb)
+            .setAmend(false)
             .build();
 
     try {
@@ -203,11 +206,15 @@
     String[] args = {
       "--output",
       outputDb.toString(),
+      "--dont-amend",
       "--map-diagnostics:DuplicateApiDatabaseEntryDiagnostic",
       "error",
       "info",
+      "--xml",
       apiVersionsXml1.toString(),
+      "--xml",
       apiVersionsXml2.toString(),
+      "--jar",
       dummyJar.toString()
     };
 
@@ -259,11 +266,15 @@
     String[] args = {
       "--output",
       outputDb.toString(),
+      "--dont-amend",
       "--map-diagnostics:DuplicateApiDatabaseEntryDiagnostic",
       "error",
       "none",
+      "--xml",
       apiVersionsXml1.toString(),
+      "--xml",
       apiVersionsXml2.toString(),
+      "--jar",
       dummyJar.toString()
     };
 
@@ -286,7 +297,7 @@
     Path jarFile = temp.newFile("android.jar").toPath();
 
     ApiDatabaseGeneratorCommand command =
-        ApiDatabaseGeneratorCommand.builder().addInputPath(xmlFile).addInputPath(jarFile).build();
+        ApiDatabaseGeneratorCommand.builder().addXmlPath(xmlFile).addJarPath(jarFile).build();
 
     assertEquals(1, command.getXmlPaths().size());
     assertEquals(xmlFile, command.getXmlPaths().get(0));
@@ -295,21 +306,68 @@
   }
 
   @Test
-  public void testInvalidInputExtension() throws Exception {
-    Path txtFile = temp.newFile("invalid.txt").toPath();
+  public void testSdkInput() throws Exception {
+    Path sdkDir = temp.newFolder("sdk").toPath();
+
+    ApiDatabaseGeneratorCommand command =
+        ApiDatabaseGeneratorCommand.builder().addSdkPath(sdkDir).build();
+
+    assertEquals(1, command.getXmlPaths().size());
+    assertEquals(sdkDir.resolve("data").resolve("api-versions.xml"), command.getXmlPaths().get(0));
+    assertEquals(1, command.getJarPaths().size());
+    assertEquals(sdkDir.resolve("android.jar"), command.getJarPaths().get(0));
+  }
+
+  @Test
+  public void testMissingJarInput() throws Exception {
+    Path xmlFile = temp.newFile("api-versions.xml").toPath();
 
     TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
     try {
-      ApiDatabaseGeneratorCommand.builder(diagnosticsHandler).addInputPath(txtFile).build();
-      fail("Expected Command to fail building due to invalid input extension");
+      ApiDatabaseGeneratorCommand.builder(diagnosticsHandler).addXmlPath(xmlFile).build();
+      fail("Expected Command to fail building due to missing JAR input");
     } catch (ApiDatabaseGeneratorException e) {
       // Expected.
     }
 
     diagnosticsHandler.assertErrorsMatch(
-        diagnosticMessage(containsString("Unsupported input file extension")),
-        diagnosticMessage(containsString("At least one SDK JAR")),
-        diagnosticMessage(containsString("At least one API XML")));
+        diagnosticMessage(containsString("At least one SDK JAR input path must be specified")));
+  }
+
+  @Test
+  public void testMissingXmlInput() throws Exception {
+    Path jarFile = temp.newFile("android.jar").toPath();
+
+    TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
+    try {
+      ApiDatabaseGeneratorCommand.builder(diagnosticsHandler).addJarPath(jarFile).build();
+      fail("Expected Command to fail building due to missing XML input");
+    } catch (ApiDatabaseGeneratorException e) {
+      // Expected.
+    }
+
+    diagnosticsHandler.assertErrorsMatch(
+        diagnosticMessage(containsString("At least one API XML input path must be specified")));
+  }
+
+  @Test
+  public void testUnexpectedPositionalArgument() throws Exception {
+    Path jarFile = temp.newFile("android.jar").toPath();
+    Path xmlFile = temp.newFile("api-versions.xml").toPath();
+    Path txtFile = temp.newFile("invalid.txt").toPath();
+
+    TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
+    String[] args = {"--jar", jarFile.toString(), "--xml", xmlFile.toString(), txtFile.toString()};
+    try {
+      ApiDatabaseGeneratorCommand.parse(args, CommandLineOrigin.INSTANCE, diagnosticsHandler)
+          .build();
+      fail("Expected Command to fail building due to unexpected positional argument");
+    } catch (ApiDatabaseGeneratorException e) {
+      // Expected.
+    }
+
+    diagnosticsHandler.assertErrorsMatch(
+        diagnosticMessage(containsString("Unexpected argument: " + txtFile)));
   }
 
   private Path writeApiXml(String filename, String... contentLines) throws Exception {
diff --git a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
index 7996e83..11e7dda 100644
--- a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
+++ b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
@@ -5,7 +5,6 @@
 
 import static com.android.tools.r8.TestBase.getTestParameters;
 
-import com.android.tools.r8.DexIndexedConsumer;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.graph.AppInfo;
@@ -44,13 +43,12 @@
     parameters.assertNoneRuntime();
   }
 
-  private static ObjectToOffsetMapping emptyObjectTObjectMapping() {
-    InternalOptions options = new InternalOptions(new DexItemFactory(), new Reporter());
-    options.programConsumer = DexIndexedConsumer.emptyConsumer();
+  private ObjectToOffsetMapping emptyObjectTObjectMapping() {
     AppView<AppInfo> appView =
         AppView.createForD8(
             AppInfo.createInitialAppInfo(
-                DexApplication.builder(options).build(Timing.empty()),
+                DexApplication.builder(new InternalOptions(new DexItemFactory(), new Reporter()))
+                    .build(Timing.empty()),
                 GlobalSyntheticsStrategy.forNonSynthesizing()));
     return new ObjectToOffsetMapping(
         appView,
diff --git a/src/test/java/com/android/tools/r8/dex/MaxTypeLimitTest.java b/src/test/java/com/android/tools/r8/dex/MaxTypeLimitTest.java
index d8e9e67..63825d5 100644
--- a/src/test/java/com/android/tools/r8/dex/MaxTypeLimitTest.java
+++ b/src/test/java/com/android/tools/r8/dex/MaxTypeLimitTest.java
@@ -3,16 +3,22 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.dex;
 
+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.assertEquals;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.ByteDataView;
 import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
+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.dex.container.DexContainerFormatTestBase;
-import com.android.tools.r8.transformers.ClassTransformer;
+import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.ZipUtils;
@@ -26,13 +32,18 @@
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.ClassWriter;
 import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
 
 @RunWith(Parameterized.class)
 public class MaxTypeLimitTest extends TestBase {
 
-  private static final int CLASS_COUNT = 33000;
-  private static Path inputApp;
+  private static final int CLASS_COUNT_32K = 33000;
+  private static final int CLASS_COUNT_64K = 65533;
+  private static final int TOTAL_TYPES_64K = 65536;
+  private static Path inputApp32K;
+  private static Path inputApp64K;
 
   @Parameter(0)
   public TestParameters parameters;
@@ -44,35 +55,41 @@
 
   @BeforeClass
   public static void generateTestApplication() {
-    inputApp = getStaticTemp().getRoot().toPath().resolve("input_app.jar");
-    generateApplication(inputApp, CLASS_COUNT);
+    inputApp32K = getStaticTemp().getRoot().toPath().resolve("input_app_32k.jar");
+    generateApplication(inputApp32K, CLASS_COUNT_32K, false);
+    inputApp64K = getStaticTemp().getRoot().toPath().resolve("input_app_64k.jar");
+    generateApplication(inputApp64K, CLASS_COUNT_64K, true);
   }
 
-  private static void generateApplication(Path output, int classCount) {
-    List<String> classes = new ArrayList<>(classCount);
-    for (int i = 0; i < classCount; ++i) {
-      classes.add("package_" + (i % 2 == 0 ? "a" : "b") + ".Class" + i);
-    }
+  private static void generateApplication(Path output, int classCount, boolean includeMain) {
     ArchiveConsumer consumer = new ArchiveConsumer(output);
-    for (String typename : classes) {
+    for (int i = 0; i < classCount; ++i) {
+      String typename = "package_" + (i % 2 == 0 ? "a" : "b") + ".Class" + i;
       String descriptor = DescriptorUtils.javaTypeToDescriptor(typename);
-      byte[] bytes =
-          transformer(ClassStub.class)
-              .setClassDescriptor(descriptor)
-              .addClassTransformer(
-                  new ClassTransformer() {
-                    @Override
-                    public MethodVisitor visitMethod(
-                        int access,
-                        String name,
-                        String descriptor,
-                        String signature,
-                        String[] exceptions) {
-                      // Strip methods to only generate class definition without methods.
-                      return null;
-                    }
-                  })
-              .transform();
+      String internalName = descriptor.substring(1, descriptor.length() - 1);
+      ClassWriter cw = new ClassWriter(0);
+      cw.visit(
+          Opcodes.V1_8,
+          Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER,
+          internalName,
+          null,
+          "java/lang/Object",
+          null);
+      if (includeMain && i == 0) {
+        MethodVisitor mv =
+            cw.visitMethod(
+                Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,
+                "main",
+                "([Ljava/lang/String;)V",
+                null,
+                null);
+        mv.visitCode();
+        mv.visitInsn(Opcodes.RETURN);
+        mv.visitMaxs(0, 1);
+        mv.visitEnd();
+      }
+      cw.visitEnd();
+      byte[] bytes = cw.toByteArray();
       consumer.accept(ByteDataView.of(bytes), descriptor, null);
     }
     consumer.finished(null);
@@ -86,10 +103,10 @@
 
   @Test
   public void testD8Api25MultidexSplitOnTypes() throws Exception {
-    // API level 25 only allows 32767 type-ids, so 33000 classes must split into 2 DEX files.
+    // API level 25 only allows 32768 type-ids, so 33000 classes must split into 2 DEX files.
     Path output =
         testForD8(Backend.DEX)
-            .addProgramFiles(inputApp)
+            .addProgramFiles(inputApp32K)
             .setMinApi(AndroidApiLevel.N_MR1)
             .compile()
             .writeToZip();
@@ -106,10 +123,10 @@
 
   @Test
   public void testD8Api26SingleDex() throws Exception {
-    // API level 26 allows 65536 type-ids, so 33000 classes fit into 1 DEX file.
+    // API level 26 allows 65535 type-ids, so 33000 classes fit into 1 DEX file.
     Path output =
         testForD8(Backend.DEX)
-            .addProgramFiles(inputApp)
+            .addProgramFiles(inputApp32K)
             .setMinApi(AndroidApiLevel.O)
             .compile()
             .writeToZip();
@@ -120,49 +137,119 @@
     assertTrue(
         "Type count " + typeCount + " must be > 32768",
         typeCount > VirtualFile.MAX_ENTRIES_ONLY_32K);
+    assertTrue(
+        "Type count " + typeCount + " must be <= 65535",
+        typeCount <= VirtualFile.MAX_ENTRIES_ONLY_65535);
   }
 
   @Test
   public void testD8Api25SingleDexOverflow() throws Exception {
-    // The workaround is not applied when compiling to mono-dex (e.g. min-api < 21), so compilation
-    // should succeed even with 33000 classes.
-    Path output =
-        testForD8(Backend.DEX)
-            .addProgramFiles(inputApp)
-            .setMinApi(AndroidApiLevel.K)
-            .compile()
-            .writeToZip();
-    List<byte[]> dexes = new ArrayList<>();
-    ZipUtils.iter(output, (entry, inputStream) -> dexes.add(ByteStreams.toByteArray(inputStream)));
-    assertEquals(1, dexes.size());
-    int typeCount = getTypeCount(dexes.get(0));
-    assertTrue(
-        "Type count " + typeCount + " must be > 32768",
-        typeCount > VirtualFile.MAX_ENTRIES_ONLY_32K);
+    // Compiling with mono-dex (e.g. min-api < 21) with 33000 classes should fail.
+    assertThrows(
+        CompilationFailedException.class,
+        () ->
+            testForD8(Backend.DEX)
+                .addProgramFiles(inputApp32K)
+                .setMinApi(AndroidApiLevel.K)
+                .compileWithExpectedDiagnostics(
+                    diagnostics ->
+                        diagnostics.assertErrorsMatch(
+                            allOf(
+                                diagnosticType(DexFileOverflowDiagnostic.class),
+                                diagnosticMessage(
+                                    containsString(
+                                        "# types: " + (CLASS_COUNT_32K + 1) + " > 32768"))))));
+    // Should not fail when alwaysAllow64KTypeIds is set.
+    testForD8(Backend.DEX)
+        .addProgramFiles(inputApp32K)
+        .setMinApi(AndroidApiLevel.K)
+        .addOptionsModification(options -> options.alwaysAllow64KTypeIds = true)
+        .compile();
   }
 
   @Test
   public void testD8LegacyMultidexMainDexRulesOverflow() throws Exception {
-    // The workaround is not applied when compiling to legacy multi dex, so compilation should
-    // succeed even with 33000 classes in the presence of main dex rules.
+    // Compiling with main-dex rules that keep all 33000 classes in main dex on legacy multidex (API
+    // 19) should fail.
+    assertThrows(
+        CompilationFailedException.class,
+        () ->
+            testForD8(Backend.DEX)
+                .addProgramFiles(inputApp32K)
+                .setMinApi(AndroidApiLevel.K)
+                .addMainDexRules("-keep class ** { *; }")
+                .compileWithExpectedDiagnostics(
+                    diagnostics ->
+                        diagnostics.assertErrorsMatch(
+                            allOf(
+                                diagnosticType(DexFileOverflowDiagnostic.class),
+                                diagnosticMessage(
+                                    containsString(
+                                        "Cannot fit requested classes in the main-dex file (#"
+                                            + " types: "
+                                            + (CLASS_COUNT_32K + 1)
+                                            + " > 32768)"))))));
+
+    // Should not fail when alwaysAllow64KTypeIds is set.
+    testForD8(Backend.DEX)
+        .addProgramFiles(inputApp32K)
+        .setMinApi(AndroidApiLevel.K)
+        .addMainDexRules("-keep class ** { *; }")
+        .addOptionsModification(options -> options.alwaysAllow64KTypeIds = true)
+        .compile();
+  }
+
+  @Test
+  public void testD8Api26MultidexSplitOn65KTypes() throws Exception {
+    // API level 26 allows at most 65535 type-ids, so 65536 types must split into 2 DEX files.
     Path output =
         testForD8(Backend.DEX)
-            .addProgramFiles(inputApp)
-            .setMinApi(AndroidApiLevel.K)
-            .addMainDexRules("-keep class ** { *; }")
+            .addProgramFiles(inputApp64K)
+            .setMinApi(AndroidApiLevel.O)
             .compile()
             .writeToZip();
     List<byte[]> dexes = new ArrayList<>();
     ZipUtils.iter(output, (entry, inputStream) -> dexes.add(ByteStreams.toByteArray(inputStream)));
-    assertEquals(1, dexes.size());
-    int typeCount = getTypeCount(dexes.get(0));
-    assertTrue(
-        "Type count " + typeCount + " must be > 32768",
-        typeCount > VirtualFile.MAX_ENTRIES_ONLY_32K);
+    assertEquals(2, dexes.size());
+    for (byte[] dex : dexes) {
+      int typeCount = getTypeCount(dex);
+      assertTrue(
+          "Type count " + typeCount + " must be <= 65535",
+          typeCount <= VirtualFile.MAX_ENTRIES_ONLY_65535);
+    }
   }
 
-  // Simple stub/template for generating the input classes.
-  public static class ClassStub {
-    public static void methodStub() {}
+  @Test
+  public void testD8Api26SingleDexOverflow() throws Exception {
+    // Compiling with mono-dex with 65536 types on API 19 should fail because 65536 > 32768.
+    assertThrows(
+        CompilationFailedException.class,
+        () ->
+            testForD8(Backend.DEX)
+                .addProgramFiles(inputApp64K)
+                .setMinApi(AndroidApiLevel.K)
+                .compileWithExpectedDiagnostics(
+                    diagnostics ->
+                        diagnostics.assertErrorsMatch(
+                            allOf(
+                                diagnosticType(DexFileOverflowDiagnostic.class),
+                                diagnosticMessage(
+                                    containsString("# types: " + TOTAL_TYPES_64K + " > 32768"))))));
+
+    // Compiling with alwaysAllow64KTypeIds allows at most 65535 types, so 65536 types still fails.
+    assertThrows(
+        CompilationFailedException.class,
+        () ->
+            testForD8(Backend.DEX)
+                .addProgramFiles(inputApp64K)
+                .setMinApi(AndroidApiLevel.K)
+                .addOptionsModification(options -> options.alwaysAllow64KTypeIds = true)
+                .compileWithExpectedDiagnostics(
+                    diagnostics ->
+                        diagnostics.assertErrorsMatch(
+                            allOf(
+                                diagnosticType(DexFileOverflowDiagnostic.class),
+                                diagnosticMessage(
+                                    containsString("# types: " + TOTAL_TYPES_64K + " > 65535"))))));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/jasmin/Regress65007724.java b/src/test/java/com/android/tools/r8/jasmin/Regress65007724.java
index 8790618..32fb392 100644
--- a/src/test/java/com/android/tools/r8/jasmin/Regress65007724.java
+++ b/src/test/java/com/android/tools/r8/jasmin/Regress65007724.java
@@ -3,12 +3,19 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.jasmin;
 
+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.assertEquals;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.D8TestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.ZipUtils;
 import java.io.IOException;
@@ -50,24 +57,42 @@
         "ldc \"Hello World!\"",
         "invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V",
         "return");
+    D8TestBuilder testBuilder =
+        testForD8(parameters.getBackend())
+            .setMinApi(parameters)
+            .addProgramClassFileData(builder.buildClasses());
 
-    SingleTestRunResult<?> d8TestRunResult =
-        testForD8(parameters)
-            .addProgramClassFileData(builder.buildClasses())
-            .compile()
-            .writeToZip(
-                p -> {
-                  try {
-                    assertTrue(ZipUtils.containsEntry(p, "classes.dex"));
-                    assertEquals(
-                        parameters.canUseNativeMultidex()
-                            && parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
-                        ZipUtils.containsEntry(p, "classes2.dex"));
-                  } catch (IOException e) {
-                    throw new RuntimeException(e);
-                  }
-                })
-            .run(parameters.getRuntime(), clazz.name);
-    d8TestRunResult.assertSuccessWithOutput("Hello World!");
+    if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L)) {
+      testBuilder
+          .compile()
+          .writeToZip(
+              p -> {
+                try {
+                  assertTrue(ZipUtils.containsEntry(p, "classes.dex"));
+                  assertEquals(
+                      parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
+                      ZipUtils.containsEntry(p, "classes2.dex"));
+                } catch (IOException e) {
+                  throw new RuntimeException(e);
+                }
+              })
+          .run(parameters.getRuntime(), clazz.name)
+          .assertSuccessWithOutput("Hello World!");
+    } else {
+      assertThrows(
+          CompilationFailedException.class,
+          () ->
+              testBuilder.compileWithExpectedDiagnostics(
+                  diagnostics ->
+                      diagnostics
+                          .assertOnlyErrors()
+                          .assertAllErrorsMatch(
+                              allOf(
+                                  diagnosticType(DexFileOverflowDiagnostic.class),
+                                  diagnosticMessage(
+                                      containsString(
+                                          "Cannot fit requested classes in a single dex file (#"
+                                              + " types: 35007 > 32768)"))))));
+    }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeMergedClassesTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeMergedClassesTest.java
new file mode 100644
index 0000000..709391f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeMergedClassesTest.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2026, 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.mappingcompose;
+
+import static com.android.tools.r8.mappingcompose.ComposeTestHelpers.doubleToSingleQuote;
+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.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.internal.StringUtils;
+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 ComposeMergedClassesTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  private static final String mappingFoo =
+      StringUtils.unixLines(
+          "# { id: 'com.android.tools.r8.mapping', version: '2.3' }",
+          "com.foo -> a:",
+          "# { id: 'com.android.tools.r8.mergedClasses', kind: 'horizontal', classIdField: '$cid',"
+              + " mergedClasses: [{ name: 'com.foo', classId: '0' }, { name: 'com.bar', classId:"
+              + " '1' }] }");
+  private static final String mappingBar =
+      StringUtils.unixLines("# { id: 'com.android.tools.r8.mapping', version: '2.2' }", "a -> b:");
+  private static final String mappingResult =
+      StringUtils.unixLines(
+          "# {'id':'com.android.tools.r8.mapping','version':'2.3'}",
+          "com.foo -> b:",
+          "# {'id':'com.android.tools.r8.mergedClasses','kind':'horizontal','classIdField':'$cid',"
+              + "'mergedClasses':[{'name':'com.foo','classId':'0'},{'name':'com.bar',"
+              + "'classId':'1'}]}");
+
+  @Test
+  public void testCompose() throws Exception {
+    ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromStringWithPreamble(mappingFoo);
+    ClassNameMapper mappingForBar = ClassNameMapper.mapperFromStringWithPreamble(mappingBar);
+    String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+    assertEquals(mappingResult, doubleToSingleQuote(composed));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/numberunboxing/CannotUnboxNumberUnboxingTest.java b/src/test/java/com/android/tools/r8/numberunboxing/CannotUnboxNumberUnboxingTest.java
index c4acb9d..c96bd86 100644
--- a/src/test/java/com/android/tools/r8/numberunboxing/CannotUnboxNumberUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/numberunboxing/CannotUnboxNumberUnboxingTest.java
@@ -36,12 +36,11 @@
 
   @Test
   public void testNumberUnboxing() throws Exception {
-    testForR8(parameters.getBackend())
+    testForR8(parameters)
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         .addOptionsModification(opt -> opt.getTestingOptions().getNumberUnboxerOptions().enable())
-        .setMinApi(parameters)
         .compile()
         .inspect(this::assertUnboxing)
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/numberunboxing/InitializerNumberUnboxingTest.java b/src/test/java/com/android/tools/r8/numberunboxing/InitializerNumberUnboxingTest.java
index 1d16a94..ee4bf47 100644
--- a/src/test/java/com/android/tools/r8/numberunboxing/InitializerNumberUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/numberunboxing/InitializerNumberUnboxingTest.java
@@ -38,12 +38,11 @@
 
   @Test
   public void testNumberUnboxing() throws Exception {
-    testForR8(parameters.getBackend())
+    testForR8(parameters)
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         .addOptionsModification(opt -> opt.getTestingOptions().getNumberUnboxerOptions().enable())
-        .setMinApi(parameters)
         .compile()
         .inspect(this::assertUnboxing)
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/numberunboxing/StaticMethodsCollidingNamesNumberUnboxingTest.java b/src/test/java/com/android/tools/r8/numberunboxing/StaticMethodsCollidingNamesNumberUnboxingTest.java
new file mode 100644
index 0000000..b3cde74
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/numberunboxing/StaticMethodsCollidingNamesNumberUnboxingTest.java
@@ -0,0 +1,105 @@
+// Copyright (c) 2026, 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.numberunboxing;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
+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.CodeInspector;
+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 StaticMethodsCollidingNamesNumberUnboxingTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public StaticMethodsCollidingNamesNumberUnboxingTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testNumberUnboxing() throws Exception {
+    testForR8(parameters)
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        .addNoParameterTypeStrengtheningAnnotation()
+        .addOptionsModification(opt -> opt.getTestingOptions().getNumberUnboxerOptions().enable())
+        .compile()
+        .inspect(this::assertUnboxing)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("32", "33", "34", "35", "42", "43", "42", "43");
+  }
+
+  private void assertUnboxing(CodeInspector codeInspector) {
+    ClassSubject mainClass = codeInspector.clazz(Main.class);
+    assertThat(mainClass, isPresent());
+    assertEquals(5, mainClass.allMethods().size());
+    mainClass
+        .allMethods()
+        .forEach(
+            m -> {
+              if (!m.getOriginalMethodName().equals("main")) {
+                assertEquals(1, m.getParameters().size());
+                assertTrue(m.getProgramMethod().getParameter(0).isIntType());
+              }
+            });
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      directPrintUnbox(31);
+      directPrintUnbox(32);
+      directPrintUnbox(33, 0);
+      directPrintUnbox(34, 1);
+
+      forwardToPrint(41);
+      forwardToPrint(42);
+      forwardToPrint(41, 0);
+      forwardToPrint(42, 1);
+    }
+
+    @NeverInline
+    private static void forwardToPrint(Integer boxed) {
+      directPrintUnbox(boxed);
+    }
+
+    // Final proto should overlap with method above.
+    @NoParameterTypeStrengthening
+    @NeverInline
+    private static void forwardToPrint(int boxed, int unused) {
+      directPrintUnbox(boxed, unused);
+    }
+
+    @NeverInline
+    private static void directPrintUnbox(Integer boxed) {
+      System.out.println(boxed + 1);
+    }
+
+    // Final proto should overlap with method above.
+    @NoParameterTypeStrengthening
+    @NeverInline
+    private static void directPrintUnbox(int boxed, int unused) {
+      System.out.println(boxed + 1);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/numberunboxing/StaticMethodsNumberUnboxingTest.java b/src/test/java/com/android/tools/r8/numberunboxing/StaticMethodsNumberUnboxingTest.java
index 135d38a..d5acd37 100644
--- a/src/test/java/com/android/tools/r8/numberunboxing/StaticMethodsNumberUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/numberunboxing/StaticMethodsNumberUnboxingTest.java
@@ -37,12 +37,11 @@
 
   @Test
   public void testNumberUnboxing() throws Exception {
-    testForR8(parameters.getBackend())
+    testForR8(parameters)
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         .addOptionsModification(opt -> opt.getTestingOptions().getNumberUnboxerOptions().enable())
-        .setMinApi(parameters)
         .compile()
         .inspect(this::assertUnboxing)
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/numberunboxing/StaticMethodsWideUnboxingMixNumberUnboxingTest.java b/src/test/java/com/android/tools/r8/numberunboxing/StaticMethodsWideUnboxingMixNumberUnboxingTest.java
new file mode 100644
index 0000000..4a03c52
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/numberunboxing/StaticMethodsWideUnboxingMixNumberUnboxingTest.java
@@ -0,0 +1,123 @@
+// Copyright (c) 2026, 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.numberunboxing;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+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.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.internal.StringUtils;
+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 StaticMethodsWideUnboxingMixNumberUnboxingTest extends TestBase {
+
+  private final String EXPECTED_RESULT =
+      StringUtils.lines(
+          "1", "5", "9", "2", "6", "10", "4", "8", "12", "5", "9", "13", "11", "15", "19", "12",
+          "16", "110", "14", "18", "112", "15", "19", "113");
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public StaticMethodsWideUnboxingMixNumberUnboxingTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testNumberUnboxing() throws Exception {
+    testForR8(parameters)
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        .addOptionsModification(opt -> opt.getTestingOptions().getNumberUnboxerOptions().enable())
+        .compile()
+        .inspect(this::assertUnboxing)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED_RESULT);
+  }
+
+  private void assertUnboxing(CodeInspector codeInspector) {
+    ClassSubject mainClass = codeInspector.clazz(Main.class);
+    assertThat(mainClass, isPresent());
+
+    MethodSubject methodSubject = mainClass.uniqueMethodWithOriginalName("printI");
+    assertThat(methodSubject, isPresent());
+    assertTrue(methodSubject.getProgramMethod().getParameter(0).isIntType());
+
+    methodSubject = mainClass.uniqueMethodWithOriginalName("printL");
+    assertThat(methodSubject, isPresent());
+    assertTrue(methodSubject.getProgramMethod().getParameter(0).isLongType());
+
+    methodSubject = mainClass.uniqueMethodWithOriginalName("printAll");
+    assertThat(methodSubject, isPresent());
+    for (int i = 0; i < 12; i += 2) {
+      assertTrue(methodSubject.getProgramMethod().getParameter(i).isIntType());
+      assertTrue(methodSubject.getProgramMethod().getParameter(i + 1).isLongType());
+    }
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      printAll(1, 2, 3, 4L, 5, 6, 7, 8L, 9, 10, 11, 12L);
+      printAll(11, 12, 13, 14L, 15, 16, 17, 18L, 19, 110, 111, 112L);
+    }
+
+    @NeverInline
+    private static void printAll(
+        int i,
+        long l,
+        Integer boxedI,
+        Long boxedL,
+        int i2,
+        long l2,
+        Integer boxedI2,
+        Long boxedL2,
+        int i3,
+        long l3,
+        Integer boxedI3,
+        Long boxedL3) {
+
+      System.out.println(i);
+      System.out.println(i2);
+      System.out.println(i3);
+      System.out.println(l);
+      System.out.println(l2);
+      System.out.println(l3);
+
+      printI(boxedI);
+      printI(boxedI2);
+      printI(boxedI3);
+      printL(boxedL);
+      printL(boxedL2);
+      printL(boxedL3);
+    }
+
+    @NeverInline
+    private static void printI(Integer boxed) {
+      System.out.println(boxed + 1);
+    }
+
+    @NeverInline
+    private static void printL(Long boxed) {
+      System.out.println(boxed + 1);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/numberunboxing/StaticMethodsWideUnboxingNumberUnboxingTest.java b/src/test/java/com/android/tools/r8/numberunboxing/StaticMethodsWideUnboxingNumberUnboxingTest.java
index c71b5bb..417ac5d 100644
--- a/src/test/java/com/android/tools/r8/numberunboxing/StaticMethodsWideUnboxingNumberUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/numberunboxing/StaticMethodsWideUnboxingNumberUnboxingTest.java
@@ -37,12 +37,11 @@
 
   @Test
   public void testNumberUnboxing() throws Exception {
-    testForR8(parameters.getBackend())
+    testForR8(parameters)
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         .addOptionsModification(opt -> opt.getTestingOptions().getNumberUnboxerOptions().enable())
-        .setMinApi(parameters)
         .compile()
         .inspect(this::assertUnboxing)
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/numberunboxing/VirtualMethodsNumberUnboxingTest.java b/src/test/java/com/android/tools/r8/numberunboxing/VirtualMethodsNumberUnboxingTest.java
index 9e4d9ae..679393e 100644
--- a/src/test/java/com/android/tools/r8/numberunboxing/VirtualMethodsNumberUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/numberunboxing/VirtualMethodsNumberUnboxingTest.java
@@ -37,12 +37,11 @@
 
   @Test
   public void testNumberUnboxing() throws Exception {
-    testForR8(parameters.getBackend())
+    testForR8(parameters)
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         .addOptionsModification(opt -> opt.getTestingOptions().getNumberUnboxerOptions().enable())
-        .setMinApi(parameters)
         .compile()
         .inspect(this::assertUnboxing)
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/numberunboxing/VirtualMethodsOverrideNumberUnboxingTest.java b/src/test/java/com/android/tools/r8/numberunboxing/VirtualMethodsOverrideNumberUnboxingTest.java
index 6105a6d..628d5d4 100644
--- a/src/test/java/com/android/tools/r8/numberunboxing/VirtualMethodsOverrideNumberUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/numberunboxing/VirtualMethodsOverrideNumberUnboxingTest.java
@@ -37,14 +37,13 @@
 
   @Test
   public void testNumberUnboxing() throws Throwable {
-    testForR8(parameters.getBackend())
+    testForR8(parameters)
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         .enableNeverClassInliningAnnotations()
         .addOptionsModification(opt -> opt.getTestingOptions().getNumberUnboxerOptions().enable())
-        .setMinApi(parameters)
         .compile()
         .inspect(this::assertUnboxing)
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/processkeeprules/sanitychecks/ProcessKeepRulesSanityCheckTest.java b/src/test/java/com/android/tools/r8/processkeeprules/sanitychecks/ProcessKeepRulesSanityCheckTest.java
index 1d2a23e..0b9b23b 100644
--- a/src/test/java/com/android/tools/r8/processkeeprules/sanitychecks/ProcessKeepRulesSanityCheckTest.java
+++ b/src/test/java/com/android/tools/r8/processkeeprules/sanitychecks/ProcessKeepRulesSanityCheckTest.java
@@ -48,6 +48,9 @@
     assertTrue(Files.exists(PROCESS_KEEP_RULES_JAR));
     assertTrue(Files.exists(PROCESS_KEEP_RULES_MAP));
     BooleanBox apiDatabaseSeen = new BooleanBox();
+    BooleanBox apiMissingSeen = new BooleanBox();
+    BooleanBox apiHiddenSeen = new BooleanBox();
+    BooleanBox jarHiddenSeen = new BooleanBox();
     BooleanBox licenseSeen = new BooleanBox();
     BooleanBox manifestSeen = new BooleanBox();
     BooleanBox versionSeen = new BooleanBox();
@@ -76,6 +79,12 @@
             licenseSeen.set();
           } else if (name.equals("resources/api_database.ser")) {
             apiDatabaseSeen.set();
+          } else if (name.equals("resources/missing.api.txt")) {
+            apiMissingSeen.set();
+          } else if (name.equals("resources/hidden.api.txt")) {
+            apiHiddenSeen.set();
+          } else if (name.equals("resources/hidden.jar.txt")) {
+            jarHiddenSeen.set();
           } else if (name.endsWith("/")) {
             fail("Unexpected directory entry '" + name + "'");
           } else {
@@ -84,6 +93,9 @@
         });
 
     assertTrue("Api database entry FOUND", apiDatabaseSeen.isFalse());
+    assertTrue("Api missing entry FOUND", apiMissingSeen.isFalse());
+    assertTrue("Api hidden entry FOUND", apiHiddenSeen.isFalse());
+    assertTrue("Api jar missing entry FOUND", jarHiddenSeen.isFalse());
     assertTrue("LICENSE entry NOT FOUND", licenseSeen.isTrue());
     assertTrue("META-INF/MANIFEST.MF entry NOT FOUND", manifestSeen.isTrue());
     assertTrue("com/android/tools/r8/Version.class entry NOT FOUND", versionSeen.isTrue());
diff --git a/src/test/java/com/android/tools/r8/redex/ReoptimizeToNativePcWithMappingTest.java b/src/test/java/com/android/tools/r8/redex/ReoptimizeToNativePcWithMappingTest.java
new file mode 100644
index 0000000..c65cf3f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/redex/ReoptimizeToNativePcWithMappingTest.java
@@ -0,0 +1,157 @@
+// Copyright (c) 2026, 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.redex;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.naming.retrace.StackTrace;
+import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.internal.Box;
+import com.android.tools.r8.utils.internal.FileUtils;
+import java.nio.file.Path;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ReoptimizeToNativePcWithMappingTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  // Cinnamon Bun outputs DEX version 039 which is only supported on Android 9.0 (Pie) and newer.
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimesStartingFromIncluding(Version.V9_0_0).build();
+  }
+
+  public ReoptimizeToNativePcWithMappingTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    AndroidApiLevel initialMinApi = AndroidApiLevel.N;
+    AndroidApiLevel reoptimizationMinApi = AndroidApiLevel.CINNAMON_BUN;
+
+    Box<String> currentFooMethodName = new Box<>();
+    Box<String> currentTestClassName = new Box<>();
+    R8TestCompileResult r8CompileResult =
+        testForR8(Backend.DEX)
+            .setMinApi(initialMinApi)
+            .setMode(CompilationMode.RELEASE)
+            .addProgramClasses(StacktracePreservationTestClass.class)
+            .addKeepRules(
+                "-keep,allowrepackage class "
+                    + StacktracePreservationTestClass.class.getName()
+                    + " { public static void main(java.lang.String[]); }",
+                "-repackageclasses repackaged")
+            .enableInliningAnnotations()
+            .compile()
+            .inspect(
+                inspector -> {
+                  ClassSubject clazz =
+                      inspector.clazz(
+                          Reference.classFromClass(StacktracePreservationTestClass.class));
+                  assertTrue(clazz.isPresent());
+                  assertTrue(clazz.getFinalName().startsWith("repackaged."));
+                  currentTestClassName.set(clazz.getFinalName());
+                  MethodSubject method = clazz.uniqueMethodWithOriginalName("foo");
+                  assertTrue(method.isPresent());
+                  currentFooMethodName.set(method.getFinalName());
+                  assertTrue("Expected debug info on N", method.hasLineNumberTable());
+                });
+
+    Path r8Output = r8CompileResult.writeToZip();
+    String r8Mapping = r8CompileResult.getProguardMap();
+    assertThat(
+        r8Mapping,
+        containsString(StacktracePreservationTestClass.class.getName() + " -> repackaged."));
+
+    r8CompileResult
+        .run(parameters.getRuntime(), currentTestClassName.get())
+        .assertFailureWithErrorThatThrows(RuntimeException.class)
+        .inspectOriginalStackTrace(
+            stacktrace -> {
+              assertThat(stacktrace, not(StackTrace.isSame(getExpectedStackTrace())));
+              StackTrace retracedStackTrace = stacktrace.retrace(r8Mapping);
+              assertThat(retracedStackTrace, StackTrace.isSame(getExpectedStackTrace()));
+            });
+
+    Path inputMap = temp.newFolder().toPath().resolve("input.map");
+    FileUtils.writeTextFile(inputMap, r8Mapping);
+
+    D8TestCompileResult d8CompileResult =
+        testForD8(Backend.DEX)
+            .setMinApi(reoptimizationMinApi)
+            .setExperimentalReoptimizeDex(true)
+            .internalEnableMappingOutput()
+            .setMode(CompilationMode.RELEASE)
+            .apply(b -> b.getBuilder().setProguardMapInputFile(inputMap))
+            .addProgramFiles(r8Output)
+            .compile()
+            .inspect(
+                inspector -> {
+                  ClassSubject clazz = inspector.clazz(currentTestClassName.get());
+                  assertTrue(clazz.isPresent());
+                  MethodSubject method =
+                      clazz.uniqueMethodWithOriginalName(currentFooMethodName.get());
+                  assertTrue(method.isPresent());
+                  assertNull(
+                      "Expected native debug info due to mapping output",
+                      method.getMethod().getCode().asDexCode().getDebugInfo());
+                });
+
+    // Verify that the output mapping is correct and composed.
+    String d8Mapping = d8CompileResult.getProguardMap();
+    assertThat(
+        d8Mapping,
+        containsString(StacktracePreservationTestClass.class.getName() + " -> repackaged."));
+
+    d8CompileResult
+        .run(parameters.getRuntime(), currentTestClassName.get())
+        .assertFailureWithErrorThatThrows(RuntimeException.class)
+        .inspectOriginalStackTrace(
+            stacktrace -> {
+              assertThat(stacktrace, not(StackTrace.isSame(getExpectedStackTrace())));
+              StackTrace retracedStackTrace = stacktrace.retrace(d8Mapping);
+              assertThat(retracedStackTrace, StackTrace.isSame(getExpectedStackTrace()));
+            });
+  }
+
+  private StackTrace getExpectedStackTrace() {
+    String className = StacktracePreservationTestClass.class.getName();
+    return StackTrace.builder()
+        .add(
+            StackTraceLine.builder()
+                .setClassName(className)
+                .setMethodName("foo")
+                .setFileName("StacktracePreservationTestClass.java")
+                .setLineNumber(19)
+                .build())
+        .add(
+            StackTraceLine.builder()
+                .setClassName(className)
+                .setMethodName("main")
+                .setFileName("StacktracePreservationTestClass.java")
+                .setLineNumber(12)
+                .build())
+        .build();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/CliParserTest.java b/src/test/java/com/android/tools/r8/utils/CliParserTest.java
index 7431575..960787d 100644
--- a/src/test/java/com/android/tools/r8/utils/CliParserTest.java
+++ b/src/test/java/com/android/tools/r8/utils/CliParserTest.java
@@ -255,12 +255,12 @@
   }
 
   @Test
-  public void testOverlapPrefixAndPrefix() {
+  public void testDuplicatePrefix() {
     CliParser<Builder> parser = new CliParser<>("Usage: test");
     parser.prefix1("--test", "[key]", "<val>", "Prefix.", (b, s, v) -> {});
     assertThrows(
         AssertionError.class,
-        () -> parser.prefix1("--test-now", "[key]", "<val>", "Prefix.", (b, s, v) -> {}));
+        () -> parser.prefix1("--test", "[key]", "<val>", "Prefix.", (b, s, v) -> {}));
   }
 
   @Test
@@ -298,16 +298,56 @@
     parser.parse(new String[] {"--flag:foo=bar"}, builder, errors::add);
 
     assertEquals(1, errors.size());
-    assertEquals("Option --flag:foo does not take a value.", errors.get(0));
+    assertEquals("Option --flag:foo does not take an argument.", errors.get(0));
     assertTrue(suffixes.isEmpty());
   }
 
   @Test
-  public void testOverlapPrefix0AndPrefix1() {
+  public void testPrefixWithAlternative() {
     CliParser<Builder> parser = new CliParser<>("Usage: test");
-    parser.prefix0("--test", "[prop]", "Prefix 0.", (b, s) -> {});
-    assertThrows(
-        AssertionError.class,
-        () -> parser.prefix1("--test-now", "[key]", "<val>", "Prefix 1.", (b, s, v) -> {}));
+    List<String> suffixes = new ArrayList<>();
+    parser.prefix0(
+        "--force-enable-assertions",
+        "[:class]",
+        "Enable assertions.",
+        (b, suffix) -> suffixes.add(suffix),
+        "--force-ea");
+
+    Builder builder = new Builder();
+    List<String> errors = new ArrayList<>();
+    parser.parse(
+        new String[] {"--force-enable-assertions:com.example.Foo", "--force-ea:com.example.Bar"},
+        builder,
+        errors::add);
+
+    assertTrue(errors.isEmpty());
+    assertEquals(ImmutableList.of(":com.example.Foo", ":com.example.Bar"), suffixes);
+  }
+
+  @Test
+  public void testOverlappingPrefixResolution() {
+    CliParser<Builder> parser = new CliParser<>("Usage: test");
+    List<String> matched = new ArrayList<>();
+    parser.prefix0(
+        "--force-passthrough-assertions",
+        "[:class]",
+        "Passthrough assertions.",
+        (b, s) -> matched.add("long" + s),
+        "--force-pa");
+
+    Builder builder = new Builder();
+    List<String> errors = new ArrayList<>();
+    parser.parse(
+        new String[] {
+          "--force-passthrough-assertions:pkg...",
+          "--force-passthrough-assertions",
+          "--force-pa:pkg...",
+          "--force-pa"
+        },
+        builder,
+        errors::add);
+
+    assertTrue(errors.isEmpty());
+    assertEquals(ImmutableList.of("long:pkg...", "long", "long:pkg...", "long"), matched);
   }
 }
diff --git a/src/test/java11/com/android/tools/r8/jdk9/backport/ArraysCompareUnsignedBackportJava9Test.java b/src/test/java11/com/android/tools/r8/jdk9/backport/ArraysCompareUnsignedBackportJava9Test.java
new file mode 100644
index 0000000..a74c2c7
--- /dev/null
+++ b/src/test/java11/com/android/tools/r8/jdk9/backport/ArraysCompareUnsignedBackportJava9Test.java
@@ -0,0 +1,347 @@
+// Copyright (c) 2026, 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.jdk9.backport;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.desugar.backports.AbstractBackportTest;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.Arrays;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public final class ArraysCompareUnsignedBackportJava9Test extends AbstractBackportTest {
+  @Parameters(name = "{0}")
+  public static Iterable<?> data() {
+    return TestBase.getTestParameters()
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+        .withDexRuntimes()
+        .withAllApiLevelsAlsoForCf()
+        .build();
+  }
+
+  public ArraysCompareUnsignedBackportJava9Test(TestParameters parameters) {
+    super(parameters, Arrays.class, Main.class);
+
+    // Arrays.compareUnsigned methods were added in Android T (API 33).
+    registerTarget(AndroidApiLevel.T, 119);
+  }
+
+  public static class Main extends MiniAssert {
+
+    public static void main(String[] args) {
+      testCompareUnsignedByte();
+      testCompareUnsignedByteRange();
+      testCompareUnsignedShort();
+      testCompareUnsignedShortRange();
+      testCompareUnsignedInt();
+      testCompareUnsignedIntRange();
+      testCompareUnsignedLong();
+      testCompareUnsignedLongRange();
+    }
+
+    private static void testCompareUnsignedByte() {
+      byte[] nullArray = System.currentTimeMillis() > 0 ? null : new byte[0];
+
+      assertTrue(Arrays.compareUnsigned(nullArray, nullArray) == 0);
+      assertTrue(Arrays.compareUnsigned(nullArray, new byte[0]) < 0);
+      assertTrue(Arrays.compareUnsigned(new byte[0], nullArray) > 0);
+
+      byte[] a = new byte[] {1, 2, 3};
+      assertTrue(Arrays.compareUnsigned(a, a) == 0);
+      assertTrue(Arrays.compareUnsigned(new byte[0], new byte[0]) == 0);
+      assertTrue(Arrays.compareUnsigned(new byte[] {1, 2, 3}, new byte[] {1, 2, 3}) == 0);
+      assertTrue(
+          Arrays.compareUnsigned(
+                  new byte[] {Byte.MIN_VALUE, -1, 0, 1, Byte.MAX_VALUE},
+                  new byte[] {Byte.MIN_VALUE, -1, 0, 1, Byte.MAX_VALUE})
+              == 0);
+
+      assertTrue(Arrays.compareUnsigned(new byte[] {1, 2}, new byte[] {1, 2, 3}) < 0);
+      assertTrue(Arrays.compareUnsigned(new byte[] {1, 2, 3}, new byte[] {1, 2}) > 0);
+
+      assertTrue(Arrays.compareUnsigned(new byte[] {0}, new byte[] {1}) < 0);
+      assertTrue(Arrays.compareUnsigned(new byte[] {1}, new byte[] {Byte.MAX_VALUE}) < 0);
+      assertTrue(
+          Arrays.compareUnsigned(new byte[] {Byte.MAX_VALUE}, new byte[] {Byte.MIN_VALUE}) < 0);
+      assertTrue(Arrays.compareUnsigned(new byte[] {Byte.MIN_VALUE}, new byte[] {(byte) -1}) < 0);
+      byte[] minusOne = new byte[] {(byte) -1};
+      byte[] zero = new byte[] {0};
+      byte[] max = new byte[] {Byte.MAX_VALUE};
+      byte[] min = new byte[] {Byte.MIN_VALUE};
+      assertTrue(Arrays.compareUnsigned(minusOne, zero) > 0);
+      assertTrue(Arrays.compareUnsigned(minusOne, max) > 0);
+      assertTrue(Arrays.compareUnsigned(minusOne, min) > 0);
+      assertTrue(Arrays.compareUnsigned(new byte[] {1, 2, (byte) -1}, new byte[] {1, 2, 0}) > 0);
+    }
+
+    private static void testCompareUnsignedByteRange() {
+      byte[] nullArray = System.currentTimeMillis() > 0 ? null : new byte[0];
+      byte[] a = new byte[] {-3, -2, -1, 0, 1, 2, 3};
+      byte[] b = new byte[] {Byte.MIN_VALUE, -1, 0, 1, Byte.MAX_VALUE};
+
+      assertTrue(Arrays.compareUnsigned(new byte[] {}, 0, 0, new byte[] {}, 0, 0) == 0);
+      assertTrue(Arrays.compareUnsigned(a, 0, 1, a, 0, 1) == 0);
+      assertTrue(Arrays.compareUnsigned(a, 2, 5, b, 1, 4) == 0);
+      assertTrue(Arrays.compareUnsigned(a, 1, 1, b, 2, 2) == 0);
+
+      assertTrue(Arrays.compareUnsigned(a, 2, 3, b, 2, 3) > 0);
+      assertTrue(Arrays.compareUnsigned(b, 2, 3, a, 2, 3) < 0);
+      assertTrue(Arrays.compareUnsigned(a, 2, 4, b, 1, 4) < 0);
+      assertTrue(Arrays.compareUnsigned(b, 1, 4, a, 2, 4) > 0);
+
+      try {
+        Arrays.compareUnsigned(nullArray, 0, 1, b, 1, 2);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.compareUnsigned(a, 0, 1, nullArray, 1, 2);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.compareUnsigned(a, 2, 1, b, 1, 2);
+        fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException expected) {
+      }
+      try {
+        Arrays.compareUnsigned(a, -1, 1, b, 1, 2);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+      try {
+        Arrays.compareUnsigned(a, 0, 10, b, 1, 2);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+    }
+
+    private static void testCompareUnsignedShort() {
+      short[] nullArray = System.currentTimeMillis() > 0 ? null : new short[0];
+
+      assertTrue(Arrays.compareUnsigned(nullArray, nullArray) == 0);
+      assertTrue(Arrays.compareUnsigned(nullArray, new short[0]) < 0);
+      assertTrue(Arrays.compareUnsigned(new short[0], nullArray) > 0);
+
+      short[] a = new short[] {1, 2, 3};
+      assertTrue(Arrays.compareUnsigned(a, a) == 0);
+      assertTrue(Arrays.compareUnsigned(new short[0], new short[0]) == 0);
+      assertTrue(Arrays.compareUnsigned(new short[] {1, 2, 3}, new short[] {1, 2, 3}) == 0);
+      assertTrue(
+          Arrays.compareUnsigned(
+                  new short[] {Short.MIN_VALUE, -1, 0, 1, Short.MAX_VALUE},
+                  new short[] {Short.MIN_VALUE, -1, 0, 1, Short.MAX_VALUE})
+              == 0);
+
+      assertTrue(Arrays.compareUnsigned(new short[] {1, 2}, new short[] {1, 2, 3}) < 0);
+      assertTrue(Arrays.compareUnsigned(new short[] {1, 2, 3}, new short[] {1, 2}) > 0);
+
+      assertTrue(Arrays.compareUnsigned(new short[] {0}, new short[] {1}) < 0);
+      assertTrue(Arrays.compareUnsigned(new short[] {1}, new short[] {Short.MAX_VALUE}) < 0);
+      assertTrue(
+          Arrays.compareUnsigned(new short[] {Short.MAX_VALUE}, new short[] {Short.MIN_VALUE}) < 0);
+      short[] minusOne = new short[] {(short) -1};
+      short[] zero = new short[] {0};
+      short[] max = new short[] {Short.MAX_VALUE};
+      short[] min = new short[] {Short.MIN_VALUE};
+      assertTrue(Arrays.compareUnsigned(minusOne, zero) > 0);
+      assertTrue(Arrays.compareUnsigned(minusOne, max) > 0);
+      assertTrue(Arrays.compareUnsigned(minusOne, min) > 0);
+      assertTrue(Arrays.compareUnsigned(new short[] {1, 2, (short) -1}, new short[] {1, 2, 0}) > 0);
+    }
+
+    private static void testCompareUnsignedShortRange() {
+      short[] nullArray = System.currentTimeMillis() > 0 ? null : new short[0];
+      short[] a = new short[] {-3, -2, -1, 0, 1, 2, 3};
+      short[] b = new short[] {Short.MIN_VALUE, -1, 0, 1, Short.MAX_VALUE};
+
+      assertTrue(Arrays.compareUnsigned(new short[] {}, 0, 0, new short[] {}, 0, 0) == 0);
+      assertTrue(Arrays.compareUnsigned(a, 0, 1, a, 0, 1) == 0);
+      assertTrue(Arrays.compareUnsigned(a, 2, 5, b, 1, 4) == 0);
+      assertTrue(Arrays.compareUnsigned(a, 1, 1, b, 2, 2) == 0);
+
+      assertTrue(Arrays.compareUnsigned(a, 2, 3, b, 2, 3) > 0);
+      assertTrue(Arrays.compareUnsigned(b, 2, 3, a, 2, 3) < 0);
+      assertTrue(Arrays.compareUnsigned(a, 2, 4, b, 1, 4) < 0);
+      assertTrue(Arrays.compareUnsigned(b, 1, 4, a, 2, 4) > 0);
+
+      try {
+        Arrays.compareUnsigned(nullArray, 0, 1, b, 1, 2);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.compareUnsigned(a, 0, 1, nullArray, 1, 2);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.compareUnsigned(a, 2, 1, b, 1, 2);
+        fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException expected) {
+      }
+      try {
+        Arrays.compareUnsigned(a, -1, 1, b, 1, 2);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+      try {
+        Arrays.compareUnsigned(a, 0, 10, b, 1, 2);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+    }
+
+    private static void testCompareUnsignedInt() {
+      int[] nullArray = System.currentTimeMillis() > 0 ? null : new int[0];
+
+      assertTrue(Arrays.compareUnsigned(nullArray, nullArray) == 0);
+      assertTrue(Arrays.compareUnsigned(nullArray, new int[0]) < 0);
+      assertTrue(Arrays.compareUnsigned(new int[0], nullArray) > 0);
+
+      int[] a = new int[] {1, 2, 3};
+      assertTrue(Arrays.compareUnsigned(a, a) == 0);
+      assertTrue(Arrays.compareUnsigned(new int[0], new int[0]) == 0);
+      assertTrue(Arrays.compareUnsigned(new int[] {1, 2, 3}, new int[] {1, 2, 3}) == 0);
+      assertTrue(
+          Arrays.compareUnsigned(
+                  new int[] {Integer.MIN_VALUE, -1, 0, 1, Integer.MAX_VALUE},
+                  new int[] {Integer.MIN_VALUE, -1, 0, 1, Integer.MAX_VALUE})
+              == 0);
+
+      assertTrue(Arrays.compareUnsigned(new int[] {1, 2}, new int[] {1, 2, 3}) < 0);
+      assertTrue(Arrays.compareUnsigned(new int[] {1, 2, 3}, new int[] {1, 2}) > 0);
+
+      assertTrue(Arrays.compareUnsigned(new int[] {0}, new int[] {1}) < 0);
+      assertTrue(Arrays.compareUnsigned(new int[] {1}, new int[] {Integer.MAX_VALUE}) < 0);
+      assertTrue(
+          Arrays.compareUnsigned(new int[] {Integer.MAX_VALUE}, new int[] {Integer.MIN_VALUE}) < 0);
+      assertTrue(Arrays.compareUnsigned(new int[] {Integer.MIN_VALUE}, new int[] {-1}) < 0);
+      assertTrue(Arrays.compareUnsigned(new int[] {-1}, new int[] {0}) > 0);
+      assertTrue(Arrays.compareUnsigned(new int[] {-1}, new int[] {Integer.MAX_VALUE}) > 0);
+      assertTrue(Arrays.compareUnsigned(new int[] {-1}, new int[] {Integer.MIN_VALUE}) > 0);
+      assertTrue(Arrays.compareUnsigned(new int[] {1, 2, -1}, new int[] {1, 2, 0}) > 0);
+    }
+
+    private static void testCompareUnsignedIntRange() {
+      int[] nullArray = System.currentTimeMillis() > 0 ? null : new int[0];
+      int[] a = new int[] {-3, -2, -1, 0, 1, 2, 3};
+      int[] b = new int[] {Integer.MIN_VALUE, -1, 0, 1, Integer.MAX_VALUE};
+
+      assertTrue(Arrays.compareUnsigned(new int[] {}, 0, 0, new int[] {}, 0, 0) == 0);
+      assertTrue(Arrays.compareUnsigned(a, 0, 1, a, 0, 1) == 0);
+      assertTrue(Arrays.compareUnsigned(a, 2, 5, b, 1, 4) == 0);
+      assertTrue(Arrays.compareUnsigned(a, 1, 1, b, 2, 2) == 0);
+
+      assertTrue(Arrays.compareUnsigned(a, 2, 3, b, 2, 3) > 0);
+      assertTrue(Arrays.compareUnsigned(b, 2, 3, a, 2, 3) < 0);
+      assertTrue(Arrays.compareUnsigned(a, 2, 4, b, 1, 4) < 0);
+      assertTrue(Arrays.compareUnsigned(b, 1, 4, a, 2, 4) > 0);
+
+      try {
+        Arrays.compareUnsigned(nullArray, 0, 1, b, 1, 2);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.compareUnsigned(a, 0, 1, nullArray, 1, 2);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.compareUnsigned(a, 2, 1, b, 1, 2);
+        fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException expected) {
+      }
+      try {
+        Arrays.compareUnsigned(a, -1, 1, b, 1, 2);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+      try {
+        Arrays.compareUnsigned(a, 0, 10, b, 1, 2);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+    }
+
+    private static void testCompareUnsignedLong() {
+      long[] nullArray = System.currentTimeMillis() > 0 ? null : new long[0];
+
+      assertTrue(Arrays.compareUnsigned(nullArray, nullArray) == 0);
+      assertTrue(Arrays.compareUnsigned(nullArray, new long[0]) < 0);
+      assertTrue(Arrays.compareUnsigned(new long[0], nullArray) > 0);
+
+      long[] a = new long[] {1L, 2L, 3L};
+      assertTrue(Arrays.compareUnsigned(a, a) == 0);
+      assertTrue(Arrays.compareUnsigned(new long[0], new long[0]) == 0);
+      assertTrue(Arrays.compareUnsigned(new long[] {1L, 2L, 3L}, new long[] {1L, 2L, 3L}) == 0);
+      assertTrue(
+          Arrays.compareUnsigned(
+                  new long[] {Long.MIN_VALUE, -1L, 0L, 1L, Long.MAX_VALUE},
+                  new long[] {Long.MIN_VALUE, -1L, 0L, 1L, Long.MAX_VALUE})
+              == 0);
+
+      assertTrue(Arrays.compareUnsigned(new long[] {1L, 2L}, new long[] {1L, 2L, 3L}) < 0);
+      assertTrue(Arrays.compareUnsigned(new long[] {1L, 2L, 3L}, new long[] {1L, 2L}) > 0);
+
+      assertTrue(Arrays.compareUnsigned(new long[] {0L}, new long[] {1L}) < 0);
+      assertTrue(Arrays.compareUnsigned(new long[] {1L}, new long[] {Long.MAX_VALUE}) < 0);
+      assertTrue(
+          Arrays.compareUnsigned(new long[] {Long.MAX_VALUE}, new long[] {Long.MIN_VALUE}) < 0);
+      assertTrue(Arrays.compareUnsigned(new long[] {Long.MIN_VALUE}, new long[] {-1L}) < 0);
+      assertTrue(Arrays.compareUnsigned(new long[] {-1L}, new long[] {0L}) > 0);
+      assertTrue(Arrays.compareUnsigned(new long[] {-1L}, new long[] {Long.MAX_VALUE}) > 0);
+      assertTrue(Arrays.compareUnsigned(new long[] {-1L}, new long[] {Long.MIN_VALUE}) > 0);
+      assertTrue(Arrays.compareUnsigned(new long[] {1L, 2L, -1L}, new long[] {1L, 2L, 0L}) > 0);
+    }
+
+    private static void testCompareUnsignedLongRange() {
+      long[] nullArray = System.currentTimeMillis() > 0 ? null : new long[0];
+      long[] a = new long[] {-3L, -2L, -1L, 0L, 1L, 2L, 3L};
+      long[] b = new long[] {Long.MIN_VALUE, -1L, 0L, 1L, Long.MAX_VALUE};
+
+      assertTrue(Arrays.compareUnsigned(new long[] {}, 0, 0, new long[] {}, 0, 0) == 0);
+      assertTrue(Arrays.compareUnsigned(a, 0, 1, a, 0, 1) == 0);
+      assertTrue(Arrays.compareUnsigned(a, 2, 5, b, 1, 4) == 0);
+      assertTrue(Arrays.compareUnsigned(a, 1, 1, b, 2, 2) == 0);
+
+      assertTrue(Arrays.compareUnsigned(a, 2, 3, b, 2, 3) > 0);
+      assertTrue(Arrays.compareUnsigned(b, 2, 3, a, 2, 3) < 0);
+      assertTrue(Arrays.compareUnsigned(a, 2, 4, b, 1, 4) < 0);
+      assertTrue(Arrays.compareUnsigned(b, 1, 4, a, 2, 4) > 0);
+
+      try {
+        Arrays.compareUnsigned(nullArray, 0, 1, b, 1, 2);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.compareUnsigned(a, 0, 1, nullArray, 1, 2);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.compareUnsigned(a, 2, 1, b, 1, 2);
+        fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException expected) {
+      }
+      try {
+        Arrays.compareUnsigned(a, -1, 1, b, 1, 2);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+      try {
+        Arrays.compareUnsigned(a, 0, 10, b, 1, 2);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+    }
+  }
+}
diff --git a/src/test/java11/com/android/tools/r8/jdk9/backport/ArraysMismatchBackportJava9Test.java b/src/test/java11/com/android/tools/r8/jdk9/backport/ArraysMismatchBackportJava9Test.java
new file mode 100644
index 0000000..b7af956
--- /dev/null
+++ b/src/test/java11/com/android/tools/r8/jdk9/backport/ArraysMismatchBackportJava9Test.java
@@ -0,0 +1,743 @@
+// Copyright (c) 2026, 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.jdk9.backport;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.desugar.backports.AbstractBackportTest;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.Arrays;
+import java.util.Comparator;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public final class ArraysMismatchBackportJava9Test extends AbstractBackportTest {
+  @Parameters(name = "{0}")
+  public static Iterable<?> data() {
+    return TestBase.getTestParameters()
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+        .withDexRuntimes()
+        .withAllApiLevelsAlsoForCf()
+        .build();
+  }
+
+  public ArraysMismatchBackportJava9Test(TestParameters parameters) {
+    super(parameters, Arrays.class, Main.class);
+
+    // Arrays.mismatch methods were added in Android T (API 33).
+    registerTarget(AndroidApiLevel.T, 202);
+  }
+
+  public static class Main extends MiniAssert {
+
+    public static void main(String[] args) {
+      testMismatchBoolean();
+      testMismatchBooleanRange();
+      testMismatchByte();
+      testMismatchByteRange();
+      testMismatchChar();
+      testMismatchCharRange();
+      testMismatchShort();
+      testMismatchShortRange();
+      testMismatchInt();
+      testMismatchIntRange();
+      testMismatchLong();
+      testMismatchLongRange();
+      testMismatchFloat();
+      testMismatchFloatRange();
+      testMismatchDouble();
+      testMismatchDoubleRange();
+      testMismatchObject();
+      testMismatchObjectRange();
+      testMismatchComparator();
+      testMismatchComparatorRange();
+    }
+
+    private static void testMismatchBoolean() {
+      boolean[] a = new boolean[] {true, false, true};
+      boolean[] b = new boolean[] {true, false, true};
+      boolean[] c = new boolean[] {true, true, true};
+      boolean[] d = new boolean[] {true, false};
+      boolean[] empty = new boolean[0];
+      boolean[] nullArray = System.currentTimeMillis() > 0 ? null : new boolean[0];
+
+      assertEquals(-1, Arrays.mismatch(a, a));
+      assertEquals(-1, Arrays.mismatch(empty, empty));
+      assertEquals(-1, Arrays.mismatch(a, b));
+      assertEquals(1, Arrays.mismatch(a, c));
+      assertEquals(2, Arrays.mismatch(a, d));
+      assertEquals(2, Arrays.mismatch(d, a));
+      assertEquals(0, Arrays.mismatch(new boolean[] {true}, new boolean[] {false}));
+
+      try {
+        Arrays.mismatch(nullArray, b);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, nullArray);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+    }
+
+    private static void testMismatchBooleanRange() {
+      boolean[] a = new boolean[] {false, true, false, true, false};
+      boolean[] b = new boolean[] {true, true, false, true, true};
+      boolean[] nullArray = System.currentTimeMillis() > 0 ? null : new boolean[0];
+
+      assertEquals(-1, Arrays.mismatch(a, 0, 0, b, 0, 0));
+      assertEquals(-1, Arrays.mismatch(a, 1, 4, b, 1, 4));
+      assertEquals(-1, Arrays.mismatch(a, 0, 5, a, 0, 5));
+      assertEquals(0, Arrays.mismatch(a, 0, 3, b, 0, 3));
+      assertEquals(2, Arrays.mismatch(a, 1, 3, b, 1, 4));
+      assertEquals(2, Arrays.mismatch(b, 1, 4, a, 1, 3));
+
+      try {
+        Arrays.mismatch(nullArray, 0, 1, b, 0, 1);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 0, 1, nullArray, 0, 1);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 2, 1, b, 0, 1);
+        fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException expected) {
+      }
+      try {
+        Arrays.mismatch(a, -1, 1, b, 0, 1);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 0, 10, b, 0, 1);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+    }
+
+    private static void testMismatchByte() {
+      byte[] a = new byte[] {1, 2, 3};
+      byte[] b = new byte[] {1, 2, 3};
+      byte[] c = new byte[] {1, 4, 3};
+      byte[] d = new byte[] {1, 2};
+      byte[] empty = new byte[0];
+      byte[] nullArray = System.currentTimeMillis() > 0 ? null : new byte[0];
+
+      assertEquals(-1, Arrays.mismatch(a, a));
+      assertEquals(-1, Arrays.mismatch(empty, empty));
+      assertEquals(-1, Arrays.mismatch(a, b));
+      assertEquals(1, Arrays.mismatch(a, c));
+      assertEquals(2, Arrays.mismatch(a, d));
+      assertEquals(2, Arrays.mismatch(d, a));
+      assertEquals(0, Arrays.mismatch(new byte[] {1}, new byte[] {2}));
+
+      try {
+        Arrays.mismatch(nullArray, b);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, nullArray);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+    }
+
+    private static void testMismatchByteRange() {
+      byte[] a = new byte[] {0, 1, 2, 3, 0};
+      byte[] b = new byte[] {9, 1, 2, 3, 9};
+      byte[] nullArray = System.currentTimeMillis() > 0 ? null : new byte[0];
+
+      assertEquals(-1, Arrays.mismatch(a, 0, 0, b, 0, 0));
+      assertEquals(-1, Arrays.mismatch(a, 1, 4, b, 1, 4));
+      assertEquals(-1, Arrays.mismatch(a, 0, 5, a, 0, 5));
+      assertEquals(0, Arrays.mismatch(a, 0, 3, b, 0, 3));
+      assertEquals(2, Arrays.mismatch(a, 1, 3, b, 1, 4));
+      assertEquals(2, Arrays.mismatch(b, 1, 4, a, 1, 3));
+
+      try {
+        Arrays.mismatch(nullArray, 0, 1, b, 0, 1);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 0, 1, nullArray, 0, 1);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 2, 1, b, 0, 1);
+        fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException expected) {
+      }
+      try {
+        Arrays.mismatch(a, -1, 1, b, 0, 1);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 0, 10, b, 0, 1);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+    }
+
+    private static void testMismatchChar() {
+      char[] a = new char[] {'a', 'b', 'c'};
+      char[] b = new char[] {'a', 'b', 'c'};
+      char[] c = new char[] {'a', 'x', 'c'};
+      char[] d = new char[] {'a', 'b'};
+      char[] empty = new char[0];
+      char[] nullArray = System.currentTimeMillis() > 0 ? null : new char[0];
+
+      assertEquals(-1, Arrays.mismatch(a, a));
+      assertEquals(-1, Arrays.mismatch(empty, empty));
+      assertEquals(-1, Arrays.mismatch(a, b));
+      assertEquals(1, Arrays.mismatch(a, c));
+      assertEquals(2, Arrays.mismatch(a, d));
+      assertEquals(2, Arrays.mismatch(d, a));
+      assertEquals(0, Arrays.mismatch(new char[] {'a'}, new char[] {'z'}));
+
+      try {
+        Arrays.mismatch(nullArray, b);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, nullArray);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+    }
+
+    private static void testMismatchCharRange() {
+      char[] a = new char[] {'x', 'a', 'b', 'c', 'x'};
+      char[] b = new char[] {'y', 'a', 'b', 'c', 'y'};
+      char[] nullArray = System.currentTimeMillis() > 0 ? null : new char[0];
+
+      assertEquals(-1, Arrays.mismatch(a, 0, 0, b, 0, 0));
+      assertEquals(-1, Arrays.mismatch(a, 1, 4, b, 1, 4));
+      assertEquals(-1, Arrays.mismatch(a, 0, 5, a, 0, 5));
+      assertEquals(0, Arrays.mismatch(a, 0, 3, b, 0, 3));
+      assertEquals(2, Arrays.mismatch(a, 1, 3, b, 1, 4));
+      assertEquals(2, Arrays.mismatch(b, 1, 4, a, 1, 3));
+
+      try {
+        Arrays.mismatch(nullArray, 0, 1, b, 0, 1);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 0, 1, nullArray, 0, 1);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 2, 1, b, 0, 1);
+        fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException expected) {
+      }
+      try {
+        Arrays.mismatch(a, -1, 1, b, 0, 1);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 0, 10, b, 0, 1);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+    }
+
+    private static void testMismatchShort() {
+      short[] a = new short[] {1, 2, 3};
+      short[] b = new short[] {1, 2, 3};
+      short[] c = new short[] {1, 4, 3};
+      short[] d = new short[] {1, 2};
+      short[] empty = new short[0];
+      short[] nullArray = System.currentTimeMillis() > 0 ? null : new short[0];
+
+      assertEquals(-1, Arrays.mismatch(a, a));
+      assertEquals(-1, Arrays.mismatch(empty, empty));
+      assertEquals(-1, Arrays.mismatch(a, b));
+      assertEquals(1, Arrays.mismatch(a, c));
+      assertEquals(2, Arrays.mismatch(a, d));
+      assertEquals(2, Arrays.mismatch(d, a));
+      assertEquals(0, Arrays.mismatch(new short[] {1}, new short[] {2}));
+
+      try {
+        Arrays.mismatch(nullArray, b);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, nullArray);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+    }
+
+    private static void testMismatchShortRange() {
+      short[] a = new short[] {0, 1, 2, 3, 0};
+      short[] b = new short[] {9, 1, 2, 3, 9};
+      short[] nullArray = System.currentTimeMillis() > 0 ? null : new short[0];
+
+      assertEquals(-1, Arrays.mismatch(a, 0, 0, b, 0, 0));
+      assertEquals(-1, Arrays.mismatch(a, 1, 4, b, 1, 4));
+      assertEquals(-1, Arrays.mismatch(a, 0, 5, a, 0, 5));
+      assertEquals(0, Arrays.mismatch(a, 0, 3, b, 0, 3));
+      assertEquals(2, Arrays.mismatch(a, 1, 3, b, 1, 4));
+      assertEquals(2, Arrays.mismatch(b, 1, 4, a, 1, 3));
+
+      try {
+        Arrays.mismatch(nullArray, 0, 1, b, 0, 1);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 0, 1, nullArray, 0, 1);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 2, 1, b, 0, 1);
+        fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException expected) {
+      }
+      try {
+        Arrays.mismatch(a, -1, 1, b, 0, 1);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 0, 10, b, 0, 1);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+    }
+
+    private static void testMismatchInt() {
+      int[] a = new int[] {1, 2, 3};
+      int[] b = new int[] {1, 2, 3};
+      int[] c = new int[] {1, 4, 3};
+      int[] d = new int[] {1, 2};
+      int[] empty = new int[0];
+      int[] nullArray = System.currentTimeMillis() > 0 ? null : new int[0];
+
+      assertEquals(-1, Arrays.mismatch(a, a));
+      assertEquals(-1, Arrays.mismatch(empty, empty));
+      assertEquals(-1, Arrays.mismatch(a, b));
+      assertEquals(1, Arrays.mismatch(a, c));
+      assertEquals(2, Arrays.mismatch(a, d));
+      assertEquals(2, Arrays.mismatch(d, a));
+      assertEquals(0, Arrays.mismatch(new int[] {1}, new int[] {2}));
+
+      try {
+        Arrays.mismatch(nullArray, b);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, nullArray);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+    }
+
+    private static void testMismatchIntRange() {
+      int[] a = new int[] {0, 1, 2, 3, 0};
+      int[] b = new int[] {9, 1, 2, 3, 9};
+      int[] nullArray = System.currentTimeMillis() > 0 ? null : new int[0];
+
+      assertEquals(-1, Arrays.mismatch(a, 0, 0, b, 0, 0));
+      assertEquals(-1, Arrays.mismatch(a, 1, 4, b, 1, 4));
+      assertEquals(-1, Arrays.mismatch(a, 0, 5, a, 0, 5));
+      assertEquals(0, Arrays.mismatch(a, 0, 3, b, 0, 3));
+      assertEquals(2, Arrays.mismatch(a, 1, 3, b, 1, 4));
+      assertEquals(2, Arrays.mismatch(b, 1, 4, a, 1, 3));
+
+      try {
+        Arrays.mismatch(nullArray, 0, 1, b, 0, 1);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 0, 1, nullArray, 0, 1);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 2, 1, b, 0, 1);
+        fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException expected) {
+      }
+      try {
+        Arrays.mismatch(a, -1, 1, b, 0, 1);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 0, 10, b, 0, 1);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+    }
+
+    private static void testMismatchLong() {
+      long[] a = new long[] {1L, 2L, 3L};
+      long[] b = new long[] {1L, 2L, 3L};
+      long[] c = new long[] {1L, 4L, 3L};
+      long[] d = new long[] {1L, 2L};
+      long[] empty = new long[0];
+      long[] nullArray = System.currentTimeMillis() > 0 ? null : new long[0];
+
+      assertEquals(-1, Arrays.mismatch(a, a));
+      assertEquals(-1, Arrays.mismatch(empty, empty));
+      assertEquals(-1, Arrays.mismatch(a, b));
+      assertEquals(1, Arrays.mismatch(a, c));
+      assertEquals(2, Arrays.mismatch(a, d));
+      assertEquals(2, Arrays.mismatch(d, a));
+      assertEquals(0, Arrays.mismatch(new long[] {1L}, new long[] {2L}));
+
+      try {
+        Arrays.mismatch(nullArray, b);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, nullArray);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+    }
+
+    private static void testMismatchLongRange() {
+      long[] a = new long[] {0L, 1L, 2L, 3L, 0L};
+      long[] b = new long[] {9L, 1L, 2L, 3L, 9L};
+      long[] nullArray = System.currentTimeMillis() > 0 ? null : new long[0];
+
+      assertEquals(-1, Arrays.mismatch(a, 0, 0, b, 0, 0));
+      assertEquals(-1, Arrays.mismatch(a, 1, 4, b, 1, 4));
+      assertEquals(-1, Arrays.mismatch(a, 0, 5, a, 0, 5));
+      assertEquals(0, Arrays.mismatch(a, 0, 3, b, 0, 3));
+      assertEquals(2, Arrays.mismatch(a, 1, 3, b, 1, 4));
+      assertEquals(2, Arrays.mismatch(b, 1, 4, a, 1, 3));
+
+      try {
+        Arrays.mismatch(nullArray, 0, 1, b, 0, 1);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 0, 1, nullArray, 0, 1);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 2, 1, b, 0, 1);
+        fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException expected) {
+      }
+      try {
+        Arrays.mismatch(a, -1, 1, b, 0, 1);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 0, 10, b, 0, 1);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+    }
+
+    private static void testMismatchFloat() {
+      float[] a = new float[] {1.0f, Float.NaN, 3.0f, 0.0f};
+      float[] b = new float[] {1.0f, Float.NaN, 3.0f, 0.0f};
+      float[] c = new float[] {1.0f, Float.NaN, 3.0f, -0.0f};
+      float[] d = new float[] {1.0f, Float.NaN};
+      float[] empty = new float[0];
+      float[] nullArray = System.currentTimeMillis() > 0 ? null : new float[0];
+
+      assertEquals(-1, Arrays.mismatch(a, a));
+      assertEquals(-1, Arrays.mismatch(empty, empty));
+      assertEquals(-1, Arrays.mismatch(a, b));
+      assertEquals(3, Arrays.mismatch(a, c)); // 0.0f != -0.0f in bit comparison
+      assertEquals(2, Arrays.mismatch(a, d));
+      assertEquals(2, Arrays.mismatch(d, a));
+      assertEquals(-1, Arrays.mismatch(new float[] {Float.NaN}, new float[] {Float.NaN}));
+
+      try {
+        Arrays.mismatch(nullArray, b);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, nullArray);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+    }
+
+    private static void testMismatchFloatRange() {
+      float[] a = new float[] {0.0f, 1.0f, Float.NaN, 3.0f, 0.0f};
+      float[] b = new float[] {9.0f, 1.0f, Float.NaN, 3.0f, 9.0f};
+      float[] nullArray = System.currentTimeMillis() > 0 ? null : new float[0];
+
+      assertEquals(-1, Arrays.mismatch(a, 0, 0, b, 0, 0));
+      assertEquals(-1, Arrays.mismatch(a, 1, 4, b, 1, 4));
+      assertEquals(-1, Arrays.mismatch(a, 0, 5, a, 0, 5));
+      assertEquals(0, Arrays.mismatch(a, 0, 3, b, 0, 3));
+      assertEquals(2, Arrays.mismatch(a, 1, 3, b, 1, 4));
+      assertEquals(2, Arrays.mismatch(b, 1, 4, a, 1, 3));
+
+      try {
+        Arrays.mismatch(nullArray, 0, 1, b, 0, 1);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 0, 1, nullArray, 0, 1);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 2, 1, b, 0, 1);
+        fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException expected) {
+      }
+      try {
+        Arrays.mismatch(a, -1, 1, b, 0, 1);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 0, 10, b, 0, 1);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+    }
+
+    private static void testMismatchDouble() {
+      double[] a = new double[] {1.0, Double.NaN, 3.0, 0.0};
+      double[] b = new double[] {1.0, Double.NaN, 3.0, 0.0};
+      double[] c = new double[] {1.0, Double.NaN, 3.0, -0.0};
+      double[] d = new double[] {1.0, Double.NaN};
+      double[] empty = new double[0];
+      double[] nullArray = System.currentTimeMillis() > 0 ? null : new double[0];
+
+      assertEquals(-1, Arrays.mismatch(a, a));
+      assertEquals(-1, Arrays.mismatch(empty, empty));
+      assertEquals(-1, Arrays.mismatch(a, b));
+      assertEquals(3, Arrays.mismatch(a, c)); // 0.0 != -0.0 in bit comparison
+      assertEquals(2, Arrays.mismatch(a, d));
+      assertEquals(2, Arrays.mismatch(d, a));
+      assertEquals(-1, Arrays.mismatch(new double[] {Double.NaN}, new double[] {Double.NaN}));
+
+      try {
+        Arrays.mismatch(nullArray, b);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, nullArray);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+    }
+
+    private static void testMismatchDoubleRange() {
+      double[] a = new double[] {0.0, 1.0, Double.NaN, 3.0, 0.0};
+      double[] b = new double[] {9.0, 1.0, Double.NaN, 3.0, 9.0};
+      double[] nullArray = System.currentTimeMillis() > 0 ? null : new double[0];
+
+      assertEquals(-1, Arrays.mismatch(a, 0, 0, b, 0, 0));
+      assertEquals(-1, Arrays.mismatch(a, 1, 4, b, 1, 4));
+      assertEquals(-1, Arrays.mismatch(a, 0, 5, a, 0, 5));
+      assertEquals(0, Arrays.mismatch(a, 0, 3, b, 0, 3));
+      assertEquals(2, Arrays.mismatch(a, 1, 3, b, 1, 4));
+      assertEquals(2, Arrays.mismatch(b, 1, 4, a, 1, 3));
+
+      try {
+        Arrays.mismatch(nullArray, 0, 1, b, 0, 1);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 0, 1, nullArray, 0, 1);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 2, 1, b, 0, 1);
+        fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException expected) {
+      }
+      try {
+        Arrays.mismatch(a, -1, 1, b, 0, 1);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 0, 10, b, 0, 1);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+    }
+
+    private static void testMismatchObject() {
+      Object[] a = new Object[] {"a", null, "c"};
+      Object[] b = new Object[] {"a", null, "c"};
+      Object[] c = new Object[] {"a", "b", "c"};
+      Object[] d = new Object[] {"a", null};
+      Object[] empty = new Object[0];
+      Object[] nullArray = System.currentTimeMillis() > 0 ? null : new Object[0];
+
+      assertEquals(-1, Arrays.mismatch(a, a));
+      assertEquals(-1, Arrays.mismatch(empty, empty));
+      assertEquals(-1, Arrays.mismatch(a, b));
+      assertEquals(1, Arrays.mismatch(a, c));
+      assertEquals(2, Arrays.mismatch(a, d));
+      assertEquals(2, Arrays.mismatch(d, a));
+      assertEquals(0, Arrays.mismatch(new Object[] {"x"}, new Object[] {"y"}));
+
+      try {
+        Arrays.mismatch(nullArray, b);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, nullArray);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+    }
+
+    private static void testMismatchObjectRange() {
+      Object[] a = new Object[] {"x", "a", null, "c", "x"};
+      Object[] b = new Object[] {"y", "a", null, "c", "y"};
+      Object[] nullArray = System.currentTimeMillis() > 0 ? null : new Object[0];
+
+      assertEquals(-1, Arrays.mismatch(a, 0, 0, b, 0, 0));
+      assertEquals(-1, Arrays.mismatch(a, 1, 4, b, 1, 4));
+      assertEquals(-1, Arrays.mismatch(a, 0, 5, a, 0, 5));
+      assertEquals(0, Arrays.mismatch(a, 0, 3, b, 0, 3));
+      assertEquals(2, Arrays.mismatch(a, 1, 3, b, 1, 4));
+      assertEquals(2, Arrays.mismatch(b, 1, 4, a, 1, 3));
+
+      try {
+        Arrays.mismatch(nullArray, 0, 1, b, 0, 1);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 0, 1, nullArray, 0, 1);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 2, 1, b, 0, 1);
+        fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException expected) {
+      }
+      try {
+        Arrays.mismatch(a, -1, 1, b, 0, 1);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 0, 10, b, 0, 1);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+    }
+
+    private static void testMismatchComparator() {
+      String[] a = new String[] {"a", "B", "c"};
+      String[] b = new String[] {"A", "b", "C"};
+      String[] c = new String[] {"a", "b", "x"};
+      String[] d = new String[] {"a", "b"};
+      String[] empty = new String[0];
+      String[] nullArray = System.currentTimeMillis() > 0 ? null : new String[0];
+      Comparator<String> caseInsensitive = String.CASE_INSENSITIVE_ORDER;
+
+      assertEquals(-1, Arrays.mismatch(a, a, caseInsensitive));
+      assertEquals(-1, Arrays.mismatch(empty, empty, caseInsensitive));
+      assertEquals(-1, Arrays.mismatch(a, b, caseInsensitive));
+      assertEquals(2, Arrays.mismatch(a, c, caseInsensitive));
+      assertEquals(2, Arrays.mismatch(a, d, caseInsensitive));
+      assertEquals(2, Arrays.mismatch(d, a, caseInsensitive));
+      assertEquals(0, Arrays.mismatch(new String[] {"a"}, new String[] {"z"}, caseInsensitive));
+
+      try {
+        Arrays.mismatch(nullArray, b, caseInsensitive);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, nullArray, caseInsensitive);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, b, null);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+    }
+
+    private static void testMismatchComparatorRange() {
+      String[] a = new String[] {"x", "a", "B", "c", "x"};
+      String[] b = new String[] {"y", "A", "b", "C", "y"};
+      String[] nullArray = System.currentTimeMillis() > 0 ? null : new String[0];
+      Comparator<String> caseInsensitive = String.CASE_INSENSITIVE_ORDER;
+
+      assertEquals(-1, Arrays.mismatch(a, 0, 0, b, 0, 0, caseInsensitive));
+      assertEquals(-1, Arrays.mismatch(a, 1, 4, b, 1, 4, caseInsensitive));
+      assertEquals(-1, Arrays.mismatch(a, 0, 5, a, 0, 5, caseInsensitive));
+      assertEquals(0, Arrays.mismatch(a, 0, 3, b, 0, 3, caseInsensitive));
+      assertEquals(2, Arrays.mismatch(a, 1, 3, b, 1, 4, caseInsensitive));
+      assertEquals(2, Arrays.mismatch(b, 1, 4, a, 1, 3, caseInsensitive));
+
+      try {
+        Arrays.mismatch(nullArray, 0, 1, b, 0, 1, caseInsensitive);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 0, 1, nullArray, 0, 1, caseInsensitive);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 0, 1, b, 0, 1, null);
+        fail("Expected NullPointerException");
+      } catch (NullPointerException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 2, 1, b, 0, 1, caseInsensitive);
+        fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException expected) {
+      }
+      try {
+        Arrays.mismatch(a, -1, 1, b, 0, 1, caseInsensitive);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+      try {
+        Arrays.mismatch(a, 0, 10, b, 0, 1, caseInsensitive);
+        fail("Expected ArrayIndexOutOfBoundsException");
+      } catch (ArrayIndexOutOfBoundsException expected) {
+      }
+    }
+  }
+}
diff --git a/src/test/java8/apimodel/com/android/tools/r8/ApiDatabaseGeneratorTestHelper.java b/src/test/java8/apimodel/com/android/tools/r8/ApiDatabaseGeneratorTestHelper.java
new file mode 100644
index 0000000..e190e8a
--- /dev/null
+++ b/src/test/java8/apimodel/com/android/tools/r8/ApiDatabaseGeneratorTestHelper.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2026, 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 com.android.tools.r8.apimodel.ParsedApiClass;
+import com.android.tools.r8.apimodel.ParsedApiClassTrimming.JarTrimmer;
+import com.android.tools.r8.apimodel.ParsedApiClassTrimming.Trimmer;
+import java.util.Collection;
+import java.util.function.Function;
+
+public class ApiDatabaseGeneratorTestHelper {
+  public static <E extends Throwable> Collection<ParsedApiClass> generateClasses(
+      ApiDatabaseGeneratorCommand command, Function<JarTrimmer, Trimmer<E>> jarTrimmerWrapper)
+      throws ApiDatabaseGeneratorException, E {
+    return ApiDatabaseGenerator.generateClasses(command, jarTrimmerWrapper);
+  }
+}
diff --git a/src/test/java8/apimodel/com/android/tools/r8/androidapi/GenerateApiAmendments.java b/src/test/java8/apimodel/com/android/tools/r8/androidapi/GenerateApiAmendments.java
new file mode 100644
index 0000000..b9dc369
--- /dev/null
+++ b/src/test/java8/apimodel/com/android/tools/r8/androidapi/GenerateApiAmendments.java
@@ -0,0 +1,271 @@
+// Copyright (c) 2026, 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 static org.junit.Assert.assertEquals;
+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.ToolHelper;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.internal.FileUtils;
+import com.android.tools.r8.utils.internal.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+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 GenerateApiAmendments extends TestBase {
+
+  private static final String API_MISSING_FILE = "missing.api.txt";
+  private static final String API_HIDDEN_FILE = "hidden.api.txt";
+  private static final String JAR_HIDDEN_FILE = "hidden.jar.txt";
+
+  private static Path getOutputDir() {
+    return Paths.get(ToolHelper.SOURCE_DIR, "main", "resources", "resources");
+  }
+
+  private static final Charset CHARSET = StandardCharsets.UTF_8;
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  @Test
+  public void testGeneratedFilesUpToDate() throws Exception {
+    Path outputDir = getOutputDir();
+    assertEquals(
+        FileUtils.readTextFile(outputDir.resolve(API_MISSING_FILE), CHARSET), generateApiMissing());
+    assertEquals(
+        FileUtils.readTextFile(outputDir.resolve(API_HIDDEN_FILE), CHARSET), generateApiHidden());
+    assertEquals(
+        FileUtils.readTextFile(outputDir.resolve(JAR_HIDDEN_FILE), CHARSET), generateJarHidden());
+  }
+
+  public static void main(String[] args) throws IOException {
+    // ----------------------------------------- IMPORTANT -----------------------------------------
+    // When these amendments are semantically changed (i.e. a format change doesn't matter)
+    // DATABASE_FORMAT_VERSION must be incremented to reflect that the output of
+    // ApiDatabaseGenerator has changed.
+    // ----------------------------------------- IMPORTANT -----------------------------------------
+    Path outputDir = getOutputDir();
+    if (!Files.exists(outputDir)) {
+      Files.createDirectories(outputDir);
+    }
+    Files.write(outputDir.resolve(API_MISSING_FILE), generateApiMissing().getBytes(CHARSET));
+    Files.write(outputDir.resolve(API_HIDDEN_FILE), generateApiHidden().getBytes(CHARSET));
+    Files.write(outputDir.resolve(JAR_HIDDEN_FILE), generateJarHidden().getBytes(CHARSET));
+  }
+
+  private static String generateApiMissing() {
+    return makeFileContent(generateApiMissingLines());
+  }
+
+  public static String generateApiHidden() throws IOException {
+    return makeFileContent(generateApiHiddenLines());
+  }
+
+  public static String generateJarHidden() throws IOException {
+    return makeFileContent(generateJarHiddenLines());
+  }
+
+  private static String makeFileContent(List<String> lines) {
+    // Since this is a stable internal resource, line endings should NOT be platform dependent.
+    String header =
+        StringUtils.join(
+            "\n",
+            ImmutableList.of(
+                "// Copyright (c) 2026, 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.",
+                "//",
+                "// This file is generated by GenerateApiAmendments.java.",
+                "// Do not edit manually."));
+    String content = StringUtils.join("\n", lines);
+    return header + "\n" + content + "\n";
+  }
+
+  private static List<String> generateApiMissingLines() {
+    ArrayList<String> lines = new ArrayList<>();
+    AndroidApiLevelDatabaseTestHelper.visitAdditionalKnownApiReferences(
+        (methodReference, apiLevel) -> lines.add(formatApiMethod(methodReference, apiLevel)));
+    Collections.sort(lines);
+    return lines;
+  }
+
+  /** See {@link com.android.tools.r8.apimodel.ApiAmendmentsParser} for the format. */
+  private static String formatApiClass(ClassReference classReference, AndroidApiLevel apiLevel) {
+    return "class " + classReference.getDescriptor() + " " + apiLevel.getLevel();
+  }
+
+  /** See {@link com.android.tools.r8.apimodel.ApiAmendmentsParser} for the format. */
+  private static String formatApiMethod(MethodReference methodReference, AndroidApiLevel apiLevel) {
+    String holder = methodReference.getHolderClass().getDescriptor();
+    String methodName = methodReference.getMethodName();
+    String descriptor = methodReference.getMethodDescriptor();
+    return "method " + holder + " " + methodName + " " + descriptor + " " + apiLevel.getLevel();
+  }
+
+  /** See {@link com.android.tools.r8.apimodel.ApiAmendmentsParser} for the format. */
+  private static String formatApiField(FieldReference fieldReference, AndroidApiLevel apiLevel) {
+    String holder = fieldReference.getHolderClass().getDescriptor();
+    String name = fieldReference.getFieldName();
+    return "field " + holder + " " + name + " " + apiLevel.getLevel();
+  }
+
+  private static List<String> generateJarHiddenLines() throws IOException {
+    ArrayList<String> lines = new ArrayList<>();
+    Map<String, Boolean> skipClasses = getSkipClassesMap();
+
+    AndroidApiLevelDatabaseTestHelper.visitHiddenReferences(
+        (classReference, superReference, interfaces, apiLevel) -> {
+          if (checkForSkip(classReference.getBinaryName(), skipClasses)) {
+            return;
+          }
+          if (superReference != null) {
+            lines.add(formatJarClass(classReference, superReference, interfaces));
+          } else {
+            lines.add(formatJarInterface(classReference, interfaces));
+          }
+        },
+        (methodReference, isStatic, apiLevel) -> {
+          if (checkForSkip(methodReference.getHolderClass().getBinaryName(), skipClasses)) {
+            return;
+          }
+          lines.add(formatJarMethod(methodReference, isStatic));
+        },
+        (fieldReference, apiLevel) -> {
+          if (checkForSkip(fieldReference.getHolderClass().getBinaryName(), skipClasses)) {
+            return;
+          }
+          lines.add(formatJarField(fieldReference));
+        });
+    ensureSkippedClasses(skipClasses);
+    Collections.sort(lines);
+    return lines;
+  }
+
+  /** See {@link com.android.tools.r8.apimodel.ApiAmendmentsParser} for the format. */
+  private static String formatJarClass(
+      ClassReference classReference,
+      ClassReference superReference,
+      List<ClassReference> interfaces) {
+    String clazz = classReference.getDescriptor();
+    String superclass = superReference.getDescriptor();
+    String implString = formatInheritances(interfaces);
+    return "class " + clazz + " extends " + superclass + " implements " + implString;
+  }
+
+  /** See {@link com.android.tools.r8.apimodel.ApiAmendmentsParser} for the format. */
+  private static String formatJarInterface(
+      ClassReference classReference, List<ClassReference> interfaces) {
+    String clazz = classReference.getDescriptor();
+    String extString = formatInheritances(interfaces);
+    return "interface " + clazz + " extends " + extString;
+  }
+
+  /** See {@link com.android.tools.r8.apimodel.ApiAmendmentsParser} for the format. */
+  private static String formatInheritances(List<ClassReference> interfaces) {
+    String extString;
+    if (interfaces.isEmpty()) {
+      extString = "none";
+    } else {
+      extString =
+          interfaces.stream()
+              .map(ClassReference::getDescriptor)
+              .sorted()
+              .collect(Collectors.joining(","));
+    }
+    return extString;
+  }
+
+  /** See {@link com.android.tools.r8.apimodel.ApiAmendmentsParser} for the format. */
+  private static String formatJarMethod(MethodReference methodReference, Boolean isStatic) {
+    String holder = methodReference.getHolderClass().getDescriptor();
+    String name = methodReference.getMethodName();
+    String descriptor = methodReference.getMethodDescriptor();
+    String virtualness = isStatic ? "static" : "instance";
+    return "method " + holder + " " + name + " " + descriptor + " " + virtualness;
+  }
+
+  /** See {@link com.android.tools.r8.apimodel.ApiAmendmentsParser} for the format. */
+  private static String formatJarField(FieldReference fieldReference) {
+    String holder = fieldReference.getHolderClass().getDescriptor();
+    String name = fieldReference.getFieldName();
+    return "field " + holder + " " + name;
+  }
+
+  private static List<String> generateApiHiddenLines() throws IOException {
+    ArrayList<String> lines = new ArrayList<>();
+    Map<String, Boolean> skipClasses = getSkipClassesMap();
+
+    AndroidApiLevelDatabaseTestHelper.visitHiddenReferences(
+        (classReference, superReference, interfaces, apiLevel) -> {
+          if (checkForSkip(classReference.getBinaryName(), skipClasses)) {
+            return;
+          }
+          lines.add(formatApiClass(classReference, apiLevel));
+        },
+        (methodReference, isStatic, apiLevel) -> {
+          if (checkForSkip(methodReference.getHolderClass().getBinaryName(), skipClasses)) {
+            return;
+          }
+          lines.add(formatApiMethod(methodReference, apiLevel));
+        },
+        (fieldReference, apiLevel) -> {
+          if (checkForSkip(fieldReference.getHolderClass().getBinaryName(), skipClasses)) {
+            return;
+          }
+          lines.add(formatApiField(fieldReference, apiLevel));
+        });
+    ensureSkippedClasses(skipClasses);
+    Collections.sort(lines);
+    return lines;
+  }
+
+  private static Map<String, Boolean> getSkipClassesMap() {
+    // These are present because of annotation inheritance, but they are not public classes.
+    Map<String, Boolean> skipClasses = new HashMap<>();
+    skipClasses.put("java/nio/DirectByteBuffer", false);
+    return skipClasses;
+  }
+
+  private static boolean checkForSkip(String holderBinaryName, Map<String, Boolean> skipClasses) {
+    boolean skip = false;
+    if (skipClasses.containsKey(holderBinaryName)) {
+      skipClasses.put(holderBinaryName, true);
+      skip = true;
+    }
+    return skip;
+  }
+
+  private static void ensureSkippedClasses(Map<String, Boolean> skippedClasses) {
+    for (Map.Entry<String, Boolean> entry : skippedClasses.entrySet()) {
+      assertTrue(entry.getKey() + " was never skipped", entry.getValue());
+    }
+  }
+}
diff --git a/src/test/java8/apimodel/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java b/src/test/java8/apimodel/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
index 8313663..fe80e6b 100644
--- a/src/test/java8/apimodel/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
+++ b/src/test/java8/apimodel/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
@@ -12,13 +12,13 @@
 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import com.android.tools.r8.ApiDatabaseGenerator;
+import com.android.tools.r8.ApiDatabaseGeneratorCommand;
 import com.android.tools.r8.ApiDatabaseGeneratorException;
-import com.android.tools.r8.DiagnosticsMatcher;
+import com.android.tools.r8.ApiDatabaseGeneratorTestHelper;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestDiagnosticMessagesImpl;
 import com.android.tools.r8.TestParameters;
@@ -26,19 +26,16 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
 import com.android.tools.r8.androidapi.AndroidApiLevelCompute.DefaultAndroidApiLevelCompute;
-import com.android.tools.r8.androidapi.AndroidApiLevelDatabaseTestHelper;
 import com.android.tools.r8.androidapi.AndroidApiLevelHashingDatabaseImpl;
 import com.android.tools.r8.androidapi.ApiDatabaseEntry;
 import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.androidapi.SunMiscUnsafeApiTest;
-import com.android.tools.r8.apimodel.AndroidApiVersionsXmlParser.ParsingException;
 import com.android.tools.r8.apimodel.ParsedApiClassTrimming.JarTrimmer;
 import com.android.tools.r8.apimodel.ParsedApiClassTrimming.ListeningDecorator;
 import com.android.tools.r8.apimodel.ParsedApiClassTrimming.SkipAnswer;
+import com.android.tools.r8.apimodel.ParsedApiClassTrimming.Trimmer;
 import com.android.tools.r8.apimodel.ParsedApiClassTrimming.TrimmerListener;
-import com.android.tools.r8.apimodel.jar.ApiClassInfo;
 import com.android.tools.r8.apimodel.jar.ApiJarInfo;
-import com.android.tools.r8.apimodel.jar.ApiJarReader;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
@@ -56,28 +53,22 @@
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.internal.BiConsumerUtils;
-import com.android.tools.r8.utils.internal.ListUtils;
 import com.android.tools.r8.utils.internal.TriConsumerUtils;
-import com.android.tools.r8.utils.internal.collections.Pair;
 import com.android.tools.r8.utils.timing.Timing;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.IdentityHashMap;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
-import org.hamcrest.CoreMatchers;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
@@ -105,24 +96,6 @@
     this.parameters = parameters;
   }
 
-  private static class GenerateDatabaseResourceFilesResult {
-
-    private final Path apiLevels;
-
-    public GenerateDatabaseResourceFilesResult(Path apiLevels) {
-      this.apiLevels = apiLevels;
-    }
-  }
-
-  private static GenerateDatabaseResourceFilesResult generateResourcesFiles(
-      Map<ApiDatabaseEntry, AndroidApiLevel> databaseEntries) throws Exception {
-    TemporaryFolder temp1 = new TemporaryFolder();
-    temp1.create();
-    Path apiLevels = temp1.newFile("api_levels.ser").toPath();
-    AndroidApiHashingDatabaseBuilderGenerator.writeEntries(databaseEntries, apiLevels);
-    return new GenerateDatabaseResourceFilesResult(apiLevels);
-  }
-
   private static Map<ApiDatabaseEntry, AndroidApiLevel> computeEntries(
       Collection<ParsedApiClass> apiClasses) throws Exception {
     Map<ApiDatabaseEntry, AndroidApiLevel> databaseEntries =
@@ -131,90 +104,25 @@
     return databaseEntries;
   }
 
-  private static void saveToTmp(Collection<ParsedApiClass> classes, String fileName)
-      throws IOException {
-    Path tmpDir = Paths.get(ToolHelper.getProjectRoot(), "tmp");
-    if (!Files.exists(tmpDir)) {
-      Files.createDirectories(tmpDir);
-    }
-    Path file = tmpDir.resolve(fileName);
-    Files.write(file, sortAndFormat(classes));
-  }
-
-  public static List<String> sortAndFormat(Collection<ParsedApiClass> classes) {
-    List<ParsedApiClass> sortedClasses = new ArrayList<>(classes);
-    sortedClasses.sort(Comparator.comparing(c -> c.getClassReference().getDescriptor()));
-    List<String> result = new ArrayList<>();
-    for (ParsedApiClass apiClass : sortedClasses) {
-      result.add(apiClass.getClassReference().getDescriptor() + " " + apiClass.getRange());
-      List<Pair<ClassReference, ApiRange>> sortedSupertypes = new ArrayList<>();
-      apiClass.forEachSupertype(
-          (reference, apiRange) -> sortedSupertypes.add(new Pair<>(reference, apiRange)));
-      sortedSupertypes.sort(Comparator.comparing(p -> p.getFirst().getDescriptor()));
-      for (Pair<ClassReference, ApiRange> pair : sortedSupertypes) {
-        result.add("  super: " + pair.getFirst().getDescriptor() + " " + pair.getSecond());
-      }
-      List<Pair<ClassReference, ApiRange>> sortedInterfaces = new ArrayList<>();
-      apiClass.forEachInterface(
-          (reference, apiRange) -> sortedInterfaces.add(new Pair<>(reference, apiRange)));
-      sortedInterfaces.sort(Comparator.comparing(p -> p.getFirst().getDescriptor()));
-      for (Pair<ClassReference, ApiRange> pair : sortedInterfaces) {
-        result.add("  implements: " + pair.getFirst().getDescriptor() + " " + pair.getSecond());
-      }
-      List<Pair<FieldTypelessReference, ApiRange>> sortedFields = new ArrayList<>();
-      apiClass.forEachField(
-          (reference, apiRange) -> sortedFields.add(new Pair<>(reference, apiRange)));
-      sortedFields.sort(Comparator.comparing(p -> p.getFirst().toString()));
-      for (Pair<FieldTypelessReference, ApiRange> pair : sortedFields) {
-        result.add("  field: " + pair.getFirst().toString() + " " + pair.getSecond());
-      }
-      List<Pair<MethodReference, ApiRange>> sortedMethods = new ArrayList<>();
-      apiClass.forEachMethod(
-          (reference, apiRange) -> sortedMethods.add(new Pair<>(reference, apiRange)));
-      sortedMethods.sort(Comparator.comparing(p -> p.getFirst().toString()));
-      for (Pair<MethodReference, ApiRange> pair : sortedMethods) {
-        result.add("  method: " + pair.getFirst().toString() + " " + pair.getSecond());
-      }
-    }
-    return result;
-  }
-
   private static Collection<ParsedApiClass> cachedParsedApiClasses = null;
 
-  private static Collection<ParsedApiClass> loadParsedApiClasses()
-      throws ParsingException, ApiDatabaseGeneratorException, IOException {
+  private static Collection<ParsedApiClass> loadParsedApiClasses() throws Exception {
     if (cachedParsedApiClasses == null) {
-      TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
-      Collection<ParsedApiClass> apiClasses =
-          AndroidApiVersionsXmlParser.parse(ToolHelper.getApiVersionsXmlFile(API_LEVEL));
-      saveToTmp(apiClasses, "1_parsed.txt");
-      ParsedApiClassVerifier.verify(apiClasses);
-      ApiJarInfo jarInfo = ApiJarReader.read(ImmutableList.of(ToolHelper.getAndroidJar(API_LEVEL)));
-      apiClasses = amendMissingData(apiClasses, jarInfo, diagnosticsHandler);
-      saveToTmp(apiClasses, "2_merged.txt");
-      ParsedApiClassVerifier.verify(apiClasses);
-      apiClasses = ParsedApiClassFlattening.flatten(apiClasses);
-      saveToTmp(apiClasses, "3_flattened.txt");
-      ParsedApiClassVerifier.verify(apiClasses);
-      apiClasses =
-          ParsedApiClassTrimming.trim(apiClasses, new ParsedApiClassTrimming.RemovedTrimmer());
-      saveToTmp(apiClasses, "4_trimmed_removed.txt");
-      ParsedApiClassVerifier.verify(apiClasses);
-      apiClasses = filterByJar(apiClasses, jarInfo);
-      saveToTmp(apiClasses, "5_trimmed_jar.txt");
-      ParsedApiClassVerifier.verify(apiClasses);
-      diagnosticsHandler.assertAllDiagnosticsMatch(
-          DiagnosticsMatcher.diagnosticMessage(CoreMatchers.startsWith("Duplicate class ")));
-      cachedParsedApiClasses = apiClasses;
+      ApiDatabaseGeneratorCommand command =
+          ApiDatabaseGeneratorCommand.builder()
+              .addXmlPath(ToolHelper.getApiVersionsXmlFile(API_LEVEL))
+              .addJarPath(ToolHelper.getAndroidJar(API_LEVEL))
+              .build();
+      cachedParsedApiClasses =
+          ApiDatabaseGeneratorTestHelper.generateClasses(
+              command, AndroidApiHashingDatabaseBuilderGeneratorTest::addJarAssertions);
     }
     return cachedParsedApiClasses;
   }
 
-  private static Collection<ParsedApiClass> filterByJar(
-      Collection<ParsedApiClass> apiClasses, ApiJarInfo jarInfo)
-      throws ApiDatabaseGeneratorException {
-    JarTrimmer jarTrimmer = new JarTrimmer(jarInfo);
+  private static Trimmer<ApiDatabaseGeneratorException> addJarAssertions(JarTrimmer jarTrimmer) {
     Map<String, Boolean> expectedTrimmedPackages = new HashMap<>();
+    ApiJarInfo jarInfo = jarTrimmer.getJarInfo();
     expectedTrimmedPackages.put("junit/", false);
     expectedTrimmedPackages.put("android/test/", false);
     expectedTrimmedPackages.put("com/android/internal/", false);
@@ -276,124 +184,16 @@
               SkipAnswer answer) {
             assertEquals("No fields should be skipped: " + field, SkipAnswer.KEEP, answer);
           }
+
+          @Override
+          public void done() {
+            for (Map.Entry<String, Boolean> entry : expectedTrimmedPackages.entrySet()) {
+              assertTrue(
+                  "Expected trimmed package not trimmed: " + entry.getKey(), entry.getValue());
+            }
+          }
         };
-    Collection<ParsedApiClass> trimResult =
-        ParsedApiClassTrimming.trim(apiClasses, new ListeningDecorator<>(jarTrimmer, listener));
-    for (Map.Entry<String, Boolean> entry : expectedTrimmedPackages.entrySet()) {
-      assertTrue("Expected trimmed package not trimmed: " + entry.getKey(), entry.getValue());
-    }
-    return trimResult;
-  }
-
-  /**
-   * Returns API data missing from {@code api-versions.xml} and {@code jarInfo} is mutated with data
-   * missing from {@code android.jar}.
-   */
-  private static Collection<ParsedApiClass> amendMissingData(
-      Collection<ParsedApiClass> apiClasses,
-      ApiJarInfo jarInfo,
-      TestDiagnosticMessagesImpl diagnosticsHandler)
-      throws ApiDatabaseGeneratorException, IOException {
-    Map<ClassReference, ApiRange> lookup = makeApiLookup(apiClasses);
-    Collection<ParsedApiClass> missingApi = missingVersionEntries(lookup, jarInfo);
-    Collection<ParsedApiClass> hiddenApi = addHiddenEntries(lookup, jarInfo);
-    return ParsedApiClassMerging.merge(
-        Iterables.concat(apiClasses, missingApi, hiddenApi), diagnosticsHandler);
-  }
-
-  /** These entries exist in android.jar and runtime but are missing in api-versions.xml. */
-  private static Collection<ParsedApiClass> missingVersionEntries(
-      Map<ClassReference, ApiRange> lookup, ApiJarInfo jarInfoForVerification) {
-    SafeApiBuilder builder = new SafeApiBuilder(lookup);
-    AndroidApiLevelDatabaseTestHelper.visitAdditionalKnownApiReferences(
-        (methodReference, apiLevel) -> {
-          try {
-            assertTrue(
-                methodReference + " was expected present in android.jar",
-                jarInfoForVerification.hasMethod(methodReference));
-          } catch (ApiDatabaseGeneratorException e) {
-            throw new RuntimeException(e);
-          }
-          builder.addMethod(methodReference, new ApiRange(apiLevel));
-        });
-    return builder.build();
-  }
-
-  /**
-   * These entries exist at runtime but not in android.jar or api-versions.xml.
-   *
-   * <p>The API entries are returned while the jar info is added to {@code jarInfo}.
-   */
-  private static Collection<ParsedApiClass> addHiddenEntries(
-      Map<ClassReference, ApiRange> lookup, ApiJarInfo jarInfo) throws IOException {
-    SafeApiBuilder builder = new SafeApiBuilder(lookup);
-
-    // This class is not present in .xml so it is skipped.
-    Map<String, Boolean> skipClasses = new HashMap<>();
-    skipClasses.put("java/nio/DirectByteBuffer", false);
-
-    AndroidApiLevelDatabaseTestHelper.visitHiddenReferences(
-        (classReference, superReference, interfaces, apiLevel) -> {
-          builder.addClass(classReference, new ApiRange(apiLevel));
-          assertFalse(
-              classReference + " already exists in the JAR", jarInfo.hasClass(classReference));
-          String superName;
-          if (superReference != null) {
-            superName = superReference.getBinaryName();
-          } else if (interfaces.isEmpty()) {
-            superName = "java/lang/Object";
-          } else {
-            superName = null;
-          }
-          boolean isInterface = superReference == null;
-          jarInfo.addClass(
-              new ApiClassInfo(
-                  classReference.getBinaryName(),
-                  superName,
-                  ListUtils.map(interfaces, ClassReference::getBinaryName),
-                  isInterface,
-                  ImmutableList.of(),
-                  ImmutableList.of()));
-        },
-        (methodReference, isStatic, apiLevel) -> {
-          if (skipClasses.containsKey(methodReference.getHolderClass().getBinaryName())) {
-            skipClasses.put(methodReference.getHolderClass().getBinaryName(), true);
-          } else {
-            builder.addMethod(methodReference, new ApiRange(apiLevel));
-            ApiClassInfo holder = jarInfo.getClassInfo(methodReference.getHolderClass());
-            assertNotNull(
-                methodReference + " could not be added, holder not present in JAR", holder);
-            holder.addMethod(
-                methodReference.getMethodName(), methodReference.getMethodDescriptor(), isStatic);
-          }
-        },
-        (fieldReference, apilevel) -> {
-          if (skipClasses.containsKey(fieldReference.getHolderClass().getBinaryName())) {
-            skipClasses.put(fieldReference.getHolderClass().getBinaryName(), true);
-          } else {
-            builder.addField(fieldReference, new ApiRange(apilevel));
-            ApiClassInfo holder = jarInfo.getClassInfo(fieldReference.getHolderClass());
-            assertNotNull(
-                fieldReference + " could not be added, holder not present in JAR", holder);
-            holder.addField(fieldReference.getFieldName());
-          }
-        });
-    for (Map.Entry<String, Boolean> entry : skipClasses.entrySet()) {
-      assertTrue(entry.getKey() + " was never skipped", entry.getValue());
-    }
-    return builder.build();
-  }
-
-  private static Map<ClassReference, ApiRange> makeApiLookup(
-      Collection<ParsedApiClass> apiClasses) {
-    Map<ClassReference, ApiRange> lookup = new HashMap<>();
-    for (ParsedApiClass apiClass : apiClasses) {
-      assertFalse(
-          apiClass.getClassReference() + " already found",
-          lookup.containsKey(apiClass.getClassReference()));
-      lookup.put(apiClass.getClassReference(), apiClass.getRange());
-    }
-    return lookup;
+    return new ListeningDecorator<>(jarTrimmer, listener);
   }
 
   @SuppressWarnings("SameParameterValue")
@@ -510,77 +310,6 @@
     assertThat(missingMemberInformation.keySet(), matchesItemsOneToOne(expectedMissingMembers));
   }
 
-  private static class SafeApiBuilder {
-    private final Map<ClassReference, ParsedApiClass> classes = new LinkedHashMap<>();
-    private final Map<ClassReference, ApiRange> lookup;
-
-    public SafeApiBuilder(Map<ClassReference, ApiRange> lookup) {
-      this.lookup = lookup;
-    }
-
-    private ParsedApiClass getOrCreateClass(ClassReference classReference) {
-      ParsedApiClass apiClass = classes.get(classReference);
-      if (apiClass == null) {
-        ApiRange lookupRange = lookup.get(classReference);
-        assertNotNull(classReference + " not found in the lookup map", lookupRange);
-        ParsedApiClass baseApiClass = new ParsedApiClass(classReference, lookupRange);
-        classes.put(classReference, baseApiClass);
-        apiClass = baseApiClass;
-      }
-      return apiClass;
-    }
-
-    public void addClass(ClassReference classReference, ApiRange apiRange) {
-      assertFalse(classReference + " was already found", classes.containsKey(classReference));
-      classes.put(classReference, new ParsedApiClass(classReference, apiRange));
-    }
-
-    public void addMethod(MethodReference methodReference, ApiRange apiRange) {
-      ParsedApiClass apiClass = getOrCreateClass(methodReference.getHolderClass());
-      assertFalse(methodReference + " was already found", apiClass.hasMethod(methodReference));
-      AndroidApiLevel apiClassIntro = apiClass.getRange().intro;
-      AndroidApiLevel apiClassRemoved = apiClass.getRange().removed;
-      assertTrue(
-          "class intro " + apiClassIntro + " must be less than method intro " + apiRange.intro,
-          apiClassIntro.isLessThanOrEqualTo(apiRange.intro));
-      assertTrue(
-          "class removed "
-              + apiClassRemoved
-              + " must be greater than method removed "
-              + apiRange.removed,
-          apiClassRemoved == null
-              || apiRange.isRemoved()
-              || apiClassRemoved.isGreaterThanOrEqualTo(apiRange.removed));
-      apiClass.registerMethod(methodReference, apiRange);
-    }
-
-    public void addField(FieldReference fieldReference, ApiRange apiRange) {
-      FieldTypelessReference field =
-          new FieldTypelessReference(
-              fieldReference.getHolderClass(), fieldReference.getFieldName());
-      ParsedApiClass apiClass = getOrCreateClass(field.getHolderClass());
-      assertFalse(field + " was already found", apiClass.hasField(field));
-      AndroidApiLevel apiClassIntro = apiClass.getRange().intro;
-      AndroidApiLevel apiClassRemoved = apiClass.getRange().removed;
-      assertTrue(
-          "class intro " + apiClassIntro + " must be less than field intro " + apiRange.intro,
-          apiClassIntro.isLessThanOrEqualTo(apiRange.intro));
-      assertTrue(
-          "class removed "
-              + apiClassRemoved
-              + " must be greater than field removed "
-              + apiRange.removed,
-          apiClassRemoved == null
-              || apiRange.isRemoved()
-              || apiClassRemoved.isGreaterThanOrEqualTo(apiRange.removed));
-      apiClass.registerField(field, apiRange);
-    }
-
-    public Collection<ParsedApiClass> build() {
-      return classes.values();
-    }
-  }
-
   @Test
   public void testEntrySize() throws Exception {
     Map<ApiDatabaseEntry, AndroidApiLevel> databaseEntries = computeEntries(loadParsedApiClasses());
@@ -590,9 +319,16 @@
   /** If this test fails, use {@link #testDumpDatabase} to diff the content in a readable format. */
   @Test
   public void testDatabaseGenerationUpToDate() throws Exception {
-    GenerateDatabaseResourceFilesResult result =
-        generateResourcesFiles(computeEntries(loadParsedApiClasses()));
-    assertTrue(TestBase.filesAreEqual(result.apiLevels, API_DATABASE));
+    temp.create();
+    Path apiLevels = temp.newFile("api_levels.ser").toPath();
+    ApiDatabaseGeneratorCommand command =
+        ApiDatabaseGeneratorCommand.builder()
+            .addXmlPath(ToolHelper.getApiVersionsXmlFile(API_LEVEL))
+            .addJarPath(ToolHelper.getAndroidJar(API_LEVEL))
+            .setOutputPath(apiLevels)
+            .build();
+    ApiDatabaseGenerator.run(command);
+    assertTrue(TestBase.filesAreEqual(apiLevels, API_DATABASE));
   }
 
   @Test
@@ -854,10 +590,24 @@
    * third_party/api_database/api_database.ser.
    */
   public static void main(String[] args) throws Exception {
-    GenerateDatabaseResourceFilesResult result =
-        generateResourcesFiles(computeEntries(loadParsedApiClasses()));
-    API_DATABASE.toFile().mkdirs();
-    Files.move(result.apiLevels, API_DATABASE, REPLACE_EXISTING);
+    // ----------------------------------------- IMPORTANT -----------------------------------------
+    // If the database format or content is changed DATABASE_FORMAT_VERSION must be incremented to
+    // reflect that the output of ApiDatabaseGenerator has changed.
+    //
+    // Updating api-versions.xml or android.jar does NOT need a version increase.
+    // ----------------------------------------- IMPORTANT -----------------------------------------
+    TemporaryFolder temp = new TemporaryFolder();
+    temp.create();
+    Path apiLevels = temp.newFile("api_levels.ser").toPath();
+    ApiDatabaseGeneratorCommand command =
+        ApiDatabaseGeneratorCommand.builder()
+            .addXmlPath(ToolHelper.getApiVersionsXmlFile(API_LEVEL))
+            .addJarPath(ToolHelper.getAndroidJar(API_LEVEL))
+            .setOutputPath(apiLevels)
+            .build();
+    ApiDatabaseGenerator.run(command);
+    API_DATABASE.getParent().toFile().mkdirs();
+    Files.move(apiLevels, API_DATABASE, REPLACE_EXISTING);
     System.out.println(
         "Updated file in: "
             + API_DATABASE
diff --git a/src/test/java8/apimodel/com/android/tools/r8/apimodel/ApiJarReaderTest.java b/src/test/java8/apimodel/com/android/tools/r8/apimodel/ApiJarReaderTest.java
index a0388fd..1c6ee68 100644
--- a/src/test/java8/apimodel/com/android/tools/r8/apimodel/ApiJarReaderTest.java
+++ b/src/test/java8/apimodel/com/android/tools/r8/apimodel/ApiJarReaderTest.java
@@ -14,7 +14,6 @@
 import com.android.tools.r8.apimodel.jar.ApiJarInfo;
 import com.android.tools.r8.apimodel.jar.ApiJarReader;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import com.google.common.collect.ImmutableList;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -35,7 +34,7 @@
   @Test
   public void testApiJarReader() throws Exception {
     AndroidApiLevel apiLevel = AndroidApiLevel.API_DATABASE_LEVEL;
-    ApiJarInfo jarInfo = ApiJarReader.read(ImmutableList.of(ToolHelper.getAndroidJar(apiLevel)));
+    ApiJarInfo jarInfo = ApiJarReader.read(ToolHelper.getAndroidJar(apiLevel));
     assertNotNull(jarInfo);
     assertEquals(6_440, jarInfo.size());
     assertTrue(jarInfo.hasClass("java/lang/Object"));
diff --git a/src/test/java8/apimodel/com/android/tools/r8/apimodel/ParsedApiClassJarTrimmerTest.java b/src/test/java8/apimodel/com/android/tools/r8/apimodel/ParsedApiClassJarTrimmerTest.java
index b98a8cb..12d33bc 100644
--- a/src/test/java8/apimodel/com/android/tools/r8/apimodel/ParsedApiClassJarTrimmerTest.java
+++ b/src/test/java8/apimodel/com/android/tools/r8/apimodel/ParsedApiClassJarTrimmerTest.java
@@ -12,11 +12,14 @@
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.apimodel.ParsedApiClassTrimming.JarTrimmer;
+import com.android.tools.r8.apimodel.jar.ApiJarInfo;
+import com.android.tools.r8.apimodel.jar.ApiJarMerging;
 import com.android.tools.r8.apimodel.jar.ApiJarReader;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.internal.ListUtils;
 import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.Collection;
@@ -144,8 +147,10 @@
         Arrays.asList(apiClassA, apiClassB, apiInterfaceI, apiClassC, apiClassD);
 
     // Run JAR trimmer.
+    List<ApiJarInfo> readJars = ListUtils.mapThrowing(jars, ApiJarReader::read);
+    ApiJarInfo jarInfo = ApiJarMerging.mergeJarInfos(readJars);
     Collection<ParsedApiClass> trimmed =
-        ParsedApiClassTrimming.trim(apiClasses, new JarTrimmer(ApiJarReader.read(jars)));
+        ParsedApiClassTrimming.trim(apiClasses, new JarTrimmer(jarInfo));
 
     // Verify results.
     Map<ClassReference, ParsedApiClass> trimmedMap = new HashMap<>();
diff --git a/src/test/java8/desugar/com/android/tools/r8/desugar/desugaredlibrary/DesugaredMethodListCommandTest.java b/src/test/java8/desugar/com/android/tools/r8/desugar/desugaredlibrary/DesugaredMethodListCommandTest.java
index 3d22aaf..9487344 100644
--- a/src/test/java8/desugar/com/android/tools/r8/desugar/desugaredlibrary/DesugaredMethodListCommandTest.java
+++ b/src/test/java8/desugar/com/android/tools/r8/desugar/desugaredlibrary/DesugaredMethodListCommandTest.java
@@ -102,7 +102,7 @@
         new String[] {"--desugared-lib"}, new Reporter(diagnosticMessages));
     diagnosticMessages.assertOnlyErrors();
     diagnosticMessages.assertErrorMessageThatMatches(
-        containsString("Missing parameter for --desugared-lib"));
+        containsString("Missing argument for --desugared-lib"));
   }
 
   @Test
diff --git a/src/test/java8/ir/com/android/tools/r8/ir/desugar/backports/ArraysMethods.java b/src/test/java8/ir/com/android/tools/r8/ir/desugar/backports/ArraysMethods.java
index b56a1b1..46cd4a4 100644
--- a/src/test/java8/ir/com/android/tools/r8/ir/desugar/backports/ArraysMethods.java
+++ b/src/test/java8/ir/com/android/tools/r8/ir/desugar/backports/ArraysMethods.java
@@ -4,6 +4,12 @@
 
 package com.android.tools.r8.ir.desugar.backports;
 
+import com.android.tools.r8.ir.desugar.backports.BackportMethodsStub.ArraysStub;
+import com.android.tools.r8.ir.desugar.backports.BackportMethodsStub.ByteStub;
+import com.android.tools.r8.ir.desugar.backports.BackportMethodsStub.ShortStub;
+import java.util.Comparator;
+import java.util.Objects;
+
 public final class ArraysMethods {
 
   public static void checkValidRange(int arrayLength, int fromIndex, int toIndex) {
@@ -304,4 +310,386 @@
     }
     return true;
   }
+
+  public static int compareUnsignedByte(byte[] a, byte[] b) {
+    if (a == b) {
+      return 0;
+    }
+    if (a == null) {
+      return -1;
+    }
+    if (b == null) {
+      return 1;
+    }
+    int i = ArraysStub.mismatch(a, b);
+    if (i >= 0 && i < Math.min(a.length, b.length)) {
+      return ByteStub.compareUnsigned(a[i], b[i]);
+    }
+    return a.length - b.length;
+  }
+
+  public static int compareUnsignedByteRange(
+      byte[] a, int aFromIndex, int aToIndex, byte[] b, int bFromIndex, int bToIndex) {
+    int i = ArraysStub.mismatch(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex);
+    if (i >= 0 && i < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex)) {
+      return ByteStub.compareUnsigned(a[aFromIndex + i], b[bFromIndex + i]);
+    }
+    return (aToIndex - aFromIndex) - (bToIndex - bFromIndex);
+  }
+
+  public static int compareUnsignedShort(short[] a, short[] b) {
+    if (a == b) {
+      return 0;
+    }
+    if (a == null) {
+      return -1;
+    }
+    if (b == null) {
+      return 1;
+    }
+    int i = ArraysStub.mismatch(a, b);
+    if (i >= 0 && i < Math.min(a.length, b.length)) {
+      return ShortStub.compareUnsigned(a[i], b[i]);
+    }
+    return a.length - b.length;
+  }
+
+  public static int compareUnsignedShortRange(
+      short[] a, int aFromIndex, int aToIndex, short[] b, int bFromIndex, int bToIndex) {
+    int i = ArraysStub.mismatch(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex);
+    if (i >= 0 && i < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex)) {
+      return ShortStub.compareUnsigned(a[aFromIndex + i], b[bFromIndex + i]);
+    }
+    return (aToIndex - aFromIndex) - (bToIndex - bFromIndex);
+  }
+
+  public static int compareUnsignedInt(int[] a, int[] b) {
+    if (a == b) {
+      return 0;
+    }
+    if (a == null) {
+      return -1;
+    }
+    if (b == null) {
+      return 1;
+    }
+    int i = ArraysStub.mismatch(a, b);
+    if (i >= 0 && i < Math.min(a.length, b.length)) {
+      return Integer.compareUnsigned(a[i], b[i]);
+    }
+    return a.length - b.length;
+  }
+
+  public static int compareUnsignedIntRange(
+      int[] a, int aFromIndex, int aToIndex, int[] b, int bFromIndex, int bToIndex) {
+    int i = ArraysStub.mismatch(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex);
+    if (i >= 0 && i < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex)) {
+      return Integer.compareUnsigned(a[aFromIndex + i], b[bFromIndex + i]);
+    }
+    return (aToIndex - aFromIndex) - (bToIndex - bFromIndex);
+  }
+
+  public static int compareUnsignedLong(long[] a, long[] b) {
+    if (a == b) {
+      return 0;
+    }
+    if (a == null) {
+      return -1;
+    }
+    if (b == null) {
+      return 1;
+    }
+    int i = ArraysStub.mismatch(a, b);
+    if (i >= 0 && i < Math.min(a.length, b.length)) {
+      return Long.compareUnsigned(a[i], b[i]);
+    }
+    return a.length - b.length;
+  }
+
+  public static int compareUnsignedLongRange(
+      long[] a, int aFromIndex, int aToIndex, long[] b, int bFromIndex, int bToIndex) {
+    int i = ArraysStub.mismatch(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex);
+    if (i >= 0 && i < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex)) {
+      return Long.compareUnsigned(a[aFromIndex + i], b[bFromIndex + i]);
+    }
+    return (aToIndex - aFromIndex) - (bToIndex - bFromIndex);
+  }
+
+  public static int mismatchBoolean(boolean[] a, boolean[] b) {
+    int length = Math.min(a.length, b.length);
+    if (a == b) {
+      return -1;
+    }
+    for (int i = 0; i < length; i++) {
+      if (a[i] != b[i]) {
+        return i;
+      }
+    }
+    return a.length != b.length ? length : -1;
+  }
+
+  public static int mismatchBooleanRange(
+      boolean[] a, int aFrom, int aTo, boolean[] b, int bFrom, int bTo) {
+    checkValidRange(a.length, aFrom, aTo);
+    checkValidRange(b.length, bFrom, bTo);
+    int aLength = aTo - aFrom;
+    int bLength = bTo - bFrom;
+    int length = Math.min(aLength, bLength);
+    for (int i = 0; i < length; i++) {
+      if (a[aFrom + i] != b[bFrom + i]) {
+        return i;
+      }
+    }
+    return aLength != bLength ? length : -1;
+  }
+
+  public static int mismatchByte(byte[] a, byte[] b) {
+    int length = Math.min(a.length, b.length);
+    if (a == b) {
+      return -1;
+    }
+    for (int i = 0; i < length; i++) {
+      if (a[i] != b[i]) {
+        return i;
+      }
+    }
+    return a.length != b.length ? length : -1;
+  }
+
+  public static int mismatchByteRange(byte[] a, int aFrom, int aTo, byte[] b, int bFrom, int bTo) {
+    checkValidRange(a.length, aFrom, aTo);
+    checkValidRange(b.length, bFrom, bTo);
+    int aLength = aTo - aFrom;
+    int bLength = bTo - bFrom;
+    int length = Math.min(aLength, bLength);
+    for (int i = 0; i < length; i++) {
+      if (a[aFrom + i] != b[bFrom + i]) {
+        return i;
+      }
+    }
+    return aLength != bLength ? length : -1;
+  }
+
+  public static int mismatchChar(char[] a, char[] b) {
+    int length = Math.min(a.length, b.length);
+    if (a == b) {
+      return -1;
+    }
+    for (int i = 0; i < length; i++) {
+      if (a[i] != b[i]) {
+        return i;
+      }
+    }
+    return a.length != b.length ? length : -1;
+  }
+
+  public static int mismatchCharRange(char[] a, int aFrom, int aTo, char[] b, int bFrom, int bTo) {
+    checkValidRange(a.length, aFrom, aTo);
+    checkValidRange(b.length, bFrom, bTo);
+    int aLength = aTo - aFrom;
+    int bLength = bTo - bFrom;
+    int length = Math.min(aLength, bLength);
+    for (int i = 0; i < length; i++) {
+      if (a[aFrom + i] != b[bFrom + i]) {
+        return i;
+      }
+    }
+    return aLength != bLength ? length : -1;
+  }
+
+  public static int mismatchShort(short[] a, short[] b) {
+    int length = Math.min(a.length, b.length);
+    if (a == b) {
+      return -1;
+    }
+    for (int i = 0; i < length; i++) {
+      if (a[i] != b[i]) {
+        return i;
+      }
+    }
+    return a.length != b.length ? length : -1;
+  }
+
+  public static int mismatchShortRange(
+      short[] a, int aFrom, int aTo, short[] b, int bFrom, int bTo) {
+    checkValidRange(a.length, aFrom, aTo);
+    checkValidRange(b.length, bFrom, bTo);
+    int aLength = aTo - aFrom;
+    int bLength = bTo - bFrom;
+    int length = Math.min(aLength, bLength);
+    for (int i = 0; i < length; i++) {
+      if (a[aFrom + i] != b[bFrom + i]) {
+        return i;
+      }
+    }
+    return aLength != bLength ? length : -1;
+  }
+
+  public static int mismatchInt(int[] a, int[] b) {
+    int length = Math.min(a.length, b.length);
+    if (a == b) {
+      return -1;
+    }
+    for (int i = 0; i < length; i++) {
+      if (a[i] != b[i]) {
+        return i;
+      }
+    }
+    return a.length != b.length ? length : -1;
+  }
+
+  public static int mismatchIntRange(int[] a, int aFrom, int aTo, int[] b, int bFrom, int bTo) {
+    checkValidRange(a.length, aFrom, aTo);
+    checkValidRange(b.length, bFrom, bTo);
+    int aLength = aTo - aFrom;
+    int bLength = bTo - bFrom;
+    int length = Math.min(aLength, bLength);
+    for (int i = 0; i < length; i++) {
+      if (a[aFrom + i] != b[bFrom + i]) {
+        return i;
+      }
+    }
+    return aLength != bLength ? length : -1;
+  }
+
+  public static int mismatchLong(long[] a, long[] b) {
+    int length = Math.min(a.length, b.length);
+    if (a == b) {
+      return -1;
+    }
+    for (int i = 0; i < length; i++) {
+      if (a[i] != b[i]) {
+        return i;
+      }
+    }
+    return a.length != b.length ? length : -1;
+  }
+
+  public static int mismatchLongRange(long[] a, int aFrom, int aTo, long[] b, int bFrom, int bTo) {
+    checkValidRange(a.length, aFrom, aTo);
+    checkValidRange(b.length, bFrom, bTo);
+    int aLength = aTo - aFrom;
+    int bLength = bTo - bFrom;
+    int length = Math.min(aLength, bLength);
+    for (int i = 0; i < length; i++) {
+      if (a[aFrom + i] != b[bFrom + i]) {
+        return i;
+      }
+    }
+    return aLength != bLength ? length : -1;
+  }
+
+  public static int mismatchFloat(float[] a, float[] b) {
+    int length = Math.min(a.length, b.length);
+    if (a == b) {
+      return -1;
+    }
+    for (int i = 0; i < length; i++) {
+      if (Float.floatToIntBits(a[i]) != Float.floatToIntBits(b[i])) {
+        return i;
+      }
+    }
+    return a.length != b.length ? length : -1;
+  }
+
+  public static int mismatchFloatRange(
+      float[] a, int aFrom, int aTo, float[] b, int bFrom, int bTo) {
+    checkValidRange(a.length, aFrom, aTo);
+    checkValidRange(b.length, bFrom, bTo);
+    int aLength = aTo - aFrom;
+    int bLength = bTo - bFrom;
+    int length = Math.min(aLength, bLength);
+    for (int i = 0; i < length; i++) {
+      if (Float.floatToIntBits(a[aFrom + i]) != Float.floatToIntBits(b[bFrom + i])) {
+        return i;
+      }
+    }
+    return aLength != bLength ? length : -1;
+  }
+
+  public static int mismatchDouble(double[] a, double[] b) {
+    int length = Math.min(a.length, b.length);
+    if (a == b) {
+      return -1;
+    }
+    for (int i = 0; i < length; i++) {
+      if (Double.doubleToLongBits(a[i]) != Double.doubleToLongBits(b[i])) {
+        return i;
+      }
+    }
+    return a.length != b.length ? length : -1;
+  }
+
+  public static int mismatchDoubleRange(
+      double[] a, int aFrom, int aTo, double[] b, int bFrom, int bTo) {
+    checkValidRange(a.length, aFrom, aTo);
+    checkValidRange(b.length, bFrom, bTo);
+    int aLength = aTo - aFrom;
+    int bLength = bTo - bFrom;
+    int length = Math.min(aLength, bLength);
+    for (int i = 0; i < length; i++) {
+      if (Double.doubleToLongBits(a[aFrom + i]) != Double.doubleToLongBits(b[bFrom + i])) {
+        return i;
+      }
+    }
+    return aLength != bLength ? length : -1;
+  }
+
+  public static int mismatchObject(Object[] a, Object[] b) {
+    int length = Math.min(a.length, b.length);
+    if (a == b) {
+      return -1;
+    }
+    for (int i = 0; i < length; i++) {
+      if (!Objects.equals(a[i], b[i])) {
+        return i;
+      }
+    }
+    return a.length != b.length ? length : -1;
+  }
+
+  public static int mismatchObjectRange(
+      Object[] a, int aFrom, int aTo, Object[] b, int bFrom, int bTo) {
+    checkValidRange(a.length, aFrom, aTo);
+    checkValidRange(b.length, bFrom, bTo);
+    int aLength = aTo - aFrom;
+    int bLength = bTo - bFrom;
+    int length = Math.min(aLength, bLength);
+    for (int i = 0; i < length; i++) {
+      if (!Objects.equals(a[aFrom + i], b[bFrom + i])) {
+        return i;
+      }
+    }
+    return aLength != bLength ? length : -1;
+  }
+
+  public static <T> int mismatchComparator(T[] a, T[] b, Comparator<? super T> cmp) {
+    Objects.requireNonNull(cmp);
+    int length = Math.min(a.length, b.length);
+    if (a == b) {
+      return -1;
+    }
+    for (int i = 0; i < length; i++) {
+      if (cmp.compare(a[i], b[i]) != 0) {
+        return i;
+      }
+    }
+    return a.length != b.length ? length : -1;
+  }
+
+  public static <T> int mismatchComparatorRange(
+      T[] a, int aFrom, int aTo, T[] b, int bFrom, int bTo, Comparator<? super T> cmp) {
+    Objects.requireNonNull(cmp);
+    checkValidRange(a.length, aFrom, aTo);
+    checkValidRange(b.length, bFrom, bTo);
+    int aLength = aTo - aFrom;
+    int bLength = bTo - bFrom;
+    int length = Math.min(aLength, bLength);
+    for (int i = 0; i < length; i++) {
+      if (cmp.compare(a[aFrom + i], b[bFrom + i]) != 0) {
+        return i;
+      }
+    }
+    return aLength != bLength ? length : -1;
+  }
 }
diff --git a/src/test/java8/ir/com/android/tools/r8/ir/desugar/backports/BackportMethodsStub.java b/src/test/java8/ir/com/android/tools/r8/ir/desugar/backports/BackportMethodsStub.java
index 41e8b6d..775e192 100644
--- a/src/test/java8/ir/com/android/tools/r8/ir/desugar/backports/BackportMethodsStub.java
+++ b/src/test/java8/ir/com/android/tools/r8/ir/desugar/backports/BackportMethodsStub.java
@@ -43,6 +43,56 @@
     }
   }
 
+  static class ByteStub {
+    public static int compareUnsigned(byte x, byte y) {
+      return 0;
+    }
+  }
+
+  static class ShortStub {
+    public static int compareUnsigned(short x, short y) {
+      return 0;
+    }
+  }
+
+  static class ArraysStub {
+    public static int mismatch(byte[] a, byte[] b) {
+      return 0;
+    }
+
+    public static int mismatch(
+        byte[] a, int aFromIndex, int aToIndex, byte[] b, int bFromIndex, int bToIndex) {
+      return 0;
+    }
+
+    public static int mismatch(short[] a, short[] b) {
+      return 0;
+    }
+
+    public static int mismatch(
+        short[] a, int aFromIndex, int aToIndex, short[] b, int bFromIndex, int bToIndex) {
+      return 0;
+    }
+
+    public static int mismatch(int[] a, int[] b) {
+      return 0;
+    }
+
+    public static int mismatch(
+        int[] a, int aFromIndex, int aToIndex, int[] b, int bFromIndex, int bToIndex) {
+      return 0;
+    }
+
+    public static int mismatch(long[] a, long[] b) {
+      return 0;
+    }
+
+    public static int mismatch(
+        long[] a, int aFromIndex, int aToIndex, long[] b, int bFromIndex, int bToIndex) {
+      return 0;
+    }
+  }
+
   static class LongStub {
     public static long parseUnsignedLong(CharSequence s, int beginIndex, int endIndex, int radix)
         throws NumberFormatException {
diff --git a/src/test/java8/ir/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java b/src/test/java8/ir/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
index 8f10a52..e9c7de6 100644
--- a/src/test/java8/ir/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
+++ b/src/test/java8/ir/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
@@ -17,9 +17,12 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.backports.BackportMethodsStub.AndroidOsBuildStub;
 import com.android.tools.r8.ir.desugar.backports.BackportMethodsStub.AndroidOsBuildVersionStub;
+import com.android.tools.r8.ir.desugar.backports.BackportMethodsStub.ArraysStub;
+import com.android.tools.r8.ir.desugar.backports.BackportMethodsStub.ByteStub;
 import com.android.tools.r8.ir.desugar.backports.BackportMethodsStub.IOStub;
 import com.android.tools.r8.ir.desugar.backports.BackportMethodsStub.LongStub;
 import com.android.tools.r8.ir.desugar.backports.BackportMethodsStub.MathStub;
+import com.android.tools.r8.ir.desugar.backports.BackportMethodsStub.ShortStub;
 import com.android.tools.r8.ir.desugar.backports.BackportMethodsStub.UnsafeStub;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.collect.ImmutableList;
@@ -110,19 +113,17 @@
   @Override
   protected String generateMethods() throws IOException {
     stubMap =
-        ImmutableMap.of(
-            classToType(IOStub.class),
-            factory.createType("Ljava/lang/IO;"),
-            classToType(LongStub.class),
-            factory.boxedLongType,
-            classToType(MathStub.class),
-            factory.mathType,
-            classToType(UnsafeStub.class),
-            factory.sunMiscUnsafeType,
-            classToType(AndroidOsBuildStub.class),
-            factory.androidOsBuildType,
-            classToType(AndroidOsBuildVersionStub.class),
-            factory.androidOsBuildVersionType);
+        ImmutableMap.<DexType, DexType>builder()
+            .put(classToType(IOStub.class), factory.createType("Ljava/lang/IO;"))
+            .put(classToType(LongStub.class), factory.boxedLongType)
+            .put(classToType(ByteStub.class), factory.boxedByteType)
+            .put(classToType(ShortStub.class), factory.boxedShortType)
+            .put(classToType(ArraysStub.class), factory.arraysType)
+            .put(classToType(MathStub.class), factory.mathType)
+            .put(classToType(UnsafeStub.class), factory.sunMiscUnsafeType)
+            .put(classToType(AndroidOsBuildStub.class), factory.androidOsBuildType)
+            .put(classToType(AndroidOsBuildVersionStub.class), factory.androidOsBuildVersionType)
+            .build();
     return super.generateMethods();
   }
 
diff --git a/src/test/java8/retrace/com/android/tools/r8/retrace/HorizontalMergingWithMergedClassesMappingTest.java b/src/test/java8/retrace/com/android/tools/r8/retrace/HorizontalMergingWithMergedClassesMappingTest.java
index 6133cc7..993b65d 100644
--- a/src/test/java8/retrace/com/android/tools/r8/retrace/HorizontalMergingWithMergedClassesMappingTest.java
+++ b/src/test/java8/retrace/com/android/tools/r8/retrace/HorizontalMergingWithMergedClassesMappingTest.java
@@ -110,11 +110,11 @@
                 StringUtils.joinLines(
                     "com.android.tools.r8.retrace.classes.SynthesizeLineNumber$A -> a:",
                     "# {\"id\":\"com.android.tools.r8.mergedClasses\", \"kind\": \"horizontal\","
-                        + " \"class_id_field\": \"$r8$classId\", \"merged_classes\": [{ \"name\":"
+                        + " \"classIdField\": \"$r8$classId\", \"mergedClasses\": [{ \"name\":"
                         + " \"com.android.tools.r8.retrace.classes.SynthesizeLineNumber$A\","
-                        + " \"class_id\": \"0\" }, { \"name\":"
+                        + " \"classId\": \"0\" }, { \"name\":"
                         + " \"com.android.tools.r8.retrace.classes.SynthesizeLineNumber$B\","
-                        + " \"class_id\": \"1\" }] }"));
+                        + " \"classId\": \"1\" }] }"));
     compilationResult
         .run(parameters.getRuntime(), SynthesizeLineNumber.Main.class, "normal")
         .inspectOriginalStackTrace(
diff --git a/src/utils/java/com/android/tools/r8/utils/internal/CliParser.java b/src/utils/java/com/android/tools/r8/utils/internal/CliParser.java
index 04bc2ca..4d15b3c 100644
--- a/src/utils/java/com/android/tools/r8/utils/internal/CliParser.java
+++ b/src/utils/java/com/android/tools/r8/utils/internal/CliParser.java
@@ -72,7 +72,7 @@
 
   /**
    * @param name must start with {@code --} and must be unique and non-overlapping
-   * @param description must not contains line breaks, must end with {@code .}, and is automatically
+   * @param description must not contain line breaks, must end with {@code .}, and is automatically
    *     wrapped
    */
   public CliParser<B> option0(String name, String description, Consumer<B> action) {
@@ -84,7 +84,7 @@
 
   /**
    * @param name must start with {@code --} and must be unique and non-overlapping
-   * @param description must not contains line breaks, must end with {@code .}, and is automatically
+   * @param description must not contain line breaks, must end with {@code .}, and is automatically
    *     wrapped
    * @param shorthand must start with {@code -} and must be unique and non-overlapping
    */
@@ -100,7 +100,7 @@
   /**
    * @param name must start with {@code --} and must be unique and non-overlapping
    * @param paramLabel must be surrounded by {@code <} and {@code >}
-   * @param description must not contains line breaks, must end with {@code .}, and is automatically
+   * @param description must not contain line breaks, must end with {@code .}, and is automatically
    *     wrapped
    */
   public CliParser<B> option1(
@@ -115,7 +115,7 @@
   /**
    * @param name must start with {@code --} and must be unique and non-overlapping
    * @param paramLabel must be surrounded by {@code <} and {@code >}
-   * @param description must not contains line breaks, must end with {@code .}, and is automatically
+   * @param description must not contain line breaks, must end with {@code .}, and is automatically
    *     wrapped
    * @param shorthand must start with {@code -} and must be unique and non-overlapping
    */
@@ -134,17 +134,61 @@
   }
 
   /**
+   * @param name must start with {@code --} and must be unique and non-overlapping
+   * @param paramLabel1 must be surrounded by {@code <} and {@code >}
+   * @param paramLabel2 must be surrounded by {@code <} and {@code >}
+   * @param description must not contain line breaks, must end with {@code .}, and is automatically
+   *     wrapped
+   */
+  public CliParser<B> option2(
+      String name,
+      String paramLabel1,
+      String paramLabel2,
+      String description,
+      TriConsumer<B, String, String> action) {
+    checkOptionName(name);
+    checkParam(paramLabel1);
+    checkParam(paramLabel2);
+    checkDescription(description);
+    base.option2(name, paramLabel1, paramLabel2, description, action);
+    return this;
+  }
+
+  /**
+   * @param name must start with {@code --} and must be unique and non-overlapping
+   * @param paramLabel1 must be surrounded by {@code <} and {@code >}
+   * @param paramLabel2 must be surrounded by {@code <} and {@code >}
+   * @param description must not contain line breaks, must end with {@code .}, and is automatically
+   *     wrapped
+   * @param shorthand must start with {@code -} and must be unique and non-overlapping
+   */
+  public CliParser<B> option2(
+      String name,
+      String paramLabel1,
+      String paramLabel2,
+      String description,
+      TriConsumer<B, String, String> action,
+      String shorthand) {
+    checkOptionName(name);
+    checkShorthand(shorthand);
+    checkParam(paramLabel1);
+    checkParam(paramLabel2);
+    checkDescription(description);
+    base.option2(name, paramLabel1, paramLabel2, description, action, shorthand);
+    return this;
+  }
+
+  /**
    * It is assumed that the suffix is optional.
    *
    * @param prefix must start with {@code --} and must be unique and non-overlapping
-   * @param suffixLabel must be surrounded by {@code [} and {@code ]}
-   * @param description must not contains line breaks, must end with {@code .}, and is automatically
+   * @param description must not contain line breaks, must end with {@code .}, and is automatically
    *     wrapped
    */
   public CliParser<B> prefix0(
       String prefix, String suffixLabel, String description, BiConsumer<B, String> action) {
     checkOptionName(prefix);
-    checkOptionalParam(suffixLabel);
+    checkSuffixLabel(suffixLabel);
     checkDescription(description);
     base.prefix0(prefix, suffixLabel, description, action);
     return this;
@@ -154,9 +198,31 @@
    * It is assumed that the suffix is optional.
    *
    * @param prefix must start with {@code --} and must be unique and non-overlapping
-   * @param suffixLabel must be surrounded by {@code [} and {@code ]}
+   * @param description must not contain line breaks, must end with {@code .}, and is automatically
+   *     wrapped
+   * @param shorthand must start with {@code -} or {@code --} and must be unique and non-overlapping
+   */
+  public CliParser<B> prefix0(
+      String prefix,
+      String suffixLabel,
+      String description,
+      BiConsumer<B, String> action,
+      String shorthand) {
+    checkOptionName(prefix);
+    checkShorthand(shorthand);
+    checkSuffixLabel(suffixLabel);
+    checkDescription(description);
+    base.prefix0(prefix, suffixLabel, description, action, shorthand);
+    return this;
+  }
+
+  /**
+   * It is assumed that the suffix is optional.
+   *
+   * @param prefix must start with {@code --} and must be unique and non-overlapping
+   * @param suffixLabel descriptive suffix label
    * @param paramLabel must be surrounded by {@code <} and {@code >}
-   * @param description must not contains line breaks, must end with {@code .}, and is automatically
+   * @param description must not contain line breaks, must end with {@code .}, and is automatically
    *     wrapped
    */
   public CliParser<B> prefix1(
@@ -166,7 +232,7 @@
       String description,
       TriConsumer<B, String, String> action) {
     checkOptionName(prefix);
-    checkOptionalParam(suffixLabel);
+    checkSuffixLabel(suffixLabel);
     checkParam(paramLabel);
     checkDescription(description);
     base.prefix1(prefix, suffixLabel, paramLabel, description, action);
@@ -177,10 +243,35 @@
    * It is assumed that the suffix is optional.
    *
    * @param prefix must start with {@code --} and must be unique and non-overlapping
-   * @param suffixLabel must be surrounded by {@code [} and {@code ]}
+   * @param paramLabel must be surrounded by {@code <} and {@code >}
+   * @param description must not contain line breaks, must end with {@code .}, and is automatically
+   *     wrapped
+   * @param shorthand must start with {@code -} or {@code --} and must be unique and non-overlapping
+   */
+  public CliParser<B> prefix1(
+      String prefix,
+      String suffixLabel,
+      String paramLabel,
+      String description,
+      TriConsumer<B, String, String> action,
+      String shorthand) {
+    checkOptionName(prefix);
+    checkShorthand(shorthand);
+    checkSuffixLabel(suffixLabel);
+    checkParam(paramLabel);
+    checkDescription(description);
+    base.prefix1(prefix, suffixLabel, paramLabel, description, action, shorthand);
+    return this;
+  }
+
+  /**
+   * It is assumed that the suffix is optional.
+   *
+   * @param prefix must start with {@code --} and must be unique and non-overlapping
+   * @param suffixLabel descriptive suffix label
    * @param paramLabel1 must be surrounded by {@code <} and {@code >}
    * @param paramLabel2 must be surrounded by {@code <} and {@code >}
-   * @param description must not contains line breaks, must end with {@code .}, and is automatically
+   * @param description must not contain line breaks, must end with {@code .}, and is automatically
    *     wrapped
    */
   public CliParser<B> prefix2(
@@ -191,7 +282,7 @@
       String description,
       QuadConsumer<B, String, String, String> action) {
     checkOptionName(prefix);
-    checkOptionalParam(suffixLabel);
+    checkSuffixLabel(suffixLabel);
     checkParam(paramLabel1);
     checkParam(paramLabel2);
     checkDescription(description);
@@ -199,6 +290,34 @@
     return this;
   }
 
+  /**
+   * It is assumed that the suffix is optional.
+   *
+   * @param prefix must start with {@code --} and must be unique and non-overlapping
+   * @param paramLabel1 must be surrounded by {@code <} and {@code >}
+   * @param paramLabel2 must be surrounded by {@code <} and {@code >}
+   * @param description must not contain line breaks, must end with {@code .}, and is automatically
+   *     wrapped
+   * @param shorthand must start with {@code -} or {@code --} and must be unique and non-overlapping
+   */
+  public CliParser<B> prefix2(
+      String prefix,
+      String suffixLabel,
+      String paramLabel1,
+      String paramLabel2,
+      String description,
+      QuadConsumer<B, String, String, String> action,
+      String shorthand) {
+    checkOptionName(prefix);
+    checkShorthand(shorthand);
+    checkSuffixLabel(suffixLabel);
+    checkParam(paramLabel1);
+    checkParam(paramLabel2);
+    checkDescription(description);
+    base.prefix2(prefix, suffixLabel, paramLabel1, paramLabel2, description, action, shorthand);
+    return this;
+  }
+
   private void checkOptionName(String name) {
     assert name.startsWith("--") : name + " does not start with --";
     assert name.length() > 2 : name + " is an empty name";
@@ -207,8 +326,11 @@
 
   private void checkShorthand(String shorthand) {
     assert shorthand.startsWith("-") : shorthand + " does not start with -";
-    assert shorthand.length() > 1 : shorthand + " is an empty name";
-    assert shorthand.charAt(1) != '-' : shorthand + " has a second '-'";
+    if (shorthand.startsWith("--")) {
+      checkOptionName(shorthand);
+    } else {
+      assert shorthand.length() > 1 : shorthand + " is an empty name";
+    }
   }
 
   private void checkParam(String param) {
@@ -216,9 +338,8 @@
     assert param.length() > 2 : "parameter label content is empty: " + param;
   }
 
-  private void checkOptionalParam(String param) {
-    assert param.startsWith("[") && param.endsWith("]") : param + " is not surrounded by [].";
-    assert param.length() > 2 : "optional parameter label content is empty: " + param;
+  private void checkSuffixLabel(String suffixLabel) {
+    assert !suffixLabel.isEmpty() : "suffix label is empty.";
   }
 
   private void checkDescription(String description) {
diff --git a/src/utils/java/com/android/tools/r8/utils/internal/CliParserBase.java b/src/utils/java/com/android/tools/r8/utils/internal/CliParserBase.java
index 2fa4b5d..1d7265c 100644
--- a/src/utils/java/com/android/tools/r8/utils/internal/CliParserBase.java
+++ b/src/utils/java/com/android/tools/r8/utils/internal/CliParserBase.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.utils.internal;
 
 import com.android.tools.r8.utils.internal.collections.Pair;
+import com.android.tools.r8.utils.internal.exceptions.Unreachable;
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
 import java.util.Deque;
@@ -17,6 +18,7 @@
 
   private final Map<String, Consumer<B>> options0 = new HashMap<>();
   private final Map<String, BiConsumer<B, String>> options1 = new HashMap<>();
+  private final Map<String, TriConsumer<B, String, String>> options2 = new HashMap<>();
   private final Map<String, BiConsumer<B, String>> prefix0 = new HashMap<>();
   private final Map<String, TriConsumer<B, String, String>> prefix1 = new HashMap<>();
   private final Map<String, QuadConsumer<B, String, String, String>> prefix2 = new HashMap<>();
@@ -104,6 +106,37 @@
   }
 
   /**
+   * @param name must be unique and non-overlapping
+   */
+  public CliParserBase<B> option2(
+      String name,
+      String paramLabel1,
+      String paramLabel2,
+      String description,
+      TriConsumer<B, String, String> action) {
+    addOption2(name, action);
+    addHelp(name, null, null, ImmutableList.of(paramLabel1, paramLabel2), description);
+    return this;
+  }
+
+  /**
+   * @param name must be unique and non-overlapping
+   * @param shorthand must be unique and non-overlapping
+   */
+  public CliParserBase<B> option2(
+      String name,
+      String paramLabel1,
+      String paramLabel2,
+      String description,
+      TriConsumer<B, String, String> action,
+      String shorthand) {
+    addOption2(name, action);
+    addOption2(shorthand, action);
+    addHelp(name, shorthand, null, ImmutableList.of(paramLabel1, paramLabel2), description);
+    return this;
+  }
+
+  /**
    * @param prefix must start with {@code --} and must be unique and non-overlapping
    */
   public CliParserBase<B> prefix0(
@@ -115,6 +148,22 @@
 
   /**
    * @param prefix must start with {@code --} and must be unique and non-overlapping
+   * @param shorthand must be unique and non-overlapping
+   */
+  public CliParserBase<B> prefix0(
+      String prefix,
+      String suffixLabel,
+      String description,
+      BiConsumer<B, String> action,
+      String shorthand) {
+    addPrefix0(prefix, action);
+    addPrefix0(shorthand, action);
+    addHelp(prefix, shorthand, suffixLabel, ImmutableList.of(), description);
+    return this;
+  }
+
+  /**
+   * @param prefix must start with {@code --} and must be unique and non-overlapping
    */
   public CliParserBase<B> prefix1(
       String prefix,
@@ -129,6 +178,23 @@
 
   /**
    * @param prefix must start with {@code --} and must be unique and non-overlapping
+   * @param shorthand must be unique and non-overlapping
+   */
+  public CliParserBase<B> prefix1(
+      String prefix,
+      String suffixLabel,
+      String paramLabel,
+      String description,
+      TriConsumer<B, String, String> action,
+      String shorthand) {
+    addPrefix1(prefix, action);
+    addPrefix1(shorthand, action);
+    addHelp(prefix, shorthand, suffixLabel, ImmutableList.of(paramLabel), description);
+    return this;
+  }
+
+  /**
+   * @param prefix must start with {@code --} and must be unique and non-overlapping
    */
   public CliParserBase<B> prefix2(
       String prefix,
@@ -143,6 +209,25 @@
   }
 
   /**
+   * @param prefix must start with {@code --} and must be unique and non-overlapping
+   * @param shorthand must be unique and non-overlapping
+   */
+  public CliParserBase<B> prefix2(
+      String prefix,
+      String suffixLabel,
+      String paramLabel1,
+      String paramLabel2,
+      String description,
+      QuadConsumer<B, String, String, String> action,
+      String shorthand) {
+    addPrefix2(prefix, action);
+    addPrefix2(shorthand, action);
+    addHelp(
+        prefix, shorthand, suffixLabel, ImmutableList.of(paramLabel1, paramLabel2), description);
+    return this;
+  }
+
+  /**
    * @param action only one positional handler can be bound
    */
   public CliParserBase<B> positional(BiConsumer<B, String> action) {
@@ -178,16 +263,32 @@
         }
       }
 
+      Pair<String, String> prefixMatch;
       if (tryParseOption0(arg, eqValue, builder, errorReporter)) {
         // Matched.
       } else if (tryParseOption1(arg, eqValue, args, builder, errorReporter)) {
         // Matched.
-      } else if (tryParsePrefix0(arg, eqValue, builder, errorReporter)) {
+      } else if (tryParseOption2(arg, eqValue, args, builder, errorReporter)) {
         // Matched.
-      } else if (tryParsePrefix1(arg, eqValue, args, builder, errorReporter)) {
-        // Matched.
-      } else if (tryParsePrefix2(arg, eqValue, args, builder, errorReporter)) {
-        // Matched.
+      } else if ((prefixMatch = findLongestMatchingPrefix(arg)) != null) {
+        String prefix = prefixMatch.getFirst();
+        String suffix = prefixMatch.getSecond();
+        if (tryParsePrefix0(prefix, suffix, eqValue, builder, errorReporter)) {
+          // Matched.
+        } else if (tryParsePrefix1(prefix, suffix, eqValue, args, builder, errorReporter)) {
+          // Matched.
+        } else if (tryParsePrefix2(prefix, suffix, eqValue, args, builder, errorReporter)) {
+          // Matched.
+        } else {
+          throw new Unreachable(
+              "The found prefix "
+                  + prefix
+                  + " of "
+                  + arg
+                  + " was not handled (suffix: "
+                  + suffix
+                  + ")");
+        }
       } else if (tryParsePositional(rawArg, builder)) {
         // Matched.
       } else {
@@ -219,71 +320,122 @@
     } else if (!args.isEmpty()) {
       options1.get(arg).accept(builder, args.removeFirst());
     } else {
-      errorReporter.accept("Missing parameter for " + arg + ".");
+      errorReporter.accept("Missing argument for " + arg + ".");
       args.clear();
     }
     return true;
   }
 
-  private boolean tryParsePrefix0(
-      String arg, String eqValue, B builder, Consumer<String> errorReporter) {
-    Pair<String, BiConsumer<B, String>> match = findMatchedPrefix(arg, prefix0);
-    if (match == null) {
+  private boolean tryParseOption2(
+      String arg, String eqValue, Deque<String> args, B builder, Consumer<String> errorReporter) {
+    if (!options2.containsKey(arg)) {
       return false;
     }
     if (eqValue != null) {
-      errorReporter.accept("Option " + arg + " does not take a value.");
+      errorReporter.accept(
+          "Cannot use '--option=argument' syntax for " + arg + " (expects 2 arguments).");
+      args.clear();
+      return true;
+    }
+
+    String arg1;
+    if (!args.isEmpty()) {
+      arg1 = args.removeFirst();
     } else {
-      match.getSecond().accept(builder, match.getFirst());
+      errorReporter.accept("Missing arguments for " + arg + " (expects 2 arguments).");
+      args.clear();
+      return true;
+    }
+
+    String arg2;
+    if (!args.isEmpty()) {
+      arg2 = args.removeFirst();
+    } else {
+      errorReporter.accept("Missing second argument for " + arg + " (expects 2 arguments).");
+      args.clear();
+      return true;
+    }
+
+    options2.get(arg).accept(builder, arg1, arg2);
+    return true;
+  }
+
+  private boolean tryParsePrefix0(
+      String prefix, String suffix, String eqValue, B builder, Consumer<String> errorReporter) {
+    BiConsumer<B, String> handler = prefix0.get(prefix);
+    if (handler == null) {
+      return false;
+    }
+    if (eqValue != null) {
+      errorReporter.accept("Option " + prefix + suffix + " does not take an argument.");
+    } else {
+      handler.accept(builder, suffix);
     }
     return true;
   }
 
   private boolean tryParsePrefix1(
-      String arg, String eqValue, Deque<String> args, B builder, Consumer<String> errorReporter) {
-    Pair<String, TriConsumer<B, String, String>> match = findMatchedPrefix(arg, prefix1);
-    if (match == null) {
+      String prefix,
+      String suffix,
+      String eqValue,
+      Deque<String> args,
+      B builder,
+      Consumer<String> errorReporter) {
+    TriConsumer<B, String, String> handler = prefix1.get(prefix);
+    if (handler == null) {
       return false;
     }
     if (eqValue != null) {
-      match.getSecond().accept(builder, match.getFirst(), eqValue);
+      handler.accept(builder, suffix, eqValue);
     } else if (!args.isEmpty()) {
-      match.getSecond().accept(builder, match.getFirst(), args.removeFirst());
+      handler.accept(builder, suffix, args.removeFirst());
     } else {
-      errorReporter.accept("Missing parameter for " + arg + ".");
+      errorReporter.accept("Missing argument for " + prefix + suffix + ".");
       args.clear();
     }
     return true;
   }
 
   private boolean tryParsePrefix2(
-      String arg, String eqValue, Deque<String> args, B builder, Consumer<String> errorReporter) {
-    Pair<String, QuadConsumer<B, String, String, String>> match = findMatchedPrefix(arg, prefix2);
-    if (match == null) {
+      String prefix,
+      String suffix,
+      String eqValue,
+      Deque<String> args,
+      B builder,
+      Consumer<String> errorReporter) {
+    QuadConsumer<B, String, String, String> handler = prefix2.get(prefix);
+    if (handler == null) {
       return false;
     }
-    String arg1;
-    String arg2;
     if (eqValue != null) {
-      arg1 = eqValue;
-      if (!args.isEmpty()) {
-        arg2 = args.removeFirst();
-      } else {
-        errorReporter.accept("Missing parameter for " + arg + ".");
-        args.clear();
-        return true;
-      }
-    } else {
-      if (args.size() >= 2) {
-        arg1 = args.removeFirst();
-        arg2 = args.removeFirst();
-      } else {
-        errorReporter.accept("Missing parameter for " + arg + ".");
-        args.clear();
-        return true;
-      }
+      errorReporter.accept(
+          "Cannot use '--option=argument' syntax for "
+              + prefix
+              + suffix
+              + " (expects 2 arguments).");
+      args.clear();
+      return true;
     }
-    match.getSecond().accept(builder, match.getFirst(), arg1, arg2);
+
+    String arg1;
+    if (!args.isEmpty()) {
+      arg1 = args.removeFirst();
+    } else {
+      errorReporter.accept("Missing arguments for " + prefix + suffix + " (expects 2 arguments).");
+      args.clear();
+      return true;
+    }
+
+    String arg2;
+    if (!args.isEmpty()) {
+      arg2 = args.removeFirst();
+    } else {
+      errorReporter.accept(
+          "Missing second argument for " + prefix + suffix + " (expects 2 arguments).");
+      args.clear();
+      return true;
+    }
+    handler.accept(builder, suffix, arg1, arg2);
     return true;
   }
 
@@ -295,13 +447,22 @@
     return true;
   }
 
-  /** The returned string in the pair is the suffix part of arg. */
-  private <V> Pair<String, V> findMatchedPrefix(String arg, Map<String, V> prefixMap) {
-    for (String prefix : prefixMap.keySet()) {
-      if (arg.startsWith(prefix)) {
-        String suffix = arg.substring(prefix.length());
-        return Pair.create(suffix, prefixMap.get(prefix));
-      }
+  /** Returns (prefix, suffix) for the longest prefix match in {@link #forEachPrefix} if any. */
+  private Pair<String, String> findLongestMatchingPrefix(String arg) {
+    final Box<String> bestMatchBox = new Box<>(null);
+    forEachPrefix(
+        prefix -> {
+          if (arg.startsWith(prefix)) {
+            String bestMatch = bestMatchBox.get();
+            if (bestMatch == null || prefix.length() > bestMatch.length()) {
+              bestMatchBox.set(prefix);
+            }
+          }
+        });
+    String bestMatch = bestMatchBox.get();
+    if (bestMatch != null) {
+      String suffix = arg.substring(bestMatch.length());
+      return Pair.create(bestMatch, suffix);
     }
     return null;
   }
@@ -316,6 +477,11 @@
     options1.put(name, action);
   }
 
+  private void addOption2(String name, TriConsumer<B, String, String> action) {
+    assert assertThatOptionIsNew(name);
+    options2.put(name, action);
+  }
+
   private void addPrefix0(String prefix, BiConsumer<B, String> action) {
     assert assertThatPrefixIsNew(prefix);
     prefix0.put(prefix, action);
@@ -348,6 +514,7 @@
   private void forEachOption(Consumer<String> action) {
     options0.keySet().forEach(action);
     options1.keySet().forEach(action);
+    options2.keySet().forEach(action);
   }
 
   private void forEachPrefix(Consumer<String> action) {
@@ -378,8 +545,7 @@
         });
     forEachPrefix(
         existing -> {
-          assert !name.startsWith(existing) && !existing.startsWith(name)
-              : "Overlap detected: Prefix " + name + " and prefix " + existing;
+          assert !name.equals(existing) : "Duplicate prefix: " + name;
         });
     return true;
   }
diff --git a/src/utils/java/com/android/tools/r8/utils/internal/ListUtils.java b/src/utils/java/com/android/tools/r8/utils/internal/ListUtils.java
index 4e23727..f801e99 100644
--- a/src/utils/java/com/android/tools/r8/utils/internal/ListUtils.java
+++ b/src/utils/java/com/android/tools/r8/utils/internal/ListUtils.java
@@ -128,6 +128,15 @@
     return result;
   }
 
+  public static <S, T, E extends Throwable> List<T> mapThrowing(
+      Collection<S> list, ThrowingFunction<S, T, E> fn) throws E {
+    List<T> result = new ArrayList<>(list.size());
+    for (S element : list) {
+      result.add(fn.apply(element));
+    }
+    return result;
+  }
+
   public static <S, T> List<T> mapNotNull(Collection<S> list, Function<S, T> fn) {
     List<T> result = new ArrayList<>(list.size());
     for (S element : list) {
diff --git a/src/utils/java/com/android/tools/r8/utils/internal/StringBuilderUtils.java b/src/utils/java/com/android/tools/r8/utils/internal/StringBuilderUtils.java
new file mode 100644
index 0000000..34dae39
--- /dev/null
+++ b/src/utils/java/com/android/tools/r8/utils/internal/StringBuilderUtils.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2026, 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.internal;
+
+public class StringBuilderUtils {
+
+  /** Append lines with platform-dependent newline characters. */
+  public static StringBuilder appendLines(StringBuilder builder, Iterable<String> lines) {
+    lines.forEach(line -> appendLine(builder, line));
+    return builder;
+  }
+
+  /** Append line with platform-dependent newline characters. */
+  public static StringBuilder appendLine(StringBuilder builder, String line) {
+    builder.append(line).append(System.lineSeparator());
+    return builder;
+  }
+}
diff --git a/third_party/api_database/api_database.tar.gz.sha1 b/third_party/api_database/api_database.tar.gz.sha1
index 3f41118..c6c0f0d 100644
--- a/third_party/api_database/api_database.tar.gz.sha1
+++ b/third_party/api_database/api_database.tar.gz.sha1
@@ -1 +1 @@
-8db44f4f89a9c2417571c9e9d5967d4930bff19a
\ No newline at end of file
+eb96f1f0c74274aedd318d9528da49b5583c3aa4
\ No newline at end of file
diff --git a/tools/archive_smali.py b/tools/archive_smali.py
index 37a2ec7..753b8e4 100755
--- a/tools/archive_smali.py
+++ b/tools/archive_smali.py
@@ -65,6 +65,8 @@
         set_rlimit_to_max()
 
     utils.ensure_google_download(utils.JAVA11_DIR)
+    utils.ensure_google_download(utils.JAVA21_DIR)
+    bot_dir = os.getcwd()
     with utils.TempDir() as temp:
         # Resolve dry run location to support relative directories.
         dry_run_output = None
@@ -102,17 +104,21 @@
 
             print('Building version: %s' % version)
 
+            # Run tests with JDK-21.
+            custom_env = os.environ.copy()
+            custom_env["JAVA_HOME"] = jdk.GetJdk21Home()
+            subprocess.check_call(['./gradlew', 'test', ], env=custom_env)
+
             # Build release to local Maven repository compiling with JDK-11.
             m2 = os.path.join(temp, 'm2')
             os.mkdir(m2)
+            custom_env["JAVA_HOME"] = jdk.GetJdk11Home()
             subprocess.check_call([
                 './gradlew',
-                '-Dorg.gradle.java.home=%s' % jdk.GetJdk11Home(),
                 '-Dmaven.repo.local=%s' % m2,
                 'release',
-                'test',
                 'publishToMavenLocal',
-            ])
+            ], env=custom_env)
             base = os.path.join('com', 'android', 'tools', 'smali')
 
             # Check that the local maven repository only has the single version directory in
@@ -181,7 +187,9 @@
                 public_url = 'https://storage.googleapis.com/%s/smali/%s' % (
                     ARCHIVE_BUCKET, version)
                 # Copy Maven ZIP for LUCI recipie to find it.
-                shutil.copyfile(maven_release_archive, 'smali-maven-release.zip')
+                shutil.copyfile(
+                    maven_release_archive,
+                    os.path.join(bot_dir, 'smali-maven-release.zip'))
                 print('Artifacts available at: %s' % public_url)
 
     print("Done!")
diff --git a/tools/create_art_tests.py b/tools/create_art_tests.py
deleted file mode 100755
index 4d7c15b..0000000
--- a/tools/create_art_tests.py
+++ /dev/null
@@ -1,112 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2017, 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.
-
-import os
-import shutil
-from string import Template
-
-OUTPUT_DIR = os.path.join('build', 'generated', 'test', 'java', 'com',
-                          'android', 'tools', 'r8', 'art')
-TEST_DIR = os.path.join('tests', '2017-10-04', 'art')
-TOOLCHAINS = [
-    ("none", os.path.join(TEST_DIR, "dx")),
-]
-TOOLS = ["r8", "d8", "r8cf"]
-TEMPLATE = Template(
-    """// Copyright (c) 2016, 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.art.$testGeneratingToolchain.$compilerUnderTest;
-
-import static com.android.tools.r8.R8RunArtTestsTest.DexTool.$testGeneratingToolchainEnum;
-
-import com.android.tools.r8.R8RunArtTestsTest;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-/**
- * Auto-generated test for the art $name test using the $testGeneratingToolchain toolchain.
- *
- * DO NOT EDIT THIS FILE. EDIT THE HERE DOCUMENT TEMPLATE IN tools/create_art_tests.py INSTEAD!
- */
-@RunWith(Parameterized.class)
-public class $testClassName extends R8RunArtTestsTest {
-
-    @Parameters(name = "{0}")
-    public static TestParametersCollection data() {
-      return TestBase.getTestParameters().$testRuntimes.build();
-    }
-
-    private final TestParameters parameters;
-
-    public $testClassName(TestParameters parameters) {
-      super("$name", $testGeneratingToolchainEnum);
-      this.parameters = parameters;
-    }
-
-    @Test
-    public void test() throws Throwable {
-      runArtTest(parameters.getRuntime().asDex().getVm(), CompilerUnderTest.$compilerUnderTestEnum);
-    }
-}
-""")
-
-
-def get_test_configurations():
-    for toolchain, source_dir in TOOLCHAINS:
-        for tool in TOOLS:
-            if tool == "d8" and toolchain == "none":
-                tool_enum = 'R8_AFTER_D8'
-            else:
-                tool_enum = tool.upper()
-            if tool == "r8cf":
-                if toolchain != "none":
-                    continue
-                tool_enum = 'D8_AFTER_R8CF'
-            output_dir = os.path.join(OUTPUT_DIR, toolchain, tool)
-            yield (tool_enum, tool, toolchain, source_dir, output_dir)
-
-
-def create_tests():
-    for tool_enum, tool, toolchain, source_dir, output_dir in get_test_configurations(
-    ):
-        test_cases = [
-            d for d in os.listdir(source_dir)
-            if os.path.isdir(os.path.join(source_dir, d))
-        ]
-        if os.path.exists(output_dir):
-            shutil.rmtree(output_dir)
-        os.makedirs(output_dir)
-        for test_case in test_cases:
-            class_name = "Art" + test_case.replace("-", "_") + "Test"
-            template = TEMPLATE
-            test_runtimes = "withDexRuntimes()"
-            if (test_case in ["800-smali"]):
-                test_runtimes = "withDexRuntimesEndingAtExcluding(\n          com.android.tools.r8.ToolHelper.DexVm.Version.V17_0_0)"
-            contents = template.substitute(
-                testRuntimes=test_runtimes,
-                name=test_case,
-                compilerUnderTestEnum=tool_enum,
-                compilerUnderTest=tool,
-                testGeneratingToolchain=toolchain,
-                testGeneratingToolchainEnum=toolchain.upper(),
-                testClassName=class_name)
-            with open(os.path.join(output_dir, class_name + ".java"),
-                      "w") as fp:
-                fp.write(contents)
-
-
-def main():
-    create_tests()
-
-
-if __name__ == "__main__":
-    main()
diff --git a/tools/trigger.py b/tools/trigger.py
index f07873a..aebb332 100755
--- a/tools/trigger.py
+++ b/tools/trigger.py
@@ -56,7 +56,7 @@
                       help='Build smali version <version>.')
     result.add_option('--builder', help='Trigger specific builder')
     (options, args) = result.parse_args()
-    if not options.cl.startswith('http'):
+    if options.cl and not options.cl.startswith('http'):
         [cl_number, patch_number] = [int(x) for x in options.cl.split('/')]
         options.cl = 'https://r8-review.googlesource.com/c/r8/+/%s/%s' % (
             cl_number, patch_number)