Merge commit 'e2ff4cb5293f7554ddb5cca73c71b15fd2bcbdca' into dev-release

Change-Id: I71311dcfa1c1b7fe906f27291b3cae62a5c3275b
diff --git a/.gitignore b/.gitignore
index edb84ad..78a056c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -96,6 +96,8 @@
 third_party/dependencies_new.tar.gz
 third_party/desugar/desugar_*.tar.gz
 third_party/desugar/desugar_*/
+third_party/examplesAndroidOLegacy
+third_party/examplesAndroidOLegacy.tar.gz
 third_party/framework
 third_party/framework.tar.gz
 third_party/gmail/*
diff --git a/build.gradle b/build.gradle
index 7485331b..c766f7b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -251,6 +251,7 @@
 
     examplesAndroidOCompile group: 'org.ow2.asm', name: 'asm', version: asmVersion
     examplesAndroidOCompile files("third_party/android_jar/lib-v26/android.jar")
+    examplesAndroidOCompile files("third_party/examplesAndroidOLegacy")
 
     examplesAndroidPCompile group: 'org.ow2.asm', name: 'asm', version: asmVersion
     // Import Guava for @Nullable annotation
@@ -313,6 +314,7 @@
                 "dagger/2.41",
                 "dart-sdk",
                 "ddmlib",
+                "examplesAndroidOLegacy",
                 "gradle/gradle",
                 "google/google-java-format/1.14.0",
                 "google-java-format",
@@ -1298,29 +1300,14 @@
 task buildExampleAndroidOJars {
     dependsOn downloadDeps
     def examplesDir = file("src/test/examplesAndroidO")
-    // NOTE: we want to enable a scenario when test needs to reference some
-    // classes generated by legacy (1.6) Java compiler to test some specific
-    // behaviour. To do so we compile all the java files located in sub-directory
-    // called 'legacy' with Java 1.6, then compile the rest of the files with
-    // Java 1.8 and a reference to previously generated 1.6 classes.
-
-    // Compiling all classes in dirs 'legacy' with old Java version.
-    task "compile_examplesAndroidO_Legacy"(type: JavaCompile) {
-        source = fileTree(dir: examplesDir, include: '**/legacy/**/*.java')
-        destinationDir = file("build/test/examplesAndroidOLegacy/classes")
-        classpath = sourceSets.main.compileClasspath
-        sourceCompatibility = JavaVersion.VERSION_1_6
-        targetCompatibility = JavaVersion.VERSION_1_6
-        options.compilerArgs += ["-Xlint:-options", "-parameters"]
-    }
     // Compiling the rest of the files as Java 1.8 code.
     task "compile_examplesAndroidO"(type: JavaCompile) {
-        dependsOn "compile_examplesAndroidO_Legacy"
         source = fileTree(dir: examplesDir, include: '**/*.java', exclude: '**/legacy/**/*.java')
         destinationDir = file("build/test/examplesAndroidO/classes")
         classpath = sourceSets.main.compileClasspath
         classpath += files("build/test/examplesAndroidOLegacy/classes")
-	classpath += files("third_party/android_jar/lib-v26/android.jar")
+        classpath += files("third_party/android_jar/lib-v26/android.jar")
+        classpath += files("third_party/examplesAndroidOLegacy")
         sourceCompatibility = JavaVersion.VERSION_1_8
         targetCompatibility = JavaVersion.VERSION_1_8
         options.compilerArgs += ["-Xlint:-options", "-parameters"]
@@ -1333,7 +1320,7 @@
                     dependsOn: "compile_examplesAndroidO") {
                 main = name + ".TestGenerator"
                 classpath = files(destinationDir, sourceSets.main.compileClasspath)
-                args destinationDir
+                args destinationDir, file("build/test/examplesAndroidO/classes/${name}").toString()
             }
         } else {
             task "generate_examplesAndroidO_${name}" () {}
@@ -1377,8 +1364,8 @@
             task "generate_examplesAndroidP_${name}"(type: JavaExec,
                     dependsOn: "compile_examplesAndroidP") {
                 main = name + ".TestGenerator"
-                classpath = files(destinationDir, sourceSets.main.compileClasspath)
-                args destinationDir
+                classpath = files(destinationDir.toString(), sourceSets.main.compileClasspath)
+                args destinationDir, file("build/test/examplesAndroidP/classes/${name}").toString()
             }
         } else {
             task "generate_examplesAndroidP_${name}" () {}
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
index 12fd28d..2a369cd 100644
--- a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
+++ b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
@@ -11,6 +11,7 @@
 import org.gradle.api.Project
 import org.gradle.api.Task
 import org.gradle.api.file.ConfigurableFileCollection
+import org.gradle.api.file.DuplicatesStrategy
 import org.gradle.api.plugins.JavaPluginExtension
 import org.gradle.api.tasks.JavaExec
 import org.gradle.api.tasks.SourceSet
@@ -116,6 +117,7 @@
     // The TEST_SOURCE_SET_NAME is the source set defined by writing java { sourcesets.test { ... }}
     .getByName(SourceSet.TEST_SOURCE_SET_NAME)
   val destinationDir = getRoot().resolveAll("build", "test", name)
+  val generateDir = getRoot().resolveAll("build", "generated", name)
   val classesOutput = destinationDir.resolve("classes")
   testSourceSet.java.destinationDirectory.set(classesOutput)
   testSourceSet.resources.destinationDirectory.set(destinationDir)
@@ -128,24 +130,35 @@
         arrayOf("compileTestJava", "debuginfo-all", "debuginfo-none").forEach { taskName ->
           if (!project.getTasksByName(taskName, false).isEmpty()) {
             var generationTask : Task? = null
-            val taskSpecificClassesOutput = getOutputName(classesOutput.toString(), taskName)
+            val compileOutput = getOutputName(classesOutput.toString(), taskName)
             if (exampleDir.resolve("TestGenerator.java").isFile) {
+              val generatedOutput = Paths.get(
+                getOutputName(generateDir.toString(), taskName), exampleDir.name).toString()
               generationTask = tasks.register<JavaExec>(
                 "generate-$name-${exampleDir.name}-$taskName") {
                 dependsOn(taskName)
                 mainClass.set("${exampleDir.name}.TestGenerator")
-                classpath = files(taskSpecificClassesOutput, testSourceSet.compileClasspath)
-                args(taskSpecificClassesOutput)
+                classpath = files(compileOutput, testSourceSet.compileClasspath)
+                args(compileOutput, generatedOutput)
+                outputs.dirs(generatedOutput)
               }.get()
             }
             jarTasks.add(tasks.register<Jar>("jar-$name-${exampleDir.name}-$taskName") {
               dependsOn(taskName)
-              if (generationTask != null) {
-                dependsOn(generationTask)
-              }
               archiveFileName.set("${getOutputName(exampleDir.name, taskName)}.jar")
               destinationDirectory.set(destinationDir)
-              from(taskSpecificClassesOutput) {
+              duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+              if (generationTask != null) {
+                // If a generation task exists, we first take the generated output and add to the
+                // current jar. Running with DuplicatesStrategy.EXCLUDE ensure that we do not
+                // overwrite with the non-generated file.
+                dependsOn(generationTask)
+                from(generationTask.outputs.files.singleFile.parentFile) {
+                  include("${exampleDir.name}/**/*.class")
+                  exclude("**/TestGenerator*")
+                }
+              }
+              from(compileOutput) {
                 include("${exampleDir.name}/**/*.class")
                 exclude("**/TestGenerator*")
               }
@@ -356,6 +369,10 @@
     "ddmlib",
     Paths.get("third_party", "ddmlib").toFile(),
     Paths.get("third_party", "ddmlib.tar.gz.sha1").toFile())
+  val examplesAndroidOLegacy = ThirdPartyDependency(
+    "examplesAndroidOLegacy",
+    Paths.get("third_party", "examplesAndroidOLegacy").toFile(),
+    Paths.get("third_party", "examplesAndroidOLegacy.tar.gz.sha1").toFile())
   val desugarJdkLibs = ThirdPartyDependency(
     "desugar-jdk-libs",
     Paths.get("third_party", "openjdk", "desugar_jdk_libs").toFile(),
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/TestConfigurationHelper.kt b/d8_r8/commonBuildSrc/src/main/kotlin/TestConfigurationHelper.kt
index 4ca414d..c3acc59 100644
--- a/d8_r8/commonBuildSrc/src/main/kotlin/TestConfigurationHelper.kt
+++ b/d8_r8/commonBuildSrc/src/main/kotlin/TestConfigurationHelper.kt
@@ -2,7 +2,12 @@
 // 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 java.util.Date
 import org.gradle.api.tasks.testing.Test
+import org.gradle.api.tasks.testing.TestListener
+import org.gradle.api.tasks.testing.TestDescriptor
+import org.gradle.api.tasks.testing.TestResult
 
 class TestConfigurationHelper {
 
@@ -69,6 +74,25 @@
         test.maxHeapSize = "4G"
       }
 
+      test.addTestListener(object: TestListener {
+        override fun beforeSuite(desc: TestDescriptor?) { }
+        override fun afterSuite(desc: TestDescriptor?, result: TestResult?) { }
+        override fun beforeTest(desc: TestDescriptor?) {
+          if (project.hasProperty("one_line_per_test")) {
+            println("Start executing ${desc}")
+          }
+        }
+        override fun afterTest(desc: TestDescriptor?, result: TestResult?) {
+          if (project.hasProperty("one_line_per_test")) {
+            println("Done executing ${desc} with result: ${result?.resultType}")
+          }
+          if (project.hasProperty("update_test_timestamp")) {
+            File(project.property("update_test_timestamp")!!.toString())
+                .writeText(Date().getTime().toString())
+          }
+        }
+      })
+
       val userDefinedCoresPerFork = System.getenv("R8_GRADLE_CORES_PER_FORK")
       val processors = Runtime.getRuntime().availableProcessors()
       // See https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.Test.html.
@@ -78,8 +102,8 @@
         // On work machines this seems to give the best test execution time (without freezing).
         test.maxParallelForks = processors.div(3)
         // On low cpu count machines (bots) we under subscribe, so increase the count.
-        if (processors == 8) {
-          test.maxParallelForks = 3
+        if (processors == 32) {
+          test.maxParallelForks = 15
         }
       }
     }
diff --git a/d8_r8/gradle.properties b/d8_r8/gradle.properties
index 1de43f9..35bcb0b 100644
--- a/d8_r8/gradle.properties
+++ b/d8_r8/gradle.properties
@@ -11,6 +11,7 @@
 
 org.gradle.parallel=true
 org.gradle.caching=true
+org.gradle.configuration-cache=true
 
 # Do not download any jdks or detect them. We provide them.
 org.gradle.java.installations.auto-detect=false
diff --git a/d8_r8/keepanno/gradle.properties b/d8_r8/keepanno/gradle.properties
index 1de43f9..35bcb0b 100644
--- a/d8_r8/keepanno/gradle.properties
+++ b/d8_r8/keepanno/gradle.properties
@@ -11,6 +11,7 @@
 
 org.gradle.parallel=true
 org.gradle.caching=true
+org.gradle.configuration-cache=true
 
 # Do not download any jdks or detect them. We provide them.
 org.gradle.java.installations.auto-detect=false
diff --git a/d8_r8/library_desugar/gradle.properties b/d8_r8/library_desugar/gradle.properties
index 1de43f9..35bcb0b 100644
--- a/d8_r8/library_desugar/gradle.properties
+++ b/d8_r8/library_desugar/gradle.properties
@@ -11,6 +11,7 @@
 
 org.gradle.parallel=true
 org.gradle.caching=true
+org.gradle.configuration-cache=true
 
 # Do not download any jdks or detect them. We provide them.
 org.gradle.java.installations.auto-detect=false
diff --git a/d8_r8/main/build.gradle.kts b/d8_r8/main/build.gradle.kts
index 4ba82d6..b6a8bda 100644
--- a/d8_r8/main/build.gradle.kts
+++ b/d8_r8/main/build.gradle.kts
@@ -79,10 +79,6 @@
 
   val depsJar by registering(Jar::class) {
     dependsOn(keepAnnoJarTask)
-    doFirst {
-      println(header("R8 full dependencies"))
-      mainJarDependencies().forEach({ println(it) })
-    }
     dependsOn(resourceShrinkerJarTask)
     dependsOn(resourceShrinkerDepsTask)
     from(mainJarDependencies().map(::zipTree))
diff --git a/d8_r8/main/gradle.properties b/d8_r8/main/gradle.properties
index 1de43f9..35bcb0b 100644
--- a/d8_r8/main/gradle.properties
+++ b/d8_r8/main/gradle.properties
@@ -11,6 +11,7 @@
 
 org.gradle.parallel=true
 org.gradle.caching=true
+org.gradle.configuration-cache=true
 
 # Do not download any jdks or detect them. We provide them.
 org.gradle.java.installations.auto-detect=false
diff --git a/d8_r8/r8lib/gradle.properties b/d8_r8/r8lib/gradle.properties
index 1de43f9..35bcb0b 100644
--- a/d8_r8/r8lib/gradle.properties
+++ b/d8_r8/r8lib/gradle.properties
@@ -11,6 +11,7 @@
 
 org.gradle.parallel=true
 org.gradle.caching=true
+org.gradle.configuration-cache=true
 
 # Do not download any jdks or detect them. We provide them.
 org.gradle.java.installations.auto-detect=false
diff --git a/d8_r8/resourceshrinker/build.gradle.kts b/d8_r8/resourceshrinker/build.gradle.kts
index 205df05..60a037b 100644
--- a/d8_r8/resourceshrinker/build.gradle.kts
+++ b/d8_r8/resourceshrinker/build.gradle.kts
@@ -55,8 +55,6 @@
   }
 
   val depsJar by registering(Jar::class) {
-    println(header("Resource shrinker dependencies"))
-    jarDependencies().forEach({ println(it) })
     from(jarDependencies().map(::zipTree))
     exclude("**/*.proto")
     exclude("versions-offline/**")
diff --git a/d8_r8/resourceshrinker/gradle.properties b/d8_r8/resourceshrinker/gradle.properties
index 1de43f9..35bcb0b 100644
--- a/d8_r8/resourceshrinker/gradle.properties
+++ b/d8_r8/resourceshrinker/gradle.properties
@@ -11,6 +11,7 @@
 
 org.gradle.parallel=true
 org.gradle.caching=true
+org.gradle.configuration-cache=true
 
 # Do not download any jdks or detect them. We provide them.
 org.gradle.java.installations.auto-detect=false
diff --git a/d8_r8/settings.gradle.kts b/d8_r8/settings.gradle.kts
index c62627b..9abecbf 100644
--- a/d8_r8/settings.gradle.kts
+++ b/d8_r8/settings.gradle.kts
@@ -18,7 +18,17 @@
   return current.getParentFile()
 }
 
-fun downloadFromGoogleStorage(sha1File : File) {
+fun downloadFromGoogleStorage(outputDir : File) {
+  val targz = File(outputDir.toString() + ".tar.gz")
+  val sha1File = File(targz.toString() + ".sha1")
+  if (outputDir.exists()
+      && outputDir.isDirectory
+      && targz.exists()
+      && sha1File.lastModified() <= targz.lastModified()) {
+      // We already downloaded, no need to recheck the hash
+      return
+  }
+
   val cmd = listOf(
     "download_from_google_storage.py",
     "--extract",
@@ -27,6 +37,7 @@
     "--sha1_file",
     "${sha1File}"
   )
+
   println("Executing command: ${cmd.joinToString(" ")}")
   val process = ProcessBuilder().command(cmd).start()
   process.waitFor()
@@ -41,8 +52,8 @@
 }
 
 val thirdParty = getRepoRoot().resolve("third_party")
-downloadFromGoogleStorage(thirdParty.resolve("dependencies.tar.gz.sha1"))
-downloadFromGoogleStorage(thirdParty.resolve("dependencies_new.tar.gz.sha1"))
+downloadFromGoogleStorage(thirdParty.resolve("dependencies"))
+downloadFromGoogleStorage(thirdParty.resolve("dependencies_new"))
 
 pluginManagement {
   repositories {
diff --git a/d8_r8/test/gradle.properties b/d8_r8/test/gradle.properties
index 1de43f9..35bcb0b 100644
--- a/d8_r8/test/gradle.properties
+++ b/d8_r8/test/gradle.properties
@@ -11,6 +11,7 @@
 
 org.gradle.parallel=true
 org.gradle.caching=true
+org.gradle.configuration-cache=true
 
 # Do not download any jdks or detect them. We provide them.
 org.gradle.java.installations.auto-detect=false
diff --git a/d8_r8/test_modules/tests_bootstrap/build.gradle.kts b/d8_r8/test_modules/tests_bootstrap/build.gradle.kts
index af2fb9f..b196db9 100644
--- a/d8_r8/test_modules/tests_bootstrap/build.gradle.kts
+++ b/d8_r8/test_modules/tests_bootstrap/build.gradle.kts
@@ -50,9 +50,7 @@
 
   withType<KotlinCompile> {
     kotlinOptions {
-      // We are using a new JDK to compile to an older language version, which is not directly
-      // compatible with java toolchains.
-      jvmTarget = "1.8"
+      enabled = false
     }
   }
 
diff --git a/d8_r8/test_modules/tests_bootstrap/gradle.properties b/d8_r8/test_modules/tests_bootstrap/gradle.properties
index 1de43f9..35bcb0b 100644
--- a/d8_r8/test_modules/tests_bootstrap/gradle.properties
+++ b/d8_r8/test_modules/tests_bootstrap/gradle.properties
@@ -11,6 +11,7 @@
 
 org.gradle.parallel=true
 org.gradle.caching=true
+org.gradle.configuration-cache=true
 
 # Do not download any jdks or detect them. We provide them.
 org.gradle.java.installations.auto-detect=false
diff --git a/d8_r8/test_modules/tests_java_10/gradle.properties b/d8_r8/test_modules/tests_java_10/gradle.properties
index 1de43f9..35bcb0b 100644
--- a/d8_r8/test_modules/tests_java_10/gradle.properties
+++ b/d8_r8/test_modules/tests_java_10/gradle.properties
@@ -11,6 +11,7 @@
 
 org.gradle.parallel=true
 org.gradle.caching=true
+org.gradle.configuration-cache=true
 
 # Do not download any jdks or detect them. We provide them.
 org.gradle.java.installations.auto-detect=false
diff --git a/d8_r8/test_modules/tests_java_11/gradle.properties b/d8_r8/test_modules/tests_java_11/gradle.properties
index 1de43f9..35bcb0b 100644
--- a/d8_r8/test_modules/tests_java_11/gradle.properties
+++ b/d8_r8/test_modules/tests_java_11/gradle.properties
@@ -11,6 +11,7 @@
 
 org.gradle.parallel=true
 org.gradle.caching=true
+org.gradle.configuration-cache=true
 
 # Do not download any jdks or detect them. We provide them.
 org.gradle.java.installations.auto-detect=false
diff --git a/d8_r8/test_modules/tests_java_17/gradle.properties b/d8_r8/test_modules/tests_java_17/gradle.properties
index 1de43f9..35bcb0b 100644
--- a/d8_r8/test_modules/tests_java_17/gradle.properties
+++ b/d8_r8/test_modules/tests_java_17/gradle.properties
@@ -11,6 +11,7 @@
 
 org.gradle.parallel=true
 org.gradle.caching=true
+org.gradle.configuration-cache=true
 
 # Do not download any jdks or detect them. We provide them.
 org.gradle.java.installations.auto-detect=false
diff --git a/d8_r8/test_modules/tests_java_20/gradle.properties b/d8_r8/test_modules/tests_java_20/gradle.properties
index 1de43f9..35bcb0b 100644
--- a/d8_r8/test_modules/tests_java_20/gradle.properties
+++ b/d8_r8/test_modules/tests_java_20/gradle.properties
@@ -11,6 +11,7 @@
 
 org.gradle.parallel=true
 org.gradle.caching=true
+org.gradle.configuration-cache=true
 
 # Do not download any jdks or detect them. We provide them.
 org.gradle.java.installations.auto-detect=false
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 49672fa..0d9db78 100644
--- a/d8_r8/test_modules/tests_java_8/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_8/build.gradle.kts
@@ -19,6 +19,7 @@
       srcDir(root.resolveAll("src", "test", "java"))
     }
   }
+
   // We are using a new JDK to compile to an older language version, which is not directly
   // compatible with java toolchains.
   sourceCompatibility = JavaVersion.VERSION_1_8
@@ -74,6 +75,7 @@
     ThirdPartyDeps.desugarJdkLibs,
     ThirdPartyDeps.desugarJdkLibsLegacy,
     ThirdPartyDeps.desugarJdkLibs11,
+    ThirdPartyDeps.examplesAndroidOLegacy,
     ThirdPartyDeps.gson,
     ThirdPartyDeps.jacoco,
     ThirdPartyDeps.java8Runtime,
@@ -147,15 +149,7 @@
   }
 
   withType<KotlinCompile> {
-    dependsOn(gradle.includedBuild("keepanno").task(":jar"))
-    dependsOn(gradle.includedBuild("resourceshrinker").task(":jar"))
-    dependsOn(gradle.includedBuild("main").task(":jar"))
-    dependsOn(thirdPartyCompileDependenciesTask)
-    kotlinOptions {
-      // We are using a new JDK to compile to an older language version, which is not directly
-      // compatible with java toolchains.
-      jvmTarget = "1.8"
-    }
+    enabled = false
   }
 
   withType<Test> {
@@ -196,6 +190,7 @@
     exclude("com/android/tools/r8/classmerging/vertical/ForceInlineConstructorWithRetargetedLibMemberTest**")
     exclude("com/android/tools/r8/ir/optimize/inliner/InlineMethodWithRetargetedLibMemberTest**")
     exclude("com/android/tools/r8/profile/art/DesugaredLibraryArtProfileRewritingTest**")
+    exclude("com/android/tools/r8/profile/art/dump/DumpArtProfileProvidersTest**")
   }
 
   val testJar by registering(Jar::class) {
@@ -210,10 +205,6 @@
     dependsOn(gradle.includedBuild("keepanno").task(":jar"))
     dependsOn(gradle.includedBuild("resourceshrinker").task(":jar"))
     dependsOn(thirdPartyCompileDependenciesTask)
-    doFirst {
-      println(header("Test Java 8 dependencies"))
-    }
-    testDependencies().forEach({ println(it) })
     from(testDependencies().map(::zipTree))
     duplicatesStrategy = DuplicatesStrategy.EXCLUDE
     archiveFileName.set("deps.jar")
diff --git a/d8_r8/test_modules/tests_java_8/gradle.properties b/d8_r8/test_modules/tests_java_8/gradle.properties
index 1de43f9..35bcb0b 100644
--- a/d8_r8/test_modules/tests_java_8/gradle.properties
+++ b/d8_r8/test_modules/tests_java_8/gradle.properties
@@ -11,6 +11,7 @@
 
 org.gradle.parallel=true
 org.gradle.caching=true
+org.gradle.configuration-cache=true
 
 # Do not download any jdks or detect them. We provide them.
 org.gradle.java.installations.auto-detect=false
diff --git a/d8_r8/test_modules/tests_java_9/gradle.properties b/d8_r8/test_modules/tests_java_9/gradle.properties
index 1de43f9..35bcb0b 100644
--- a/d8_r8/test_modules/tests_java_9/gradle.properties
+++ b/d8_r8/test_modules/tests_java_9/gradle.properties
@@ -11,6 +11,7 @@
 
 org.gradle.parallel=true
 org.gradle.caching=true
+org.gradle.configuration-cache=true
 
 # Do not download any jdks or detect them. We provide them.
 org.gradle.java.installations.auto-detect=false
diff --git a/d8_r8/test_modules/tests_java_examples/gradle.properties b/d8_r8/test_modules/tests_java_examples/gradle.properties
index 1de43f9..35bcb0b 100644
--- a/d8_r8/test_modules/tests_java_examples/gradle.properties
+++ b/d8_r8/test_modules/tests_java_examples/gradle.properties
@@ -11,6 +11,7 @@
 
 org.gradle.parallel=true
 org.gradle.caching=true
+org.gradle.configuration-cache=true
 
 # Do not download any jdks or detect them. We provide them.
 org.gradle.java.installations.auto-detect=false
diff --git a/d8_r8/test_modules/tests_java_examplesAndroidN/gradle.properties b/d8_r8/test_modules/tests_java_examplesAndroidN/gradle.properties
index 1de43f9..35bcb0b 100644
--- a/d8_r8/test_modules/tests_java_examplesAndroidN/gradle.properties
+++ b/d8_r8/test_modules/tests_java_examplesAndroidN/gradle.properties
@@ -11,6 +11,7 @@
 
 org.gradle.parallel=true
 org.gradle.caching=true
+org.gradle.configuration-cache=true
 
 # Do not download any jdks or detect them. We provide them.
 org.gradle.java.installations.auto-detect=false
diff --git a/d8_r8/test_modules/tests_java_examplesAndroidO/build.gradle.kts b/d8_r8/test_modules/tests_java_examplesAndroidO/build.gradle.kts
index 048fc5d..062128c 100644
--- a/d8_r8/test_modules/tests_java_examplesAndroidO/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_examplesAndroidO/build.gradle.kts
@@ -22,8 +22,14 @@
   targetCompatibility = JavaVersion.VERSION_1_8
 }
 
+// NOTE: we want to enable a scenario when test needs to reference some classes generated by legacy
+// (1.6) Java compiler to test some specific behaviour. To do so we compile all the java files
+// located in a sub-directory called 'legacy' with Java 1.6, then compile the rest of the files with
+// Java 1.8 and a reference to previously generated 1.6 classes.
+
 dependencies {
   testCompileOnly(Deps.asm)
+  testCompileOnly(resolve(ThirdPartyDeps.examplesAndroidOLegacy))
   testCompileOnly(resolve(getThirdPartyAndroidJar("lib-v26"),"android.jar"))
 }
 
@@ -33,6 +39,7 @@
 val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
   "compileDeps",
   listOf(
+    ThirdPartyDeps.examplesAndroidOLegacy,
     Jdk.JDK_11.getThirdPartyDependency(),
     getThirdPartyAndroidJar("lib-v26")))
 
diff --git a/d8_r8/test_modules/tests_java_examplesAndroidO/gradle.properties b/d8_r8/test_modules/tests_java_examplesAndroidO/gradle.properties
index 1de43f9..35bcb0b 100644
--- a/d8_r8/test_modules/tests_java_examplesAndroidO/gradle.properties
+++ b/d8_r8/test_modules/tests_java_examplesAndroidO/gradle.properties
@@ -11,6 +11,7 @@
 
 org.gradle.parallel=true
 org.gradle.caching=true
+org.gradle.configuration-cache=true
 
 # Do not download any jdks or detect them. We provide them.
 org.gradle.java.installations.auto-detect=false
diff --git a/d8_r8/test_modules/tests_java_examplesAndroidP/gradle.properties b/d8_r8/test_modules/tests_java_examplesAndroidP/gradle.properties
index 1de43f9..35bcb0b 100644
--- a/d8_r8/test_modules/tests_java_examplesAndroidP/gradle.properties
+++ b/d8_r8/test_modules/tests_java_examplesAndroidP/gradle.properties
@@ -11,6 +11,7 @@
 
 org.gradle.parallel=true
 org.gradle.caching=true
+org.gradle.configuration-cache=true
 
 # Do not download any jdks or detect them. We provide them.
 org.gradle.java.installations.auto-detect=false
diff --git a/d8_r8/test_modules/tests_java_kotlinR8TestResources/gradle.properties b/d8_r8/test_modules/tests_java_kotlinR8TestResources/gradle.properties
index 1de43f9..35bcb0b 100644
--- a/d8_r8/test_modules/tests_java_kotlinR8TestResources/gradle.properties
+++ b/d8_r8/test_modules/tests_java_kotlinR8TestResources/gradle.properties
@@ -11,6 +11,7 @@
 
 org.gradle.parallel=true
 org.gradle.caching=true
+org.gradle.configuration-cache=true
 
 # Do not download any jdks or detect them. We provide them.
 org.gradle.java.installations.auto-detect=false
diff --git a/src/main/java/com/android/tools/r8/MapConsumerToPartitionMapConsumer.java b/src/main/java/com/android/tools/r8/MapConsumerToPartitionMapConsumer.java
index fcd3113..2fd47d9 100644
--- a/src/main/java/com/android/tools/r8/MapConsumerToPartitionMapConsumer.java
+++ b/src/main/java/com/android/tools/r8/MapConsumerToPartitionMapConsumer.java
@@ -10,7 +10,9 @@
 import com.android.tools.r8.naming.ProguardMapMarkerInfo;
 import com.android.tools.r8.retrace.ProguardMapPartitioner;
 import com.android.tools.r8.retrace.internal.ProguardMapProducerInternal;
+import com.android.tools.r8.utils.ListUtils;
 import java.io.IOException;
+import java.util.List;
 
 public class MapConsumerToPartitionMapConsumer implements MapConsumer {
 
@@ -27,7 +29,9 @@
       ProguardMapMarkerInfo makerInfo,
       ClassNameMapper classNameMapper) {
     try {
-      classNameMapper.setPreamble(makerInfo.toPreamble());
+      List<String> newPreamble =
+          ListUtils.joinNewArrayList(makerInfo.toPreamble(), classNameMapper.getPreamble());
+      classNameMapper.setPreamble(newPreamble);
       partitionMapConsumer.acceptMappingPartitionMetadata(
           ProguardMapPartitioner.builder(diagnosticsHandler)
               .setProguardMapProducer(new ProguardMapProducerInternal(classNameMapper))
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index b58107d..d18a979 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -280,14 +280,13 @@
         executorService.submit(
             () -> {
               try {
-                String content = map.getString();
                 builder.setProguardMap(
                     ClassNameMapper.mapperFromString(
-                        content,
+                        map.getString(),
                         options.reporter,
                         options.mappingComposeOptions().allowEmptyMappedRanges,
                         options.testing.enableExperimentalMapFileVersion,
-                        false));
+                        true));
               } catch (IOException | ResourceException e) {
                 throw new CompilationError("Failure to read proguard map file", e, map.getOrigin());
               }
diff --git a/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java b/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
index dede86b..70305e4 100644
--- a/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
+++ b/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
@@ -29,6 +29,7 @@
 import com.android.tools.r8.dex.code.DexInstruction;
 import com.android.tools.r8.dex.code.DexNop;
 import com.android.tools.r8.dex.code.DexSwitchPayload;
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexCode.Try;
 import com.android.tools.r8.graph.DexCode.TryHandler;
@@ -41,6 +42,7 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.lightir.ByteUtils;
 import com.google.common.collect.Lists;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
@@ -200,12 +202,56 @@
     for (int i = 0; i < code.tries.length; i++) {
       Try theTry = code.tries[i];
       TryTargets targets = tryTargets.get(theTry);
-      result[i] = new Try(targets.getStartOffset(), targets.getStartToEndDelta(), -1);
+      int startToEndDelta = targets.getStartToEndDelta();
+      if (startToEndDelta > ByteUtils.MAX_U2) {
+        return rewriteSplitTryOffsets(code);
+      }
+      result[i] = new Try(targets.getStartOffset(), startToEndDelta, -1);
       result[i].handlerIndex = theTry.handlerIndex;
     }
     return result;
   }
 
+  // Note: this algorithm should be aligned with DexBuilder.splitOverflowingRanges.
+  private Try[] rewriteSplitTryOffsets(DexCode code) {
+    // It is unlikely we have 10 overflows (unlikely we have any to begin with).
+    int tentativeCapacity = code.tries.length + 10;
+    List<Try> result = new ArrayList<>(tentativeCapacity);
+    for (Try theTry : code.tries) {
+      TryTargets targets = tryTargets.get(theTry);
+      int startToEndDelta = targets.getStartToEndDelta();
+      int start = targets.getStartOffset();
+      while (startToEndDelta > ByteUtils.MAX_U2) {
+        // Find instruction offset under limit.
+        int maxOffset = start + ByteUtils.MAX_U2;
+        int intermediateEnd = -1;
+        for (int i = code.instructions.length - 1; i >= 0; i--) {
+          DexInstruction instruction = code.instructions[i];
+          // Note that the instructions have been expanded, so getOffset is the rewritten offset.
+          if (instruction.getOffset() <= maxOffset) {
+            intermediateEnd = instruction.getOffset();
+            break;
+          }
+        }
+        if (intermediateEnd <= start) {
+          throw new Unreachable("Unexpected try-catch handler end point: " + intermediateEnd);
+        }
+        int intermediateDelta = intermediateEnd - start;
+        Try splitTry = new Try(start, intermediateDelta, -1);
+        splitTry.handlerIndex = theTry.handlerIndex;
+        result.add(splitTry);
+        start = intermediateEnd;
+        startToEndDelta -= intermediateDelta;
+      }
+      assert startToEndDelta > 0;
+      Try rewrittenTry = new Try(start, startToEndDelta, -1);
+      rewrittenTry.handlerIndex = theTry.handlerIndex;
+      result.add(rewrittenTry);
+    }
+    assert result.size() > code.tries.length;
+    return result.toArray(Try.EMPTY_ARRAY);
+  }
+
   private TryHandler[] rewriteHandlerOffsets() {
     DexCode code = getCode();
     TryHandler[] result = new TryHandler[code.handlers.length];
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index 83ca93c..7b2d440 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -38,6 +38,7 @@
 import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
+import com.android.tools.r8.lightir.ByteUtils;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.ArrayUtils;
 import com.android.tools.r8.utils.DexDebugUtils.PositionInfo;
@@ -912,6 +913,7 @@
       this.instructionCount = instructionCount;
       this.handlerOffset = handlerOffset;
       this.handlerIndex = NO_INDEX;
+      assert ByteUtils.isU2(instructionCount);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index a360308..c40197a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -724,7 +724,10 @@
 
   public boolean needsRegister() {
     assert needsRegister >= 0;
-    assert !hasUsersInfo() || (needsRegister > 0) == internalComputeNeedsRegister();
+    // This has quadratic behavior so don't check for large user sets.
+    assert !hasUsersInfo()
+        || numberOfAllUsers() > 100
+        || (needsRegister > 0) == internalComputeNeedsRegister();
     return needsRegister > 0;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
index 14b9549..89ab593 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
@@ -65,6 +65,7 @@
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.conversion.passes.TrivialGotosCollapser;
 import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.lightir.ByteUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOutputMode;
 import com.google.common.collect.BiMap;
@@ -339,7 +340,7 @@
     }
 
     // Construct try-catch info.
-    TryInfo tryInfo = computeTryInfo();
+    TryInfo tryInfo = computeTryInfo(dexInstructions);
 
     // Return the dex code.
     DexCode code =
@@ -825,11 +826,13 @@
 
   // Helpers for computing the try items and handlers.
 
-  private TryInfo computeTryInfo() {
+  private TryInfo computeTryInfo(List<DexInstruction> dexInstructions) {
     // Canonical map of handlers.
     BiMap<CatchHandlers<BasicBlock>, Integer> canonicalHandlers = HashBiMap.create();
     // Compute the list of try items and their handlers.
     List<TryItem> tryItems = computeTryItems(canonicalHandlers);
+    // Split the try items if they overflow the range limit.
+    tryItems = splitOverflowingRanges(tryItems, dexInstructions);
     // Compute handler sets before dex items which depend on the handler index.
     Try[] tries = getDexTryItems(tryItems, canonicalHandlers);
     TryHandler[] handlers = getDexTryHandlers(canonicalHandlers.inverse());
@@ -916,6 +919,65 @@
     return coalescedTryItems;
   }
 
+  private static int numberOfOverflowingRanges(List<TryItem> tryItems) {
+    int numberOfOverflows = 0;
+    for (TryItem tryItem : tryItems) {
+      int instructionCount = tryItem.end - tryItem.start;
+      while (instructionCount > ByteUtils.MAX_U2) {
+        ++numberOfOverflows;
+        instructionCount -= ByteUtils.MAX_U2;
+      }
+    }
+    return numberOfOverflows;
+  }
+
+  // Note: this algorithm should be aligned with JumboStringRewriter.rewriteSplitTryOffsets.
+  private List<TryItem> splitOverflowingRanges(
+      List<TryItem> tryItems, List<DexInstruction> dexInstructions) {
+    // The fast path is that there will not be any overflows.
+    int overflows = numberOfOverflowingRanges(tryItems);
+    if (overflows == 0) {
+      return tryItems;
+    }
+    // The overflow may not fall on an instruction header, so we add a single entry just in case.
+    // Multiple try items overflowing is unlikely so that just causes reallocating the backing.
+    int tentativeCapacity = tryItems.size() + overflows + 1;
+    ArrayList<TryItem> splitTryItems = new ArrayList<>(tentativeCapacity);
+    for (TryItem tryItem : tryItems) {
+      if (tryItem.end - tryItem.start <= ByteUtils.MAX_U2) {
+        splitTryItems.add(tryItem);
+        continue;
+      }
+      final CatchHandlers<BasicBlock> handlers = tryItem.handlers;
+      final int end = tryItem.end;
+      // The iteration is based on the start offset advancing on each split.
+      int start = tryItem.start;
+      while (end - start > ByteUtils.MAX_U2) {
+        // Find a new end that does not overflow the U2 limit on the delta.
+        // It must be on an instruction offset so scan backwards in the block to find one.
+        int maxOffset = start + ByteUtils.MAX_U2;
+        assert maxOffset < end;
+        int intermediateEnd = -1;
+        for (int i = dexInstructions.size() - 1; i >= 0; i--) {
+          DexInstruction instruction = dexInstructions.get(i);
+          if (instruction.getOffset() <= maxOffset) {
+            intermediateEnd = instruction.getOffset();
+            break;
+          }
+        }
+        if (intermediateEnd <= start) {
+          throw new Unreachable("Unexpected try-catch handler end point: " + intermediateEnd);
+        }
+        splitTryItems.add(new TryItem(handlers, start, intermediateEnd));
+        start = intermediateEnd;
+      }
+      assert start < end;
+      splitTryItems.add(new TryItem(handlers, start, end));
+    }
+    assert splitTryItems.size() >= tryItems.size() + overflows;
+    return splitTryItems;
+  }
+
   private int trimEnd(BasicBlock block) {
     // Trim the range end for non-throwing instructions when end has been computed.
     List<com.android.tools.r8.ir.code.Instruction> instructions = block.getInstructions();
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 38ce1d8..3c8315b 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
@@ -187,7 +187,7 @@
           appView
               .contextIndependentDefinitionForWithResolutionResult(provider.method.holder)
               .toSingleClassWithProgramOverLibrary();
-      if (!clazz.isProgramDefinition()) {
+      if (clazz == null || !clazz.isProgramDefinition()) {
         appView
             .reporter()
             .warning(
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java
index ac54eba..5957cef 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java
@@ -65,6 +65,12 @@
         if (signature.isQualified()) {
           qualifiedMethodMembers.computeIfAbsent(signature, k -> new ArrayList<>(2)).add(entry);
         } else if (methodMembers.put(signature, entry) != null) {
+          // TODO(b/293630963): We are simply not able to handle positions correctly for outlines
+          //  at this point. Remove when we do not call GraphLens.getOriginalMethodSignature when
+          //  constructing positions.
+          if (true) {
+            return this;
+          }
           reporter.error(
               ProguardMapError.duplicateSourceMember(
                   signature.toString(), this.originalName, entry.getPosition()));
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapStringConsumer.java b/src/main/java/com/android/tools/r8/naming/ProguardMapStringConsumer.java
index be3d3ba..71b60d9 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapStringConsumer.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapStringConsumer.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.StringConsumer;
 import com.android.tools.r8.utils.ChainableStringConsumer;
+import com.android.tools.r8.utils.StringUtils;
 
 /***
  * Default implementation of a MapConsumer that wraps around a string consumer for streamed string
@@ -29,6 +30,7 @@
       ClassNameMapper classNameMapper) {
     this.diagnosticsHandler = diagnosticsHandler;
     accept(markerInfo.serializeToString());
+    accept(StringUtils.unixLines(classNameMapper.getPreamble()));
     classNameMapper.write(this);
   }
 
diff --git a/src/test/examplesAndroidO/invokecustom/TestGenerator.java b/src/test/examplesAndroidO/invokecustom/TestGenerator.java
index a4cd89a..4879521 100644
--- a/src/test/examplesAndroidO/invokecustom/TestGenerator.java
+++ b/src/test/examplesAndroidO/invokecustom/TestGenerator.java
@@ -13,6 +13,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
 import org.objectweb.asm.ClassReader;
 import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.ClassWriter;
@@ -26,19 +27,24 @@
 public class TestGenerator {
 
   private final Path classNamePath;
+  private final Path outputClassNamePath;
 
   public static void main(String[] args) throws IOException {
-    assert args.length == 1;
-    TestGenerator testGenerator = new TestGenerator(Paths.get(args[0],
-        TestGenerator.class.getPackage().getName(), InvokeCustom.class.getSimpleName() + ".class"));
+    assert args.length == 2;
+    String fileName = InvokeCustom.class.getSimpleName() + ".class";
+    Path inputFile = Paths.get(args[0], TestGenerator.class.getPackage().getName(), fileName);
+    Path outputFile = Paths.get(args[1], fileName);
+    TestGenerator testGenerator = new TestGenerator(inputFile, outputFile);
     testGenerator.generateTests();
   }
 
-  public TestGenerator(Path classNamePath) {
+  public TestGenerator(Path classNamePath, Path outputClassNamePath) {
     this.classNamePath = classNamePath;
+    this.outputClassNamePath = outputClassNamePath;
   }
 
   private void generateTests() throws IOException {
+    Files.createDirectories(outputClassNamePath.getParent());
     try (InputStream input = Files.newInputStream(classNamePath)) {
       ClassReader cr = new ClassReader(input);
       ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
@@ -63,7 +69,8 @@
               super.visitEnd();
             }
           }, 0);
-      try (OutputStream output = Files.newOutputStream(classNamePath)) {
+      try (OutputStream output =
+          Files.newOutputStream(outputClassNamePath, StandardOpenOption.CREATE)) {
         output.write(cw.toByteArray());
       }
     }
diff --git a/src/test/examplesAndroidO/invokecustom2/TestGenerator.java b/src/test/examplesAndroidO/invokecustom2/TestGenerator.java
index 377084e..2a4eba4 100644
--- a/src/test/examplesAndroidO/invokecustom2/TestGenerator.java
+++ b/src/test/examplesAndroidO/invokecustom2/TestGenerator.java
@@ -14,6 +14,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
 import org.objectweb.asm.ClassReader;
 import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.ClassWriter;
@@ -25,19 +26,24 @@
 public class TestGenerator {
 
   private final Path classNamePath;
+  private final Path outputClassNamePath;
 
   public static void main(String[] args) throws IOException {
-    assert args.length == 1;
-    TestGenerator testGenerator = new TestGenerator(Paths.get(args[0],
-        TestGenerator.class.getPackage().getName(), InvokeCustom.class.getSimpleName() + ".class"));
+    assert args.length == 2;
+    String fileName = invokecustom.InvokeCustom.class.getSimpleName() + ".class";
+    Path inputFile = Paths.get(args[0], TestGenerator.class.getPackage().getName(), fileName);
+    Path outputFile = Paths.get(args[1], fileName);
+    TestGenerator testGenerator = new TestGenerator(inputFile, outputFile);
     testGenerator.generateTests();
   }
 
-  public TestGenerator(Path classNamePath) {
+  public TestGenerator(Path classNamePath, Path outputClassNamePath) {
     this.classNamePath = classNamePath;
+    this.outputClassNamePath = outputClassNamePath;
   }
 
   private void generateTests() throws IOException {
+    Files.createDirectories(outputClassNamePath.getParent());
     try (InputStream input = Files.newInputStream(classNamePath)) {
       ClassReader cr = new ClassReader(input);
       ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
@@ -58,7 +64,8 @@
               super.visitEnd();
             }
           }, 0);
-      try (OutputStream output = Files.newOutputStream(classNamePath)) {
+      try (OutputStream output =
+          Files.newOutputStream(outputClassNamePath, StandardOpenOption.CREATE)) {
         output.write(cw.toByteArray());
       }
     }
diff --git a/src/test/examplesAndroidO/stringconcat/TestGenerator.java b/src/test/examplesAndroidO/stringconcat/TestGenerator.java
index 72bdfbf..4755c4d 100644
--- a/src/test/examplesAndroidO/stringconcat/TestGenerator.java
+++ b/src/test/examplesAndroidO/stringconcat/TestGenerator.java
@@ -12,6 +12,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
 import java.util.ArrayList;
 import java.util.List;
 import org.objectweb.asm.ClassReader;
@@ -40,13 +41,16 @@
       false);
 
   public static void main(String[] args) throws IOException {
-    assert args.length == 1;
-    generateTests(Paths.get(args[0],
-        TestGenerator.class.getPackage().getName(),
-        StringConcat.class.getSimpleName() + ".class"));
+    assert args.length == 2;
+    String fileName = StringConcat.class.getSimpleName() + ".class";
+    Path inputFile = Paths.get(args[0], TestGenerator.class.getPackage().getName(), fileName);
+    Path outputFile = Paths.get(args[1], fileName);
+    generateTests(inputFile, outputFile);
   }
 
-  private static void generateTests(Path classNamePath) throws IOException {
+  private static void generateTests(Path classNamePath, Path outputClassNamePath)
+      throws IOException {
+    Files.createDirectories(outputClassNamePath.getParent());
     try (InputStream input = Files.newInputStream(classNamePath)) {
       ClassReader cr = new ClassReader(input);
       ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
@@ -221,7 +225,8 @@
               };
             }
           }, 0);
-      try (OutputStream output = Files.newOutputStream(classNamePath)) {
+      try (OutputStream output =
+          Files.newOutputStream(outputClassNamePath, StandardOpenOption.CREATE)) {
         output.write(cw.toByteArray());
       }
     }
diff --git a/src/test/examplesAndroidOLegacy/README.txt b/src/test/examplesAndroidOLegacy/README.txt
new file mode 100644
index 0000000..5a90476
--- /dev/null
+++ b/src/test/examplesAndroidOLegacy/README.txt
@@ -0,0 +1,13 @@
+// Copyright (c) 2023, 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.
+
+The examplesAndroidOLegacy source set is no longer included in build.gradle
+after transitioning to gradle v8. If we need to update the project, one should
+generate new class files and place in third_party/examplesAndroidOLegacy or
+make a direct dependency on the generated jars.
+
+See this commit where we check in the class files to get a sense of how to
+compile the source sets:
+https://r8-review.git.corp.google.com/c/r8/+/83161
+
diff --git a/src/test/examplesAndroidO/lambdadesugaring/legacy/Legacy.java b/src/test/examplesAndroidOLegacy/lambdadesugaring/legacy/Legacy.java
similarity index 100%
rename from src/test/examplesAndroidO/lambdadesugaring/legacy/Legacy.java
rename to src/test/examplesAndroidOLegacy/lambdadesugaring/legacy/Legacy.java
diff --git a/src/test/examplesAndroidP/invokecustom/TestGenerator.java b/src/test/examplesAndroidP/invokecustom/TestGenerator.java
index b08441c..ef5f8de 100644
--- a/src/test/examplesAndroidP/invokecustom/TestGenerator.java
+++ b/src/test/examplesAndroidP/invokecustom/TestGenerator.java
@@ -13,6 +13,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
 import org.objectweb.asm.ClassReader;
 import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.ClassWriter;
@@ -24,19 +25,24 @@
 public class TestGenerator {
 
   private final Path classNamePath;
+  private final Path outputClassNamePath;
 
   public static void main(String[] args) throws IOException {
-    assert args.length == 1;
-    TestGenerator testGenerator = new TestGenerator(Paths.get(args[0],
-        TestGenerator.class.getPackage().getName(), InvokeCustom.class.getSimpleName() + ".class"));
+    assert args.length == 2;
+    String fileName = InvokeCustom.class.getSimpleName() + ".class";
+    Path inputFile = Paths.get(args[0], TestGenerator.class.getPackage().getName(), fileName);
+    Path outputFile = Paths.get(args[1], fileName);
+    TestGenerator testGenerator = new TestGenerator(inputFile, outputFile);
     testGenerator.generateTests();
   }
 
-  public TestGenerator(Path classNamePath) {
+  public TestGenerator(Path classNamePath, Path outputClassNamePath) {
     this.classNamePath = classNamePath;
+    this.outputClassNamePath = outputClassNamePath;
   }
 
   private void generateTests() throws IOException {
+    Files.createDirectories(outputClassNamePath.getParent());
     try (InputStream inputStream = Files.newInputStream(classNamePath)) {
       ClassReader cr = new ClassReader(inputStream);
       ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
@@ -52,7 +58,8 @@
               super.visitEnd();
             }
           }, 0);
-      try (OutputStream output = Files.newOutputStream(classNamePath)) {
+      try (OutputStream output =
+          Files.newOutputStream(outputClassNamePath, StandardOpenOption.CREATE)) {
         output.write(cw.toByteArray());
       }
     }
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index 646e805..d3a155f 100644
--- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -27,13 +27,13 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
-import java.nio.file.DirectoryStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -118,8 +118,10 @@
     private Path makeRelative(Path testJarFile, Path classFile) {
       Path regularParent =
           testJarFile.getParent().resolve(Paths.get("classes"));
-      Path legacyParent = regularParent.resolve(Paths.get("..",
-          regularParent.getFileName().toString() + "Legacy", "classes"));
+      Path legacyParent =
+          regularParent.resolve(
+              Paths.get(
+                  ToolHelper.THIRD_PARTY_DIR, regularParent.getFileName().toString() + "Legacy"));
 
       if (classFile.startsWith(regularParent)) {
         return regularParent.relativize(classFile);
@@ -129,13 +131,22 @@
     }
 
     private List<String> collectClassFiles(Path testJarFile) {
-      List<String> result = new ArrayList<>();
+      Map<String, String> result = new HashMap<>();
       // Collect Java 8 classes.
-      collectClassFiles(getClassesRoot(testJarFile), result, false);
+      visitFiles(
+          getClassesRoot(testJarFile),
+          path -> result.put(path.toFile().getName(), path.toString()));
+      // Collect generated classes, overwrite non-generated files.
+      visitFiles(
+          getGeneratedRoot(testJarFile),
+          path -> result.put(path.toFile().getName(), path.toString()));
       // Collect legacy classes.
-      collectClassFiles(getLegacyClassesRoot(testJarFile), result, true);
-      Collections.sort(result);
-      return result;
+      visitFiles(
+          getLegacyClassesRoot(testJarFile, packageName),
+          path -> result.put(path.toFile().getName(), path.toString()));
+      List<String> files = new ArrayList<>(result.values());
+      Collections.sort(files);
+      return files;
     }
 
     Path getClassesRoot(Path testJarFile) {
@@ -143,30 +154,10 @@
       return parent.resolve(Paths.get("classes", packageName));
     }
 
-    Path getLegacyClassesRoot(Path testJarFile) {
-      Path parent = testJarFile.getParent();
-      Path legacyPath = Paths.get("..",
-          parent.getFileName().toString() + "Legacy", "classes", packageName);
-      return parent.resolve(legacyPath);
-    }
-
-    private void collectClassFiles(Path dir, List<String> result, boolean takeLegacy) {
-      if (Files.exists(dir)) {
-        try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
-          for (Path entry: stream) {
-            if (Files.isDirectory(entry)) {
-              if (entry.getFileName().toString().equals("legacy") && !takeLegacy) {
-                return;
-              }
-              collectClassFiles(entry, result, takeLegacy);
-            } else {
-              result.add(entry.toString());
-            }
-          }
-        } catch (IOException x) {
-          throw new AssertionError(x);
-        }
-      }
+    Path getGeneratedRoot(Path testJarFile) {
+      String sourceSet = testJarFile.getParent().toFile().getName();
+      Path parent = testJarFile.getParent().getParent().getParent();
+      return parent.resolve(Paths.get("generated", sourceSet, packageName));
     }
 
     AndroidApp compileClassFilesInIntermediate(
diff --git a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
index 78bbcf3..a803a91 100644
--- a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
@@ -38,7 +38,7 @@
     @Override
     void addClasspathReference(Path testJarFile, D8Command.Builder builder) {
       addClasspathPath(getClassesRoot(testJarFile), builder);
-      addClasspathPath(getLegacyClassesRoot(testJarFile), builder);
+      addClasspathPath(getLegacyClassesRoot(testJarFile, packageName), builder);
     }
 
     private void addClasspathPath(Path location, D8Command.Builder builder) {
diff --git a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
index b4cc992..b8e9066 100644
--- a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
@@ -41,13 +41,10 @@
           ToolHelper.getAndroidJar(
               androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion.getLevel()));
       builder.addProgramFiles(inputFile);
+      visitFiles(getLegacyClassesRoot(inputFile, packageName), builder::addProgramFiles);
       ToolHelper.runD8(builder, this::combinedOptionConsumer);
     }
 
-    D8TestRunner withIntermediate(boolean intermediate) {
-      return withBuilderTransformation(builder -> builder.setIntermediate(intermediate));
-    }
-
     @Override
     D8TestRunner self() {
       return this;
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index fa1d579..9acb6cf 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -129,7 +129,7 @@
         .withBuilderTransformation(
             b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
         .withDexCheck(inspector -> checkLambdaCount(inspector, 10, "lambdadesugaring"))
-        .run();
+        .run(Paths.get(ToolHelper.THIRD_PARTY_DIR, "examplesAndroidOLegacy"));
 
     test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
         .withOptionConsumer(o -> o.testing.enableLir())
@@ -137,7 +137,7 @@
         .withBuilderTransformation(
             b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
         .withDexCheck(inspector -> checkLambdaCount(inspector, 1, "lambdadesugaring"))
-        .run();
+        .run(Paths.get(ToolHelper.THIRD_PARTY_DIR, "examplesAndroidOLegacy"));
   }
 
   @Test
@@ -157,7 +157,7 @@
                         "}"),
                     Origin.unknown()))
         .withDexCheck(inspector -> checkTestMultipleInterfacesCheckCastCount(inspector, 0))
-        .run();
+        .run(Paths.get(ToolHelper.THIRD_PARTY_DIR, "examplesAndroidOLegacy"));
   }
 
   @Test
@@ -169,7 +169,7 @@
         .withBuilderTransformation(
             b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
         .withDexCheck(inspector -> checkLambdaCount(inspector, 10, "lambdadesugaring"))
-        .run();
+        .run(Paths.get(ToolHelper.THIRD_PARTY_DIR, "examplesAndroidOLegacy"));
 
     test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
         .withMinApiLevel(AndroidApiLevel.N)
@@ -177,7 +177,7 @@
         .withBuilderTransformation(
             b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
         .withDexCheck(inspector -> checkLambdaCount(inspector, 1, "lambdadesugaring"))
-        .run();
+        .run(Paths.get(ToolHelper.THIRD_PARTY_DIR, "examplesAndroidOLegacy"));
   }
 
   @Override
@@ -326,9 +326,9 @@
       for (Consumer<R8Command.Builder> transformation : builderTransformations) {
         transformation.accept(builder);
       }
-
       builder.addLibraryFiles(ToolHelper.getAndroidJar(
           androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion.getLevel()));
+      visitFiles(getLegacyClassesRoot(inputFile, packageName), builder::addProgramFiles);
       R8Command command = builder.addProgramFiles(inputFile).build();
       ToolHelper.runR8(command, this::combinedOptionConsumer);
     }
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index 59e0d13..276f815 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -12,6 +12,7 @@
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeFalse;
 
+import com.android.tools.r8.TestRuntime.CfRuntime;
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.origin.Origin;
@@ -37,6 +38,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
@@ -168,7 +171,7 @@
       return Paths.get(EXAMPLE_DIR, packageName + JAR_EXTENSION);
     }
 
-    void run() throws Throwable {
+    void run(Path... additionalJavaClasspaths) throws Throwable {
       if (minSdkErrorExpected(testName)) {
         thrown.expect(CompilationFailedException.class);
       }
@@ -190,7 +193,11 @@
         }
       }
 
-      execute(testName, qualifiedMainClass, new Path[]{inputFile}, new Path[]{out}, args);
+      List<Path> paths = new ArrayList<>();
+      paths.add(inputFile);
+      paths.addAll(Arrays.asList(additionalJavaClasspaths));
+
+      execute(testName, qualifiedMainClass, paths.toArray(new Path[0]), new Path[] {out}, args);
     }
 
     abstract C withMinApiLevel(AndroidApiLevel minApiLevel);
@@ -368,7 +375,7 @@
     test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
         .withMinApiLevel(ToolHelper.getMinApiLevelForDexVmNoHigherThan(AndroidApiLevel.K))
         .withKeepAll()
-        .run();
+        .run(Paths.get(ToolHelper.THIRD_PARTY_DIR, "examplesAndroidOLegacy"));
   }
 
   @Test
@@ -599,6 +606,7 @@
         .addLibraryFiles(ToolHelper.getAndroidJar(minApi))
         .setIntermediate(true)
         .addProgramFiles(input);
+    visitFiles(getLegacyClassesRoot(input, packageName), command::addProgramFiles);
     ToolHelper.runD8(command, option -> {
       option.interfaceMethodDesugaring = OffOrAuto.Auto;
     });
@@ -645,7 +653,9 @@
         javaArgs.add(0, qualifiedMainClass);
         ToolHelper.ProcessResult javaResult =
             ToolHelper.runJava(
-                ImmutableList.copyOf(jars), javaArgs.toArray(StringUtils.EMPTY_ARRAY));
+                CfRuntime.getCheckedInJdk11(),
+                ImmutableList.copyOf(jars),
+                javaArgs.toArray(StringUtils.EMPTY_ARRAY));
         assertEquals("JVM run failed", javaResult.exitCode, 0);
         assertTrue(
             "JVM output does not match art output.\n\tjvm: "
@@ -671,4 +681,25 @@
     }
   }
 
+  protected Path getLegacyClassesRoot(Path testJarFile, String packageName) {
+    Path parent = testJarFile.getParent();
+    return Paths.get(
+        ToolHelper.THIRD_PARTY_DIR, parent.getFileName().toString() + "Legacy", packageName);
+  }
+
+  public void visitFiles(Path dir, Consumer<Path> consumer) {
+    if (Files.exists(dir)) {
+      try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
+        for (Path entry : stream) {
+          if (Files.isDirectory(entry)) {
+            visitFiles(entry, consumer);
+          } else {
+            consumer.accept(entry);
+          }
+        }
+      } catch (IOException x) {
+        throw new AssertionError(x);
+      }
+    }
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java b/src/test/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java
index 6130b2a..c1dde31 100644
--- a/src/test/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java
+++ b/src/test/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java
@@ -105,17 +105,17 @@
 
   public static class AndroidTestResourceBuilder {
     private String manifest;
-    private Map<String, String> stringValues = new TreeMap<>();
-    private Map<String, byte[]> drawables = new TreeMap<>();
-    private List<Class> classesToRemap = new ArrayList();
+    private final Map<String, String> stringValues = new TreeMap<>();
+    private final Map<String, byte[]> drawables = new TreeMap<>();
+    private final List<Class<?>> classesToRemap = new ArrayList<>();
 
     // Create the android resources from the passed in R classes
     // All values will be generated based on the fields in the class.
     // This takes the actual inner classes (e.g., R$String)
     // These R classes will be used to rewrite the namespace and class names on the aapt2
     // generated names.
-    AndroidTestResourceBuilder addRClassInitializeWithDefaultValues(Class... rClasses) {
-      for (Class rClass : rClasses) {
+    AndroidTestResourceBuilder addRClassInitializeWithDefaultValues(Class<?>... rClasses) {
+      for (Class<?> rClass : rClasses) {
         classesToRemap.add(rClass);
         RClassType rClassType = RClassType.fromClass(rClass);
         for (Field declaredField : rClass.getDeclaredFields()) {
diff --git a/src/test/java/com/android/tools/r8/androidresources/ResourceShrinkerIntegrationTest.java b/src/test/java/com/android/tools/r8/androidresources/ResourceShrinkerIntegrationTest.java
index dbb464b..e234c69 100644
--- a/src/test/java/com/android/tools/r8/androidresources/ResourceShrinkerIntegrationTest.java
+++ b/src/test/java/com/android/tools/r8/androidresources/ResourceShrinkerIntegrationTest.java
@@ -28,7 +28,7 @@
   }
 
   @Test
-  public void testResourceShrinkerClassAvailable() throws Exception {
+  public void testResourceShrinkerClassAvailable() {
     if (ToolHelper.isNewGradleSetup()) {
       assertTrue(
           ResourceTracing.getImpl().getClass() != ResourceTracing.NoOpResourceTracingImpl.class);
diff --git a/src/test/java/com/android/tools/r8/dex/TryCatchRangeOverflowTest.java b/src/test/java/com/android/tools/r8/dex/TryCatchRangeOverflowTest.java
new file mode 100644
index 0000000..e9caf80
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dex/TryCatchRangeOverflowTest.java
@@ -0,0 +1,194 @@
+// Copyright (c) 2023, 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.dex;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.D8TestBuilder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexCode.Try;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.code.Add;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.Arrays;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+// Regression test for b/297320921
+@RunWith(Parameterized.class)
+public class TryCatchRangeOverflowTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDefaultDexRuntime().withMinimumApiLevel().build();
+  }
+
+  public TryCatchRangeOverflowTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  // Each add/2addr instruction has size 1, so we add have as many instruction minus some padding
+  // to make room for the instructions before and after but still in the same block.
+  // Notice that this value may change if the generated code by the compiler changes. It must then
+  // be updated to the precise limit again so that the test for jumbo-string exactly hits the
+  // crossing point.
+  private final int PADDING = 33;
+  private final int UNSPLIT_LIMIT = 0xFFFF - PADDING;
+  private final int SPLIT_2_LIMIT = 0xFFFF * 2 - PADDING;
+
+  @Test
+  public void testWithinU2() throws Exception {
+    parameters.assumeDexRuntime();
+    int addCount = UNSPLIT_LIMIT;
+    compile(addCount)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("" + addCount)
+        .inspect(inspector -> checkTryCatchHandlers(1, inspector));
+  }
+
+  @Test
+  public void testJumboExceedsU2() throws Exception {
+    parameters.assumeDexRuntime();
+    int addCount = UNSPLIT_LIMIT;
+    compile(addCount)
+        .addOptionsModification(o -> o.testing.forceJumboStringProcessing = true)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("" + addCount)
+        .inspect(inspector -> checkTryCatchHandlers(2, inspector));
+  }
+
+  @Test
+  public void testExceedsU2() throws Exception {
+    parameters.assumeDexRuntime();
+    // Test with a few values above the limit.
+    for (int addCount : Arrays.asList(UNSPLIT_LIMIT + 1, UNSPLIT_LIMIT + 2, UNSPLIT_LIMIT + 100)) {
+      compile(addCount)
+          .run(parameters.getRuntime(), TestClass.class)
+          .assertSuccessWithOutputLines("" + addCount)
+          .inspect(inspector -> checkTryCatchHandlers(2, inspector));
+    }
+  }
+
+  @Test
+  public void testWithinU2x2() throws Exception {
+    parameters.assumeDexRuntime();
+    int addCount = SPLIT_2_LIMIT;
+    compile(addCount)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("" + addCount)
+        .inspect(inspector -> checkTryCatchHandlers(2, inspector));
+  }
+
+  @Test
+  public void testJumboExceedsU2x2() throws Exception {
+    parameters.assumeDexRuntime();
+    int addCount = SPLIT_2_LIMIT;
+    compile(addCount)
+        .addOptionsModification(o -> o.testing.forceJumboStringProcessing = true)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("" + addCount)
+        .inspect(inspector -> checkTryCatchHandlers(3, inspector));
+  }
+
+  @Test
+  public void testExceedsU2x2() throws Exception {
+    parameters.assumeDexRuntime();
+    int addCount = SPLIT_2_LIMIT + 1;
+    compile(addCount)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("" + addCount)
+        .inspect(inspector -> checkTryCatchHandlers(3, inspector));
+  }
+
+  private D8TestBuilder compile(int addCount) throws Exception {
+    return testForD8(Backend.DEX)
+        .addProgramClasses(TestClass.class)
+        .addOptionsModification(
+            o ->
+                o.testing.irModifier =
+                    (code, appView) -> amendCodeWithAddInstructions(addCount, code))
+        .setMinApi(parameters);
+  }
+
+  private static void amendCodeWithAddInstructions(int addCount, IRCode code) {
+    if (!code.context().getReference().qualifiedName().endsWith("main")) {
+      return;
+    }
+    InstructionListIterator it = code.instructionListIterator();
+    while (it.hasNext()) {
+      Instruction instruction = it.next();
+      if (instruction.isAdd()) {
+        TypeElement outType = instruction.getOutType();
+        DebugLocalInfo localInfo = instruction.getLocalInfo();
+        // Create the last value which will replace the users of the original value in the
+        // continuations.
+        Value newLastValue = code.createValue(outType, localInfo);
+        instruction.outValue().replaceUsers(newLastValue);
+
+        Add add = instruction.asAdd();
+        NumericType numericType = add.getNumericType();
+        assert add.rightValue().isConstNumber();
+        for (int i = 1; i < addCount; i++) {
+          Value dest = i == addCount - 1 ? newLastValue : code.createValue(outType, localInfo);
+          Add newAdd = Add.create(numericType, dest, add.outValue(), add.rightValue());
+          add.outValue().addDebugLocalEnd(newAdd);
+          newAdd.setPosition(add.getPosition());
+          it.add(newAdd);
+          add = newAdd;
+        }
+        return;
+      }
+    }
+    fail("Expected to find an Add instruction.");
+  }
+
+  private static void checkTryCatchHandlers(int tryCount, CodeInspector inspector)
+      throws NoSuchMethodException {
+
+    MethodSubject main = inspector.method(TestClass.class.getMethod("main", String[].class));
+    Try[] tries = main.getMethod().getCode().asDexCode().tries;
+    assertEquals(Arrays.toString(tries), tryCount, tries.length);
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      int i = 0;
+      try {
+        String str;
+        int len = args.length;
+        if (len == 0) {
+          str = "";
+        } else if (len == 1 /* Using a constant 1 here causes the add to be an add/2addr */) {
+          str = "Strings might become jumbos";
+        } else if (len % 2 == 0) {
+          str = "We need 4";
+        } else {
+          str = "to ensure overflow.";
+        }
+        i = str.length();
+        ++i; // repeated count number of times.
+        i += args[0].length();
+      } catch (Throwable e) {
+        System.out.println(i);
+        return;
+      }
+      System.out.println("unexpected i " + i);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeUnknownJsonD8Test.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeUnknownJsonD8Test.java
new file mode 100644
index 0000000..032f5da
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeUnknownJsonD8Test.java
@@ -0,0 +1,107 @@
+// Copyright (c) 2023, 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.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.D8TestBuilder;
+import com.android.tools.r8.PartitionMapConsumer;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.retrace.MappingPartition;
+import com.android.tools.r8.retrace.MappingPartitionMetadata;
+import com.android.tools.r8.retrace.PartitionMappingSupplier;
+import com.android.tools.r8.retrace.PartitionedToProguardMappingConverter;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.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.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+/** This is a regression test for b/297927590. */
+@RunWith(Parameterized.class)
+public class ComposeUnknownJsonD8Test extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  private static final String CUSTOM_DATA = "# {'id':'custom_info','base64/deflate':''}";
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDefaultDexRuntime().withMinimumApiLevel().build();
+  }
+
+  private void testD8(ThrowableConsumer<D8TestBuilder> testBuilder) throws Exception {
+    Path inputMap = temp.newFolder().toPath().resolve("input.map");
+    FileUtils.writeTextFile(
+        inputMap,
+        CUSTOM_DATA,
+        "# {'id':'com.android.tools.r8.mapping','version':'2.2'}",
+        "X -> X:");
+    testForD8(parameters.getBackend())
+        .addProgramClasses(A.class)
+        .setMinApi(parameters)
+        .addOptionsModification(
+            options ->
+                assertTrue(options.mappingComposeOptions().enableExperimentalMappingComposition))
+        .apply(b -> b.getBuilder().setProguardInputMapFile(inputMap))
+        .apply(testBuilder)
+        .allowStdoutMessages()
+        .collectStdout()
+        .compile()
+        .assertStdoutThatMatches(containsString("Info: Could not find a handler for custom_info"));
+  }
+
+  @Test
+  public void testD8CompositionMappingFile() throws Exception {
+    StringBuilder mappingComposed = new StringBuilder();
+    testD8(
+        builder ->
+            builder
+                .getBuilder()
+                .setProguardMapConsumer((string, handler) -> mappingComposed.append(string)));
+    assertThat(doubleToSingleQuote(mappingComposed.toString()), containsString(CUSTOM_DATA));
+  }
+
+  @Test
+  public void testD8CompositionPartition() throws Exception {
+    Box<byte[]> metadataBytes = new Box<>();
+    testD8(
+        builder ->
+            builder
+                .getBuilder()
+                .setPartitionMapConsumer(
+                    new PartitionMapConsumer() {
+                      @Override
+                      public void acceptMappingPartition(MappingPartition mappingPartition) {}
+
+                      @Override
+                      public void acceptMappingPartitionMetadata(
+                          MappingPartitionMetadata mappingPartitionMetadata) {
+                        metadataBytes.set(mappingPartitionMetadata.getBytes());
+                      }
+                    }));
+    StringBuilder mappingComposed = new StringBuilder();
+    PartitionedToProguardMappingConverter.builder()
+        .setPartitionMappingSupplier(
+            PartitionMappingSupplier.builder()
+                .setMetadata(metadataBytes.get())
+                .setMappingPartitionFromKeySupplier(key -> new byte[0])
+                .build())
+        .setConsumer((string, handler) -> mappingComposed.append(string))
+        .build()
+        .run();
+    assertThat(doubleToSingleQuote(mappingComposed.toString()), containsString(CUSTOM_DATA));
+  }
+
+  public static class A {}
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeWithMissingDebugInfoTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeWithMissingDebugInfoTest.java
new file mode 100644
index 0000000..950abdc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeWithMissingDebugInfoTest.java
@@ -0,0 +1,98 @@
+// Copyright (c) 2023, 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 junit.framework.TestCase.assertEquals;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.ProguardMappingSupplier;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
+import com.android.tools.r8.utils.FileUtils;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+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;
+
+/** This is a regression test for b/297970886 */
+@RunWith(Parameterized.class)
+public class ComposeWithMissingDebugInfoTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimesAndAllApiLevels().build();
+  }
+
+  @Test
+  public void testD8WithComposition() throws Exception {
+    Path inputMap = temp.newFolder().toPath().resolve("input.map");
+    FileUtils.writeTextFile(
+        inputMap,
+        "# {'id':'com.android.tools.r8.mapping','version':'2.2'}",
+        typeName(Main.class) + " -> " + typeName(Main.class) + ":",
+        "  12:12:int foo():12:12 -> a",
+        "  112:112:int foo():112:112 -> a");
+    StringBuilder mappingComposed = new StringBuilder();
+    testForD8(parameters.getBackend())
+        .addProgramClassFileData(
+            transformer(Main.class)
+                .setPredictiveLineNumbering(MethodPredicate.onName("a"), 12)
+                .transform())
+        .release()
+        .setMinApi(parameters)
+        .apply(
+            b ->
+                b.getBuilder()
+                    .setProguardInputMapFile(inputMap)
+                    .setProguardMapConsumer((string, handler) -> mappingComposed.append(string)))
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("42");
+    Set<String> foundMethods =
+        ProguardMappingSupplier.builder()
+            .setProguardMapProducer(ProguardMapProducer.fromString(mappingComposed.toString()))
+            .setLoadAllDefinitions(true)
+            .build()
+            .createRetracer(new DiagnosticsHandler() {})
+            .retraceMethod(
+                Reference.method(
+                    Reference.classFromTypeName(typeName(Main.class)),
+                    "a",
+                    new ArrayList<>(),
+                    Reference.typeFromTypeName("int")))
+            .stream()
+            .map(method -> method.getRetracedMethod().getMethodName())
+            .collect(Collectors.toSet());
+    Set<String> expectedMethods = new HashSet<>();
+    expectedMethods.add("a");
+    // TODO(b/297970886): We should observe 'foo' and not 'a'.
+    assertEquals(expectedMethods, foundMethods);
+  }
+
+  public static class Main {
+
+    private int val;
+
+    public static void main(String[] args) {
+      System.out.println(new Main().a());
+    }
+
+    private int a() {
+      val = 42;
+      return val;
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/SeedMapperTests.java b/src/test/java/com/android/tools/r8/naming/SeedMapperTests.java
index bb88cdb..31e8267 100644
--- a/src/test/java/com/android/tools/r8/naming/SeedMapperTests.java
+++ b/src/test/java/com/android/tools/r8/naming/SeedMapperTests.java
@@ -88,7 +88,10 @@
     Reporter reporter = new Reporter(testDiagnosticMessages);
     try {
       SeedMapper.seedMapperFromFile(reporter, applyMappingFile);
-      fail("Should have thrown an error");
+      // TODO(b/293630963): Re-enable check.
+      if (false) {
+        fail("Should have thrown an error");
+      }
     } catch (RuntimeException e) {
       assertEquals(1, testDiagnosticMessages.getErrors().size());
       Diagnostic diagnostic = testDiagnosticMessages.getErrors().get(0);
diff --git a/third_party/dependencies.tar.gz.sha1 b/third_party/dependencies.tar.gz.sha1
index fc52c9a..1c1ad7b 100644
--- a/third_party/dependencies.tar.gz.sha1
+++ b/third_party/dependencies.tar.gz.sha1
@@ -1 +1 @@
-0ed74e4bea29a056eb1870c244e6b0cf46aaac70
\ No newline at end of file
+cfbdceb60320ae102577b1cc274ce0af4432722a
\ No newline at end of file
diff --git a/third_party/dependencies_new.tar.gz.sha1 b/third_party/dependencies_new.tar.gz.sha1
index a678343..89aecc2 100644
--- a/third_party/dependencies_new.tar.gz.sha1
+++ b/third_party/dependencies_new.tar.gz.sha1
@@ -1 +1 @@
-ce954d74e0bb5c3b2f116c327558e71819e38a48
\ No newline at end of file
+80d787df74a6a07f673def5ebc38dff24515fe1c
\ No newline at end of file
diff --git a/third_party/examplesAndroidOLegacy.tar.gz.sha1 b/third_party/examplesAndroidOLegacy.tar.gz.sha1
new file mode 100644
index 0000000..6a750e4
--- /dev/null
+++ b/third_party/examplesAndroidOLegacy.tar.gz.sha1
@@ -0,0 +1 @@
+8b3f21b30f966d72d47925f2156e4912d9e68d97
\ No newline at end of file
diff --git a/tools/create_local_maven_with_dependencies.py b/tools/create_local_maven_with_dependencies.py
index 138af36..1c345b7 100755
--- a/tools/create_local_maven_with_dependencies.py
+++ b/tools/create_local_maven_with_dependencies.py
@@ -69,7 +69,7 @@
   'org.jetbrains.kotlin:kotlin-script-runtime:1.8.10',
   'org.jetbrains.kotlin:kotlin-tooling-core:1.8.10',
   'net.ltgt.errorprone:net.ltgt.errorprone.gradle.plugin:3.0.1',
-
+  'com.google.errorprone:javac:9+181-r4173-1',
   # Resource shrinker
   'com.android.tools.build:aapt2-proto:{version}'.format(version = AAPT2_PROTO_VERSION),
   'com.android.tools.layoutlib:layoutlib-api:{version}'.format(version = STUDIO_SDK_VERSION),
diff --git a/tools/r8_release.py b/tools/r8_release.py
index b991ad2..c5c5f51 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -7,6 +7,7 @@
 import datetime
 import os.path
 import re
+import stat
 import subprocess
 import sys
 import urllib.request
@@ -27,6 +28,23 @@
 
 GITHUB_DESUGAR_JDK_LIBS = 'https://github.com/google/desugar_jdk_libs'
 
+def install_gerrit_change_id_hook(checkout_dir):
+  with utils.ChangedWorkingDirectory(checkout_dir):
+    # Fancy way of getting the string ".git".
+    git_dir = subprocess.check_output(
+        ['git', 'rev-parse', '--git-dir']).decode('utf-8').strip()
+    commit_msg_hooks = '%s/hooks/commit-msg' % git_dir
+    if not os.path.exists(os.path.dirname(commit_msg_hooks)):
+      os.mkdir(os.path.dirname(commit_msg_hooks))
+    # Install commit hook to generate Gerrit 'Change-Id:'.
+    urllib.request.urlretrieve(
+        'https://gerrit-review.googlesource.com/tools/hooks/commit-msg',
+        commit_msg_hooks)
+    st = os.stat(commit_msg_hooks)
+    os.chmod(
+        commit_msg_hooks,
+        st.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
+
 def checkout_r8(temp, branch):
   subprocess.check_call(['git', 'clone', utils.REPO_SOURCE, temp])
   with utils.ChangedWorkingDirectory(temp):
@@ -36,6 +54,7 @@
       '--upstream',
       'origin/%s' % branch,
       'dev-release'])
+  install_gerrit_change_id_hook(temp)
   return temp
 
 
@@ -70,7 +89,7 @@
 
         # Verify that the merge point from main is not empty.
         merge_diff_output = subprocess.check_output([
-          'git', 'diff', 'HEAD..%s' % commithash]).decode('utf-8')
+            'git', 'diff', 'HEAD..%s' % commithash]).decode('utf-8')
         other_diff = version_change_diff(
             merge_diff_output, old_version, "main")
         if not other_diff:
@@ -78,14 +97,16 @@
             'is the same as exiting release (%s).' % old_version)
           sys.exit(1)
 
+        subprocess.check_call([
+            'git', 'cl', 'new-branch', 'release-%s' % version])
+
         if args.dev_pre_cherry_pick:
           for pre_commit in args.dev_pre_cherry_pick:
             subprocess.check_call([
                 'git', 'cherry-pick', '--no-edit', pre_commit])
 
         # Merge the desired commit from main on to the branch.
-        subprocess.check_call([
-          'git', 'merge', '--no-ff', '--no-edit', commithash])
+        subprocess.check_call(['git', 'merge', '--no-ff', '--no-edit', commithash])
 
         # Rewrite the version, commit and validate.
         sed(old_version, version, R8_VERSION_FILE)
@@ -98,19 +119,15 @@
 
         validate_version_change_diff(version_diff_output, "main", version)
 
-        # Double check that we want to push the release.
-        if not args.dry_run:
-          answer = input('Publish dev release version %s [y/N]:' % version)
-          if answer != 'y':
-            print('Aborting dev release for %s' % version)
-            sys.exit(1)
+        maybe_check_call(args, ['git', 'cl', 'upload', '--no-squash'])
 
-        maybe_check_call(args, [
-          'git', 'push', 'origin', 'HEAD:%s' % R8_DEV_BRANCH])
-        maybe_tag(args, version)
+        if args.dry_run:
+          input(
+              'DryRun: check %s for content of version %s [enter to continue]:'
+              % (temp, version))
 
-        return "%s dev version %s from hash %s" % (
-          'DryRun: omitted publish of' if args.dry_run else 'Published',
+        return "%s dev version %s from hash %s for review" % (
+          'DryRun: omitted upload of' if args.dry_run else 'Uploaded',
           version,
           commithash)